Merge branch 'release-4.3' into release-4.4
diff --git a/.clang-format b/.clang-format index 84b96ab..a733a1a 100644 --- a/.clang-format +++ b/.clang-format
@@ -33,6 +33,8 @@ Priority: 3 - Regex: '^<sys/' Priority: 5 + - Regex: '^"QCMake' + Priority: 11 - Regex: '^(<|")Qt?[A-Z]' Priority: 6 - Regex: '^<cmtp/'
diff --git a/.clang-tidy b/.clang-tidy index f83585a..3a0fd82 100644 --- a/.clang-tidy +++ b/.clang-tidy
@@ -3,8 +3,10 @@ bugprone-*,\ -bugprone-assignment-in-if-condition,\ -bugprone-crtp-constructor-accessibility,\ +-bugprone-derived-method-shadowing-base-method,\ -bugprone-easily-swappable-parameters,\ -bugprone-empty-catch,\ +-bugprone-exception-escape,\ -bugprone-implicit-widening-of-multiplication-result,\ -bugprone-inc-dec-in-conditions,\ -bugprone-macro-parentheses,\ @@ -13,8 +15,10 @@ -bugprone-return-const-ref-from-parameter,\ -bugprone-suspicious-stringview-data-usage,\ -bugprone-switch-missing-default-case,\ +-bugprone-throwing-static-initialization,\ -bugprone-too-small-loop-variable,\ -bugprone-unchecked-optional-access,\ +-bugprone-unchecked-string-to-number-conversion,\ -bugprone-unused-local-non-trivial-variable,\ -bugprone-unused-return-value,\ -bugprone-use-after-move,\ @@ -22,17 +26,22 @@ -misc-confusable-identifiers,\ -misc-const-correctness,\ -misc-include-cleaner,\ +-misc-multiple-inheritance,\ -misc-non-private-member-variables-in-classes,\ -misc-no-recursion,\ +-misc-override-with-different-visibility,\ +-misc-predictable-rand,\ -misc-static-assert,\ -misc-use-anonymous-namespace,\ -misc-use-internal-linkage,\ modernize-*,\ -modernize-avoid-c-arrays,\ +-modernize-avoid-c-style-cast,\ -modernize-concat-nested-namespaces,\ -modernize-macro-to-enum,\ -modernize-return-braced-init-list,\ -modernize-type-traits,\ +-modernize-unary-static-assert, \ -modernize-use-emplace,\ -modernize-use-nodiscard,\ -modernize-use-noexcept,\ @@ -59,6 +68,9 @@ -readability-make-member-function-const,\ -readability-math-missing-parentheses,\ -readability-named-parameter,\ +-readability-redundant-casting,\ +-readability-redundant-parentheses,\ +-readability-redundant-typename,\ -readability-simplify-boolean-expr,\ -readability-static-accessed-through-instance,\ -readability-suspicious-call-argument,\ @@ -78,4 +90,18 @@ value: '0' - key: modernize-use-auto.MinTypeNameLength value: '80' + - key: readability-identifier-naming.ClassCase + value: 'CamelCase' + - key: readability-identifier-naming.ClassIgnoredRegexp + value: 'cm[A-Z][A-Za-z]+|(const_)?iterator' + - key: readability-identifier-naming.ClassMemberCase + value: 'CamelCase' + - key: readability-identifier-naming.ClassMemberIgnoredRegexp + value: 'prop[A-Z_]+' + - key: readability-identifier-naming.PrivateMemberCase + value: 'CamelCase' + - key: readability-identifier-naming.ParameterCase + value: 'camelBack' + - key: readability-identifier-naming.LocalVariableCase + value: 'camelBack' ...
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a78d723..03183f7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml
@@ -65,7 +65,7 @@ p:doc-package: extends: - - .fedora43_sphinx_package + - .fedora44_sphinx_package - .cmake_prep_doc_linux - .linux_x86_64_tags - .cmake_doc_artifacts @@ -132,9 +132,9 @@ - .rules needs: [] -l:tidy-fedora43: +l:tidy-fedora44: extends: - - .fedora43_tidy + - .fedora44_tidy - .cmake_build_linux - .cmake_tidy_artifacts - .linux_x86_64_tags @@ -142,9 +142,22 @@ - .rules needs: [] -l:sphinx-fedora43: +l:pvs-studio-fedora44: extends: - - .fedora43_sphinx + - .fedora44_pvs_studio + - .cmake_build_linux + - .linux_x86_64_tags + - .cmake_cdash_artifacts + - .rules + variables: + CMAKE_CI_JOB_CONTINUOUS: "true" + CMAKE_CI_JOB_NIGHTLY: "true" + CMAKE_CI_JOB_NO_MR: "true" + needs: [] + +l:sphinx-fedora44: + extends: + - .fedora44_sphinx - .cmake_build_linux - .cmake_sphinx_artifacts - .linux_x86_64_tags @@ -154,9 +167,9 @@ CMAKE_CI_JOB_HELP: "true" needs: [] -l:clang-analyzer-fedora43: +l:clang-analyzer-fedora44: extends: - - .fedora43_clang_analyzer + - .fedora44_clang_analyzer - .cmake_build_linux - .linux_x86_64_tags - .rules @@ -164,17 +177,17 @@ CMAKE_CI_JOB_NIGHTLY: "true" needs: [] -l:clang-fuzzing-fedora43: +l:clang-fuzzing-fedora44: extends: - - .fedora43_clang_fuzzing + - .fedora44_clang_fuzzing - .cmake_build_linux - .linux_x86_64_tags - .rules needs: [] -l:clazy-fedora43: +l:clazy-fedora44: extends: - - .fedora43_clazy + - .fedora44_clazy - .cmake_build_linux - .linux_x86_64_tags - .rules @@ -182,9 +195,9 @@ # Coverage builds -b:fedora43-gcc-gcov: +b:fedora44-gcc-gcov: extends: - - .fedora43_gcc_gcov + - .fedora44_gcc_gcov - .cmake_build_linux - .cmake_build_artifacts - .linux_x86_64_tags @@ -194,9 +207,9 @@ CMAKE_CI_JOB_RUN_MANUALLY: "true" needs: [] -t:fedora43-gcc-gcov: +t:fedora44-gcc-gcov: extends: - - .fedora43_gcc_gcov + - .fedora44_gcc_gcov - .cmake_test_linux - .cmake_coverage_artifacts - .linux_x86_64_tags_x11 @@ -204,25 +217,25 @@ # Run coverage analysis even if the tests fail. allow_failure: true needs: - - b:fedora43-gcc-gcov + - b:fedora44-gcc-gcov variables: CMAKE_CI_JOB_NIGHTLY: "true" -a:fedora43-gcc-gcov: +a:fedora44-gcc-gcov: extends: - - .fedora43_gcc_gcov + - .fedora44_gcc_gcov - .cmake_coverage_linux - .cmake_cdash_artifacts - .linux_x86_64_tags - .rules needs: - - t:fedora43-gcc-gcov + - t:fedora44-gcc-gcov variables: CMAKE_CI_JOB_NIGHTLY: "true" -b:fedora43-bullseye-coverage: +b:fedora44-bullseye-coverage: extends: - - .fedora43_bullseye_coverage + - .fedora44_bullseye_coverage - .cmake_build_linux - .cmake_build_artifacts - .linux_x86_64_tags @@ -233,9 +246,9 @@ CMAKE_CI_JOB_RUN_MANUALLY: "true" needs: [] -t:fedora43-bullseye-coverage: +t:fedora44-bullseye-coverage: extends: - - .fedora43_bullseye_coverage + - .fedora44_bullseye_coverage - .cmake_test_linux - .cmake_coverage_artifacts - .linux_x86_64_tags_x11 @@ -243,20 +256,20 @@ # Run coverage analysis even if the tests fail. allow_failure: true needs: - - b:fedora43-bullseye-coverage + - b:fedora44-bullseye-coverage variables: CMAKE_CI_JOB_NIGHTLY: "true" CMAKE_CI_JOB_NO_MR: "true" -a:fedora43-bullseye-coverage: +a:fedora44-bullseye-coverage: extends: - - .fedora43_bullseye_coverage + - .fedora44_bullseye_coverage - .cmake_coverage_linux - .cmake_cdash_artifacts - .linux_x86_64_tags - .rules needs: - - t:fedora43-bullseye-coverage + - t:fedora44-bullseye-coverage variables: CMAKE_CI_JOB_NIGHTLY: "true" CMAKE_CI_JOB_NO_MR: "true" @@ -352,9 +365,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-hip-radeon: +t:fedora44-hip-radeon: extends: - - .fedora43_hip_radeon + - .fedora44_hip_radeon - .cmake_test_linux_release - .linux_x86_64_tags_rocm6.2 - .rules @@ -362,9 +375,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-makefiles-lfortran: +t:fedora44-makefiles-lfortran: extends: - - .fedora43_makefiles_lfortran + - .fedora44_makefiles_lfortran - .cmake_test_linux_release - .linux_x86_64_tags - .rules @@ -372,9 +385,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-ninja-lfortran: +t:fedora44-ninja-lfortran: extends: - - .fedora43_ninja_lfortran + - .fedora44_ninja_lfortran - .cmake_test_linux_release - .linux_x86_64_tags - .rules @@ -382,17 +395,17 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-ninja-multi: +t:fedora44-ninja-multi: extends: - - .fedora43_ninja_multi + - .fedora44_ninja_multi - .cmake_test_linux_release - .linux_x86_64_tags - .rules - .needs_centos7_x86_64 -t:fedora43-ninja-clang: +t:fedora44-ninja-clang: extends: - - .fedora43_ninja_clang + - .fedora44_ninja_clang - .cmake_test_linux_release - .linux_x86_64_tags - .rules @@ -400,9 +413,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-ninja-multi-clang: +t:fedora44-ninja-gcc-std-reloc: extends: - - .fedora43_ninja_multi_clang + - .fedora44_ninja_gcc_std_reloc - .cmake_test_linux_release - .linux_x86_64_tags - .rules @@ -410,9 +423,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-makefiles-clang: +t:fedora44-ninja-multi-clang: extends: - - .fedora43_makefiles_clang + - .fedora44_ninja_multi_clang - .cmake_test_linux_release - .linux_x86_64_tags - .rules @@ -420,17 +433,27 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-makefiles: +t:fedora44-makefiles-clang: extends: - - .fedora43_makefiles + - .fedora44_makefiles_clang + - .cmake_test_linux_release + - .linux_x86_64_tags + - .rules + - .needs_centos7_x86_64 + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + +t:fedora44-makefiles: + extends: + - .fedora44_makefiles - .cmake_test_linux_release - .linux_x86_64_v3_tags - .rules - .needs_centos7_x86_64 -t:fedora43-makefiles-nospace: +t:fedora44-makefiles-nospace: extends: - - .fedora43_makefiles + - .fedora44_makefiles - .cmake_test_linux_release - .linux_x86_64_v3_tags - .cmake_junit_artifacts @@ -438,12 +461,12 @@ - .needs_centos7_x86_64 variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci" - CMAKE_CI_BUILD_NAME: fedora43_makefiles_nospace + CMAKE_CI_BUILD_NAME: fedora44_makefiles_nospace CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-fastbuild: +t:fedora44-fastbuild: extends: - - .fedora43_fastbuild + - .fedora44_fastbuild - .cmake_test_linux_release - .linux_x86_64_v3_tags - .rules @@ -451,9 +474,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-instrumentation: +t:fedora44-instrumentation: extends: - - .fedora43_ninja_instrumentation + - .fedora44_ninja_instrumentation - .cmake_test_linux_release - .linux_x86_64_tags - .rules @@ -462,11 +485,11 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:nvhpc24.9-ninja: +t:nvhpc26.3-ninja: extends: - .nvhpc_ninja - .cmake_test_linux_release - - .linux_x86_64_v3_tags_cuda_arch_52 + - .linux_x86_64_v3_tags_cuda_arch_75 - .rules - .needs_centos7_x86_64 variables: @@ -605,6 +628,8 @@ - .cmake_junit_artifacts - .rules - .needs_centos7_x86_64 + variables: + CMAKE_CI_JOB_NIGHTLY: "true" t:cuda13.0-aarch64-nvidia: extends: @@ -629,6 +654,15 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" +t:cuda13.2-nvidia: + extends: + - .cuda13.2_nvidia + - .cmake_test_linux_release + - .linux_x86_64_tags_cuda_arch_75 + - .cmake_junit_artifacts + - .rules + - .needs_centos7_x86_64 + t:hip6.3-nvidia: extends: - .hip6.3_nvidia @@ -649,36 +683,6 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:linux-gcc-cxx-modules-reloc-ninja: - extends: - - .gcc_cxx_modules_reloc_ninja - - .cmake_test_linux_release - - .linux_x86_64_tags - - .rules - - .needs_centos7_x86_64 - variables: - CMAKE_CI_JOB_NIGHTLY: "true" - -t:linux-gcc-cxx-modules-ninja: - extends: - - .gcc_cxx_modules_ninja - - .cmake_test_linux_release - - .linux_x86_64_tags - - .rules - - .needs_centos7_x86_64 - variables: - CMAKE_CI_JOB_NIGHTLY: "true" - -t:linux-gcc-cxx-modules-ninja-multi: - extends: - - .gcc_cxx_modules_ninja_multi - - .cmake_test_linux_release - - .linux_x86_64_tags - - .rules - - .needs_centos7_x86_64 - variables: - CMAKE_CI_JOB_NIGHTLY: "true" - t:debian10-legacy: extends: - .debian10_legacy @@ -690,9 +694,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -b:fedora43-ninja: +b:fedora44-ninja: extends: - - .fedora43_ninja + - .fedora44_ninja - .cmake_build_linux - .cmake_build_artifacts - .linux_x86_64_tags @@ -702,9 +706,9 @@ CMAKE_CI_JOB_RUN_MANUALLY: "true" needs: [] -b:fedora43-makefiles-symlinked: +b:fedora44-makefiles-symlinked: extends: - - .fedora43_makefiles_symlinked + - .fedora44_makefiles_symlinked - .cmake_build_linux - .cmake_build_artifacts - .linux_x86_64_tags @@ -747,9 +751,9 @@ CMAKE_CI_JOB_RUN_MANUALLY: "true" needs: [] -b:fedora43-extdeps: +b:fedora44-extdeps: extends: - - .fedora43_extdeps + - .fedora44_extdeps - .cmake_build_linux_standalone - .linux_x86_64_tags - .rules @@ -758,27 +762,27 @@ CMAKE_CI_JOB_RUN_MANUALLY: "true" needs: [] -t:fedora43-ninja: +t:fedora44-ninja: extends: - - .fedora43_ninja + - .fedora44_ninja - .cmake_test_linux - .linux_x86_64_tags_x11 - .cmake_test_artifacts - .rules needs: - - b:fedora43-ninja + - b:fedora44-ninja variables: CMAKE_CI_JOB_CONTINUOUS: "true" -t:fedora43-makefiles-symlinked: +t:fedora44-makefiles-symlinked: extends: - - .fedora43_makefiles_symlinked + - .fedora44_makefiles_symlinked - .cmake_test_linux - .linux_x86_64_tags_x11 - .cmake_test_artifacts - .rules needs: - - b:fedora43-makefiles-symlinked + - b:fedora44-makefiles-symlinked variables: CMAKE_CI_JOB_NIGHTLY: "true" @@ -817,34 +821,6 @@ CMAKE_CI_BUILD_NAME: intel2020u4_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2020u4-el8 -t:intel2021.1.1-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2021.1.1_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.1.1-el8 - -t:intel2021.1.2-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2021.1.2_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.1.2-el8 - -t:intel2021.2.0-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2021.2.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.2.0-el8 - -t:intel2021.3.0-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2021.3.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.3.0-el8 - t:intel2021.4.0-makefiles: extends: - .cmake_test_linux_intelclassic_makefiles @@ -852,20 +828,6 @@ CMAKE_CI_BUILD_NAME: intel2021.4.0_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.4.0-el8 -t:intel2021.5.0-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2021.5.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2022.0.2-el8 - -t:intel2021.6.0-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2021.6.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2022.1.0-el8 - t:intel2021.7.0-makefiles: extends: - .cmake_test_linux_intelclassic_makefiles @@ -873,20 +835,6 @@ CMAKE_CI_BUILD_NAME: intel2021.7.0_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2022.2.0-el8 -t:intel2021.8.0-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2021.8.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.0.0-el8 - -t:intel2021.9.0-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2021.9.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.1.0-el8 - t:intel2021.10.0-makefiles: extends: - .cmake_test_linux_intelclassic_makefiles @@ -894,34 +842,6 @@ CMAKE_CI_BUILD_NAME: intel2021.10.0_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.2.1-el8 -t:oneapi2021.1.1-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2021.1.1_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.1.1-el8 - -t:oneapi2021.1.2-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2021.1.2_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.1.2-el8 - -t:oneapi2021.2.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2021.2.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.2.0-el8 - -t:oneapi2021.3.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2021.3.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.3.0-el8 - t:oneapi2021.4.0-makefiles: extends: - .cmake_test_linux_inteloneapi_makefiles @@ -929,20 +849,6 @@ CMAKE_CI_BUILD_NAME: oneapi2021.4.0_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.4.0-el8 -t:oneapi2022.0.2-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2022.0.2_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2022.0.2-el8 - -t:oneapi2022.1.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2022.1.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2022.1.0-el8 - t:oneapi2022.2.0-makefiles: extends: - .cmake_test_linux_inteloneapi_makefiles @@ -950,20 +856,6 @@ CMAKE_CI_BUILD_NAME: oneapi2022.2.0_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2022.2.0-el8 -t:oneapi2023.0.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2023.0.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.0.0-el8 - -t:oneapi2023.1.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2023.1.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.1.0-el8 - t:oneapi2023.2.0-makefiles: extends: - .cmake_test_linux_inteloneapi_makefiles @@ -971,20 +863,6 @@ CMAKE_CI_BUILD_NAME: oneapi2023.2.1_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.2.1-el8 -t:oneapi2024.0.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2024.0.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2024.0.0-el8 - -t:oneapi2024.1.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2024.1.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2024.1.0-el8 - t:oneapi2024.2.0-makefiles: extends: - .cmake_test_linux_inteloneapi_makefiles @@ -992,27 +870,6 @@ CMAKE_CI_BUILD_NAME: oneapi2024.2.0_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2024.2.0-rocky9 -t:oneapi2025.0.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2025.0.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2025.0.0-rocky9 - -t:oneapi2025.1.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2025.1.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2025.1.0-rocky9 - -t:oneapi2025.2.0-makefiles: - extends: - - .cmake_test_linux_inteloneapi_makefiles - variables: - CMAKE_CI_BUILD_NAME: oneapi2025.2.0_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2025.2.0-rocky9 - t:oneapi2025.3.0-makefiles: extends: - .cmake_test_linux_inteloneapi_makefiles @@ -1118,9 +975,9 @@ ## Sanitizer builds -b:fedora43-asan: +b:fedora44-asan: extends: - - .fedora43_asan + - .fedora44_asan - .cmake_build_linux - .cmake_build_artifacts - .linux_x86_64_tags @@ -1130,21 +987,21 @@ CMAKE_CI_JOB_RUN_MANUALLY: "true" needs: [] -t:fedora43-asan: +t:fedora44-asan: extends: - - .fedora43_asan + - .fedora44_asan - .cmake_memcheck_linux - .linux_x86_64_tags - .cmake_junit_artifacts - .rules needs: - - b:fedora43-asan + - b:fedora44-asan variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora43-ninja-valgrind: +t:fedora44-ninja-valgrind: extends: - - .fedora43_ninja_valgrind + - .fedora44_ninja_valgrind - .cmake_test_linux_release # Use a fast CPU so that timeouts are less of a concern. - .linux_x86_64_tags_fast_x11 @@ -1581,7 +1438,20 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-clang21.1-cl-ninja: +t:windows-clang22.1-cl-fastbuild: + extends: + - .windows_clang_fastbuild + - .cmake_test_windows_external + - .windows_x86_64_tags_concurrent_vs2022_msvc14.44 + - .cmake_junit_artifacts + - .rules + needs: + - t:windows-vs2026-x64-ninja + variables: + CMAKE_CI_BUILD_NAME: windows_clang22.1_cl_fastbuild + CMAKE_CI_JOB_NIGHTLY: "true" + +t:windows-clang22.1-cl-ninja: extends: - .windows_clang_ninja - .cmake_test_windows_external @@ -1591,10 +1461,10 @@ needs: - t:windows-vs2026-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_clang21.1_cl_ninja + CMAKE_CI_BUILD_NAME: windows_clang22.1_cl_ninja CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-clang21.1-cl-nmake: +t:windows-clang22.1-cl-nmake: extends: - .windows_clang_nmake - .cmake_test_windows_external @@ -1604,10 +1474,10 @@ needs: - t:windows-vs2026-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_clang21.1_cl_nmake + CMAKE_CI_BUILD_NAME: windows_clang22.1_cl_nmake CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-clang21.1-gnu-ninja: +t:windows-clang22.1-gnu-ninja: extends: - .windows_clang_ninja - .cmake_test_windows_external @@ -1617,10 +1487,10 @@ needs: - t:windows-vs2026-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_clang21.1_gnu_ninja + CMAKE_CI_BUILD_NAME: windows_clang22.1_gnu_ninja CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-clang21.1-gnu-nmake: +t:windows-clang22.1-gnu-nmake: extends: - .windows_clang_nmake - .cmake_test_windows_external @@ -1630,7 +1500,7 @@ needs: - t:windows-vs2026-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_clang21.1_gnu_nmake + CMAKE_CI_BUILD_NAME: windows_clang22.1_gnu_nmake CMAKE_CI_JOB_NIGHTLY: "true" t:windows-intel2021.9-ninja: @@ -1707,9 +1577,57 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-orangec6.73.1: +t:windows-orangec6.73: extends: - - .windows_orangec6.73.1 + - .windows_orangec6.73 + - .cmake_test_windows_external + - .windows_x86_64_tags_concurrent + - .cmake_junit_artifacts + - .rules + needs: + - t:windows-vs2026-x64-ninja + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + +t:windows-orangec7.0: + extends: + - .windows_orangec7.0 + - .cmake_test_windows_external + - .windows_x86_64_tags_concurrent + - .cmake_junit_artifacts + - .rules + needs: + - t:windows-vs2026-x64-ninja + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + +t:windows-orangec7.10: + extends: + - .windows_orangec7.10 + - .cmake_test_windows_external + - .windows_x86_64_tags_concurrent + - .cmake_junit_artifacts + - .rules + needs: + - t:windows-vs2026-x64-ninja + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + +t:windows-pellesc13.01-x64: + extends: + - .windows_pellesc13.01_x64 + - .cmake_test_windows_external + - .windows_x86_64_tags_concurrent + - .cmake_junit_artifacts + - .rules + needs: + - t:windows-vs2026-x64-ninja + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + +t:windows-pellesc13.01-x86: + extends: + - .windows_pellesc13.01_x86 - .cmake_test_windows_external - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts
diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore index 91e677b..c0e73eb 100644 --- a/.gitlab/.gitignore +++ b/.gitlab/.gitignore
@@ -19,6 +19,8 @@ /openmp /open-watcom* /orangec +/pellesc +/pvs-studio /python* /qt* /sccache*
diff --git a/.gitlab/ci/bullseye.sh b/.gitlab/ci/bullseye.sh index 8c4cd2b..132588c 100755 --- a/.gitlab/ci/bullseye.sh +++ b/.gitlab/ci/bullseye.sh
@@ -7,12 +7,12 @@ exit 1 fi -readonly version="9.22.3" +readonly version="9.25.1" case "$(uname -s)-$(uname -m)" in Linux-x86_64) tarball="BullseyeCoverage-$version-Linux-x64.tar.xz" - sha256sum="d5be7e65d9363161b67fa77a30407c7c200d995af79a422c4e2e278802ba0776" + sha256sum="cc77ef1ffd846e9911a5a22c41a8e24c5e1f1c4c59c0c688b6756fcb14fa4af5" shatool="sha256sum" ;; *)
diff --git a/.gitlab/ci/clang-env.ps1 b/.gitlab/ci/clang-env.ps1 index 451fca3..896bcbe 100644 --- a/.gitlab/ci/clang-env.ps1 +++ b/.gitlab/ci/clang-env.ps1
@@ -11,5 +11,7 @@ } else { $env:CC = "clang-cl" $env:CXX = "clang-cl" + $env:FC = "flang" clang-cl --version + flang --version }
diff --git a/.gitlab/ci/clang.ps1 b/.gitlab/ci/clang.ps1 index 327fe3b..26abb5c 100644 --- a/.gitlab/ci/clang.ps1 +++ b/.gitlab/ci/clang.ps1
@@ -1,10 +1,10 @@ $erroractionpreference = "stop" -if ("$env:CMAKE_CI_BUILD_NAME".Contains("clang21.1")) { - # LLVM/Clang 21.1.8 - # https://github.com/llvm/llvm-project/releases/tag/llvmorg-21.1.8 - $filename = "llvm-21.1.8-win-x86_64-1" - $sha256sum = "3DB0063A6E434C694799CDD70052F27F08A758F1FF4B4C04D9910AD4198F6064" +if ("$env:CMAKE_CI_BUILD_NAME".Contains("clang22.1")) { + # LLVM/Clang 22.1.5 + # https://github.com/llvm/llvm-project/releases/tag/llvmorg-22.1.5 + $filename = "llvm-22.1.5-win-x86_64-1" + $sha256sum = "6230FBFBFB9B5B9E148A716787BCB384CB5D6042DDF5C219BFADD1AC20CB0A3F" } else { throw ('unknown CMAKE_CI_BUILD_NAME: ' + "$env:CMAKE_CI_BUILD_NAME") } @@ -30,6 +30,7 @@ $null = New-Item -ItemType HardLink -Path "$bin\clang++.exe" -Target "$bin\clang.exe" $null = New-Item -ItemType HardLink -Path "$bin\clang-cl.exe" -Target "$bin\clang.exe" $null = New-Item -ItemType HardLink -Path "$bin\clang-cpp.exe" -Target "$bin\clang.exe" +$null = New-Item -ItemType HardLink -Path "$bin\flang-new.exe" -Target "$bin\flang.exe" $null = New-Item -ItemType HardLink -Path "$bin\ld.lld.exe" -Target "$bin\lld.exe" $null = New-Item -ItemType HardLink -Path "$bin\ld64.lld.exe" -Target "$bin\lld.exe" $null = New-Item -ItemType HardLink -Path "$bin\lld-link.exe" -Target "$bin\lld.exe"
diff --git a/.gitlab/ci/cmake.ps1 b/.gitlab/ci/cmake.ps1 index 94f6694..ee0f4db 100644 --- a/.gitlab/ci/cmake.ps1 +++ b/.gitlab/ci/cmake.ps1
@@ -1,12 +1,12 @@ $erroractionpreference = "stop" -$version = "4.2.0" +$version = "4.3.2" if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { - $sha256sum = "CF35A516C4F5F4646B301E51C8E24B168CC012C3B1453B8F675303B54EB0EF45" + $sha256sum = "83D20C23F5C5F64B3B328785E35B23C532E33057A97ED6294ACACA3781B78A01" $platform = "windows-x86_64" } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { - $sha256sum = "9EB3E88083DDA569A4086C48517F5D49BD7B505DADA79075EA7CB6E2AC6E0A1E" + $sha256sum = "42A2A88FC57E70D04A92F6EAAAC2094241995F53584BC2AFDAE7E6EF982B3781" $platform = "windows-arm64" } else { throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE")
diff --git a/.gitlab/ci/cmake.sh b/.gitlab/ci/cmake.sh index a0e2a5a..bc41ddd 100755 --- a/.gitlab/ci/cmake.sh +++ b/.gitlab/ci/cmake.sh
@@ -2,22 +2,22 @@ set -e -readonly version="4.2.0" +readonly version="4.3.2" case "$(uname -s)-$(uname -m)" in Linux-x86_64) shatool="sha256sum" - sha256sum="bbcebd4c433eab3af03a8c80bb5d84e8dfc3ff8a4ab9d01547b21240c23f7c2c" + sha256sum="791ae3604841ca03cb3889a3ad89165346e4b180ae3448efd4b0caa9ef46d245" platform="linux-x86_64" ;; Linux-aarch64) shatool="sha256sum" - sha256sum="86f52e9769cd7745e5227931e689e122dc1afc5648cc9b04db601b165f3ac993" + sha256sum="377079ab739f5765176f427609d9a2015b756ea20d5cba908d279c3731a2f481" platform="linux-aarch64" ;; Darwin-*) shatool="shasum -a 256" - sha256sum="b8b040a06343b2b6bc090b03a9c2bb4e98037518846989fb7c40ebbf30655c5d" + sha256sum="808ab43a0db04c8eec9ed7db12b90d7be1c8e2e75f4a060724d604a2043ccaf7" platform="macos-universal" ;; *)
diff --git a/.gitlab/ci/configure_cuda13.2_nvidia.cmake b/.gitlab/ci/configure_cuda13.2_nvidia.cmake new file mode 100644 index 0000000..6a66194 --- /dev/null +++ b/.gitlab/ci/configure_cuda13.2_nvidia.cmake
@@ -0,0 +1,8 @@ +set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "") +set(CMake_TEST_CUDA_ARCH "75" CACHE STRING "") +set(CMake_TEST_CUDA_CUPTI "ON" CACHE STRING "") +set(CMake_TEST_CUDA_STANDARDS "03;11;14;17;20" CACHE STRING "") +set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenMP_CUDA "ON" CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian13_aarch64_ninja.cmake b/.gitlab/ci/configure_debian13_aarch64_ninja.cmake index 90fb7d9..c42b18d 100644 --- a/.gitlab/ci/configure_debian13_aarch64_ninja.cmake +++ b/.gitlab/ci/configure_debian13_aarch64_ninja.cmake
@@ -106,6 +106,7 @@ 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_MATH_OVERFLOW ON CACHE BOOL "") set(CMake_TEST_Qt5 "ON" CACHE BOOL "") set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
diff --git a/.gitlab/ci/configure_debian13_ninja_common.cmake b/.gitlab/ci/configure_debian13_ninja_common.cmake index 8b1bf14..7cc4099 100644 --- a/.gitlab/ci/configure_debian13_ninja_common.cmake +++ b/.gitlab/ci/configure_debian13_ninja_common.cmake
@@ -106,6 +106,7 @@ 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_MATH_OVERFLOW ON CACHE BOOL "") set(CMake_TEST_Qt5 "ON" CACHE BOOL "") set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
diff --git a/.gitlab/ci/configure_fedora43_asan.cmake b/.gitlab/ci/configure_fedora43_asan.cmake deleted file mode 100644 index 9913430..0000000 --- a/.gitlab/ci/configure_fedora43_asan.cmake +++ /dev/null
@@ -1,5 +0,0 @@ -set(CMAKE_C_FLAGS "-fsanitize=address" CACHE STRING "") -set(CMAKE_CXX_FLAGS "-fsanitize=address" CACHE STRING "") -set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_bullseye_coverage.cmake b/.gitlab/ci/configure_fedora43_bullseye_coverage.cmake deleted file mode 100644 index 5b35c57..0000000 --- a/.gitlab/ci/configure_fedora43_bullseye_coverage.cmake +++ /dev/null
@@ -1,21 +0,0 @@ -# Compile with Bullseye compiler wrappers, but do not test with them. -set(CMAKE_C_COMPILER "/opt/bullseye/bin/cc" CACHE PATH "") -set(CMAKE_CXX_COMPILER "/opt/bullseye/bin/c++" CACHE PATH "") - -# Bullseye records a COVFILE id in object files, so they cannot be cached. -set(configure_no_sccache 1) - -# Do not bootstrap for the coverage test suite. -set(CMAKE_SKIP_BOOTSTRAP_TEST TRUE CACHE BOOL "") - -# Shrink stress tests when running with Bullseye. -set(ENV{KWSYS_TEST_PROCESS_1_COUNT} 11) - -set(CMake_TEST_GUI "ON" CACHE BOOL "") -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") -set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") -set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") -set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") -set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_clang_analyzer.cmake b/.gitlab/ci/configure_fedora43_clang_analyzer.cmake deleted file mode 100644 index c18dfe6..0000000 --- a/.gitlab/ci/configure_fedora43_clang_analyzer.cmake +++ /dev/null
@@ -1,4 +0,0 @@ -set(configure_no_sccache 1) -set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_clazy.cmake b/.gitlab/ci/configure_fedora43_clazy.cmake deleted file mode 100644 index 1943447..0000000 --- a/.gitlab/ci/configure_fedora43_clazy.cmake +++ /dev/null
@@ -1,5 +0,0 @@ -set(configure_no_sccache 1) -set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") -set(CMAKE_USE_SYSTEM_JSONCPP ON CACHE BOOL "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_common_clang.cmake b/.gitlab/ci/configure_fedora43_common_clang.cmake deleted file mode 100644 index 941a040..0000000 --- a/.gitlab/ci/configure_fedora43_common_clang.cmake +++ /dev/null
@@ -1,17 +0,0 @@ -set(CMAKE_Fortran_COMPILER "/usr/bin/flang-21" CACHE FILEPATH "") -set(CMAKE_Fortran_COMPILER_ID "LLVMFlang" CACHE STRING "") -set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 "1" CACHE BOOL "") - -set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") -set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23;26" CACHE STRING "") - -set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenACC_C "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenACC_CXX "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenACC_Fortran "OFF" CACHE BOOL "") # flang-new fails producing LLVM IR -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 "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora43_gcc_gcov.cmake b/.gitlab/ci/configure_fedora43_gcc_gcov.cmake deleted file mode 100644 index b2eb3ac..0000000 --- a/.gitlab/ci/configure_fedora43_gcc_gcov.cmake +++ /dev/null
@@ -1,41 +0,0 @@ -set(CMake_TEST_GUI "ON" CACHE BOOL "") -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") -set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") -set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") -set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") -set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "") - -string(CONCAT coverage_flags - "--coverage " - "-fprofile-abs-path " - # These files are committed generated sources and contain relative path - # `#line` directives. CI coverage then cannot find the source files reliably. - # See related issue #20001. - "-fprofile-exclude-files=cmExprParser[.].*\\;cmFortranParser[.].* " - "-fdiagnostics-show-option " - "-Wall " - "-Wextra " - "-Wshadow " - "-Wpointer-arith " - "-Winvalid-pch " - "-Wcast-align " - "-Wdisabled-optimization " - "-Wwrite-strings " - "-fstack-protector-all " - "-Wconversion " - "-Wno-error=sign-conversion " - "-Wno-error=conversion ") -string(CONCAT link_flags - "--coverage ") - -set(CMAKE_C_FLAGS "${coverage_flags}" CACHE STRING "") -set(CMAKE_CXX_FLAGS "${coverage_flags} -Woverloaded-virtual -Wstrict-null-sentinel" CACHE STRING "") -# Apply `LDFLAGS`. -set(CMAKE_EXE_LINKER_FLAGS_INIT "${link_flags}" CACHE STRING "") -set(CMAKE_SHARED_LINKER_FLAGS_INIT "${link_flags}" CACHE STRING "") -set(CMAKE_MODULE_LINKER_FLAGS_INIT "${link_flags}" CACHE STRING "") - -# Do not bootstrap for the coverage test suite. -set(CMAKE_SKIP_BOOTSTRAP_TEST TRUE CACHE BOOL "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_makefiles.cmake b/.gitlab/ci/configure_fedora43_makefiles.cmake deleted file mode 100644 index 3ddf236..0000000 --- a/.gitlab/ci/configure_fedora43_makefiles.cmake +++ /dev/null
@@ -1,119 +0,0 @@ -set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") -set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23;26" CACHE STRING "") - -set(CMake_TEST_CTestUpdate_BZR "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_BIN2C_LARGE_FILE "ON" CACHE BOOL "") - set(CMake_TEST_CTestUpdate_P4 "ON" CACHE BOOL "") -endif() - -set(CMake_TEST_ASM_NASM "ON" CACHE BOOL "") -set(CMake_TEST_FindALSA "ON" CACHE BOOL "") -set(CMake_TEST_FindASPELL "ON" CACHE BOOL "") -set(CMake_TEST_FindBacktrace "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/lib64/mpich/bin/h5pcc" CACHE FILEPATH "") -# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/lib64/mpich/bin/h5pc++" CACHE FILEPATH "") # h5pc++ does not exist -set(CMake_TEST_FindHDF5_MPICH_ENVMOD "PATH=path_list_prepend:/usr/lib64/mpich/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/mpich/lib" CACHE STRING "") -set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/lib64/mpich/bin/h5pfc" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/lib64/openmpi/bin/h5pcc" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_OpenMPI_ENVMOD "PATH=path_list_prepend:/usr/lib64/openmpi/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/openmpi/lib" CACHE STRING "") -# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/lib64/openmpi/bin/h5pc++" CACHE FILEPATH "") # h5pc++ does not exist -set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/lib64/openmpi/bin/h5pfc" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "") -set(CMake_TEST_FindIconv "ON" CACHE BOOL "") -set(CMake_TEST_FindICU "ON" CACHE BOOL "") -set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "") -set(CMake_TEST_FindIntl "ON" CACHE BOOL "") -set(CMake_TEST_FindJasper "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_ENVMOD "PATH=path_list_prepend:/usr/lib64/mpich/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/mpich/lib;FI_PROVIDER=set:tcp" CACHE STRING "") -set(CMake_TEST_FindMPI "ON" CACHE BOOL "") -set(CMake_TEST_FindODBC "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenACC_C "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenACC_CXX "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenACC_Fortran "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_NumPy "ON" CACHE BOOL "") -set(CMake_TEST_FindPython2_PyPy "ON" CACHE BOOL "") -set(CMake_TEST_FindPython3_PyPy "ON" CACHE BOOL "") -set(CMake_TEST_FindRuby "ON" CACHE BOOL "") -set(CMake_TEST_FindRuby_RBENV "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_ELF_LARGE "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 "") -if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") - set(CMake_TEST_ISPC "ON" CACHE STRING "") -endif() -set(CMake_TEST_LOCALE_CHARSET "ISO-8859-1" CACHE STRING "") -set(CMake_TEST_Qt5 "ON" CACHE BOOL "") -set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") -set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") -set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") -set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "") -set(CMake_TEST_UseSWIG "ON" CACHE BOOL "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora43_makefiles_clang.cmake b/.gitlab/ci/configure_fedora43_makefiles_clang.cmake deleted file mode 100644 index e3f1ef9..0000000 --- a/.gitlab/ci/configure_fedora43_makefiles_clang.cmake +++ /dev/null
@@ -1,5 +0,0 @@ -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_fedora43_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora43_makefiles_lfortran.cmake b/.gitlab/ci/configure_fedora43_makefiles_lfortran.cmake deleted file mode 100644 index 918c2c3..0000000 --- a/.gitlab/ci/configure_fedora43_makefiles_lfortran.cmake +++ /dev/null
@@ -1 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_lfortran.cmake")
diff --git a/.gitlab/ci/configure_fedora43_makefiles_symlinked.cmake b/.gitlab/ci/configure_fedora43_makefiles_symlinked.cmake deleted file mode 100644 index c5ef84c..0000000 --- a/.gitlab/ci/configure_fedora43_makefiles_symlinked.cmake +++ /dev/null
@@ -1,5 +0,0 @@ -set(CMake_QT_MAJOR_VERSION "6" CACHE STRING "") -set(CMake_TEST_GUI "ON" CACHE BOOL "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja.cmake b/.gitlab/ci/configure_fedora43_ninja.cmake deleted file mode 100644 index 0371ebc..0000000 --- a/.gitlab/ci/configure_fedora43_ninja.cmake +++ /dev/null
@@ -1,24 +0,0 @@ -set(CMake_TEST_GUI "ON" CACHE BOOL "") -if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") - set(CMake_TEST_CPACK_APPIMAGE "ON" CACHE STRING "") - set(CMake_TEST_CPACK_APPIMAGE_RUNTIME_FILE "$ENV{CI_PROJECT_DIR}/.gitlab/appimagetool/lib/appimagetool/runtime" CACHE FILEPATH "") - set(CMake_TEST_ISPC "ON" CACHE STRING "") -endif() -set(CMake_TEST_LOCALE_CHARSET "UTF-8" CACHE STRING "") -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") -set(CMake_TEST_Rust "ON" CACHE STRING "") -set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") -set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") -set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") -set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "") - -# "Release" flags without "-DNDEBUG" so we get assertions. -set(CMAKE_C_FLAGS_RELEASE "-O3" CACHE STRING "") -set(CMAKE_CXX_FLAGS_RELEASE "-O3" CACHE STRING "") - -# Cover compilation with C++11 only and not higher standards. -set(CMAKE_CXX_STANDARD "11" CACHE STRING "") -# Qt 6 requires C++17, so use Qt 5. -set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_clang.cmake b/.gitlab/ci/configure_fedora43_ninja_clang.cmake deleted file mode 100644 index 6cf0f85..0000000 --- a/.gitlab/ci/configure_fedora43_ninja_clang.cmake +++ /dev/null
@@ -1,3 +0,0 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_instrumentation.cmake b/.gitlab/ci/configure_fedora43_ninja_instrumentation.cmake deleted file mode 100644 index daf845f..0000000 --- a/.gitlab/ci/configure_fedora43_ninja_instrumentation.cmake +++ /dev/null
@@ -1,3 +0,0 @@ -set(CMake_ENABLE_INSTRUMENTATION "ON" CACHE BOOL "") -set(CMake_TEST_GUI "OFF" CACHE BOOL "") -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_ninja.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_lfortran.cmake b/.gitlab/ci/configure_fedora43_ninja_lfortran.cmake deleted file mode 100644 index 918c2c3..0000000 --- a/.gitlab/ci/configure_fedora43_ninja_lfortran.cmake +++ /dev/null
@@ -1 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_lfortran.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_multi_clang.cmake b/.gitlab/ci/configure_fedora43_ninja_multi_clang.cmake deleted file mode 100644 index 522d012..0000000 --- a/.gitlab/ci/configure_fedora43_ninja_multi_clang.cmake +++ /dev/null
@@ -1,3 +0,0 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_valgrind.cmake b/.gitlab/ci/configure_fedora43_ninja_valgrind.cmake deleted file mode 100644 index d156e20..0000000 --- a/.gitlab/ci/configure_fedora43_ninja_valgrind.cmake +++ /dev/null
@@ -1,6 +0,0 @@ -set(CMake_TEST_Qt5 ON CACHE BOOL "") -set(CMake_TEST_Qt6 ON CACHE BOOL "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_valgrind.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_ninja.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora43_tidy.cmake b/.gitlab/ci/configure_fedora43_tidy.cmake deleted file mode 100644 index 5b0bd62..0000000 --- a/.gitlab/ci/configure_fedora43_tidy.cmake +++ /dev/null
@@ -1,7 +0,0 @@ -set(CMake_RUN_CLANG_TIDY ON CACHE BOOL "") -set(CMake_USE_CLANG_TIDY_MODULE ON CACHE BOOL "") -set(CMake_CLANG_TIDY_MODULE "$ENV{CI_PROJECT_DIR}/Utilities/ClangTidyModule/build/libcmake-clang-tidy-module.so" CACHE FILEPATH "") -set(CMake_CLANG_TIDY_EXPORT_FIXES_DIR "$ENV{CI_PROJECT_DIR}/.gitlab/clang-tidy-fixes" CACHE PATH "") -set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora44_asan.cmake b/.gitlab/ci/configure_fedora44_asan.cmake new file mode 100644 index 0000000..88f40c3 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_asan.cmake
@@ -0,0 +1,5 @@ +set(CMAKE_C_FLAGS "-fsanitize=address" CACHE STRING "") +set(CMAKE_CXX_FLAGS "-fsanitize=address" CACHE STRING "") +set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora44_bullseye_coverage.cmake b/.gitlab/ci/configure_fedora44_bullseye_coverage.cmake new file mode 100644 index 0000000..d2e44a9 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_bullseye_coverage.cmake
@@ -0,0 +1,22 @@ +# Compile with Bullseye compiler wrappers, but do not test with them. +set(CMAKE_C_COMPILER "/opt/bullseye/bin/cc" CACHE PATH "") +set(CMAKE_CXX_COMPILER "/opt/bullseye/bin/c++" CACHE PATH "") + +# Bullseye records a COVFILE id in object files, so they cannot be cached. +set(configure_no_sccache 1) + +# Do not bootstrap for the coverage test suite. +set(CMAKE_SKIP_BOOTSTRAP_TEST TRUE CACHE BOOL "") + +# Shrink stress tests when running with Bullseye. +set(ENV{KWSYS_TEST_PROCESS_1_COUNT} 11) + +set(CMake_TEST_GUI "ON" CACHE BOOL "") +set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") +set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora44_clang_analyzer.cmake b/.gitlab/ci/configure_fedora44_clang_analyzer.cmake new file mode 100644 index 0000000..58046b9 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_clang_analyzer.cmake
@@ -0,0 +1,4 @@ +set(configure_no_sccache 1) +set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_clang_fuzzing.cmake b/.gitlab/ci/configure_fedora44_clang_fuzzing.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_clang_fuzzing.cmake rename to .gitlab/ci/configure_fedora44_clang_fuzzing.cmake
diff --git a/.gitlab/ci/configure_fedora44_clazy.cmake b/.gitlab/ci/configure_fedora44_clazy.cmake new file mode 100644 index 0000000..7f2ec78 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_clazy.cmake
@@ -0,0 +1,5 @@ +set(configure_no_sccache 1) +set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") +set(CMAKE_USE_SYSTEM_JSONCPP ON CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_common.cmake b/.gitlab/ci/configure_fedora44_common.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_common.cmake rename to .gitlab/ci/configure_fedora44_common.cmake
diff --git a/.gitlab/ci/configure_fedora44_common_clang.cmake b/.gitlab/ci/configure_fedora44_common_clang.cmake new file mode 100644 index 0000000..bd0290c --- /dev/null +++ b/.gitlab/ci/configure_fedora44_common_clang.cmake
@@ -0,0 +1,19 @@ +set(CMAKE_Fortran_COMPILER "/usr/bin/flang" CACHE FILEPATH "") +set(CMAKE_Fortran_COMPILER_ID "LLVMFlang" CACHE STRING "") +set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 "1" CACHE BOOL "") + +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23;26" CACHE STRING "") + +set(CMake_TEST_CLANG_COVERAGE "ON" CACHE BOOL "") + +set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenACC_C "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenACC_CXX "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenACC_Fortran "OFF" CACHE BOOL "") # flang-new fails producing LLVM IR +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 "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora43_common_lfortran.cmake b/.gitlab/ci/configure_fedora44_common_lfortran.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_common_lfortran.cmake rename to .gitlab/ci/configure_fedora44_common_lfortran.cmake
diff --git a/.gitlab/ci/configure_fedora43_extdeps.cmake b/.gitlab/ci/configure_fedora44_extdeps.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_extdeps.cmake rename to .gitlab/ci/configure_fedora44_extdeps.cmake
diff --git a/.gitlab/ci/configure_fedora43_fastbuild.cmake b/.gitlab/ci/configure_fedora44_fastbuild.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_fastbuild.cmake rename to .gitlab/ci/configure_fedora44_fastbuild.cmake
diff --git a/.gitlab/ci/configure_fedora44_gcc_gcov.cmake b/.gitlab/ci/configure_fedora44_gcc_gcov.cmake new file mode 100644 index 0000000..e54437e --- /dev/null +++ b/.gitlab/ci/configure_fedora44_gcc_gcov.cmake
@@ -0,0 +1,31 @@ +set(CMake_TEST_GUI "ON" CACHE BOOL "") +set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") +set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "") + +string(JOIN " " coverage_flags + "--coverage" + "-fprofile-abs-path" + # These files are committed generated sources and contain relative path + # `#line` directives. CI coverage then cannot find the source files reliably. + # See related issue #20001. + "-fprofile-exclude-files=cmExprParser[.].*\\;cmFortranParser[.].*" +) +string(JOIN " " link_flags + "--coverage" +) + +set(CMAKE_C_FLAGS "${coverage_flags}" CACHE STRING "") +set(CMAKE_CXX_FLAGS "${coverage_flags}" CACHE STRING "") +# Apply `LDFLAGS`. +set(CMAKE_EXE_LINKER_FLAGS_INIT "${link_flags}" CACHE STRING "") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "${link_flags}" CACHE STRING "") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "${link_flags}" CACHE STRING "") + +# Do not bootstrap for the coverage test suite. +set(CMAKE_SKIP_BOOTSTRAP_TEST TRUE CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_hip_radeon.cmake b/.gitlab/ci/configure_fedora44_hip_radeon.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_hip_radeon.cmake rename to .gitlab/ci/configure_fedora44_hip_radeon.cmake
diff --git a/.gitlab/ci/configure_fedora44_makefiles.cmake b/.gitlab/ci/configure_fedora44_makefiles.cmake new file mode 100644 index 0000000..49ed52f --- /dev/null +++ b/.gitlab/ci/configure_fedora44_makefiles.cmake
@@ -0,0 +1,122 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23;26" CACHE STRING "") + +set(CMake_TEST_CTestUpdate_BZR "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_BIN2C_LARGE_FILE "ON" CACHE BOOL "") + set(CMake_TEST_CTestUpdate_P4 "ON" CACHE BOOL "") +endif() + +set(CMake_TEST_ASM_NASM "ON" CACHE BOOL "") +set(CMake_TEST_FindALSA "ON" CACHE BOOL "") +set(CMake_TEST_FindASPELL "ON" CACHE BOOL "") +set(CMake_TEST_FindBacktrace "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/lib64/mpich/bin/h5pcc" CACHE FILEPATH "") +# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/lib64/mpich/bin/h5pc++" CACHE FILEPATH "") # h5pc++ does not exist +set(CMake_TEST_FindHDF5_MPICH_ENVMOD "PATH=path_list_prepend:/usr/lib64/mpich/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/mpich/lib" CACHE STRING "") +set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/lib64/mpich/bin/h5pfc" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/lib64/openmpi/bin/h5pcc" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_OpenMPI_ENVMOD "PATH=path_list_prepend:/usr/lib64/openmpi/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/openmpi/lib" CACHE STRING "") +# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/lib64/openmpi/bin/h5pc++" CACHE FILEPATH "") # h5pc++ does not exist +set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/lib64/openmpi/bin/h5pfc" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "") +set(CMake_TEST_FindIconv "ON" CACHE BOOL "") +set(CMake_TEST_FindICU "ON" CACHE BOOL "") +set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "") +set(CMake_TEST_FindIntl "ON" CACHE BOOL "") +set(CMake_TEST_FindJasper "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_ENVMOD "PATH=path_list_prepend:/usr/lib64/mpich/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/mpich/lib;FI_PROVIDER=set:tcp" CACHE STRING "") +set(CMake_TEST_FindMPI "ON" CACHE BOOL "") +set(CMake_TEST_FindODBC "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenACC_C "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenACC_CXX "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenACC_Fortran "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_NumPy "ON" CACHE BOOL "") +set(CMake_TEST_FindPython2_PyPy "ON" CACHE BOOL "") +set(CMake_TEST_FindPython3_PyPy "ON" CACHE BOOL "") +set(CMake_TEST_FindRuby "ON" CACHE BOOL "") +set(CMake_TEST_FindRuby_RBENV "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_ELF_LARGE "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 "") +if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(CMake_TEST_ISPC "ON" CACHE STRING "") +endif() +set(CMake_TEST_LOCALE_CHARSET "ISO-8859-1" CACHE STRING "") +set(CMake_TEST_LOCALE_C_UTF8 ON CACHE BOOL "") +set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "") +set(CMake_TEST_Qt5 "ON" CACHE BOOL "") +set(CMake_TEST_Rust "ON" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") +set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "") +set(CMake_TEST_UseSWIG "ON" CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora44_makefiles_clang.cmake b/.gitlab/ci/configure_fedora44_makefiles_clang.cmake new file mode 100644 index 0000000..02bf288 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_makefiles_clang.cmake
@@ -0,0 +1,5 @@ +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_fedora44_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora44_makefiles_lfortran.cmake b/.gitlab/ci/configure_fedora44_makefiles_lfortran.cmake new file mode 100644 index 0000000..2289116 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_makefiles_lfortran.cmake
@@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_lfortran.cmake")
diff --git a/.gitlab/ci/configure_fedora44_makefiles_symlinked.cmake b/.gitlab/ci/configure_fedora44_makefiles_symlinked.cmake new file mode 100644 index 0000000..e14b28f --- /dev/null +++ b/.gitlab/ci/configure_fedora44_makefiles_symlinked.cmake
@@ -0,0 +1,5 @@ +set(CMake_QT_MAJOR_VERSION "6" CACHE STRING "") +set(CMake_TEST_GUI "ON" CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora44_ninja.cmake b/.gitlab/ci/configure_fedora44_ninja.cmake new file mode 100644 index 0000000..d536871 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_ninja.cmake
@@ -0,0 +1,45 @@ +set(CMake_TEST_GUI "ON" CACHE BOOL "") +if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(CMake_TEST_CPACK_APPIMAGE "ON" CACHE STRING "") + set(CMake_TEST_CPACK_APPIMAGE_RUNTIME_FILE "$ENV{CI_PROJECT_DIR}/.gitlab/appimagetool/lib/appimagetool/runtime" CACHE FILEPATH "") + set(CMake_TEST_ISPC "ON" CACHE STRING "") +endif() +set(CMake_TEST_LOCALE_CHARSET "UTF-8" CACHE STRING "") +set(CMake_TEST_LOCALE_C_UTF8 ON CACHE BOOL "") +set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") +set(CMake_TEST_Rust "ON" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") +set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "") + +string(JOIN " " flags + "-fdiagnostics-show-option" + "-Wall" + "-Wextra" + "-Wshadow" + "-Wpointer-arith" + "-Winvalid-pch" + "-Wcast-align" + "-Wdisabled-optimization" + "-Wwrite-strings" + "-fstack-protector-all" + "-Wconversion" + "-Wno-error=sign-conversion" + "-Wno-error=conversion" +) + +set(CMAKE_C_FLAGS "${flags}" CACHE STRING "") +set(CMAKE_CXX_FLAGS "${flags} -Woverloaded-virtual -Wstrict-null-sentinel" CACHE STRING "") + +# "Release" flags without "-DNDEBUG" so we get assertions. +set(CMAKE_C_FLAGS_RELEASE "-O3" CACHE STRING "") +set(CMAKE_CXX_FLAGS_RELEASE "-O3" CACHE STRING "") + +# Cover compilation with C++11 only and not higher standards. +set(CMAKE_CXX_STANDARD "11" CACHE STRING "") +# Qt 6 requires C++17, so use Qt 5. +set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora44_ninja_clang.cmake b/.gitlab/ci/configure_fedora44_ninja_clang.cmake new file mode 100644 index 0000000..5e007f4 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_ninja_clang.cmake
@@ -0,0 +1,3 @@ +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora44_ninja_gcc_std_reloc.cmake b/.gitlab/ci/configure_fedora44_ninja_gcc_std_reloc.cmake new file mode 100644 index 0000000..6cd5f3e --- /dev/null +++ b/.gitlab/ci/configure_fedora44_ninja_gcc_std_reloc.cmake
@@ -0,0 +1,4 @@ +set(CMake_TEST_CXX_STDLIB_MODULES_JSON "$ENV{CMAKE_CI_CXX_STDLIB_MODULES_JSON}" CACHE FILEPATH "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora44_ninja_instrumentation.cmake b/.gitlab/ci/configure_fedora44_ninja_instrumentation.cmake new file mode 100644 index 0000000..1033477 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_ninja_instrumentation.cmake
@@ -0,0 +1,3 @@ +set(CMake_ENABLE_INSTRUMENTATION "ON" CACHE BOOL "") +set(CMake_TEST_GUI "OFF" CACHE BOOL "") +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_ninja.cmake")
diff --git a/.gitlab/ci/configure_fedora44_ninja_lfortran.cmake b/.gitlab/ci/configure_fedora44_ninja_lfortran.cmake new file mode 100644 index 0000000..2289116 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_ninja_lfortran.cmake
@@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_lfortran.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_multi.cmake b/.gitlab/ci/configure_fedora44_ninja_multi.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_ninja_multi.cmake rename to .gitlab/ci/configure_fedora44_ninja_multi.cmake
diff --git a/.gitlab/ci/configure_fedora44_ninja_multi_clang.cmake b/.gitlab/ci/configure_fedora44_ninja_multi_clang.cmake new file mode 100644 index 0000000..3a54edf --- /dev/null +++ b/.gitlab/ci/configure_fedora44_ninja_multi_clang.cmake
@@ -0,0 +1,3 @@ +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora44_ninja_valgrind.cmake b/.gitlab/ci/configure_fedora44_ninja_valgrind.cmake new file mode 100644 index 0000000..53f1b83 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_ninja_valgrind.cmake
@@ -0,0 +1,6 @@ +set(CMake_TEST_Qt5 ON CACHE BOOL "") +set(CMake_TEST_Qt6 ON CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_valgrind.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_ninja.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora44_pvs_studio.cmake b/.gitlab/ci/configure_fedora44_pvs_studio.cmake new file mode 100644 index 0000000..b15137e --- /dev/null +++ b/.gitlab/ci/configure_fedora44_pvs_studio.cmake
@@ -0,0 +1,3 @@ +set(configure_no_sccache 1) +set(CMake_RUN_PVS_STUDIO ON CACHE BOOL "") +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_sphinx.cmake b/.gitlab/ci/configure_fedora44_sphinx.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_sphinx.cmake rename to .gitlab/ci/configure_fedora44_sphinx.cmake
diff --git a/.gitlab/ci/configure_fedora43_sphinx_package.cmake b/.gitlab/ci/configure_fedora44_sphinx_package.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_sphinx_package.cmake rename to .gitlab/ci/configure_fedora44_sphinx_package.cmake
diff --git a/.gitlab/ci/configure_fedora44_tidy.cmake b/.gitlab/ci/configure_fedora44_tidy.cmake new file mode 100644 index 0000000..18e7743 --- /dev/null +++ b/.gitlab/ci/configure_fedora44_tidy.cmake
@@ -0,0 +1,7 @@ +set(CMake_RUN_CLANG_TIDY ON CACHE BOOL "") +set(CMake_USE_CLANG_TIDY_MODULE ON CACHE BOOL "") +set(CMake_CLANG_TIDY_MODULE "$ENV{CI_PROJECT_DIR}/Utilities/ClangTidyModule/build/libcmake-clang-tidy-module.so" CACHE FILEPATH "") +set(CMake_CLANG_TIDY_EXPORT_FIXES_DIR "$ENV{CI_PROJECT_DIR}/.gitlab/clang-tidy-fixes" CACHE PATH "") +set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_valgrind.cmake b/.gitlab/ci/configure_fedora44_valgrind.cmake similarity index 100% rename from .gitlab/ci/configure_fedora43_valgrind.cmake rename to .gitlab/ci/configure_fedora44_valgrind.cmake
diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake deleted file mode 100644 index b7faa36..0000000 --- a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake +++ /dev/null
@@ -1,3 +0,0 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,bmionly,build_database,import_std23" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake deleted file mode 100644 index b7faa36..0000000 --- a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake +++ /dev/null
@@ -1,3 +0,0 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,bmionly,build_database,import_std23" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_reloc_ninja.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_reloc_ninja.cmake deleted file mode 100644 index eb728ed..0000000 --- a/.gitlab/ci/configure_linux_gcc_cxx_modules_reloc_ninja.cmake +++ /dev/null
@@ -1,13 +0,0 @@ -# "Misplace" the `libstdc++.modules.json` file so that -# `CMAKE_CXX_STDLIB_MODULES_JSON` is needed to use `import std`. -set(gcc_prefix "/opt/gcc-importstd") -set(CMake_TEST_CXX_STDLIB_MODULES_JSON - "${gcc_prefix}/lib64.reloc/libstdc++.modules.json" - CACHE FILEPATH "") -file(MAKE_DIRECTORY - "${gcc_prefix}/lib64.reloc") -file(RENAME - "${gcc_prefix}/lib64/libstdc++.modules.json" - "${CMake_TEST_CXX_STDLIB_MODULES_JSON}") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_macos_arm64_ninja.cmake b/.gitlab/ci/configure_macos_arm64_ninja.cmake index c1924fe..1d0787c 100644 --- a/.gitlab/ci/configure_macos_arm64_ninja.cmake +++ b/.gitlab/ci/configure_macos_arm64_ninja.cmake
@@ -7,6 +7,8 @@ set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "") set(CMake_TEST_GUI "ON" CACHE BOOL "") set(CMake_TEST_LOCALE_CHARSET "UTF-8" CACHE STRING "") +set(CMake_TEST_LOCALE_C_UTF8 ON CACHE BOOL "") +set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "") set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
diff --git a/.gitlab/ci/configure_macos_x86_64_makefiles.cmake b/.gitlab/ci/configure_macos_x86_64_makefiles.cmake index 99beb68..1a5a6e5 100644 --- a/.gitlab/ci/configure_macos_x86_64_makefiles.cmake +++ b/.gitlab/ci/configure_macos_x86_64_makefiles.cmake
@@ -6,6 +6,7 @@ if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") set(CMake_TEST_ISPC "ON" CACHE STRING "") endif() +set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "") set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
diff --git a/.gitlab/ci/configure_macos_x86_64_ninja.cmake b/.gitlab/ci/configure_macos_x86_64_ninja.cmake index 1af144f..c3c3d83 100644 --- a/.gitlab/ci/configure_macos_x86_64_ninja.cmake +++ b/.gitlab/ci/configure_macos_x86_64_ninja.cmake
@@ -10,6 +10,8 @@ set(CMake_TEST_ISPC "ON" CACHE STRING "") endif() set(CMake_TEST_LOCALE_CHARSET "UTF-8" CACHE STRING "") +set(CMake_TEST_LOCALE_C_UTF8 ON CACHE BOOL "") +set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "") set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
diff --git a/.gitlab/ci/configure_nvhpc_ninja.cmake b/.gitlab/ci/configure_nvhpc_ninja.cmake index e25ff72..b72c4aa 100644 --- a/.gitlab/ci/configure_nvhpc_ninja.cmake +++ b/.gitlab/ci/configure_nvhpc_ninja.cmake
@@ -1,9 +1,9 @@ set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "") -set(CMake_TEST_CUDA_ARCH "52" CACHE STRING "") +set(CMake_TEST_CUDA_ARCH "75" CACHE STRING "") set(CMake_TEST_CUDA_CUPTI "ON" CACHE STRING "") -set(CMake_TEST_C_STANDARDS "90;99;11;17" CACHE STRING "") -set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "") +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23;26" CACHE STRING "") set(configure_no_sccache 1)
diff --git a/.gitlab/ci/configure_windows_clang_common.cmake b/.gitlab/ci/configure_windows_clang_common.cmake index 0d00285..e6daf50 100644 --- a/.gitlab/ci/configure_windows_clang_common.cmake +++ b/.gitlab/ci/configure_windows_clang_common.cmake
@@ -7,6 +7,8 @@ set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "") endif() +set(CMAKE_Fortran_COMPILER "" CACHE STRING "") + set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_windows_clang_fastbuild.cmake b/.gitlab/ci/configure_windows_clang_fastbuild.cmake new file mode 100644 index 0000000..ba19834 --- /dev/null +++ b/.gitlab/ci/configure_windows_clang_fastbuild.cmake
@@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_clang_common.cmake")
diff --git a/.gitlab/ci/configure_windows_clang_ninja.cmake b/.gitlab/ci/configure_windows_clang_ninja.cmake index 214c754..c92d9b6 100644 --- a/.gitlab/ci/configure_windows_clang_ninja.cmake +++ b/.gitlab/ci/configure_windows_clang_ninja.cmake
@@ -1,4 +1,7 @@ -if("$ENV{CMAKE_CI_BUILD_NAME}" MATCHES "(^|_)gnu(_|$)") - set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database" CACHE STRING "") +if("$ENV{CMAKE_CI_BUILD_NAME}" MATCHES "(^|_)cl(_|$)") + set(CMAKE_Fortran_COMPILER "flang" CACHE STRING "") + set(CMAKE_Fortran_COMPILER_ID "LLVMFlang" CACHE STRING "") + set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 "1" CACHE BOOL "") endif() include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_clang_common.cmake")
diff --git a/.gitlab/ci/configure_windows_common.cmake b/.gitlab/ci/configure_windows_common.cmake index 1246885..b243ece 100644 --- a/.gitlab/ci/configure_windows_common.cmake +++ b/.gitlab/ci/configure_windows_common.cmake
@@ -3,6 +3,7 @@ set(CMAKE_PREFIX_PATH "$ENV{CI_PROJECT_DIR}/.gitlab/qt" CACHE STRING "") set(CMake_TEST_Java OFF CACHE BOOL "") set(CMake_TEST_LOCALE_CHARSET "UTF-8" CACHE STRING "") +set(CMake_TEST_LOCALE_C_UTF8 ON CACHE BOOL "") set(Python_FIND_REGISTRY NEVER CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_windows_intelclassic_ninja.cmake b/.gitlab/ci/configure_windows_intelclassic_ninja.cmake index c2d708b..84b9a88 100644 --- a/.gitlab/ci/configure_windows_intelclassic_ninja.cmake +++ b/.gitlab/ci/configure_windows_intelclassic_ninja.cmake
@@ -1 +1,4 @@ +set(CMake_TEST_C_STANDARDS "90;99;11" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20" CACHE STRING "") + include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_intelcompiler_common.cmake")
diff --git a/.gitlab/ci/configure_windows_inteloneapi_ninja.cmake b/.gitlab/ci/configure_windows_inteloneapi_ninja.cmake index c2d708b..cb252e0 100644 --- a/.gitlab/ci/configure_windows_inteloneapi_ninja.cmake +++ b/.gitlab/ci/configure_windows_inteloneapi_ninja.cmake
@@ -1 +1,4 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23;26" CACHE STRING "") + include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_intelcompiler_common.cmake")
diff --git a/.gitlab/ci/configure_windows_orangec6.73.1.cmake b/.gitlab/ci/configure_windows_orangec6.73.cmake similarity index 100% rename from .gitlab/ci/configure_windows_orangec6.73.1.cmake rename to .gitlab/ci/configure_windows_orangec6.73.cmake
diff --git a/.gitlab/ci/configure_windows_orangec7.0.cmake b/.gitlab/ci/configure_windows_orangec7.0.cmake new file mode 100644 index 0000000..e5ca56b --- /dev/null +++ b/.gitlab/ci/configure_windows_orangec7.0.cmake
@@ -0,0 +1,3 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "11;14;17" CACHE STRING "") +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_orangec_common.cmake")
diff --git a/.gitlab/ci/configure_windows_orangec7.10.cmake b/.gitlab/ci/configure_windows_orangec7.10.cmake new file mode 100644 index 0000000..e5ca56b --- /dev/null +++ b/.gitlab/ci/configure_windows_orangec7.10.cmake
@@ -0,0 +1,3 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "11;14;17" CACHE STRING "") +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_orangec_common.cmake")
diff --git a/.gitlab/ci/configure_windows_pellesc13.01_x64.cmake b/.gitlab/ci/configure_windows_pellesc13.01_x64.cmake new file mode 100644 index 0000000..db8a63c --- /dev/null +++ b/.gitlab/ci/configure_windows_pellesc13.01_x64.cmake
@@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_pellesc_common.cmake")
diff --git a/.gitlab/ci/configure_windows_pellesc13.01_x86.cmake b/.gitlab/ci/configure_windows_pellesc13.01_x86.cmake new file mode 100644 index 0000000..db8a63c --- /dev/null +++ b/.gitlab/ci/configure_windows_pellesc13.01_x86.cmake
@@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_pellesc_common.cmake")
diff --git a/.gitlab/ci/configure_windows_pellesc_common.cmake b/.gitlab/ci/configure_windows_pellesc_common.cmake new file mode 100644 index 0000000..7027e7c --- /dev/null +++ b/.gitlab/ci/configure_windows_pellesc_common.cmake
@@ -0,0 +1,8 @@ +set(CMake_TEST_CXX OFF CACHE BOOL "") +set(CMake_TEST_Java OFF CACHE BOOL "") + +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") + +set(configure_no_sccache 1) + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake index e04dea3..3739e45 100644 --- a/.gitlab/ci/ctest_exclusions.cmake +++ b/.gitlab/ci/ctest_exclusions.cmake
@@ -49,6 +49,29 @@ ) endif() +if ("$ENV{CMAKE_CONFIGURATION}" STREQUAL "windows_orangec7.10") + list(APPEND test_exclusions + # FIXME(OrangeC#1155): OrangeC 7.10 does not reconcile pointer-to-array and pointer-to-pointer + "^testdriver[1-5]$" + ) +elseif ("$ENV{CMAKE_CONFIGURATION}" STREQUAL "windows_orangec7.0") + list(APPEND test_exclusions + # FIXME(OrangeC#1136): OrangeC 7.0 no longer fails when linking a missing library + "^RunCMake.CheckModules$" + ) +endif() + +if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows_clang_fastbuild") + list(APPEND test_exclusions + # FIXME(#27697): These fail with clang-cl and FASTBuild. + "^BuildDepends$" + "^ExportImport$" + "^Module.CheckIPOSupported-C$" + "^RunCMake.PrecompileHeaders$" + "^RunCMake.PrecompileHeaders-Reuse$" + ) +endif() + string(REPLACE ";" "|" test_exclusions "${test_exclusions}") if (test_exclusions) set(test_exclusions "(${test_exclusions})")
diff --git a/.gitlab/ci/ctest_memcheck_fedora43.valgrind.supp b/.gitlab/ci/ctest_memcheck_fedora44.valgrind.supp similarity index 100% rename from .gitlab/ci/ctest_memcheck_fedora43.valgrind.supp rename to .gitlab/ci/ctest_memcheck_fedora44.valgrind.supp
diff --git a/.gitlab/ci/ctest_memcheck_fedora43_asan.lsan.supp b/.gitlab/ci/ctest_memcheck_fedora44_asan.lsan.supp similarity index 100% rename from .gitlab/ci/ctest_memcheck_fedora43_asan.lsan.supp rename to .gitlab/ci/ctest_memcheck_fedora44_asan.lsan.supp
diff --git a/.gitlab/ci/ctest_test.cmake b/.gitlab/ci/ctest_test.cmake index f6165b1..3d2c2c9 100644 --- a/.gitlab/ci/ctest_test.cmake +++ b/.gitlab/ci/ctest_test.cmake
@@ -20,10 +20,18 @@ set(CTEST_TEST_TIMEOUT "$ENV{CMAKE_CI_TEST_TIMEOUT}") endif () +set(test_load "${nproc}") +# Tart VMs start up with lots of macOS tasks running, so they end up stalling +# waiting for the load from them to fade away. Set the load to 0 on such +# runners because they are always configured to run a single job at a time. +if ("$ENV{CI_RUNNER_TAGS}" MATCHES "\"tart\"") + set(test_load "0") +endif () + include("${CMAKE_CURRENT_LIST_DIR}/ctest_exclusions.cmake") ctest_test( PARALLEL_LEVEL "${nproc}" - TEST_LOAD "${nproc}" + TEST_LOAD "${test_load}" OUTPUT_JUNIT "${CTEST_BINARY_DIRECTORY}/junit.xml" RETURN_VALUE test_result EXCLUDE "${test_exclusions}")
diff --git a/.gitlab/ci/docker/cuda13.2-x86_64/Dockerfile b/.gitlab/ci/docker/cuda13.2-x86_64/Dockerfile new file mode 100644 index 0000000..a910749 --- /dev/null +++ b/.gitlab/ci/docker/cuda13.2-x86_64/Dockerfile
@@ -0,0 +1,36 @@ +# syntax=docker/dockerfile:1 + +ARG BASE_IMAGE=kitware/nvidia-cuda:13.2.1-devel-ubuntu24.04 + +FROM ${BASE_IMAGE} AS apt-config +ADD https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/cross-linux-sbsa/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 + +COPY llvm.list /etc/apt/sources.list.d/llvm.list +COPY llvm-snapshot.gpg.key /root/llvm-snapshot.gpg.key +RUN apt-key add /root/llvm-snapshot.gpg.key + +FROM apt-config 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. +RUN --mount=type=tmpfs,target=/var/log \ + --mount=type=bind,source=docker-clean,target=/etc/apt/apt.conf.d/docker-clean \ + --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ + apt-get update \ + && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst) + +FROM apt-config +MAINTAINER Brad King <brad.king@kitware.com> + +RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \ + --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ + --mount=type=bind,source=dpkg-exclude,target=/etc/dpkg/dpkg.cfg.d/exclude \ + --mount=type=bind,source=docker-clean,target=/etc/apt/apt.conf.d/docker-clean \ + --mount=type=cache,from=apt-cache,source=/var/lib/apt/lists,target=/var/lib/apt/lists \ + --mount=type=cache,from=apt-cache,source=/var/cache/apt,target=/var/cache/apt,sharing=private \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/cuda13.2-x86_64/deps_packages.lst b/.gitlab/ci/docker/cuda13.2-x86_64/deps_packages.lst new file mode 100644 index 0000000..3d2e780 --- /dev/null +++ b/.gitlab/ci/docker/cuda13.2-x86_64/deps_packages.lst
@@ -0,0 +1,14 @@ +# Host tools +curl +git +unzip + +# Compilers +g++ +g++-aarch64-linux-gnu +clang-22 +libomp-22-dev + +# CUDA Extras +cuda-cross-sbsa-13-2 +cuda-opencl-dev-13-2
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/.gitlab/ci/docker/cuda13.2-x86_64/docker-clean similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to .gitlab/ci/docker/cuda13.2-x86_64/docker-clean
diff --git a/.gitlab/ci/docker/cuda13.2-x86_64/dpkg-exclude b/.gitlab/ci/docker/cuda13.2-x86_64/dpkg-exclude new file mode 100644 index 0000000..60b6565 --- /dev/null +++ b/.gitlab/ci/docker/cuda13.2-x86_64/dpkg-exclude
@@ -0,0 +1,21 @@ +# Drop all man pages +path-exclude=/usr/share/man/* + +# Drop all info pages +path-exclude=/usr/share/info/* + +# Drop all README files except from the some packages +path-exclude=/usr/**/*README* +path-include=/usr/share/devscripts/templates/README.mk-build-deps +path-include=/usr/share/equivs/template/debian/README.Debian.in + +# Drop all translations +path-exclude=/usr/share/locale/*/LC_MESSAGES/*.mo + +# Drop all documentation ... +path-exclude=/usr/share/doc/* +path-exclude=/usr/share/doc-base/* +path-exclude=/usr/share/gtk-doc/* + +# Per package excludes +path-exclude=/usr/share/gnupg/help.*.txt
diff --git a/.gitlab/ci/docker/cuda13.2-x86_64/install_deps.sh b/.gitlab/ci/docker/cuda13.2-x86_64/install_deps.sh new file mode 100755 index 0000000..d1c8aed --- /dev/null +++ b/.gitlab/ci/docker/cuda13.2-x86_64/install_deps.sh
@@ -0,0 +1,5 @@ +#!/bin/sh + +set -e + +apt-get install -y $(grep '^[^#]\+$' /root/deps_packages.lst)
diff --git a/.gitlab/ci/docker/cuda13.2-x86_64/llvm-snapshot.gpg.key b/.gitlab/ci/docker/cuda13.2-x86_64/llvm-snapshot.gpg.key new file mode 100644 index 0000000..aa6b105 --- /dev/null +++ b/.gitlab/ci/docker/cuda13.2-x86_64/llvm-snapshot.gpg.key
@@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.12 (GNU/Linux) + +mQINBFE9lCwBEADi0WUAApM/mgHJRU8lVkkw0CHsZNpqaQDNaHefD6Rw3S4LxNmM +EZaOTkhP200XZM8lVdbfUW9xSjA3oPldc1HG26NjbqqCmWpdo2fb+r7VmU2dq3NM +R18ZlKixiLDE6OUfaXWKamZsXb6ITTYmgTO6orQWYrnW6ckYHSeaAkW0wkDAryl2 +B5v8aoFnQ1rFiVEMo4NGzw4UX+MelF7rxaaregmKVTPiqCOSPJ1McC1dHFN533FY +Wh/RVLKWo6npu+owtwYFQW+zyQhKzSIMvNujFRzhIxzxR9Gn87MoLAyfgKEzrbbT +DhqqNXTxS4UMUKCQaO93TzetX/EBrRpJj+vP640yio80h4Dr5pAd7+LnKwgpTDk1 +G88bBXJAcPZnTSKu9I2c6KY4iRNbvRz4i+ZdwwZtdW4nSdl2792L7Sl7Nc44uLL/ +ZqkKDXEBF6lsX5XpABwyK89S/SbHOytXv9o4puv+65Ac5/UShspQTMSKGZgvDauU +cs8kE1U9dPOqVNCYq9Nfwinkf6RxV1k1+gwtclxQuY7UpKXP0hNAXjAiA5KS5Crq +7aaJg9q2F4bub0mNU6n7UI6vXguF2n4SEtzPRk6RP+4TiT3bZUsmr+1ktogyOJCc +Ha8G5VdL+NBIYQthOcieYCBnTeIH7D3Sp6FYQTYtVbKFzmMK+36ERreL/wARAQAB +tD1TeWx2ZXN0cmUgTGVkcnUgLSBEZWJpYW4gTExWTSBwYWNrYWdlcyA8c3lsdmVz +dHJlQGRlYmlhbi5vcmc+iQI4BBMBAgAiBQJRPZQsAhsDBgsJCAcDAgYVCAIJCgsE +FgIDAQIeAQIXgAAKCRAVz00Yr090Ibx+EADArS/hvkDF8juWMXxh17CgR0WZlHCC +9CTBWkg5a0bNN/3bb97cPQt/vIKWjQtkQpav6/5JTVCSx2riL4FHYhH0iuo4iAPR +udC7Cvg8g7bSPrKO6tenQZNvQm+tUmBHgFiMBJi92AjZ/Qn1Shg7p9ITivFxpLyX +wpmnF1OKyI2Kof2rm4BFwfSWuf8Fvh7kDMRLHv+MlnK/7j/BNpKdozXxLcwoFBmn +l0WjpAH3OFF7Pvm1LJdf1DjWKH0Dc3sc6zxtmBR/KHHg6kK4BGQNnFKujcP7TVdv +gMYv84kun14pnwjZcqOtN3UJtcx22880DOQzinoMs3Q4w4o05oIF+sSgHViFpc3W +R0v+RllnH05vKZo+LDzc83DQVrdwliV12eHxrMQ8UYg88zCbF/cHHnlzZWAJgftg +hB08v1BKPgYRUzwJ6VdVqXYcZWEaUJmQAPuAALyZESw94hSo28FAn0/gzEc5uOYx +K+xG/lFwgAGYNb3uGM5m0P6LVTfdg6vDwwOeTNIExVk3KVFXeSQef2ZMkhwA7wya +KJptkb62wBHFE+o9TUdtMCY6qONxMMdwioRE5BYNwAsS1PnRD2+jtlI0DzvKHt7B +MWd8hnoUKhMeZ9TNmo+8CpsAtXZcBho0zPGz/R8NlJhAWpdAZ1CmcPo83EW86Yq7 +BxQUKnNHcwj2ebkCDQRRPZQsARAA4jxYmbTHwmMjqSizlMJYNuGOpIidEdx9zQ5g +zOr431/VfWq4S+VhMDhs15j9lyml0y4ok215VRFwrAREDg6UPMr7ajLmBQGau0Fc +bvZJ90l4NjXp5p0NEE/qOb9UEHT7EGkEhaZ1ekkWFTWCgsy7rRXfZLxB6sk7pzLC +DshyW3zjIakWAnpQ5j5obiDy708pReAuGB94NSyb1HoW/xGsGgvvCw4r0w3xPStw +F1PhmScE6NTBIfLliea3pl8vhKPlCh54Hk7I8QGjo1ETlRP4Qll1ZxHJ8u25f/ta +RES2Aw8Hi7j0EVcZ6MT9JWTI83yUcnUlZPZS2HyeWcUj+8nUC8W4N8An+aNps9l/ +21inIl2TbGo3Yn1JQLnA1YCoGwC34g8QZTJhElEQBN0X29ayWW6OdFx8MDvllbBV +ymmKq2lK1U55mQTfDli7S3vfGz9Gp/oQwZ8bQpOeUkc5hbZszYwP4RX+68xDPfn+ +M9udl+qW9wu+LyePbW6HX90LmkhNkkY2ZzUPRPDHZANU5btaPXc2H7edX4y4maQa +xenqD0lGh9LGz/mps4HEZtCI5CY8o0uCMF3lT0XfXhuLksr7Pxv57yue8LLTItOJ +d9Hmzp9G97SRYYeqU+8lyNXtU2PdrLLq7QHkzrsloG78lCpQcalHGACJzrlUWVP/ +fN3Ht3kAEQEAAYkCHwQYAQIACQUCUT2ULAIbDAAKCRAVz00Yr090IbhWEADbr50X +OEXMIMGRLe+YMjeMX9NG4jxs0jZaWHc/WrGR+CCSUb9r6aPXeLo+45949uEfdSsB +pbaEdNWxF5Vr1CSjuO5siIlgDjmT655voXo67xVpEN4HhMrxugDJfCa6z97P0+ML +PdDxim57uNqkam9XIq9hKQaurxMAECDPmlEXI4QT3eu5qw5/knMzDMZj4Vi6hovL +wvvAeLHO/jsyfIdNmhBGU2RWCEZ9uo/MeerPHtRPfg74g+9PPfP6nyHD2Wes6yGd +oVQwtPNAQD6Cj7EaA2xdZYLJ7/jW6yiPu98FFWP74FN2dlyEA2uVziLsfBrgpS4l +tVOlrO2YzkkqUGrybzbLpj6eeHx+Cd7wcjI8CalsqtL6cG8cUEjtWQUHyTbQWAgG +5VPEgIAVhJ6RTZ26i/G+4J8neKyRs4vz+57UGwY6zI4AB1ZcWGEE3Bf+CDEDgmnP +LSwbnHefK9IljT9XU98PelSryUO/5UPw7leE0akXKB4DtekToO226px1VnGp3Bov +1GBGvpHvL2WizEwdk+nfk8LtrLzej+9FtIcq3uIrYnsac47Pf7p0otcFeTJTjSq3 +krCaoG4Hx0zGQG2ZFpHrSrZTVy6lxvIdfi0beMgY6h78p6M9eYZHQHc02DjFkQXN +bXb5c6gCHESH5PXwPU4jQEE7Ib9J6sbk7ZT2Mw== +=j+4q +-----END PGP PUBLIC KEY BLOCK-----
diff --git a/.gitlab/ci/docker/cuda13.2-x86_64/llvm.list b/.gitlab/ci/docker/cuda13.2-x86_64/llvm.list new file mode 100644 index 0000000..2e783da --- /dev/null +++ b/.gitlab/ci/docker/cuda13.2-x86_64/llvm.list
@@ -0,0 +1,2 @@ +deb http://apt.llvm.org/noble/ llvm-toolchain-noble-22 main +deb-src http://apt.llvm.org/noble/ llvm-toolchain-noble-22 main
diff --git a/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh b/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh index 1ad42c4..3e76005 100755 --- a/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh +++ b/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh
@@ -8,7 +8,10 @@ --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \ 7D2BAF1CF37B13E2069D6956105BD0E739499BDB -curl -sSL https://get.rvm.io | bash -s stable +curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer -o rvm-installer && +curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer.asc -o rvm-installer.asc && +gpg2 --verify rvm-installer.asc rvm-installer && +bash rvm-installer stable # keep version in sync with `env_debian*_ninja.sh` /usr/local/rvm/bin/rvm install ruby-3.2.2
diff --git a/.gitlab/ci/docker/fedora43-hip/Dockerfile b/.gitlab/ci/docker/fedora43-hip/Dockerfile deleted file mode 100644 index 0f0cbed..0000000 --- a/.gitlab/ci/docker/fedora43-hip/Dockerfile +++ /dev/null
@@ -1,12 +0,0 @@ -# syntax=docker/dockerfile:1 - -ARG BASE_IMAGE=fedora:43 - -FROM ${BASE_IMAGE} AS dnf-cache -LABEL maintainer="Brad King <brad.king@kitware.com>" -RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ - --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ - --mount=type=cache,target=/var/lib/dnf \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - dnf install -y $(grep -h '^[^#]\+$' /root/*.lst)
diff --git a/.gitlab/ci/docker/fedora43-hip/deps_packages.lst b/.gitlab/ci/docker/fedora43-hip/deps_packages.lst deleted file mode 100644 index abae13a..0000000 --- a/.gitlab/ci/docker/fedora43-hip/deps_packages.lst +++ /dev/null
@@ -1,16 +0,0 @@ -# Install development tools. -clang -clang-tools-extra -compiler-rt -gcc-c++ -git-core -make - -# Install HIP language toolchain. -hsakmt-devel -lld -llvm -rocm-comgr-devel -rocm-hip-devel -rocm-runtime-devel -rocminfo
diff --git a/.gitlab/ci/docker/fedora43/Dockerfile b/.gitlab/ci/docker/fedora43/Dockerfile deleted file mode 100644 index 86ee3b8..0000000 --- a/.gitlab/ci/docker/fedora43/Dockerfile +++ /dev/null
@@ -1,135 +0,0 @@ -# syntax=docker/dockerfile:1 - -ARG BASE_IMAGE=fedora:43 - -FROM ${BASE_IMAGE} AS dnf-cache -# Populate DNF cache w/ the fresh metadata and prefetch packages. -RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ - --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ - --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.lst \ - --mount=type=bind,source=rbenv_packages.lst,target=/root/rbenv_packages.lst \ - --mount=type=bind,source=rust_packages.lst,target=/root/rust_packages.lst \ - --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.lst \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - dnf install --downloadonly -y $(grep -h '^[^#]\+$' /root/*.lst) - - -FROM ${BASE_IMAGE} AS rust-build-env -LABEL maintainer="Brad King <brad.king@kitware.com>" -# Pre-install prerequisites to build Rust projects. -RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ - --mount=type=bind,source=rust_packages.lst,target=/root/rust_packages.lst \ - --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - dnf install -y $(grep '^[^#]\+$' /root/rust_packages.lst) - - -FROM rust-build-env AS rust-build -LABEL maintainer="Brad King <brad.king@kitware.com>" -# Build the needed Rust packages. -# https://doc.rust-lang.org/cargo/guide/cargo-home.html?highlight=.cargo#caching-the-cargo-home-in-ci -RUN --mount=type=bind,source=build_rust.sh,target=/root/build_rust.sh \ - --mount=type=cache,target=/root/.cargo/registry/index \ - --mount=type=cache,target=/root/.cargo/registry/cache \ - --mount=type=tmpfs,target=/tmp \ - sh /root/build_rust.sh - - -FROM ${BASE_IMAGE} AS rvm-build-env -LABEL maintainer="Brad King <brad.king@kitware.com>" -# Pre-install prerequisites for RVM. -RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ - --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.lst \ - --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - dnf install -y $(grep '^[^#]\+$' /root/rvm_packages.lst) - - -FROM rvm-build-env AS rvm-build -LABEL maintainer="Brad King <brad.king@kitware.com>" -# Build Ruby with RVM. -RUN --mount=type=bind,source=build_rvm.sh,target=/root/build_rvm.sh \ - --mount=type=cache,target=/usr/local/rvm/archives \ - --mount=type=cache,target=/usr/local/rvm/gem-cache \ - --mount=type=cache,target=/usr/local/rvm/src \ - --mount=type=tmpfs,target=/tmp \ - sh /root/build_rvm.sh - - -FROM ${BASE_IMAGE} AS rbenv -# Pre-install prerequisites for `rbenv`. -RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ - --mount=type=bind,source=rbenv_packages.lst,target=/root/rbenv_packages.lst \ - --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - dnf install -y $(grep '^[^#]\+$' /root/rbenv_packages.lst) - - -FROM rbenv AS rbenv-build -ENV RBENV_ROOT=/opt/rbenv -ENV RBENV_BUILD_ROOT=/root/.cache/rbenv-build -ENV RUBY_BUILD_CACHE_PATH=/root/.cache/rbenv -# Build Ruby with `rbenv`. -RUN --mount=type=cache,target=/root/.cache \ - --mount=type=bind,source=build_rbenv.sh,target=/root/build_rbenv.sh \ - --mount=type=tmpfs,target=/tmp \ - sh /root/build_rbenv.sh - - -FROM ${BASE_IMAGE} AS iwyu-build-env -LABEL maintainer="Kyle Edwards <kyle.edwards@kitware.com>" -# Pre-install prerequisites to build IWYU. -RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ - --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.lst \ - --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - dnf install -y $(grep '^[^#]\+$' /root/iwyu_packages.lst) - - -FROM iwyu-build-env AS iwyu-build -LABEL maintainer="Kyle Edwards <kyle.edwards@kitware.com>" -# Build IWYU. -RUN --mount=type=bind,source=build_iwyu.sh,target=/root/build_iwyu.sh \ - --mount=type=cache,target=/root/include-what-you-use \ - --mount=type=tmpfs,target=/tmp \ - sh /root/build_iwyu.sh - - -FROM ${BASE_IMAGE} AS p4-dl -# Download Perforce. -# NOTE `curl` is pre-installed in the base image. -RUN curl -C- -L https://www.perforce.com/downloads/perforce/r21.2/bin.linux26x86_64/helix-core-server.tgz \ - | tar -C /usr/local/bin -xvzf - -- p4 p4d - - -FROM ${BASE_IMAGE} -LABEL maintainer="Brad King <brad.king@kitware.com>" - -ENV RBENV_ROOT=/opt/rbenv - -COPY --from=p4-dl --chown=root:root /usr/local/bin/p4 /usr/local/bin/p4d /usr/local/bin - -RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ - --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \ - --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ - --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - sh /root/install_deps.sh - -RUN --mount=type=bind,from=iwyu-build,source=/root,target=/root \ - tar -C / -xf /root/iwyu.tar - -RUN --mount=type=bind,from=rust-build,source=/root,target=/root \ - tar -C /usr/local -xf /root/rust.tar - -RUN --mount=type=bind,from=rvm-build,source=/root,target=/root \ - tar -C /usr/local -xf /root/rvm.tar - -RUN --mount=type=bind,from=rbenv-build,source=/root,target=/root \ - tar -C / -xf /root/rbenv.tar && rbenv global 3.4.3
diff --git a/.gitlab/ci/docker/fedora43/build_rbenv.sh b/.gitlab/ci/docker/fedora43/build_rbenv.sh deleted file mode 100755 index feb574f..0000000 --- a/.gitlab/ci/docker/fedora43/build_rbenv.sh +++ /dev/null
@@ -1,28 +0,0 @@ -#!/bin/sh - -set -e - -echo "gem: --no-document" > ~/.gemrc - -# Ruby rbenv -export RUBY_CONFIGURE_OPTS=--disable-install-doc -export RUBY_BUILD_CURL_OPTS=-C- -rbenv install 3.4.3 -k -s -v - -cat <<EOF >/tmp/exclude.lst -*LICENSE* -*/doc/* -*/man/* -*.md -BSDL -CONTRIBUTING.* -COPYING -LEGAL -PSFL -README.rdoc -History.rdoc -gem_make.out -test-unit-*/test -rss-*/test -EOF -tar -cf /root/rbenv.tar --exclude-from=/tmp/exclude.lst ${RBENV_ROOT}
diff --git a/.gitlab/ci/docker/fedora43/build_rust.sh b/.gitlab/ci/docker/fedora43/build_rust.sh deleted file mode 100755 index 5ab91f7..0000000 --- a/.gitlab/ci/docker/fedora43/build_rust.sh +++ /dev/null
@@ -1,10 +0,0 @@ -#!/bin/sh - -set -e - -typos_version=1.44.0 -cargo install --root /usr/local --version "$typos_version" typos-cli - -strip /usr/local/bin/typos - -tar -C /usr/local -cf /root/rust.tar bin/typos
diff --git a/.gitlab/ci/docker/fedora43/build_rvm.sh b/.gitlab/ci/docker/fedora43/build_rvm.sh deleted file mode 100755 index 22c4333..0000000 --- a/.gitlab/ci/docker/fedora43/build_rvm.sh +++ /dev/null
@@ -1,41 +0,0 @@ -#!/bin/sh - -set -e - -echo "gem: --no-document" > ~/.gemrc - -gpg2 --keyserver hkps://keyserver.ubuntu.com \ - --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \ - 7D2BAF1CF37B13E2069D6956105BD0E739499BDB - -curl -sSL https://get.rvm.io | bash -s stable --ignore-dotfiles - -export rvm_silence_banner=1 - -# keep version in sync with `env_fedora*_makefiles.cmake` -/usr/local/rvm/bin/rvm install ruby-3.3.8 --no-docs --disable-binary - -for p in archives docs examples gem-cache log src; do - touch /usr/local/rvm/${p}/.tar_exclude -done - -cat <<EOF >/tmp/exclude.lst -*LICENSE* -*/doc/* -*/man/* -*.md -BSDL -CONTRIBUTING.* -COPYING -LEGAL -PSFL -README.rdoc -History.rdoc -gem_make.out -test-unit-*/test -rss-*/test -EOF -tar -C /usr/local \ - --exclude-tag-under=.tar_exclude \ - --exclude-from=/tmp/exclude.lst \ - -cf /root/rvm.tar rvm
diff --git a/.gitlab/ci/docker/fedora43/deps_packages.lst b/.gitlab/ci/docker/fedora43/deps_packages.lst deleted file mode 100644 index 143c296..0000000 --- a/.gitlab/ci/docker/fedora43/deps_packages.lst +++ /dev/null
@@ -1,148 +0,0 @@ -glibc-locale-source - -# Install build requirements. -ncurses-devel -openssl-devel -qt5-qtbase-devel -qt6-qtbase-devel - -# Install development tools. -clang -clang-devel -clang-tools-extra -clang-tools-extra-devel -compiler-rt -flang -gcc-c++ -git-core -lfortran -llvm-devel -make -rust - -# Install optional external build dependencies. -bzip2-devel -expat-devel -jsoncpp-devel -libarchive-devel -libcurl-devel -libuv-devel -libzstd-devel -rhash-devel -xz-devel -zlib-devel - -# Install documentation tools. -python3-sphinx -python3-sphinxcontrib-qthelp -qt5-qttools-devel -qt6-qttools-devel -texinfo - -# Install lint tools. -clang-analyzer -clazy -codespell -valgrind - -# Tools needed for the test suite. -file -findutils -jq -which - -# Install ASM_NASM language toolchain. -nasm - -# Packages needed to test CTest. -breezy -mercurial -subversion - -# Packages needed to test CPack. -appstream -patchelf -rpm-build - -# Packages needed to test find modules. -alsa-lib-devel -aspell -aspell-devel -aspell-en -blas-devel -boost-devel -boost-python3-devel -bzip2-devel -cups-devel -DevIL-devel -doxygen -expat-devel -fontconfig-devel -freeglut-devel -freetype-devel -gdal-devel -gettext -giflib-devel -glew-devel -gmock -gnutls-devel -grpc-devel -grpc-plugins -gsl-devel -gtest-devel -gtk2-devel -hdf5-devel -hdf5-mpich-devel -hdf5-openmpi-devel -ImageMagick-c++-devel -jasper-devel -java-21-openjdk-devel -jsoncpp-devel -lapack-devel -libarchive-devel -libcurl-devel -libicu-devel -libinput-devel -libjpeg-turbo-devel -libomp-devel -libpng-devel -libtiff-devel -libuv-devel -libxml2-devel -libxslt-devel -mpich-devel -openal-soft-devel -openmpi-devel -opensp-devel -patch -perl -postgresql-server-devel -protobuf-c-devel -protobuf-devel -protobuf-lite-devel -pypy2 -pypy2-devel -pypy3 -pypy3-devel -python3 -python3-devel -python3-jsmin -python3-jsonschema -python3-numpy -rbenv -ruby -ruby-build-rbenv -ruby-devel -rubygems -SDL-devel -sqlite-devel -swig -systemd-devel -unixODBC-devel -wxGTK-devel -xalan-c-devel -xerces-c-devel -xz-devel - -# Packages needed to test third-party binaries. -ncurses-compat-libs
diff --git a/.gitlab/ci/docker/fedora43/rbenv_packages.lst b/.gitlab/ci/docker/fedora43/rbenv_packages.lst deleted file mode 100644 index d16afff..0000000 --- a/.gitlab/ci/docker/fedora43/rbenv_packages.lst +++ /dev/null
@@ -1,18 +0,0 @@ -rbenv -ruby-build-rbenv - -# Packages needed for `rbenv` -# https://github.com/rbenv/ruby-build/wiki#fedora -autoconf -bzip2 -gcc -gdbm-devel -libffi-devel -libyaml-devel -make -ncurses-devel -openssl-devel -patch -readline-devel -rust -zlib-devel
diff --git a/.gitlab/ci/docker/fedora44-hip/Dockerfile b/.gitlab/ci/docker/fedora44-hip/Dockerfile new file mode 100644 index 0000000..463d19f --- /dev/null +++ b/.gitlab/ci/docker/fedora44-hip/Dockerfile
@@ -0,0 +1,12 @@ +# syntax=docker/dockerfile:1 + +ARG BASE_IMAGE=fedora:44 + +FROM ${BASE_IMAGE} AS dnf-cache +LABEL maintainer="Brad King <brad.king@kitware.com>" +RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ + --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ + --mount=type=cache,target=/var/lib/dnf \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + dnf install -y $(grep -h '^[^#]\+$' /root/*.lst)
diff --git a/.gitlab/ci/docker/fedora44-hip/deps_packages.lst b/.gitlab/ci/docker/fedora44-hip/deps_packages.lst new file mode 100644 index 0000000..bac9e53 --- /dev/null +++ b/.gitlab/ci/docker/fedora44-hip/deps_packages.lst
@@ -0,0 +1,15 @@ +# Install development tools. +clang +clang-tools-extra +compiler-rt +gcc-c++ +git-core +make + +# Install HIP language toolchain. +lld +llvm +rocm-comgr-devel +rocm-hip-devel +rocm-runtime-devel +rocminfo
diff --git a/.gitlab/ci/docker/fedora43-hip/dnf.conf b/.gitlab/ci/docker/fedora44-hip/dnf.conf similarity index 100% rename from .gitlab/ci/docker/fedora43-hip/dnf.conf rename to .gitlab/ci/docker/fedora44-hip/dnf.conf
diff --git a/.gitlab/ci/docker/fedora44/Dockerfile b/.gitlab/ci/docker/fedora44/Dockerfile new file mode 100644 index 0000000..d36ccc9 --- /dev/null +++ b/.gitlab/ci/docker/fedora44/Dockerfile
@@ -0,0 +1,135 @@ +# syntax=docker/dockerfile:1 + +ARG BASE_IMAGE=fedora:44 + +FROM ${BASE_IMAGE} AS dnf-cache +# Populate DNF cache w/ the fresh metadata and prefetch packages. +RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ + --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ + --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.lst \ + --mount=type=bind,source=rbenv_packages.lst,target=/root/rbenv_packages.lst \ + --mount=type=bind,source=rust_packages.lst,target=/root/rust_packages.lst \ + --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.lst \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + dnf install --downloadonly -y $(grep -h '^[^#]\+$' /root/*.lst) + + +FROM ${BASE_IMAGE} AS rust-build-env +LABEL maintainer="Brad King <brad.king@kitware.com>" +# Pre-install prerequisites to build Rust projects. +RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ + --mount=type=bind,source=rust_packages.lst,target=/root/rust_packages.lst \ + --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + dnf install -y $(grep '^[^#]\+$' /root/rust_packages.lst) + + +FROM rust-build-env AS rust-build +LABEL maintainer="Brad King <brad.king@kitware.com>" +# Build the needed Rust packages. +# https://doc.rust-lang.org/cargo/guide/cargo-home.html?highlight=.cargo#caching-the-cargo-home-in-ci +RUN --mount=type=bind,source=build_rust.sh,target=/root/build_rust.sh \ + --mount=type=cache,target=/root/.cargo/registry/index \ + --mount=type=cache,target=/root/.cargo/registry/cache \ + --mount=type=tmpfs,target=/tmp \ + sh /root/build_rust.sh + + +FROM ${BASE_IMAGE} AS rvm-build-env +LABEL maintainer="Brad King <brad.king@kitware.com>" +# Pre-install prerequisites for RVM. +RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ + --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.lst \ + --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + dnf install -y $(grep '^[^#]\+$' /root/rvm_packages.lst) + + +FROM rvm-build-env AS rvm-build +LABEL maintainer="Brad King <brad.king@kitware.com>" +# Build Ruby with RVM. +RUN --mount=type=bind,source=build_rvm.sh,target=/root/build_rvm.sh \ + --mount=type=cache,target=/usr/local/rvm/archives \ + --mount=type=cache,target=/usr/local/rvm/gem-cache \ + --mount=type=cache,target=/usr/local/rvm/src \ + --mount=type=tmpfs,target=/tmp \ + sh /root/build_rvm.sh + + +FROM ${BASE_IMAGE} AS rbenv +# Pre-install prerequisites for `rbenv`. +RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ + --mount=type=bind,source=rbenv_packages.lst,target=/root/rbenv_packages.lst \ + --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + dnf install -y $(grep '^[^#]\+$' /root/rbenv_packages.lst) + + +FROM rbenv AS rbenv-build +ENV RBENV_ROOT=/opt/rbenv +ENV RBENV_BUILD_ROOT=/root/.cache/rbenv-build +ENV RUBY_BUILD_CACHE_PATH=/root/.cache/rbenv +# Build Ruby with `rbenv`. +RUN --mount=type=cache,target=/root/.cache \ + --mount=type=bind,source=build_rbenv.sh,target=/root/build_rbenv.sh \ + --mount=type=tmpfs,target=/tmp \ + sh /root/build_rbenv.sh + + +FROM ${BASE_IMAGE} AS iwyu-build-env +LABEL maintainer="Kyle Edwards <kyle.edwards@kitware.com>" +# Pre-install prerequisites to build IWYU. +RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ + --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.lst \ + --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + dnf install -y $(grep '^[^#]\+$' /root/iwyu_packages.lst) + + +FROM iwyu-build-env AS iwyu-build +LABEL maintainer="Kyle Edwards <kyle.edwards@kitware.com>" +# Build IWYU. +RUN --mount=type=bind,source=build_iwyu.sh,target=/root/build_iwyu.sh \ + --mount=type=cache,target=/root/include-what-you-use \ + --mount=type=tmpfs,target=/tmp \ + sh /root/build_iwyu.sh + + +FROM ${BASE_IMAGE} AS p4-dl +# Download Perforce. +# NOTE `curl` is pre-installed in the base image. +RUN curl -C- -L https://www.perforce.com/downloads/perforce/r21.2/bin.linux26x86_64/helix-core-server.tgz \ + | tar -C /usr/local/bin -xvzf - -- p4 p4d + + +FROM ${BASE_IMAGE} +LABEL maintainer="Brad King <brad.king@kitware.com>" + +ENV RBENV_ROOT=/opt/rbenv + +COPY --from=p4-dl --chown=root:root /usr/local/bin/p4 /usr/local/bin/p4d /usr/local/bin + +RUN --mount=type=bind,source=dnf.conf,target=/etc/dnf/dnf.conf \ + --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \ + --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ + --mount=type=cache,from=dnf-cache,source=/var/cache/libdnf5,target=/var/cache/libdnf5,sharing=private \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + sh /root/install_deps.sh + +RUN --mount=type=bind,from=iwyu-build,source=/root,target=/root \ + tar -C / -xf /root/iwyu.tar + +RUN --mount=type=bind,from=rust-build,source=/root,target=/root \ + tar -C /usr/local -xf /root/rust.tar + +RUN --mount=type=bind,from=rvm-build,source=/root,target=/root \ + tar -C /usr/local -xf /root/rvm.tar + +RUN --mount=type=bind,from=rbenv-build,source=/root,target=/root \ + tar -C / -xf /root/rbenv.tar && rbenv global 3.4.9
diff --git a/.gitlab/ci/docker/fedora43/build_iwyu.sh b/.gitlab/ci/docker/fedora44/build_iwyu.sh similarity index 100% rename from .gitlab/ci/docker/fedora43/build_iwyu.sh rename to .gitlab/ci/docker/fedora44/build_iwyu.sh
diff --git a/.gitlab/ci/docker/fedora44/build_rbenv.sh b/.gitlab/ci/docker/fedora44/build_rbenv.sh new file mode 100755 index 0000000..f5ddd06 --- /dev/null +++ b/.gitlab/ci/docker/fedora44/build_rbenv.sh
@@ -0,0 +1,28 @@ +#!/bin/sh + +set -e + +echo "gem: --no-document" > ~/.gemrc + +# Ruby rbenv +export RUBY_CONFIGURE_OPTS=--disable-install-doc +export RUBY_BUILD_CURL_OPTS=-C- +rbenv install 3.4.9 -k -s -v + +cat <<EOF >/tmp/exclude.lst +*LICENSE* +*/doc/* +*/man/* +*.md +BSDL +CONTRIBUTING.* +COPYING +LEGAL +PSFL +README.rdoc +History.rdoc +gem_make.out +test-unit-*/test +rss-*/test +EOF +tar -cf /root/rbenv.tar --exclude-from=/tmp/exclude.lst ${RBENV_ROOT}
diff --git a/.gitlab/ci/docker/fedora44/build_rust.sh b/.gitlab/ci/docker/fedora44/build_rust.sh new file mode 100755 index 0000000..1ee87eb --- /dev/null +++ b/.gitlab/ci/docker/fedora44/build_rust.sh
@@ -0,0 +1,10 @@ +#!/bin/sh + +set -e + +typos_version=1.45.2 +cargo install --root /usr/local --version "$typos_version" typos-cli + +strip /usr/local/bin/typos + +tar -C /usr/local -cf /root/rust.tar bin/typos
diff --git a/.gitlab/ci/docker/fedora44/build_rvm.sh b/.gitlab/ci/docker/fedora44/build_rvm.sh new file mode 100755 index 0000000..9da34fe --- /dev/null +++ b/.gitlab/ci/docker/fedora44/build_rvm.sh
@@ -0,0 +1,44 @@ +#!/bin/sh + +set -e + +echo "gem: --no-document" > ~/.gemrc + +gpg2 --keyserver hkps://keyserver.ubuntu.com \ + --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \ + 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + +curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer -o rvm-installer && +curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer.asc -o rvm-installer.asc && +gpg2 --verify rvm-installer.asc rvm-installer && +bash rvm-installer stable --ignore-dotfiles + +export rvm_silence_banner=1 + +# keep version in sync with `env_fedora*_makefiles.cmake` +/usr/local/rvm/bin/rvm install ruby-3.3.8 --no-docs --disable-binary + +for p in archives docs examples gem-cache log src; do + touch /usr/local/rvm/${p}/.tar_exclude +done + +cat <<EOF >/tmp/exclude.lst +*LICENSE* +*/doc/* +*/man/* +*.md +BSDL +CONTRIBUTING.* +COPYING +LEGAL +PSFL +README.rdoc +History.rdoc +gem_make.out +test-unit-*/test +rss-*/test +EOF +tar -C /usr/local \ + --exclude-tag-under=.tar_exclude \ + --exclude-from=/tmp/exclude.lst \ + -cf /root/rvm.tar rvm
diff --git a/.gitlab/ci/docker/fedora44/deps_packages.lst b/.gitlab/ci/docker/fedora44/deps_packages.lst new file mode 100644 index 0000000..b763bb7 --- /dev/null +++ b/.gitlab/ci/docker/fedora44/deps_packages.lst
@@ -0,0 +1,148 @@ +glibc-locale-source + +# Install build requirements. +ncurses-devel +openssl-devel +qt5-qtbase-devel +qt6-qtbase-devel + +# Install development tools. +clang +clang-devel +clang-tools-extra +clang-tools-extra-devel +compiler-rt +flang +gcc-c++ +git-core +lfortran +llvm-devel +make +rust + +# Install optional external build dependencies. +bzip2-devel +expat-devel +jsoncpp-devel +libarchive-devel +libcurl-devel +libuv-devel +libzstd-devel +rhash-devel +xz-devel +zlib-devel + +# Install documentation tools. +python3-sphinx +python3-sphinxcontrib-qthelp +qt5-qttools-devel +qt6-qttools-devel +texinfo + +# Install lint tools. +clang-analyzer +clazy +codespell +valgrind + +# Tools needed for the test suite. +file +findutils +jq +which + +# Install ASM_NASM language toolchain. +nasm + +# Packages needed to test CTest. +breezy +mercurial +subversion + +# Packages needed to test CPack. +appstream +patchelf +rpm-build + +# Packages needed to test find modules. +alsa-lib-devel +aspell +aspell-devel +aspell-en +blas-devel +boost-devel +boost-python3-devel +bzip2-devel +cups-devel +DevIL-devel +doxygen +expat-devel +fontconfig-devel +freeglut-devel +freetype-devel +gdal-devel +gettext +giflib-devel +glew-devel +gmock +gnutls-devel +grpc-devel +grpc-plugins +gsl-devel +gtest-devel +gtk2-devel +hdf5-devel +hdf5-mpich-devel +hdf5-openmpi-devel +ImageMagick-c++-devel +jasper-devel +java-25-openjdk-devel +jsoncpp-devel +lapack-devel +libarchive-devel +libcurl-devel +libicu-devel +libinput-devel +libjpeg-turbo-devel +libomp-devel +libpng-devel +libtiff-devel +libuv-devel +libxml2-devel +libxslt-devel +mpich-devel +openal-soft-devel +openmpi-devel +opensp-devel +patch +perl +postgresql-server-devel +protobuf-c-devel +protobuf-devel +protobuf-lite-devel +pypy2 +pypy2-devel +pypy3 +pypy3-devel +python3 +python3-devel +python3-jsmin +python3-jsonschema +python3-numpy +rbenv +ruby +ruby-build-rbenv +ruby-devel +rubygems +SDL-devel +sqlite-devel +swig +systemd-devel +unixODBC-devel +wxGTK-devel +xalan-c-devel +xerces-c-devel +xz-devel + +# Packages needed to test third-party binaries. +ncurses-compat-libs
diff --git a/.gitlab/ci/docker/fedora43/dnf.conf b/.gitlab/ci/docker/fedora44/dnf.conf similarity index 100% rename from .gitlab/ci/docker/fedora43/dnf.conf rename to .gitlab/ci/docker/fedora44/dnf.conf
diff --git a/.gitlab/ci/docker/fedora43/install_deps.sh b/.gitlab/ci/docker/fedora44/install_deps.sh similarity index 100% rename from .gitlab/ci/docker/fedora43/install_deps.sh rename to .gitlab/ci/docker/fedora44/install_deps.sh
diff --git a/.gitlab/ci/docker/fedora43/iwyu_packages.lst b/.gitlab/ci/docker/fedora44/iwyu_packages.lst similarity index 100% rename from .gitlab/ci/docker/fedora43/iwyu_packages.lst rename to .gitlab/ci/docker/fedora44/iwyu_packages.lst
diff --git a/.gitlab/ci/docker/fedora44/rbenv_packages.lst b/.gitlab/ci/docker/fedora44/rbenv_packages.lst new file mode 100644 index 0000000..7cba990 --- /dev/null +++ b/.gitlab/ci/docker/fedora44/rbenv_packages.lst
@@ -0,0 +1,19 @@ +rbenv +ruby-build-rbenv +ruby-build-ruby + +# Packages needed for `rbenv` +# https://github.com/rbenv/ruby-build/wiki#fedora +autoconf +bzip2 +gcc +gdbm-devel +libffi-devel +libyaml-devel +make +ncurses-devel +openssl-devel +patch +readline-devel +rust +zlib-devel
diff --git a/.gitlab/ci/docker/fedora43/rust_packages.lst b/.gitlab/ci/docker/fedora44/rust_packages.lst similarity index 100% rename from .gitlab/ci/docker/fedora43/rust_packages.lst rename to .gitlab/ci/docker/fedora44/rust_packages.lst
diff --git a/.gitlab/ci/docker/fedora43/rvm_packages.lst b/.gitlab/ci/docker/fedora44/rvm_packages.lst similarity index 100% rename from .gitlab/ci/docker/fedora43/rvm_packages.lst rename to .gitlab/ci/docker/fedora44/rvm_packages.lst
diff --git a/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile b/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile deleted file mode 100644 index 7f6e297..0000000 --- a/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile +++ /dev/null
@@ -1,9 +0,0 @@ -FROM fedora:41 -MAINTAINER Ben Boeckel <ben.boeckel@kitware.com> - -# Install build dependencies for packages. -COPY install_deps.sh /root/install_deps.sh -RUN sh /root/install_deps.sh - -COPY install_gcc.sh /root/install_gcc.sh -RUN sh /root/install_gcc.sh
diff --git a/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh b/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh deleted file mode 100755 index b8b706b..0000000 --- a/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh +++ /dev/null
@@ -1,7 +0,0 @@ -#!/bin/sh - -set -e - -dnf install -y --setopt=install_weak_deps=False \ - gcc-c++ mpfr-devel libmpc-devel isl-devel flex bison file findutils diffutils git-core -dnf clean all
diff --git a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh deleted file mode 100755 index 32cb9ec..0000000 --- a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh +++ /dev/null
@@ -1,25 +0,0 @@ -#!/bin/sh - -set -e - -readonly revision="10e702789eeabcc88451e34c2a5c7dccb96190a5" # master as of 21 Nov 2024 -readonly tarball="git://gcc.gnu.org/git/gcc.git" - -readonly workdir="$HOME/gcc" -readonly srcdir="$workdir/gcc" -readonly builddir="$workdir/build" -readonly njobs="$( nproc )" - -mkdir -p "$workdir" -cd "$workdir" -git clone "$tarball" "$srcdir" -git -C "$srcdir" checkout "$revision" -mkdir -p "$builddir" -cd "$builddir" -"$srcdir/configure" \ - --disable-multilib \ - --enable-languages=c,c++ \ - --prefix="/opt/gcc-importstd" -make "-j$njobs" -make "-j$njobs" install-strip -rm -rf "$workdir"
diff --git a/.gitlab/ci/docker/nvhpc24.9/Dockerfile b/.gitlab/ci/docker/nvhpc24.9/Dockerfile deleted file mode 100644 index b94b14a..0000000 --- a/.gitlab/ci/docker/nvhpc24.9/Dockerfile +++ /dev/null
@@ -1,6 +0,0 @@ -# https://catalog.ngc.nvidia.com/orgs/nvidia/containers/nvhpc/tags -FROM kitware/nvidia-nvhpc:24.9-devel-cuda_multi-ubuntu24.04 -MAINTAINER Brad King <brad.king@kitware.com> - -COPY install_deps.sh /root/install_deps.sh -RUN sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/nvhpc26.3/Dockerfile b/.gitlab/ci/docker/nvhpc26.3/Dockerfile new file mode 100644 index 0000000..14b9bca --- /dev/null +++ b/.gitlab/ci/docker/nvhpc26.3/Dockerfile
@@ -0,0 +1,6 @@ +# https://catalog.ngc.nvidia.com/orgs/nvidia/containers/nvhpc/tags +FROM kitware/nvidia-nvhpc:26.3-devel-cuda_multi-ubuntu24.04 +MAINTAINER Brad King <brad.king@kitware.com> + +COPY install_deps.sh /root/install_deps.sh +RUN sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/nvhpc24.9/install_deps.sh b/.gitlab/ci/docker/nvhpc26.3/install_deps.sh similarity index 100% rename from .gitlab/ci/docker/nvhpc24.9/install_deps.sh rename to .gitlab/ci/docker/nvhpc26.3/install_deps.sh
diff --git a/.gitlab/ci/env_fedora43_common_clang.sh b/.gitlab/ci/env_fedora43_common_clang.sh deleted file mode 100644 index 55533e7..0000000 --- a/.gitlab/ci/env_fedora43_common_clang.sh +++ /dev/null
@@ -1,3 +0,0 @@ -export CC=/usr/bin/clang-21 -export CXX=/usr/bin/clang++-21 -export FC=/usr/bin/flang-21
diff --git a/.gitlab/ci/env_fedora43_hip_radeon.sh b/.gitlab/ci/env_fedora43_hip_radeon.sh deleted file mode 100644 index 2c942a8..0000000 --- a/.gitlab/ci/env_fedora43_hip_radeon.sh +++ /dev/null
@@ -1,2 +0,0 @@ -export HIPCXX=/usr/bin/clang++-21 -export HIPFLAGS='--rocm-path=/usr --rocm-device-lib-path=/usr/lib64/rocm/llvm/lib/clang/19/amdgcn/bitcode'
diff --git a/.gitlab/ci/env_fedora43_makefiles_clang.sh b/.gitlab/ci/env_fedora43_makefiles_clang.sh deleted file mode 100644 index 3953f88..0000000 --- a/.gitlab/ci/env_fedora43_makefiles_clang.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora43_common_clang.sh
diff --git a/.gitlab/ci/env_fedora43_makefiles_lfortran.sh b/.gitlab/ci/env_fedora43_makefiles_lfortran.sh deleted file mode 100644 index 6b02339..0000000 --- a/.gitlab/ci/env_fedora43_makefiles_lfortran.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora43_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora43_makefiles_symlinked.cmake b/.gitlab/ci/env_fedora43_makefiles_symlinked.cmake deleted file mode 100644 index fb004e8..0000000 --- a/.gitlab/ci/env_fedora43_makefiles_symlinked.cmake +++ /dev/null
@@ -1 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/env_fedora43_makefiles.cmake")
diff --git a/.gitlab/ci/env_fedora43_makefiles_symlinked.sh b/.gitlab/ci/env_fedora43_makefiles_symlinked.sh deleted file mode 100644 index 448b592..0000000 --- a/.gitlab/ci/env_fedora43_makefiles_symlinked.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora43_makefiles.sh
diff --git a/.gitlab/ci/env_fedora43_ninja.sh b/.gitlab/ci/env_fedora43_ninja.sh deleted file mode 100644 index 0ef4bce..0000000 --- a/.gitlab/ci/env_fedora43_ninja.sh +++ /dev/null
@@ -1,7 +0,0 @@ -if test "$CMAKE_CI_NIGHTLY" = "true"; then - source .gitlab/ci/appimagetool-env.sh - source .gitlab/ci/ispc-env.sh -fi - -# Test in a UTF-8 locale. -export LANG=en_US.UTF-8
diff --git a/.gitlab/ci/env_fedora43_ninja_clang.sh b/.gitlab/ci/env_fedora43_ninja_clang.sh deleted file mode 100644 index 3953f88..0000000 --- a/.gitlab/ci/env_fedora43_ninja_clang.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora43_common_clang.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_instrumentation.sh b/.gitlab/ci/env_fedora43_ninja_instrumentation.sh deleted file mode 100644 index cc89d0b..0000000 --- a/.gitlab/ci/env_fedora43_ninja_instrumentation.sh +++ /dev/null
@@ -1 +0,0 @@ -source .gitlab/ci/env_fedora43_ninja.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_lfortran.sh b/.gitlab/ci/env_fedora43_ninja_lfortran.sh deleted file mode 100644 index 6b02339..0000000 --- a/.gitlab/ci/env_fedora43_ninja_lfortran.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora43_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_multi_clang.sh b/.gitlab/ci/env_fedora43_ninja_multi_clang.sh deleted file mode 100644 index 3953f88..0000000 --- a/.gitlab/ci/env_fedora43_ninja_multi_clang.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora43_common_clang.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_valgrind.sh b/.gitlab/ci/env_fedora43_ninja_valgrind.sh deleted file mode 100644 index cc89d0b..0000000 --- a/.gitlab/ci/env_fedora43_ninja_valgrind.sh +++ /dev/null
@@ -1 +0,0 @@ -source .gitlab/ci/env_fedora43_ninja.sh
diff --git a/.gitlab/ci/env_fedora43_asan.sh b/.gitlab/ci/env_fedora44_asan.sh similarity index 100% rename from .gitlab/ci/env_fedora43_asan.sh rename to .gitlab/ci/env_fedora44_asan.sh
diff --git a/.gitlab/ci/env_fedora43_bullseye_coverage.sh b/.gitlab/ci/env_fedora44_bullseye_coverage.sh similarity index 100% rename from .gitlab/ci/env_fedora43_bullseye_coverage.sh rename to .gitlab/ci/env_fedora44_bullseye_coverage.sh
diff --git a/.gitlab/ci/env_fedora43_clang_analyzer.sh b/.gitlab/ci/env_fedora44_clang_analyzer.sh similarity index 100% rename from .gitlab/ci/env_fedora43_clang_analyzer.sh rename to .gitlab/ci/env_fedora44_clang_analyzer.sh
diff --git a/.gitlab/ci/env_fedora43_clang_fuzzing.sh b/.gitlab/ci/env_fedora44_clang_fuzzing.sh similarity index 100% rename from .gitlab/ci/env_fedora43_clang_fuzzing.sh rename to .gitlab/ci/env_fedora44_clang_fuzzing.sh
diff --git a/.gitlab/ci/env_fedora43_clazy.sh b/.gitlab/ci/env_fedora44_clazy.sh similarity index 100% rename from .gitlab/ci/env_fedora43_clazy.sh rename to .gitlab/ci/env_fedora44_clazy.sh
diff --git a/.gitlab/ci/env_fedora44_common_clang.sh b/.gitlab/ci/env_fedora44_common_clang.sh new file mode 100644 index 0000000..2200841 --- /dev/null +++ b/.gitlab/ci/env_fedora44_common_clang.sh
@@ -0,0 +1,3 @@ +export CC=/usr/bin/clang +export CXX=/usr/bin/clang++ +export FC=/usr/bin/flang
diff --git a/.gitlab/ci/env_fedora43_common_lfortran.sh b/.gitlab/ci/env_fedora44_common_lfortran.sh similarity index 100% rename from .gitlab/ci/env_fedora43_common_lfortran.sh rename to .gitlab/ci/env_fedora44_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora43_extdeps.sh b/.gitlab/ci/env_fedora44_extdeps.sh similarity index 100% rename from .gitlab/ci/env_fedora43_extdeps.sh rename to .gitlab/ci/env_fedora44_extdeps.sh
diff --git a/.gitlab/ci/env_fedora43_fastbuild.sh b/.gitlab/ci/env_fedora44_fastbuild.sh similarity index 100% rename from .gitlab/ci/env_fedora43_fastbuild.sh rename to .gitlab/ci/env_fedora44_fastbuild.sh
diff --git a/.gitlab/ci/env_fedora44_hip_radeon.patch b/.gitlab/ci/env_fedora44_hip_radeon.patch new file mode 100644 index 0000000..a6411ed --- /dev/null +++ b/.gitlab/ci/env_fedora44_hip_radeon.patch
@@ -0,0 +1,14 @@ +--- a/usr/lib/clang/22/include/cuda_wrappers/new ++++ b/usr/lib/clang/22/include/cuda_wrappers/new +@@ -98,9 +98,9 @@ + __device__ inline void *operator new[](__SIZE_TYPE__, void *__ptr) CUDA_NOEXCEPT { + return __ptr; + } +-#endif + __device__ inline void operator delete(void *, void *) CUDA_NOEXCEPT {} + __device__ inline void operator delete[](void *, void *) CUDA_NOEXCEPT {} ++#endif + + #pragma pop_macro("CUDA_NOEXCEPT") + +---
diff --git a/.gitlab/ci/env_fedora44_hip_radeon.sh b/.gitlab/ci/env_fedora44_hip_radeon.sh new file mode 100644 index 0000000..dfa0121 --- /dev/null +++ b/.gitlab/ci/env_fedora44_hip_radeon.sh
@@ -0,0 +1,4 @@ +export HIPCXX=/usr/bin/clang++ +export HIPFLAGS='--rocm-path=/usr --rocm-device-lib-path=/usr/lib64/rocm/llvm/lib/clang/20/lib/amdgcn/bitcode' + +git apply --unsafe-paths --directory=/ .gitlab/ci/env_fedora44_hip_radeon.patch
diff --git a/.gitlab/ci/env_fedora43_makefiles.cmake b/.gitlab/ci/env_fedora44_makefiles.cmake similarity index 100% rename from .gitlab/ci/env_fedora43_makefiles.cmake rename to .gitlab/ci/env_fedora44_makefiles.cmake
diff --git a/.gitlab/ci/env_fedora43_makefiles.sh b/.gitlab/ci/env_fedora44_makefiles.sh similarity index 100% rename from .gitlab/ci/env_fedora43_makefiles.sh rename to .gitlab/ci/env_fedora44_makefiles.sh
diff --git a/.gitlab/ci/env_fedora44_makefiles_clang.sh b/.gitlab/ci/env_fedora44_makefiles_clang.sh new file mode 100644 index 0000000..4d2ba81 --- /dev/null +++ b/.gitlab/ci/env_fedora44_makefiles_clang.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora44_common_clang.sh
diff --git a/.gitlab/ci/env_fedora44_makefiles_lfortran.sh b/.gitlab/ci/env_fedora44_makefiles_lfortran.sh new file mode 100644 index 0000000..409404e --- /dev/null +++ b/.gitlab/ci/env_fedora44_makefiles_lfortran.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora44_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora44_makefiles_symlinked.cmake b/.gitlab/ci/env_fedora44_makefiles_symlinked.cmake new file mode 100644 index 0000000..8c39210 --- /dev/null +++ b/.gitlab/ci/env_fedora44_makefiles_symlinked.cmake
@@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/env_fedora44_makefiles.cmake")
diff --git a/.gitlab/ci/env_fedora44_makefiles_symlinked.sh b/.gitlab/ci/env_fedora44_makefiles_symlinked.sh new file mode 100644 index 0000000..84a747c --- /dev/null +++ b/.gitlab/ci/env_fedora44_makefiles_symlinked.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora44_makefiles.sh
diff --git a/.gitlab/ci/env_fedora44_ninja.sh b/.gitlab/ci/env_fedora44_ninja.sh new file mode 100644 index 0000000..3607fb1 --- /dev/null +++ b/.gitlab/ci/env_fedora44_ninja.sh
@@ -0,0 +1,14 @@ +if test "$CMAKE_CI_NIGHTLY" = "true"; then + source .gitlab/ci/appimagetool-env.sh + source .gitlab/ci/ispc-env.sh +fi + +# This job builds CMake as C++11. Prevent C++17 headers from being used. +if test "$CMAKE_CONFIGURATION" = "fedora44_ninja" -a "$CI_JOB_STAGE" = "build"; then + for i in filesystem optional string_view; do + sed -i -e '$a#error "Use <cm/'"$i"'> instead"' "/usr/include/c++/16/$i" + done +fi + +# Test in a UTF-8 locale. +export LANG=en_US.UTF-8
diff --git a/.gitlab/ci/env_fedora44_ninja_clang.sh b/.gitlab/ci/env_fedora44_ninja_clang.sh new file mode 100644 index 0000000..4d2ba81 --- /dev/null +++ b/.gitlab/ci/env_fedora44_ninja_clang.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora44_common_clang.sh
diff --git a/.gitlab/ci/env_fedora44_ninja_gcc_std_reloc.sh b/.gitlab/ci/env_fedora44_ninja_gcc_std_reloc.sh new file mode 100644 index 0000000..ae640b7 --- /dev/null +++ b/.gitlab/ci/env_fedora44_ninja_gcc_std_reloc.sh
@@ -0,0 +1,7 @@ +# "Misplace" the `libstdc++.modules.json` file so that +# `CMAKE_CXX_STDLIB_MODULES_JSON` is needed to use `import std`. +stdlib_modules_dir="/usr/lib/gcc/x86_64-redhat-linux/16" +mkdir "$stdlib_modules_dir.reloc" +mv "$stdlib_modules_dir/libstdc++.modules.json" "$stdlib_modules_dir.reloc" +export CMAKE_CI_CXX_STDLIB_MODULES_JSON="$stdlib_modules_dir.reloc/libstdc++.modules.json" +unset stdlib_modules_dir
diff --git a/.gitlab/ci/env_fedora44_ninja_instrumentation.sh b/.gitlab/ci/env_fedora44_ninja_instrumentation.sh new file mode 100644 index 0000000..99f7538 --- /dev/null +++ b/.gitlab/ci/env_fedora44_ninja_instrumentation.sh
@@ -0,0 +1 @@ +source .gitlab/ci/env_fedora44_ninja.sh
diff --git a/.gitlab/ci/env_fedora44_ninja_lfortran.sh b/.gitlab/ci/env_fedora44_ninja_lfortran.sh new file mode 100644 index 0000000..409404e --- /dev/null +++ b/.gitlab/ci/env_fedora44_ninja_lfortran.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora44_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_multi.sh b/.gitlab/ci/env_fedora44_ninja_multi.sh similarity index 100% rename from .gitlab/ci/env_fedora43_ninja_multi.sh rename to .gitlab/ci/env_fedora44_ninja_multi.sh
diff --git a/.gitlab/ci/env_fedora44_ninja_multi_clang.sh b/.gitlab/ci/env_fedora44_ninja_multi_clang.sh new file mode 100644 index 0000000..4d2ba81 --- /dev/null +++ b/.gitlab/ci/env_fedora44_ninja_multi_clang.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora44_common_clang.sh
diff --git a/.gitlab/ci/env_fedora44_ninja_valgrind.sh b/.gitlab/ci/env_fedora44_ninja_valgrind.sh new file mode 100644 index 0000000..99f7538 --- /dev/null +++ b/.gitlab/ci/env_fedora44_ninja_valgrind.sh
@@ -0,0 +1 @@ +source .gitlab/ci/env_fedora44_ninja.sh
diff --git a/.gitlab/ci/env_fedora44_pvs_studio.sh b/.gitlab/ci/env_fedora44_pvs_studio.sh new file mode 100644 index 0000000..36dba85 --- /dev/null +++ b/.gitlab/ci/env_fedora44_pvs_studio.sh
@@ -0,0 +1 @@ +. .gitlab/ci/pvs-studio-env.sh
diff --git a/.gitlab/ci/env_windows_clang_fastbuild.ps1 b/.gitlab/ci/env_windows_clang_fastbuild.ps1 new file mode 100644 index 0000000..e6c1235 --- /dev/null +++ b/.gitlab/ci/env_windows_clang_fastbuild.ps1
@@ -0,0 +1,2 @@ +. .gitlab/ci/fastbuild-env.ps1 +. .gitlab/ci/clang-env.ps1
diff --git a/.gitlab/ci/env_windows_orangec6.73.1.ps1 b/.gitlab/ci/env_windows_orangec6.73.ps1 similarity index 100% rename from .gitlab/ci/env_windows_orangec6.73.1.ps1 rename to .gitlab/ci/env_windows_orangec6.73.ps1
diff --git a/.gitlab/ci/env_windows_orangec6.73.1.ps1 b/.gitlab/ci/env_windows_orangec7.0.ps1 similarity index 100% copy from .gitlab/ci/env_windows_orangec6.73.1.ps1 copy to .gitlab/ci/env_windows_orangec7.0.ps1
diff --git a/.gitlab/ci/env_windows_orangec6.73.1.ps1 b/.gitlab/ci/env_windows_orangec7.10.ps1 similarity index 100% copy from .gitlab/ci/env_windows_orangec6.73.1.ps1 copy to .gitlab/ci/env_windows_orangec7.10.ps1
diff --git a/.gitlab/ci/env_windows_pellesc13.01_x64.ps1 b/.gitlab/ci/env_windows_pellesc13.01_x64.ps1 new file mode 100644 index 0000000..4921beb --- /dev/null +++ b/.gitlab/ci/env_windows_pellesc13.01_x64.ps1
@@ -0,0 +1,3 @@ +$povars_bat = "povars64.bat" +. .gitlab/ci/ninja-env.ps1 +. .gitlab/ci/pellesc-env.ps1
diff --git a/.gitlab/ci/env_windows_pellesc13.01_x86.ps1 b/.gitlab/ci/env_windows_pellesc13.01_x86.ps1 new file mode 100644 index 0000000..269e461 --- /dev/null +++ b/.gitlab/ci/env_windows_pellesc13.01_x86.ps1
@@ -0,0 +1,3 @@ +$povars_bat = "povars32.bat" +. .gitlab/ci/ninja-env.ps1 +. .gitlab/ci/pellesc-env.ps1
diff --git a/.gitlab/ci/intel-vars.ps1 b/.gitlab/ci/intel-vars.ps1 index dde0aa2..c95a34c 100644 --- a/.gitlab/ci/intel-vars.ps1 +++ b/.gitlab/ci/intel-vars.ps1
@@ -1,9 +1,7 @@ $erroractionpreference = "stop" -cmd /c "`".gitlab\intel\setvars.bat`" & set" | -foreach { - if ($_ -match "=") { - $v = $_.split("=") - [Environment]::SetEnvironmentVariable($v[0], $v[1]) - } +$all_env = cmd /c "`".gitlab\intel\setvars.bat`" >NUL & powershell -Command `"Get-ChildItem env: | Select-Object -Property Key,Value | ConvertTo-Json`"" | ConvertFrom-Json + +foreach ($envvar in $all_env) { + [Environment]::SetEnvironmentVariable($envvar.Key, $envvar.Value) }
diff --git a/.gitlab/ci/orangec.ps1 b/.gitlab/ci/orangec.ps1 index 2201e12..1239b0f 100644 --- a/.gitlab/ci/orangec.ps1 +++ b/.gitlab/ci/orangec.ps1
@@ -1,9 +1,19 @@ $erroractionpreference = "stop" -if ("$env:CMAKE_CONFIGURATION".Contains("orangec6.73.1")) { - # OrangeC 6.73.1 +if ("$env:CMAKE_CONFIGURATION" -eq "windows_orangec7.10") { + # OrangeC 7.10 + $archive = "ZippedBinaries71059.zip" + $release = "Orange-C-v7.10.1" + $sha256sum = "123F450D68CB26E19ED3A6646C8212C625A9158C93BCB031FE8E24CF315033E2" +} elseif ("$env:CMAKE_CONFIGURATION" -eq "windows_orangec7.0") { + # OrangeC 7.0.7 + $archive = "ZippedBinaries707.zip" + $release = "v7.0.7" + $sha256sum = "853DDE0F4819E65E3EC8F94F67C88C07062F3736BF534D2834FDEFCCE3A0B598" +} elseif ("$env:CMAKE_CONFIGURATION" -eq "windows_orangec6.73") { + # OrangeC 6.73.8 $archive = "ZippedBinaries6738.zip" - $release = "Orange-C-v6.73.1" + $release = "Orange-C-v6.73.1" # release numbering seems mismatched $sha256sum = "29BC506AB105B2BF1002129C37826B2153DF1C8D0F22B9A2C38ACA3FB72A5B89" } else { throw ('unknown CMAKE_CONFIGURATION: ' + "$env:CMAKE_CONFIGURATION")
diff --git a/.gitlab/ci/pellesc-env.ps1 b/.gitlab/ci/pellesc-env.ps1 new file mode 100644 index 0000000..23b8a62 --- /dev/null +++ b/.gitlab/ci/pellesc-env.ps1
@@ -0,0 +1,6 @@ +& "$pwsh" -File ".gitlab/ci/pellesc.ps1" +Invoke-Expression -Command .gitlab/ci/pellesc-vars.ps1 + +$env:CC = "pocc" +$env:CXX = "pocc-does-not-support-c++" +pocc | Select -First 1
diff --git a/.gitlab/ci/pellesc-vars.ps1 b/.gitlab/ci/pellesc-vars.ps1 new file mode 100644 index 0000000..739676d --- /dev/null +++ b/.gitlab/ci/pellesc-vars.ps1
@@ -0,0 +1,7 @@ +$erroractionpreference = "stop" + +$all_env = cmd /c "`".gitlab\pellesc\bin\$povars_bat`" >NUL & powershell -Command `"Get-ChildItem env: | Select-Object -Property Key,Value | ConvertTo-Json`"" | ConvertFrom-Json + +foreach ($envvar in $all_env) { + [Environment]::SetEnvironmentVariable($envvar.Key, $envvar.Value) +}
diff --git a/.gitlab/ci/pellesc.ps1 b/.gitlab/ci/pellesc.ps1 new file mode 100644 index 0000000..0caacb2 --- /dev/null +++ b/.gitlab/ci/pellesc.ps1
@@ -0,0 +1,33 @@ +$erroractionpreference = "stop" + +if ("$env:CMAKE_CONFIGURATION".Contains("pellesc13.01")) { + # PellesC 13.01 + # https://www.pellesc.se/1300/setup.exe + $filename = "pellesc-13.01-1" + $sha256sum = "E48AF208D1A46F47490011979538C1DBC427B36D47554A76C3FF83284E8F83AE" +} else { + throw ('unknown CMAKE_CONFIGURATION: ' + "$env:CMAKE_CONFIGURATION") +} +$archive = "$filename.zip" + +$outdir = $pwd.Path +$outdir = "$outdir\.gitlab" +$ProgressPreference = 'SilentlyContinue' +# This URL is only visible inside of Kitware's network. See above filename table. +Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/internal/$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") +Move-Item -Path "$outdir\$filename" -Destination "$outdir\pellesc" +Remove-Item "$outdir\$archive" + +$scripts = "povars32.bat", "povars64.bat" +foreach ($script in $scripts) { + $bat = Get-Content -Path "$outdir\pellesc\bin\$script.in" -Raw + $bat = $bat -Replace "@PellesCDir@","$outdir\pellesc" + $bat | Set-Content -Path "$outdir\pellesc\bin\$script" +}
diff --git a/.gitlab/ci/post_build_fedora43_tidy.sh b/.gitlab/ci/post_build_fedora44_tidy.sh similarity index 100% rename from .gitlab/ci/post_build_fedora43_tidy.sh rename to .gitlab/ci/post_build_fedora44_tidy.sh
diff --git a/.gitlab/ci/pre_build_fedora43_bullseye_coverage.sh b/.gitlab/ci/pre_build_fedora44_bullseye_coverage.sh similarity index 100% rename from .gitlab/ci/pre_build_fedora43_bullseye_coverage.sh rename to .gitlab/ci/pre_build_fedora44_bullseye_coverage.sh
diff --git a/.gitlab/ci/pre_configure_fedora43_bullseye_coverage.sh b/.gitlab/ci/pre_configure_fedora44_bullseye_coverage.sh similarity index 100% rename from .gitlab/ci/pre_configure_fedora43_bullseye_coverage.sh rename to .gitlab/ci/pre_configure_fedora44_bullseye_coverage.sh
diff --git a/.gitlab/ci/pre_configure_fedora43_tidy.sh b/.gitlab/ci/pre_configure_fedora44_tidy.sh similarity index 100% rename from .gitlab/ci/pre_configure_fedora43_tidy.sh rename to .gitlab/ci/pre_configure_fedora44_tidy.sh
diff --git a/.gitlab/ci/pre_test_fedora43_bullseye_coverage.sh b/.gitlab/ci/pre_test_fedora44_bullseye_coverage.sh similarity index 100% rename from .gitlab/ci/pre_test_fedora43_bullseye_coverage.sh rename to .gitlab/ci/pre_test_fedora44_bullseye_coverage.sh
diff --git a/.gitlab/ci/pvs-studio-env.sh b/.gitlab/ci/pvs-studio-env.sh new file mode 100644 index 0000000..c179b30 --- /dev/null +++ b/.gitlab/ci/pvs-studio-env.sh
@@ -0,0 +1,13 @@ +.gitlab/ci/pvs-studio.sh +export PATH=$PWD/.gitlab/pvs-studio/bin:$PATH +pvs-studio --version + +if test -f "$CMAKE_CI_PVS_STUDIO_LICENSE_USER" -a -f "$CMAKE_CI_PVS_STUDIO_LICENSE_KEY"; then + pvs-studio-analyzer credentials "$(<"$CMAKE_CI_PVS_STUDIO_LICENSE_USER")" "$(<"$CMAKE_CI_PVS_STUDIO_LICENSE_KEY")" +else + echo "The CMAKE_CI_PVS_STUDIO_LICENSE_USER and CMAKE_CI_PVS_STUDIO_LICENSE_KEY files were not both provided!" + exit 1 +fi +rm -f "$CMAKE_CI_PVS_STUDIO_LICENSE_USER" "$CMAKE_CI_PVS_STUDIO_LICENSE_KEY" +unset CMAKE_CI_PVS_STUDIO_LICENSE_KEY +unset CMAKE_CI_PVS_STUDIO_LICENSE_USER
diff --git a/.gitlab/ci/pvs-studio.sh b/.gitlab/ci/pvs-studio.sh new file mode 100755 index 0000000..b0eddf3 --- /dev/null +++ b/.gitlab/ci/pvs-studio.sh
@@ -0,0 +1,33 @@ +#!/bin/sh + +set -e + +readonly version="7.42.105112.761" + +case "$(uname -s)-$(uname -m)" in + Linux-x86_64) + # https://files.pvs-studio.com/pvs-studio-7.42.105112.761-x86_64.tgz + shatool="sha256sum" + sha256sum="0ced11d17855cc172a860f8cc19a89f10adb6ab7185992fb57474118e9bdabd7" + filename="pvs-studio-$version-x86_64" + ;; + *) + echo "Unrecognized platform $(uname -s)-$(uname -m)" + exit 1 + ;; +esac +readonly shatool +readonly sha256sum + +cd .gitlab + +# This URL is only visible inside of Kitware's network. See above filename table. +baseurl="https://cmake.org/files/dependencies/internal" + +tarball="$filename.tgz" +echo "$sha256sum $tarball" > pvs-studio.sha256sum +curl -OL "$baseurl/$tarball" +$shatool --check pvs-studio.sha256sum +tar xzf "$tarball" +rm "$tarball" pvs-studio.sha256sum +mv "$filename" "pvs-studio"
diff --git a/.gitlab/ci/repackage/pellesc.ps1 b/.gitlab/ci/repackage/pellesc.ps1 new file mode 100644 index 0000000..0d10129 --- /dev/null +++ b/.gitlab/ci/repackage/pellesc.ps1
@@ -0,0 +1,74 @@ +# Pelles C compilers are available only via installers. +# Run an installer and repackage the installation directory. + +# From the Pelles C download page, download "setup.exe". +# Run this script passing "setup.exe" followed by the version number. + +#Requires -RunAsAdministrator + +param ( + [Parameter(Mandatory=$true)] + [string]$installer, + [Parameter(Mandatory=$true)] + [string]$version, + [string]$revision = "1", + [string]$basedir = "c:\PellesC" + ) + +$erroractionpreference = "stop" + +Add-Type -AssemblyName System.IO.Compression.FileSystem + +$installer_file = Get-Item $installer +$installer_name = $installer_file.Name +$package_name = "pellesc-$version-$revision" +$package_dir = New-Item -Force -ItemType Directory -Path "$basedir\$package_name" +if (-not $package_dir) { + Write-Host "Failed to create package install dir." + Exit 1 +} + +Write-Host "Installing to: $package_dir" + +# The installer treats everything after /D= as the destination. +# Start-Process adds a trailing space, so use "cmd" instead. +& "$env:ComSpec" /c start /wait "$installer_file" /S /D=$package_dir +Write-Host "" +Remove-Item "$package_dir/uninst.exe" -Force + +# Convert environment scripts to templates. +$bats = @( + "bin\povars32.bat" + "bin\povars64.bat" + ) +foreach ($p in $bats) { + $bat = Get-Content -Path "$package_dir\$p" -Raw + $bat = $bat -Replace "PellesCDir=.*","PellesCDir=@PellesCDir@" + $bat | Set-Content -Path "$package_dir\$p.in" + Remove-Item "$package_dir\$p" -Force +} + +@" +This was repackaged from an installation by + + Pelles C $version "$installer_name" + +using CMake's ".gitlab/ci/repackage/pellesc.ps1" script. + +Copy or rename the environment script templates in the "bin" directory: + + povars32.bat.in => povars32.bat + povars64.bat.in => povars64.bat + +and replace the "@PellesCDir@" placeholders with + + C:\path\to\this\directory + +Then use "povars32.bat" or "povars64.bat" to establish an environment. + +"@ | Add-Content -NoNewline "$package_dir/README.txt" + +Write-Host "Repackaging to: $package_name.zip" +$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal +$includeBaseDirectory = $true +[System.IO.Compression.ZipFile]::CreateFromDirectory("$package_dir", "$package_name.zip", $compressionLevel, $includeBaseDirectory)
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index 7ec2288..4c675f0 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml
@@ -5,7 +5,7 @@ ### Release .linux_prep_source: - image: "fedora:43" + image: "fedora:44" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" @@ -58,7 +58,7 @@ ### Debian .debian13: - image: "kitware/cmake:ci-debian13-x86_64-2026-02-08" + image: "kitware/cmake:ci-debian13-x86_64-2026-04-28" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" @@ -81,15 +81,15 @@ ### Fedora -.fedora43: - image: "kitware/cmake:ci-fedora43-x86_64-2026-02-08" +.fedora44: + image: "kitware/cmake:ci-fedora44-x86_64-2026-04-30" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci/long file name for testing purposes" CMAKE_ARCH: x86_64 -.fedora43_hip: - image: "kitware/cmake:ci-fedora43-hip-x86_64-2025-10-29" +.fedora44_hip: + image: "kitware/cmake:ci-fedora44-hip-x86_64-2026-04-30" variables: # FIXME(rocclr): device modules fail loading from binaries in paths with spaces @@ -98,26 +98,26 @@ #### Coverage builds -.fedora43_gcc_gcov: - extends: .fedora43 +.fedora44_gcc_gcov: + extends: .fedora44 variables: # See issue #20001 CMAKE_GENERATOR: "Unix Makefiles" - CMAKE_CONFIGURATION: fedora43_gcc_gcov + CMAKE_CONFIGURATION: fedora44_gcc_gcov CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_CI_BUILD_TYPE: Debug CMAKE_CI_TEST_TIMEOUT: "1500" CMAKE_CI_NO_INSTALL: 1 -.fedora43_bullseye_coverage: - extends: .fedora43 +.fedora44_bullseye_coverage: + extends: .fedora44 environment: name: bullseye-coverage variables: # See issue #20001 CMAKE_GENERATOR: "Unix Makefiles" - CMAKE_CONFIGURATION: fedora43_bullseye_coverage + CMAKE_CONFIGURATION: fedora44_bullseye_coverage CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_CI_BUILD_TYPE: Debug CMAKE_CI_TEST_TIMEOUT: "1500" @@ -125,53 +125,61 @@ #### Lint builds -.fedora43_tidy: - extends: .fedora43 +.fedora44_tidy: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_tidy + CMAKE_CONFIGURATION: fedora44_tidy CMAKE_CI_NO_INSTALL: 1 -.fedora43_clang_analyzer: - extends: .fedora43 +.fedora44_pvs_studio: + extends: .fedora44 + environment: + name: pvs-studio + variables: + CMAKE_CONFIGURATION: fedora44_pvs_studio + CMAKE_CI_NO_INSTALL: 1 + +.fedora44_clang_analyzer: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_clang_analyzer + CMAKE_CONFIGURATION: fedora44_clang_analyzer CMAKE_CI_BUILD_TYPE: Debug CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_CI_NO_INSTALL: 1 -.fedora43_clang_fuzzing: - extends: .fedora43 +.fedora44_clang_fuzzing: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_clang_fuzzing + CMAKE_CONFIGURATION: fedora44_clang_fuzzing CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_CI_NO_INSTALL: 1 -.fedora43_clazy: - extends: .fedora43 +.fedora44_clazy: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_clazy + CMAKE_CONFIGURATION: fedora44_clazy CMAKE_CI_BUILD_TYPE: Debug CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_CI_NO_INSTALL: 1 -.fedora43_sphinx: - extends: .fedora43 +.fedora44_sphinx: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_sphinx + CMAKE_CONFIGURATION: fedora44_sphinx CTEST_NO_WARNINGS_ALLOWED: 1 CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx" CMAKE_CI_NO_INSTALL: 1 -.fedora43_sphinx_package: - extends: .fedora43 +.fedora44_sphinx_package: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_sphinx_package + CMAKE_CONFIGURATION: fedora44_sphinx_package CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx" #### Build and test @@ -227,53 +235,53 @@ CMAKE_CI_BUILD_TYPE: Release CTEST_NO_WARNINGS_ALLOWED: 1 -.fedora43_extdeps: - extends: .fedora43 +.fedora44_extdeps: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_extdeps + CMAKE_CONFIGURATION: fedora44_extdeps CMAKE_CI_BUILD_TYPE: Release CTEST_NO_WARNINGS_ALLOWED: 1 -.fedora43_ninja: - extends: .fedora43 +.fedora44_ninja: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_ninja + CMAKE_CONFIGURATION: fedora44_ninja CMAKE_CI_BUILD_TYPE: Release CTEST_NO_WARNINGS_ALLOWED: 1 -.fedora43_makefiles: - extends: .fedora43 +.fedora44_makefiles: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_makefiles + CMAKE_CONFIGURATION: fedora44_makefiles CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_GENERATOR: "Unix Makefiles" -.fedora43_makefiles_symlinked: - extends: .fedora43 +.fedora44_makefiles_symlinked: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_makefiles_symlinked + CMAKE_CONFIGURATION: fedora44_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" -.fedora43_fastbuild: - extends: .fedora43 +.fedora44_fastbuild: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_fastbuild + CMAKE_CONFIGURATION: fedora44_fastbuild CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_GENERATOR: "FASTBuild" -.fedora43_ninja_instrumentation: - extends: .fedora43 +.fedora44_ninja_instrumentation: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_ninja_instrumentation + CMAKE_CONFIGURATION: fedora44_ninja_instrumentation CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_GENERATOR: "Ninja" @@ -292,50 +300,57 @@ variables: CMAKE_CONFIGURATION: debian13_ninja_clang -.fedora43_makefiles_clang: - extends: .fedora43 +.fedora44_makefiles_clang: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_makefiles_clang + CMAKE_CONFIGURATION: fedora44_makefiles_clang CMAKE_GENERATOR: "Unix Makefiles" -.fedora43_makefiles_lfortran: - extends: .fedora43 +.fedora44_makefiles_lfortran: + extends: .fedora44 variables: # FIXME(lfortran): -rpath flags with spaces not forwarded GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci" - CMAKE_CONFIGURATION: fedora43_makefiles_lfortran + CMAKE_CONFIGURATION: fedora44_makefiles_lfortran CMAKE_GENERATOR: "Unix Makefiles" CTEST_LABELS: "Fortran" -.fedora43_ninja_lfortran: - extends: .fedora43 +.fedora44_ninja_lfortran: + extends: .fedora44 variables: # FIXME(lfortran): -rpath flags with spaces not forwarded GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci" - CMAKE_CONFIGURATION: fedora43_ninja_lfortran + CMAKE_CONFIGURATION: fedora44_ninja_lfortran CTEST_LABELS: "Fortran" -.fedora43_ninja_multi: - extends: .fedora43 +.fedora44_ninja_multi: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_ninja_multi + CMAKE_CONFIGURATION: fedora44_ninja_multi CMAKE_GENERATOR: "Ninja Multi-Config" -.fedora43_ninja_clang: - extends: .fedora43 +.fedora44_ninja_clang: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_ninja_clang + CMAKE_CONFIGURATION: fedora44_ninja_clang -.fedora43_ninja_multi_clang: - extends: .fedora43 +.fedora44_ninja_gcc_std_reloc: + extends: .fedora44 variables: - CMAKE_CONFIGURATION: fedora43_ninja_multi_clang + CMAKE_CONFIGURATION: fedora44_ninja_gcc_std_reloc + CTEST_LABELS: "CXXModules" + +.fedora44_ninja_multi_clang: + extends: .fedora44 + + variables: + CMAKE_CONFIGURATION: fedora44_ninja_multi_clang CMAKE_GENERATOR: "Ninja Multi-Config" ### Sanitizers @@ -358,23 +373,23 @@ CTEST_MEMORYCHECK_TYPE: Valgrind CMAKE_CI_RUN_MEMCHECK: "true" CMAKE_CI_TEST_TIMEOUT: "1500" - CMAKE_VALGRIND_CONFIGURATION: fedora43 + CMAKE_VALGRIND_CONFIGURATION: fedora44 -.fedora43_asan: +.fedora44_asan: extends: - - .fedora43 + - .fedora44 - .fedora_asan_addon variables: - CMAKE_CONFIGURATION: fedora43_asan + CMAKE_CONFIGURATION: fedora44_asan -.fedora43_ninja_valgrind: +.fedora44_ninja_valgrind: extends: - - .fedora43 + - .fedora44 - .fedora_valgrind_addon variables: - CMAKE_CONFIGURATION: fedora43_ninja_valgrind + CMAKE_CONFIGURATION: fedora44_ninja_valgrind ### Intel Compiler @@ -400,7 +415,7 @@ ### NVHPC Compiler .nvhpc: - image: "kitware/cmake:ci-nvhpc24.9-x86_64-2024-09-27" + image: "kitware/cmake:ci-nvhpc26.3-x86_64-2026-03-31" variables: CMAKE_ARCH: x86_64 CMAKE_CUDA_ARCHITECTURES_NATIVE_CLAMP: 1 @@ -558,6 +573,18 @@ CMAKE_CONFIGURATION: cuda13.0_aarch64_nvidia CTEST_NO_WARNINGS_ALLOWED: 1 +.cuda13.2: + extends: .cuda + image: "kitware/cmake:ci-cuda13.2-x86_64-2026-05-13" + variables: + CMAKE_ARCH: x86_64 + +.cuda13.2_nvidia: + extends: .cuda13.2 + variables: + CMAKE_CONFIGURATION: cuda13.2_nvidia + CTEST_NO_WARNINGS_ALLOWED: 1 + ### HIP builds .hip6.3: @@ -584,11 +611,11 @@ CMAKE_CONFIGURATION: debian13_hip_radeon CTEST_LABELS: "HIP" -.fedora43_hip_radeon: - extends: .fedora43_hip +.fedora44_hip_radeon: + extends: .fedora44_hip variables: - CMAKE_CONFIGURATION: fedora43_hip_radeon + CMAKE_CONFIGURATION: fedora44_hip_radeon CTEST_LABELS: "HIP" .hip6.3_nvidia: @@ -599,37 +626,6 @@ CTEST_LABELS: "HIP" CMAKE_CUDA_ARCHITECTURES_NATIVE_CLAMP: 1 -### C++ modules - -.gcc_cxx_modules_x86_64: - image: "kitware/cmake:ci-gcc_cxx_modules-x86_64-2024-12-23" - - variables: - GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" - CMAKE_ARCH: x86_64 - CC: "/opt/gcc-importstd/bin/gcc" - CXX: "/opt/gcc-importstd/bin/g++" - -.gcc_cxx_modules_ninja: - extends: .gcc_cxx_modules_x86_64 - - variables: - CMAKE_CONFIGURATION: linux_gcc_cxx_modules_ninja - -.gcc_cxx_modules_reloc_ninja: - extends: .gcc_cxx_modules_x86_64 - - variables: - CMAKE_CONFIGURATION: linux_gcc_cxx_modules_reloc_ninja - CTEST_LABELS: "CXXModules" - -.gcc_cxx_modules_ninja_multi: - extends: .gcc_cxx_modules_x86_64 - - variables: - CMAKE_CONFIGURATION: linux_gcc_cxx_modules_ninja_multi - CMAKE_GENERATOR: "Ninja Multi-Config" - ### Debian 10 legacy packages .debian10: @@ -690,10 +686,10 @@ - docker - linux-x86_64 -.linux_x86_64_v3_tags_cuda_arch_52: +.linux_x86_64_v3_tags_cuda_arch_75: tags: - cmake - - cuda-arch-52 + - cuda-arch-75 - docker - linux-x86_64-v3 @@ -774,14 +770,14 @@ .cmake_version_update_linux: stage: build - extends: .fedora43 + extends: .fedora44 script: - .gitlab/ci/cmake_version_update.sh interruptible: false # The job internally fetches and retries. .cmake_spellcheck_linux: stage: build - extends: .fedora43 + extends: .fedora44 script: - .gitlab/ci/codespell.bash - .gitlab/ci/typos.bash @@ -952,7 +948,7 @@ .cmake_org_help: stage: build extends: - - .fedora43 + - .fedora44 - .linux_x86_64_tags - .cmake_org_help_artifacts script:
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 66c47ae..28451fa 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" .macos_tart: - image: "registry.hub.docker.com/kitware/macos-ci-images:macos-26-xcode-26.6-20260710" + image: "registry.hub.docker.com/kitware/macos-ci-images:macos-26-xcode-26.6-20260724" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci" TART_EXECUTOR_HOST_DIR: "false"
diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index 55bc515..d11b793 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml
@@ -45,25 +45,25 @@ variables: VCVARSALL: "${VS180COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" VCVARSPLATFORM: "x64" - VCVARSVERSION: "14.50.35717" + VCVARSVERSION: "14.51.36231" .windows_vcvarsall_vs2026_x86: variables: VCVARSALL: "${VS180COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" VCVARSPLATFORM: "x86" - VCVARSVERSION: "14.50.35717" + VCVARSVERSION: "14.51.36231" .windows_vcvarsall_vs2026_x64_arm64: variables: VCVARSALL: "${VS180COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" VCVARSPLATFORM: "x64_arm64" - VCVARSVERSION: "14.50.35717" + VCVARSVERSION: "14.51.36231" .windows_arm64_vcvarsall_vs2026: variables: VCVARSALL: "${VS180COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" VCVARSPLATFORM: "arm64" - VCVARSVERSION: "14.50.35717" + VCVARSVERSION: "14.51.36231" .windows_vs2026_x64_pch: extends: @@ -129,7 +129,7 @@ CMAKE_CONFIGURATION: windows_vs2026_x64 CMAKE_GENERATOR: "Visual Studio 18 2026" CMAKE_GENERATOR_PLATFORM: "x64" - CMAKE_GENERATOR_TOOLSET: "v145,version=14.50.35717" + CMAKE_GENERATOR_TOOLSET: "v145,version=14.51.36231" CMAKE_CI_JOB_NIGHTLY_IGNORE_DEPS: "true" .windows_vs2022_x64: @@ -243,6 +243,14 @@ variables: CMAKE_CONFIGURATION: windows_vs2026_x64_fastbuild +.windows_clang_fastbuild: + extends: + - .windows_fastbuild + - .windows_vcvarsall_vs2022_x64_msvc14.44 + + variables: + CMAKE_CONFIGURATION: windows_clang_fastbuild + .windows_clang_ninja: extends: - .windows_ninja @@ -305,11 +313,44 @@ CMAKE_CI_BUILD_TYPE: Release CMAKE_CI_JOB_NIGHTLY_IGNORE_DEPS: "true" -.windows_orangec6.73.1: +.windows_orangec6.73: extends: .windows_orangec variables: - CMAKE_CONFIGURATION: windows_orangec6.73.1 + CMAKE_CONFIGURATION: windows_orangec6.73 + +.windows_orangec7.0: + extends: .windows_orangec + + variables: + CMAKE_CONFIGURATION: windows_orangec7.0 + +.windows_orangec7.10: + extends: .windows_orangec + + variables: + CMAKE_CONFIGURATION: windows_orangec7.10 + +.windows_pellesc: + extends: .windows + + variables: + CMAKE_GENERATOR: "Ninja" + CMAKE_CI_BUILD_TYPE: Release + CMAKE_CI_JOB_NIGHTLY_IGNORE_DEPS: "true" + CTEST_LABELS: "PellesC" + +.windows_pellesc13.01_x64: + extends: .windows_pellesc + + variables: + CMAKE_CONFIGURATION: windows_pellesc13.01_x64 + +.windows_pellesc13.01_x86: + extends: .windows_pellesc + + variables: + CMAKE_CONFIGURATION: windows_pellesc13.01_x86 .windows_arm64_vs2026: extends: .windows @@ -318,7 +359,7 @@ CMAKE_CONFIGURATION: windows_arm64_vs2026 CMAKE_GENERATOR: "Visual Studio 18 2026" CMAKE_GENERATOR_PLATFORM: "ARM64" - CMAKE_GENERATOR_TOOLSET: "v145,version=14.50.35717" + CMAKE_GENERATOR_TOOLSET: "v145,version=14.51.36231" CMAKE_CI_JOB_NIGHTLY_IGNORE_DEPS: "true" .windows_arm64_vs2022: @@ -370,7 +411,7 @@ - windows-x86_64 - shell - vs2026 - - msvc-14.50 + - msvc-14.51 - nonconcurrent .windows_x86_64_tags_nonconcurrent_vs2026_arm64: @@ -379,7 +420,7 @@ - windows-x86_64 - shell - vs2026 - - msvc-14.50-arm64 + - msvc-14.51-arm64 - nonconcurrent .windows_x86_64_tags_concurrent_vs2026: @@ -388,7 +429,7 @@ - windows-x86_64 - shell - vs2026 - - msvc-14.50 + - msvc-14.51 - concurrent .windows_x86_64_tags_concurrent_vs2026_android: @@ -398,7 +439,7 @@ - shell - vs2026 - vs18-android - - msvc-14.50 + - msvc-14.51 - concurrent .windows_x86_64_tags_concurrent_vs2022_msvc14.44: @@ -452,7 +493,7 @@ - windows-arm64 - shell - vs2026 - - msvc-14.50 + - msvc-14.51 - nonconcurrent .windows_arm64_tags_concurrent_vs2026: @@ -461,7 +502,7 @@ - windows-arm64 - shell - vs2026 - - msvc-14.50 + - msvc-14.51 - concurrent .windows_arm64_tags_concurrent_vs2022:
diff --git a/.gitlab/upload.yml b/.gitlab/upload.yml index 47316d1..7e7516b 100644 --- a/.gitlab/upload.yml +++ b/.gitlab/upload.yml
@@ -1,7 +1,7 @@ # Steps for uploading artifacts .rsync_upload_package: - image: "fedora:43" + image: "fedora:44" stage: upload tags: - cmake @@ -21,7 +21,7 @@ .rsync_upload_help: stage: upload - image: "fedora:43" + image: "fedora:44" tags: - cmake - docker
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 64acb89..1dc9a10 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml
@@ -113,7 +113,7 @@ # NOTE See BUG https://github.com/crate-ci/typos/issues/390 - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.44.0 + rev: v1.48.0 hooks: - id: typos # NOTE Override hook's default args to prevent automatic
diff --git a/.pvsconfig b/.pvsconfig new file mode 100644 index 0000000..4d7413f --- /dev/null +++ b/.pvsconfig
@@ -0,0 +1,100 @@ +# Identical sub-expressions to the left and to the right of 'foo' operator. +//-V::501 +# Pointer to local variable 'X' is stored outside the scope of this variable. Such a pointer will become invalid. +//-V::506 +# The 'new type(n)' pattern was detected. Probably meant: 'new type[n]'. +//-V::508 +# The 'x' variable is assigned values twice successively. Perhaps this is a mistake. +//-V::519 +# Possible null pointer dereference. +//-V::522 +# Constant value is represented by an octal form. +//-V::536 +# Iterators are passed as arguments to 'Foo' function. Consider inspecting the expression. +//-V::539 +# Expression is always true/false. +//-V::547 +# Expression of the 'A - B > 0' kind will work as 'A != B'. +//-V::555 +# Possible array overrun. +//-V::557 +# Part of conditional expression is always true/false. +//-V::560 +# It is suspicious that the argument of sizeof() operator is the expression. +//-V::568 +# Variable is assigned to itself. +//-V::570 +# Recurring check. This condition was already verified in previous line. +//-V::571 +# Function receives suspicious argument. +//-V::575 +# Conditional expressions of 'if' statements located next to each other are identical. +//-V::581 +# The 'Foo' function is called twice to deallocate the same resource. +//-V::586 +# Expression is enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or typo is present. +//-V::592 +# Pointer was used before its check for nullptr. Check lines: N1, N2. +//-V::595 +# Possible division or mod by zero. +//-V::609 +# Condition of a loop is always true/false. +//-V::654 +# Possible meaningless check for null, as memory was allocated using 'new' operator. Memory allocation will lead to an exception. +//-V::668 +# An object is used as an argument to its own method. +//-V::678 +# Pattern A || (A && ...) was detected. The expression is excessive or contains a logical error. +//-V::686 +# The class implements a copy constructor/operator=, but lacks the operator=/copy constructor. +//-V::690 +# Excessive check can be simplified. The '||' operator is surrounded by opposite expressions 'x' and '!x'. +//-V::728 +# Not all members of a class are initialized inside the constructor. +//-V::730 +# Suspicious access to element of '...' array by a constant index inside a loop. +//-V::767 +# The pointer in the expression equals nullptr. The resulting value is meaningless and should not be used. +//-V::769 +# Two similar code fragments were found. +//-V::778 +# Value of a variable is checked after it is used. Possible error in program's logic. Check lines: N1, N2. +//-V::781 +# Variable is assigned but not used by the end of the function. +//-V::1001 +# Pointer was used unsafely after its check for nullptr. +//-V::1004 +# Value from the uninitialized optional is used. It may be an error. +//-V::1007 +# The value is out of range of enum values. This causes unspecified or undefined behavior. +//-V::1016 +# A pointer without owner is added to the container by the 'emplace_back' method. A memory leak will occur in case of an exception. +//-V::1023 +# Possible overflow. Consider casting operands, not the result. +//-V::1028 +# Variable is used after it is moved. +//-V::1030 +# Two or more case-branches perform the same actions. +//-V::1037 +# This file is marked with copyleft license, which requires you to open the derived source code. +//-V::1042 +# Loop break conditions do not depend on the number of iterations. +//-V::1044 +# Variable 'foo' was assigned the same value. +//-V::1048 +# It is possible that an assigned variable should be checked in the next condition. Consider checking for typos. +//-V::1051 +# Calling the 'foo' virtual function in the constructor/destructor may lead to unexpected result at runtime. +//-V::1053 +# Return value is not always used. Consider inspecting the 'foo' function. +//-V::1071 +# Conditional initialization inside the constructor may leave some members uninitialized. +//-V::1077 +# Call of the 'Foo' function will lead to buffer underflow. +//-V::1086 +# Waiting on condition variable without predicate. A thread can wait indefinitely or experience a spurious wake-up. +//-V::1089 +# The 'emplace' / 'insert' function call contains potentially dangerous move operation. Moved object can be destroyed even if there is no insertion. +//-V::1098 +# Preprocessing directive is present within a macro argument. This leads to undefined behavior. +//-V::1119
diff --git a/.typos.toml b/.typos.toml index 0db7ca8..43377bd 100644 --- a/.typos.toml +++ b/.typos.toml
@@ -99,4 +99,9 @@ # NOTE Allow to mark block of text to exclude from spellchecking as RST comments "(?s)\\.\\.\\s+(NOQA|noqa):? spellcheck(: *|=| +)off.*?\\n\\.\\.\\s+(NOQA|noqa):? spellcheck(: *|=| +)on" ] + +[type.rst.extend-identifiers] +# CMake build options in the `Help/guide/tutorial/Configuration and Cache Variables.rst` +DCOMPRESSION_SOFTWARE_USE_ZLIB = "DCOMPRESSION_SOFTWARE_USE_ZLIB" +DCOMPRESSION_SOFTWARE_USE_ZSTD = "DCOMPRESSION_SOFTWARE_USE_ZSTD" # END Type-specific settings
diff --git a/Auxiliary/bash-completion/cmake b/Auxiliary/bash-completion/cmake index 3c30d34..c46e02a 100644 --- a/Auxiliary/bash-completion/cmake +++ b/Auxiliary/bash-completion/cmake
@@ -114,6 +114,31 @@ _filedir -d return ;; + + --target) + local quoted + printf -v quoted %q "$cur" + # Options allowed right after `--target` + local target_options='--verbose --clean-first --config --' + local build_dir="build" + for ((i=0; i < ${#COMP_WORDS[@]}; i++)); do + if [[ "${COMP_WORDS[i]}" == "--build" ]]; then + build_dir="${COMP_WORDS[i+1]}" + break + fi + done + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W "$target_options" -- "$quoted" ) ) + else + local targets=$( cmake --build "$build_dir" --target help 2>/dev/null \ + | sed -n -e 's/^... \([^ ]*\).*$/\1/p' \ + -e '/^\[/d' -e 's/^\([^ :]*\):.*$/\1/p' | \ + grep -v '^/' | sort -u ) + COMPREPLY=( $( compgen -W "$targets $target_options" -- "$quoted" ) ) + fi + return + ;; + --install|--open) _filedir -d return
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index 280dd9b..a2b2167 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim
@@ -415,6 +415,8 @@ \ Swift_LANGUAGE_VERSION \ Swift_MODULE_DIRECTORY \ Swift_MODULE_NAME + \ Swift_PACKAGE_NAME + \ Swift_SEPARATE_MODULE_EMISSION \ TARGET_ARCHIVES_MAY_BE_SHARED_LIBS \ TARGET_MESSAGES \ TARGET_SUPPORTS_SHARED_LIBS @@ -2037,6 +2039,7 @@ \ CMAKE_Swift_NUM_THREADS \ CMAKE_Swift_OUTPUT_EXTENSION \ CMAKE_Swift_PLATFORM_ID + \ CMAKE_Swift_SEPARATE_MODULE_EMISSION \ CMAKE_Swift_SIMULATE_ID \ CMAKE_Swift_SIMULATE_VERSION \ CMAKE_Swift_SIZEOF_DATA_PTR
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f542c5..4f8a022 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,13 +1,33 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file LICENSE.rst or https://cmake.org/licensing for details. -cmake_minimum_required(VERSION 3.13...4.1 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...4.2 FATAL_ERROR) + +if(CMake_TEST_HOST_CMAKE) + get_filename_component(CMake_TEST_EXTERNAL_CMAKE "${CMAKE_COMMAND}" DIRECTORY) +endif() + +set(CMake_LANGUAGES "") +if(CMake_TEST_EXTERNAL_CMAKE) + foreach(lang C CXX) + if(NOT DEFINED CMake_TEST_${lang}) + set(CMake_TEST_${lang} 1) + endif() + if(CMake_TEST_${lang}) + list(APPEND CMake_LANGUAGES ${lang}) + endif() + endforeach() +else() + set(CMake_TEST_C 1) + set(CMake_TEST_CXX 1) + list(APPEND CMake_LANGUAGES C CXX) +endif() set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake) - -project(CMake) +project(CMake LANGUAGES ${CMake_LANGUAGES}) unset(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX) unset(CMAKE_USER_MAKE_RULES_OVERRIDE_C) +unset(CMake_LANGUAGES) # FIXME: This block should go away after a transition period. if(MSVC AND NOT CMAKE_VERSION VERSION_LESS 3.15) @@ -30,10 +50,6 @@ unset(CMAKE_BOOTSTRAP CACHE) endif() -if(CMake_TEST_HOST_CMAKE) - get_filename_component(CMake_TEST_EXTERNAL_CMAKE "${CMAKE_COMMAND}" DIRECTORY) -endif() - if(NOT CMake_TEST_EXTERNAL_CMAKE) if(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") message(FATAL_ERROR @@ -375,6 +391,22 @@ list(APPEND CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${CMake_IWYU_OPTIONS}) endif() +option(CMake_RUN_PVS_STUDIO "Run pvs-studio-analyzer with the compiler." OFF) +if(CMake_RUN_PVS_STUDIO) + if(CMake_SOURCE_DIR STREQUAL CMake_BINARY_DIR) + message(FATAL_ERROR "CMake_RUN_PVS_STUDIO requires an out-of-source build!") + endif() + find_program(PVS_STUDIO_COMMAND NAMES pvs-studio-analyzer) + if(NOT PVS_STUDIO_COMMAND) + message(FATAL_ERROR "CMake_RUN_PVS_STUDIO is ON but pvs-studio-analyzer is not found!") + endif() + set(CMAKE_CXX_PVS_STUDIO + "${PVS_STUDIO_COMMAND}" analyze + -a GA + --apply-pvs-configs + --project-root "${CMAKE_SOURCE_DIR}" + ) +endif() #----------------------------------------------------------------------- # a macro that only sets the FOLDER target property if it's @@ -397,6 +429,7 @@ execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} -dumpversion OUTPUT_VARIABLE _GXX_VERSION + RESULT_VARIABLE _GXX_VERSION_RESULT ) string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" _GXX_VERSION_SHORT ${_GXX_VERSION}) @@ -466,6 +499,7 @@ if(CMake_TEST_EXTERNAL_CMAKE) set(KWIML_TEST_ENABLE 1) + set(KWIML_TEST_LABELS "PellesC") add_subdirectory(Utilities/KWIML) endif()
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 7247467..7b918dc 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst
@@ -45,6 +45,9 @@ The merge request will enter the `CMake Review Process`_ for consideration. +Please note `CMake's AI policy`_ if using AI tools or services as part of +preparing or submitting a change. + .. _`Kitware's GitLab Instance`: https://gitlab.kitware.com .. _`CMake Repository`: https://gitlab.kitware.com/cmake/cmake .. _`Utilities/SetupForDevelopment.sh`: Utilities/SetupForDevelopment.sh @@ -53,6 +56,7 @@ .. _`CMake Testing Guide`: Help/dev/testing.rst .. _`commit messages`: Help/dev/review.rst#commit-messages .. _`CMake Review Process`: Help/dev/review.rst +.. _`CMake's AI policy`: Help/dev/ai-policy.rst CMake Dashboard Client ======================
diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index d4d030e..cd5479a 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst
@@ -64,6 +64,7 @@ * `Petr Gotthard <mailto:gotthard@honeywell.com>`__ * `Philip Lowman <mailto:philip@yhbt.com>`__ * `Philippe Proulx <mailto:pproulx@efficios.com>`__ +* `Qualcomm Technologies, Inc. <https://www.qualcomm.com>`__ * Raffi Enficiaud, Max Planck Society * Raumfeld * `Roger Leigh <mailto:rleigh@codelibre.net>`__
diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in index 75642c9..591da55 100644 --- a/CTestCustom.cmake.in +++ b/CTestCustom.cmake.in
@@ -51,6 +51,7 @@ "Warning: Function .* can throw only the exceptions thrown by the function .* it overrides\\." "WarningMessagesDialog\\.cxx" "warning.*directory name.*CMake-Xcode.*/bin/.*does not exist.*" + "stl_algo(base)?\\.h:[0-9:]* warning: loop not unrolled" "stl_deque.h:1051" "Tests/CMakeLib/testCTestResourceSpec.cxx:.*warning: missing initializer for member.*cmCTestResourceSpec::.*" # GCC 4.8 disagrees with later compilers on C++11 initializer list conversion "(Lexer|Parser).*warning.*conversion.*may (alter its value|change the sign)"
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index cb18bcc..4dc92b2 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst
@@ -188,6 +188,12 @@ cause the custom command to re-run whenever the target is recompiled. + .. versionchanged:: 4.4 + + Previously an executable target could be specified using the name + of the target with ``.exe`` appended. This is no longer allowed. + See policy :policy:`CMP0212`. + 2. If the argument is an absolute path, a file-level dependency is created on that path.
diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst index bbfcca2..919b107 100644 --- a/Help/command/add_test.rst +++ b/Help/command/add_test.rst
@@ -8,7 +8,8 @@ add_test(NAME <name> COMMAND <command> [<arg>...] [CONFIGURATIONS <config>...] [WORKING_DIRECTORY <dir>] - [COMMAND_EXPAND_LISTS]) + [COMMAND_EXPAND_LISTS] + [BUILD_DEPENDS <dependencies>...]) Adds a test called ``<name>``. The test name may contain arbitrary characters, expressed as a :ref:`Quoted Argument` or :ref:`Bracket Argument` @@ -59,6 +60,25 @@ <launcher> <emulator> <command> + * .. versionadded:: 4.4 + + When the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is enabled, + the :ref:`Ninja Generators` generate a convenience build target named + ``test_prep/<name>`` that depends on the test executable target. Building + this target ensures the executable is up-to-date before the test runs. + + Additionally, targets referenced by the test command via generator + expressions are added as dependencies of the ``test_prep/<name>`` target. + + If multiple tests in different directories share the same name, their + dependencies are merged into a single ``test_prep/<name>`` target. + + Tests with names that are not valid target names are excluded from this + behavior. + + The ``BUILD_DEPENDS`` keyword can be used to add explicit build + dependencies. + The command may be specified using :manual:`generator expressions <cmake-generator-expressions(7)>`. @@ -71,6 +91,15 @@ :variable:`CMAKE_CURRENT_BINARY_DIR`. The working directory may be specified using :manual:`generator expressions <cmake-generator-expressions(7)>`. +``BUILD_DEPENDS`` + .. versionadded:: 4.4 + + Specify a list of targets or files that must be built before the test can + run. Each dependency is added to the ``test_prep/<name>`` build target + described above when :variable:`CMAKE_TEST_BUILD_DEPENDS` is enabled + with the :ref:`Ninja Generators`. The test name must be a valid target name + in order to list build dependencies with this keyword. + ``COMMAND_EXPAND_LISTS`` .. versionadded:: 3.16
diff --git a/Help/command/block.rst b/Help/command/block.rst index 7b5b581..abdbd37 100644 --- a/Help/command/block.rst +++ b/Help/command/block.rst
@@ -7,7 +7,8 @@ .. code-block:: cmake - block([SCOPE_FOR [POLICIES] [VARIABLES]] [PROPAGATE <var-name>...]) + block([SCOPE_FOR [DIAGNOSTICS] [POLICIES] [VARIABLES]] + [PROPAGATE <var-name>...]) <commands> endblock() @@ -19,6 +20,13 @@ ``SCOPE_FOR`` Specify which scopes must be created. + ``DIAGNOSTICS`` + .. versionadded:: 4.4 + + Create a new diagnostic scope. This is equivalent to + :command:`cmake_diagnostic(PUSH)` with an automatic + :command:`cmake_diagnostic(POP)` when leaving the block scope. + ``POLICIES`` Create a new policy scope. This is equivalent to :command:`cmake_policy(PUSH)` with an automatic @@ -31,7 +39,7 @@ .. code-block:: cmake - block(SCOPE_FOR VARIABLES POLICIES) + block(SCOPE_FOR VARIABLES POLICIES DIAGNOSTICS) ``PROPAGATE`` When a variable scope is created by the :command:`block` command, this
diff --git a/Help/command/build_command.rst b/Help/command/build_command.rst index 21adab9..6bb7d58 100644 --- a/Help/command/build_command.rst +++ b/Help/command/build_command.rst
@@ -29,7 +29,7 @@ .. versionadded:: 3.21 The ``PARALLEL_LEVEL`` argument can be used to set the - :option:`--parallel <cmake--build --parallel>` flag. + :cmake-build-option:`--parallel` flag. .. code-block:: cmake @@ -39,7 +39,7 @@ compatibility. Use the first signature instead. It sets the given ``<cachevariable>`` to a command-line string as -above but without the :option:`--target <cmake--build --target>` option. +above but without the :cmake-build-option:`--target` option. The ``<makecommand>`` is ignored but should be the full path to devenv, nmake, make or one of the end user build tools for legacy invocations.
diff --git a/Help/command/cmake_diagnostic.rst b/Help/command/cmake_diagnostic.rst new file mode 100644 index 0000000..29b2589 --- /dev/null +++ b/Help/command/cmake_diagnostic.rst
@@ -0,0 +1,156 @@ +cmake_diagnostic +---------------- + +.. versionadded:: 4.4 + +Manage CMake Diagnostic settings. See the :manual:`cmake-diagnostics(7)` +manual for a list of available categories. + +Synopsis +^^^^^^^^ + +.. parsed-literal:: + + `Setting Diagnostics`_ + cmake_diagnostic(`SET`_ <category> <action> [RECURSE]) + cmake_diagnostic(`PROMOTE`_ <category> <action> [NO_RECURSE]) + cmake_diagnostic(`DEMOTE`_ <category> <action> [NO_RECURSE]) + + `Checking Diagnostic Actions`_ + cmake_diagnostic(`GET`_ <diagnostic> <out-var>) + + `CMake Diagnostic Stack`_ + cmake_diagnostic(`PUSH`_) + cmake_diagnostic(`POP`_) + +Setting Diagnostics +^^^^^^^^^^^^^^^^^^^ + +.. signature:: + cmake_diagnostic(SET CMD_<CATEGORY> <action> [RECURSE]) + cmake_diagnostic(PROMOTE CMD_<CATEGORY> <action> [NO_RECURSE]) + cmake_diagnostic(DEMOTE CMD_<CATEGORY> <action> [NO_RECURSE]) + :target: + SET + PROMOTE + DEMOTE + +Set or alter the action taken when a diagnostic belonging to a particular +category is triggered. + +The ``SET`` subcommand sets the action for the specified diagnostic category. +The ``PROMOTE`` subcommand increases the severity for the specified diagnostic +category, or does nothing if the action was already set to an equal or higher +severity. The ``DEMOTE`` subcommand decreases the severity for the specified +diagnostic category, or does nothing if the action was already set to an equal +or lower severity. + +The possible ``<action>``\ s (in order of severity) are: + +``IGNORE`` + Do nothing. + +``WARN`` + Report a warning and continue processing. + +``SEND_ERROR`` + Report an error, continue processing, but skip generation. + + The :manual:`cmake(1)` executable will return a non-zero + :ref:`exit code <CMake Exit Code>`. + +``FATAL_ERROR`` + Report an error, stop processing and generation. + + The :manual:`cmake(1)` executable will return a non-zero + :ref:`exit code <CMake Exit Code>`. + +Some diagnostic categories are hierarchical. The ``RECURSE`` and +``NO_RECURSE`` options determine whether changing the action for a diagnostic +category also modifies any child categories. By default, the ``PROMOTE`` and +``DEMOTE`` subcommands are recursive, while the ``SET`` subcommand is not. +Note that the alteration for child categories is independent of the prior +action set on any parents; that is, ``PROMOTE`` and ``DEMOTE``, when operating +recursively, will operate on all child categories even if a parent category's +action was not altered. + +Checking Diagnostic Actions +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. signature:: cmake_diagnostic(GET CMD_<CATEGORY> <variable>) + :target: GET + +Check what action is currently specified for a diagnostic category. +The output ``<variable>`` value will be one of ``IGNORE``, ``WARN``, +``SEND_ERROR`` or ``FATAL_ERROR``. + +CMake Diagnostic Stack +^^^^^^^^^^^^^^^^^^^^^^ + +CMake keeps diagnostic settings on a stack, so changes made by the +``cmake_diagnostic`` command affect only the top of the stack. A new entry on +the diagnostic stack is managed automatically for each subdirectory to protect +its parents and siblings. CMake also manages a new entry for scripts loaded by +:command:`include` and :command:`find_package` commands except when invoked +with the ``NO_DIAGNOSTIC_SCOPE`` option. The ``cmake_diagnostic`` command +provides an interface to manage custom entries on the diagnostic stack: + +.. signature:: cmake_diagnostic(PUSH) + + Create a new entry on the diagnostic stack. + +.. signature:: cmake_diagnostic(POP) + + Remove the last diagnostic stack entry created with + ``cmake_diagnostic(PUSH)``. + +Each ``PUSH`` must have a matching ``POP`` to erase any changes. +This is useful to make temporary changes to diagnostic settings. +Calls to the :command:`cmake_diagnostic(SET)`, +:command:`cmake_diagnostic(PROMOTE)`, or :command:`cmake_diagnostic(DEMOTE)` +commands influence only the current top of the diagnostic stack. + +The :command:`block(SCOPE_FOR DIAGNOSTICS)` command offers a more flexible +and more secure way to manage the diagnostic stack. The pop action is done +automatically when leaving the block scope, so there is no need to +precede each :command:`return` with a call to :command:`cmake_diagnostic(POP)`. + +.. code-block:: cmake + + # stack management with cmake_diagnostic() + function(my_func) + cmake_diagnostic(PUSH) + cmake_diagnostic(SET ...) + if (<cond1>) + ... + cmake_diagnostic(POP) + return() + elseif(<cond2>) + ... + cmake_diagnostic(POP) + return() + endif() + ... + cmake_diagnostic(POP) + endfunction() + + # stack management with block()/endblock() + function(my_func) + block(SCOPE_FOR DIAGNOSTICS) + cmake_diagnostic(SET ...) + if (<cond1>) + ... + return() + elseif(<cond2>) + ... + return() + endif() + ... + endblock() + endfunction() + +Commands created by the :command:`function` and :command:`macro` commands +record diagnostic settings when they are created and use the pre-record +diagnostics when they are invoked. If the function or macro implementation +sets diagnostics, the changes automatically propagate up through callers until +they reach the closest nested diagnostic stack entry.
diff --git a/Help/command/cmake_instrumentation.rst b/Help/command/cmake_instrumentation.rst index 9f55503..0ab67a5 100644 --- a/Help/command/cmake_instrumentation.rst +++ b/Help/command/cmake_instrumentation.rst
@@ -25,7 +25,7 @@ See :ref:`cmake-instrumentation API v1` for details. ``DATA_VERSION`` is a version value of the form ``major`` or ``major.minor``. -Currently, the only supported version is ``1.0``. See +Currently, the maximum supported version is ``1.1``. See :ref:`cmake-instrumentation Data Version` for details. Each of the optional keywords ``HOOKS``, ``OPTIONS``, and ``CALLBACK`` @@ -76,7 +76,7 @@ API_VERSION 1 DATA_VERSION 1.0 HOOKS postGenerate preCMakeBuild postCMakeBuild - OPTIONS staticSystemInformation dynamicSystemInformation trace + OPTIONS staticSystemInformation dynamicSystemInformation compileTrace trace CALLBACK ${CMAKE_COMMAND} -P /path/to/handle_data.cmake CALLBACK ${CMAKE_COMMAND} -P /path/to/handle_data_2.cmake CUSTOM_CONTENT myString STRING string @@ -92,7 +92,7 @@ "postGenerate", "preCMakeBuild", "postCMakeBuild" ], "options": [ - "staticSystemInformation", "dynamicSystemInformation", "trace" + "staticSystemInformation", "dynamicSystemInformation", "compileTrace", "trace" ], "callbacks": [ "/path/to/cmake -P /path/to/handle_data.cmake",
diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst index e3a30fa..c81aaac 100644 --- a/Help/command/cmake_language.rst +++ b/Help/command/cmake_language.rst
@@ -495,8 +495,6 @@ .. versionadded:: 3.25 -.. _query_message_log_level: - .. signature:: cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>) @@ -506,9 +504,8 @@ See :command:`message` for the possible logging levels. The current message logging level can be set either using the - :option:`--log-level <cmake --log-level>` - command line option of the :manual:`cmake(1)` program or using - the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable. + :cmake-option:`--log-level` command line option of the :manual:`cmake(1)` + program or using the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable. If both the command line option and the variable are set, the command line option takes precedence. If neither are set, the default logging level
diff --git a/Help/command/cmake_parse_arguments.rst b/Help/command/cmake_parse_arguments.rst index 441cbfa..e5505bc 100644 --- a/Help/command/cmake_parse_arguments.rst +++ b/Help/command/cmake_parse_arguments.rst
@@ -11,6 +11,9 @@ cmake_parse_arguments(PARSE_ARGV <N> <prefix> <options> <one_value_keywords> <multi_value_keywords>) + cmake_parse_arguments(PARSE_ARGN <prefix> <options> + <one_value_keywords> <multi_value_keywords>) + .. versionadded:: 3.5 This command is implemented natively. Previously, it has been defined in the module :module:`CMakeParseArguments`. @@ -30,6 +33,12 @@ the ``<N>``-th argument, where ``<N>`` is an unsigned integer. This allows for the values to have special characters like ``;`` in them. +.. versionadded:: 4.4 + The ``PARSE_ARGN`` signature is only for use in a :command:`function` + body. This starts parsing after the last named argument of the calling + function and works exactly like ``PARSE_ARGV`` with ``<N>`` being the number + of parameters in the function definition. + The ``<options>`` argument contains all options for the respective function or macro. These are keywords that have no value following them, like the ``OPTIONAL`` keyword of the :command:`install` command.
diff --git a/Help/command/cmake_path.rst b/Help/command/cmake_path.rst index 5fb0ef1..2a36325 100644 --- a/Help/command/cmake_path.rst +++ b/Help/command/cmake_path.rst
@@ -668,7 +668,7 @@ For reference, the algorithm used to compute the relative path is the same as that used by C++ `std::filesystem::path::lexically_relative - <https://en.cppreference.com/w/cpp/filesystem/path/lexically_normal>`_. + <https://en.cppreference.com/cpp/filesystem/path/lexically_normal>`_. See :ref:`Creating a Path Variable` for details on the output variable.
diff --git a/Help/command/create_test_sourcelist.rst b/Help/command/create_test_sourcelist.rst index d0b43c4..f3aca7a 100644 --- a/Help/command/create_test_sourcelist.rst +++ b/Help/command/create_test_sourcelist.rst
@@ -60,3 +60,44 @@ .. variable:: CMAKE_TESTDRIVER_AFTER_TESTMAIN Code to be placed directly after the call to each test's function. + +The generated test driver supports the following command-line arguments: + +``<name>`` + Run the test with the exact name ``<name>`` (case-insensitive). + +``-R <substr>`` + Run the first test whose name contains ``<substr>`` (case-insensitive). + +``-A [<skip_test>...]`` + .. versionadded:: 3.21 + + Run all tests and print results in `TAP <https://testanything.org/>`_ + format. + + Any additional arguments after ``-A`` are interpreted as exact test names + to skip. + +``-N`` + .. versionadded:: 4.4 + + List all available test names (one per line) and exit. + +Example +^^^^^^^ + +.. code-block:: cmake + + create_test_sourcelist(SRCS main.c test1.c test2.c) + add_executable(MyTests ${SRCS}) + discover_tests(COMMAND MyTests + DISCOVERY_ARGS -N + DISCOVERY_MATCH "^(.+)$" + TEST_NAME "${PROJECT_NAME}.\\1" + TEST_ARGS "\\1" + ) + +See Also +^^^^^^^^ + +* :command:`discover_tests`
diff --git a/Help/command/ctest_build.rst b/Help/command/ctest_build.rst index bce1739..c831ede 100644 --- a/Help/command/ctest_build.rst +++ b/Help/command/ctest_build.rst
@@ -15,14 +15,16 @@ [NUMBER_WARNINGS <num-warn-var>] [RETURN_VALUE <result-var>] [CAPTURE_CMAKE_ERROR <result-var>] + [PRESET <preset>] + [PRESETS_FILE <file>] ) Build the project and store results in ``Build.xml`` for submission with the :command:`ctest_submit` command. The :variable:`CTEST_BUILD_COMMAND` variable may be set to explicitly -specify the build command line. Otherwise the build command line is -computed automatically based on the options given. +specify the build command line when no preset is selected. Otherwise +the build command line is computed automatically based on the options given. The options are: @@ -75,6 +77,33 @@ ``NUMBER_WARNINGS <num-warn-var>`` Store the number of build warnings detected in the given variable. +``PRESET <preset>`` + .. versionadded:: 4.4 + + Specify a :manual:`preset <cmake-presets(7)>` to use when building the + project. Any value set in the CTest script will take priority over a + corresponding setting from the preset. For example, the ``TARGET`` + argument will override the :preset:`buildPresets.targets` setting from + the chosen preset. + + When a preset is specified, the :variable:`CTEST_BUILD_COMMAND` + variable is ignored so that the preset is always honored. + + See also the :variable:`CTEST_BUILD_PRESET` and + :variable:`CTEST_PRESET` variables. + +``PRESETS_FILE <file>`` + .. versionadded:: 4.4 + + Specify a :manual:`presets <cmake-presets(7)>` file to use instead of the + default ``CMakePresets.json`` in the source directory. + A relative path is interpreted relative to the source directory. + Has no effect unless a preset is selected via the ``PRESET`` argument + or the :variable:`CTEST_BUILD_PRESET` or :variable:`CTEST_PRESET` + variables. + + See also the :variable:`CTEST_PRESETS_FILE` variable. + ``RETURN_VALUE <result-var>`` Store the return value of the native build tool in the given variable.
diff --git a/Help/command/ctest_configure.rst b/Help/command/ctest_configure.rst index f23dd22..f68be71 100644 --- a/Help/command/ctest_configure.rst +++ b/Help/command/ctest_configure.rst
@@ -7,7 +7,10 @@ ctest_configure([BUILD <build-dir>] [SOURCE <source-dir>] [APPEND] [OPTIONS <options>] [RETURN_VALUE <result-var>] [QUIET] - [CAPTURE_CMAKE_ERROR <result-var>]) + [CAPTURE_CMAKE_ERROR <result-var>] + [PRESET <preset>] + [PRESETS_FILE <file>] + ) Configure the project build tree and record results in ``Configure.xml`` for submission with the :command:`ctest_submit` command. @@ -30,7 +33,36 @@ produced by a previous call to this command. ``OPTIONS <options>`` - Specify command-line arguments to pass to the configuration tool. + Specify a :ref:`semicolon-separated list <CMake Language Lists>` of + command-line arguments to pass to the configuration tool. + This option is ignored when :variable:`CTEST_CONFIGURE_COMMAND` is used. + +``PRESET <preset>`` + .. versionadded:: 4.4 + + Specify a :manual:`preset <cmake-presets(7)>` to use when configuring the + project. Any value set in the CTest script will take priority over a + corresponding setting from the preset. For example, the + :variable:`CTEST_BINARY_DIRECTORY` variable will override the + :preset:`configurePresets.binaryDir` setting from the chosen preset. + + When a preset is specified, the :variable:`CTEST_CONFIGURE_COMMAND` + variable is ignored so that the preset is always honored. + + See also the :variable:`CTEST_CONFIGURE_PRESET` and + :variable:`CTEST_PRESET` variables. + +``PRESETS_FILE <file>`` + .. versionadded:: 4.4 + + Specify a :manual:`presets <cmake-presets(7)>` file to use instead of the + default ``CMakePresets.json`` in the source directory. + A relative path is interpreted relative to the source directory. + Has no effect unless a preset is selected via the ``PRESET`` argument + or the :variable:`CTEST_CONFIGURE_PRESET` or :variable:`CTEST_PRESET` + variables. + + See also the :variable:`CTEST_PRESETS_FILE` variable. ``RETURN_VALUE <result-var>`` Store in the ``<result-var>`` variable the return value of the native @@ -39,8 +71,9 @@ ``CAPTURE_CMAKE_ERROR <result-var>`` .. versionadded:: 3.7 - Store in the ``<result-var>`` variable -1 if there are any errors running - the command and prevent ctest from returning non-zero if an error occurs. + Store in the ``<result-var>`` variable ``-1`` if there are any errors running + the command and prevent :manual:`ctest(1)` from returning non-zero if an + error occurs. ``QUIET`` .. versionadded:: 3.3
diff --git a/Help/command/ctest_coverage.rst b/Help/command/ctest_coverage.rst index 319c978..5a85007 100644 --- a/Help/command/ctest_coverage.rst +++ b/Help/command/ctest_coverage.rst
@@ -39,8 +39,9 @@ ``CAPTURE_CMAKE_ERROR <result-var>`` .. versionadded:: 3.7 - Store in the ``<result-var>`` variable -1 if there are any errors running - the command and prevent ctest from returning non-zero if an error occurs. + Store in the ``<result-var>`` variable ``-1`` if there are any errors running + the command and prevent :manual:`ctest(1)` from returning non-zero if an + error occurs. ``QUIET`` .. versionadded:: 3.3
diff --git a/Help/command/ctest_empty_binary_directory.rst b/Help/command/ctest_empty_binary_directory.rst index 5d26de1..1f91d0c 100644 --- a/Help/command/ctest_empty_binary_directory.rst +++ b/Help/command/ctest_empty_binary_directory.rst
@@ -1,7 +1,7 @@ ctest_empty_binary_directory ---------------------------- -empties the binary directory +Empties the binary directory. .. code-block:: cmake
diff --git a/Help/command/ctest_memcheck.rst b/Help/command/ctest_memcheck.rst index 4ca7364..7e91f4d 100644 --- a/Help/command/ctest_memcheck.rst +++ b/Help/command/ctest_memcheck.rst
@@ -26,6 +26,8 @@ [CAPTURE_CMAKE_ERROR <result-var>] [REPEAT <mode>:<n>] [OUTPUT_JUNIT <file>] + [PRESET <preset>] + [PRESETS_FILE <path>] [DEFECT_COUNT <defect-count-var>] [QUIET] )
diff --git a/Help/command/ctest_read_custom_files.rst b/Help/command/ctest_read_custom_files.rst index 53c093e..48b2060 100644 --- a/Help/command/ctest_read_custom_files.rst +++ b/Help/command/ctest_read_custom_files.rst
@@ -1,13 +1,13 @@ ctest_read_custom_files ----------------------- -read CTestCustom files. +Read CTestCustom files. .. code-block:: cmake ctest_read_custom_files(<directory>...) -Read all the CTestCustom.ctest or CTestCustom.cmake files from the +Read all the ``CTestCustom.ctest`` or ``CTestCustom.cmake`` files from the given directory. By default, invoking :manual:`ctest(1)` without a script will read custom
diff --git a/Help/command/ctest_run_script.rst b/Help/command/ctest_run_script.rst index 0d94eb0..734fa28 100644 --- a/Help/command/ctest_run_script.rst +++ b/Help/command/ctest_run_script.rst
@@ -1,14 +1,21 @@ ctest_run_script ---------------- -runs a :option:`ctest -S` script +Run a :option:`ctest -S` script. .. code-block:: cmake - ctest_run_script([NEW_PROCESS] script_file_name script_file_name1 - script_file_name2 ... [RETURN_VALUE var]) + ctest_run_script([NEW_PROCESS] + <script-file>... + [RETURN_VALUE <result-var>] + ) Runs a script or scripts much like if it was run from :option:`ctest -S`. -If ``NEW_PROCESS`` is specified then each script will be run in a separate -process. If ``RETURN_VALUE`` is specified the return value of the last script -run will be put into ``var``. +The options are: + +``NEW_PROCESS`` + Run each script in a separate process. + +``RETURN_VALUE <result-var>`` + Store in the ``<result-var>`` variable ``0`` for success and + non-zero on failure.
diff --git a/Help/command/ctest_sleep.rst b/Help/command/ctest_sleep.rst index 42b9768..723c2d4 100644 --- a/Help/command/ctest_sleep.rst +++ b/Help/command/ctest_sleep.rst
@@ -1,16 +1,15 @@ ctest_sleep ----------- -sleeps for some amount of time +Sleep for some amount of time. -.. code-block:: cmake - +.. signature:: ctest_sleep(<seconds>) -Sleep for given number of seconds. + Sleep for ``<seconds>`` seconds. -.. code-block:: cmake - +.. signature:: ctest_sleep(<time1> <duration> <time2>) -Sleep for t=(time1 + duration - time2) seconds if t > 0. + Sleep for ``<time1> + <duration> - <time2>`` seconds, if this sum is greater + than zero.
diff --git a/Help/command/ctest_start.rst b/Help/command/ctest_start.rst index 9c4e3dc..9184fac 100644 --- a/Help/command/ctest_start.rst +++ b/Help/command/ctest_start.rst
@@ -1,7 +1,7 @@ ctest_start ----------- -Starts the testing for a given model +Perform the :ref:`CTest Start Step` as a :ref:`Dashboard Client`. .. code-block:: cmake
diff --git a/Help/command/ctest_submit.rst b/Help/command/ctest_submit.rst index 56a47e8..e973663 100644 --- a/Help/command/ctest_submit.rst +++ b/Help/command/ctest_submit.rst
@@ -22,7 +22,9 @@ The options are: ``PARTS <part>...`` - Specify a subset of parts to submit. Valid part names are: + Specify a subset of parts to submit. Takes precedence over the + :variable:`CTEST_SUBMIT_PARTS` variable if both are set. + Valid part names are: * ``Start`` - nothing. * ``Update`` - :command:`ctest_update` results, in ``Update.xml``. @@ -83,8 +85,9 @@ ``CAPTURE_CMAKE_ERROR <result-var>`` .. versionadded:: 3.13 - Store in the ``<result-var>`` variable -1 if there are any errors running - the command and prevent ctest from returning non-zero if an error occurs. + Store in the ``<result-var>`` variable ``-1`` if there are any errors running + the command and prevent :manual:`ctest(1)` from returning non-zero if an + error occurs. ``QUIET`` .. versionadded:: 3.3
diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst index 8cf7134..e5a174c 100644 --- a/Help/command/ctest_test.rst +++ b/Help/command/ctest_test.rst
@@ -28,6 +28,8 @@ [CAPTURE_CMAKE_ERROR <result-var>] [REPEAT <mode>:<n>] [OUTPUT_JUNIT <file>] + [PRESET <preset>] + [PRESETS_FILE <file>] [QUIET] ) @@ -109,8 +111,8 @@ .. versionadded:: 3.29 - The ``<level>`` may be omitted, or ``0``, to let ctest use a default - level of parallelism, or unbounded parallelism, respectively, as + The ``<level>`` may be omitted, or ``0``, to let :manual:`ctest(1)` use a + default level of parallelism, or unbounded parallelism, respectively, as documented by the :option:`ctest --parallel` option. ``RESOURCE_SPEC_FILE <file>`` @@ -170,8 +172,9 @@ ``CAPTURE_CMAKE_ERROR <result-var>`` .. versionadded:: 3.7 - Store in the ``<result-var>`` variable -1 if there are any errors running - the command and prevent ctest from returning non-zero if an error occurs. + Store in the ``<result-var>`` variable ``-1`` if there are any errors running + the command and prevent :manual:`ctest(1)` from returning non-zero if an + error occurs. ``OUTPUT_JUNIT <file>`` .. versionadded:: 3.21 @@ -182,6 +185,39 @@ file is **not** uploaded to CDash because it would be redundant with CTest's ``Test.xml`` file. +``COVERAGE_TOOL <tool>`` + .. versionadded:: 4.4 + + Specify the ``<tool>`` used for collecting coverage during the running + of the tests. See the CTest :ref:`CoverageTool <ctest-CoverageTool>` + setting for details. + +``PRESET <preset>`` + .. versionadded:: 4.4 + + Specify a :manual:`preset <cmake-presets(7)>` to use when running tests. + Any value set in the CTest script will take priority over a corresponding + setting from the preset. For example, the ``INCLUDE`` argument will override + the :preset:`filter.include.name <testPresets.filter.include.name>` setting + from the chosen preset. + + This option also applies to the :command:`ctest_memcheck` command. + See also the :variable:`CTEST_TEST_PRESET` and + :variable:`CTEST_PRESET` variables. + +``PRESETS_FILE <file>`` + .. versionadded:: 4.4 + + Specify a :manual:`presets <cmake-presets(7)>` file to use instead of the + default ``CMakePresets.json`` in the source directory. + A relative path is interpreted relative to the source directory. + Has no effect unless a preset is selected via the ``PRESET`` argument + or the :variable:`CTEST_TEST_PRESET` or :variable:`CTEST_PRESET` + variables. + + This option also applies to the :command:`ctest_memcheck` command. + See also the :variable:`CTEST_PRESETS_FILE` variable. + ``QUIET`` .. versionadded:: 3.3 @@ -194,9 +230,9 @@ :variable:`CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE` and :variable:`CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION` variables, along with their corresponding :manual:`ctest(1)` command line options -:option:`--test-output-size-passed <ctest --test-output-size-passed>`, -:option:`--test-output-size-failed <ctest --test-output-size-failed>`, and -:option:`--test-output-truncation <ctest --test-output-truncation>`. +:ctest-option:`--test-output-size-passed`, +:ctest-option:`--test-output-size-failed`, and +:ctest-option:`--test-output-truncation`. .. _`Additional Test Measurements`:
diff --git a/Help/command/ctest_update.rst b/Help/command/ctest_update.rst index 836cdf1..37a261a 100644 --- a/Help/command/ctest_update.rst +++ b/Help/command/ctest_update.rst
@@ -7,6 +7,8 @@ ctest_update([SOURCE <source-dir>] [RETURN_VALUE <result-var>] + [VERSION_ONLY] + [VERSION_OVERRIDE <version>] [CAPTURE_CMAKE_ERROR <result-var>] [QUIET]) @@ -23,21 +25,39 @@ Store in the ``<result-var>`` variable the number of files updated or ``-1`` on error. +``VERSION_ONLY`` + .. versionadded:: 4.4 + + Record the currently checked out revision without updating the source tree to + a different version. This is equivalent to setting the + :variable:`CTEST_UPDATE_VERSION_ONLY` variable. + +``VERSION_OVERRIDE <version>`` + .. versionadded:: 4.4 + + Report ``<version>`` as the current revision without querying or updating the + source tree. This is equivalent to setting the + :variable:`CTEST_UPDATE_VERSION_OVERRIDE` variable, and supersedes + ``VERSION_ONLY``. + ``CAPTURE_CMAKE_ERROR <result-var>`` .. versionadded:: 3.13 - Store in the ``<result-var>`` variable -1 if there are any errors running - the command and prevent ctest from returning non-zero if an error occurs. + Store in the ``<result-var>`` variable ``-1`` if there are any errors running + the command and prevent :manual:`ctest(1)` from returning non-zero if an + error occurs. ``QUIET`` .. versionadded:: 3.3 - Tell CTest to suppress most non-error messages that it would - have otherwise printed to the console. CTest will still report + Suppress any CTest-specific non-error messages that would have otherwise + been printed to the console. CTest will still report the new revision of the repository and any conflicting files that were found. -The update always follows the version control branch currently checked -out in the source directory. See the :ref:`CTest Update Step` -documentation for information about variables that change the behavior -of ``ctest_update()``. +By default, the update follows the version control branch currently checked +out in the source directory. The ``VERSION_ONLY`` and +``VERSION_OVERRIDE`` options instead record revision information without +changing the source tree. See the :ref:`CTest Update Step` documentation +for information about further variables that change the behavior of +``ctest_update()``.
diff --git a/Help/command/ctest_upload.rst b/Help/command/ctest_upload.rst index 344979a..2c0f8b2 100644 --- a/Help/command/ctest_upload.rst +++ b/Help/command/ctest_upload.rst
@@ -13,14 +13,15 @@ Specify a list of files to be sent along with the build results to the dashboard server. +``CAPTURE_CMAKE_ERROR <result-var>`` + .. versionadded:: 3.7 + + Store in the ``<result-var>`` variable ``-1`` if there are any errors running + the command and prevent :manual:`ctest(1)` from returning non-zero if an + error occurs. + ``QUIET`` .. versionadded:: 3.3 Suppress any CTest-specific non-error output that would have been printed to the console otherwise. - -``CAPTURE_CMAKE_ERROR <result-var>`` - .. versionadded:: 3.7 - - Store in the ``<result-var>`` variable -1 if there are any errors running - the command and prevent ctest from returning non-zero if an error occurs.
diff --git a/Help/command/discover_tests.rst b/Help/command/discover_tests.rst new file mode 100644 index 0000000..6ef6b66 --- /dev/null +++ b/Help/command/discover_tests.rst
@@ -0,0 +1,119 @@ +discover_tests +-------------- + +.. versionadded:: 4.4 + +Register tests with names and properties discovered at test time by +:manual:`ctest(1)`. + +.. code-block:: cmake + + discover_tests(COMMAND <command> [<arg>...] [COMMAND_EXPAND_LISTS] + [CONFIGURATIONS <config>...] + DISCOVERY_ARGS <arg>... + DISCOVERY_MATCH <regex> + [DISCOVERY_PROPERTIES <key> <value> [<key> <value>]...] + TEST_NAME <replacement> + TEST_ARGS <replacement>... + [TEST_PROPERTIES <key> <replacement> [<key> <replacement>]...] + ) + +This command configures test discovery rather than defining a single test at +configure time. During test execution, :manual:`ctest(1)` runs the specified +discovery command, parses its output, and registers one or more tests based on +the provided regular expression and replacement strings. + +``discover_tests`` options are: + +``COMMAND`` + Specify the command-line used for test discovery. + + The command is executed by :manual:`ctest(1)` at test time (not by CMake at + configure time). With ``DISCOVERY_ARGS`` appended, it must print the list of + available tests in a format matched by ``DISCOVERY_MATCH``. + + If ``<command>`` specifies an executable target created by + :command:`add_executable`: + + * It will automatically be replaced by the location of the executable + created at build time. + + * The target's :prop_tgt:`CROSSCOMPILING_EMULATOR`, if set, will be + used to run the command on the host:: + + <emulator> <command> + + The emulator is used only when + :variable:`cross-compiling <CMAKE_CROSSCOMPILING>`. + + * The target's :prop_tgt:`TEST_LAUNCHER`, if set, will be used to launch the + command:: + + <launcher> <command> + + If the :prop_tgt:`CROSSCOMPILING_EMULATOR` is also set, both are used:: + + <launcher> <emulator> <command> + + The command may be specified using + :manual:`generator expressions <cmake-generator-expressions(7)>`. + +``COMMAND_EXPAND_LISTS`` + Lists in ``COMMAND`` arguments will be expanded, including those created with + :manual:`generator expressions <cmake-generator-expressions(7)>`. + +``CONFIGURATIONS`` + Restrict the test discovery only to the named configurations. + +``DISCOVERY_ARGS`` + Additional arguments passed to ``COMMAND`` when performing discovery. + +``DISCOVERY_MATCH`` + Regular expression used to parse each line produced by the discovery command. + Capturing groups may be referenced by ``TEST_NAME``, ``TEST_ARGS``, and + values in ``TEST_PROPERTIES`` using ``\1``, ``\2``, etc. + +``DISCOVERY_PROPERTIES`` + Specify properties for the discovery run itself. + +``TEST_NAME`` + Replacement string used to generate the test name for each discovered test. + It may reference capture groups from ``DISCOVERY_MATCH``. + +``TEST_ARGS`` + Replacement strings used to generate the arguments passed to the discovered + test. Each argument may reference capture groups from ``DISCOVERY_MATCH``. + +``TEST_PROPERTIES`` + Specify test properties to set on each discovered test. Values are + replacement strings and may reference capture groups from + ``DISCOVERY_MATCH``. + +CTest executes the discovery step to obtain the list of tests and then runs +each discovered test using the command-line produced by ``COMMAND`` together +with ``TEST_ARGS``. The pass/fail behavior of each discovered test follows +the usual CTest rules (exit code ``0`` indicates success unless inverted by +the :prop_test:`WILL_FAIL` property). Output written to stdout or stderr is +captured by :manual:`ctest(1)` and only affects the pass/fail status via the +:prop_test:`PASS_REGULAR_EXPRESSION`, :prop_test:`FAIL_REGULAR_EXPRESSION`, +or :prop_test:`SKIP_REGULAR_EXPRESSION` test properties. + +Example usage: + +.. code-block:: cmake + + discover_tests(COMMAND testDriver --exe $<TARGET_FILE:myexe> + DISCOVERY_ARGS --list-tests + DISCOVERY_MATCH "^([^,]+),([^,]+),([^,]+),(.*)$" + TEST_NAME "${PROJECT_NAME}.\\1.\\2" + TEST_ARGS --run-test "\\1.\\2" + TEST_PROPERTIES + PROCESSORS "\\3" + LABELS "\\4" + ) + +This example configures discovery by running ``testDriver --list-tests``. +For each line of output that matches ``DISCOVERY_MATCH``, a test name is +generated using ``TEST_NAME``, the per-test command-line is generated using +``TEST_ARGS``, and test properties are populated from the remaining capture +groups.
diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst index 37f2b42..429be72 100644 --- a/Help/command/execute_process.rst +++ b/Help/command/execute_process.rst
@@ -22,6 +22,8 @@ [OUTPUT_STRIP_TRAILING_WHITESPACE] [ERROR_STRIP_TRAILING_WHITESPACE] [ENCODING <name>] + [ENVIRONMENT <variable=value>...] + [ENVIRONMENT_MODIFICATION <env-mod-op>...] [ECHO_OUTPUT_VARIABLE] [ECHO_ERROR_VARIABLE] [COMMAND_ERROR_IS_FATAL <ANY|LAST|NONE>]) @@ -187,6 +189,27 @@ `UTF-8 RFC <https://datatracker.ietf.org/doc/html/rfc3629>`_ naming convention. +``ENVIRONMENT <variable=value>...`` + .. versionadded:: 4.4 + + Add environment variable definitions to the environment of the child + processes. Each entry must be of the form ``NAME=value`` and is interpreted + as setting ``NAME`` to ``value`` for the duration of the + ``execute_process()`` call. + + These entries are processed in order. If the same variable name is given + multiple times, the last value wins. + +``ENVIRONMENT_MODIFICATION <env-mod-op>...`` + .. versionadded:: 4.4 + + Apply one or more :prop_test:`ENVIRONMENT_MODIFICATION` operations to the + environment of the child processes. Each operation is applied in the given + order, and failures to parse an operation are treated as errors. + + Environment modifications are applied after ``ENVIRONMENT`` entries, if both + options are specified. + ``COMMAND_ERROR_IS_FATAL <ANY|LAST|NONE>`` .. versionadded:: 3.19
diff --git a/Help/command/export.rst b/Help/command/export.rst index 7b74205..775282f 100644 --- a/Help/command/export.rst +++ b/Help/command/export.rst
@@ -174,7 +174,7 @@ .. code-block:: cmake - export(SBOM <sbom-name> EXPORT <export-name> + export(SBOM <sbom-name> EXPORTS <export-names>... [FORMAT <string>] [PROJECT <project-name>|NO_PROJECT_METADATA] [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] @@ -189,9 +189,10 @@ Experimental. Gated by ``CMAKE_EXPERIMENTAL_GENERATE_SBOM``. Generates a software bill of materials (SBOM) document describing the targets -in the export ``<export-name>`` and their dependencies in the build tree. -Files are written to the ``sbom/<sbom-name>`` subdirectory of the current -build directory. +in the listed ``<export-names>`` export sets and their dependencies in the +build tree. Targets from all listed exports are aggregated into one document. +The generated SBOM will be written to the ``sbom/<sbom-name>`` subdirectory of +the current build directory. See :command:`install(SBOM)` for details about the supported SBOM formats and a description of the other options.
diff --git a/Help/command/file.rst b/Help/command/file.rst index 5bc7b3c..42b0871 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst
@@ -43,7 +43,7 @@ file(`COPY_FILE`_ <oldname> <newname> [...]) file({`COPY`_ | `INSTALL`_} <file>... DESTINATION <dir> [...]) file(`SIZE`_ <filename> <out-var>) - file(`READ_SYMLINK`_ <linkname> <out-var>) + file(`READ_SYMLINK`_ <linkname> <out-var> [...]) file(`CREATE_LINK`_ <original> <linkname> [...]) file(`CHMOD`_ <files>... <directories>... PERMISSIONS <permissions>... [...]) file(`CHMOD_RECURSE`_ <files>... <directories>... PERMISSIONS <permissions>... [...]) @@ -577,13 +577,21 @@ pointing to a file and is readable. .. signature:: - file(READ_SYMLINK <linkname> <variable>) + file(READ_SYMLINK <linkname> <variable> [RESULT <result>]) .. versionadded:: 3.14 Query the symlink ``<linkname>`` and stores the path it points to - in the result ``<variable>``. If ``<linkname>`` does not exist - or is not a symlink, CMake issues a fatal error. + in the result ``<variable>``. + + The options are: + + ``RESULT <result>`` + .. versionadded:: 4.4 + + Capture the status of the operation in a ``<result>`` variable. The + variable is set to ``0`` on success or an error message otherwise. + If not specified, and the operation fails, a fatal error is emitted. Note that this command returns the raw symlink path and does not resolve a relative path. The following is an example of how to ensure that an @@ -919,6 +927,7 @@ [FORMAT <format>] [COMPRESSION <compression> [COMPRESSION_LEVEL <compression-level>]] + [ENCODING <encoding>] [MTIME <mtime>] [THREADS <number>] [WORKING_DIRECTORY <dir>] @@ -1010,6 +1019,39 @@ formats too. The ``Zstd`` algorithm compression level can be set between 0-19, except for ``zip`` format. + ``ENCODING <encoding>`` + .. versionadded:: 4.4 + + Specify the pathname character encoding used in the archive. + + The ``<encoding>`` may be one of: + + ``UTF-8`` + Archive pathnames are encoded as UTF-8. + + In CMake 4.4 and below, this is the default. + See policy :policy:`CMP0213` for CMake 4.3 and below compatibility + details. + + ``OEM`` + On Windows platforms, pathnames are encoded as using the original + equipment manufacturer (OEM) code page. On non-Windows platforms, + pathnames are encoded according to the current locale. + + In CMake 4.3 and below, the ``OEM`` encoding (current locale) + was always used. + + ``UTF-16LE``, ``UTF-16BE`` + Archive pathnames are encoded as UTF-16 little-endian or big-endian. + + ``...`` + Any encoding name supported by ``iconv`` on the current platform. + On Windows, code page names may be specified. + + .. note:: + ``7zip`` archives always encode paths as ``UTF-16LE``, + so this option is silently ignored for that format. + ``MTIME <mtime>`` Specify the modification time recorded in tarball entries. @@ -1036,6 +1078,7 @@ file(ARCHIVE_EXTRACT INPUT <archive> [DESTINATION <dir>] + [ENCODING <encoding>] [PATTERNS <pattern>...] [LIST_ONLY] [VERBOSE] @@ -1054,6 +1097,41 @@ If ``DESTINATION`` is not given, the current binary directory will be used. + ``ENCODING <encoding>`` + .. versionadded:: 4.4 + + Specify the pathname character encoding used in the archive. + + The ``<encoding>`` may be one of: + + ``UTF-8`` + Archive pathnames are encoded as UTF-8. + + In CMake 4.3 and below, this is the default + if the :policy:`CMP0213` is ``NEW``. + + ``OEM`` + On Windows platforms, pathnames are encoded as using the original + equipment manufacturer (OEM) code page. On non-Windows platforms, + pathnames are encoded according to the current locale. + + In CMake 4.3 and below, the ``OEM`` encoding (current locale) + was always used. + + In CMake 4.4 and above, the ``OEM`` encoding (current locale) + is only used if the :policy:`CMP0213` is not ``NEW``. + + ``UTF-16LE``, ``UTF-16BE`` + Archive pathnames are encoded as UTF-16 little-endian or big-endian. + + ``...`` + Any encoding name supported by ``iconv`` on the current platform. + On Windows, code page names may be specified. + + .. note:: + ``7zip`` archives always encode paths as ``UTF-16LE``, + so this option is silently ignored for that format. + ``PATTERNS <pattern>...`` Extract/list only files and directories that match one of the given patterns. Wildcards are supported. If the ``PATTERNS`` option is
diff --git a/Help/command/if.rst b/Help/command/if.rst index c12ec12..23f5092 100644 --- a/Help/command/if.rst +++ b/Help/command/if.rst
@@ -41,19 +41,22 @@ 1. `Parentheses`_. -2. Unary tests such as `COMMAND`_, `POLICY`_, `TARGET`_, `TEST`_, - `EXISTS`_, `IS_READABLE`_, `IS_WRITABLE`_, `IS_EXECUTABLE`_, - `IS_DIRECTORY`_, `IS_SYMLINK`_, `IS_ABSOLUTE`_, and `DEFINED`_. +2. Unary tests such as: -3. Binary tests such as `EQUAL`_, `LESS`_, `LESS_EQUAL`_, `GREATER`_, - `GREATER_EQUAL`_, `STREQUAL`_, `STRLESS`_, `STRLESS_EQUAL`_, - `STRGREATER`_, `STRGREATER_EQUAL`_, `VERSION_EQUAL`_, `VERSION_LESS`_, - `VERSION_LESS_EQUAL`_, `VERSION_GREATER`_, `VERSION_GREATER_EQUAL`_, - `PATH_EQUAL`_, `IN_LIST`_, `IS_NEWER_THAN`_, and `MATCHES`_. + * The `Existence Checks`_ :cref:`COMMAND`, :cref:`DEFINED`, + :cref:`DIAGNOSTIC`, :cref:`EXISTS`, :cref:`POLICY`, :cref:`TARGET`, and + :cref:`TEST`. + * The `File Operations`_ :cref:`IS_READABLE`, :cref:`IS_WRITABLE`, + :cref:`IS_EXECUTABLE`, :cref:`IS_DIRECTORY`, :cref:`IS_SYMLINK`, and + :cref:`IS_ABSOLUTE`. -4. Unary logical operator `NOT`_. +3. Binary tests such as those described in `Comparisons`_, + `Version Comparisons`_, and `Path Comparisons`_, as well as :cref:`IN_LIST` + and :cref:`IS_NEWER_THAN`. -5. Binary logical operators `AND`_ and `OR`_, from left to right, +4. Unary logical operator :cref:`NOT`. + +5. Binary logical operators :cref:`AND` and :cref:`OR`, from left to right, without any short-circuit. Basic Expressions @@ -91,6 +94,11 @@ to ``NEW`` and the string's value happens to be a variable name that is affected by :policy:`CMP0054`'s behavior. +.. signature:: if() + :target: empty + + False if no argument is provided. + Logic Operators """"""""""""""" @@ -124,6 +132,12 @@ True if the given name is a command, macro or function that can be invoked. +.. signature:: if(DIAGNOSTIC <category>) + + .. versionadded:: 4.4 + + True if the given name is an existing diagnostic category. + .. signature:: if(POLICY <policy-id>) True if the given name is an existing policy (of the form ``CMP<NNNN>``). @@ -181,7 +195,7 @@ False if the given path is an empty string. .. note:: - Prefer ``if(IS_READABLE)`` to check file readability. ``if(EXISTS)`` + Prefer :command:`if(IS_READABLE)` to check file readability. ``if(EXISTS)`` may be changed in the future to only check file existence. .. signature:: if(IS_READABLE <path-to-file-or-directory>) @@ -228,7 +242,7 @@ time stamps are exactly the same, an ``IS_NEWER_THAN`` comparison returns true, so that any dependent build operations will occur in the event of a tie. This includes the case of passing the same file name for - both file1 and file2. + both ``file1`` and ``file2``. .. signature:: if(IS_DIRECTORY <path>) @@ -262,10 +276,8 @@ :target: MATCHES True if the given string or variable's value matches the given regular - expression. See :ref:`Regex Specification` for regex format. - - .. versionadded:: 2.6 - ``()`` groups are captured in :variable:`CMAKE_MATCH_<n>` variables. + expression. See :ref:`Regex Specification` for regex format. ``()`` groups + are captured in :variable:`CMAKE_MATCH_<n>` variables. .. signature:: if(<variable|string> LESS <variable|string>) :target: LESS @@ -415,7 +427,7 @@ ... endif() - See :ref:`cmake_path(COMPARE) <Path Comparison>` for more details. + See :command:`cmake_path(COMPARE)` for more details. Variable Expansion ^^^^^^^^^^^^^^^^^^ @@ -452,40 +464,40 @@ Automatic evaluation applies in the other cases whenever the above-documented condition syntax accepts ``<variable|string>``: -* The left hand argument to `MATCHES`_ is first checked to see if it is +* The left hand argument to :cref:`MATCHES` is first checked to see if it is a defined variable. If so, the variable's value is used, otherwise the original value is used. -* If the left hand argument to `MATCHES`_ is missing it returns false +* If the left hand argument to :cref:`MATCHES` is missing it returns false without error -* Both left and right hand arguments to `LESS`_, `GREATER`_, `EQUAL`_, - `LESS_EQUAL`_, and `GREATER_EQUAL`_, are independently tested to see if - they are defined variables. If so, their defined values are used otherwise - the original value is used. +* Both left and right hand arguments to :cref:`LESS`, :cref:`GREATER`, + :cref:`EQUAL`, :cref:`LESS_EQUAL`, and :cref:`GREATER_EQUAL` are + independently tested to see if they are defined variables. If so, their + defined values are used; otherwise the original value is used. -* Both left and right hand arguments to `STRLESS`_, `STRGREATER`_, - `STREQUAL`_, `STRLESS_EQUAL`_, and `STRGREATER_EQUAL`_ are independently - tested to see if they are defined variables. If so, their defined values are - used otherwise the original value is used. +* Both left and right hand arguments to :cref:`STRLESS`, :cref:`STRGREATER`, + :cref:`STREQUAL`, :cref:`STRLESS_EQUAL`, and :cref:`STRGREATER_EQUAL` are + independently tested to see if they are defined variables. If so, their + defined values are used; otherwise the original value is used. -* Both left and right hand arguments to `VERSION_LESS`_, - `VERSION_GREATER`_, `VERSION_EQUAL`_, `VERSION_LESS_EQUAL`_, and - `VERSION_GREATER_EQUAL`_ are independently tested to see if they are defined - variables. If so, their defined values are used otherwise the original value - is used. +* Both left and right hand arguments to :cref:`VERSION_LESS`, + :cref:`VERSION_GREATER`, :cref:`VERSION_EQUAL`, :cref:`VERSION_LESS_EQUAL`, + and :cref:`VERSION_GREATER_EQUAL` are independently tested to see if they are + defined variables. If so, their defined values are used; otherwise the + original value is used. -* The left hand argument to `IN_LIST`_ is tested to see if it is a defined - variable. If so, the variable's value is used, otherwise the original +* The left hand argument to :cref:`IN_LIST` is tested to see if it is a defined + variable. If so, the variable's value is used; otherwise the original value is used. -* The right hand argument to `NOT`_ is tested to see if it is a boolean - constant. If so, the value is used, otherwise it is assumed to be a +* The right hand argument to :cref:`NOT` is tested to see if it is a boolean + constant. If so, the value is used; otherwise it is assumed to be a variable and it is dereferenced. -* The left and right hand arguments to `AND`_ and `OR`_ are independently - tested to see if they are boolean constants. If so, they are used as - such, otherwise they are assumed to be variables and are dereferenced. +* The left and right hand arguments to :cref:`AND` and :cref:`OR` are + independently tested to see if they are boolean constants. If so, they are + used as such, otherwise they are assumed to be variables and are dereferenced. .. versionchanged:: 3.1 To prevent ambiguity, potential variable or keyword names can be
diff --git a/Help/command/include.rst b/Help/command/include.rst index 80968da..9e87db0 100644 --- a/Help/command/include.rst +++ b/Help/command/include.rst
@@ -6,7 +6,7 @@ .. code-block:: cmake include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>] - [NO_POLICY_SCOPE]) + [NO_POLICY_SCOPE] [NO_DIAGNOSTIC_SCOPE]) Loads and runs CMake code from the file given. Variable reads and writes access the scope of the caller (dynamic scoping). If ``OPTIONAL`` @@ -23,3 +23,6 @@ See the :command:`cmake_policy` command documentation for discussion of the ``NO_POLICY_SCOPE`` option. + +See the :command:`cmake_diagnostic` command documentation for discussion of the +``NO_DIAGNOSTIC_SCOPE`` option.
diff --git a/Help/command/include/SUPPORTED_LANGUAGES.rst b/Help/command/include/SUPPORTED_LANGUAGES.rst index a98c07a..2a83835 100644 --- a/Help/command/include/SUPPORTED_LANGUAGES.rst +++ b/Help/command/include/SUPPORTED_LANGUAGES.rst
@@ -1,25 +1,59 @@ +The supported languages are: -Supported languages are ``C``, ``CXX`` (i.e. C++), ``CSharp`` (i.e. C#), ``CUDA``, -``OBJC`` (i.e. Objective-C), ``OBJCXX`` (i.e. Objective-C++), ``Fortran``, ``HIP``, -``ISPC``, ``Swift``, ``ASM``, ``ASM_NASM``, ``ASM_MARMASM``, ``ASM_MASM``, and ``ASM-ATT``. +``C`` +``CXX`` + C++ + +``CSharp`` .. versionadded:: 3.8 - Added ``CSharp`` and ``CUDA`` support. - .. versionadded:: 3.15 - Added ``Swift`` support. + C# +``CUDA`` + .. versionadded:: 3.8 + +``OBJC`` .. versionadded:: 3.16 - Added ``OBJC`` and ``OBJCXX`` support. - .. versionadded:: 3.18 - Added ``ISPC`` support. + Objective-C +``OBJCXX`` + .. versionadded:: 3.16 + + Objective-C++ + +``Fortran`` + +``HIP`` .. versionadded:: 3.21 - Added ``HIP`` support. +``ISPC`` + .. versionadded:: 3.18 + +``Swift`` + .. versionadded:: 3.15 + +``ASM`` + Assembly language supported by the C compiler. + + If enabling ``ASM``, list it last so that CMake can check + whether the ``C`` or ``CXX`` compiler supports assembly. + +``ASM_NASM`` + Netwide Assembler + +``ASM_MARMASM`` .. versionadded:: 3.26 - Added ``ASM_MARMASM`` support. -If enabling ``ASM``, list it last so that CMake can check whether -compilers for other languages like ``C`` work for assembly too. + Microsoft Assembler (ARM, ARM64) + +``ASM_MASM`` + Microsoft Assembler (x86, x64) + +``ASM_POASM`` + .. versionadded:: 4.4 + + Pelles C toolchain assembler. + +``ASM-ATT``
diff --git a/Help/command/install.rst b/Help/command/install.rst index f7fcb94..75016b0 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst
@@ -60,6 +60,10 @@ ``<dir>`` should be a relative path. An absolute path is allowed, but not recommended. + .. versionadded:: 4.4 + The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be + enabled to warn or error out when an absolute destination is provided. + When a relative path is given, it is interpreted relative to the value of the :variable:`CMAKE_INSTALL_PREFIX` variable. The prefix can be relocated at install time using the ``DESTDIR`` @@ -201,6 +205,8 @@ - DLLs (these go to ``RUNTIME``, see below), - on macOS when marked as ``FRAMEWORK`` (see below). + * *Module libraries* + ``RUNTIME`` Target artifacts of this kind include: @@ -243,7 +249,10 @@ File sets are defined by the :command:`target_sources(FILE_SET)` command. If the file set ``<set-name>`` exists and is ``PUBLIC`` or ``INTERFACE``, - any files in the set are installed under the destination (see below). + any files in the set of type ``HEADERS`` are installed under + the destination (see below). Other types do not have any default + destination, so ``DESTINATION`` option must be specified for each + ``FILE_SET``. The directory structure relative to the file set's base directories is preserved. For example, a file added to the file set as ``/blah/include/myproj/here.h`` with a base directory ``/blah/include`` @@ -263,12 +272,13 @@ ``DESTINATION`` is omitted, a default destination will be taken from the appropriate variable from :module:`GNUInstallDirs`, or set to a built-in default value if that variable is not defined. The same is true for file - sets, and the public and private headers associated with the installed - targets through the :prop_tgt:`PUBLIC_HEADER` and :prop_tgt:`PRIVATE_HEADER` - target properties. A destination must always be provided for module libraries, - Apple bundles and frameworks. A destination can be omitted for interface and - object libraries, but they are handled differently (see the discussion of this - topic toward the end of this section). + sets of type ``HEADERS``, and the public and private headers associated with + the installed targets through the :prop_tgt:`PUBLIC_HEADER` and + :prop_tgt:`PRIVATE_HEADER` target properties. A destination must always be + provided for module libraries, Apple bundles and frameworks. A destination + can be omitted for interface and object libraries, but they are handled + differently (see the discussion of this topic toward the end of this + section). For shared libraries on DLL platforms, if neither ``RUNTIME`` nor ``ARCHIVE`` destinations are specified, both the ``RUNTIME`` and ``ARCHIVE`` components are @@ -1203,7 +1213,7 @@ .. code-block:: cmake - install(SBOM <sbom-name> EXPORT <export-name> + install(SBOM <sbom-name> EXPORTS <export-names>... [PROJECT <project-name>|NO_PROJECT_METADATA] [DESTINATION <dir>] [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] @@ -1222,9 +1232,11 @@ the interface is designed to allow additional SBOM formats or schema versions to be supported in future CMake releases. - Target installations are associated with the export ``<export-name>`` - using the ``EXPORT`` option of the :command:`install(TARGETS)` signature - documented above. If ``DESTINATION`` is not specified, a platform-specific + Target installations are associated with each export ``<export-names>`` + in the ``EXPORTS`` list using the ``EXPORTS`` option of the + :command:`install(TARGETS)` signature documented above. A single SBOM may + cover multiple export sets; targets from all listed exports are aggregated + into one document. If ``DESTINATION`` is not specified, a platform-specific default is used. Several options may be used to specify package metadata:
diff --git a/Help/command/list.rst b/Help/command/list.rst index abae8d2..a72667d 100644 --- a/Help/command/list.rst +++ b/Help/command/list.rst
@@ -19,7 +19,7 @@ `Modification`_ list(`APPEND`_ <list> [<element>...]) - list(`FILTER`_ <list> {INCLUDE | EXCLUDE} REGEX <regex>) + list(`FILTER`_ <list> <INCLUDE|EXCLUDE> <MODE>) list(`INSERT`_ <list> <index> [<element>...]) list(`POP_BACK`_ <list> [<out-var>...]) list(`POP_FRONT`_ <list> [<out-var>...]) @@ -120,15 +120,54 @@ that empty list. .. signature:: - list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>) + list(FILTER <list> <INCLUDE|EXCLUDE> <MODE>) -.. versionadded:: 3.6 + .. versionadded:: 3.6 -Includes or removes items from the list that match the mode's pattern. -In ``REGEX`` mode, items will be matched against the given regular expression. + Includes or removes items from the list that match the mode's pattern. -For more information on regular expressions look under -:ref:`string(REGEX) <Regex Specification>`. + ``<MODE>`` must be one of the following: + + ``REGEX`` + Items will be matched against the given regular expression. + + .. code-block:: cmake + + list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>) + + For more information on regular expressions look under + :ref:`string(REGEX) <Regex Specification>`. + + ``PREDICATE`` + Specify a user-defined :command:`function` as a predicate. + + .. code-block:: cmake + + list(FILTER <list> <INCLUDE|EXCLUDE> PREDICATE <function>) + + .. versionadded:: 4.4 + + ``<function>`` is a user-defined :command:`function` that acts as a + unary predicate. The function must accept exactly two parameters: the + input value and the name of an output variable. The function must set the + output variable to a boolean value in the calling scope. + The output variable is interpreted using standard CMake boolean evaluation. + If the function does not set the output variable, it is an error. + + Example: + + .. code-block:: cmake + + function(file_exists path result) + if(EXISTS "${path}") + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() + endfunction() + + set(candidate_files main.c missing.c utils.c) + list(FILTER candidate_files INCLUDE PREDICATE file_exists) .. signature:: list(INSERT <list> <element_index> <element> [<element> ...]) @@ -203,8 +242,7 @@ the other ones will remain the same as before the transformation. ``<ACTION>`` specifies the action to apply to the elements of the list. - The actions have exactly the same semantics as sub-commands of the - :command:`string` command. ``<ACTION>`` must be one of the following: + ``<ACTION>`` must be one of the following: :command:`APPEND <string(APPEND)>`, :command:`PREPEND <string(PREPEND)>` Append, prepend specified value to each element of the list. @@ -251,6 +289,45 @@ element instead of the beginning of each repeated search. See policy :policy:`CMP0186`. + ``APPLY`` + Invoke a user-defined :command:`function` for each element of the list. + The function must accept exactly two parameters: the input value and the + name of an output variable. The function must set the output variable + in the calling scope. + + .. signature:: + list(TRANSFORM <list> APPLY <function> ...) + :target: TRANSFORM_APPLY + + .. versionadded:: 4.4 + + ``<function>`` is a :command:`function` with exactly two formal parameters. + Set the output variable via + :command:`set(\<variable\> \<value\> PARENT_SCOPE) <set>`: + + .. code-block:: cmake + + function(<function> <input> <output>) + # Transform <input>, store result in ${<output>} + set(${<output>} "<result>" PARENT_SCOPE) + endfunction() + + Before each invocation, the output variable is unset in the calling scope + to prevent stale values. + + Example: + + .. code-block:: cmake + + function(make_absolute in out) + cmake_path(ABSOLUTE_PATH in BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + set(${out} "${in}" PARENT_SCOPE) + endfunction() + + set(mylist main.c utils.c io.c) + list(TRANSFORM mylist APPLY make_absolute) + # mylist is now absolute paths relative to CMAKE_CURRENT_SOURCE_DIR + ``<SELECTOR>`` determines which elements of the list will be transformed. Only one type of selector can be specified at a time. When given, ``<SELECTOR>`` must be one of the following: @@ -278,6 +355,41 @@ list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...) + ``PREDICATE`` + Specify a user-defined :command:`function` as a predicate. + Only elements for which the function returns a true value will be + transformed. + + .. code-block:: cmake + + list(TRANSFORM <list> <ACTION> PREDICATE <function> ...) + + .. versionadded:: 4.4 + + ``<function>`` is a user-defined :command:`function` with exactly + two formal parameters: the input value and the name of an output + variable. The function must set the output variable to a boolean + value. Standard CMake boolean evaluation is used. + If the function does not set the output variable, it is an error. + The function is evaluated for each element in turn, immediately + before that element would be transformed. + + Example: + + .. code-block:: cmake + + function(is_relative path result) + if(NOT IS_ABSOLUTE "${path}") + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() + endfunction() + + set(search_paths /usr/include src lib /opt/lib) + list(TRANSFORM search_paths PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/" + PREDICATE is_relative) + Ordering ^^^^^^^^ @@ -289,6 +401,7 @@ .. signature:: list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>]) + list(SORT <list> COMPARATOR <function> [CASE <case>] [ORDER <order>]) Sorts the list in-place alphabetically. @@ -337,3 +450,47 @@ ``DESCENDING`` Sorts the list in descending order. + + Instead of the built-in ``COMPARE`` methods, a user-defined comparison + :command:`function` may be used with the ``COMPARATOR`` keyword. + + .. versionadded:: 4.4 + + ``COMPARATOR`` is mutually exclusive with ``COMPARE``. The ``CASE`` + and ``ORDER`` options may still be used alongside ``COMPARATOR``: + the ``CASE`` filter is applied to both values before they are passed to + the function, and ``ORDER DESCENDING`` reverses the comparison by + swapping the two arguments. + + The function must accept exactly three parameters: two input values and + the name of an output variable. It must set the output variable to a + boolean value (``TRUE`` if the first value should come before the second, + ``FALSE`` otherwise) in the calling scope. + If the function does not set the output variable, it is an error. + + The comparator must define a + `strict weak ordering <https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings>`_. + In particular: + + * It must return ``FALSE`` when comparing an element to itself + (irreflexivity). + * If it returns ``TRUE`` for ``(a, b)``, it must return ``FALSE`` + for ``(b, a)`` (asymmetry). + + An error is raised if the comparator violates these requirements. + + Example: + + .. code-block:: cmake + + function(version_less a b result) + if("${a}" VERSION_LESS "${b}") + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() + endfunction() + + set(versions 3.1 1.2 2.0 1.10) + list(SORT versions COMPARATOR version_less) + # versions is now: 1.2;1.10;2.0;3.1
diff --git a/Help/command/message.rst b/Help/command/message.rst index e8a4ea0..a25a5a6 100644 --- a/Help/command/message.rst +++ b/Help/command/message.rst
@@ -44,12 +44,10 @@ CMake Warning, continue processing. ``AUTHOR_WARNING`` - CMake Warning (dev), continue processing. + CMake :diagnostic:`CMD_AUTHOR` diagnostic. ``DEPRECATION`` - CMake Deprecation Error or Warning if variable - :variable:`CMAKE_ERROR_DEPRECATED` or :variable:`CMAKE_WARN_DEPRECATED` - is enabled, respectively, else no message. + CMake :diagnostic:`CMD_DEPRECATED` diagnostic. (none) or ``NOTICE`` Important message printed to stderr to attract user's attention.
diff --git a/Help/command/project.rst b/Help/command/project.rst index ad4f32e..0646116 100644 --- a/Help/command/project.rst +++ b/Help/command/project.rst
@@ -175,7 +175,7 @@ Selects which programming languages are needed to build the project. -.. include:: include/SUPPORTED_LANGUAGES.rst + .. include:: include/SUPPORTED_LANGUAGES.rst By default ``C`` and ``CXX`` are enabled if no language options are given. Specify language ``NONE``, or use the ``LANGUAGES`` keyword and list no languages,
diff --git a/Help/command/string.rst b/Help/command/string.rst index b7ed69a..6564ec5 100644 --- a/Help/command/string.rst +++ b/Help/command/string.rst
@@ -60,6 +60,8 @@ string(JSON <out-var> [ERROR_VARIABLE <error-var>] `EQUAL <JSON-EQUAL_>`__ <json-string1> <json-string2>) string(JSON <out-var> [ERROR_VARIABLE <error-var>] + `PARTIAL_EQUAL <JSON-EQUAL_>`__ <json-string1> <json-string2>) + string(JSON <out-var> [ERROR_VARIABLE <error-var>] `STRING_ENCODE <STRING-ENCODE_>`__ <string>) Search and Replace @@ -639,6 +641,42 @@ .. signature:: string(JSON <out-var> [ERROR_VARIABLE <error-var>] + PARTIAL_EQUAL <json-pattern> <json-actual>) + :target: JSON-PARTIAL_EQUAL + + .. versionadded:: 4.4 + + Compare two JSON values using *partial* equality. The first argument + (``<json-pattern>``) is treated as a pattern to match against the second + argument (``<json-actual>``), which is treated as the complete JSON value. + The contents of ``<json-pattern>`` and ``<json-actual>`` must be valid JSON. + + The ``<out-var>`` will be set to a true value if ``<json-pattern>`` is + contained within ``<json-actual>`` according to the rules below, or a false + value otherwise. + + Partial equality is not symmetric. In general, + ``PARTIAL_EQUAL(A, B)`` and ``PARTIAL_EQUAL(B, A)`` may produce different + results. + + Matching rules: + + * **Scalars** (null, number, string, boolean) must match exactly. + * **Objects** match if every member in ``<json-pattern>`` exists in + ``<json-actual>`` and the corresponding values also match recursively. + Extra members in ``<json-actual>`` are ignored. + * **Arrays** match if every element in ``<json-pattern>`` matches a + distinct element in ``<json-actual>`` recursively. Once an element in + ``<json-actual>`` successfully matches an element in ``<json-pattern>``, + it is consumed and cannot be reused to match any subsequent elements. + Array element order is not significant. Extra elements in + ``<json-actual>`` are ignored. + + Type differences do not match (for example, an object does not match an + array, and a string does not match a number). + +.. signature:: + string(JSON <out-var> [ERROR_VARIABLE <error-var>] STRING_ENCODE <string>) :target: STRING-ENCODE
diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst index 4ef1f50..e31fdb7 100644 --- a/Help/command/target_sources.rst +++ b/Help/command/target_sources.rst
@@ -77,12 +77,30 @@ Adds a file set to a target, or adds files to an existing file set. Targets have zero or more named file sets. Each file set has a name, a type, a scope of ``INTERFACE``, ``PUBLIC``, or ``PRIVATE``, one or more base directories, and -files within those directories. The acceptable types include: +files within those directories. + +.. versionchanged:: 4.4 + A file may belong to at most one non-``HEADERS`` file set in a target. + See policy :policy:`CMP0211`. + + The acceptable types include: ``HEADERS`` Sources intended to be used via a language's ``#include`` mechanism. +``SOURCES`` + .. versionadded:: 4.4 + + Specifies sources to use when building a target and/or its dependents. + With the scope ``PRIVATE`` and ``PUBLIC``, ``<files>`` will populate the + :prop_fs:`SOURCES` property of ``<set>``, which are used when building the + target itself. With the scope ``PUBLIC`` and ``INTERFACE``, ``<files>`` will + populate the :prop_fs:`INTERFACE_SOURCES` property of ``<set>``, which are + used when building dependents. The sources specified by the + :prop_fs:`INTERFACE_SOURCES` property are propagated, transitively, to all + the dependents. + ``CXX_MODULES`` .. versionadded:: 3.28 @@ -91,7 +109,8 @@ ``INTERFACE`` scope except on ``IMPORTED`` targets. The optional default file sets are named after their type. The target may not -be a custom target or :prop_tgt:`FRAMEWORK` target. +be a custom target or, for ``HEADERS`` and ``CXX_MODULES`` types, a +:prop_tgt:`FRAMEWORK` target. Files in a ``PRIVATE`` or ``PUBLIC`` file set are marked as source files for the purposes of IDE integration. Additionally, files in ``HEADERS`` file sets @@ -100,8 +119,8 @@ :command:`install(TARGETS)` command, and exported with the :command:`install(EXPORT)` and :command:`export` commands. -Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, or -``PRIVATE`` and accepts the following arguments: +Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, +or ``PRIVATE`` and accepts the following arguments: ``FILE_SET <set>`` @@ -158,6 +177,15 @@ * :prop_tgt:`HEADER_DIRS` * :prop_tgt:`HEADER_DIRS_<NAME>` +For file sets of type ``SOURCES``: + +* :prop_tgt:`SOURCE_SETS` +* :prop_tgt:`INTERFACE_SOURCE_SETS` +* :prop_tgt:`SOURCE_SET` +* :prop_tgt:`SOURCE_SET_<NAME>` +* :prop_tgt:`SOURCE_DIRS` +* :prop_tgt:`SOURCE_DIRS_<NAME>` + For file sets of type ``CXX_MODULES``: * :prop_tgt:`CXX_MODULE_SETS`
diff --git a/Help/cpack_gen/archive.rst b/Help/cpack_gen/archive.rst index 21e67ce..3560bed 100644 --- a/Help/cpack_gen/archive.rst +++ b/Help/cpack_gen/archive.rst
@@ -217,3 +217,18 @@ It is selected automatically by the archive library backend and not directly set by CMake itself. The default compression level may vary between archive formats, platforms, etc. + +.. variable:: CPACK_ARCHIVE_ENCODING + + .. versionadded:: 4.4 + + Specify the pathname character encoding used in package archives. + + :Default: ``UTF-8`` + + See the :option:`cmake -E tar <cmake-E tar>` tool's + :option:`--cmake-tar-encoding <cmake-E_tar --cmake-tar-encoding>` flag + for supported encoding names. + + In CMake 4.3 and below, the ``OEM`` encoding (current locale) + was always used.
diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst index d738804..c3f60c6 100644 --- a/Help/cpack_gen/deb.rst +++ b/Help/cpack_gen/deb.rst
@@ -290,13 +290,13 @@ Possible values are: ``lzma`` - Lempel–Ziv–Markov chain algorithm + Lempel-Ziv-Markov chain algorithm ``xz`` - XZ Utils LZMA2 (Lempel–Ziv–Markov chain algorithm, version 2) compression + XZ Utils LZMA2 (Lempel-Ziv-Markov chain algorithm, version 2) compression ``bzip2`` - bzip2 Burrows–Wheeler algorithm + bzip2 Burrows-Wheeler algorithm ``gzip`` GNU Gzip Deflate compression @@ -323,11 +323,11 @@ The valid range and interpretation depend on the selected compression type: - - ``gzip`` – level 1–9 - - ``bzip2`` – level 1–9 - - ``xz`` – level 1–9 - - ``lzma`` – level 1–9 - - ``zstd`` – level 1–19 + - ``gzip`` - level 1-9 + - ``bzip2`` - level 1-9 + - ``xz`` - level 1-9 + - ``lzma`` - level 1-9 + - ``zstd`` - level 1-19 Example usage:
diff --git a/Help/cpack_gen/nsis.rst b/Help/cpack_gen/nsis.rst index 53b1e9b..a6bc84a 100644 --- a/Help/cpack_gen/nsis.rst +++ b/Help/cpack_gen/nsis.rst
@@ -249,3 +249,9 @@ allowing an install. Allowed values for this variable are ``on``, ``off``, and ``force``. If not specified, the default behavior is ``on``. + +.. variable:: CPACK_NSIS_IGNORE_COMPONENTS_PAGE + + .. versionadded:: 4.4 + + If set, do not display the page containing the license during installation.
diff --git a/Help/cpack_gen/rpm.rst b/Help/cpack_gen/rpm.rst index b25b822..5e64803 100644 --- a/Help/cpack_gen/rpm.rst +++ b/Help/cpack_gen/rpm.rst
@@ -252,13 +252,13 @@ Possible values are: ``lzma`` - Lempel–Ziv–Markov chain algorithm + Lempel-Ziv-Markov chain algorithm ``xz`` - XZ Utils LZMA2 (Lempel–Ziv–Markov chain algorithm, version 2) compression + XZ Utils LZMA2 (Lempel-Ziv-Markov chain algorithm, version 2) compression ``bzip2`` - bzip2 Burrows–Wheeler algorithm + bzip2 Burrows-Wheeler algorithm ``gzip`` GNU Gzip Deflate compression @@ -553,6 +553,10 @@ set(CPACK_RPM_SPEC_MORE_DEFINE "%define __spec_install_post /bin/true") + .. versionadded:: 4.4 + If the variable is a list, its string elements will be concatenated using + ``\n`` before being substituted into the spec template. + .. variable:: CPACK_RPM_PACKAGE_DEBUG Toggle CPack RPM generator debug output.
diff --git a/Help/dev/README.rst b/Help/dev/README.rst index 84766a6..050ec07 100644 --- a/Help/dev/README.rst +++ b/Help/dev/README.rst
@@ -41,12 +41,14 @@ * The `CMake Testing Guide`_. * The `CMake Experimental Features Guide`_. * The `CMake Debugging Guide`_. +* The `CMake Diagnostics Guide`_. .. _`CMake Source Code Guide`: source.rst .. _`CMake Documentation Guide`: documentation.rst .. _`CMake Testing Guide`: testing.rst .. _`CMake Experimental Features Guide`: experimental.rst .. _`CMake Debugging Guide`: debug.rst +.. _`CMake Diagnostics Guide`: diagnostics.rst Maintainer Documentation ========================
diff --git a/Help/dev/ai-policy.rst b/Help/dev/ai-policy.rst new file mode 100644 index 0000000..b18a5cd --- /dev/null +++ b/Help/dev/ai-policy.rst
@@ -0,0 +1,18 @@ +AI Policy +********* + +The CMake project supports the responsible use of AI services and tools. +Contributors are welcome to use AI tools and services to help them be more +effective, subject to the following guidelines: + +* A human author must be responsible for any submission to the CMake project's + GitLab instance or forums. This includes merge requests, issues, and forum + posts. AI tools and services must not make submissions or comments without + a human author being associated with and responsible for them. + +* All merge request submissions are expected to be fully understood by the + contributing author. + +* AI tools and services are not considered authors. They should not be listed + as co-authors, and their review is not considered adequate for demonstrating + a human author's understanding or the correctness of a proposed change.
diff --git a/Help/dev/diagnostics.rst b/Help/dev/diagnostics.rst new file mode 100644 index 0000000..394f4d6 --- /dev/null +++ b/Help/dev/diagnostics.rst
@@ -0,0 +1,157 @@ +CMake Diagnostics Guide +*********************** + +The following is a guide on how CMake should issue diagnostics. +See documentation on `CMake Development`_ for more information. + +.. _`CMake Development`: README.rst + +TL;DR Porting Guide (from CMake < 4.4) +====================================== + +I want to write ``IssueMessage(MessageType::AUTHOR_WARNING, ...``: + Write ``IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, ...`` instead. + +I want to write ``IssueMessage(MessageType::DEPRECATION_WARNING, ...``: + Write ``IssueDiagnostic(cmDiagnostics::CMD_DEPRECATED, ...`` instead. + +I want to write ``IssueMessage(MessageType::AUTHOR_ERROR, ...``: + No, you don't. Under the old system, there isn't a meaningful difference + between ``AUTHOR_WARNING`` and ``AUTHOR_ERROR``; at least, not as arguments + to ``IssueMessage``. + +I want to write ``IssueMessage(MessageType::DEPRECATION_ERROR, ...``: + See ``AUTHOR_ERROR``. + +History +======= + +Prior to CMake 4.4, CMake did not have a formal system for issuing diagnostics. +The messaging system had message types for author diagnostics and deprecation +diagnostics which conflated category and severity. Worse, although these types +*purported* to allow the caller to specify severity, this information is not +actually honored. Other diagnostics existed which did not use these types. + +More importantly, the mechanisms for managing severity differed depending on +the diagnostic. Variables to control a diagnostic might be documented, +undocumented, or non-existent. Spelling of command-line options to control +diagnostics was inconsistent, as was the ability to make diagnostics fatal. +Adding new categories required significant effort, as the logic to handle a +diagnostic was bespoke at every layer. + +CMake Diagnostic Framework +========================== + +CMake 4.4 introduced a structured framework for issuing and managing +diagnostics. The ``cmDiagnostics.h`` header enumerates all available +diagnostic categories. All code to manage diagnostics leverages either this +table or helper code generated from the same, such that it is the *only* +non-documentation location that needs to be updated to add a new diagnostic +category. The framework is also responsible for determining the severity of a +diagnostic, based on the combination of category defaults and user preferences, +and for presenting the user with a consistent set of control mechanisms. + +Numeric identifiers associated with diagnostic categories are only used +internally and are never persisted. This means that numbers can be reassigned +at any time. This also permits ensuring that numbers are consecutive, which +allows iterating over all categories using a simple ``for`` loop. (Note that +such loops will typically start from ``1``, not ``0``, as ``0`` is reserved +for the 'NONE' category which is used primarily as a virtual parent.) Where +necessary, macros provided by ``cmDiagnostics.h`` can generate code for each +diagnostic category. + +Refer to ``Help/manual/cmake-diagnostics.7.rst`` for user-facing documentation. + +Issuing Diagnostics +=================== + +Diagnostics are the CMake equivalent of compiler warnings, with categorization +providing both additional information and additional response control to users. + +Diagnostics are issued via the ``IssueDiagnostic`` method. This method is +provided on various objects, with ``cmMakefile`` being the most prominent, and +typically exists alongside an ``IssueMessage`` method. The latter is not +deprecated, and should still be used to issue messages outside of the +diagnostic categories, such as hard errors or informational messages. + +The historic ``{AUTHOR,DEPRECATION}_{WARNING,ERROR}`` message types, however, +have been removed. These are replaced by ``IssueDiagnostic`` and the +``CMD_{AUTHOR,DEPRECATED}`` diagnostic categories. Because the methods have +similar signatures, porting in many cases can be accomplished by replacing the +method name and first argument (the former message type, newly the diagnostic +category). For example, where prior versions of CMake wrote: + +.. code:: c++ + + mf->IssueMessage(MessageType::AUTHOR_WARNING, ...); + +CMake 4.4 and later should write: + +.. code:: c++ + + mf->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, ...); + +(Note that the ``"cmDiagnostics.h"`` header may need to be newly included.) + +As previously mentioned, the purported severity was ignored by ``cmMessenger``, +which instead used its own internal state to decide whether a message should +be a warning or an error. This logic overrode the severity component of the +message type as passed into the method. As a result, although the new method +does not accept a severity, this is actually consistent with the historic +behavior and does not represent any loss of functionality. + +.. note:: + + When issuing diagnostics targeted at build-system authors, please consult the + complete list of available diagnostics, as a more specific category may be + available. In some cases, it may even make sense to add a new category. + +Adding Diagnostic Categories +============================ + +Adding new diagnostic categories involves four steps. + +1. The new category must be added to ``cmDiagnostics.h``. The diagnostics + table looks like this: + + .. code:: c++ + + #define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT) \ + SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12) \ + SELECT(ACTION, Warn, CMD_AUTHOR, CMD_DEPRECATED, 1) \ + ... + + Add a new entry. All entries must begin with ``SELECT(ACTION,``. The + second argument is the *default* state of the diagnostic and should + typically be ``Warn``, but may be ``Ignore`` if the diagnostic should be + opt-in. Defaulting to ``SendError`` would be exceptional, and defaulting + to ``FatalError`` is not recommended under any circumstances. The third + argument is the *parent* of the diagnostic (see below). The fourth is the + actual name of the diagnostic. The fifth and final argument is the CMake + preset version corresponding to the introduction of the argument, which + should be the latest preset version as used by the version of CMake under + development. + + .. note:: + + In the event that the development version has not already introduced a + new preset version, adding a new diagnostic will additionally require + introducing a new preset version. + + Diagnostics are hierarchical and **must** be listed in order of a + depth-first search. (If you imagine the order of items in a typical tree + view which has been fully expanded, that is depth-first order.) Each + diagnostic has a direct parent. Recursive alterations to a diagnostic will + also affect all descendant diagnostics. + +2. A documentation page for the new diagnostic must be created under + ``Help/diagnostic``, and a link to the same added to the Diagnostics + manual, ``Help/manual/cmake-diagnostics.7.rst``. + +3. The new presets fields must be mentioned in the CMake presets version + history, found at the bottom of ``Help/manual/cmake-presets.7.rst``. Note + that the documentation of the fields themselves is generated. + +4. The ``Utilities/Scripts/regenerate-presets.py`` script must be run to + generate the updated presets schema and documentation. Don't forget to + review and commit the changes made by this script.
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index 3d52871..ee2bc83 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst
@@ -37,7 +37,7 @@ * The package name associated with specific targets may be specified using the ``CMAKE_EXPORT_FIND_PACKAGE_NAME`` variable and/or -``EXPORT_FIND_PACKAGE_NAME`` target property. + ``EXPORT_FIND_PACKAGE_NAME`` target property. Export |CPS| Package Information for ``install(EXPORT)`` ======================================================== @@ -66,7 +66,7 @@ set * variable ``CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`` to -* value ``451f2fe2-a8a2-47c3-bc32-94786d8fc91b``. +* value ``f35a9ac6-8463-4d38-8eec-5d6008153e7d``. This UUID may change in future versions of CMake. Be sure to use the value documented here by the source tree of the version of CMake with which you are @@ -87,7 +87,7 @@ In order to activate support for exporting build databases, set * variable ``CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE`` to -* value ``73194a1d-c0b5-41b9-9190-a4512925e192``. +* value ``70ef007e-b743-492d-9407-e35eeac03a40``. This UUID may change in future versions of CMake. Be sure to use the value documented here by the source tree of the version of CMake with which you are @@ -102,14 +102,14 @@ * Targets with the property set to a true value will have their C++ build information exported to the build database. -Software Bill Of Materials |SBOM| +Software Bill Of Materials (SBOM) ================================= In order to activate support for the :command:`install(SBOM)` command, set * variable ``CMAKE_EXPERIMENTAL_GENERATE_SBOM`` to -* value ``ca494ed3-b261-4205-a01f-603c95e4cae0``. +* value ``2d856d6d-53e8-488b-a17f-d486d2cac317``. This UUID may change in future versions of CMake. Be sure to use the value documented here by the source tree of the version of CMake with which you are @@ -127,7 +127,7 @@ In order to activate support for Rust, set * variable ``CMAKE_EXPERIMENTAL_RUST`` to -* value ``3cc9b32c-47d3-4056-8953-d74e69fc0d6c``. +* value ``b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1``. This UUID may change in future versions of CMake. Be sure to use the value documented here by the source tree of the version of CMake with which you are
diff --git a/Help/dev/source.rst b/Help/dev/source.rst index 3aad194..1144ee2 100644 --- a/Help/dev/source.rst +++ b/Help/dev/source.rst
@@ -209,6 +209,9 @@ * ``<cm/map>`` : ``cm::erase_if``, ``cm::ssize`` + * ``<cm/ranges>``: + ``cm::ranges::views::keys``, ``cm::ranges::views::values`` + * ``<cm/set>`` : ``cm::erase_if``, ``cm::ssize`` @@ -244,6 +247,14 @@ * ``cm::contains``: Checks if element or key is contained in container. + * ``cm::keys`` + Extract keys from associative container or sequence container with tuple as + element + + * ``cm::values`` + Extract values from associative container or sequence container with tuple as + element + * ``<cmext/enum_set>`` * ``cm::enum_set``: @@ -275,6 +286,12 @@ * ``<cmext/type_traits>``: + * ``cm::is_pair``: + Checks if a type is a std::pair<> type. + + * ``cm::is_tuple``: + Checks if a type is a std::tuple<> type. + * ``cm::is_container``: Checks if a type is a container type. @@ -312,6 +329,15 @@ A raw pointer **must** not be deleted. Only the object(s) owning the smart pointer are allowed to delete dynamically allocated memory. +CMake Module/Script Style +========================= + +``execute_process`` should capture ``RESULT_VARIABLE`` to avoid +unexpected termination of CMake in case the user or project has set +``CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL`` to a value other than +``NONE``. Output should be captured for both stdout and stderr by using +``OUTPUT_VARIABLE``/``OUTPUT_QUIET`` and ``ERROR_VARIABLE``/``ERROR_QUIET``. + Third Parties =============
diff --git a/Help/diagnostic/CMD_AUTHOR.rst b/Help/diagnostic/CMD_AUTHOR.rst new file mode 100644 index 0000000..fee9bea --- /dev/null +++ b/Help/diagnostic/CMD_AUTHOR.rst
@@ -0,0 +1,18 @@ +CMD_AUTHOR +---------- + +.. versionadded:: 4.4 + +.. diagnostic:: + :default: warn + + Warn about a build system's incorrect use of CMake, or of a CMake interface + provided by a dependency. This is the category triggered by + :command:`message(AUTHOR_WARNING)`. It is also the ancestor of many other + diagnostic categories. + + The most important aspect of this category is that it represents issues with + a project's build system which typically require alteration to the same. + This is to say that users simply trying to build a project obtained elsewhere + will typically not be interested in these warnings, except to perhaps report + them to the project's developer(s).
diff --git a/Help/diagnostic/CMD_DEPRECATED.rst b/Help/diagnostic/CMD_DEPRECATED.rst new file mode 100644 index 0000000..14caf57 --- /dev/null +++ b/Help/diagnostic/CMD_DEPRECATED.rst
@@ -0,0 +1,19 @@ +CMD_DEPRECATED +-------------- + +.. versionadded:: 4.4 + +.. diagnostic:: + :default: warn + :parent: CMD_AUTHOR + + Warn about use of a deprecated function or package. This is the category + triggered by :command:`message(DEPRECATION)`. + + .. note:: + + If :policy:`CMP0218` is not set to ``NEW``, :command:`message(DEPRECATION)` + invocations, along with builtin deprecation messages that existed prior to + CMake 4.4, will ignore this diagnostic state and will instead use the + :variable:`CMAKE_WARN_DEPRECATED` and :variable:`CMAKE_ERROR_DEPRECATED` + variables to determine the severity of deprecation messages.
diff --git a/Help/diagnostic/CMD_EXPERIMENTAL.rst b/Help/diagnostic/CMD_EXPERIMENTAL.rst new file mode 100644 index 0000000..7d69ffe --- /dev/null +++ b/Help/diagnostic/CMD_EXPERIMENTAL.rst
@@ -0,0 +1,12 @@ +CMD_EXPERIMENTAL +---------------- + +.. versionadded:: 4.4 + +.. diagnostic:: + :default: warn + :parent: CMD_AUTHOR + + Warn if a build system uses an experimental feature, or if CMake detects + that the gate variable for an experimental feature is set to an unrecognized + value.
diff --git a/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst b/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst new file mode 100644 index 0000000..e414f38 --- /dev/null +++ b/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst
@@ -0,0 +1,12 @@ +CMD_INSTALL_ABSOLUTE_DESTINATION +-------------------------------- + +.. versionadded:: 4.4 + +.. diagnostic:: + :default: ignore + :parent: CMD_AUTHOR + + Warn when an :command:`install` command specifies an absolute ``DESTINATION`` + path. Absolute destinations are typically undesirable because they prevent + the installation prefix from being overridden at install time.
diff --git a/Help/diagnostic/CMD_POLICY.rst b/Help/diagnostic/CMD_POLICY.rst new file mode 100644 index 0000000..e0678ed --- /dev/null +++ b/Help/diagnostic/CMD_POLICY.rst
@@ -0,0 +1,12 @@ +CMD_POLICY +---------- + +.. versionadded:: 4.4 + +.. diagnostic:: + :default: warn + :parent: CMD_AUTHOR + + Warn if a build system potentially depends on the ``OLD`` behavior of a CMake + Policy. See the :command:`cmake_policy` command for more details. Setting a + policy to ``OLD`` will also disable warnings for that specific policy.
diff --git a/Help/diagnostic/CMD_UNINITIALIZED.rst b/Help/diagnostic/CMD_UNINITIALIZED.rst new file mode 100644 index 0000000..99a22ab --- /dev/null +++ b/Help/diagnostic/CMD_UNINITIALIZED.rst
@@ -0,0 +1,9 @@ +CMD_UNINITIALIZED +----------------- + +.. versionadded:: 4.4 + +.. diagnostic:: + :default: ignore + + Warn if an uninitialized variable is dereferenced.
diff --git a/Help/diagnostic/CMD_UNUSED_CLI.rst b/Help/diagnostic/CMD_UNUSED_CLI.rst new file mode 100644 index 0000000..bab0ad4 --- /dev/null +++ b/Help/diagnostic/CMD_UNUSED_CLI.rst
@@ -0,0 +1,14 @@ +CMD_UNUSED_CLI +-------------- + +.. versionadded:: 4.4 + +.. diagnostic:: + :default: ignore + + Warn about variables that are declared on the command line, but not used. + + This diagnostic is issued outside of the configuration / generation phases. + Accordingly, while the action of this warning category can be queried as + usual, the :command:`cmake_diagnostic` command is unable to affect the action + of this diagnostic.
diff --git a/Help/envvar/ASM_DIALECT.rst b/Help/envvar/ASM_DIALECT.rst index 4c271fd..43de075 100644 --- a/Help/envvar/ASM_DIALECT.rst +++ b/Help/envvar/ASM_DIALECT.rst
@@ -7,9 +7,15 @@ files. ``ASM<DIALECT>`` can be one of: * ``ASM`` + * ``ASM_NASM`` (Netwide Assembler) + * ``ASM_MASM`` (Microsoft Assembler) + * ``ASM_MARMASM`` (Microsoft ARM Assembler) + +* ``ASM_POASM`` (Pelles Macro Assembler) + * ``ASM-ATT`` (Assembler AT&T) Will only be used by CMake on the first configuration to determine
diff --git a/Help/envvar/ASM_DIALECTFLAGS.rst b/Help/envvar/ASM_DIALECTFLAGS.rst index e2b48a1..918768c 100644 --- a/Help/envvar/ASM_DIALECTFLAGS.rst +++ b/Help/envvar/ASM_DIALECTFLAGS.rst
@@ -7,9 +7,15 @@ of an assembly language. ``ASM<DIALECT>FLAGS`` can be one of: * ``ASMFLAGS`` + * ``ASM_NASMFLAGS`` + * ``ASM_MASMFLAGS`` + * ``ASM_MARMASMFLAGS`` + +* ``ASM_POASMFLAGS`` + * ``ASM-ATTFLAGS`` .. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_ASM<DIALECT>_FLAGS <CMAKE_<LANG>_FLAGS>`
diff --git a/Help/envvar/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst b/Help/envvar/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst new file mode 100644 index 0000000..0a8723e --- /dev/null +++ b/Help/envvar/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,11 @@ +CMAKE_DISABLE_PRECOMPILE_HEADERS +-------------------------------- + +.. versionadded:: 4.4 + +.. include:: include/ENV_VAR.rst + +The default value for the :variable:`CMAKE_DISABLE_PRECOMPILE_HEADERS` variable +when there is no explicit configuration given on the first run while creating a +new build tree. On later runs in an existing build tree the value persists in +the cache as :variable:`CMAKE_DISABLE_PRECOMPILE_HEADERS`.
diff --git a/Help/generator/FASTBuild.rst b/Help/generator/FASTBuild.rst index f16b166..e42ab36 100644 --- a/Help/generator/FASTBuild.rst +++ b/Help/generator/FASTBuild.rst
@@ -23,8 +23,8 @@ This generator also produces IDE project files for Visual Studio and Xcode, which are placed under: - * ``<path-to-build>/VisualStudio`` – Visual Studio solution and projects - * ``<path-to-build>/XCode`` – Xcode workspace and projects + * ``<path-to-build>/VisualStudio`` - Visual Studio solution and projects + * ``<path-to-build>/XCode`` - Xcode workspace and projects These IDE files can be generated by building ``xcode`` or ``solution`` targets and will build using FASTBuild as the backend. @@ -77,9 +77,9 @@ The following target properties can be used to fine-tune behavior on a per-target basis: -* :prop_tgt:`FASTBUILD_CACHING` – +* :prop_tgt:`FASTBUILD_CACHING` - disables caching for a specific target. -* :prop_tgt:`FASTBUILD_DISTRIBUTION` – +* :prop_tgt:`FASTBUILD_DISTRIBUTION` - disables distributed compilation for a specific target. Notes
diff --git a/Help/generator/Ninja.rst b/Help/generator/Ninja.rst index 5f5ffb8..d90e8c5 100644 --- a/Help/generator/Ninja.rst +++ b/Help/generator/Ninja.rst
@@ -31,6 +31,26 @@ Created only if the :prop_gbl:`INSTALL_PARALLEL` global property is ``ON``. Runs the install step for each subdirectory independently and in parallel. +``test_prep/<test-name>`` + + .. versionadded:: 4.4 + + Created only if the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is + enabled. Builds all known build dependencies for the named test, including + the executable target invoked by the test, targets referenced by generator + expressions in the test command, and explicit ``BUILD_DEPENDS`` entries. + + Tests with names that are not valid target names are excluded. If multiple + tests in different directories share the same name, their dependencies are + merged into one ``test_prep/<test-name>`` target. + +``test_prep/all`` + + .. versionadded:: 4.4 + + Created only if the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is + enabled. Depends on every generated ``test_prep/<test-name>`` target. + For each subdirectory ``sub/dir`` of the project, additional targets are generated:
diff --git a/Help/guide/ide-integration/index.rst b/Help/guide/ide-integration/index.rst index f335e1f..809cf73 100644 --- a/Help/guide/ide-integration/index.rst +++ b/Help/guide/ide-integration/index.rst
@@ -40,7 +40,7 @@ user-specific counterpart, ``CMakeUserPresets.json``. This file contains information on the various configure presets that a user may want. Each preset may have a different compiler, build flags, etc. The details of this format are -explained in the :manual:`cmake(1)` manual. +explained in the :manual:`cmake-presets(7)` manual. IDE vendors are encouraged to read and evaluate this file the same way CMake does, and present the user with the presets listed in the file. Users should be @@ -71,12 +71,12 @@ of the platform to be exceeded, the IDE should instead construct a temporary cache script and pass it with the :option:`-C <cmake -C>` flag. -While reading, parsing, and evaluating the contents of ``CMakePresets.json`` is +While reading, parsing, and evaluating the contents of CMake presets files is straightforward, it is not trivial. In addition to the documentation, IDE vendors may also wish to refer to the CMake source code and test cases for a better understanding of how to implement the format. :download:`This file <../../manual/presets/schema.json>` provides a -machine-readable JSON schema for the ``CMakePresets.json`` format that IDE +machine-readable JSON schema for the CMake presets file format that IDE vendors may find useful for validation and providing editing assistance. Configuring
diff --git a/Help/guide/tutorial/Before You Begin.rst b/Help/guide/tutorial/Before You Begin.rst index d3d3a5b..754b27e 100644 --- a/Help/guide/tutorial/Before You Begin.rst +++ b/Help/guide/tutorial/Before You Begin.rst
@@ -227,7 +227,7 @@ If we're using a multi-configuration generator, we will want to specify the build configuration. The default configurations are ``Debug``, ``Release``, -``RelWithDebInfo``, and ``MinRelSize``. The result of the build will be stored +``RelWithDebInfo``, and ``MinSizeRel``. The result of the build will be stored in a configuration-specific subdirectory of the build folder. So for example we could run:
diff --git a/Help/guide/tutorial/Configuration and Cache Variables.rst b/Help/guide/tutorial/Configuration and Cache Variables.rst index 250748b..91ef2fe 100644 --- a/Help/guide/tutorial/Configuration and Cache Variables.rst +++ b/Help/guide/tutorial/Configuration and Cache Variables.rst
@@ -84,17 +84,18 @@ .. code-block:: console - $ cmake -P StickyCacheVariable.cmake + $ cmake -B build + ... StickyCacheVariable: I will not change -Because :option:`-D <cmake -D>` flags are processed before any other commands, +Because :option:`-D <cmake -D>` flags are processed before any project commands, they take precedence for setting the value of a cache variable. .. code-block:: console - $ cmake \ - -DStickyCacheVariable="Commandline always wins" \ - -P StickyCacheVariable.cmake + $ cmake -B build \ + -DStickyCacheVariable="Commandline always wins" + ... StickyCacheVariable: Commandline always wins While cache variables cannot ordinarily be changed, they can be *shadowed* by @@ -113,10 +114,16 @@ .. code-block:: console - $ cmake -P ShadowVariable.cmake + $ cmake -B build + ... ShadowVariable: Hiding the cache variable ShadowVariable: In the shadows +.. note:: + :ref:`Script mode <Script Processing Mode>` operates slightly differently, + only :option:`-D <cmake -D>` flags provided before the :option:`-P <cmake -P>` + flag in the command are evaluated and available in the running script. + Exercise 1 - Using Options ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +320,7 @@ * :variable:`CMAKE_<LANG>_STANDARD` * :variable:`CMAKE_CXX_STANDARD` * :prop_tgt:`CXX_STANDARD` -* `cppreference \<format\> <https://en.cppreference.com/w/cpp/utility/format/format.html>`_ +* `cppreference \<format\> <https://en.cppreference.com/cpp/utility/format/format>`_ Files to Edit ------------- @@ -506,6 +513,12 @@ Command line flags can be mixed with presets. Command line flags have precedence over values found in a preset. +.. note:: + On CMake 4.4 and newer, CMake can also load presets from any file specified + with :option:`cmake --presets-file`. This can be useful when reusing settings + across multiple projects, since it avoids having to duplicate them in + separate ``CMakePresets.json`` files for each project. + Presets also support limited macros, variables that can be brace-expanded inside the preset. The only one of interest to us is the ``${sourceDir}`` macro, which expands to the root directory of the project. We can use this to set our @@ -557,7 +570,7 @@ Build and Run ------------- -We can now use the preset file to manage our configuration. +We can now use the presets file to manage our configuration. .. code-block:: console
diff --git a/Help/guide/tutorial/Getting Started with CMake.rst b/Help/guide/tutorial/Getting Started with CMake.rst index 7f492f0..93a5599 100644 --- a/Help/guide/tutorial/Getting Started with CMake.rst +++ b/Help/guide/tutorial/Getting Started with CMake.rst
@@ -596,7 +596,7 @@ ``TODO 9``. In this exercise, we need to add the ``MathFunctions`` target to the ``Tutorial`` target's linked libraries using :command:`target_link_libraries`. -After modifying the CML, update ``tutorial.cxx`` to use the +After modifying the CML, update ``Tutorial.cxx`` to use the ``mathfunctions::sqrt()`` function instead of ``std::sqrt``. Build and Run
diff --git a/Help/guide/tutorial/Installation Commands and Concepts.rst b/Help/guide/tutorial/Installation Commands and Concepts.rst index bc78718..ff938a3 100644 --- a/Help/guide/tutorial/Installation Commands and Concepts.rst +++ b/Help/guide/tutorial/Installation Commands and Concepts.rst
@@ -1,18 +1,18 @@ Step 9: Installation Commands and Concepts ========================================== -Projects need to do more than build and test their code, they need to make it +Projects need to do more than build and test their code. They need to make it available to consumers. The layout of files in the build tree is unsuitable -for consumption by other projects, binaries are in unexpected places, header +for consumption by other projects. Binaries are in unexpected places, header files are located far away in the source tree, and there's no clear way to discover what targets are provided or how to use them. -This translation, moving artifacts from the source and build trees into a final -layout suitable for consumption, is known as installation. CMake supports a -complete installation workflow as part of the project description, controlling -both the layout of artifacts in the install tree, and reconstructing targets -for other CMake projects which want to consume the libraries provided by the -install tree. +Moving artifacts from the source and build trees into a final layout suitable +for consumption is known as installation. CMake supports a complete +installation workflow as part of the project description, controlling both the +layout of artifacts in the install tree and reconstructing targets for other +CMake projects which want to consume the libraries provided by the install +tree. Background ^^^^^^^^^^ @@ -73,7 +73,8 @@ using that build tree defaults to a given prefix, we can use any of: * the :option:`cmake --install-prefix` option; -* the :ref:`installDir <CMakePresets installDir>` field in CMake presets; or +* the :ref:`installDir <CMakePresets.configurePresets.installDir>` + field in CMake presets; or * the :variable:`CMAKE_INSTALL_PREFIX` variable. .. note::
diff --git a/Help/guide/tutorial/Step10/TutorialProject/Tests/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/Tests/CMakeLists.txt index 1f4482c..07bf701 100644 --- a/Help/guide/tutorial/Step10/TutorialProject/Tests/CMakeLists.txt +++ b/Help/guide/tutorial/Step10/TutorialProject/Tests/CMakeLists.txt
@@ -8,7 +8,7 @@ # TODO1: Find the SimpleTest package. This should be a required dependency when # building tests. -# TODO2: Add the SimpleTest::SimpleTest target to Test MathFunctions +# TODO2: Add the SimpleTest::SimpleTest target to TestMathFunctions target_link_libraries(TestMathFunctions PRIVATE
diff --git a/Help/guide/user-interaction/index.rst b/Help/guide/user-interaction/index.rst index fe8a45a..12d6b84 100644 --- a/Help/guide/user-interaction/index.rst +++ b/Help/guide/user-interaction/index.rst
@@ -71,7 +71,7 @@ for the provider of the software, not intended for the consumer of the software. Such warnings end with "This warning is for project developers". Users may disable -such warnings by passing the :option:`-Wno-dev <cmake -Wno-dev>` +such warnings by passing the :option:`-Wno-author <cmake -Wno->` flag to :manual:`cmake(1)`. cmake-gui tool @@ -425,7 +425,7 @@ presets can set the build directory, generator, cache variables, environment variables, and other command-line options. All of these options can be overridden by the -user. The full details of the ``CMakePresets.json`` format +user. The full details of the CMake presets format are listed in the :manual:`cmake-presets(7)` manual. Using presets on the command-line
diff --git a/Help/index.rst b/Help/index.rst index f9ffcba..fa7d199 100644 --- a/Help/index.rst +++ b/Help/index.rst
@@ -60,6 +60,7 @@ /manual/cmake-configure-log.7 /manual/cmake-cxxmodules.7 /manual/cmake-developer.7 + /manual/cmake-diagnostics.7 /manual/cmake-env-variables.7 /manual/cmake-file-api.7 /manual/cmake-generator-expressions.7
diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst index 6f1a947..8a25b62 100644 --- a/Help/manual/cmake-commands.7.rst +++ b/Help/manual/cmake-commands.7.rst
@@ -17,6 +17,7 @@ /command/block /command/break + /command/cmake_diagnostic /command/cmake_host_system_information /command/cmake_language /command/cmake_minimum_required @@ -93,6 +94,7 @@ /command/cmake_instrumentation /command/create_test_sourcelist /command/define_property + /command/discover_tests /command/enable_language /command/enable_testing /command/export
diff --git a/Help/manual/cmake-cxxmodules.7.rst b/Help/manual/cmake-cxxmodules.7.rst index 9dc14d2..fe15040 100644 --- a/Help/manual/cmake-cxxmodules.7.rst +++ b/Help/manual/cmake-cxxmodules.7.rst
@@ -50,8 +50,11 @@ be scanned. - If the target does not use at least C++ 20, it will not be scanned. - If the source file is not the language ``CXX``, it will not be scanned. -- If the :prop_sf:`CXX_SCAN_FOR_MODULES` source file property is set, its +- If the source file belongs to a file set that is not of type ``CXX_MODULES``, + and the :prop_fs:`CXX_SCAN_FOR_MODULES` file set property is set, its value will be used. +- If the :prop_sf:`CXX_SCAN_FOR_MODULES` source file property is set in the + target's directory, its value will be used. - If the :prop_tgt:`CXX_SCAN_FOR_MODULES` target property is set, its value will be used. Set the :variable:`CMAKE_CXX_SCAN_FOR_MODULES` variable to initialize this property on all targets as they are created. @@ -69,7 +72,15 @@ modules includes: * MSVC toolset 14.34 and newer (provided with Visual Studio 17.4 and newer) + * LLVM/Clang 16.0 and newer + + .. versionadded:: 4.4 + + ``clang-cl`` version 19.1 and newer, except with the + :ref:`Visual Studio Generators` (i.e., the ``ClangCl`` + :variable:`toolset <CMAKE_GENERATOR_TOOLSET>`). + * GCC 14 and newer ``import std`` Support @@ -509,26 +520,34 @@ all sources may get scanned, only modules that are actually used will be generated. -The first step CMake performs is to generate a :term:`synthetic target` for -each unique usage of a module-providing target. These targets are based on -other targets, but provide only :term:`BMI` files for other targets rather -than object files. This is because the compatibility of :term:`BMI` files is -extremely narrow and cannot be shared between arbitrary ``import`` instances. -Due to the internal workings of toolchains, there can generally only be a -single set of settings for a variety of flags for any one compilation, -including :term:`BMI` files for imported modules. As an example, the C++ -standard in use needs to be consistent across all modules, but there are many -settings which may cause incompatibilities. +The first step CMake performs is to generate any +:term:`synthetic targets <synthetic target>` needed for a module-providing +target. These targets are based on the module provider, but produce only +:term:`BMI` files for consumers rather than object files. This is necessary +because the compatibility of :term:`BMI` files is extremely narrow and cannot be +shared between arbitrary ``import`` instances. -.. note:: +Due to the internal workings of toolchains, there can generally only be a single +set of settings for a variety of flags for any one compilation, including +:term:`BMI` files for imported modules. As an example, the C++ standard in +use needs to be consistent across all modules imported to a given translation +unit, mixing standards will cause incompatibilities. - CMake currently assumes that all usages are compatible and will only create - one set of :term:`BMIs <BMI>` for each target. This may cause build - failures where multiple :term:`BMI` files are required, but CMake only - provides one set. See `CMake Issue 25916`_ for progress on removing this - assumption. + .. versionadded:: 4.4 -.. _`CMake Issue 25916`: https://gitlab.kitware.com/cmake/cmake/-/issues/25916 + Prior to CMake 4.4, CMake assumed all usages were compatible and would only + create one set of :term:`BMIs <BMI>` for each module-providing target. This + could cause build failures due to incompatible :term:`BMI` usage. + + In CMake 4.4 and later, CMake attempts to determine :term:`BMI` + compatibility on a target-by-target basis and will generate + :term:`synthetic targets <synthetic target>` to provide compatible + :term:`BMIs <BMI>` for consumers. + +The generated :term:`synthetic target` has a mix of properties from both +the provider of the module and the consumer. :prop_tgt:`COMPILE_FEATURES` and +:prop_tgt:`COMPILE_OPTIONS` are adopted from the consumer, while all other +relevant properties are inherited from the provider. Once all of the :term:`synthetic targets <synthetic target>` are created, CMake looks at each target that has any source that might use C++ modules and @@ -653,11 +672,6 @@ provides the locations of modules that are discovered by the :term:`scan` command, any modules missed by it will not be provided to the compilation. -It is possible for toolchains to reject the :term:`BMI` file that CMake -provides to a compilation as incompatible. This is because CMake assumes that -all usages are compatible at the moment. See `CMake Issue 25916`_ for -progress on removing this assumption. - Install ^^^^^^^
diff --git a/Help/manual/cmake-diagnostics.7.rst b/Help/manual/cmake-diagnostics.7.rst new file mode 100644 index 0000000..cafc50d --- /dev/null +++ b/Help/manual/cmake-diagnostics.7.rst
@@ -0,0 +1,95 @@ +.. cmake-manual-description: CMake Diagnostics Reference + +cmake-diagnostics(7) +******************** + +.. only:: html + + .. contents:: + +.. _cmake-diagnostics-intro: + +Introduction +============ + +.. versionadded:: 4.4 + +CMake Diagnostics are the mechanism by which CMake categorizes and presents +certain advisory information about a project's configuration and the generation +of its build system. These diagnostics can be seen as the build system +equivalent of compiler warnings. Diagnostics provide feedback on potential +issues in several categories: + +* Issues that may impact the success of the build. + +* Issues that may impact the correctness of the build. + +* Issues that may impact the correctness of the project packaging. + +* Issues that may impact the ability of the project + to be built with newer versions of dependencies. + +* Issues that may impact the ability of the project + to be built with newer versions of CMake. + +Controlling Diagnostics +======================= + +Each diagnostic category has an associated action to be taken when that +diagnostic is triggered. Most categories will warn by default. The available +actions are described in the :command:`cmake_diagnostic` command documentation. + +CMake maintains a diagnostic state stack that is similar to the policy state. +The initial state of the stack is determined by four factors, which are, in +order of precedence: + +* The default action associated with the diagnostic. + +* The action associated with the diagnostic stored in the CMake variable cache, + which is used to persist the initial state between CMake runs. + +* The :preset:`configurePresets.warnings` and :preset:`configurePresets.errors` + fields of :manual:`CMake Presets <cmake-presets(7)>`. + +* The :option:`-W[no-][error=] <cmake -W>` command line arguments. + +.. note:: + + Because command line arguments operate both recursively and in the order + specified, some combinations of diagnostic arguments may result in later + arguments completely overwriting the action of earlier arguments. For + example, ``-Wno-child -Wparent`` will result in the ``child`` warning being + enabled, because ``-Wparent`` promotes both ``parent`` and ``child`` to at + least ``WARN`` severity. CMake presets are evaluated in order from most + ancestral to least ancestral. + +During script execution, the :command:`cmake_diagnostic` command can be used to +query or alter the state, or to perform limited stack manipulations. + +When a diagnostic is issued at configure time (or during script execution, when +CMake is running in script mode), the current diagnostic state controls the +action. Diagnostics issued at generate time, or outside of the configuration +and generation phases must make use of recorded state information. While CMake +strives to preserve this information in a way that matches the recorded state +to the state as of the CMake command which ultimately causes a diagnostic to be +issued, CMake may sometimes fall back to the state when processing of a +subdirectory completed, or even the root state. This may limit the ability of +the :command:`cmake_diagnostic` command to control such diagnostics, especially +if called from a function or included file. This is especially the case for +diagnostics that are not directly coupled to a CMake command. + +Diagnostic Categories +===================== + +The following categories are defined: + +.. toctree:: + :maxdepth: 1 + + /diagnostic/CMD_AUTHOR + /diagnostic/CMD_DEPRECATED + /diagnostic/CMD_EXPERIMENTAL + /diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION + /diagnostic/CMD_POLICY + /diagnostic/CMD_UNINITIALIZED + /diagnostic/CMD_UNUSED_CLI
diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst index f1b1cb1..e3779c0 100644 --- a/Help/manual/cmake-env-variables.7.rst +++ b/Help/manual/cmake-env-variables.7.rst
@@ -52,6 +52,7 @@ /envvar/CMAKE_CONFIG_TYPE /envvar/CMAKE_CONFIGURATION_TYPES /envvar/CMAKE_CROSSCOMPILING_EMULATOR + /envvar/CMAKE_DISABLE_PRECOMPILE_HEADERS /envvar/CMAKE_EXPORT_BUILD_DATABASE /envvar/CMAKE_EXPORT_COMPILE_COMMANDS /envvar/CMAKE_FASTBUILD_VERBOSE_GENERATOR
diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst index 857d498..64dd5bd 100644 --- a/Help/manual/cmake-file-api.7.rst +++ b/Help/manual/cmake-file-api.7.rst
@@ -636,14 +636,14 @@ ``targets`` array. ``abstractTargetIndexes`` + .. codemodel-versionadded:: 2.9 + Optional member that is present when the directory itself has abstract targets, excluding those belonging to subdirectories. The value is a JSON array of entries corresponding to the abstract targets. Each entry is an unsigned integer 0-based index into the main ``abstractTargets`` array. - This field was added in codemodel version 2.9. - ``minimumCMakeVersion`` Optional member present when a minimum required version of CMake is known for the directory. This is the ``<min>`` version given to the @@ -666,12 +666,12 @@ or equivalent rule is available. ``jsonFile`` + .. codemodel-versionadded:: 2.3 + A JSON string specifying a path relative to the codemodel file to another JSON file containing a `"codemodel" version 2 "directory" object`_. - This field was added in codemodel version 2.3. - ``projects`` A JSON array of entries corresponding to the top-level project and sub-projects defined in the build system. Each (sub-)project @@ -711,14 +711,14 @@ ``targets`` array. ``abstractTargetIndexes`` + .. codemodel-versionadded:: 2.9 + Optional member that is present when the project itself has abstract targets, excluding those belonging to sub-projects. The value is a JSON array of entries corresponding to the abstract targets. Each entry is an unsigned integer 0-based index into the main ``abstractTargets`` array. - This field was added in codemodel version 2.9. - ``targets`` A JSON array of entries corresponding to the build system targets. Such targets are created by calls to :command:`add_executable`, @@ -747,6 +747,8 @@ `"codemodel" version 2 "target" object`_. ``abstractTargets`` + .. codemodel-versionadded:: 2.9 + A JSON array of entries corresponding to targets that are not present in the build system. These are imported targets or interface libraries created by calls to :command:`add_executable` or :command:`add_library`. @@ -777,8 +779,6 @@ to another JSON file containing a `"codemodel" version 2 "target" object`_. - This field was added in codemodel version 2.9. - "codemodel" version 2 "directory" object ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -791,6 +791,8 @@ with members: ``codemodelVersion`` + .. codemodel-versionadded:: 2.9 + This specifies the codemodel version this file is part of. It will match the ``version`` field of the codemodel object kind that references this file. It is a JSON object with the following members: @@ -801,8 +803,6 @@ ``minor`` The codemodel minor version. - This field was added in codemodel version 2.9. - ``paths`` A JSON object containing members: @@ -913,21 +913,21 @@ ``runtimeDependencySetName`` and ``runtimeDependencySetType``. ``fileSet`` + .. codemodel-versionadded:: 2.4 + An :command:`install(TARGETS)` call with ``FILE_SET``. The ``destination`` and ``paths`` members are populated. The ``isOptional`` member may exist. This type has additional members ``fileSetName``, ``fileSetType``, ``fileSetDirectories``, and ``fileSetTarget``. - This type was added in codemodel version 2.4. - ``cxxModuleBmi`` + .. codemodel-versionadded:: 2.5 + An :command:`install(TARGETS)` call with ``CXX_MODULES_BMI``. The ``destination`` member is populated and the ``isOptional`` member may exist. This type has an additional ``cxxModuleBmiTarget`` member. - This type was added in codemodel version 2.5. - ``isExcludeFromAll`` Optional member that is present with boolean value ``true`` when :command:`install` is called with the ``EXCLUDE_FROM_ALL`` option. @@ -1006,26 +1006,32 @@ frameworks. ``fileSetName`` + .. codemodel-versionadded:: 2.4 + Optional member that is present when ``type`` is ``fileSet``. The value is a string with the name of the file set. - This field was added in codemodel version 2.4. - ``fileSetType`` + .. codemodel-versionadded:: 2.4 + Optional member that is present when ``type`` is ``fileSet``. The value is a string with the type of the file set. - This field was added in codemodel version 2.4. + .. codemodel-versionchanged:: 2.11 + + This field additionally accounts for ``SOURCES`` file sets. ``fileSetDirectories`` + .. codemodel-versionadded:: 2.4 + Optional member that is present when ``type`` is ``fileSet``. The value is a list of strings with the file set's base directories (determined by genex-evaluation of :prop_tgt:`HEADER_DIRS` or :prop_tgt:`HEADER_DIRS_<NAME>`). - This field was added in codemodel version 2.4. - ``fileSetTarget`` + .. codemodel-versionadded:: 2.4 + Optional member that is present when ``type`` is ``fileSet``. The value is a JSON object with members: @@ -1038,9 +1044,9 @@ An unsigned integer 0-based index into the main "codemodel" object's ``targets`` array for the target. - This field was added in codemodel version 2.4. - ``cxxModuleBmiTarget`` + .. codemodel-versionadded:: 2.5 + Optional member that is present when ``type`` is ``cxxModuleBmi``. The value is a JSON object with members: @@ -1053,8 +1059,6 @@ An unsigned integer 0-based index into the main "codemodel" object's ``targets`` array for the target. - This field was added in codemodel version 2.5. - ``scriptFile`` Optional member that is present when ``type`` is ``script``. The value is a string specifying the path to the script file on disk, @@ -1084,6 +1088,8 @@ with members: ``codemodelVersion`` + .. codemodel-versionadded:: 2.9 + This specifies the codemodel version this file is part of. It will match the ``version`` field of the codemodel object kind that references this file. It is a JSON object with the following members: @@ -1094,8 +1100,6 @@ ``minor`` The codemodel minor version. - This field was added in codemodel version 2.9. - ``name`` A string specifying the logical name of the target. @@ -1110,19 +1114,21 @@ or ``UTILITY``. ``imported`` + .. codemodel-versionadded:: 2.9 + Optional member that is present with boolean value ``true`` if the target is an imported target. - This field was added in codemodel version 2.9. - ``local`` + .. codemodel-versionadded:: 2.9 + Optional member that is present with boolean value ``true`` if the target is only defined with local scope rather than being a global target. Currently, only imported targets will potentially have this field. - This field was added in codemodel version 2.9. - ``abstract`` + .. codemodel-versionadded:: 2.9 + Optional member that is present with boolean value ``true`` if the target is an abstract target. Abstract targets are not part of the build system, they only exist to describe dependencies or to provide usage @@ -1131,17 +1137,17 @@ cannot be built, so they should not be presented to the user as a buildable target. - This field was added in codemodel version 2.9. Abstract targets were not - included in codemodel version 2.8 and earlier. + Abstract targets were not included in codemodel version 2.8 and earlier. ``symbolic`` + .. codemodel-versionadded:: 2.9 + Optional member that is present with boolean value ``true`` if the target is :prop_tgt:`SYMBOLIC`. Symbolic targets are created by calls to :command:`add_library(INTERFACE SYMBOLIC) <add_library(INTERFACE-SYMBOLIC)>`, and are also abstract targets that are not part of the build system. - This field was added in codemodel version 2.9. Symbolic targets were not - included in codemodel version 2.8 and earlier. + Symbolic targets were not included in codemodel version 2.8 and earlier. ``backtrace`` Optional member that is present when a CMake language backtrace to @@ -1220,6 +1226,8 @@ index into the ``backtraceGraph`` member's ``nodes`` array. ``launchers`` + .. codemodel-versionadded:: 2.7 + Optional member that is present on executable targets that have at least one launcher specified by the project. The value is a JSON array of entries corresponding to the specified launchers. @@ -1247,8 +1255,6 @@ A start program for the execution of tests. See the :prop_tgt:`TEST_LAUNCHER` target property. - This field was added in codemodel version 2.7. - ``link`` Optional member that is present for non-imported executables and shared library targets that link into a runtime binary. The value is a JSON object @@ -1320,21 +1326,19 @@ or link-time code generation) is enabled. ``debugger`` + .. codemodel-versionadded:: 2.8 + Optional member that is present when the target has one of the following fields set. The value is a JSON object of entries corresponding to debugger specific values set. - This field was added in codemodel version 2.8. - ``workingDirectory`` Optional member that is present when the :prop_tgt:`DEBUGGER_WORKING_DIRECTORY` target property is set. The member will also be present in :ref:`Visual Studio Generators` when :prop_tgt:`VS_DEBUGGER_WORKING_DIRECTORY` is set. - This field was added in codemodel version 2.8. - ``dependencies`` Optional member that is present when the target depends on other targets. It is only present if the target is part of the build system. @@ -1364,6 +1368,8 @@ the ``backtraceGraph`` member's ``nodes`` array. ``linkLibraries`` + .. codemodel-versionadded:: 2.9 + Optional member that may be present when the target links directly to one or more other targets or libraries. It contains items that are used when linking this target. These come from the target's @@ -1413,9 +1419,9 @@ :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` property created the relationship. The value matches the main ``id`` member of that target. - This field was added in codemodel version 2.9. - ``interfaceLinkLibraries`` + .. codemodel-versionadded:: 2.9 + Optional member that may be present when the target has one or more interface link libraries. It contains items that are used when linking consumers of this target. These come from the target's @@ -1453,9 +1459,9 @@ The value is an unsigned integer 0-based index into the ``backtraceGraph`` member's ``nodes`` array. - This field was added in codemodel version 2.9. - ``compileDependencies`` + .. codemodel-versionadded:: 2.9 + Optional member that may be present when the target links directly to one or more other targets that may provide usage requirements to this one. They affect how this target's sources are compiled. These relationships are @@ -1495,9 +1501,9 @@ :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` property created the relationship. The value matches the main ``id`` member of that target. - This field was added in codemodel version 2.9. - ``interfaceCompileDependencies`` + .. codemodel-versionadded:: 2.9 + Optional member that may be present when the target has one or more interface linking relationships to other targets. It contains items that affect how consumers' sources are compiled. These relationships are defined by the @@ -1525,9 +1531,9 @@ The value is an unsigned integer 0-based index into the ``backtraceGraph`` member's ``nodes`` array. - This field was added in codemodel version 2.9. - ``objectDependencies`` + .. codemodel-versionadded:: 2.9 + Optional member that is present when the target has one or more entries in its :prop_tgt:`SOURCES` property where the entry is specified using :genex:`$<TARGET_OBJECTS:...>`, and where no other generator expression is @@ -1547,9 +1553,9 @@ The value is an unsigned integer 0-based index into the ``backtraceGraph`` member's ``nodes`` array. - This field was added in codemodel version 2.9. - ``orderDependencies`` + .. codemodel-versionadded:: 2.9 + Optional member that is present when the target has one or more direct order dependencies on other targets. Such dependencies may arise from calls to :command:`add_dependencies` or from internal CMake processing. @@ -1571,9 +1577,9 @@ The value is an unsigned integer 0-based index into the ``backtraceGraph`` member's ``nodes`` array. - This field was added in codemodel version 2.9. - ``fileSets`` + .. codemodel-versionadded:: 2.5 + An optional member that is present when a target defines one or more file sets. The value is a JSON array of entries corresponding to the target's file sets. Each entry is a JSON object with members: @@ -1595,8 +1601,6 @@ directory then the path is specified relative to that directory. Otherwise the path is absolute. - This field was added in codemodel version 2.5. - ``sources`` A JSON array of entries corresponding to the target's source files. Each entry is a JSON object with members: @@ -1623,13 +1627,29 @@ the source is :prop_sf:`GENERATED`. ``fileSetIndex`` + .. codemodel-versionadded:: 2.5 + + .. deprecated:: 4.4 + Use the ``fileSetIndexes`` field instead, which is an array, and + accounts for sources assigned to multiple file sets. + Optional member that is present when the source is part of a file set. The value is an unsigned integer 0-based index into the ``fileSets`` - array. + array representing the file set to which the source was most recently added. - This field was added in codemodel version 2.5. + ``fileSetIndexes`` + .. codemodel-versionadded:: 2.11 + + Optional member that is present when the source is part of at least one + file set; see also policy :policy:`CMP0211`. The value is + an array of unsigned integer 0-based indexes inte the ``fileSets`` array. ``backtrace`` + .. deprecated:: 4.4 + Use the ``backtraces`` field instead, which is an array, and + accounts for sources added to multiple file sets over multiple command + invocations. + Optional member that is present when a CMake language backtrace to the :command:`target_sources`, :command:`add_executable`, :command:`add_library`, :command:`add_custom_target`, or other @@ -1637,7 +1657,23 @@ available. The value is an unsigned integer 0-based index into the ``backtraceGraph`` member's ``nodes`` array. + ``backtraces`` + .. codemodel-versionadded:: 2.11 + + Optional member that is present when CMake language backtraces to + the :command:`target_sources`, :command:`add_executable`, + :command:`add_library`, :command:`add_custom_target`, or other + command invocation that added this source to the target is + available. It's possible for a single source file to be included in + multiple :command:`target_sources` invocations each naming it in a + different file set (see also policy :policy:`CMP0211`), so there could be + multiple backtraces. The value is a JSON array with each entry being an + unsigned integer 0-based index into the ``backtraceGraph`` member's + ``nodes`` array. + ``interfaceSources`` + .. codemodel-versionadded:: 2.10 + An optional member that is present when a target defines one or more interface sources. The value is a JSON array of entries corresponding to the target's interface source files. Each entry is a JSON object @@ -1660,11 +1696,20 @@ the source is :prop_sf:`GENERATED`. ``fileSetIndex`` + .. deprecated:: 4.4 + Use the ``fileSetIndexes`` field instead, which is an array, and + accounts for sources assigned to multiple file sets. + Optional member that is present when the source is part of a file set. The value is an unsigned integer 0-based index into the ``fileSets`` - array. + array representing the file set to which the source was most recently added. - This field was added in codemodel version 2.10. + ``fileSetIndexes`` + .. codemodel-versionadded:: 2.11 + + Optional member that is present when the source is part of at least one + file set; see also policy :policy:`CMP0211`. The value is + an array of unsigned integer 0-based indexes inte the ``fileSets`` array. ``sourceGroups`` Optional member that is present when sources are grouped together by @@ -1681,14 +1726,14 @@ main ``sources`` array for the target. ``interfaceSourceIndexes`` + .. codemodel-versionadded:: 2.10 + Optional member that is present when at least one interface source file is part of the source group. The value is a JSON array listing the interface sources belonging to the group. Each entry is an unsigned integer 0-based index into the main ``interfaceSources`` array for the target. - This field was added in codemodel version 2.10. - ``compileGroups`` Optional member that is present when the target has sources that compile. The value is a JSON array of entries corresponding to groups of sources @@ -1705,6 +1750,8 @@ of the toolchain is used to compile the source file. ``languageStandard`` + .. codemodel-versionadded:: 2.2 + Optional member that is present when the language standard is set explicitly (e.g. via :prop_tgt:`CXX_STANDARD`) or implicitly by compile features. Each entry is a JSON object with two members: @@ -1722,8 +1769,6 @@ ``standard`` String representing the language standard. - This field was added in codemodel version 2.2. - ``compileCommandFragments`` Optional member that is present when fragments of the compiler command line invocation are available. The value is a JSON array of entries @@ -1761,6 +1806,8 @@ member's ``nodes`` array. ``frameworks`` + .. codemodel-versionadded:: 2.6 + Optional member that is present when, on Apple platforms, there are frameworks. The value is a JSON array with an entry for each directory. Each entry is a JSON object with members: @@ -1780,9 +1827,9 @@ an unsigned integer 0-based index into the ``backtraceGraph`` member's ``nodes`` array. - This field was added in codemodel version 2.6. - ``precompileHeaders`` + .. codemodel-versionadded:: 2.1 + Optional member that is present when :command:`target_precompile_headers` or other command invocations set :prop_tgt:`PRECOMPILE_HEADERS` on the target. The value is a JSON array with an entry for each header. Each @@ -1798,8 +1845,6 @@ unsigned integer 0-based index into the ``backtraceGraph`` member's ``nodes`` array. - This field was added in codemodel version 2.1. - ``defines`` Optional member that is present when there are preprocessor definitions. The value is a JSON array with an entry for each definition. Each @@ -2089,6 +2134,8 @@ if the path specifies a file in the CMake installation. ``globsDependent`` + .. cmakefiles-versionadded:: 1.1 + Optional member that is present when the project calls :command:`file(GLOB)` or :command:`file(GLOB_RECURSE)` with the ``CONFIGURE_DEPENDS`` option. The value is a JSON array of JSON objects, each specifying a globbing @@ -2096,8 +2143,6 @@ no longer matches the same list of paths, CMake considers the build system to be out of date. - This field was added in ``cmakeFiles`` version 1.1. - The members of each entry are: ``expression`` @@ -2233,6 +2278,8 @@ language. Its value is a JSON string holding the path to the compiler. ``commandFragment`` + .. toolchains-versionadded:: 1.1 + Optional member that is present when the :variable:`CMAKE_<LANG>_COMPILER` variable is a list containing multiple elements or the :envvar:`CC` or similar environment variable contains @@ -2240,8 +2287,6 @@ Its value is a JSON string holding the second and further elements (mandatory arguments to the compiler) as a command line fragment. - This field was added in toolchains version 1.1. - ``id`` Optional member that is present when the :variable:`CMAKE_<LANG>_COMPILER_ID` variable is defined for the current
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index ac25627..51ed27e 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -43,6 +43,12 @@ :variable:`CMAKE_CXX_COMPILER_VERSION <CMAKE_<LANG>_COMPILER_VERSION>` is less than 4.2.0. +.. note:: + + Generator expressions are supported in the values of many properties, + but not all. Check a property's documentation for explicit mention + of generator expressions before using them. + Whitespace And Quoting ====================== @@ -3298,13 +3304,23 @@ Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on (see policy :policy:`CMP0112`). -.. genex:: $<TARGET_OBJECTS:tgt> +.. genex:: $<TARGET_OBJECTS:tgt[,SOURCE_FILES:source_file[;source_file]...]> .. versionadded:: 3.1 List of objects resulting from building ``tgt``. This would typically be used on :ref:`object library <Object Libraries>` targets. + Additional arguments: + + ``SOURCE_FILES:source_file[;source_file]...`` + + .. versionadded:: 4.4 + + An optional list of one or more source files for the target. Only the + object files built from those source files will be returned (but not + necessarily in the same order they were specified). + .. genex:: $<TARGET_RUNTIME_DLLS:tgt> .. versionadded:: 3.21
diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst index ce7ebd9..f209724 100644 --- a/Help/manual/cmake-instrumentation.7.rst +++ b/Help/manual/cmake-instrumentation.7.rst
@@ -17,8 +17,7 @@ build, test and install steps for a CMake project. All interactions with the CMake instrumentation API must specify both an API -version and a Data version. At this time, there is only one version for each of -these: see the `API v1`_ and `Data Version`_. +version and a `Data Version`_. There is only one API version, see the `API v1`_. .. note:: @@ -212,6 +211,11 @@ trace file remains even after `Indexing`_ occurs and all `Callbacks`_ are executed, until the next time `Indexing`_ occurs. +``data/compile-trace/`` + A subset of the collected data, containing any trace files generated by + the compiler, when the ``compileTrace`` `option <v1 Query Files_>`_ is + enabled. + ``cdash/`` Holds temporary files used internally to generate XML content to be submitted to CDash. @@ -229,11 +233,11 @@ version will be generated and sent to the user `Callbacks`_ defined in that query. -Currently, the only supported version is ``1.0``. A new major version number -will be created whenever previously included data is removed or reformatted such -that scripts written to parse this data may become incompatible with the new -format. A new minor version number will be created whenever new data becomes -available. +Currently, the only supported major version is ``1``, and the maximum supported +minor version is also ``1``. A new major version number will be created whenever +previously included data is removed or reformatted such that scripts written to +parse this data may become incompatible with the new format. A new minor version +number will be created whenever new data becomes available. .. _`cmake-instrumentation v1 Query Files`: @@ -256,8 +260,9 @@ for the most recent minor version, even if an earlier minor version is requested. - Currently, the only supported version is ``1.0``. Query files with an unknown - data version will be ignored. + Currently, the only supported major version is ``1``. Query files + requesting a minor version up to the maximum described in `Data Version`_ + are accepted; those with an unknown data version will be ignored. ``callbacks`` A list of command-line strings for `Callbacks`_ to handle collected @@ -300,6 +305,25 @@ generated by CMake, and includes information from immediately before and after the command is executed. + ``captureOutput`` + .. versionadded:: 4.4 + + Enables collection of command output in generated `v1 Snippet Files`_. + When enabled, snippets for ``compile``, ``link``, ``custom``, ``test``, and + ``install`` commands include ``stdout`` and ``stderr`` fields. + + Only available as of data version ``1.1``. + + ``compileTrace`` + .. versionadded:: 4.4 + + Enables collection of JSON files generated by Clang's + ``-ftime-trace`` option. When such files are created for an + instrumented compile command, they are copied into the instrumentation + ``data`` directory and referenced from the compile snippet. + + Only available as of data version ``1.1``. + ``cdashSubmit`` Enables including instrumentation data in CDash. This is equivalent to having the :envvar:`CTEST_USE_INSTRUMENTATION` environment @@ -345,6 +369,7 @@ "options": [ "staticSystemInformation", "dynamicSystemInformation", + "captureOutput", "cdashSubmit", "trace" ] @@ -358,13 +383,13 @@ ``/usr/bin/cmake -P callback.cmake arg index-<timestamp>.json`` will be executed in that order. The index file will contain the ``staticSystemInformation`` data and each snippet file listed in the index will -contain the ``dynamicSystemInformation`` data. Additionally, the index file -will contain the path to the generated `Google Trace File`_. Once both -callbacks have completed, the index file and data files listed by it (including -snippet files, but not the trace file) will be deleted from the project build -tree. The instrumentation data will be present in the XML files submitted to -CDash, but with truncated command strings because ``cdashVerbose`` was not -enabled. +contain the ``dynamicSystemInformation`` data and captured command output. +Additionally, the index file will contain the path to the generated +`Google Trace File`_. Once both callbacks have completed, the index file and +data files listed by it (including snippet files, but not the trace file) will +be deleted from the project build tree. The instrumentation data will be +present in the XML files submitted to CDash, but with truncated command +strings because ``cdashVerbose`` was not enabled. v1 Data Files ------------- @@ -413,8 +438,8 @@ ``<role>-<hash>-<timestamp>.json`` and contain the following data: ``version`` - The `Data Version`_ of the snippet file. Currently the version is - always ``{ "major": 1, "minor": 0 }``. + The `Data Version`_ of the snippet file, written as + ``{ "major": 1, "minor": N }`` where ``N`` is the most recent minor version. ``command`` The full command executed. Excluded when ``role`` is ``build``. @@ -426,6 +451,24 @@ The exit code of the command, an integer. This will be ``null`` when ``role`` is ``build``. + ``stdout`` + .. versionadded:: 4.4 + + The standard output produced by the command. Only included when enabled by + the ``captureOutput`` `option <v1 Query Files_>`_ and when ``role`` is one + of: ``compile``, ``link``, ``custom``, ``install`` or ``test``. For + ``test`` snippets, this field contains the merged standard out and standard + error streams. + + ``stderr`` + .. versionadded:: 4.4 + + The standard error output produced by the command. Only included when + enabled by the ``captureOutput`` `option <v1 Query Files_>`_ and when + ``role`` is one of: ``compile``, ``link``, ``custom``, ``install`` or + ``test``. For ``test`` snippets, error output is merged with ``stdout``, + and the value of ``stderr`` is always empty. + ``role`` The type of command executed, which will be one of the following values: @@ -481,6 +524,16 @@ or ``Debug``. Only included when ``role`` is one of: ``compile``, ``link``, ``custom``, ``install``, ``test``. + ``traceFile`` + .. versionadded:: 4.4 + + A path, relative to the instrumentation ``data`` directory, referencing a + copied JSON file generated by Clang's ``-ftime-trace`` option. Only + included when the ``compileTrace`` `option + <v1 Query Files_>`_ is enabled and ``role`` is + ``compile``. If no JSON file was produced by the compile command, this value + is ``null``. + ``dynamicSystemInformation`` Specifies the dynamic information collected about the host machine CMake is being run from. Data is collected for every snippet file @@ -520,15 +573,18 @@ { "version": { "major": 1, - "minor": 0 + "minor": 1 }, "command" : "\"/usr/bin/c++\" \"-MD\" \"-MT\" \"CMakeFiles/main.dir/main.cxx.o\" \"-MF\" \"CMakeFiles/main.dir/main.cxx.o.d\" \"-o\" \"CMakeFiles/main.dir/main.cxx.o\" \"-c\" \"<src>/main.cxx\"", "role" : "compile", "result" : 1, + "stdout" : "<compiler stdout>", + "stderr" : "<compiler stderr>", "target": "main", "language" : "C++", "outputs" : [ "CMakeFiles/main.dir/main.cxx.o" ], "outputSizes" : [ 0 ], + "traceFile" : "compile-trace/main.cxx-<hash>.json", "source" : "<src>/main.cxx", "config" : "Debug", "dynamicSystemInformation" : @@ -543,6 +599,10 @@ "cmakeContent" : "content/cmake-2025-07-11T12-46-32-0572.json" } +.. versionadded:: 4.4 + The JSON format is described in machine-readable form by + :download:`this JSON schema </manual/instrumentation/snippet-v1-schema.json>`. + .. _`cmake-instrumentation v1 Index File`: v1 Index File @@ -554,8 +614,8 @@ `Callbacks`_ are executed. ``version`` - The `Data Version`_ of the index file. Currently this is always written as: - ``{ "major": 1, "minor": 0 }``. + The `Data Version`_ of the index file, written as + ``{ "major": 1, "minor": N }`` where ``N`` is the most recent minor version. ``buildDir`` The build directory of the CMake project. @@ -641,6 +701,10 @@ "trace": "trace/trace-<timestamp>.json" } +.. versionadded:: 4.4 + The JSON format is described in machine-readable form by + :download:`this JSON schema </manual/instrumentation/index-v1-schema.json>`. + .. _`cmake-instrumentation v1 CMake Content File`: v1 CMake Content File @@ -653,8 +717,8 @@ Each CMake content file contains the following: ``version`` - The `Data Version`_ of the content file. Currently the version is - always ``{ "major": 1, "minor": 0 }``. + The `Data Version`_ of the content file, written as + ``{ "major": 1, "minor": N }`` where ``N`` is the most recent minor version. ``project`` The value of :variable:`CMAKE_PROJECT_NAME`.
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index c6bdacb..9a4f7c0 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst
@@ -36,10 +36,10 @@ .. code-block:: cmake - cmake_minimum_required(VERSION 3.10...4.2) + cmake_minimum_required(VERSION 3.10...4.3) This uses the ``<min>...<policy_max>`` syntax to enable the ``NEW`` behaviors -of policies introduced in CMake 4.2 and earlier while only requiring a +of policies introduced in CMake 4.3 and earlier while only requiring a minimum version of CMake 3.10. The project is expected to work with both the ``OLD`` and ``NEW`` behaviors of policies introduced between those versions. @@ -94,6 +94,22 @@ The following policies are supported. +Policies Introduced by CMake 4.4 +-------------------------------- + +.. toctree:: + :maxdepth: 1 + + CMP0219: Macro invocations preserve backslashes in arguments. </policy/CMP0219> + CMP0218: The CMAKE_WARN_DEPRECATED and CMAKE_ERROR_DEPRECATED variables are ignored. </policy/CMP0218> + CMP0217: The MACROS directory property does not exist anymore. </policy/CMP0217> + CMP0216: Swift targets have a default project name. </policy/CMP0216> + CMP0215: Ninja generators emit Swift modules separately from compilation. </policy/CMP0215> + CMP0214: Honor CMAKE_EXE_LINKER_FLAGS for Swift executable targets. </policy/CMP0214> + CMP0213: file(ARCHIVE_{CREATE,EXTRACT}) encode archive paths as UTF-8 by default. </policy/CMP0213> + CMP0212: add_custom_command DEPENDS does not strip .exe suffixes. </policy/CMP0212> + CMP0211: A file may belong to at most one non-HEADERS file set in a target. </policy/CMP0211> + Policies Introduced by CMake 4.3 --------------------------------
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst index 8bbee0f..732287c 100644 --- a/Help/manual/cmake-presets.7.rst +++ b/Help/manual/cmake-presets.7.rst
@@ -1,4 +1,4 @@ -.. cmake-manual-description: CMakePresets.json +.. cmake-manual-description: CMake Presets Reference cmake-presets(7) **************** @@ -16,12 +16,16 @@ for common ways to configure a project. This may be done to support CI builds, or for users who frequently use the same build. CMake supports two main files, ``CMakePresets.json`` and ``CMakeUserPresets.json``, that allow users to -specify common configure options and share them with others. CMake also -supports files included with the ``include`` field. +specify common configure options and share them with others. + +.. presets-versionadded:: 4 + + CMake also supports files included with the :preset:`include` field. See + `Includes`_ for more details. ``CMakePresets.json`` and ``CMakeUserPresets.json`` live in the project's root directory. They both have exactly the same format, and both are optional -(though at least one must be present if :option:`--preset <cmake --preset>` +(though at least one must be present if :cmake-option:`--preset` is specified). ``CMakePresets.json`` is meant to specify project-wide build details, while ``CMakeUserPresets.json`` is meant for developers to specify their own local build details. @@ -31,6 +35,13 @@ project is using Git, ``CMakePresets.json`` may be tracked, and ``CMakeUserPresets.json`` should be added to the ``.gitignore``. +.. versionadded:: 4.4 + + CMake also supports specifying a file from which to read presets via the + :cmake-option:`--presets-file` option. If this option is specified, neither + of ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be + present, and any presets defined in those files will be ignored/unavailable. + Format ====== @@ -39,84 +50,27 @@ .. literalinclude:: presets/example.json :language: json -Preset files specifying version ``10`` or above may include comments using the -key ``$comment`` at any level within the JSON object to provide documentation. +.. presets-versionadded:: 10 + + Presets files may include comments using the key ``$comment`` at any level + within the JSON object to provide documentation. The root object recognizes the following fields: -.. _`CMakePresets schema`: - -``$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. - -``version`` - A required integer representing the version of the JSON schema. See - `Versions`_ for discussion of the supported versions and the corresponding - version of CMake in which they were added. - -``cmakeMinimumRequired`` - An optional object representing the minimum version of CMake needed to - build this project. This object consists of the following fields: - - ``major`` - An optional integer representing the major version. - - ``minor`` - An optional integer representing the minor version. - - ``patch`` - An optional integer representing the patch version. - -``include`` - An optional array of strings representing files to include. If the filenames - are not absolute, they are considered relative to the current file. - This is allowed in preset files specifying version ``4`` or above. - See `Includes`_ for discussion of the constraints on included files. - -``vendor`` - An optional map containing vendor-specific information. CMake does not - interpret the contents of this field except to verify that it is a map if - it does exist. However, the keys should be a vendor-specific domain name - followed by a ``/``-separated path. For example, the Example IDE 1.0 could - use ``example.com/ExampleIDE/1.0``. The value of each field can be anything - desired by the vendor, though will typically be a map. - -``configurePresets`` - An optional array of `Configure Preset`_ objects. - This is allowed in preset files specifying version ``1`` or above. - -``buildPresets`` - An optional array of `Build Preset`_ objects. - This is allowed in preset files specifying version ``2`` or above. - -``testPresets`` - An optional array of `Test Preset`_ objects. - This is allowed in preset files specifying version ``2`` or above. - -``packagePresets`` - An optional array of `Package Preset`_ objects. - This is allowed in preset files specifying version ``6`` or above. - -``workflowPresets`` - An optional array of `Workflow Preset`_ objects. - This is allowed in preset files specifying version ``6`` or above. +.. include:: presets/root-properties.rst Includes ^^^^^^^^ -``CMakePresets.json`` and ``CMakeUserPresets.json`` can include other files -with the ``include`` field in file version ``4`` and later. Files included -by these files can also include other files. If ``CMakePresets.json`` and -``CMakeUserPresets.json`` are both present, ``CMakeUserPresets.json`` -implicitly includes ``CMakePresets.json``, even with no ``include`` field, -in all versions of the format. +.. presets-versionadded:: 4 -If a preset file contains presets that inherit from presets in another file, +CMake presets files can include other files with the :preset:`include` field. +Files included in this manner can also include other files. If +``CMakePresets.json`` and ``CMakeUserPresets.json`` are both present, +``CMakeUserPresets.json`` implicitly includes ``CMakePresets.json``, even with +no :preset:`include` field, in all versions of the format. + +If a presets file contains presets that inherit from presets in another file, the file must include the other file either directly or indirectly. Include cycles are not allowed among files. If ``a.json`` includes ``b.json``, ``b.json`` cannot include ``a.json``. However, a file may be @@ -126,11 +80,18 @@ guaranteed to be provided by the project. ``CMakeUserPresets.json`` may include files from anywhere. -Starting from version ``7``, the ``include`` field supports -`macro expansion`_, but only ``$penv{}`` macro expansion. Starting from version -``9``, other macro expansions are also available, except for ``$env{}`` and -preset-specific macros, i.e., those derived from the fields inside a preset's -definition like ``presetName``. +.. presets-versionchanged:: 7 + + The :preset:`include` field supports `macro expansion`_, but only ``$penv{}`` + macro expansion. + +.. presets-versionchanged:: 9 + + The :preset:`include` field supports `macro expansion`_, except for + ``$env{}`` and preset-specific macros (i.e., those derived from the fields + inside a preset's definition like ``presetName``). + +.. _`Configure Preset`: Configure Preset ^^^^^^^^^^^^^^^^ @@ -138,1051 +99,69 @@ Each entry of the ``configurePresets`` array is a JSON object that may contain the following fields: -``name`` - A required string representing the machine-friendly name of the preset. - This identifier is used in the :option:`cmake --preset` option. - There must not be two configure presets in the union of ``CMakePresets.json`` - and ``CMakeUserPresets.json`` in the same directory with the same name. - However, a configure preset may have the same name as a build, test, - package, or workflow preset. +.. include:: presets/configurePresets-properties.rst -``hidden`` - An optional boolean specifying whether or not a preset should be hidden. - If a preset is hidden, it cannot be used in the - :option:`--preset <cmake --preset>` argument, will not show up in the - :manual:`CMake GUI <cmake-gui(1)>`, and does not have to have a valid - ``generator`` or ``binaryDir``, even from inheritance. ``hidden`` presets are - intended to be used as a base for other presets to inherit via the - ``inherits`` field. - -``inherits`` - An optional array of strings representing the names of presets to inherit - from. This field can also be a string, which is equivalent to an array - containing one string. - - The preset will inherit all of the fields from the ``inherits`` - presets by default (except ``name``, ``hidden``, ``inherits``, - ``description``, and ``displayName``), but can override them as - desired. If multiple ``inherits`` presets provide conflicting values for - the same field, the earlier preset in the ``inherits`` array will be - preferred. - - A preset can only inherit from another preset that is defined in the - same file or in one of the files it includes (directly or indirectly). - Presets in ``CMakePresets.json`` may not inherit from presets in - ``CMakeUserPresets.json``. - -``condition`` - An optional `Condition`_ object. This is allowed in preset files specifying - version ``3`` or above. - -``vendor`` - An optional map containing vendor-specific information. CMake does not - interpret the contents of this field except to verify that it is a map - if it does exist. However, it should follow the same conventions as the - root-level ``vendor`` field. If vendors use their own per-preset - ``vendor`` field, they should implement inheritance in a sensible manner - when appropriate. - -``displayName`` - An optional string with a human-friendly name of the preset. - -``description`` - An optional string with a human-friendly description of the preset. - -.. _`CMakePresets generator`: - -``generator`` - An optional string representing the :manual:`generator <cmake-generators(7)>` - to use for the preset. If ``generator`` is not specified, it must be - inherited from the ``inherits`` preset (unless this preset is ``hidden``). - In version ``3`` or above, this field may be omitted to fall back to regular - generator discovery procedure. - - Note that for :ref:`Visual Studio generators`, unlike in the command line - :option:`-G <cmake -G>` argument, you cannot include the platform name - in the generator name. Use the ``architecture`` field instead. - -``architecture``, ``toolset`` - Optional fields representing the platform and toolset, respectively, for - :manual:`generators <cmake-generators(7)>` that support them. - - See :option:`cmake -A` option for possible values for ``architecture`` - and :option:`cmake -T` for ``toolset``. - - Each may be either a string or an object with the following fields: - - ``value`` - An optional string representing the value. - - ``strategy`` - An optional string telling CMake how to handle the ``architecture`` or - ``toolset`` field. Valid values are: - - ``"set"`` - Set the respective value. This will result in an error for generators - that do not support the respective field. - - ``"external"`` - Do not set the value, even if the generator supports it. This is - useful if, for example, a preset uses the Ninja generator, and an IDE - knows how to set up the Visual C++ environment from the - ``architecture`` and ``toolset`` fields. In that case, CMake will - ignore the field, but the IDE can use them to set up the environment - before invoking CMake. - - If no ``strategy`` field is given, or if the field uses the string form - rather than the object form, the behavior is the same as ``"set"``. - -.. _`CMakePresets toolchainFile`: - -``toolchainFile`` - An optional string representing the path to the toolchain file. - This field supports `macro expansion`_. If a relative path is specified, - it is calculated relative to the build directory, and if not found, - relative to the source directory. This field takes precedence over any - :variable:`CMAKE_TOOLCHAIN_FILE` value. It is allowed in preset files - specifying version ``3`` or above. - -.. _`CMakePresets graphviz`: - -``graphviz`` - An optional string representing the path to the graphviz input file, - that will contain all the library and executable dependencies - in the project. See the documentation for :option:`cmake --graphviz` for - more details. - - This field supports `macro expansion`_. If a relative path is specified, - it is calculated relative to the current working directory. It is allowed - in preset files specifying version ``10`` or above. - -.. _`CMakePresets binaryDir`: - -``binaryDir`` - An optional string representing the path to the output binary directory. - This field supports `macro expansion`_. If a relative path is specified, - it is calculated relative to the source directory. If ``binaryDir`` is not - specified, it must be inherited from the ``inherits`` preset (unless this - preset is ``hidden``). In version ``3`` or above, this field may be - omitted. - -.. _`CMakePresets installDir`: - -``installDir`` - An optional string representing the path to the installation directory, - which will be used as the :variable:`CMAKE_INSTALL_PREFIX` variable. - This field supports `macro expansion`_. If a relative path is specified, - it is calculated relative to the source directory. This is allowed in - preset files specifying version ``3`` or above. - -``cmakeExecutable`` - An optional string representing the path to the CMake executable to use - for this preset. This is reserved for use by IDEs, and is not used by - CMake itself. IDEs that use this field should expand any macros in it. - -``cacheVariables`` - An optional map of cache variables. The key is the variable name (which - may not be an empty string), and the value is either ``null``, a boolean - (which is equivalent to a value of ``"TRUE"`` or ``"FALSE"`` and a type - of ``BOOL``), a string representing the value of the variable (which - supports `macro expansion`_), or an object with the following fields: - - ``type`` - An optional string representing the type of the variable. - - ``value`` - A required string or boolean representing the value of the variable. - A boolean is equivalent to ``"TRUE"`` or ``"FALSE"``. This field - supports `macro expansion`_. - - Cache variables are inherited through the ``inherits`` field, and the - preset's variables will be the union of its own ``cacheVariables`` and - the ``cacheVariables`` from all its parents. If multiple presets in this - union define the same variable, the standard rules of ``inherits`` are - applied. Setting a variable to ``null`` causes it to not be set, even if - a value was inherited from another preset. - -``environment`` - An optional map of environment variables. The key is the variable name - (which may not be an empty string), and the value is either ``null`` or - a string representing the value of the variable. Each variable is set - regardless of whether or not a value was given to it by the process's - environment. - - This field supports `macro expansion`_, and environment variables in this map - may reference each other, and may be listed in any order, as long as such - references do not cause a cycle (for example, if ``ENV_1`` is - ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` - allows one to prepend or append values to existing environment variables by - accessing only values from the parent environment. - - Environment variables are inherited through the ``inherits`` field, and - the preset's environment will be the union of its own ``environment`` and - the ``environment`` from all its parents. If multiple presets in this - union define the same variable, the standard rules of ``inherits`` are - applied. Setting a variable to ``null`` causes it to not be set, even if - a value was inherited from another preset. - -``warnings`` - An optional object specifying the warnings to enable. The object may - contain the following fields: - - ``dev`` - An optional boolean. Equivalent to passing :option:`-Wdev <cmake -Wdev>` - or :option:`-Wno-dev <cmake -Wno-dev>` on the command line. This may not - be set to ``false`` if ``errors.dev`` is set to ``true``. - - ``deprecated`` - An optional boolean. Equivalent to passing - :option:`-Wdeprecated <cmake -Wdeprecated>` or - :option:`-Wno-deprecated <cmake -Wno-deprecated>` on the command line. - This may not be set to ``false`` if ``errors.deprecated`` is set to - ``true``. - - ``uninitialized`` - An optional boolean. Setting this to ``true`` is equivalent to passing - :option:`--warn-uninitialized <cmake --warn-uninitialized>` on the command - line. - - ``unusedCli`` - An optional boolean. Setting this to ``false`` is equivalent to passing - :option:`--no-warn-unused-cli <cmake --no-warn-unused-cli>` on the command - line. - - ``systemVars`` - An optional boolean. Setting this to ``true`` is equivalent to passing - :option:`--check-system-vars <cmake --check-system-vars>` on the command - line. - -``errors`` - An optional object specifying the errors to enable. The object may - contain the following fields: - - ``dev`` - An optional boolean. Equivalent to passing :option:`-Werror=dev <cmake -Werror>` - or :option:`-Wno-error=dev <cmake -Werror>` on the command line. - This may not be set to ``true`` if ``warnings.dev`` is set to ``false``. - - ``deprecated`` - An optional boolean. Equivalent to passing - :option:`-Werror=deprecated <cmake -Werror>` or - :option:`-Wno-error=deprecated <cmake -Werror>` on the command line. - This may not be set to ``true`` if ``warnings.deprecated`` is set to - ``false``. - -``debug`` - An optional object specifying debug options. The object may contain the - following fields: - - ``output`` - An optional boolean. Setting this to ``true`` is equivalent to passing - :option:`--debug-output <cmake --debug-output>` on the command line. - - ``tryCompile`` - An optional boolean. Setting this to ``true`` is equivalent to passing - :option:`--debug-trycompile <cmake --debug-trycompile>` on the command - line. - - ``find`` - An optional boolean. Setting this to ``true`` is equivalent to passing - :option:`--debug-find <cmake --debug-find>` on the command line. - -.. _`CMakePresets trace`: - -``trace`` - An optional object specifying trace options. This is allowed in preset - files specifying version ``7``. The object may contain the following fields: - - ``mode`` - An optional string that specifies the trace mode. Valid values are: - - ``on`` - Causes a trace of all calls made and from where to be printed. - Equivalent to passing :option:`--trace <cmake --trace>` on the command - line. - - ``off`` - A trace of all calls will not be printed. - - ``expand`` - Causes a trace with variables expanded of all calls made and from where - to be printed. Equivalent to passing :option:`--trace-expand <cmake --trace-expand>` - on the command line. - - ``format`` - An optional string that specifies the format output of the trace. - Valid values are: - - ``human`` - Prints each trace line in a human-readable format. - This is the default format. Equivalent to passing - :option:`--trace-format=human <cmake --trace-format>` - on the command line. - - ``json-v1`` - Prints each line as a separate JSON document. Equivalent to passing - :option:`--trace-format=json-v1 <cmake --trace-format>` - on the command line. - - ``source`` - An optional array of strings representing the paths of source files to - be traced. This field can also be a string, which is equivalent to an - array containing one string. Equivalent to passing - :option:`--trace-source <cmake --trace-source>` on the command line. - - ``redirect`` - An optional string specifying a path to a trace output file. Equivalent - to passing :option:`--trace-redirect <cmake --trace-redirect>` - on the command line. +.. _`Build Preset`: Build Preset ^^^^^^^^^^^^ +.. presets-versionadded:: 2 + Each entry of the ``buildPresets`` array is a JSON object that may contain the following fields: -``name`` - A required string representing the machine-friendly name of the preset. - This identifier is used in the - :option:`cmake --build --preset <cmake--build --preset>` option. - There must not be two build presets in the union of ``CMakePresets.json`` - and ``CMakeUserPresets.json`` in the same directory with the same name. - However, a build preset may have the same name as a configure, test, - package, or workflow preset. +.. include:: presets/buildPresets-properties.rst -``hidden`` - An optional boolean specifying whether or not a preset should be hidden. - If a preset is hidden, it cannot be used in the - :option:`--preset <cmake--build --preset>` argument - and does not have to have a valid ``configurePreset``, even from - inheritance. ``hidden`` presets are intended to be used as a base for - other presets to inherit via the ``inherits`` field. - -``inherits`` - An optional array of strings representing the names of presets to inherit - from. This field can also be a string, which is equivalent to an array - containing one string. - - The preset will inherit all of the fields from the - ``inherits`` presets by default (except ``name``, ``hidden``, - ``inherits``, ``description``, and ``displayName``), but can override - them as desired. If multiple ``inherits`` presets provide conflicting - values for the same field, the earlier preset in the ``inherits`` array - will be preferred. - - A preset can only inherit from another preset that is defined in the - same file or in one of the files it includes (directly or indirectly). - Presets in ``CMakePresets.json`` may not inherit from presets in - ``CMakeUserPresets.json``. - -``condition`` - An optional `Condition`_ object. This is allowed in preset files specifying - version ``3`` or above. - -``vendor`` - An optional map containing vendor-specific information. CMake does not - interpret the contents of this field except to verify that it is a map - if it does exist. However, it should follow the same conventions as the - root-level ``vendor`` field. If vendors use their own per-preset - ``vendor`` field, they should implement inheritance in a sensible manner - when appropriate. - -``displayName`` - An optional string with a human-friendly name of the preset. - -``description`` - An optional string with a human-friendly description of the preset. - -.. _`CMakePresets build environment`: - -``environment`` - An optional map of environment variables. The key is the variable name - (which may not be an empty string), and the value is either ``null`` or - a string representing the value of the variable. Each variable is set - regardless of whether or not a value was given to it by the process's - environment. - - This field supports `macro expansion`_, and environment variables in this map - may reference each other, and may be listed in any order, as long as such - references do not cause a cycle (for example, if ``ENV_1`` is - ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` - allows one to prepend or append values to existing environment variables by - accessing only values from the parent environment. - - Environment variables are inherited through the ``inherits`` field, and - the preset's environment will be the union of its own ``environment`` - and the ``environment`` from all its parents. If multiple presets in - this union define the same variable, the standard rules of ``inherits`` - are applied. Setting a variable to ``null`` causes it to not be set, - even if a value was inherited from another preset. - - .. note:: - - For a CMake project using :module:`ExternalProject` with a configuration - preset having environment variables needed in the ExternalProject, use a - build preset that inherits that configuration preset or the ExternalProject - will not have the environment variables set in the configuration preset. - Example: suppose the host defaults to one compiler (say Clang) - and the user wishes to use another compiler (say GCC). Set configuration - preset environment variables :envvar:`CC` and :envvar:`CXX` and use a build - preset that inherits that configuration preset. Otherwise the - ExternalProject may use a different (system default) compiler than the - top-level CMake project. - -``configurePreset`` - An optional string specifying the name of a configure preset to - associate with this build preset. If ``configurePreset`` is not - specified, it must be inherited from the inherits preset (unless this - preset is hidden). The build directory is inferred from the configure - preset, so the build will take place in the same ``binaryDir`` that the - configuration did. - -``inheritConfigureEnvironment`` - An optional boolean that defaults to true. If true, the environment - variables from the associated configure preset are inherited after all - inherited build preset environments, but before environment variables - explicitly specified in this build preset. - -.. _`CMakePresets build jobs`: - -``jobs`` - An optional integer. Equivalent to passing - :option:`--parallel <cmake--build --parallel>` or ``-j`` on the command line. - If the value is ``0``, it is equivalent to passing ``--parallel`` with - ``<jobs>`` omitted; alternatively, one can define the environment variable - :envvar:`CMAKE_BUILD_PARALLEL_LEVEL` as an empty string using the - ``environment`` field. - - .. versionchanged:: 4.3 - - This field does not accept negative integer values, regardless of the - version in the preset file. - -``targets`` - An optional string or array of strings. Equivalent to passing - :option:`--target <cmake--build --target>` or ``-t`` on the command line. - Vendors may ignore the targets property or hide build presets that - explicitly specify targets. This field supports macro expansion. - -``configuration`` - An optional string. Equivalent to passing - :option:`--config <cmake--build --config>` on the command line. - -``cleanFirst`` - An optional bool. If true, equivalent to passing - :option:`--clean-first <cmake--build --clean-first>` on the command line. - -.. _`CMakePresets resolvePackageReferences`: - -``resolvePackageReferences`` - An optional string that specifies the package resolve mode. This is - allowed in preset files specifying version ``4`` or above. - - Package references are used to define dependencies to packages from - external package managers. Currently only NuGet in combination with the - :ref:`Visual Studio generators` is supported. If there are no targets that - define package references, this option does nothing. Valid values are: - - ``on`` - Causes package references to be resolved before attempting a build. - - ``off`` - Package references will not be resolved. Note that this may cause - errors in some build environments, such as .NET SDK style projects. - - ``only`` - Only resolve package references, but do not perform a build. - - .. note:: - - The command line parameter - :option:`--resolve-package-references <cmake--build --resolve-package-references>` - will take priority over this setting. If the command line parameter is not - provided and this setting is not specified, an environment-specific cache - variable will be evaluated to decide, if package restoration should be - performed. - - When using the :ref:`Visual Studio generators`, package references are - defined using the :prop_tgt:`VS_PACKAGE_REFERENCES` property. Package - references are restored using NuGet. It can be disabled by setting the - :variable:`CMAKE_VS_NUGET_PACKAGE_RESTORE` variable to ``OFF``. This can - also be done from within a configure preset. - -``verbose`` - An optional bool. If true, equivalent to passing - :option:`--verbose <cmake--build --verbose>` on the command line. - -``nativeToolOptions`` - An optional array of strings. Equivalent to passing options after ``--`` - on the command line. The array values support macro expansion. +.. _`Test Preset`: Test Preset ^^^^^^^^^^^ +.. presets-versionadded:: 2 + Each entry of the ``testPresets`` array is a JSON object that may contain the following fields: -``name`` - A required string representing the machine-friendly name of the preset. - This identifier is used in the :option:`ctest --preset` option. - There must not be two test presets in the union of ``CMakePresets.json`` - and ``CMakeUserPresets.json`` in the same directory with the same name. - However, a test preset may have the same name as a configure, build, - package, or workflow preset. +.. include:: presets/testPresets-properties.rst -``hidden`` - An optional boolean specifying whether or not a preset should be hidden. - If a preset is hidden, it cannot be used in the - :option:`--preset <ctest --preset>` argument - and does not have to have a valid ``configurePreset``, even from - inheritance. ``hidden`` presets are intended to be used as a base for - other presets to inherit via the ``inherits`` field. - -``inherits`` - An optional array of strings representing the names of presets to inherit - from. This field can also be a string, which is equivalent to an array - containing one string. - - The preset will inherit all of the fields from the - ``inherits`` presets by default (except ``name``, ``hidden``, - ``inherits``, ``description``, and ``displayName``), but can override - them as desired. If multiple ``inherits`` presets provide conflicting - values for the same field, the earlier preset in the ``inherits`` array - will be preferred. - - A preset can only inherit from another preset that is defined in the - same file or in one of the files it includes (directly or indirectly). - Presets in ``CMakePresets.json`` may not inherit from presets in - ``CMakeUserPresets.json``. - -``condition`` - An optional `Condition`_ object. This is allowed in preset files specifying - version ``3`` or above. - -``vendor`` - An optional map containing vendor-specific information. CMake does not - interpret the contents of this field except to verify that it is a map - if it does exist. However, it should follow the same conventions as the - root-level ``vendor`` field. If vendors use their own per-preset - ``vendor`` field, they should implement inheritance in a sensible manner - when appropriate. - -``displayName`` - An optional string with a human-friendly name of the preset. - -``description`` - An optional string with a human-friendly description of the preset. - -``environment`` - An optional map of environment variables. The key is the variable name - (which may not be an empty string), and the value is either ``null`` or - a string representing the value of the variable. Each variable is set - regardless of whether or not a value was given to it by the process's - environment. - - This field supports `macro expansion`_, and environment variables in this map - may reference each other, and may be listed in any order, as long as such - references do not cause a cycle (for example, if ``ENV_1`` is - ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` - allows one to prepend or append values to existing environment variables by - accessing only values from the parent environment. - - Environment variables are inherited through the ``inherits`` field, and - the preset's environment will be the union of its own ``environment`` - and the ``environment`` from all its parents. If multiple presets in - this union define the same variable, the standard rules of ``inherits`` - are applied. Setting a variable to ``null`` causes it to not be set, - even if a value was inherited from another preset. - -``configurePreset`` - An optional string specifying the name of a configure preset to - associate with this test preset. If ``configurePreset`` is not - specified, it must be inherited from the inherits preset (unless this - preset is hidden). The build directory is inferred from the configure - preset, so tests will run in the same ``binaryDir`` that the - configuration did and build did. - -``inheritConfigureEnvironment`` - An optional boolean that defaults to true. If true, the environment - variables from the associated configure preset are inherited after all - inherited test preset environments, but before environment variables - explicitly specified in this test preset. - -``configuration`` - An optional string. Equivalent to passing - :option:`--build-config <ctest --build-config>` on the command line. - -``overwriteConfigurationFile`` - An optional array of configuration options to overwrite options - specified in the CTest configuration file. Equivalent to passing - :option:`--overwrite <ctest --overwrite>` for each value in the array. - The array values support macro expansion. - -.. _`CMakePresets output`: - -``output`` - An optional object specifying output options. The object may contain the - following fields. - - ``shortProgress`` - An optional bool. If true, equivalent to passing - :option:`--progress <ctest --progress>` on the command line. - - ``verbosity`` - An optional string specifying verbosity level. Must be one of the - following: - - ``default`` - Equivalent to passing no verbosity flags on the command line. - - ``verbose`` - Equivalent to passing :option:`--verbose <ctest --verbose>` on - the command line. - - ``extra`` - Equivalent to passing :option:`--extra-verbose <ctest --extra-verbose>` - on the command line. - - ``debug`` - An optional bool. If true, equivalent to passing - :option:`--debug <ctest --debug>` on the command line. - - ``outputOnFailure`` - An optional bool. If true, equivalent to passing - :option:`--output-on-failure <ctest --output-on-failure>` on the command - line. - - ``quiet`` - An optional bool. If true, equivalent to passing - :option:`--quiet <ctest --quiet>` on the command line. - - ``outputLogFile`` - An optional string specifying a path to a log file. Equivalent to - passing :option:`--output-log <ctest --output-log>` on the command line. - This field supports macro expansion. - -.. _`CMakePresets outputJUnitFile`: - - ``outputJUnitFile`` - An optional string specifying a path to a JUnit file. Equivalent to - passing :option:`--output-junit <ctest --output-junit>` on the command line. - This field supports macro expansion. This is allowed in preset files - specifying version ``6`` or above. - - ``labelSummary`` - An optional bool. If false, equivalent to passing - :option:`--no-label-summary <ctest --no-label-summary>` on the command - line. - - ``subprojectSummary`` - An optional bool. If false, equivalent to passing - :option:`--no-subproject-summary <ctest --no-subproject-summary>` - on the command line. - - ``maxPassedTestOutputSize`` - An optional integer specifying the maximum output for passed tests in - bytes. Equivalent to passing - :option:`--test-output-size-passed <ctest --test-output-size-passed>` - on the command line. - - ``maxFailedTestOutputSize`` - An optional integer specifying the maximum output for failed tests in - bytes. Equivalent to passing - :option:`--test-output-size-failed <ctest --test-output-size-failed>` - on the command line. - -.. _`CMakePresets testOutputTruncation`: - - ``testOutputTruncation`` - An optional string specifying the test output truncation mode. Equivalent - to passing - :option:`--test-output-truncation <ctest --test-output-truncation>` on - the command line. This is allowed in preset files specifying version - ``5`` or above. - - ``maxTestNameWidth`` - An optional integer specifying the maximum width of a test name to - output. Equivalent to passing :option:`--max-width <ctest --max-width>` - on the command line. - -``filter`` - An optional object specifying how to filter the tests to run. The object - may contain the following fields. - - ``include`` - An optional object specifying which tests to include. The object may - contain the following fields. - - ``name`` - An optional string specifying a regex for test names. Equivalent to - passing :option:`--tests-regex <ctest --tests-regex>` on the command - line. This field supports macro expansion. CMake regex syntax is - described under :ref:`string(REGEX) <Regex Specification>`. - - ``label`` - An optional string specifying a regex for test labels. Equivalent to - passing :option:`--label-regex <ctest --label-regex>` on the command - line. This field supports macro expansion. - - ``useUnion`` - An optional bool. Equivalent to passing :option:`--union <ctest --union>` - on the command line. - - ``index`` - An optional object specifying tests to include by test index. The - object may contain the following fields. Can also be an optional - string specifying a file with the command line syntax for - :option:`--tests-information <ctest --tests-information>`. - If specified as a string, this field supports macro expansion. - - ``start`` - An optional integer specifying a test index to start testing at. - - ``end`` - An optional integer specifying a test index to stop testing at. - - ``stride`` - An optional integer specifying the increment. - - ``specificTests`` - An optional array of integers specifying specific test indices to - run. - - ``exclude`` - An optional object specifying which tests to exclude. The object may - contain the following fields. - - ``name`` - An optional string specifying a regex for test names. Equivalent to - passing :option:`--exclude-regex <ctest --exclude-regex>` on the - command line. This field supports macro expansion. - - ``label`` - An optional string specifying a regex for test labels. Equivalent to - passing :option:`--label-exclude <ctest --label-exclude>` on the - command line. This field supports macro expansion. - - ``fixtures`` - An optional object specifying which fixtures to exclude from adding - tests. The object may contain the following fields. - - ``any`` - An optional string specifying a regex for text fixtures to exclude - from adding any tests. Equivalent to - :option:`--fixture-exclude-any <ctest --fixture-exclude-any>` on - the command line. This field supports macro expansion. - - ``setup`` - An optional string specifying a regex for text fixtures to exclude - from adding setup tests. Equivalent to - :option:`--fixture-exclude-setup <ctest --fixture-exclude-setup>` - on the command line. This field supports macro expansion. - - ``cleanup`` - An optional string specifying a regex for text fixtures to exclude - from adding cleanup tests. Equivalent to - :option:`--fixture-exclude-cleanup <ctest --fixture-exclude-cleanup>` - on the command line. This field supports macro expansion. - -``execution`` - An optional object specifying options for test execution. The object may - contain the following fields. - - ``stopOnFailure`` - An optional bool. If true, equivalent to passing - :option:`--stop-on-failure <ctest --stop-on-failure>` on the command - line. - - ``enableFailover`` - An optional bool. If true, equivalent to passing :option:`-F <ctest -F>` - on the command line. - -.. _`CMakePresets test jobs`: - - ``jobs`` - An optional integer. Equivalent to passing - :option:`--parallel <ctest --parallel>` on the command line. If the value - is ``0``, it is equivalent to unbounded parallelism. - - In preset files specifying version ``11`` or above, this field can also be - a string, in which case it must be empty, and is equivalent to passing - ``--parallel`` with ``<jobs>`` omitted. - - .. versionchanged:: 4.3 - - This field does not accept negative integer values, regardless of the - version in the preset file. - - ``resourceSpecFile`` - An optional string. Equivalent to passing - :option:`--resource-spec-file <ctest --resource-spec-file>` on - the command line. This field supports macro expansion. - - ``testLoad`` - An optional integer. Equivalent to passing - :option:`--test-load <ctest --test-load>` on the command line. - - ``showOnly`` - An optional string. Equivalent to passing - :option:`--show-only <ctest --show-only>` on the - command line. The string must be one of the following values: - - ``human`` - - ``json-v1`` - - ``repeat`` - An optional object specifying how to repeat tests. Equivalent to - passing :option:`--repeat <ctest --repeat>` on the command line. - The object must have the following fields. - - ``mode`` - A required string. Must be one of the following values: - - ``until-fail`` - - ``until-pass`` - - ``after-timeout`` - - ``count`` - A required integer. - - ``interactiveDebugging`` - An optional bool. If true, equivalent to passing - :option:`--interactive-debug-mode 1 <ctest --interactive-debug-mode>` - on the command line. If false, equivalent to passing - :option:`--interactive-debug-mode 0 <ctest --interactive-debug-mode>` - on the command line. - - ``scheduleRandom`` - An optional bool. If true, equivalent to passing - :option:`--schedule-random <ctest --schedule-random>` on the command - line. - - ``timeout`` - An optional integer. Equivalent to passing - :option:`--timeout <ctest --timeout>` on the command line. - - ``noTestsAction`` - An optional string specifying the behavior if no tests are found. Must - be one of the following values: - - ``default`` - Equivalent to not passing any value on the command line. - - ``error`` - Equivalent to passing :option:`--no-tests=error <ctest --no-tests>` - on the command line. - - ``ignore`` - Equivalent to passing :option:`--no-tests=ignore <ctest --no-tests>` - on the command line. +.. _`Package Preset`: Package Preset ^^^^^^^^^^^^^^ -Package presets may be used in schema version ``6`` or above. Each entry of -the ``packagePresets`` array is a JSON object that may contain the following -fields: +.. presets-versionadded:: 6 -``name`` - A required string representing the machine-friendly name of the preset. - This identifier is used in the :option:`cpack --preset` option. - There must not be two package presets in the union of ``CMakePresets.json`` - and ``CMakeUserPresets.json`` in the same directory with the same name. - However, a package preset may have the same name as a configure, build, - test, or workflow preset. +Each entry of the ``packagePresets`` array is a JSON object +that may contain the following fields: -``hidden`` - An optional boolean specifying whether or not a preset should be hidden. - If a preset is hidden, it cannot be used in the - :option:`--preset <cpack --preset>` argument - and does not have to have a valid ``configurePreset``, even from - inheritance. ``hidden`` presets are intended to be used as a base for - other presets to inherit via the ``inherits`` field. - -``inherits`` - An optional array of strings representing the names of presets to inherit - from. This field can also be a string, which is equivalent to an array - containing one string. - - The preset will inherit all of the fields from the - ``inherits`` presets by default (except ``name``, ``hidden``, - ``inherits``, ``description``, and ``displayName``), but can override - them as desired. If multiple ``inherits`` presets provide conflicting - values for the same field, the earlier preset in the ``inherits`` array - will be preferred. - - A preset can only inherit from another preset that is defined in the - same file or in one of the files it includes (directly or indirectly). - Presets in ``CMakePresets.json`` may not inherit from presets in - ``CMakeUserPresets.json``. - -``condition`` - An optional `Condition`_ object. - -``vendor`` - An optional map containing vendor-specific information. CMake does not - interpret the contents of this field except to verify that it is a map - if it does exist. However, it should follow the same conventions as the - root-level ``vendor`` field. If vendors use their own per-preset - ``vendor`` field, they should implement inheritance in a sensible manner - when appropriate. - -``displayName`` - An optional string with a human-friendly name of the preset. - -``description`` - An optional string with a human-friendly description of the preset. - -``environment`` - An optional map of environment variables. The key is the variable name - (which may not be an empty string), and the value is either ``null`` or - a string representing the value of the variable. Each variable is set - regardless of whether or not a value was given to it by the process's - environment. - - This field supports `macro expansion`_, and environment variables in this map - may reference each other, and may be listed in any order, as long as such - references do not cause a cycle (for example, if ``ENV_1`` is - ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` - allows one to prepend or append values to existing environment variables by - accessing only values from the parent environment. - - Environment variables are inherited through the ``inherits`` field, and - the preset's environment will be the union of its own ``environment`` - and the ``environment`` from all its parents. If multiple presets in - this union define the same variable, the standard rules of ``inherits`` - are applied. Setting a variable to ``null`` causes it to not be set, - even if a value was inherited from another preset. - -``configurePreset`` - An optional string specifying the name of a configure preset to - associate with this package preset. If ``configurePreset`` is not - specified, it must be inherited from the inherits preset (unless this - preset is hidden). The build directory is inferred from the configure - preset, so packaging will run in the same ``binaryDir`` that the - configuration did and build did. - -``inheritConfigureEnvironment`` - An optional boolean that defaults to true. If true, the environment - variables from the associated configure preset are inherited after all - inherited package preset environments, but before environment variables - explicitly specified in this package preset. - -``generators`` - An optional array of strings representing generators for CPack to use. - -``configurations`` - An optional array of strings representing build configurations for CPack to - package. - -``variables`` - An optional map of variables to pass to CPack, equivalent to - :option:`-D <cpack -D>` arguments. Each key is the name of a variable, and - the value is the string to assign to that variable. - -``configFile`` - An optional string representing the config file for CPack to use. - -``output`` - An optional object specifying output options. Valid keys are: - - ``debug`` - An optional boolean specifying whether or not to print debug information. - A value of ``true`` is equivalent to passing - :option:`--debug <cpack --debug>` on the command line. - - ``verbose`` - An optional boolean specifying whether or not to print verbosely. A value - of ``true`` is equivalent to passing :option:`--verbose <cpack --verbose>` - on the command line. - -``packageName`` - An optional string representing the package name. - - .. note:: - - Due to problems with the implementation, this field does not affect the - name of the final package file produced. Other aspects of the package - may use the value though, leading to inconsistencies. - A future CMake release may address this problem, but until then, it is - recommended that this field not be used. - -``packageVersion`` - An optional string representing the package version. - - .. note:: - - Due to problems with the implementation, this field does not affect the - name of the final package file produced. Other aspects of the package - may use the value though, leading to inconsistencies. - A future CMake release may address this problem, but until then, it is - recommended that this field not be used. - -``packageDirectory`` - An optional string representing the directory in which to place the package. - -``vendorName`` - An optional string representing the vendor name. +.. include:: presets/packagePresets-properties.rst .. _`Workflow Preset`: Workflow Preset ^^^^^^^^^^^^^^^ -Workflow presets may be used in schema version ``6`` or above. Each entry of -the ``workflowPresets`` array is a JSON object that may contain the following -fields: +.. presets-versionadded:: 6 -``name`` - A required string representing the machine-friendly name of the preset. - This identifier is used in the - :option:`cmake --workflow --preset <cmake--workflow --preset>` option. There - must not be two workflow presets in the union of ``CMakePresets.json`` and - ``CMakeUserPresets.json`` in the same directory with the same name. However, - a workflow preset may have the same name as a configure, build, test, or - package preset. +Each entry of the ``workflowPresets`` array is a JSON object +that may contain the following fields: -``vendor`` - An optional map containing vendor-specific information. CMake does not - interpret the contents of this field except to verify that it is a map - if it does exist. However, it should follow the same conventions as the - root-level ``vendor`` field. - -``displayName`` - An optional string with a human-friendly name of the preset. - -``description`` - An optional string with a human-friendly description of the preset. - -``steps`` - A required array of objects describing the steps of the workflow. The first - step must be a configure preset, and all subsequent steps must be - non-configure presets whose ``configurePreset`` field matches the starting - configure preset. Each object may contain the following fields: - - ``type`` - A required string. The first step must be ``configure``. Subsequent steps - must be either ``build``, ``test``, or ``package``. - - ``name`` - A required string representing the name of the configure, build, test, or - package preset to run as this workflow step. +.. include:: presets/workflowPresets-properties.rst Condition ^^^^^^^^^ -The ``condition`` field of a preset, allowed in preset files specifying version -``3`` or above, is used to determine whether or not the preset is enabled. For -example, this can be used to disable a preset on platforms other than Windows. -``condition`` may be either a boolean, ``null``, or an object. If it is a -boolean, the boolean indicates whether the preset is enabled or disabled. If it -is ``null``, the preset is enabled, but the ``null`` condition is not inherited -by any presets that may inherit from the preset. Sub-conditions (for example in -a ``not``, ``anyOf``, or ``allOf`` condition) may not be ``null``. If it is an -object, it has the following fields: +.. presets-versionadded:: 3 + +The ``condition`` field of a preset is used to determine whether or not the +preset is enabled. For example, this can be used to disable a preset on +platforms other than Windows. ``condition`` may be either a boolean, ``null``, +or an object. If it is a boolean, the boolean indicates whether the preset is +enabled or disabled. If it is ``null``, the preset is enabled, but the ``null`` +condition is not inherited by any presets that may inherit from the preset. +Sub-conditions (for example in a ``not``, ``anyOf``, or ``allOf`` condition) +may not be ``null``. If it is an object, it has the following fields: ``type`` A required string with one of the following values: @@ -1196,9 +175,7 @@ A required boolean which provides a constant value for the condition's evaluation. - ``"equals"`` - - ``"notEquals"`` + ``"equals"``, ``"notEquals"`` Indicates that the condition compares two strings to see if they are equal (or not equal). The condition object will have the following additional fields: @@ -1209,9 +186,7 @@ ``rhs`` Second string to compare. This field supports macro expansion. - ``"inList"`` - - ``"notInList"`` + ``"inList"``, ``"notInList"`` Indicates that the condition searches for a string in a list of strings. The condition object will have the following additional fields: @@ -1222,9 +197,7 @@ A required array of strings to search. This field supports macro expansion, and uses short-circuit evaluation. - ``"matches"`` - - ``"notMatches"`` + ``"matches"``, ``"notMatches"`` Indicates that the condition searches for a regular expression in a string. The condition object will have the following additional fields: @@ -1235,9 +208,7 @@ A required regular expression to search for. This field supports macro expansion. - ``"anyOf"`` - - ``"allOf"`` + ``"anyOf"``, ``"allOf"`` Indicates that the condition is an aggregation of zero or more nested conditions. The condition object will have the following additional fields: @@ -1257,12 +228,14 @@ ^^^^^^^^^^^^^^^ As mentioned above, some fields support macro expansion. Macros are -recognized in the form ``$<macro-namespace>{<macro-name>}``. All macros are -evaluated in the context of the preset being used, even if the macro is in a -field that was inherited from another preset. For example, if the ``Base`` -preset sets variable ``PRESET_NAME`` to ``${presetName}``, and the -``Derived`` preset inherits from ``Base``, ``PRESET_NAME`` will be set to -``Derived``. +recognized in the form ``$<macro-namespace>{<macro-name>}``. + +In general, macros are evaluated in the context of the preset being used, even +if the macro is in a field that was inherited from another preset. For example, +if the ``Base`` preset sets variable ``PRESET_NAME`` to ``${presetName}``, and +the ``Derived`` preset inherits from ``Base``, ``PRESET_NAME`` will be set to +``Derived``. The ``${fileDir}`` macro as of preset version ``12`` is an +exception to this rule. It is an error to not put a closing brace at the end of a macro name. For example, ``${sourceDir`` is invalid. A dollar sign (``$``) followed by @@ -1297,15 +270,47 @@ .. _`CMakePresets hostSystemName`: ``${hostSystemName}`` + .. presets-versionadded:: 3 + The name of the host operating system. Contains the same value as - :variable:`CMAKE_HOST_SYSTEM_NAME`. This is allowed in preset files - specifying version ``3`` or above. + :variable:`CMAKE_HOST_SYSTEM_NAME`. .. _`CMakePresets fileDir`: ``${fileDir}`` - Path to the directory containing the preset file which contains the macro. - This is allowed in preset files specifying version ``4`` or above. + .. presets-versionadded:: 4 + + Path to the directory containing the presets file which defines the preset + being used. + + .. presets-versionchanged:: 12 + + This macro *always* expands to the directory of the current presets file + containing the macro, regardless of the preset being used. + + For example, consider the following scenario. + + * ``/path/to/CMakePresets.json`` includes + ``/path/to/subdir/CMakePresets.json``. + * ``/path/to/subdir/CMakePresets.json`` defines preset ``Base``, which + sets variable ``MY_DIR`` to ``${fileDir}``. + * ``/path/to/CMakePresets.json`` defines preset ``Derived``, and + ``Derived`` inherits from ``Base``. + + Under preset versions ``4``-``11``, ``MY_DIR`` will be set to ``/path/to/`` + when using the ``Derived`` preset, and ``/path/to/subdir/`` when using the + ``Base`` preset. + + When ``/path/to/subdir/CMakePresets.json`` specifies version ``12`` or + above, ``MY_DIR`` will always be set to ``/path/to/subdir/``, regardless of + the preset being used. + + .. note:: + + Since the ``${fileDir}`` macro in version 12 is expanded in the context + of the current presets file, it is the version of the current file, rather + than the version of the root file containing the preset being used, which + dictates this behavior. ``${dollar}`` A literal dollar sign (``$``). @@ -1313,6 +318,8 @@ .. _`CMakePresets pathListSep`: ``${pathListSep}`` + .. presets-versionadded:: 5 + Native character for separating lists of paths, such as ``:`` or ``;``. For example, by setting ``PATH`` to @@ -1320,8 +327,6 @@ expand to the underlying operating system's character used for concatenation in ``PATH``. - This is allowed in preset files specifying version ``5`` or above. - ``$env{<variable-name>}`` Environment variable with name ``<variable-name>``. The variable name may not be an empty string. If the variable is defined in the ``environment`` @@ -1358,9 +363,8 @@ Versions ======== -The JSON schema of ``CMakePresets.json`` and ``CMakeUserPresets.json`` -follows a version scheme where new versions are added and allowed in newer -versions of CMake. +The JSON schema of CMake presets files follows a version scheme where new +versions are added and allowed in newer versions of CMake. A list of the supported versions along with the version of CMake in which they were added and a summary of the new features and changes is given below. @@ -1384,10 +388,10 @@ `Build <Build Preset_>`_, and `Test Presets <Test Preset_>`_. * Changes to `Configure Presets <Configure Preset_>`_ - * The `installDir <CMakePresets installDir_>`_ field was added. - * The `toolchainFile <CMakePresets toolchainFile_>`_ field was added. - * The `binaryDir <CMakePresets binaryDir_>`_ field is now optional. - * The `generator <CMakePresets generator_>`_ field is now optional. + * The :preset:`configurePresets.installDir` field was added. + * The :preset:`configurePresets.toolchainFile` field was added. + * The :preset:`configurePresets.binaryDir` field is now optional. + * The :preset:`configurePresets.generator` field is now optional. * Changes to `Macro Expansion`_ @@ -1401,9 +405,7 @@ ``CMakePresets.json`` and ``CMakeUserPresets.json``. * Changes to `Build Presets <Build Preset_>`_ - * The - `resolvePackageReferences <CMakePresets resolvePackageReferences_>`_ - field was added. + * The :preset:`buildPresets.resolvePackageReferences` field was added. * Changes to `Macro Expansion`_ @@ -1414,8 +416,8 @@ * Changes to `Test Presets <Test Preset_>`_ - * The `testOutputTruncation <CMakePresets testOutputTruncation_>`_ - field was added to the `output <CMakePresets output_>`_ object. + * The :preset:`testPresets.output.testOutputTruncation` field was added + to the :preset:`testPresets.output` object. * Changes to `Macro Expansion`_ @@ -1428,31 +430,32 @@ * `Workflow Presets <Workflow Preset_>`_ were added. * Changes to `Test Presets <Test Preset_>`_ - * The `outputJUnitFile <CMakePresets outputJUnitFile_>`_ field was added - to the `output <CMakePresets output_>`_ object. + * The :preset:`testPresets.output.outputJUnitFile` field was added to the + :preset:`testPresets.output` object. ``7`` .. versionadded:: 3.27 * Changes to `Configure Presets <Configure Preset_>`_ - * The `trace <CMakePresets trace_>`_ field was added. + * The :preset:`configurePresets.trace` field was added. * Changes to `Includes`_ - * The ``include`` field now supports ``$penv{}`` `macro expansion`_. + * The :preset:`include` field now supports ``$penv{}`` `macro expansion`_. ``8`` .. versionadded:: 3.28 - * The `$schema <CMakePresets schema_>`_ field was added to the root object. + * The :preset:`$schema` field was added to the root object. ``9`` .. versionadded:: 3.30 * Changes to `Includes`_ - * The ``include`` field now supports other types of `macro expansion`_. + * The :preset:`include` field now supports other types of + `macro expansion`_. ``10`` .. versionadded:: 3.31 @@ -1461,19 +464,45 @@ throughout ``CMakePresets.json`` and ``CMakeUserPresets.json``. * Changes to `Configure Presets <Configure Preset_>`_: - * The `graphviz <CMakePresets graphviz_>`_ field was added. + * The :preset:`configurePresets.graphviz` field was added. ``11`` .. versionadded:: 4.3 * Changes to `Test Presets <Test Preset_>`_ - * The `jobs <CMakePresets test jobs_>`_ field now accepts an empty string - representing :option:`--parallel <ctest --parallel>` with ``<jobs>`` - omitted. + * The :preset:`testPresets.execution.jobs` field now accepts an empty + string representing :ctest-option:`--parallel` with ``<jobs>`` omitted. + + ``12`` + .. versionadded:: 4.4 + + * Changes to `Configure Presets <Configure Preset_>`_: + + * The ``dev`` field is renamed to ``author`` in + :preset:`configurePresets.warnings` and + :preset:`configurePresets.errors`. + + * The ``uninitialized`` and ``unusedCli`` fields were added to + :preset:`configurePresets.errors`. + + * The ``installAbsoluteDestination`` field was added to + :preset:`configurePresets.warnings` and :preset:`configurePresets.errors`. + + * Changes to `Macro Expansion`_ + + * The `${fileDir} <CMakePresets fileDir_>`_ macro now always expands to + the directory of presets file containing the ``${fileDir}`` macro, + regardless of whether it is inherited by another preset in a different + directory. + + * Changes to `Test Presets <Test Preset_>`_ + + * The :preset:`testPresets.execution.testPassthroughArguments` field was + added to forward arguments to test executables. Schema ====== :download:`This file </manual/presets/schema.json>` provides a machine-readable -JSON schema for the ``CMakePresets.json`` format. +JSON schema for the CMake presets file format.
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 2102fc5..fc16f2d 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst
@@ -84,7 +84,6 @@ /prop_dir/LINK_DIRECTORIES /prop_dir/LINK_OPTIONS /prop_dir/LISTFILE_STACK - /prop_dir/MACROS /prop_dir/PARENT_DIRECTORY /prop_dir/RULE_LAUNCH_COMPILE /prop_dir/RULE_LAUNCH_CUSTOM @@ -307,6 +306,7 @@ /prop_tgt/INTERFACE_LINK_OPTIONS /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE /prop_tgt/INTERFACE_PRECOMPILE_HEADERS + /prop_tgt/INTERFACE_SOURCE_SETS /prop_tgt/INTERFACE_SOURCES /prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES /prop_tgt/INTERPROCEDURAL_OPTIMIZATION @@ -405,9 +405,15 @@ /prop_tgt/RUNTIME_OUTPUT_DIRECTORY_CONFIG /prop_tgt/RUNTIME_OUTPUT_NAME /prop_tgt/RUNTIME_OUTPUT_NAME_CONFIG + /prop_tgt/Rust_EDITION /prop_tgt/SKIP_BUILD_RPATH /prop_tgt/SKIP_LINTING /prop_tgt/SOURCE_DIR + /prop_tgt/SOURCE_DIRS + /prop_tgt/SOURCE_DIRS_NAME + /prop_tgt/SOURCE_SET + /prop_tgt/SOURCE_SET_NAME + /prop_tgt/SOURCE_SETS /prop_tgt/SOURCES /prop_tgt/SOVERSION /prop_tgt/SPDX_LICENSE @@ -420,6 +426,8 @@ /prop_tgt/Swift_LANGUAGE_VERSION /prop_tgt/Swift_MODULE_DIRECTORY /prop_tgt/Swift_MODULE_NAME + /prop_tgt/Swift_PACKAGE_NAME + /prop_tgt/Swift_SEPARATE_MODULE_EMISSION /prop_tgt/SYMBOLIC /prop_tgt/SYSTEM /prop_tgt/TEST_LAUNCHER @@ -525,6 +533,32 @@ /prop_tgt/XCODE_XCCONFIG /prop_tgt/XCTEST +.. _`File Set Properties`: + +Properties on File Sets +======================= + +.. toctree:: + :maxdepth: 1 + + /prop_fs/BASE_DIRS + /prop_fs/COMPILE_DEFINITIONS + /prop_fs/COMPILE_OPTIONS + /prop_fs/CXX_SCAN_FOR_MODULES + /prop_fs/JOB_POOL_COMPILE + /prop_fs/INCLUDE_DIRECTORIES + /prop_fs/INDEPENDENT_FILES + /prop_fs/INTERFACE_COMPILE_DEFINITIONS + /prop_fs/INTERFACE_COMPILE_OPTIONS + /prop_fs/INTERFACE_INCLUDE_DIRECTORIES + /prop_fs/INTERFACE_SOURCES + /prop_fs/SCOPE + /prop_fs/SKIP_LINTING + /prop_fs/SKIP_PRECOMPILE_HEADERS + /prop_fs/SKIP_UNITY_BUILD_INCLUSION + /prop_fs/SOURCES + /prop_fs/TYPE + .. _`Test Properties`: Properties on Tests @@ -594,6 +628,7 @@ /prop_sf/OBJECT_DEPENDS /prop_sf/OBJECT_NAME /prop_sf/OBJECT_OUTPUTS + /prop_sf/Rust_EMIT /prop_sf/SKIP_AUTOGEN /prop_sf/SKIP_AUTOMOC /prop_sf/SKIP_AUTORCC @@ -670,6 +705,7 @@ /prop_dir/COMPILE_DEFINITIONS_CONFIG /prop_dir/INTERPROCEDURAL_OPTIMIZATION /prop_dir/INTERPROCEDURAL_OPTIMIZATION_CONFIG + /prop_dir/MACROS /prop_dir/TEST_INCLUDE_FILE
diff --git a/Help/manual/cmake-qt.7.rst b/Help/manual/cmake-qt.7.rst index fe269c2..80b3698 100644 --- a/Help/manual/cmake-qt.7.rst +++ b/Help/manual/cmake-qt.7.rst
@@ -231,31 +231,58 @@ them to the :prop_tgt:`AUTOGEN_TARGET_DEPENDS` target property. .. note:: - If Qt 5.15 or later is used and the generator is either :generator:`Ninja` or - :ref:`Makefile Generators`, see :ref:`<ORIGIN>_autogen_timestamp_deps`. + If Qt 5.15 or later is used and a depfile is used to track the ``moc`` + dependencies, see :ref:`<ORIGIN>_autogen_timestamp_deps`. When using the + :ref:`Visual Studio Generators`, the ``<ORIGIN>_autogen`` target may not be + created at all, see `Visual Studio Generators`_ below. .. _`<ORIGIN>_autogen_timestamp_deps`: The ``<ORIGIN>_autogen_timestamp_deps`` target ============================================== -If Qt 5.15 or later is used and the generator is either :generator:`Ninja` or -:ref:`Makefile Generators`, the ``<ORIGIN>_autogen_timestamp_deps`` target is -also created in addition to the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` +If Qt 5.15 or later is used and the generator is :generator:`Ninja`, a +:ref:`Makefile <Makefile Generators>` generator, a +:ref:`Visual Studio <Visual Studio Generators>` generator or +:generator:`Xcode`, the ``moc`` dependencies are tracked with a depfile. In +that case the ``<ORIGIN>_autogen_timestamp_deps`` target is also created in +addition to the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target. This target does not have any sources or commands to execute, but it has dependencies that were previously inherited by the pre-Qt 5.15 :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target. These dependencies will serve as a list of order-only dependencies for the custom command, without forcing the custom command to re-execute. +.. versionadded:: 4.4 + Depfile support for the :ref:`Visual Studio Generators` and + :generator:`Xcode`. Earlier versions used a depfile only for the + :generator:`Ninja` and :ref:`Makefile Generators`. + +.. note:: + When using the :ref:`Visual Studio Generators`, neither this target nor the + :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target is created in the common + case, see `Visual Studio Generators`_ below. + Visual Studio Generators ======================== -When using the :ref:`Visual Studio Generators`, CMake -generates a ``PRE_BUILD`` :command:`custom command <add_custom_command>` -instead of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` +When using the :ref:`Visual Studio Generators`, CMake adds the ``moc`` and +``uic`` step to the ``<ORIGIN>`` project itself instead of creating the +:ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` :command:`custom target <add_custom_target>` (for :prop_tgt:`AUTOMOC` and -:prop_tgt:`AUTOUIC`). This isn't always possible though and an +:prop_tgt:`AUTOUIC`). With Qt 5.15 or later, the step is a +:command:`custom command <add_custom_command>` with a depfile, and therefore +runs only when one of its dependencies changed. With earlier Qt versions it is +a ``PRE_BUILD`` command that runs on every build of ``<ORIGIN>``. + +.. versionchanged:: 4.4.3 + With Qt 5.15 or later, the ``moc`` and ``uic`` step is a custom command in + the ``<ORIGIN>`` project. CMake 4.4.0 through 4.4.2 created the + :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` and + :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` + targets instead. + +This isn't always possible though and an :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` :command:`custom target <add_custom_target>` is used, when either @@ -266,6 +293,10 @@ - :prop_tgt:`AUTOGEN_TARGET_DEPENDS` lists a source file - :variable:`CMAKE_GLOBAL_AUTOGEN_TARGET` is enabled +With Qt 5.15 or later, the +:ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` target +is created along with it. + qtmain.lib on Windows =====================
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index f9ca444..1ba0cc9 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst
@@ -142,6 +142,8 @@ /variable/CMAKE_Swift_COMPILATION_MODE /variable/CMAKE_Swift_MODULE_DIRECTORY /variable/CMAKE_Swift_NUM_THREADS + /variable/CMAKE_Swift_SEPARATE_MODULE_EMISSION + /variable/CMAKE_TEST_BUILD_DEPENDS /variable/CMAKE_TEST_LAUNCHER /variable/CMAKE_TOOLCHAIN_FILE /variable/CMAKE_TWEAK_VERSION @@ -223,7 +225,6 @@ /variable/CMAKE_ECLIPSE_MAKE_ARGUMENTS /variable/CMAKE_ECLIPSE_RESOURCE_ENCODING /variable/CMAKE_ECLIPSE_VERSION - /variable/CMAKE_ERROR_DEPRECATED /variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION /variable/CMAKE_EXECUTE_PROCESS_COMMAND_ECHO /variable/CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL @@ -317,7 +318,6 @@ /variable/CMAKE_TLS_VERIFY /variable/CMAKE_TLS_VERSION /variable/CMAKE_USER_MAKE_RULES_OVERRIDE - /variable/CMAKE_WARN_DEPRECATED /variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION /variable/CMAKE_XCODE_GENERATE_SCHEME /variable/CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY @@ -577,6 +577,7 @@ /variable/CMAKE_POSITION_INDEPENDENT_CODE /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY_CONFIG + /variable/CMAKE_Rust_EDITION /variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS /variable/CMAKE_SHARED_LINKER_FLAGS /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG @@ -738,12 +739,14 @@ /variable/CTEST_BINARY_DIRECTORY /variable/CTEST_BUILD_COMMAND /variable/CTEST_BUILD_NAME + /variable/CTEST_BUILD_PRESET /variable/CTEST_BZR_COMMAND /variable/CTEST_BZR_UPDATE_OPTIONS /variable/CTEST_CHANGE_ID /variable/CTEST_CHECKOUT_COMMAND /variable/CTEST_CONFIGURATION_TYPE /variable/CTEST_CONFIGURE_COMMAND + /variable/CTEST_CONFIGURE_PRESET /variable/CTEST_COVERAGE_COMMAND /variable/CTEST_COVERAGE_EXTRA_FLAGS /variable/CTEST_CUSTOM_COVERAGE_EXCLUDE @@ -792,18 +795,23 @@ /variable/CTEST_P4_COMMAND /variable/CTEST_P4_OPTIONS /variable/CTEST_P4_UPDATE_OPTIONS + /variable/CTEST_PRESET + /variable/CTEST_PRESETS_FILE /variable/CTEST_RESOURCE_SPEC_FILE /variable/CTEST_RUN_CURRENT_SCRIPT /variable/CTEST_SCRIPT_DIRECTORY /variable/CTEST_SITE /variable/CTEST_SOURCE_DIRECTORY /variable/CTEST_SUBMIT_INACTIVITY_TIMEOUT + /variable/CTEST_SUBMIT_PARTS /variable/CTEST_SUBMIT_URL /variable/CTEST_SVN_COMMAND /variable/CTEST_SVN_OPTIONS /variable/CTEST_SVN_UPDATE_OPTIONS /variable/CTEST_TEST_LOAD + /variable/CTEST_TEST_PRESET /variable/CTEST_TEST_TIMEOUT + /variable/CTEST_TEST_COVERAGE_TOOL /variable/CTEST_TLS_VERIFY /variable/CTEST_TLS_VERSION /variable/CTEST_UPDATE_COMMAND @@ -875,8 +883,10 @@ /variable/CMAKE_AUTOMOC_RELAXED_MODE /variable/CMAKE_BACKWARDS_COMPATIBILITY + /variable/CMAKE_ERROR_DEPRECATED /variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY /variable/CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY + /variable/CMAKE_WARN_DEPRECATED Deprecated Variables that Describe the System =============================================
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index 62b8074..996603a 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst
@@ -99,9 +99,8 @@ This chooses the kind of buildsystem to generate. See the :manual:`cmake-generators(7)` manual for documentation of all generators. Run :option:`cmake --help` to see a list of generators available locally. - Optionally use the :option:`-G <cmake -G>` option below to specify a - generator, or simply accept the default CMake chooses for the current - platform. + Optionally use the :cmake-option:`-G` option below to specify a generator, + or simply accept the default CMake chooses for the current platform. When using one of the :ref:`Command-Line Build Tool Generators` CMake expects that the environment needed by the compiler toolchain @@ -158,11 +157,11 @@ In all cases the ``<options>`` may be zero or more of the `Options`_ below. The above styles for specifying the source and build trees may be mixed. -Paths specified with :option:`-S <cmake -S>` or :option:`-B <cmake -B>` -are always classified as source or build trees, respectively. Paths -specified with plain arguments are classified based on their content -and the types of paths given earlier. If only one type of path is given, -the current working directory (cwd) is used for the other. For example: +Paths specified with :cmake-option:`-S` or :cmake-option:`-B` are always +classified as source or build trees, respectively. Paths specified with plain +arguments are classified based on their content and the types of paths given +earlier. If only one type of path is given, the current working directory +(cwd) is used for the other. For example: ============================== ============ =========== Command Line Source Dir Build Dir @@ -225,12 +224,12 @@ List non-advanced cached variables. List ``CACHE`` variables will run CMake and list all the variables from - the CMake ``CACHE`` that are not marked as ``INTERNAL`` or :prop_cache:`ADVANCED`. - This will effectively display current CMake settings, which can then be - changed with :option:`-D <cmake -D>` option. Changing some of the variables - may result in more variables being created. If ``A`` is specified, then it - will display also advanced variables. If ``H`` is specified, it will also - display help for each variable. + the CMake ``CACHE`` that are not marked as ``INTERNAL`` or + :prop_cache:`ADVANCED`. This will effectively display current CMake + settings, which can then be changed with :cmake-option:`-D` option. + Changing some of the variables may result in more variables being created. + If ``A`` is specified, then it will display also advanced variables. + If ``H`` is specified, it will also display help for each variable. .. option:: -LR[A][H] <regex> @@ -427,8 +426,8 @@ synonym for this option. .. versionadded:: 3.25 - See the :command:`cmake_language` command for a way to - :ref:`query the current message logging level <query_message_log_level>`. + See the :command:`cmake_language(GET_MESSAGE_LOG_LEVEL)` command for a way + to query the current message logging level. .. option:: --log-context @@ -499,7 +498,7 @@ is an entry in the given comma-separated list of case-sensitive package names. - Like :option:`--debug-find <cmake --debug-find>`, but limiting scope + Like :cmake-option:`--debug-find`, but limiting scope to the specified packages. .. option:: --debug-find-var=<var>[,...] @@ -510,7 +509,7 @@ as the result variable, where ``<var>`` is an entry in the given comma-separated list. - Like :option:`--debug-find <cmake --debug-find>`, but limiting scope + Like :cmake-option:`--debug-find`, but limiting scope to the specified variable names. .. option:: --trace @@ -523,7 +522,7 @@ Put cmake in trace mode. - Like :option:`--trace <cmake --trace>`, but with variables expanded. + Like :cmake-option:`--trace`, but with variables expanded. .. option:: --trace-format=<format> @@ -624,22 +623,23 @@ .. option:: --warn-uninitialized - Warn about uninitialized values. + .. deprecated:: 4.4 - Print a warning when an uninitialized variable is used. + Compatibility synonym for ``-Wuninitialized``. .. option:: --warn-unused-vars + .. deprecated:: 3.19 + Does nothing. In CMake versions 3.2 and below this enabled warnings about unused variables. In CMake versions 3.3 through 3.18 the option was broken. In CMake 3.19 and above the option has been removed. .. option:: --no-warn-unused-cli - Don't warn about command line options. + .. deprecated:: 4.4 - Don't find variables that are declared on the command line, but not - used. + Compatibility synonym for ``-Wno-unused-cli``. .. option:: --check-system-vars @@ -669,9 +669,8 @@ .. versionadded:: 3.18 - Used in conjunction with - :option:`--profiling-format <cmake --profiling-format>` to output to a - given path. + Used in conjunction with :cmake-option:`--profiling-format` + to output to a given path. .. option:: --profiling-format=<file> @@ -687,48 +686,75 @@ .. option:: --preset <preset>, --preset=<preset> - Reads a :manual:`preset <cmake-presets(7)>` from ``CMakePresets.json`` and - ``CMakeUserPresets.json`` files, which must be located in the same directory - as the top level ``CMakeLists.txt`` file. The preset may specify the - generator, the build directory, a list of variables, and other arguments to - pass to CMake. At least one of ``CMakePresets.json`` or - ``CMakeUserPresets.json`` must be present. + Reads a :manual:`preset <cmake-presets(7)>` from CMake presets files. + ``CMakePresets.json`` and ``CMakeUserPresets.json`` are the default files, + which must be located in the same directory as the top level + ``CMakeLists.txt`` file. A file can also be specified with + :cmake-option:`--presets-file`. + + The preset may specify the generator, the build directory, a list of + variables, and other arguments to pass to CMake. + The :manual:`CMake GUI <cmake-gui(1)>` also recognizes and supports ``CMakePresets.json`` and ``CMakeUserPresets.json`` files. For full details on these files, see :manual:`cmake-presets(7)`. The presets are read before all other command line options, although the - :option:`-S <cmake -S>` option can be used to specify the source directory + :cmake-option:`-S` option can be used to specify the source directory containing the ``CMakePresets.json`` and ``CMakeUserPresets.json`` files. - If :option:`-S <cmake -S>` is not given, the current directory is assumed to - be the top level source directory and must contain the presets files. The - options specified by the chosen preset (variables, generator, etc.) can all - be overridden by manually specifying them on the command line. For example, - if the preset sets a variable called ``MYVAR`` to ``1``, but the user sets - it to ``2`` with a ``-D`` argument, the value ``2`` is preferred. + If neither :cmake-option:`-S` nor :cmake-option:`--presets-file` are given, + the current working directory is assumed to be the top level source directory + and must contain ``CMakePresets.json`` and/or ``CMakeUserPresets.json``. + + The options specified by the chosen preset (variables, generator, etc.) + can all be overridden by manually specifying them on the command line. + For example, if the preset sets a variable called ``MYVAR`` to ``1``, + but the user sets it to ``2`` with a :cmake-option:`-D` argument, + the value ``2`` is preferred. .. versionadded:: 3.21 - The ``-B`` option may optionally be specified with a different binary - directory than the one specified by the ``binaryDir`` key of the - configure preset. + The :cmake-option:`-B` option may optionally be specified with a different + binary directory than the one specified by the + :preset:`configurePresets.binaryDir` field. + + .. versionchanged:: 4.4 + If :cmake-option:`--presets-file` is specified, neither of + ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be + present. In prior versions, the presence of these files in the top-level + source directory (whether via :cmake-option:`-S` or the current working + directory) was strictly required. + +.. option:: --presets-file <file>, --presets-file=<file> + + .. versionadded:: 4.4 + + Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The + specified path may be absolute or relative to the current working directory. + If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and + ``CMakeUserPresets.json`` will be ignored. .. option:: --list-presets[=<type>] Lists the available presets of the specified ``<type>``. Valid values for ``<type>`` are ``configure``, ``build``, ``test``, ``package``, or ``all``. - If ``<type>`` is omitted, ``configure`` is assumed. The current working - directory must contain CMake preset files unless the :option:`-S <cmake -S>` - option is used to specify a different top level source directory. + If ``<type>`` is omitted, ``configure`` is assumed. + + .. versionchanged:: 4.4 + If :cmake-option:`--presets-file` is specified, the presets defined in the + given ``<file>`` will be listed. Otherwise, the top-level source directory + (whether via :cmake-option:`-S` or the current working directory) must + contain ``CMakePresets.json`` and/or ``CMakeUserPresets.json``. + In prior versions, the latter was strictly required. .. option:: --debugger - Enables interactive debugging of the CMake language. CMake exposes a debugging - interface on the pipe named by :option:`--debugger-pipe <cmake --debugger-pipe>` - that conforms to the `Debug Adapter Protocol`_ specification with the following - modifications. + Enables interactive debugging of the CMake language. CMake exposes a + debugging interface on the pipe named by :cmake-option:`--debugger-pipe` + that conforms to the `Debug Adapter Protocol`_ specification with the + following modifications. - The ``initialize`` response includes an additional field named ``cmakeVersion`` - which specifies the version of CMake being debugged. + The ``initialize`` response includes an additional field named + ``cmakeVersion`` which specifies the version of CMake being debugged. .. code-block:: json :caption: Debugger initialize response @@ -779,8 +805,8 @@ .. code-block:: shell - cmake --build <dir> [<options>] [-- <build-tool-options>] - cmake --build --preset <preset> [<options>] [-- <build-tool-options>] + cmake --build <dir> [<options>] [-- <build-tool-options>] + cmake --build [<dir>] --preset <preset> [<options>] [-- <build-tool-options>] This abstracts a native build tool's command-line interface with the following options: @@ -794,19 +820,41 @@ .. option:: --preset <preset>, --preset=<preset> - Use a build preset to specify build options. The project binary directory - is inferred from the ``configurePreset`` key unless a directory is specified - after ``--build``. The current working directory must contain CMake preset - files. See :manual:`preset <cmake-presets(7)>` for more details. + Use a build :manual:`preset <cmake-presets(7)>` to specify build options. + The project binary directory is inferred from the + :preset:`buildPresets.configurePreset` key unless a directory is specified + after ``--build``. .. versionadded:: 4.3 ``cmake --build`` now supports specifying a build directory and preset together. + .. versionchanged:: 4.4 + ``cmake --build <dir> --preset`` no longer needs to be called from the + directory containing ``CMakePresets.json`` or ``CMakeUserPresets.json``. + If :cmake-build-option:`--presets-file` is specified, CMake will use that + file; otherwise, the presets file(s) can be inferred from the current + build directory's ``CMakeCache.txt``. + +.. option:: --presets-file <file>, --presets-file=<file> + + .. versionadded:: 4.4 + + Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The + specified path may be absolute or relative to the current working directory. + If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and + ``CMakeUserPresets.json`` will be ignored. + .. option:: --list-presets - Lists the available build presets. The current working directory must - contain CMake preset files. + Lists the available build presets. + + .. versionchanged:: 4.4 + ``cmake --build <dir> --list-presets`` no longer needs to be called from + the directory containing ``CMakePresets.json`` or ``CMakeUserPresets.json``. + If :cmake-build-option:`--presets-file` is specified, only presets defined + in the given ``<file>`` will be listed; otherwise, the presets file(s) are + inferred from the current build directory's ``CMakeCache.txt``. .. option:: -j [<jobs>], --parallel [<jobs>] @@ -922,6 +970,13 @@ Component-based install. Only install component ``<comp>``. + .. versionadded:: 4.4 + + Supports installing more than one component: + + * ``--component <compA> <compB>`` + * ``--component <compA> --component <compB>`` + .. option:: --default-directory-permissions <permissions> Default directory install permissions. Permissions in format ``<u=rwx,g=rx,o=rx>``. @@ -1115,7 +1170,7 @@ A JSON object with version information. Keys are: ``string`` - The full version string as displayed by cmake :option:`--version <cmake --version>`. + The full version string as displayed by cmake :cmake-option:`--version`. ``major`` The major version number in integer form. ``minor`` @@ -1176,8 +1231,8 @@ ``debugger`` .. versionadded:: 3.27 - ``true`` if the :option:`--debugger <cmake --debugger>` mode - is supported and ``false`` otherwise. + ``true`` if the :cmake-option:`--debugger` mode is supported + and ``false`` otherwise. .. option:: cat [--] <files>... @@ -1200,6 +1255,10 @@ ``cat`` can now print the standard input by passing the ``-`` argument. + .. versionadded:: 4.4 + + ``cat`` will print the standard input when no arguments are passed. + .. program:: cmake-E .. option:: chdir <dir> <cmd> [<arg>...] @@ -1222,11 +1281,12 @@ .. program:: cmake-E -.. option:: copy <file>... <destination>, copy -t <destination> <file>... +.. option:: copy <file>... <destination>, + copy -t <destination> <file>... Copy files to ``<destination>`` (either file or directory). If multiple files are specified, or if ``-t`` is specified, the - ``<destination>`` must be directory and it must exist. If ``-t`` is not + ``<destination>`` must be a directory and it must exist. If ``-t`` is not specified, the last argument is assumed to be the ``<destination>``. Wildcards are not supported. ``copy`` does follow symlinks. That means it does not copy symlinks, but the files or directories it point to. @@ -1237,10 +1297,13 @@ .. versionadded:: 3.26 Support for ``-t`` argument. -.. option:: copy_directory <dir>... <destination> +.. option:: copy_directory <dir>... <destination>, + copy_directory -t <destination> <dir>... - Copy content of ``<dir>...`` directories to ``<destination>`` directory. - If ``<destination>`` directory does not exist it will be created. + Copy content of ``<dir>...`` directories to a ``<destination>`` directory. + If the ``<destination>`` directory does not exist it will be created. + If ``-t`` is not specified, the last argument is assumed to be the + ``<destination>``. ``copy_directory`` does follow symlinks. .. versionadded:: 3.5 @@ -1250,52 +1313,75 @@ The command now fails when the source directory does not exist. Previously it succeeded by creating an empty destination directory. -.. option:: copy_directory_if_different <dir>... <destination> + .. versionadded:: 4.4 + Support for the ``-t`` argument. + +.. option:: copy_directory_if_different <dir>... <destination>, + copy_directory_if_different -t <destination> <dir>... .. versionadded:: 3.26 - Copy changed content of ``<dir>...`` directories to ``<destination>`` directory. - If ``<destination>`` directory does not exist it will be created. + Copy changed content of ``<dir>...`` directories to a ``<destination>`` directory. + If the ``<destination>`` directory does not exist it will be created. + If ``-t`` is not specified, the last argument is assumed to be the + ``<destination>``. ``copy_directory_if_different`` does follow symlinks. The command fails when the source directory does not exist. -.. option:: copy_directory_if_newer <dir>... <destination> + .. versionadded:: 4.4 + Support for the ``-t`` argument. + +.. option:: copy_directory_if_newer <dir>... <destination>, + copy_directory_if_newer -t <destination> <dir>... .. versionadded:: 4.2 - Copy content of ``<dir>...`` directories to ``<destination>`` directory + Copy content of ``<dir>...`` directories to a ``<destination>`` directory if source files are newer than destination files (based on file timestamps). - If ``<destination>`` directory does not exist it will be created. + If the ``<destination>`` directory does not exist it will be created. + If ``-t`` is not specified, the last argument is assumed to be the + ``<destination>``. ``copy_directory_if_newer`` does follow symlinks. The command fails when the source directory does not exist. This is faster than ``copy_directory_if_different`` as it only compares file timestamps instead of file contents. -.. option:: copy_if_different <file>... <destination> + .. versionadded:: 4.4 + Support for the ``-t`` argument. + +.. option:: copy_if_different <file>... <destination>, + copy_if_different -t <destination> <file>... Copy files to ``<destination>`` (either file or directory) if they have changed. - If multiple files are specified, the ``<destination>`` must be - directory and it must exist. + If multiple files are specified, or if ``-t`` is specified, + the ``<destination>`` must be a directory and it must exist. ``copy_if_different`` does follow symlinks. .. versionadded:: 3.5 Support for multiple input files. -.. option:: copy_if_newer <file>... <destination> + .. versionadded:: 4.4 + Support for the ``-t`` argument. + +.. option:: copy_if_newer <file>... <destination>, + copy_if_newer -t <destination> <file>... .. versionadded:: 4.2 Copy files to ``<destination>`` (either file or directory) if source files are newer than destination files (based on file timestamps). - If multiple files are specified, the ``<destination>`` must be - directory and it must exist. + If multiple files are specified, or if ``-t`` is specified, + the ``<destination>`` must be a directory and it must exist. ``copy_if_newer`` does follow symlinks. This is faster than ``copy_if_different`` as it only compares file timestamps instead of file contents. + .. versionadded:: 4.4 + Support for the ``-t`` argument. + .. option:: create_symlink <old> <new> Create a symbolic link ``<new>`` naming ``<old>``. @@ -1661,6 +1747,38 @@ not directly set by CMake itself. The default compression level may vary between archive formats, platforms, etc. + .. option:: --cmake-tar-encoding=<encoding> + + .. versionadded:: 4.4 + + Specify the pathname character encoding used in the archive. + + The ``<encoding>`` may be one of: + + ``UTF-8`` + Archive pathnames are encoded as UTF-8. + + This is the default since CMake 4.4. + + ``OEM`` + On Windows platforms, pathnames are encoded as using the original + equipment manufacturer (OEM) code page. On non-Windows platforms, + pathnames are encoded according to the current locale. + + In CMake 4.3 and below, the ``OEM`` encoding (current locale) + was always used. + + ``UTF-16LE``, ``UTF-16BE`` + Archive pathnames are encoded as UTF-16 little-endian or big-endian. + + ``...`` + Any encoding name supported by ``iconv`` on the current platform. + On Windows, code page names may be specified. + + .. note:: + ``7zip`` archives always encode paths as ``UTF-16LE``, + so this option is silently ignored for that format. + .. option:: --cmake-tar-threads=<number> .. versionadded:: 4.3 @@ -1834,10 +1952,8 @@ .. option:: --preset <preset>, --preset=<preset> - Use a workflow preset to specify a workflow. The project binary directory - is inferred from the initial configure preset. The current working directory - must contain CMake preset files. - See :manual:`preset <cmake-presets(7)>` for more details. + Use a workflow :manual:`preset <cmake-presets(7)>` to specify a workflow. + The project binary directory is inferred from the initial configure preset. .. versionchanged:: 3.31 When following immediately after the ``--workflow`` option, @@ -1848,10 +1964,31 @@ $ cmake --workflow my-preset + .. versionchanged:: 4.4 + If :cmake-workflow-option:`--presets-file` is specified, neither of + ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be + present. Otherwise, they are required to be present in the top level + source directory. In prior versions, this was strictly required. + +.. option:: --presets-file <file>, --presets-file=<file> + + .. versionadded:: 4.4 + + Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The + specified path may be absolute or relative to the current working directory. + If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and + ``CMakeUserPresets.json`` will be ignored. + .. option:: --list-presets - Lists the available workflow presets. The current working directory must - contain CMake preset files. + Lists the available workflow presets. + + .. versionchanged:: 4.4 + If :cmake-workflow-option:`--presets-file` is specified, neither of + ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be + present, and only presets defined in the given ``<file>`` will be listed. + Otherwise, they are required to be present in the top level source + directory. In prior versions, this was strictly required. .. option:: --fresh
diff --git a/Help/manual/cpack.1.rst b/Help/manual/cpack.1.rst index d1ca268..b15be01 100644 --- a/Help/manual/cpack.1.rst +++ b/Help/manual/cpack.1.rst
@@ -26,11 +26,11 @@ <cpack-generators(7)>` manual. The command ``cpack --help`` prints a list of generators supported for the target platform. Which of them are to be used can be selected through the :variable:`CPACK_GENERATOR` variable -or through the command-line option :option:`-G <cpack -G>`. +or through the command-line option :cpack-option:`-G`. The :program:`cpack` program is steered by a configuration file written in the :manual:`CMake language <cmake-language(7)>`. Unless chosen differently -through the command-line option :option:`--config <cpack --config>`, the +through the command-line option :cpack-option:`--config`, the file ``CPackConfig.cmake`` in the current directory is used. In the standard CMake workflow, the file ``CPackConfig.cmake`` is generated @@ -68,6 +68,9 @@ Set a CPack variable. This will override any value set for ``<var>`` in the input file read by :program:`cpack`. + .. versionadded:: 4.0 + The single-argument form ``-D<var>=<value>`` is now supported. + .. option:: --config <configFile> Specify the configuration file read by :program:`cpack` to provide the packaging @@ -120,13 +123,37 @@ Override/define :variable:`CPACK_PACKAGE_VENDOR`. -.. option:: --preset <presetName> +.. option:: --preset <preset>, --preset=<preset> - Use a preset from :manual:`cmake-presets(7)`. + Use a package :manual:`preset <cmake-presets(7)>` to specify package + options. The project binary directory is inferred from the + :preset:`packagePresets.configurePreset` key. + + .. versionchanged:: 4.4 + If :cpack-option:`--presets-file` is specified, neither of + ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be + present. Otherwise, they are required to be present in the top level + source directory. In prior versions, this was strictly required. + +.. option:: --presets-file <file>, --presets-file=<file> + + .. versionadded:: 4.4 + + Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The + specified path may be absolute or relative to the current working directory. + If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and + ``CMakeUserPresets.json`` will be ignored. .. option:: --list-presets - List presets from :manual:`cmake-presets(7)`. + Lists the available package presets. + + .. versionchanged:: 4.4 + If :cpack-option:`--presets-file` is specified, neither of + ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be + present, and only presets defined in the given ``<file>`` will be listed. + Otherwise, they are required to be present in the top level source + directory. In prior versions, this was strictly required. .. include:: include/OPTIONS_HELP.rst
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst index 2cbb41b..e807425 100644 --- a/Help/manual/ctest.1.rst +++ b/Help/manual/ctest.1.rst
@@ -11,7 +11,7 @@ .. parsed-literal:: `Run Tests`_ - ctest [<options>] [--test-dir <path-to-build>] + ctest [<options>...] [--test-dir <path-to-build>] [-- <test-args>...] `Build and Test Mode`_ ctest --build-and-test <path-to-source> <path-to-build> @@ -20,10 +20,13 @@ [--test-command <command> [<args>...]] `Dashboard Client`_ - ctest -D <dashboard> [-- <dashboard-options>...] - ctest -M <model> -T <action> [-- <dashboard-options>...] - ctest -S <script> [-- <dashboard-options>...] - ctest -SP <script> [-- <dashboard-options>...] + ctest -D <dashboard> [-- <dashboard-options>...] + ctest -M <model> -T <action> [-- <dashboard-options>...] + ctest [-M <model>] -T Configure --source-dir <path-to-source> + [--build-dir <path-to-build>] + [-- <dashboard-options>...] + ctest -S <script> [-- <dashboard-options>...] + ctest -SP <script> [-- <dashboard-options>...] `View Help`_ ctest --help[-<topic>] @@ -42,24 +45,68 @@ Run Tests ========= +:program:`ctest` runs tests defined by a ``CTestTestfile.cmake`` file, +typically in a build tree generated by :manual:`cmake(1)`: + +.. code-block:: shell + + ctest [<options>...] [--test-dir <path-to-build>] [-- <test-args>...] + +If all tests pass, the exit code is ``0``. The exit code is non-zero +in the following cases: + +* An error occurs, such as failure to process invalid arguments. + +* .. versionadded:: 4.4 + No arguments are given and no ``CTestTestfile.cmake`` exists. + +* The :ctest-option:`--no-tests=error` argument is given and no tests are + executed, e.g., when ``CTestTestfile.cmake`` defines no tests, or when the + given arguments exclude all tests. + +* At least one test fails. + +The options for running tests are: + .. program:: ctest .. option:: --preset <preset>, --preset=<preset> - Use a test preset to specify test options. The project binary directory - is inferred from the ``configurePreset`` key. The current working directory - must contain CMake preset files. - See :manual:`preset <cmake-presets(7)>` for more details. + Use a test :manual:`preset <cmake-presets(7)>` to specify test options. The + project binary directory is inferred from the + :preset:`testPresets.configurePreset` key. .. versionadded:: 3.30 - The ``--test-dir`` option may optionally be specified with a different - binary directory than the one specified by the ``configurePreset`` key - of the test preset. + The :ctest-option:`--test-dir` option may be specified with a different + binary directory than the one specified by the + :preset:`testPresets.configurePreset` key. + + .. versionchanged:: 4.4 + If :ctest-option:`--presets-file` is specified, neither of + ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be + present. Otherwise, they are required to be present in the top level + source directory. In prior versions, this was strictly required. + +.. option:: --presets-file <file>, --presets-file=<file> + + .. versionadded:: 4.4 + + Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The + specified path may be absolute or relative to the current working directory. + If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and + ``CMakeUserPresets.json`` will be ignored. .. option:: --list-presets Lists the available test presets. The current working directory must contain - CMake preset files. + ``CMakePresets.json`` and/or ``CMakeUserPresets.json``. + + .. versionchanged:: 4.4 + If :ctest-option:`--presets-file` is specified, neither of + ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be + present, and only presets defined in the given ``<file>`` will be listed. + Otherwise, they are required to be present in the top level source + directory. In prior versions, this was strictly required. .. option:: -C <cfg>, --build-config <cfg> @@ -109,7 +156,7 @@ Output anything outputted by the test program if the test should fail. This option can also be enabled by setting the - :envvar:`CTEST_OUTPUT_ON_FAILURE` environment variable + :envvar:`CTEST_OUTPUT_ON_FAILURE` environment variable. .. option:: --stop-on-failure @@ -157,9 +204,8 @@ .. option:: --test-load <level> - While running tests in parallel (e.g. with :option:`-j <ctest -j>`), try - not to start tests when they may cause the CPU load to pass above a given - threshold. + While running tests in parallel (e.g. with :ctest-option:`-j`), try not to + start tests when they may cause the CPU load to pass above a given threshold. When :program:`ctest` is run as a `Dashboard Client`_ this sets the ``TestLoad`` option of the `CTest Test Step`_. @@ -169,10 +215,10 @@ Make CTest quiet. This option will suppress all the output. The output log file will - still be generated if the :option:`--output-log <ctest --output-log>` is - specified. Options such as :option:`--verbose <ctest --verbose>`, - :option:`--extra-verbose <ctest --extra-verbose>`, and - :option:`--debug <ctest --debug>` are ignored + still be generated if the :ctest-option:`--output-log` is specified. + Options such as :ctest-option:`--verbose`, + :ctest-option:`--extra-verbose`, and + :ctest-option:`--debug` are ignored if ``--quiet`` is specified. .. option:: -O <file>, --output-log <file> @@ -189,16 +235,16 @@ This option tells CTest to write test results to ``<file>`` in JUnit XML format. If ``<file>`` already exists, it will be overwritten. If using the - :option:`-S <ctest -S>` option to run a dashboard script, use the - ``OUTPUT_JUNIT`` keyword with the :command:`ctest_test` command instead. + :ctest-option:`-S` option to run a dashboard script, use the ``OUTPUT_JUNIT`` + keyword with the :command:`ctest_test` command instead. .. option:: -N, --show-only[=<format>] Disable actual execution of tests. This option tells CTest to list the tests that would be run but not - actually run them. Useful in conjunction with the :option:`-R <ctest -R>` - and :option:`-E <ctest -E>` options. + actually run them. Useful in conjunction with the :ctest-option:`-R` + and :ctest-option:`-E` options. .. versionadded:: 3.14 @@ -259,7 +305,8 @@ The file must contain one exact test name per line. Lines that do not exactly match any test names are ignored. This option can be combined with the other options like - ``-R``, ``-E``, ``-L`` or ``-LE``. + :ctest-option:`-R`, :ctest-option:`-E`, :ctest-option:`-L`, or + :ctest-option:`-LE`. .. option:: --exclude-from-file <filename> @@ -271,7 +318,8 @@ The file must contain one exact test name per line. Lines that do not exactly match any test names are ignored. This option can be combined with the other options like - ``-R``, ``-E``, ``-L`` or ``-LE``. + :ctest-option:`-R`, :ctest-option:`-E`, :ctest-option:`-L`, or + :ctest-option:`-LE`. .. option:: -FA <regex>, --fixture-exclude-any <regex> @@ -287,13 +335,11 @@ .. option:: -FS <regex>, --fixture-exclude-setup <regex> - Same as :option:`-FA <ctest -FA>` except only matching setup tests are - excluded. + Same as :ctest-option:`-FA` except only matching setup tests are excluded. .. option:: -FC <regex>, --fixture-exclude-cleanup <regex> - Same as :option:`-FA <ctest -FA>` except only matching cleanup tests are - excluded. + Same as :ctest-option:`-FA` except only matching cleanup tests are excluded. .. option:: -I [Start,End,Stride,test#,test#|Test file], --tests-information @@ -307,11 +353,11 @@ .. option:: -U, --union - Take the Union of :option:`-I <ctest -I>` and :option:`-R <ctest -R>`. + Take the Union of :ctest-option:`-I` and :ctest-option:`-R`. - When both :option:`-R <ctest -R>` and :option:`-I <ctest -I>` are specified - by default the intersection of tests are run. By specifying ``-U`` the union - of tests is run instead. + When both :ctest-option:`-R` and :ctest-option:`-I` are specified by default + the intersection of tests are run. By specifying ``-U`` the union of tests + is run instead. .. option:: --rerun-failed @@ -320,8 +366,8 @@ This option tells CTest to perform only the tests that failed during its previous run. When this option is specified, CTest ignores all other options intended to modify the list of tests to run ( - :option:`-L <ctest -L>`, :option:`-R <ctest -R>`, :option:`-E <ctest -E>`, - :option:`-LE <ctest -LE>`, :option:`-I <ctest -I>`, etc). In the event that + :ctest-option:`-L`, :ctest-option:`-R`, :ctest-option:`-E`, + :ctest-option:`-LE`, :ctest-option:`-I`, etc). In the event that CTest runs and no tests fail, subsequent calls to CTest with the ``--rerun-failed`` option will run the set of tests that most recently failed (if any). @@ -405,12 +451,13 @@ See `Label and Subproject Summary`_. -.. option:: --test-dir <dir> +.. option:: --test-dir <path-to-build> .. versionadded:: 3.20 Specify the directory in which to look for tests, typically a CMake project build directory. If not specified, the current directory is used. + See also :ctest-dashboard-option:`--build-dir`. .. option:: --test-output-size-passed <size> @@ -431,7 +478,7 @@ Truncate ``tail`` (default), ``middle`` or ``head`` of test output once maximum output size is reached. -.. option:: --overwrite +.. option:: --overwrite <option-name> Overwrite CTest configuration option. @@ -441,7 +488,7 @@ .. option:: --force-new-ctest-process Ignored. This option once disabled a now-removed optimization - for tests running ``ctest`` itself. + for tests running :program:`ctest` itself. .. option:: --schedule-random @@ -450,16 +497,16 @@ This option will run the tests in a random order. It is commonly used to detect implicit dependencies in a test suite. -.. option:: --schedule-random-seed +.. option:: --schedule-random-seed <seed> .. versionadded:: 4.1 - Override the random order seed + Override the random order seed. This option is used to allow recreating failures owing to - random order of execution by ``--schedule-random``. + random order of execution by :ctest-option:`--schedule-random`. -.. option:: --submit-index +.. option:: --submit-index <index> Legacy option for old Dart2 dashboard server feature. Do not use. @@ -489,13 +536,21 @@ .. option:: --no-tests=<action> - Regard no tests found either as error (when ``<action>`` is set to - ``error``) or ignore it (when ``<action>`` is set to ``ignore``). + Specify behavior when no tests are executed, e.g., when + ``CTestTestfile.cmake`` defines no tests, or when the given arguments + exclude all tests. The ``<action>`` may be one of: - If no tests were found, the default behavior of CTest is to always log an - error message but to return an error code in script mode only. This option - unifies the behavior of CTest by either returning an error code if no tests - were found or by ignoring it. + ``error`` + Consider running no tests to be an error. + + This is the default when running :option:`ctest -S` scripts. + + ``ignore`` + Consider running no tests to be normal. + + This is the default when `running tests <Run Tests_>`__ via the + :program:`ctest` command line, even when running as a + `dashboard client <Dashboard Client_>`__. .. versionadded:: 3.26 @@ -510,6 +565,31 @@ the :ref:`cmake-instrumentation Indexing` section of CMake instrumentation for more details. +.. option:: -- <test-args>... + + .. versionadded:: 4.4 + + Forward extra arguments to test executables. + + All arguments after ``--`` are appended to the end of every executed + test's command line. This is useful for passing options to test + frameworks like Google Test or doctest. For example: + + .. code-block:: shell + + ctest -R my_test -- --gtest_filter=SomeTest.Case + ctest --verbose -- --success + + All selected tests receive the same extra arguments. + Use :ctest-option:`-R` or :ctest-option:`-L` to limit + which tests are executed. + + A bare ``--`` with no following arguments is silently ignored. + + When both ``<test-args>`` and + :preset:`testPresets.execution.testPassthroughArguments` are provided, + the test preset arguments will appear first, followed by the ``<test-args>``. + View Help ========= @@ -528,17 +608,16 @@ Each individual filter is a regular expression applied to the labels attached to a test. -When :option:`-L <ctest -L>` is used, in order for a test to be included in a -test run, each regular expression must match at least one -label. Using more than one :option:`-L <ctest -L>` option means "match **all** -of these". +When :ctest-option:`-L` is used, in order for a test to be included in a test +run, each regular expression must match at least one label. Using more than +one :ctest-option:`-L` option means "match **all** of these". -The :option:`-LE <ctest -LE>` option works just like :option:`-L <ctest -L>`, +The :ctest-option:`-LE` option works just like :ctest-option:`-L`, but excludes tests rather than including them. A test is excluded if each regular expression matches at least one label. -If a test has no labels attached to it, then :option:`-L <ctest -L>` will never -include that test, and :option:`-LE <ctest -LE>` will never exclude that test. +If a test has no labels attached to it, then :ctest-option:`-L` will never +include that test, and :ctest-option:`-LE` will never exclude that test. As an example of tests with labels, consider five tests, with the following labels: @@ -625,11 +704,11 @@ then that will be run after the build is complete. Other options that affect this mode include: -.. option:: --build-and-test +.. option:: --build-and-test <path-to-source> <path-to-build> Switch into the build and test mode. -.. option:: --build-target +.. option:: --build-target <tgt> Specify a specific target to build. The option can be given multiple times with different targets, in which case each target is built in turn. @@ -640,11 +719,11 @@ .. option:: --build-nocmake - Run the build without running cmake first. + Run the build without running :program:`cmake` first. - Skip the cmake step. + Skip the :program:`cmake` step. -.. option:: --build-run-dir +.. option:: --build-run-dir <dir> Specify directory to run programs from. @@ -654,57 +733,59 @@ Run CMake twice. -.. option:: --build-exe-dir +.. option:: --build-exe-dir <dir> Specify the directory for the executable. -.. option:: --build-generator +.. option:: --build-generator <generator-name> Specify the generator to use. See the :manual:`cmake-generators(7)` manual. -.. option:: --build-generator-platform +.. option:: --build-generator-platform <platform-name> Specify the generator-specific platform. -.. option:: --build-generator-toolset +.. option:: --build-generator-toolset <toolset-name> Specify the generator-specific toolset. -.. option:: --build-project +.. option:: --build-project <project-name> Specify the name of the project to build. -.. option:: --build-makeprogram +.. option:: --build-makeprogram <program-name> Specify the explicit make program to be used by CMake when configuring and - building the project. Only applicable for Make and Ninja based generators. + building the project. Only applicable for :ref:`Makefile Generators` and + :ref:`Ninja Generators`. .. option:: --build-noclean Skip the make clean step. -.. option:: --build-config-sample +.. option:: --build-config-sample <exe-name> A sample executable to use to determine the configuration that - should be used. e.g. ``Debug``, ``Release`` etc. + should be used (e.g. ``Debug``, ``Release``, etc.). -.. option:: --build-options +.. option:: --build-options [<options>...] - Additional options for configuring the build (i.e. for CMake, not for - the build tool). Note that if this is specified, the ``--build-options`` - keyword and its arguments must be the last option given on the command - line, with the possible exception of ``--test-command``. + Additional options for configuring the build (i.e. for :manual:`cmake(1)`, + not for the build tool). Note that if this is specified, the + ``--build-options`` keyword and its arguments must be the last option given + on the command line, with the possible exception of + :ctest-option:`--test-command`. -.. option:: --test-command +.. option:: --test-command <command> The command to run as the test step with the - :option:`--build-and-test <ctest --build-and-test>` option. + :ctest-option:`--build-and-test` option. All arguments following this keyword will be assumed to be part of the test command line, so it must be the last option given. -.. option:: --test-timeout +.. option:: --test-timeout <timeout> - The time limit in seconds + The time limit in seconds. .. _`Dashboard Client`: @@ -745,8 +826,7 @@ This option tells CTest to act as a CDash client where the ``<model>`` can be ``Experimental``, ``Nightly``, and ``Continuous``. - Combining ``-M`` and :option:`-T <ctest -T>` is similar to - :option:`-D <ctest -D>`. + Combining ``-M`` and :ctest-option:`-T` is similar to :ctest-option:`-D`. .. option:: -T <action>, --test-action <action> @@ -755,8 +835,7 @@ This option tells CTest to act as a CDash client and perform some action such as ``start``, ``build``, ``test`` etc. See `Dashboard Client Steps`_ for the full list of actions. - Combining :option:`-M <ctest -M>` and ``-T`` is similar to - :option:`-D <ctest -D>`. + Combining :ctest-option:`-M` and ``-T`` is similar to :ctest-option:`-D`. .. option:: -S <script>, --script <script> @@ -772,29 +851,80 @@ Execute a dashboard for a configuration. - This option does the same operations as :option:`-S <ctest -S>` but it - will do them in a separate process. This is primarily useful in cases - where the script may modify the environment and you do not want the modified - environment to impact other :option:`-S <ctest -S>` scripts. + This option does the same operations as :ctest-option:`-S` but it will do + them in a separate process. This is primarily useful in cases where the + script may modify the environment and you do not want the modified + environment to impact other :ctest-option:`-S` scripts. .. _`Dashboard Options`: The available ``<dashboard-options>`` are the following: -.. option:: -D <var>:<type>=<value> +.. program:: ctest-dashboard - Define a variable for script mode. +.. option:: -D <var>:<type>=<value>, -D <var>=<value> - Pass in variable values on the command line. Use in conjunction - with :option:`-S <ctest -S>` to pass variable values to a dashboard script. + Define a variable for dashboard client mode. + + Pass in variable values on the command line. Use in conjunction with + :ctest-option:`-S` to pass variable values to a dashboard script, or with + :ctest-option:`-T` to override `Dashboard Client Configuration`_ settings. Parsing ``-D`` arguments as variable values is only attempted if the value following ``-D`` does not match any of the known dashboard types. + The ``<var>:<type>=<value>`` form accepts any variable name. The simpler + ``<var>=<value>`` form (without a type) is accepted only for variables whose + names begin with ``CTEST_``. In both cases the value overrides the + corresponding setting from the `Dashboard Client Configuration`_ file + (e.g., ``DartConfiguration.tcl``) when using :ctest-option:`-T`. + + .. note:: + These definitions set CTest script variables + (e.g., :variable:`CTEST_BUILD_NAME`), not CMake cache variables. + To pass CMake cache variables to the configure step, consider using a + `CTest Script`_, a :manual:`configure preset <cmake-presets(7)>`, or + setting :variable:`CTEST_CONFIGURE_COMMAND`. + +.. option:: --source-dir <path-to-source> + + .. versionadded:: 4.4 + + Specify the project source directory. When combined with + :option:`-T Configure <ctest -T>`, this allows CTest to perform an initial + configure step for an empty binary directory. The binary directory defaults + to the current working directory; use :ctest-dashboard-option:`--build-dir` to + specify a different location. The binary directory is created automatically + if it does not yet exist. + + When a :manual:`configure preset <cmake-presets(7)>` is specified that + defines a :preset:`binaryDir <configurePresets.binaryDir>`, CTest uses that + path as the binary directory automatically (without requiring + :ctest-dashboard-option:`--build-dir`). An explicit + :ctest-dashboard-option:`--build-dir` takes precedence over the preset's + :preset:`binaryDir <configurePresets.binaryDir>`. + + A CMake generator must also be specified. Use + :option:`-D CTEST_CMAKE_GENERATOR=\<gen\> <ctest-dashboard -D>` to supply one, + or set :variable:`CTEST_CONFIGURE_PRESET` to specify a + :manual:`configure preset <cmake-presets(7)>` that includes a + :preset:`generator <configurePresets.generator>`. + + See the `CTest Configure Step`_ settings for more details. + +.. option:: --build-dir <path-to-build> + + .. versionadded:: 4.4 + + Specify the project binary (build) directory. This is an alias for + :ctest-option:`--test-dir` intended for use with + :ctest-dashboard-option:`--source-dir` when bootstrapping a build with + :option:`-T Configure <ctest -T>`. + .. option:: --group <group> - Specify what group you'd like to submit results to + Specify the group to which to submit results. - Submit dashboard to specified group instead of default one. By + Submit dashboard to specified group instead of the default. By default, the dashboard is submitted to Nightly, Experimental, or Continuous group, but by specifying this option, the group can be arbitrary. @@ -924,6 +1054,15 @@ ctest -D <mode>[<step>] ctest -M <mode> [-T <step>]... +The :ctest-dashboard-option:`--source-dir` option additionally allows the +``Configure`` step to be performed on a build tree that does not yet +exist, in which case the :ctest-dashboard-option:`--build-dir` option specifies +where to create it:: + + ctest [-M <mode>] -T Configure + --source-dir <path-to-source> [--build-dir <path-to-build>] + ctest --build-and-test <path-to-source> <path-to-build> + The ``<mode>`` must be one of the above `Dashboard Client Modes`_, and each ``<step>`` must be one of the above `Dashboard Client Steps`_. @@ -997,6 +1136,13 @@ * `CTest Script`_ variable: :variable:`CTEST_SOURCE_DIRECTORY` * :module:`CTest` module variable: :variable:`PROJECT_SOURCE_DIR` +``CheckoutCommand`` + Command-line to run before the start step to initialize the source + directory, e.g. to perform an initial checkout from version control. + + * `CTest Script`_ variable: :variable:`CTEST_CHECKOUT_COMMAND` + * :module:`CTest` module variable: none + .. _`CTest Update Step`: CTest Update Step @@ -1157,6 +1303,7 @@ to a different version. * `CTest Script`_ variable: :variable:`CTEST_UPDATE_VERSION_ONLY` + * :command:`ctest_update` option: ``VERSION_ONLY`` .. _`UpdateVersionOverride`: @@ -1170,6 +1317,7 @@ CTest not to update the source tree to a different version. * `CTest Script`_ variable: :variable:`CTEST_UPDATE_VERSION_OVERRIDE` + * :command:`ctest_update` option: ``VERSION_OVERRIDE <version>`` Additional configuration settings include: @@ -1214,6 +1362,27 @@ See `Label and Subproject Summary`_. +``CMakeGenerator`` + The CMake generator to use for the configure step when + ``ConfigureCommand`` is not set. + + * `CTest Script`_ variable: ``CTEST_CMAKE_GENERATOR`` + * :module:`CTest` module variable: none + +``CMakeGeneratorPlatform`` + The CMake generator platform to use for the configure step when + ``ConfigureCommand`` is not set. + + * `CTest Script`_ variable: ``CTEST_CMAKE_GENERATOR_PLATFORM`` + * :module:`CTest` module variable: none + +``CMakeGeneratorToolset`` + The CMake generator toolset to use for the configure step when + ``ConfigureCommand`` is not set. + + * `CTest Script`_ variable: ``CTEST_CMAKE_GENERATOR_TOOLSET`` + * :module:`CTest` module variable: none + .. _`CTest Build Step`: CTest Build Step @@ -1227,9 +1396,9 @@ ``DefaultCTestConfigurationType`` When the build system to be launched allows build-time selection of the configuration (e.g. ``Debug``, ``Release``), this specifies - the default configuration to be built when no :option:`-C <ctest -C>` - option is given to the :program:`ctest` command. The value will be substituted - into the value of ``MakeCommand`` to replace the literal string + the default configuration to be built when no :ctest-option:`-C` + option is given to the :program:`ctest` command. The value will be + substituted into the value of ``MakeCommand`` to replace the literal string ``${CTEST_CONFIGURATION_TYPE}`` if it appears. * `CTest Script`_ variable: :variable:`CTEST_CONFIGURATION_TYPE` @@ -1271,6 +1440,20 @@ * `CTest Script`_ variable: :variable:`CTEST_USE_LAUNCHERS` * :module:`CTest` module variable: ``CTEST_USE_LAUNCHERS`` +``BuildFlags`` + Additional flags to pass to the build tool when building the project. + Only used when ``MakeCommand`` is not set explicitly. + + * `CTest Script`_ variable: ``CTEST_BUILD_FLAGS`` + * :module:`CTest` module variable: none + +``BuildTarget`` + Specify a specific target to build instead of the default target. + Only used when ``MakeCommand`` is not set explicitly. + + * `CTest Script`_ variable: ``CTEST_BUILD_TARGET`` + * :module:`CTest` module variable: none + .. _`CTest Test Step`: CTest Test Step @@ -1301,7 +1484,7 @@ See `Label and Subproject Summary`_. ``TestLoad`` - While running tests in parallel (e.g. with :option:`-j <ctest -j>`), + While running tests in parallel (e.g. with :ctest-option:`-j`), try not to start tests when they may cause the CPU load to pass above a given threshold. @@ -1311,13 +1494,46 @@ ``TimeOut`` The default timeout for each test if not specified by the :prop_test:`TIMEOUT` test property or the - :option:`--timeout <ctest --timeout>` flag. + :ctest-option:`--timeout` flag. * `CTest Script`_ variable: :variable:`CTEST_TEST_TIMEOUT` * :module:`CTest` module variable: ``DART_TESTING_TIMEOUT`` +.. _`ctest-CoverageTool`: + +``CoverageTool`` + .. versionadded:: 4.4 + + Specify the tool used for collecting coverage during the running + of the tests. The tool may be one of: + + ``LLVM-COV`` + This value indicates the usage of Clang's source-based code coverage + which "operates on AST and preprocessor information directly". + For more information, see the `Clang Source-based Code Coverage`_ + documentation. + + CTest will run each test with the ``LLVM_PROFILE_FILE`` environment + variable set to ``<directory>/<test-name>_<process-id>.profraw``, + where ``<directory>`` is the absolute path to the directory where + the ``CTestTestfile.cmake`` that describes the test is located. + This configures Clang Source-based Code Coverage to uniquely identify + the coverage information gathered by each test without any collisions. + + Any existing ``<directory>/<test-name>_*.profraw`` files for a test + are removed before running the test. Any existing ``LLVM_PROFILE_FILE`` + environment variable is ignored. + + If no value is specified, no work is done and nothing is added to the + environment. + + * `CTest Script`_ variable: :variable:`CTEST_TEST_COVERAGE_TOOL` + * :module:`CTest` module variable: :variable:`CTEST_TEST_COVERAGE_TOOL` + To report extra test values to CDash, see :ref:`Additional Test Measurements`. +.. _`Clang Source-based Code Coverage`: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html + .. _`CTest Coverage Step`: CTest Coverage Step @@ -1344,6 +1560,13 @@ These options are the first arguments passed to ``CoverageCommand``. +``ExtraCoverageGlob`` + A semicolon-separated list of glob patterns for additional source files + to include in the coverage analysis. + + * `CTest Script`_ variable: :variable:`CTEST_EXTRA_COVERAGE_GLOB` + * :module:`CTest` module variable: none + .. _`CTest MemCheck Step`: CTest MemCheck Step @@ -1583,6 +1806,35 @@ * `CTest Script`_ variable: :variable:`CTEST_SUBMIT_INACTIVITY_TIMEOUT` * :module:`CTest` module variable: ``CTEST_SUBMIT_INACTIVITY_TIMEOUT`` +``NotesFiles`` + A semicolon-separated list of notes files to upload with the submission. + + * `CTest Script`_ variable: :variable:`CTEST_NOTES_FILES` + * :module:`CTest` module variable: none + +``ExtraSubmitFiles`` + A semicolon-separated list of extra ``.xml`` files to upload with the + submission. + + * `CTest Script`_ variable: :variable:`CTEST_EXTRA_SUBMIT_FILES` + * :module:`CTest` module variable: none + +``SubmitParts`` + A semicolon-separated list of part names to submit. When set, only the + named parts are submitted rather than all available parts. Valid names + are the same as the ``PARTS`` option of :command:`ctest_submit`. + + * `CTest Script`_ variable: :variable:`CTEST_SUBMIT_PARTS` + * :module:`CTest` module variable: none + +``TimeLimit`` + Maximum total time (in seconds) allowed for the entire dashboard run. + CTest will not start additional steps once the remaining time drops + below two minutes. + + * `CTest Script`_ variable: ``CTEST_TIME_LIMIT`` + * :module:`CTest` module variable: none + ``TLSVersion`` .. versionadded:: 3.30 @@ -1677,9 +1929,9 @@ Test name. This cannot be empty. ``config`` Optional field specifying the configuration for which the test will run. - This will always match the :option:`-C <ctest -C>` option specified on the - ``ctest`` command line. If no such option was given, this field will not - be present. + This will always match the :ctest-option:`-C` option specified on the + :program:`ctest` command line. If no such option was given, this field + will not be present. ``command`` Optional array where the first element is the test command and the remaining elements are the command arguments. Normally, this field should @@ -1716,11 +1968,11 @@ When the resource allocation feature is used, CTest will not oversubscribe resources. For example, if a resource has 8 slots, CTest will not run tests that collectively use more than 8 slots at a time. This has the effect of -limiting how many tests can run at any given time, even if a high ``-j`` -argument is used, if those tests all use some slots from the same resource. -In addition, it means that a single test that uses more of a resource than is -available on a machine will not run at all (and will be reported as -``Not Run``). +limiting how many tests can run at any given time, even if a high +:ctest-option:`-j` argument is used, if those tests all use some slots from the +same resource. In addition, it means that a single test that uses more of a +resource than is available on a machine will not run at all (and will be +reported as ``Not Run``). A common use case for this feature is for tests that require the use of a GPU. Multiple tests can simultaneously allocate memory from a GPU, but if too many @@ -1758,27 +2010,27 @@ described below. Using this information to decide which resource to connect to is left to the test writer. -The ``RESOURCE_GROUPS`` property tells CTest what resources a test expects -to use grouped in a way meaningful to the test. The test itself must read -the :ref:`environment variables <ctest-resource-environment-variables>` to +The :prop_test:`RESOURCE_GROUPS` property tells CTest what resources a test +expects to use grouped in a way meaningful to the test. The test itself must +read the :ref:`environment variables <ctest-resource-environment-variables>` to determine which resources have been allocated to each group. For example, each group may correspond to a process the test will spawn when executed. -Note that even if a test specifies a ``RESOURCE_GROUPS`` property, it is still -possible for that to test to run without any resource allocation (and without -the corresponding +Note that even if a test specifies a :prop_test:`RESOURCE_GROUPS` property, it +is still possible for that to test to run without any resource allocation +(and without the corresponding :ref:`environment variables <ctest-resource-environment-variables>`) if the user does not pass a resource specification file. Passing this file, -either through the ``--resource-spec-file`` command-line argument or the -``RESOURCE_SPEC_FILE`` argument to :command:`ctest_test`, is what activates the -resource allocation feature. Tests should check the +either through the :ctest-option:`--resource-spec-file` command-line argument +or the ``RESOURCE_SPEC_FILE`` argument to :command:`ctest_test`, is what +activates the resource allocation feature. Tests should check the ``CTEST_RESOURCE_GROUP_COUNT`` environment variable to find out whether or not resource allocation is activated. This variable will always (and only) be defined if resource allocation is activated. If resource allocation is not activated, then the ``CTEST_RESOURCE_GROUP_COUNT`` variable will not exist, -even if it exists for the parent :program:`ctest` process. If a test absolutely must -have resource allocation, then it can return a failing exit code or use the -:prop_test:`SKIP_RETURN_CODE` or :prop_test:`SKIP_REGULAR_EXPRESSION` +even if it exists for the parent :program:`ctest` process. If a test absolutely +must have resource allocation, then it can return a failing exit code or use +the :prop_test:`SKIP_RETURN_CODE` or :prop_test:`SKIP_REGULAR_EXPRESSION` properties to indicate a skipped test. .. _`ctest-resource-specification-file`: @@ -1788,18 +2040,17 @@ The resource specification file is a JSON file which is passed to CTest in one of a number of ways. It can be specified on the command line with the -:option:`ctest --resource-spec-file` option, it can be given using the +:ctest-option:`--resource-spec-file` option, it can be given using the ``RESOURCE_SPEC_FILE`` argument of :command:`ctest_test`, or it can be generated dynamically as part of test execution (see :ref:`ctest-resource-dynamically-generated-spec-file`). If a dashboard script is used and ``RESOURCE_SPEC_FILE`` is not specified, the value of :variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script is used -instead. If :option:`--resource-spec-file <ctest --resource-spec-file>`, -``RESOURCE_SPEC_FILE``, and :variable:`CTEST_RESOURCE_SPEC_FILE` in the -dashboard script are not specified, the value of -:variable:`CTEST_RESOURCE_SPEC_FILE` in the CMake build is used instead. -If none of these are specified, no resource spec file is used. +instead. If :ctest-option:`--resource-spec-file`, ``RESOURCE_SPEC_FILE``, and +:variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script are not specified, +the value of :variable:`CTEST_RESOURCE_SPEC_FILE` in the CMake build is used +instead. If none of these are specified, no resource spec file is used. The resource specification file must be a JSON object. All examples in this document assume the following resource specification file: @@ -1919,9 +2170,9 @@ * ``CTEST_RESOURCE_GROUP_COUNT=3`` This variable will only be defined if :manual:`ctest(1)` has been given a - ``--resource-spec-file``, or if :command:`ctest_test` has been given a - ``RESOURCE_SPEC_FILE``. If no resource specification file has been given, - this variable will not be defined. + :ctest-option:`--resource-spec-file`, or if :command:`ctest_test` has been + given a ``RESOURCE_SPEC_FILE``. If no resource specification file has been + given, this variable will not be defined. .. envvar:: CTEST_RESOURCE_GROUP_<num> @@ -1996,17 +2247,17 @@ On POSIX systems, when running under the context of a `Job Server`_, CTest shares its job slots. This is independent of the :prop_test:`PROCESSORS` -test property, which still counts against CTest's :option:`-j <ctest -j>` -parallel level. CTest acquires exactly one token from the job server before -running each test, and returns it when the test finishes. +test property, which still counts against CTest's :ctest-option:`-j` parallel +level. CTest acquires exactly one token from the job server before running +each test, and returns it when the test finishes. For example, consider the ``Makefile``: .. literalinclude:: CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make :language: make -When invoked via ``make -j 2 test``, ``ctest`` connects to the job server, -acquires a token for each test, and runs at most 2 tests concurrently. +When invoked via ``make -j 2 test``, CTest connects to the job server, acquires +a token for each test, and runs at most 2 tests concurrently. On Windows systems, job server integration is not yet implemented.
diff --git a/Help/manual/file_api/schema_target.json b/Help/manual/file_api/schema_target.json index 68cb14e..6a05fd2 100644 --- a/Help/manual/file_api/schema_target.json +++ b/Help/manual/file_api/schema_target.json
@@ -34,6 +34,9 @@ }, { "$ref": "#/definitions/codemodelV2_10" + }, + { + "$ref": "#/definitions/codemodelV2_11" } ], "definitions": { @@ -68,7 +71,25 @@ }, "minor": { "type": "integer", - "minimum": 10 + "const": 10 + } + }, + "additionalProperties": false + }, + "codemodelVersionV2_11": { + "type": "object", + "required": [ + "major", + "minor" + ], + "properties": { + "major": { + "type": "integer", + "const": 2 + }, + "minor": { + "type": "integer", + "minimum": 11 } }, "additionalProperties": false @@ -81,7 +102,22 @@ "type": "string", "description": "Unique identifier for the target. The format is unspecified and should not be interpreted by clients." }, - "typeV2_0": { + "fileset-typeV2_5": { + "type": "string", + "enum": [ + "HEADERS", + "CXX_MODULES" + ] + }, + "fileset-typeV2_11": { + "type": "string", + "enum": [ + "HEADERS", + "CXX_MODULES", + "SOURCES" + ] + }, + "target-typeV2_0": { "type": "string", "enum": [ "EXECUTABLE", @@ -93,7 +129,7 @@ "UTILITY" ] }, - "typeV2_9": { + "target-typeV2_9": { "type": "string", "enum": [ "EXECUTABLE", @@ -127,6 +163,14 @@ "minimum": 0, "description": "Index into backtraceGraph nodes array" }, + "backtraces": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "description": "Indices into the backtraceGraph nodes array" + }, "folder": { "type": "object", "description": "This will only be present if the FOLDER target property is set", @@ -530,12 +574,45 @@ "type": "string" }, "type": { + "$ref": "#/definitions/fileset-typeV2_5" + }, + "visibility": { "type": "string", "enum": [ - "HEADERS", - "CXX_MODULES" + "PUBLIC", + "PRIVATE", + "INTERFACE" ] }, + "baseDirectories": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + }, + "description": "Base directories containing the file set's files. If a directory is inside the top level source directory, its path is specified relative to that directory. Otherwise, its path is absolute." + } + }, + "additionalProperties": false + } + }, + "fileSetsV2_11": { + "type": "array", + "items": { + "type": "object", + "required": [ + "name", + "type", + "visibility", + "baseDirectories" + ], + "properties": { + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/fileset-typeV2_11" + }, "visibility": { "type": "string", "enum": [ @@ -579,6 +656,14 @@ "minimum": 0, "description": "Index into the fileSets array. Only present if the file is part of a file set." }, + "sourcesFileSetIndexesV2_11": { + "type": "array", + "description": "Indexes into the fileSets array. Only present if the file is part of a file set.", + "items": { + "type": "integer", + "minimum": 0 + } + }, "sourcesV2_0": { "type": "array", "items": { @@ -636,6 +721,42 @@ "additionalProperties": false } }, + "sourcesV2_11": { + "type": "array", + "items": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "$ref": "#/definitions/sourcesPath" + }, + "compileGroupIndex": { + "$ref": "#/definitions/sourcesCompileGroupIndex" + }, + "sourceGroupIndex": { + "$ref": "#/definitions/sourcesSourceGroupIndex" + }, + "isGenerated": { + "$ref": "#/definitions/sourcesIsGenerated" + }, + "backtrace": { + "$ref": "#/definitions/backtrace" + }, + "backtraces": { + "$ref": "#/definitions/backtraces" + }, + "fileSetIndex": { + "$ref": "#/definitions/sourcesFileSetIndexV2_5" + }, + "fileSetIndexes": { + "$ref": "#/definitions/sourcesFileSetIndexesV2_11" + } + }, + "additionalProperties": false + } + }, "interfaceSourcesV2_10": { "type": "array", "items": { @@ -660,6 +781,33 @@ "additionalProperties": false } }, + "interfaceSourcesV2_11": { + "type": "array", + "items": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "$ref": "#/definitions/sourcesPath" + }, + "sourceGroupIndex": { + "$ref": "#/definitions/sourcesSourceGroupIndex" + }, + "isGenerated": { + "$ref": "#/definitions/sourcesIsGenerated" + }, + "fileSetIndex": { + "$ref": "#/definitions/sourcesFileSetIndexV2_5" + }, + "fileSetIndexes": { + "$ref": "#/definitions/sourcesFileSetIndexesV2_11" + } + }, + "additionalProperties": false + } + }, "sourceGroupsV2_0": { "type": "array", "items": { @@ -738,12 +886,7 @@ ], "properties": { "backtraces": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "description": "Indices into the backtraceGraph nodes array" + "$ref": "#/definitions/backtraces" }, "standard": { "type": "string", @@ -1094,7 +1237,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_0" + "$ref": "#/definitions/target-typeV2_0" }, "backtrace": { "$ref": "#/definitions/backtrace" @@ -1158,7 +1301,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_0" + "$ref": "#/definitions/target-typeV2_0" }, "backtrace": { "$ref": "#/definitions/backtrace" @@ -1222,7 +1365,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_0" + "$ref": "#/definitions/target-typeV2_0" }, "backtrace": { "$ref": "#/definitions/backtrace" @@ -1286,7 +1429,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_0" + "$ref": "#/definitions/target-typeV2_0" }, "backtrace": { "$ref": "#/definitions/backtrace" @@ -1353,7 +1496,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_0" + "$ref": "#/definitions/target-typeV2_0" }, "backtrace": { "$ref": "#/definitions/backtrace" @@ -1420,7 +1563,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_0" + "$ref": "#/definitions/target-typeV2_0" }, "backtrace": { "$ref": "#/definitions/backtrace" @@ -1490,7 +1633,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_0" + "$ref": "#/definitions/target-typeV2_0" }, "backtrace": { "$ref": "#/definitions/backtrace" @@ -1567,7 +1710,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_9" + "$ref": "#/definitions/target-typeV2_9" }, "imported": { "$ref": "#/definitions/importedV2_9" @@ -1674,7 +1817,7 @@ "$ref": "#/definitions/id" }, "type": { - "$ref": "#/definitions/typeV2_9" + "$ref": "#/definitions/target-typeV2_9" }, "imported": { "$ref": "#/definitions/importedV2_9" @@ -1762,6 +1905,116 @@ } }, "additionalProperties": false + }, + "codemodelV2_11": { + "required": [ + "codemodelVersion", + "name", + "id", + "type", + "paths", + "sources", + "backtraceGraph" + ], + "properties": { + "codemodelVersion": { + "$ref": "#/definitions/codemodelVersionV2_11" + }, + "name": { + "$ref": "#/definitions/name" + }, + "id": { + "$ref": "#/definitions/id" + }, + "type": { + "$ref": "#/definitions/target-typeV2_9" + }, + "imported": { + "$ref": "#/definitions/importedV2_9" + }, + "local": { + "$ref": "#/definitions/localV2_9" + }, + "abstract": { + "$ref": "#/definitions/abstractV2_9" + }, + "backtrace": { + "$ref": "#/definitions/backtrace" + }, + "folder": { + "$ref": "#/definitions/folder" + }, + "paths": { + "$ref": "#/definitions/paths" + }, + "nameOnDisk": { + "$ref": "#/definitions/nameOnDisk" + }, + "artifacts": { + "$ref": "#/definitions/artifacts" + }, + "isGeneratorProvided": { + "$ref": "#/definitions/isGeneratorProvided" + }, + "install": { + "$ref": "#/definitions/install" + }, + "launchers": { + "$ref": "#/definitions/launchersV2_7" + }, + "link": { + "$ref": "#/definitions/link" + }, + "archive": { + "$ref": "#/definitions/archive" + }, + "debugger": { + "$ref": "#/definitions/debuggerV2_8" + }, + "dependencies": { + "$ref": "#/definitions/dependencies" + }, + "linkLibraries": { + "$ref": "#/definitions/linkLibrariesV2_9" + }, + "interfaceLinkLibraries": { + "$ref": "#/definitions/interfaceLinkLibrariesV2_9" + }, + "compileDependencies": { + "$ref": "#/definitions/compileDependenciesV2_9" + }, + "interfaceCompileDependencies": { + "$ref": "#/definitions/interfaceCompileDependenciesV2_9" + }, + "objectDependencies": { + "$ref": "#/definitions/onlyTargetDependenciesArrayV2_9" + }, + "orderDependencies": { + "$ref": "#/definitions/onlyTargetDependenciesArrayV2_9" + }, + "fileSets": { + "$ref": "#/definitions/fileSetsV2_11" + }, + "sources": { + "$ref": "#/definitions/sourcesV2_11" + }, + "interfaceSources": { + "$ref": "#/definitions/interfaceSourcesV2_11" + }, + "symbolic": { + "$ref": "#/definitions/symbolicV2_9" + }, + "sourceGroups": { + "$ref": "#/definitions/sourceGroupsV2_10" + }, + "compileGroups": { + "$ref": "#/definitions/compileGroupsV2_6" + }, + "backtraceGraph": { + "$ref": "#/definitions/backtraceGraph" + } + }, + "additionalProperties": false } } }
diff --git a/Help/manual/include/OPTIONS_BUILD.rst b/Help/manual/include/OPTIONS_BUILD.rst index 4d8f88e..213586c 100644 --- a/Help/manual/include/OPTIONS_BUILD.rst +++ b/Help/manual/include/OPTIONS_BUILD.rst
@@ -45,6 +45,16 @@ This option may also be given as a single argument: ``-D<var>:<type>=<value>`` or ``-D<var>=<value>``. + The cache :prop_cache:`HELPSTRING` is populated as follows: + + * If the cache entry is known to CMake, its help string is + set to a one-line description of the variable. + + .. versionadded:: 4.4 + + * Otherwise, its help string is set to the generic default + ``No help, variable specified on the command line.`` + It's important to note that the order of ``-C`` and ``-D`` arguments is significant. They will be carried out in the order they are listed, with the last argument taking precedence over the previous ones. For example, if you @@ -138,59 +148,34 @@ transition and not for publication of a final product. CMake will always emit a warning when the project file is anything other than ``CMakeLists.txt``. -.. option:: -Wno-dev +.. option:: -W<category> - Suppress developer warnings. + Promote the specified diagnostic category (and any children) to at least + ``WARN`` severity. Note that this has no effect on diagnostics already set to + a higher severity. - Suppress warnings that are meant for the author of the - ``CMakeLists.txt`` files. By default this will also turn off - deprecation warnings. + See the :manual:`cmake-diagnostics(7)` manual for a list of accepted values + for ``<category>``. -.. option:: -Wdev +.. option:: -Wno-<category> - Enable developer warnings. + Disable (ignore) all diagnostics of the specified category, including any + child categories. - Enable warnings that are meant for the author of the ``CMakeLists.txt`` - files. By default this will also turn on deprecation warnings. +.. option:: -Werror=<category> -.. option:: -Wdeprecated + Promote the specified diagnostic category (and any children) to at least + ``SEND_ERROR`` severity. Note that this has no effect on diagnostics already + set to a higher severity. - Enable deprecated functionality warnings. +.. option:: -Wno-error=<category> - Enable warnings for usage of deprecated functionality, that are meant - for the author of the ``CMakeLists.txt`` files. + Demote the specified diagnostic category (and any children) to at most + ``WARN`` severity. Note that this has no effect on diagnostics already + set to a lower severity. -.. option:: -Wno-deprecated +.. option:: -Wdev, -Wno-dev - Suppress deprecated functionality warnings. + .. deprecated:: 4.4 - Suppress warnings for usage of deprecated functionality, that are meant - for the author of the ``CMakeLists.txt`` files. - -.. option:: -Werror=<what> - - Treat CMake warnings as errors. ``<what>`` must be one of the following: - - ``dev`` - Make developer warnings errors. - - Make warnings that are meant for the author of the ``CMakeLists.txt`` files - errors. By default this will also turn on deprecated warnings as errors. - - ``deprecated`` - Make deprecated macro and function warnings errors. - - Make warnings for usage of deprecated macros and functions, that are meant - for the author of the ``CMakeLists.txt`` files, errors. - -.. option:: -Wno-error=<what> - - Do not treat CMake warnings as errors. ``<what>`` must be one of the following: - - ``dev`` - Make warnings that are meant for the author of the ``CMakeLists.txt`` files not - errors. By default this will also turn off deprecated warnings as errors. - - ``deprecated`` - Make warnings for usage of deprecated macros and functions, that are meant - for the author of the ``CMakeLists.txt`` files, not errors. + Compatibility synonyms for ``-Wauthor`` / ``-Wno-author``.
diff --git a/Help/manual/instrumentation/index-v1-schema.json b/Help/manual/instrumentation/index-v1-schema.json new file mode 100644 index 0000000..540cb5b --- /dev/null +++ b/Help/manual/instrumentation/index-v1-schema.json
@@ -0,0 +1,184 @@ +{ + "$schema": "http://json-schema.org/draft/2020-12/schema#", + "type": "object", + "required": [ + "version", + "buildDir", + "dataDir", + "hook", + "snippets" + ], + "definitions": { + "nonEmptyStringOrNull": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ] + }, + "positiveIntegerOrNull": { + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "null" + } + ] + } + }, + "properties": { + "version": { + "description": "The data version of the index file.", + "type": "object", + "required": [ + "major", + "minor" + ], + "properties": { + "major": { + "type": "integer", + "const": 1 + }, + "minor": { + "type": "integer", + "enum": [ + 0, + 1 + ] + } + }, + "additionalProperties": false + }, + "buildDir": { + "type": "string", + "description": "The build directory of the CMake project.", + "minLength": 1 + }, + "dataDir": { + "type": "string", + "description": "The full path to the <build>/.cmake/instrumentation/v1/data/ directory.", + "minLength": 1 + }, + "hook": { + "type": "string", + "description": "The name of the hook responsible for generating the index file. In addition to the hooks that can be specified by one of the v1 Query Files, this value may be set to manual if indexing is performed by invoking ctest --collect-instrumentation.", + "enum": [ + "postGenerate", + "preBuild", + "postBuild", + "preCMakeBuild", + "postCMakeBuild", + "postCMakeInstall", + "postCMakeWorkflow", + "postCTest", + "manual" + ] + }, + "snippets": { + "type": "array", + "description": "Contains a list of v1 Snippet Files. This includes all snippet files generated since the previous index file was created. The file paths are relative to dataDir.", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + }, + "trace": { + "type": "string", + "description": "Contains the path to the Google Trace File. This includes data from all corresponding snippets in the index file. The file path is relative to dataDir. Only included when enabled by the v1 Query Files.", + "minLength": 1 + }, + "staticSystemInformation": { + "type": "object", + "description": "Specifies the static information collected about the host machine CMake is being run from. Only included when enabled by the v1 Query Files.", + "properties": { + "OSName": { + "description": "The host operating system name.", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "OSPlatform": { + "description": "The name of the CPU CMake is running on.", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "OSRelease": { + "description": "The OS sub-type (e.g., 'Professional' on Windows).", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "OSVersion": { + "description": "The OS build ID.", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "familyId": { + "description": "The processor family name.", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "hostname": { + "description": "Hostname.", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "is64Bits": { + "type": "boolean", + "description": "True if processor is 64 bit." + }, + "modelId": { + "description": "The processor model ID", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "numberOfLogicalCPU": { + "description": "Number of logical cores.", + "$ref": "#/definitions/positiveIntegerOrNull" + }, + "numberOfPhysicalCPU": { + "description": "Number of physical cores.", + "$ref": "#/definitions/positiveIntegerOrNull" + }, + "processorAPICID": { + "description": "The processor APIC ID.", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "processorCacheSize": { + "description": "The processor L1 cache size.", + "$ref": "#/definitions/positiveIntegerOrNull" + }, + "processorClockFrequency": { + "description": "The processor clock frequency.", + "anyOf": [ + { + "type": "number", + "minimum": 1 + }, + { + "type": "null" + } + ] + }, + "processorName": { + "description": "Human readable processor name.", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "totalPhysicalMemory": { + "description": "Total physical memory in MiB", + "$ref": "#/definitions/positiveIntegerOrNull" + }, + "totalVirtualMemory": { + "description": "Total virtual memory in MiB", + "$ref": "#/definitions/positiveIntegerOrNull" + }, + "vendorID": { + "description": "The processor vendor ID.", + "$ref": "#/definitions/nonEmptyStringOrNull" + }, + "vendorString": { + "description": "The processor vendor string.", + "$ref": "#/definitions/nonEmptyStringOrNull" + } + } + } + } +}
diff --git a/Help/manual/instrumentation/query-v1-schema.json b/Help/manual/instrumentation/query-v1-schema.json index 9dba215..1962603 100644 --- a/Help/manual/instrumentation/query-v1-schema.json +++ b/Help/manual/instrumentation/query-v1-schema.json
@@ -22,7 +22,10 @@ }, "minor": { "type": "integer", - "const": 0 + "enum": [ + 0, + 1 + ] } }, "additionalProperties": false @@ -62,9 +65,11 @@ "enum": [ "staticSystemInformation", "dynamicSystemInformation", + "compileTrace", "cdashSubmit", "cdashVerbose", - "trace" + "trace", + "captureOutput" ], "type": "string" }
diff --git a/Help/manual/instrumentation/snippet-v1-schema.json b/Help/manual/instrumentation/snippet-v1-schema.json new file mode 100644 index 0000000..4e42037 --- /dev/null +++ b/Help/manual/instrumentation/snippet-v1-schema.json
@@ -0,0 +1,527 @@ +{ + "$schema": "http://json-schema.org/draft/2020-12/schema#", + "oneOf": [ + { + "$ref": "#/definitions/snippetV1_0" + }, + { + "$ref": "#/definitions/snippetV1_1" + } + ], + "definitions": { + "snippetConditionals": { + "allOf": [ + { + "if": { + "properties": { + "role": { + "enum": [ + "configure", + "generate", + "compile", + "link", + "custom", + "cmakeBuild", + "cmakeInstall", + "install", + "ctest", + "test" + ] + } + }, + "required": [ + "role" + ] + }, + "then": { + "required": [ + "command" + ] + } + }, + { + "if": { + "properties": { + "role": { + "enum": [ + "compile", + "link" + ] + } + }, + "required": [ + "role" + ] + }, + "then": { + "required": [ + "language", + "target", + "outputs", + "outputSizes" + ] + } + }, + { + "if": { + "properties": { + "role": { + "const": "compile" + } + }, + "required": [ + "role" + ] + }, + "then": { + "required": [ + "source" + ] + } + }, + { + "if": { + "properties": { + "role": { + "const": "custom" + } + }, + "required": [ + "role" + ] + }, + "then": { + "required": [ + "outputs", + "outputSizes" + ] + } + }, + { + "if": { + "properties": { + "role": { + "enum": [ + "compile", + "link", + "custom", + "install", + "test" + ] + } + }, + "required": [ + "role" + ] + }, + "then": { + "required": [ + "config" + ] + } + }, + { + "if": { + "properties": { + "role": { + "const": "ctest" + } + }, + "required": [ + "role" + ] + }, + "then": { + "required": [ + "showOnly" + ] + } + }, + { + "if": { + "properties": { + "role": { + "const": "test" + } + }, + "required": [ + "role" + ] + }, + "then": { + "required": [ + "testName" + ] + } + } + ] + }, + "versionV1_0": { + "type": "object", + "required": [ + "major", + "minor" + ], + "properties": { + "major": { + "type": "integer", + "const": 1 + }, + "minor": { + "type": "integer", + "const": 0 + } + }, + "additionalProperties": false + }, + "versionV1_1": { + "type": "object", + "required": [ + "major", + "minor" + ], + "properties": { + "major": { + "type": "integer", + "const": 1 + }, + "minor": { + "type": "integer", + "const": 1 + } + }, + "additionalProperties": false + }, + "fields": { + "command": { + "type": "string", + "description": "The full command executed. Excluded when role is build.", + "minLength": 1 + }, + "workingDir": { + "type": "string", + "description": "The working directory in which the command was executed.", + "minLength": 1 + }, + "result": { + "description": "The exit code of the command, or null when unavailable.", + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "role": { + "type": "string", + "description": "The type of command executed.", + "enum": [ + "configure", + "generate", + "compile", + "link", + "custom", + "build", + "cmakeBuild", + "cmakeInstall", + "install", + "ctest", + "test" + ] + }, + "target": { + "type": "string", + "description": "The CMake target associated with the command.", + "minLength": 1 + }, + "targetLabels": { + "type": "array", + "description": "Optional labels associated with the target.", + "items": { + "type": "string" + } + }, + "timeStart": { + "type": "integer", + "description": "Command start time in milliseconds since epoch.", + "minimum": 0 + }, + "duration": { + "type": "integer", + "description": "Command duration in milliseconds.", + "minimum": 0 + }, + "outputs": { + "type": "array", + "description": "The command output file paths.", + "items": { + "type": "string", + "minLength": 1 + } + }, + "outputSizes": { + "type": "array", + "description": "Sizes in bytes for each output file.", + "items": { + "type": "integer", + "minimum": 0 + } + }, + "source": { + "type": "string", + "description": "The source file being compiled.", + "minLength": 1 + }, + "language": { + "type": "string", + "description": "The language used by the compile or link command.", + "minLength": 1 + }, + "testName": { + "type": "string", + "description": "The name of the test being executed.", + "minLength": 1 + }, + "config": { + "type": "string", + "description": "The build configuration, such as Release or Debug. May be empty for some test runs." + }, + "dynamicSystemInformation": { + "type": "object", + "description": "Dynamic host information collected before and after command execution.", + "required": [ + "beforeHostMemoryUsed", + "afterHostMemoryUsed", + "beforeCPULoadAverage", + "afterCPULoadAverage" + ], + "properties": { + "beforeHostMemoryUsed": { + "type": "number", + "description": "Host memory used in KiB at command start.", + "minimum": 0 + }, + "afterHostMemoryUsed": { + "type": "number", + "description": "Host memory used in KiB at command end.", + "minimum": 0 + }, + "beforeCPULoadAverage": { + "description": "Average CPU load at command start.", + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "afterCPULoadAverage": { + "description": "Average CPU load at command end.", + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + } + } + }, + "cmakeContent": { + "description": "Relative path to the corresponding v1 CMake content file.", + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ] + }, + "showOnly": { + "type": "boolean", + "description": "True when ctest was invoked with --show-only." + }, + "stdout": { + "type": "string", + "description": "The standard output produced by the command." + }, + "stderr": { + "type": "string", + "description": "The standard error output produced by the command." + }, + "traceFile": { + "description": "A path, relative to the data directory, referencing a copied JSON file generated by Clang's -ftime-trace option.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "snippetV1_0": { + "type": "object", + "required": [ + "version", + "role", + "result", + "timeStart", + "duration", + "workingDir" + ], + "properties": { + "version": { + "$ref": "#/definitions/versionV1_0" + }, + "command": { + "$ref": "#/definitions/fields/command" + }, + "workingDir": { + "$ref": "#/definitions/fields/workingDir" + }, + "result": { + "$ref": "#/definitions/fields/result" + }, + "role": { + "$ref": "#/definitions/fields/role" + }, + "target": { + "$ref": "#/definitions/fields/target" + }, + "targetLabels": { + "$ref": "#/definitions/fields/targetLabels" + }, + "timeStart": { + "$ref": "#/definitions/fields/timeStart" + }, + "duration": { + "$ref": "#/definitions/fields/duration" + }, + "outputs": { + "$ref": "#/definitions/fields/outputs" + }, + "outputSizes": { + "$ref": "#/definitions/fields/outputSizes" + }, + "source": { + "$ref": "#/definitions/fields/source" + }, + "language": { + "$ref": "#/definitions/fields/language" + }, + "testName": { + "$ref": "#/definitions/fields/testName" + }, + "config": { + "$ref": "#/definitions/fields/config" + }, + "dynamicSystemInformation": { + "$ref": "#/definitions/fields/dynamicSystemInformation" + }, + "cmakeContent": { + "$ref": "#/definitions/fields/cmakeContent" + }, + "showOnly": { + "$ref": "#/definitions/fields/showOnly" + } + }, + "additionalProperties": false, + "allOf": [ + { + "$ref": "#/definitions/snippetConditionals" + } + ] + }, + "snippetV1_1": { + "type": "object", + "required": [ + "version", + "role", + "result", + "timeStart", + "duration", + "workingDir" + ], + "properties": { + "version": { + "$ref": "#/definitions/versionV1_1" + }, + "command": { + "$ref": "#/definitions/fields/command" + }, + "workingDir": { + "$ref": "#/definitions/fields/workingDir" + }, + "result": { + "$ref": "#/definitions/fields/result" + }, + "role": { + "$ref": "#/definitions/fields/role" + }, + "target": { + "$ref": "#/definitions/fields/target" + }, + "targetLabels": { + "$ref": "#/definitions/fields/targetLabels" + }, + "timeStart": { + "$ref": "#/definitions/fields/timeStart" + }, + "duration": { + "$ref": "#/definitions/fields/duration" + }, + "outputs": { + "$ref": "#/definitions/fields/outputs" + }, + "outputSizes": { + "$ref": "#/definitions/fields/outputSizes" + }, + "source": { + "$ref": "#/definitions/fields/source" + }, + "language": { + "$ref": "#/definitions/fields/language" + }, + "testName": { + "$ref": "#/definitions/fields/testName" + }, + "config": { + "$ref": "#/definitions/fields/config" + }, + "dynamicSystemInformation": { + "$ref": "#/definitions/fields/dynamicSystemInformation" + }, + "cmakeContent": { + "$ref": "#/definitions/fields/cmakeContent" + }, + "showOnly": { + "$ref": "#/definitions/fields/showOnly" + }, + "stderr": { + "$ref": "#/definitions/fields/stderr" + }, + "stdout": { + "$ref": "#/definitions/fields/stdout" + }, + "traceFile": { + "$ref": "#/definitions/fields/traceFile" + } + }, + "additionalProperties": false, + "allOf": [ + { + "$ref": "#/definitions/snippetConditionals" + } + ] + } + } +}
diff --git a/Help/manual/presets/architecture-properties.rst b/Help/manual/presets/architecture-properties.rst new file mode 100644 index 0000000..31182b5 --- /dev/null +++ b/Help/manual/presets/architecture-properties.rst
@@ -0,0 +1,27 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.configurePresets.architecture.value`: + +``value`` + An optional string representing the value. + +.. _`CMakePresets.configurePresets.architecture.strategy`: + +``strategy`` + An optional string telling CMake how to handle the field. + Valid values are: + + ``"set"`` + Set the respective value. This will result in an error for generators + that do not support the respective field. + + ``"external"`` + Do not set the value, even if the generator supports it. This is + useful if, for example, a preset uses the Ninja generator, and an IDE + knows how to set up the Visual C++ environment from the architecture + and toolset fields. In that case, CMake will ignore the field, but + the IDE can use them to set up the environment before invoking CMake. + + If no ``strategy`` field is given, or if the field uses the string form + rather than the object form, the behavior is the same as ``"set"``.
diff --git a/Help/manual/presets/buildPresets-properties.rst b/Help/manual/presets/buildPresets-properties.rst new file mode 100644 index 0000000..1256280 --- /dev/null +++ b/Help/manual/presets/buildPresets-properties.rst
@@ -0,0 +1,214 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.buildPresets.name`: + +``name`` + A required string representing the machine-friendly name of the + preset. This identifier is used in the + :option:`cmake --build --preset <cmake--build --preset>` option. + There must not be two build presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a build preset may have the + same name as a configure, test, package, or workflow preset. + +.. _`CMakePresets.buildPresets.hidden`: + +``hidden`` + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the + :cmake-build-option:`--preset` argument and does not have to have a + valid :preset:`buildPresets.configurePreset`, even from + inheritance. ``hidden`` presets are intended to be used as a base + for other presets to inherit via the + :preset:`buildPresets.inherits` field. + +.. _`CMakePresets.buildPresets.inherits`: + +``inherits`` + An optional array of strings representing the names of presets from which + to inherit. This field can also be a string, which is equivalent to an + array containing one string. + + The preset will inherit all of the fields from the ``inherits`` presets + by default (except ``name``, ``hidden``, ``inherits``, ``description``, + and ``displayName``), but can override them as desired. If multiple + ``inherits`` presets provide conflicting values for the same field, the + earlier preset in the ``inherits`` array will be preferred. + + A preset can only inherit from another preset that is defined in the same + file or in one of the files it includes (directly or indirectly). Presets + in ``CMakePresets.json`` may not inherit from presets in + ``CMakeUserPresets.json``. + +.. _`CMakePresets.buildPresets.condition`: + +``condition`` + .. presets-versionadded:: 3 + + An optional `Condition`_ object. + +.. _`CMakePresets.buildPresets.vendor`: + +``vendor`` + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. + +.. _`CMakePresets.buildPresets.displayName`: + +``displayName`` + An optional string with a human-friendly name of the preset. + +.. _`CMakePresets.buildPresets.description`: + +``description`` + An optional string with a human-friendly description of the preset. + +.. _`CMakePresets.buildPresets.environment`: + +``environment`` + An optional map of environment variables. The key is the variable name + (which may not be an empty string), and the value is either ``null`` or a + string representing the value of the variable. Each variable is set + regardless of whether or not a value was given to it by the process's + environment. + + This field supports `macro expansion`_, and environment variables in this + map may reference each other, and may be listed in any order, as long as + such references do not cause a cycle (for example, if ``ENV_1`` is + ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables + by accessing only values from the parent environment. + + Environment variables are inherited through the ``inherits`` field, and + the preset's environment will be the union of its own ``environment`` and + the ``environment`` from all its parents. If multiple presets in this + union define the same variable, the standard rules of ``inherits`` are + applied. Setting a variable to ``null`` causes it to not be set, even if + a value was inherited from another preset. + + .. note:: + + For a CMake project using :module:`ExternalProject` with a + configuration preset having environment variables needed in the + ExternalProject, use a build preset that inherits that + configuration preset or the ExternalProject will not have the + environment variables set in the configuration preset. Example: + suppose the host defaults to one compiler (say Clang) and the user + wishes to use another compiler (say GCC). Set configuration preset + environment variables :envvar:`CC` and :envvar:`CXX` and use a + build preset that inherits that configuration preset. Otherwise the + ExternalProject may use a different (system default) compiler than + the top-level CMake project. + + +.. _`CMakePresets.buildPresets.configurePreset`: + +``configurePreset`` + An optional string specifying the name of a configure preset to + associate with this build preset. If ``configurePreset`` is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build directory is inferred from the + configure preset, so the build will take place in the same + ``binaryDir`` that the configuration did. + +.. _`CMakePresets.buildPresets.inheritConfigureEnvironment`: + +``inheritConfigureEnvironment`` + An optional boolean that defaults to ``true``. If ``true``, the + environment variables from the associated configure preset are + inherited after all inherited build preset environments, but before + environment variables explicitly specified in this build preset. + +.. _`CMakePresets.buildPresets.jobs`: + +``jobs`` + An optional integer. Equivalent to passing + :cmake-build-option:`--parallel` or ``-j`` on the command line. + If the value is ``0``, it is equivalent to passing ``--parallel`` + with ``<jobs>`` omitted; alternatively, one can define the + environment variable :envvar:`CMAKE_BUILD_PARALLEL_LEVEL` as an + empty string using the ``environment`` field. + + .. versionchanged:: 4.3 + + This field does not accept negative integer values, regardless of + the version in the presets file. + +.. _`CMakePresets.buildPresets.targets`: + +``targets`` + An optional string or array of strings. Equivalent to passing + :cmake-build-option:`--target` or ``-t`` on the command line. + Vendors may ignore the targets property or hide build presets that + explicitly specify targets. This field supports `macro expansion`_. + +.. _`CMakePresets.buildPresets.configuration`: + +``configuration`` + An optional string. Equivalent to passing + :cmake-build-option:`--config` on the command line. + +.. _`CMakePresets.buildPresets.cleanFirst`: + +``cleanFirst`` + An optional bool. If ``true``, equivalent to passing + :cmake-build-option:`--clean-first` on the command line. + +.. _`CMakePresets.buildPresets.resolvePackageReferences`: + +``resolvePackageReferences`` + .. presets-versionadded:: 4 + + An optional string that specifies the package resolve mode. + + Package references are used to define dependencies to packages from + external package managers. Currently only NuGet in combination with + the :ref:`Visual Studio generators` is supported. If there are no + targets that define package references, this option does nothing. + Valid values are: + + ``on`` + Causes package references to be resolved before attempting a + build. + + ``off`` + Package references will not be resolved. Note that this may cause + errors in some build environments, such as .NET SDK style + projects. + + ``only`` + Only resolve package references, but do not perform a build. + + .. note:: + + The command line parameter + :cmake-build-option:`--resolve-package-references` + will take priority over this setting. If the command line + parameter is not provided and this setting is not specified, an + environment-specific cache variable will be evaluated to decide, + if package restoration should be performed. + + When using the :ref:`Visual Studio generators`, + package references are defined using the + :prop_tgt:`VS_PACKAGE_REFERENCES` property. Package references + are restored using NuGet. It can be disabled by setting the + :variable:`CMAKE_VS_NUGET_PACKAGE_RESTORE` variable to ``OFF``. + This can also be done from within a configure preset. + +.. _`CMakePresets.buildPresets.verbose`: + +``verbose`` + An optional boolean. If ``true``, equivalent to passing + :cmake-build-option:`--verbose` on the command line. + +.. _`CMakePresets.buildPresets.nativeToolOptions`: + +``nativeToolOptions`` + An optional array of strings. Equivalent to passing options after + :cmake-build-option:`--` on the command line. The array values + support `macro expansion`_.
diff --git a/Help/manual/presets/cacheVariables-properties.rst b/Help/manual/presets/cacheVariables-properties.rst new file mode 100644 index 0000000..76e1a66 --- /dev/null +++ b/Help/manual/presets/cacheVariables-properties.rst
@@ -0,0 +1,7 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.configurePresets.cacheVariables.type`: + +``type`` + An optional string representing the type of the variable. It should be BOOL, FILEPATH, PATH, STRING, or INTERNAL.
diff --git a/Help/manual/presets/cmakeMinimumRequired-properties.rst b/Help/manual/presets/cmakeMinimumRequired-properties.rst new file mode 100644 index 0000000..e3ede9b --- /dev/null +++ b/Help/manual/presets/cmakeMinimumRequired-properties.rst
@@ -0,0 +1,17 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.cmakeMinimumRequired.major`: + +``major`` + An optional integer representing the major version. + +.. _`CMakePresets.cmakeMinimumRequired.minor`: + +``minor`` + An optional integer representing the minor version. + +.. _`CMakePresets.cmakeMinimumRequired.patch`: + +``patch`` + An optional integer representing the patch version.
diff --git a/Help/manual/presets/configurePresets-properties.rst b/Help/manual/presets/configurePresets-properties.rst new file mode 100644 index 0000000..c15874b --- /dev/null +++ b/Help/manual/presets/configurePresets-properties.rst
@@ -0,0 +1,251 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.configurePresets.name`: + +``name`` + A required string representing the machine-friendly name of the + preset. This identifier is used in the :option:`cmake --preset` + option. There must not be two configure presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a configure preset may have + the same name as a build, test, package, or workflow preset. + +.. _`CMakePresets.configurePresets.hidden`: + +``hidden`` + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the + :cmake-option:`--preset` argument, will not show up in the + :manual:`CMake GUI <cmake-gui(1)>`, and does not have to have a + valid ``generator`` or ``binaryDir``, even from inheritance. + ``hidden`` presets are intended to be used as a base for other + presets to inherit via the ``inherits`` field. + +.. _`CMakePresets.configurePresets.inherits`: + +``inherits`` + An optional array of strings representing the names of presets from which + to inherit. This field can also be a string, which is equivalent to an + array containing one string. + + The preset will inherit all of the fields from the ``inherits`` presets + by default (except ``name``, ``hidden``, ``inherits``, ``description``, + and ``displayName``), but can override them as desired. If multiple + ``inherits`` presets provide conflicting values for the same field, the + earlier preset in the ``inherits`` array will be preferred. + + A preset can only inherit from another preset that is defined in the same + file or in one of the files it includes (directly or indirectly). Presets + in ``CMakePresets.json`` may not inherit from presets in + ``CMakeUserPresets.json``. + +.. _`CMakePresets.configurePresets.condition`: + +``condition`` + .. presets-versionadded:: 3 + + An optional `Condition`_ object. + +.. _`CMakePresets.configurePresets.vendor`: + +``vendor`` + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. + +.. _`CMakePresets.configurePresets.displayName`: + +``displayName`` + An optional string with a human-friendly name of the preset. + +.. _`CMakePresets.configurePresets.description`: + +``description`` + An optional string with a human-friendly description of the preset. + +.. _`CMakePresets.configurePresets.generator`: + +``generator`` + An optional string representing the + :manual:`generator <cmake-generators(7)>` to use for the preset. + + .. presets-versionchanged:: 3 + + If omitted, CMake will fall back to regular generator discovery + procedure. In previous versions, if not specified, this field + must be inherited from the ``inherits`` preset (unless this + preset is ``hidden``). + + Note that for :ref:`Visual Studio generators`, unlike in the + command line :cmake-option:`-G` argument, you cannot include the + platform name in the generator name. Use the ``architecture`` + field instead. + +.. _`CMakePresets.configurePresets.architecture`: + +``architecture`` + Optional field representing the platform for + :manual:`generators <cmake-generators(7)>` that support it. + + See :option:`cmake -A` for possible values. + + ``architecture`` may be either a string or an object with the + following fields: + + .. include:: presets/architecture-properties.rst + +.. _`CMakePresets.configurePresets.toolset`: + +``toolset`` + Optional field representing the toolset for + :manual:`generators <cmake-generators(7)>` that support it. + + See :option:`cmake -T` for possible values. + + ``toolset`` may be either a string or an object with the following + fields: + + .. include:: presets/toolset-properties.rst + +.. _`CMakePresets.configurePresets.toolchainFile`: + +``toolchainFile`` + .. presets-versionadded:: 3 + + An optional string representing the path to the toolchain file. + This field supports `macro expansion`_. If a relative path is + specified, it is calculated relative to the build directory, and if + not found, relative to the source directory. This field takes + precedence over any :variable:`CMAKE_TOOLCHAIN_FILE` value. + +.. _`CMakePresets.configurePresets.graphviz`: + +``graphviz`` + .. presets-versionadded:: 10 + + An optional string representing the path to the graphviz input + file, that will contain all the library and executable dependencies + in the project. See the documentation for + :option:`cmake --graphviz` for more details. + + This field supports `macro expansion`_. If a relative path is + specified, it is calculated relative to the current working + directory. + +.. _`CMakePresets.configurePresets.binaryDir`: + +``binaryDir`` + An optional string representing the path to the output binary + directory. This field supports `macro expansion`_. If a relative + path is specified, it is calculated relative to the source + directory. + + .. presets-versionchanged:: 3 + + If omitted, CMake will calculate the path using regular methods. + In previous versions, if not specified, this field must be + inherited from the ``inherits`` preset (unless this preset is + ``hidden``). + +.. _`CMakePresets.configurePresets.installDir`: + +``installDir`` + .. presets-versionadded:: 3 + + An optional string representing the path to the installation + directory, which will be used as the + :variable:`CMAKE_INSTALL_PREFIX` variable. This field supports + `macro expansion`_. If a relative path is specified, it is + calculated relative to the source directory. + +.. _`CMakePresets.configurePresets.cmakeExecutable`: + +``cmakeExecutable`` + An optional string representing the path to the CMake executable to use for this preset. This is reserved for use by IDEs, and is not used by CMake itself. IDEs that use this field should expand any macros in it. + +.. _`CMakePresets.configurePresets.cacheVariables`: + +``cacheVariables`` + An optional map of cache variables. The key is the variable name + (which may not be an empty string), and the value is either + ``null``, a boolean (which is equivalent to a value of ``"TRUE"`` + or ``"FALSE"`` and a type of ``BOOL``), a string representing the + value of the variable (which supports `macro expansion`_), or an + object with the following fields: + + ``type`` + An optional string representing the type of the variable. + + ``value`` + A required string or boolean representing the value of the + variable. A boolean is equivalent to ``"TRUE"`` or ``"FALSE"``. + This field supports `macro expansion`_. + + Cache variables are inherited through the ``inherits`` field, and + the preset's variables will be the union of its own + ``cacheVariables`` and the ``cacheVariables`` from all its parents. + If multiple presets in this union define the same variable, the + standard rules of ``inherits`` are applied. Setting a variable to + ``null`` causes it to not be set, even if a value was inherited + from another preset. + +.. _`CMakePresets.configurePresets.environment`: + +``environment`` + An optional map of environment variables. The key is the variable name + (which may not be an empty string), and the value is either ``null`` or a + string representing the value of the variable. Each variable is set + regardless of whether or not a value was given to it by the process's + environment. + + This field supports `macro expansion`_, and environment variables in this + map may reference each other, and may be listed in any order, as long as + such references do not cause a cycle (for example, if ``ENV_1`` is + ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables + by accessing only values from the parent environment. + + Environment variables are inherited through the ``inherits`` field, and + the preset's environment will be the union of its own ``environment`` and + the ``environment`` from all its parents. If multiple presets in this + union define the same variable, the standard rules of ``inherits`` are + applied. Setting a variable to ``null`` causes it to not be set, even if + a value was inherited from another preset. + +.. _`CMakePresets.configurePresets.warnings`: + +``warnings`` + An optional object specifying the warnings to enable. + The object may contain the following fields: + + .. include:: presets/warnings-properties.rst + +.. _`CMakePresets.configurePresets.errors`: + +``errors`` + An optional object specifying the errors to enable. + The object may contain the following fields: + + .. include:: presets/errors-properties.rst + +.. _`CMakePresets.configurePresets.debug`: + +``debug`` + An optional object specifying debug options. + The object may contain the following fields: + + .. include:: presets/debug-properties.rst + +.. _`CMakePresets.configurePresets.trace`: + +``trace`` + .. presets-versionadded:: 7 + + An optional object specifying trace options. + The object may contain the following fields: + + .. include:: presets/trace-properties.rst
diff --git a/Help/manual/presets/debug-properties.rst b/Help/manual/presets/debug-properties.rst new file mode 100644 index 0000000..6c8cbde --- /dev/null +++ b/Help/manual/presets/debug-properties.rst
@@ -0,0 +1,20 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.configurePresets.debug.output`: + +``output`` + An optional boolean. Setting this to ``true`` is equivalent to + passing :cmake-option:`--debug-output` on the command line. + +.. _`CMakePresets.configurePresets.debug.tryCompile`: + +``tryCompile`` + An optional boolean. Setting this to ``true`` is equivalent to + passing :cmake-option:`--debug-trycompile` on the command line. + +.. _`CMakePresets.configurePresets.debug.find`: + +``find`` + An optional boolean. Setting this to ``true`` is equivalent to + passing :cmake-option:`--debug-find` on the command line.
diff --git a/Help/manual/presets/errors-properties.rst b/Help/manual/presets/errors-properties.rst new file mode 100644 index 0000000..00b65a0 --- /dev/null +++ b/Help/manual/presets/errors-properties.rst
@@ -0,0 +1,84 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.configurePresets.errors.author`: + +``author`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :cmake-option:`-Werror=author` or + :cmake-option:`-Wno-error=author` on the command line. + This may not be set to ``true`` if ``warnings.author`` is set to ``false``. + +.. _`CMakePresets.configurePresets.errors.deprecated`: + +``deprecated`` + An optional boolean. Equivalent to passing + :cmake-option:`-Werror=deprecated` or + :cmake-option:`-Wno-error=deprecated` + on the command line. + This may not be set to ``true`` + if ``warnings.deprecated`` is set to ``false``. + +.. _`CMakePresets.configurePresets.errors.dev`: + +``dev`` + .. presets-versionremoved:: 12 + + An optional boolean. Equivalent to passing + :cmake-option:`-Werror=dev` or + :cmake-option:`-Wno-error=dev` + on the command line. + This may not be set to ``true`` + if ``warnings.dev`` is set to ``false``. + +.. _`CMakePresets.configurePresets.errors.experimental`: + +``experimental`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :cmake-option:`-Werror=experimental` or + :cmake-option:`-Wno-error=experimental` on the command line. + This may not be set to ``true`` if ``warnings.experimental`` is set to ``false``. + +.. _`CMakePresets.configurePresets.errors.installAbsoluteDestination`: + +``installAbsoluteDestination`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :cmake-option:`-Werror=install-absolute-destination` or + :cmake-option:`-Wno-error=install-absolute-destination` on the command line. + This may not be set to ``true`` if ``warnings.installAbsoluteDestination`` is set to ``false``. + +.. _`CMakePresets.configurePresets.errors.policy`: + +``policy`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :cmake-option:`-Werror=policy` or + :cmake-option:`-Wno-error=policy` on the command line. + This may not be set to ``true`` if ``warnings.policy`` is set to ``false``. + +.. _`CMakePresets.configurePresets.errors.uninitialized`: + +``uninitialized`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing + :cmake-option:`-Werror=uninitialized` or + :cmake-option:`-Wno-error=uninitialized` + on the command line. + This may not be set to ``true`` + if ``warnings.uninitialized`` is set to ``false``. + +.. _`CMakePresets.configurePresets.errors.unusedCli`: + +``unusedCli`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing + :cmake-option:`-Werror=unused-cli` or + :cmake-option:`-Wno-error=unused-cli` + on the command line. + This may not be set to ``true`` + if ``warnings.unusedCli`` is set to ``false``.
diff --git a/Help/manual/presets/exclude-properties.rst b/Help/manual/presets/exclude-properties.rst new file mode 100644 index 0000000..24775b5 --- /dev/null +++ b/Help/manual/presets/exclude-properties.rst
@@ -0,0 +1,25 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.filter.exclude.name`: + +``name`` + An optional string specifying a regex for test names. + Equivalent to passing :ctest-option:`--exclude-regex` on + the command line. This field supports `macro expansion`_. + +.. _`CMakePresets.testPresets.filter.exclude.label`: + +``label`` + An optional string specifying a regex for test labels. + Equivalent to passing :ctest-option:`--label-exclude` on + the command line. This field supports `macro expansion`_. + +.. _`CMakePresets.testPresets.filter.exclude.fixtures`: + +``fixtures`` + An optional object specifying which fixtures to exclude + from adding tests. The object may contain the following + fields: + + .. include:: presets/fixtures-properties.rst
diff --git a/Help/manual/presets/execution-properties.rst b/Help/manual/presets/execution-properties.rst new file mode 100644 index 0000000..8e3d4a0 --- /dev/null +++ b/Help/manual/presets/execution-properties.rst
@@ -0,0 +1,115 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.execution.stopOnFailure`: + +``stopOnFailure`` + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--stop-on-failure` on the command line. + +.. _`CMakePresets.testPresets.execution.enableFailover`: + +``enableFailover`` + An optional bool. If ``true``, equivalent to passing + :ctest-option:`-F` on the command line. + +.. _`CMakePresets.testPresets.execution.jobs`: + +``jobs`` + An optional integer. Equivalent to passing + :ctest-option:`--parallel` on the command line. If the value is + ``0``, it is equivalent to unbounded parallelism. + + .. presets-versionchanged:: 11 + + This field can also be a string, in which case it must be + empty, and is equivalent to passing ``--parallel`` with + ``<jobs>`` omitted. + + .. versionchanged:: 4.3 + + This field does not accept negative integer values, + regardless of the version in the presets file. + +.. _`CMakePresets.testPresets.execution.resourceSpecFile`: + +``resourceSpecFile`` + An optional string. Equivalent to passing + :ctest-option:`--resource-spec-file` on the command line. This + field supports `macro expansion`_. + +.. _`CMakePresets.testPresets.execution.testLoad`: + +``testLoad`` + An optional integer. Equivalent to passing + :ctest-option:`--test-load` on the command line. + +.. _`CMakePresets.testPresets.execution.showOnly`: + +``showOnly`` + An optional string. Equivalent to passing + :ctest-option:`--show-only` on the command line. The string + must be one of the following values: + + * ``human`` + + * ``json-v1`` + +.. _`CMakePresets.testPresets.execution.repeat`: + +``repeat`` + An optional object specifying how to repeat tests. Equivalent + to passing :ctest-option:`--repeat` on the command line. + The object must have the following fields: + + .. include:: presets/repeat-properties.rst + +.. _`CMakePresets.testPresets.execution.interactiveDebugging`: + +``interactiveDebugging`` + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--interactive-debug-mode 1` on the command line. + If ``false``, equivalent to passing + :ctest-option:`--interactive-debug-mode 0` on the command line. + +.. _`CMakePresets.testPresets.execution.scheduleRandom`: + +``scheduleRandom`` + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--schedule-random` on the command line. + +.. _`CMakePresets.testPresets.execution.timeout`: + +``timeout`` + An optional integer. Equivalent to passing + :ctest-option:`--timeout` on the command line. + +.. _`CMakePresets.testPresets.execution.noTestsAction`: + +``noTestsAction`` + An optional string specifying the behavior if no tests are + found. Must be one of the following values: + + ``default`` + Equivalent to not passing any value on the command line. + + ``error`` + Equivalent to passing :ctest-option:`--no-tests=error` + on the command line. + + ``ignore`` + Equivalent to passing :ctest-option:`--no-tests=ignore` + on the command line. + +.. _`CMakePresets.testPresets.execution.testPassthroughArguments`: + +``testPassthroughArguments`` + .. presets-versionadded:: 12 + + An optional array of strings. Each element is forwarded as an + argument to every test executable, equivalent to passing + arguments after :option:`ctest --` on the command line. + + When both preset and command-line passthrough arguments are + specified, preset arguments appear first, followed by + command-line arguments.
diff --git a/Help/manual/presets/filter-properties.rst b/Help/manual/presets/filter-properties.rst new file mode 100644 index 0000000..083e268 --- /dev/null +++ b/Help/manual/presets/filter-properties.rst
@@ -0,0 +1,18 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.filter.include`: + +``include`` + An optional object specifying which tests to include. + The object may contain the following fields: + + .. include:: presets/include-properties.rst + +.. _`CMakePresets.testPresets.filter.exclude`: + +``exclude`` + An optional object specifying which tests to exclude. + The object may contain the following fields: + + .. include:: presets/exclude-properties.rst
diff --git a/Help/manual/presets/fixtures-properties.rst b/Help/manual/presets/fixtures-properties.rst new file mode 100644 index 0000000..727236a --- /dev/null +++ b/Help/manual/presets/fixtures-properties.rst
@@ -0,0 +1,27 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.filter.exclude.fixtures.any`: + +``any`` + An optional string specifying a regex for text fixtures + to exclude from adding any tests. Equivalent to passing + :ctest-option:`--fixture-exclude-any` on the command + line. This field supports `macro expansion`_. + +.. _`CMakePresets.testPresets.filter.exclude.fixtures.setup`: + +``setup`` + An optional string specifying a regex for text fixtures + to exclude from adding setup tests. Equivalent to + passing :ctest-option:`--fixture-exclude-setup` on the + command line. This field supports `macro expansion`_. + +.. _`CMakePresets.testPresets.filter.exclude.fixtures.cleanup`: + +``cleanup`` + An optional string specifying a regex for text fixtures + to exclude from adding cleanup tests. Equivalent to + passing :ctest-option:`--fixture-exclude-cleanup` on + the command line. + This field supports `macro expansion`_.
diff --git a/Help/manual/presets/include-properties.rst b/Help/manual/presets/include-properties.rst new file mode 100644 index 0000000..b55c0e1 --- /dev/null +++ b/Help/manual/presets/include-properties.rst
@@ -0,0 +1,37 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.filter.include.name`: + +``name`` + An optional string specifying a regex for test names. + Equivalent to passing :ctest-option:`--tests-regex` on the + command line. This field supports `macro expansion`_. + + CMake regex syntax is described under + :ref:`string(REGEX) <Regex Specification>`. + +.. _`CMakePresets.testPresets.filter.include.label`: + +``label`` + An optional string specifying a regex for test labels. + Equivalent to passing :ctest-option:`--label-regex` on the + command line. This field supports `macro expansion`_. + +.. _`CMakePresets.testPresets.filter.include.useUnion`: + +``useUnion`` + An optional bool. Equivalent to passing + :ctest-option:`--union` on the command line. + +.. _`CMakePresets.testPresets.filter.include.index`: + +``index`` + An optional object specifying tests to include by test + index. The object may contain the following fields. Can + also be an optional string specifying a file with the + command line syntax for + :ctest-option:`--tests-information`. If specified as a + string, this field supports `macro expansion`_. + + .. include:: presets/index-properties.rst
diff --git a/Help/manual/presets/index-properties.rst b/Help/manual/presets/index-properties.rst new file mode 100644 index 0000000..6bbce33 --- /dev/null +++ b/Help/manual/presets/index-properties.rst
@@ -0,0 +1,22 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.filter.include.index.start`: + +``start`` + An optional integer specifying a test index at which to start testing. + +.. _`CMakePresets.testPresets.filter.include.index.end`: + +``end`` + An optional integer specifying a test index at which to stop testing. + +.. _`CMakePresets.testPresets.filter.include.index.stride`: + +``stride`` + An optional integer specifying the increment. + +.. _`CMakePresets.testPresets.filter.include.index.specificTests`: + +``specificTests`` + An optional array of integers specifying specific test indices to run.
diff --git a/Help/manual/presets/packageOutput-properties.rst b/Help/manual/presets/packageOutput-properties.rst new file mode 100644 index 0000000..d8ab6fb --- /dev/null +++ b/Help/manual/presets/packageOutput-properties.rst
@@ -0,0 +1,16 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.packagePresets.output.debug`: + +``debug`` + An optional boolean specifying whether or not to print debug + information. A value of ``true`` is equivalent to passing + :cpack-option:`--debug` on the command line. + +.. _`CMakePresets.packagePresets.output.verbose`: + +``verbose`` + An optional boolean specifying whether or not to print + verbosely. A value of ``true`` is equivalent to passing + :cpack-option:`--verbose` on the command line.
diff --git a/Help/manual/presets/packagePresets-properties.rst b/Help/manual/presets/packagePresets-properties.rst new file mode 100644 index 0000000..863500f --- /dev/null +++ b/Help/manual/presets/packagePresets-properties.rst
@@ -0,0 +1,175 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.packagePresets.name`: + +``name`` + A required string representing the machine-friendly name of the + preset. This identifier is used in the :option:`cpack --preset` + option. There must not be two package presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a package preset may have + the same name as a configure, build, test, or workflow preset. + +.. _`CMakePresets.packagePresets.hidden`: + +``hidden`` + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the + :cpack-option:`--preset` argument and does not have to have a valid + :preset:`packagePresets.configurePreset`, even from + inheritance. ``hidden`` presets are intended to be used as a base + for other presets to inherit via the + :preset:`packagePresets.inherits` field. + +.. _`CMakePresets.packagePresets.inherits`: + +``inherits`` + An optional array of strings representing the names of presets from which + to inherit. This field can also be a string, which is equivalent to an + array containing one string. + + The preset will inherit all of the fields from the ``inherits`` presets + by default (except ``name``, ``hidden``, ``inherits``, ``description``, + and ``displayName``), but can override them as desired. If multiple + ``inherits`` presets provide conflicting values for the same field, the + earlier preset in the ``inherits`` array will be preferred. + + A preset can only inherit from another preset that is defined in the same + file or in one of the files it includes (directly or indirectly). Presets + in ``CMakePresets.json`` may not inherit from presets in + ``CMakeUserPresets.json``. + +.. _`CMakePresets.packagePresets.condition`: + +``condition`` + An optional `Condition`_ object. + +.. _`CMakePresets.packagePresets.vendor`: + +``vendor`` + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. + +.. _`CMakePresets.packagePresets.displayName`: + +``displayName`` + An optional string with a human-friendly name of the preset. + +.. _`CMakePresets.packagePresets.description`: + +``description`` + An optional string with a human-friendly description of the preset. + +.. _`CMakePresets.packagePresets.environment`: + +``environment`` + An optional map of environment variables. The key is the variable name + (which may not be an empty string), and the value is either ``null`` or a + string representing the value of the variable. Each variable is set + regardless of whether or not a value was given to it by the process's + environment. + + This field supports `macro expansion`_, and environment variables in this + map may reference each other, and may be listed in any order, as long as + such references do not cause a cycle (for example, if ``ENV_1`` is + ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables + by accessing only values from the parent environment. + + Environment variables are inherited through the ``inherits`` field, and + the preset's environment will be the union of its own ``environment`` and + the ``environment`` from all its parents. If multiple presets in this + union define the same variable, the standard rules of ``inherits`` are + applied. Setting a variable to ``null`` causes it to not be set, even if + a value was inherited from another preset. + +.. _`CMakePresets.packagePresets.configurePreset`: + +``configurePreset`` + An optional string specifying the name of a configure preset to + associate with this package preset. If ``configurePreset`` is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build directory is inferred from the + configure preset, so packaging will run in the same ``binaryDir`` + that the configuration did and build did. + +.. _`CMakePresets.packagePresets.inheritConfigureEnvironment`: + +``inheritConfigureEnvironment`` + An optional boolean that defaults to ``true``. If ``true``, the + environment variables from the associated configure preset are + inherited after all inherited package preset environments, but + before environment variables explicitly specified in this package + preset. + +.. _`CMakePresets.packagePresets.generators`: + +``generators`` + An optional array of strings representing generators for CPack to use. + +.. _`CMakePresets.packagePresets.configurations`: + +``configurations`` + An optional array of strings representing build configurations for CPack to package. + +.. _`CMakePresets.packagePresets.variables`: + +``variables`` + An optional map of variables to pass to CPack, equivalent to + :cpack-option:`-D` arguments. Each key is the name of a variable, + and the value is the string to assign to that variable. + +.. _`CMakePresets.packagePresets.configFile`: + +``configFile`` + An optional string representing the config file for CPack to use. + +.. _`CMakePresets.packagePresets.output`: + +``output`` + An optional object specifying output options. Valid keys are: + + .. include:: presets/packageOutput-properties.rst + +.. _`CMakePresets.packagePresets.packageName`: + +``packageName`` + An optional string representing the package name. + + .. note:: + + Due to problems with the implementation, this field does not affect + the name of the final package file produced. However, other aspects + of the package may use the value, leading to inconsistencies. A + future CMake release may address this problem, but until then, it + is recommended that this field not be used. + + +.. _`CMakePresets.packagePresets.packageVersion`: + +``packageVersion`` + An optional string representing the package version. + + .. note:: + + Due to problems with the implementation, this field does not affect + the name of the final package file produced. However, other aspects + of the package may use the value, leading to inconsistencies. A + future CMake release may address this problem, but until then, it + is recommended that this field not be used. + + +.. _`CMakePresets.packagePresets.packageDirectory`: + +``packageDirectory`` + An optional string representing the directory in which to place the package. + +.. _`CMakePresets.packagePresets.vendorName`: + +``vendorName`` + An optional string representing the vendor name.
diff --git a/Help/manual/presets/repeat-properties.rst b/Help/manual/presets/repeat-properties.rst new file mode 100644 index 0000000..98800ff --- /dev/null +++ b/Help/manual/presets/repeat-properties.rst
@@ -0,0 +1,18 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.execution.repeat.mode`: + +``mode`` + A required string. Must be one of the following values: + + * ``until-fail`` + + * ``until-pass`` + + * ``after-timeout`` + +.. _`CMakePresets.testPresets.execution.repeat.count`: + +``count`` + A required integer.
diff --git a/Help/manual/presets/root-properties.rst b/Help/manual/presets/root-properties.rst new file mode 100644 index 0000000..a1ccccb --- /dev/null +++ b/Help/manual/presets/root-properties.rst
@@ -0,0 +1,78 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.$schema`: + +``$schema`` + .. presets-versionadded:: 8 + + 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. + +.. _`CMakePresets.version`: + +``version`` + A required integer representing the version of the JSON schema. See + `Versions`_ for discussion of the supported versions and the + corresponding version of CMake in which they were added. + +.. _`CMakePresets.cmakeMinimumRequired`: + +``cmakeMinimumRequired`` + An optional object representing the minimum version of CMake + needed to build this project. This object consists of the following + fields: + + .. include:: presets/cmakeMinimumRequired-properties.rst + +.. _`CMakePresets.include`: + +``include`` + .. presets-versionadded:: 4 + + An optional array of strings representing files to include. If the + filenames are not absolute, they are considered relative to the current + file. See `Includes`_ for discussion of the constraints on included + files. + +.. _`CMakePresets.vendor`: + +``vendor`` + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, the keys should be a vendor-specific domain name + followed by a ``/``-separated path. For example, the Example IDE 1.0 + could use ``example.com/ExampleIDE/1.0``. The value of each field can be + anything desired by the vendor, though will typically be a map. + +.. _`CMakePresets.configurePresets`: + +``configurePresets`` + An optional array of `Configure Preset`_ objects. + +.. _`CMakePresets.buildPresets`: + +``buildPresets`` + .. presets-versionadded:: 2 + + An optional array of `Build Preset`_ objects. + +.. _`CMakePresets.testPresets`: + +``testPresets`` + .. presets-versionadded:: 2 + + An optional array of `Test Preset`_ objects. + +.. _`CMakePresets.packagePresets`: + +``packagePresets`` + .. presets-versionadded:: 6 + + An optional array of `Package Preset`_ objects. + +.. _`CMakePresets.workflowPresets`: + +``workflowPresets`` + .. presets-versionadded:: 6 + + An optional array of `Workflow Preset`_ objects.
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json index 6b60967..ff30429 100644 --- a/Help/manual/presets/schema.json +++ b/Help/manual/presets/schema.json
@@ -1,184 +1,440 @@ { "$schema": "http://json-schema.org/draft/2020-12/schema#", "type": "object", - "description": "The presets specify the generator and the build directory, and optionally a list of variables and other arguments to pass to CMake.", "oneOf": [ { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { "version": { - "const": 1, - "description": "A required integer representing the version of the JSON schema." + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 1 }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV1" } + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v1..v2" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { "version": { - "const": 2, - "description": "A required integer representing the version of the JSON schema." + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 2 }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV1" }, - "buildPresets": { "$ref": "#/definitions/buildPresetsV2" }, - "testPresets": { "$ref": "#/definitions/testPresetsV2" } + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v1..v2" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v2" + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v2" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { "version": { - "const": 3, - "description": "A required integer representing the version of the JSON schema." + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 3 }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, - "buildPresets": { "$ref": "#/definitions/buildPresetsV3" }, - "testPresets": { "$ref": "#/definitions/testPresetsV3" } + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v3..v6" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v3" + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v3..v4" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { "version": { - "const": 4, - "description": "A required integer representing the version of the JSON schema." + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 4 }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, - "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, - "testPresets": { "$ref": "#/definitions/testPresetsV3" }, - "include": { "$ref": "#/definitions/include" } + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v3..v6" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v4..v9" + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v3..v4" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { "version": { - "const": 5, - "description": "A required integer representing the version of the JSON schema." + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 5 }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, - "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, - "testPresets": { "$ref": "#/definitions/testPresetsV5" }, - "include": { "$ref": "#/definitions/include" } + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v3..v6" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v4..v9" + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v5" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { "version": { - "const": 6, - "description": "A required integer representing the version of the JSON schema." + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 6 }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, - "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, - "testPresets": { "$ref": "#/definitions/testPresetsV6" }, - "packagePresets": { "$ref": "#/definitions/packagePresetsV6" }, - "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" }, - "include": { "$ref": "#/definitions/include" } + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v3..v6" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v4..v9" + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v6..v9" + }, + "packagePresets": { + "$ref": "#/definitions/packagePresets@v6..v9" + }, + "workflowPresets": { + "$ref": "#/definitions/workflowPresets@v6..v9" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { "version": { - "const": 7, - "description": "A required integer representing the version of the JSON schema." + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 7 }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "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" } + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v7..v9" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v4..v9" + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v6..v9" + }, + "packagePresets": { + "$ref": "#/definitions/packagePresets@v6..v9" + }, + "workflowPresets": { + "$ref": "#/definitions/workflowPresets@v6..v9" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { - "$schema": { "$ref": "#/definitions/$schema" }, - "version": { - "const": 8, - "description": "A required integer representing the version of the JSON schema." + "$schema": { + "$ref": "#/definitions/$schema@v8.." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "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" } + "version": { + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 8 + }, + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v7..v9" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v4..v9" + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v6..v9" + }, + "packagePresets": { + "$ref": "#/definitions/packagePresets@v6..v9" + }, + "workflowPresets": { + "$ref": "#/definitions/workflowPresets@v6..v9" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { - "$schema": { "$ref": "#/definitions/$schema" }, - "version": { - "const": 9, - "description": "A required integer representing the version of the JSON schema." + "$schema": { + "$ref": "#/definitions/$schema@v8.." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, - "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" } + "version": { + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 9 + }, + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v1..v9" + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v7..v9" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v4..v9" + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v6..v9" + }, + "packagePresets": { + "$ref": "#/definitions/packagePresets@v6..v9" + }, + "workflowPresets": { + "$ref": "#/definitions/workflowPresets@v6..v9" + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { - "$schema": { "$ref": "#/definitions/$schema" }, - "$comment": { "$ref": "#/definitions/$comment" }, - "version": { - "const": 10, - "description": "A required integer representing the version of the JSON schema." + "$schema": { + "$ref": "#/definitions/$schema@v8.." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV10" }, - "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV10" }, - "buildPresets": { "$ref": "#/definitions/buildPresetsV10" }, - "testPresets": { "$ref": "#/definitions/testPresetsV10" }, - "packagePresets": { "$ref": "#/definitions/packagePresetsV10" }, - "workflowPresets": { "$ref": "#/definitions/workflowPresetsV10" }, - "include": { "$ref": "#/definitions/include" } + "version": { + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 10 + }, + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v10.." + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v10..v11" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v10.." + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v10" + }, + "packagePresets": { + "$ref": "#/definitions/packagePresets@v10.." + }, + "workflowPresets": { + "$ref": "#/definitions/workflowPresets@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } }, + "required": [ + "version" + ], "additionalProperties": false }, { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", "properties": { - "$schema": { "$ref": "#/definitions/$schema" }, - "$comment": { "$ref": "#/definitions/$comment" }, - "version": { - "const": 11, - "description": "A required integer representing the version of the JSON schema." + "$schema": { + "$ref": "#/definitions/$schema@v8.." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV10" }, - "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV10" }, - "buildPresets": { "$ref": "#/definitions/buildPresetsV10" }, - "testPresets": { "$ref": "#/definitions/testPresetsV11" }, - "packagePresets": { "$ref": "#/definitions/packagePresetsV10" }, - "workflowPresets": { "$ref": "#/definitions/workflowPresetsV10" }, - "include": { "$ref": "#/definitions/include" } + "version": { + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 11 + }, + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v10.." + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v10..v11" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v10.." + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v11" + }, + "packagePresets": { + "$ref": "#/definitions/packagePresets@v10.." + }, + "workflowPresets": { + "$ref": "#/definitions/workflowPresets@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } }, + "required": [ + "version" + ], + "additionalProperties": false + }, + { + "description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.", + "properties": { + "$schema": { + "$ref": "#/definitions/$schema@v8.." + }, + "version": { + "type": "integer", + "description": "A required integer representing the version of the JSON schema.", + "const": 12 + }, + "cmakeMinimumRequired": { + "$ref": "#/definitions/cmakeMinimumRequired@v10.." + }, + "include": { + "$ref": "#/definitions/include@v4.." + }, + "vendor": { + "$ref": "#/definitions/vendor@v1.." + }, + "configurePresets": { + "$ref": "#/definitions/configurePresets@v12" + }, + "buildPresets": { + "$ref": "#/definitions/buildPresets@v10.." + }, + "testPresets": { + "$ref": "#/definitions/testPresets@v12" + }, + "packagePresets": { + "$ref": "#/definitions/packagePresets@v10.." + }, + "workflowPresets": { + "$ref": "#/definitions/workflowPresets@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "version" + ], "additionalProperties": false } ], @@ -186,1945 +442,3616 @@ "version" ], "definitions": { - "$schema": { + "$schema@v8..": { "type": "string", - "description": "The schema against which to verify this document.", - "format": "uri-reference" + "format": "uri-reference", + "description": "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." }, - "$comment": { + "cmakeMinimumRequired@v1..v9": { + "type": "object", + "description": "An optional object representing the minimum version of CMake needed to build this project.", + "unevaluatedProperties": false, + "properties": { + "major": { + "$ref": "#/definitions/cmakeMinimumRequired.major@v1.." + }, + "minor": { + "$ref": "#/definitions/cmakeMinimumRequired.minor@v1.." + }, + "patch": { + "$ref": "#/definitions/cmakeMinimumRequired.patch@v1.." + } + } + }, + "cmakeMinimumRequired@v10..": { + "type": "object", + "description": "An optional object representing the minimum version of CMake needed to build this project.", + "unevaluatedProperties": false, + "properties": { + "major": { + "$ref": "#/definitions/cmakeMinimumRequired.major@v1.." + }, + "minor": { + "$ref": "#/definitions/cmakeMinimumRequired.minor@v1.." + }, + "patch": { + "$ref": "#/definitions/cmakeMinimumRequired.patch@v1.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "cmakeMinimumRequired.major@v1..": { + "type": "integer", + "description": "An optional integer representing the major version." + }, + "cmakeMinimumRequired.minor@v1..": { + "type": "integer", + "description": "An optional integer representing the minor version." + }, + "cmakeMinimumRequired.patch@v1..": { + "type": "integer", + "description": "An optional integer representing the patch version." + }, + "$comment@v10..": { + "$ref": "#/definitions/comment@v10.." + }, + "comment@v10..": { "anyOf": [ { "type": "string", - "description": "The single-line comment" + "description": "A single-line comment." }, { "type": "array", - "description": "The multi-line comment", + "description": "A multi-line comment.", "minItems": 1, "items": { "type": "string", - "description": "One line of the multi-line comment" + "description": "One line of the multi-line comment." } } ] }, - "commonPropertiesAll": { - "properties": { - "name": { - "type": "string", - "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, test, package, or workflow) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.", - "minLength": 1 - }, - "vendor": { - "type": "object", - "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field.", - "properties": {} - }, - "displayName": { - "type": "string", - "description": "An optional string with a human-friendly name of the preset." - }, - "description": { - "type": "string", - "description": "An optional string with a human-friendly description of the preset." - } - } - }, - "configureBuildTestPackagePresetsEnvironmentProperty": { - "properties": { - "environment": { - "type": "object", - "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.", - "properties": {}, - "additionalProperties": { - "anyOf": [ - { - "type": "null", - "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." - }, - { - "type": "string", - "description": "A string representing the value of the variable." - } - ] - }, - "propertyNames": { - "pattern": "^.+$" - } - } - } - }, - "commentAsProperty": { - "properties": { - "$comment": { "$ref": "#/definitions/$comment" } - } - }, - "cmakeMinimumRequiredObject": { - "properties": { - "major": { - "type": "integer", - "description": "An optional integer representing the major version." - }, - "minor": { - "type": "integer", - "description": "An optional integer representing the minor version." - }, - "patch": { - "type": "integer", - "description": "An optional integer representing the patch version." - } - } - }, - "cmakeMinimumRequiredV10": { - "type": "object", - "description": "An optional object representing the minimum version of CMake needed to build this project. Available in version 10 and higher.", - "allOf": [ - { "$ref": "#/definitions/cmakeMinimumRequiredObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - "cmakeMinimumRequiredV1": { - "type": "object", - "description": "An optional object representing the minimum version of CMake needed to build this project. Available in version 1 and higher.", - "allOf": [ - { "$ref": "#/definitions/cmakeMinimumRequiredObject" } - ], - "unevaluatedProperties": false - }, - "vendor": { - "type": "object", - "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, the keys should be a vendor-specific domain name followed by a /-separated path. For example, the Example IDE 1.0 could use example.com/ExampleIDE/1.0. The value of each field can be anything desired by the vendor, though will typically be a map.", - "properties": {} - }, - "configurePresetsArchitectureAsStringV1": { - "type": "string", - "description": "An optional string representing the platform for generators that support it." - }, - "configurePresetsArchitectureAsObjectV1": { - "properties": { - "value": { - "type": "string", - "description": "An optional string representing the value." - }, - "strategy": { - "type": "string", - "description": "An optional string telling CMake how to handle the field. Valid values are: \"set\" Set the respective value. This will result in an error for generators that do not support the respective field. \"external\" Do not set the value, even if the generator supports it. This is useful if, for example, a preset uses the Ninja generator, and an IDE knows how to set up the Visual C++ environment from the architecture and toolset fields. In that case, CMake will ignore the field, but the IDE can use them to set up the environment before invoking CMake.", - "enum": [ - "set", - "external" - ] - } - } - }, - "configurePresetsArchitectureV10": { - "anyOf": [ - { "$ref": "#/definitions/configurePresetsArchitectureAsStringV1" }, - { - "type": "object", - "description": "An optional object representing the platform for generators that support it. Available in version 10 and higher.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsArchitectureAsObjectV1" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - ] - }, - "configurePresetsArchitectureV1": { - "anyOf": [ - { "$ref": "#/definitions/configurePresetsArchitectureAsStringV1" }, - { - "type": "object", - "description": "An optional object representing the platform for generators that support it. Available in version 1 and higher.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsArchitectureAsObjectV1" } - ], - "unevaluatedProperties": false - } - ] - }, - "configurePresetsToolsetAsStringV1": { - "type": "string", - "description": "An optional string representing the toolset for generators that support it." - }, - "configurePresetsToolsetAsObjectV1": { - "properties": { - "value": { - "type": "string", - "description": "An optional string representing the value." - }, - "strategy": { - "type": "string", - "description": "An optional string telling CMake how to handle the field. Valid values are: \"set\" Set the respective value. This will result in an error for generators that do not support the respective field. \"external\" Do not set the value, even if the generator supports it. This is useful if, for example, a preset uses the Ninja generator, and an IDE knows how to set up the Visual C++ environment from the architecture and toolset fields. In that case, CMake will ignore the field, but the IDE can use them to set up the environment before invoking CMake.", - "enum": [ - "set", - "external" - ] - } - } - }, - "configurePresetsToolsetV10": { - "anyOf": [ - { "$ref": "#/definitions/configurePresetsToolsetAsStringV1" }, - { - "type": "object", - "description": "An optional object representing the toolset for generators that support it. Available in version 10 and higher.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsToolsetAsObjectV1" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - ] - }, - "configurePresetsToolsetV1": { - "anyOf": [ - { "$ref": "#/definitions/configurePresetsToolsetAsStringV1" }, - { - "type": "object", - "description": "An optional object representing the toolset for generators that support it. Available in version 1 and higher.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsToolsetAsObjectV1" } - ], - "unevaluatedProperties": false - } - ] - }, - "configurePresetsCacheVariablesAdditionalPropertiesAsNull": { - "type": "null", - "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." - }, - "configurePresetsCacheVariablesAdditionalPropertiesAsBoolean": { - "type": "boolean", - "description": "A boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"." - }, - "configurePresetsCacheVariablesAdditionalPropertiesAsString": { - "type": "string", - "description": "A string representing the value of the variable (which supports macro expansion)." - }, - "configurePresetsCacheVariablesAdditionalPropertiesAsObject": { - "properties": { - "type": { - "type": "string", - "description": "An optional string representing the type of the variable. It should be BOOL, FILEPATH, PATH, STRING, or INTERNAL.", - "enum": [ - "BOOL", "FILEPATH", "PATH", "STRING", "INTERNAL" - ] - }, - "value": { - "anyOf": [ - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsBoolean" }, - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsString" } - ] - } - }, - "required": [ - "value" - ] - }, - "configurePresetsCacheVariablesAdditionalPropertiesV10": { - "anyOf": [ - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsNull" }, - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsBoolean" }, - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsString" }, - { - "type": "object", - "description": "An optional object representing the cache variables for generators that support it. Available in version 10 and higher.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - ] - }, - "configurePresetsCacheVariablesAdditionalPropertiesV1": { - "anyOf": [ - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsNull" }, - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsBoolean" }, - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsString" }, - { - "type": "object", - "description": "An optional object representing the cache variables for generators that support it. Available in version 1 and higher.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsObject" } - ], - "unevaluatedProperties": false - } - ] - }, - "configurePresetsTraceV10": { - "type": "object", - "description": "An optional object specifying trace options.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsTraceObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - "configurePresetsTraceV7": { - "type": "object", - "description": "An optional object specifying trace options.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsTraceObject" } - ], - "unevaluatedProperties": false - }, - "configurePresetsTraceObject": { - "properties": { - "mode": { - "type": "string", - "description": "An optional string that specifies the trace mode.", - "enum": [ - "on", "off", "expand" - ] - }, - "format": { - "type": "string", - "description": "An optional string that specifies the trace output format.", - "enum": [ - "human", "json-v1" - ] - }, - "source": { - "anyOf": [ - { - "type": "string", - "description": "An optional string representing the path to one source file to be traced.", - "minLength": 1 - }, - { - "type": "array", - "description": "An optional array of strings representing the paths to source files to be traced.", - "items": { - "type": "string", - "description": "A string representing the path to one source file to be traced.", - "minLength": 1 - } - } - ] - }, - "redirect": { - "type": "string", - "description": "An optional string specifying a path to a trace output file." - } - } - }, - "configurePresetsDebugV10": { - "type": "object", - "description": "An optional object specifying debug options.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsDebugObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - "configurePresetsDebugV1": { - "type": "object", - "description": "An optional object specifying debug options.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsDebugObject" } - ], - "unevaluatedProperties": false - }, - "configurePresetsErrorsV10": { - "type": "object", - "description": "An optional object specifying errors.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsErrorsObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - "configurePresetsErrorsV1": { - "type": "object", - "description": "An optional object specifying errors.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsErrorsObject" } - ], - "unevaluatedProperties": false - }, - "configurePresetsWarningsV10": { - "type": "object", - "description": "An optional object specifying warnings.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsWarningsObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - "configurePresetsWarningsV1": { - "type": "object", - "description": "An optional object specifying warnings.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsWarningsObject" } - ], - "unevaluatedProperties": false - }, - "configurePresetsDebugObject": { - "properties": { - "output": { - "type": "boolean", - "description": "An optional boolean. Setting this to true is equivalent to passing --debug-output on the command line." - }, - "tryCompile": { - "type": "boolean", - "description": "An optional boolean. Setting this to true is equivalent to passing --debug-trycompile on the command line." - }, - "find": { - "type": "boolean", - "description": "An optional boolean. Setting this to true is equivalent to passing --debug-find on the command line." - } - } - }, - "configurePresetsErrorsObject": { - "properties": { - "dev": { - "type": "boolean", - "description": "An optional boolean. Equivalent to passing -Werror=dev or -Wno-error=dev on the command line. This may not be set to true if warnings.dev is set to false." - }, - "deprecated": { - "type": "boolean", - "description": "An optional boolean. Equivalent to passing -Werror=deprecated or -Wno-error=deprecated on the command line. This may not be set to true if warnings.deprecated is set to false." - } - } - }, - "configurePresetsWarningsObject": { - "properties": { - "dev": { - "type": "boolean", - "description": "An optional boolean. Equivalent to passing -Wdev or -Wno-dev on the command line. This may not be set to false if errors.dev is set to true." - }, - "deprecated": { - "type": "boolean", - "description": "An optional boolean. Equivalent to passing -Wdeprecated or -Wno-deprecated on the command line. This may not be set to false if errors.deprecated is set to true." - }, - "uninitialized": { - "type": "boolean", - "description": "An optional boolean. Setting this to true is equivalent to passing --warn-uninitialized on the command line." - }, - "unusedCli": { - "type": "boolean", - "description": "An optional boolean. Setting this to false is equivalent to passing --no-warn-unused-cli on the command line." - }, - "systemVars": { - "type": "boolean", - "description": "An optional boolean. Setting this to true is equivalent to passing --check-system-vars on the command line." - } - } - }, - "configurePresetsItemsV10": { - "properties": { - "graphviz": { - "type": "string", - "description": "An optional string specifying the path to graphviz dot file. Available in version 10 and higher." - } - } - }, - "configurePresetsItemsV3": { - "properties": { - "toolchainFile": { - "type": "string", - "description": "An optional string representing the path to the toolchain file. This field supports macro expansion. If a relative path is specified, it is calculated relative to the build directory, and if not found, relative to the source directory." - }, - "installDir": { - "type": "string", - "description": "An optional string representing the path to the installation directory, which will be used as the CMAKE_INSTALL_PREFIX variable. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory." - } - } - }, - "configurePresetsItemsV1": { - "allOf": [ - { "$ref": "#/definitions/commonPropertiesAll" }, - { "$ref": "#/definitions/configureBuildTestPackagePresetsEnvironmentProperty" }, - { - "properties": { - "hidden": { - "type": "boolean", - "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset= argument, will not show up in the CMake GUI, and does not have to have a valid generator or binaryDir, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field." - }, - "inherits": { - "anyOf": [ - { - "type": "string", - "description": "An optional string representing the name of the preset to inherit from.", - "minLength": 1 - }, - { - "type": "array", - "description": "An optional array of strings representing the names of presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.", - "items": { - "type": "string", - "description": "An optional string representing the name of the preset to inherit from.", - "minLength": 1 - } - } - ] - }, - "cmakeExecutable": { - "type": "string", - "description": "An optional string representing the path to the CMake executable to use for this preset. This is reserved for use by IDEs, and is not used by CMake itself. IDEs that use this field should expand any macros in it." - } - } - } - ], - "required": [ - "name" - ] - }, - "configurePresetsAdditionalPropertiesV10": { - "properties": { - "toolset": { "$ref": "#/definitions/configurePresetsToolsetV10" }, - "architecture": { "$ref": "#/definitions/configurePresetsArchitectureV10" }, - "cacheVariables": { - "type": "object", - "description": "An optional map of cache variables. The key is the variable name (which must not be an empty string). Cache variables are inherited through the inherits field, and the preset's variables will be the union of its own cacheVariables and the cacheVariables from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied.", - "properties": {}, - "propertyNames": { - "pattern": "^.+$" - }, - "additionalProperties": { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesV10" } - }, - "binaryDir": { - "type": "string", - "description": "An optional string representing the path to the output binary directory. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory. If binaryDir is not specified, the path is calculated using regular methods." - }, - "generator": { - "type": "string", - "description": "An optional string representing the generator to use for the preset. If generator is not specified, the normal generator discovery procedure is used. Note that for Visual Studio generators, unlike in the command line -G argument, you cannot include the platform name in the generator name. Use the architecture field instead." - }, - "condition": { "$ref": "#/definitions/topConditionV10" }, - "warnings": { "$ref": "#/definitions/configurePresetsWarningsV10" }, - "errors": { "$ref": "#/definitions/configurePresetsErrorsV10" }, - "debug": { "$ref": "#/definitions/configurePresetsDebugV10" }, - "trace": { "$ref": "#/definitions/configurePresetsTraceV10" } - } - }, - "configurePresetsAdditionalPropertiesV7": { - "allOf": [ - { "$ref": "#/definitions/configurePresetsAdditionalPropertiesV3" }, - { - "properties": { - "trace": { "$ref": "#/definitions/configurePresetsTraceV7" } - } - } - ] - }, - "configurePresetsAdditionalPropertiesV3": { - "allOf": [ - { "$ref": "#/definitions/configurePresetsAdditionalPropertiesV1" }, - { - "properties": { - "condition": { "$ref": "#/definitions/topConditionV3" }, - "binaryDir": { - "type": "string", - "description": "An optional string representing the path to the output binary directory. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory. If binaryDir is not specified, the path is calculated using regular methods." - }, - "generator": { - "type": "string", - "description": "An optional string representing the generator to use for the preset. If generator is not specified, the normal generator discovery procedure is used. Note that for Visual Studio generators, unlike in the command line -G argument, you cannot include the platform name in the generator name. Use the architecture field instead." - } - } - } - ] - }, - "configurePresetsAdditionalPropertiesV1": { - "properties": { - "toolset": { "$ref": "#/definitions/configurePresetsToolsetV1" }, - "architecture": { "$ref": "#/definitions/configurePresetsArchitectureV1" }, - "cacheVariables": { - "type": "object", - "description": "An optional map of cache variables. The key is the variable name (which must not be an empty string). Cache variables are inherited through the inherits field, and the preset's variables will be the union of its own cacheVariables and the cacheVariables from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied.", - "properties": {}, - "propertyNames": { - "pattern": "^.+$" - }, - "additionalProperties": { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesV1" } - }, - "binaryDir": { - "type": "string", - "description": "An optional string representing the path to the output binary directory. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory. If binaryDir is not specified, it must be inherited from the inherits preset (unless this preset is hidden)." - }, - "generator": { - "type": "string", - "description": "An optional string representing the generator to use for the preset. If generator is not specified, it must be inherited from the inherits preset (unless this preset is hidden). Note that for Visual Studio generators, unlike in the command line -G argument, you cannot include the platform name in the generator name. Use the architecture field instead." - }, - "warnings": { "$ref": "#/definitions/configurePresetsWarningsV1" }, - "errors": { "$ref": "#/definitions/configurePresetsErrorsV1" }, - "debug": { "$ref": "#/definitions/configurePresetsDebugV1" } - } - }, - "configurePresetsV10": { - "type": "array", - "description": "An optional array of configure preset objects. Available in version 10 and higher.", - "items": { - "type": "object", - "description": "A configure preset object.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsItemsV10" }, - { "$ref": "#/definitions/configurePresetsItemsV3" }, - { "$ref": "#/definitions/configurePresetsItemsV1" }, - { "$ref": "#/definitions/configurePresetsAdditionalPropertiesV10" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - }, - "configurePresetsV7": { - "type": "array", - "description": "An optional array of configure preset objects. Available in version 7 and higher.", - "items": { - "type": "object", - "description": "A configure preset object.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsItemsV3" }, - { "$ref": "#/definitions/configurePresetsItemsV1" }, - { "$ref": "#/definitions/configurePresetsAdditionalPropertiesV7" } - ], - "unevaluatedProperties": false - } - }, - "configurePresetsV3": { - "type": "array", - "description": "An optional array of configure preset objects. Available in version 3 and higher.", - "items": { - "type": "object", - "description": "A configure preset object.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsItemsV3" }, - { "$ref": "#/definitions/configurePresetsItemsV1" }, - { "$ref": "#/definitions/configurePresetsAdditionalPropertiesV3" } - ], - "unevaluatedProperties": false - } - }, - "configurePresetsV1": { - "type": "array", - "description": "An optional array of configure preset objects. Available in version 1 and higher.", - "items": { - "type": "object", - "description": "A configure preset object.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsItemsV1" }, - { "$ref": "#/definitions/configurePresetsAdditionalPropertiesV1" } - ], - "unevaluatedProperties": false - } - }, - "buildPresetsItemsV4": { - "properties": { - "resolvePackageReferences": { - "type": "string", - "description": "An optional string specifying the package resolve behavior. Valid values are \"on\" (packages are resolved prior to the build), \"off\" (packages are not resolved prior to the build), and \"only\" (packages are resolved, but no build will be performed).", - "enum": [ - "on", "off", "only" - ] - } - } - }, - "buildPresetsItemsV2": { - "allOf": [ - { "$ref": "#/definitions/commonPropertiesAll" }, - { "$ref": "#/definitions/configureBuildTestPackagePresetsEnvironmentProperty" }, - { - "properties": { - "hidden": { - "type": "boolean", - "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field." - }, - "inherits": { - "anyOf": [ - { - "type": "string", - "description": "An optional string representing the name of the build preset to inherit from.", - "minLength": 1 - }, - { - "type": "array", - "description": "An optional array of strings representing the names of build presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.", - "items": { - "type": "string", - "description": "An optional string representing the name of the preset to inherit from.", - "minLength": 1 - } - } - ] - }, - "configurePreset": { - "type": "string", - "description": "An optional string specifying the name of a configure preset to associate with this build preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build tree directory is inferred from the configure preset.", - "minLength": 1 - }, - "inheritConfigureEnvironment": { - "type": "boolean", - "description": "An optional boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited build preset environments, but before environment variables explicitly specified in this build preset." - }, - "jobs": { - "type": "integer", - "description": "An optional non-negative integer. Equivalent to passing --parallel or -j on the command line.", - "minimum": 0 - }, - "targets": { - "anyOf": [ - { - "type": "string", - "description": "An optional string. Equivalent to passing --target or -t on the command line. Vendors may ignore the targets property or hide build presets that explicitly specify targets." - }, - { - "type": "array", - "description": "An optional array of strings. Equivalent to passing --target or -t on the command line. Vendors may ignore the targets property or hide build presets that explicitly specify targets.", - "items": { - "type": "string", - "description": "An optional string. Equivalent to passing --target or -t on the command line. Vendors may ignore the targets property or hide build presets that explicitly specify targets." - } - } - ] - }, - "configuration": { - "type": "string", - "description": "An optional string. Equivalent to passing --config on the command line." - }, - "cleanFirst": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --clean-first on the command line." - }, - "verbose": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --verbose on the command line." - }, - "nativeToolOptions": { - "type": "array", - "description": "An optional array of strings. Equivalent to passing options after -- on the command line.", - "items": { - "type": "string", - "description": "An optional string representing an option to pass after -- on the command line." - } - } - } - } - ], - "required": [ - "name" - ] - }, - "buildPresetsAdditionalPropertiesV10": { - "properties": { - "condition": { "$ref": "#/definitions/topConditionV10" } - } - }, - "buildPresetsAdditionalPropertiesV3": { - "properties": { - "condition": { "$ref": "#/definitions/topConditionV3" } - } - }, - "buildPresetsV10": { - "type": "array", - "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 10 and higher.", - "items": { - "type": "object", - "description": "A build preset object.", - "allOf": [ - { "$ref": "#/definitions/buildPresetsItemsV4" }, - { "$ref": "#/definitions/buildPresetsItemsV2" }, - { "$ref": "#/definitions/buildPresetsAdditionalPropertiesV10" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - }, - "buildPresetsV4": { - "type": "array", - "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 4 and higher.", - "items": { - "type": "object", - "description": "A build preset object.", - "allOf": [ - { "$ref": "#/definitions/buildPresetsItemsV4" }, - { "$ref": "#/definitions/buildPresetsItemsV2" }, - { "$ref": "#/definitions/buildPresetsAdditionalPropertiesV3" } - ], - "unevaluatedProperties": false - } - }, - "buildPresetsV3": { - "type": "array", - "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 3 and higher.", - "items": { - "type": "object", - "description": "A build preset object.", - "allOf": [ - { "$ref": "#/definitions/buildPresetsItemsV2" }, - { "$ref": "#/definitions/buildPresetsAdditionalPropertiesV3" } - ], - "unevaluatedProperties": false - } - }, - "buildPresetsV2": { - "type": "array", - "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 2 and higher.", - "items": { - "type": "object", - "description": "A build preset object.", - "allOf": [ - { "$ref": "#/definitions/buildPresetsItemsV2" } - ], - "unevaluatedProperties": false - } - }, - "testPresetsExecutionJobsV11": { - "oneOf": [ - { - "type": "integer", - "description": "An optional non-negative integer. Equivalent to passing --parallel or -j on the command line.", - "minimum": 0 - }, - { - "type": "string", - "description": "An optional string. Equivalent to passing --parallel or -j on the command line with the number of jobs omitted.", - "minLength": 0, - "maxLength": 0 - } - ] - }, - "testPresetsExecutionJobsV2": { - "type": "integer", - "description": "An optional non-negative integer. Equivalent to passing --parallel or -j on the command line.", - "minimum": 0 - }, - "testPresetsExecutionRepeatV10": { - "type": "object", - "description": "An optional object specifying how to repeat tests. Equivalent to passing --repeat on the command line.", - "allOf": [ - { "$ref": "#/definitions/testPresetsExecutionRepeatObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - "testPresetsExecutionRepeatV2": { - "type": "object", - "description": "An optional object specifying how to repeat tests. Equivalent to passing --repeat on the command line.", - "allOf": [ - { "$ref": "#/definitions/testPresetsExecutionRepeatObject" } - ], - "unevaluatedProperties": false - }, - "testPresetsExecutionRepeatObject": { - "properties": { - "mode": { - "type": "string", - "description": "A required string. Must be one of the following values: \"until-fail\", \"until-pass\", or \"after-timeout\".", - "enum": [ - "until-fail", "until-pass", "after-timeout" - ] - }, - "count": { - "type": "integer", - "description": "A required integer." - } - }, - "required": [ - "mode", "count" - ] - }, - "testPresetsFilterIncludeIndexAsStringV2": { - "type": "string", - "description": "An optional string specifying a file with the command line syntax for --tests-information. Available in version 2 and higher." - }, - "testPresetsFilterIncludeIndexAsObjectV10": { - "type": "object", - "description": "An optional object specifying tests to include by test index. Available in version 10 and higher.", - "properties": { - "$comment": { "$ref": "#/definitions/$comment" } - } - }, - "testPresetsFilterIncludeIndexAsObjectV2": { - "type": "object", - "description": "An optional object specifying tests to include by test index. Available in version 2 and higher.", - "properties": { - "start": { - "type": "integer", - "description": "An optional integer specifying a test index to start testing at." - }, - "end": { - "type": "integer", - "description": "An optional integer specifying a test index to stop testing at." - }, - "stride": { - "type": "integer", - "description": "An optional integer specifying the increment." - }, - "specificTests": { - "type": "array", - "description": "An optional array of integers specifying specific test indices to run.", - "items": { - "type": "integer", - "description": "An integer specifying the test to run by index." - } - } - } - }, - "testPresetsFilterIncludeIndexV10": { - "anyOf": [ - { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsStringV2" }, - { - "type": "object", - "description": "An optional object specifying test preset filters. Available in version 10 and higher.", - "allOf": [ - { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsObjectV2" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - ] - }, - "testPresetsFilterIncludeIndexV2": { - "anyOf": [ - { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsStringV2" }, - { - "type": "object", - "description": "An optional object specifying test preset filters. Available in version 2 and higher.", - "allOf": [ - { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsObjectV2" } - ], - "unevaluatedProperties": false - } - ] - }, - "testPresetsFilterIncludeV10": { - "type": "object", - "description": "An optional object specifying which tests to include.", - "allOf": [ - { "$ref": "#/definitions/testPresetsFilterIncludeObject" }, - { "$ref": "#/definitions/commentAsProperty" }, - { - "properties": { - "index": { "$ref": "#/definitions/testPresetsFilterIncludeIndexV10" } - } - } - ] - }, - "testPresetsFilterIncludeV2": { - "type": "object", - "description": "An optional object specifying which tests to include.", - "allOf": [ - { "$ref": "#/definitions/testPresetsFilterIncludeObject" }, - { - "properties": { - "index": { "$ref": "#/definitions/testPresetsFilterIncludeIndexV2" } - } - } - ] - }, - "testPresetsFilterIncludeObject": { - "properties": { - "name": { - "type": "string", - "description": "An optional string specifying a regex for test names. Equivalent to passing --tests-regex on the command line." - }, - "label": { - "type": "string", - "description": "An optional string specifying a regex for test labels. Equivalent to passing --label-regex on the command line." - }, - "useUnion": { - "type": "boolean", - "description": "An optional boolean. Equivalent to passing --union on the command line." - } - } - }, - "testPresetsFilterExcludeV10": { - "type": "object", - "description": "An optional object specifying which tests to exclude.", - "allOf": [ - { "$ref": "#/definitions/testPresetsFilterExcludeObject" }, - { "$ref": "#/definitions/commentAsProperty" }, - { - "properties": { - "fixtures": { - "allOf": [ - { "$ref": "#/definitions/testPresetsFilterExcludeFixturesV2" }, - { "$ref": "#/definitions/commentAsProperty" } - ] - } - } - } - ] - }, - "testPresetsFilterExcludeV2": { - "type": "object", - "description": "An optional object specifying which tests to exclude.", - "allOf": [ - { "$ref": "#/definitions/testPresetsFilterExcludeObject" }, - { - "properties": { - "fixtures": { "$ref": "#/definitions/testPresetsFilterExcludeFixturesV2" } - } - } - ] - }, - "testPresetsFilterExcludeFixturesV2": { - "type": "object", - "description": "An optional object specifying which fixtures to exclude from adding tests.", - "properties": { - "any": { - "type": "string", - "description": "An optional string specifying a regex for text fixtures to exclude from adding any tests. Equivalent to --fixture-exclude-any on the command line." - }, - "setup": { - "type": "string", - "description": "An optional string specifying a regex for text fixtures to exclude from adding setup tests. Equivalent to --fixture-exclude-setup on the command line." - }, - "cleanup": { - "type": "string", - "description": "An optional string specifying a regex for text fixtures to exclude from adding cleanup tests. Equivalent to --fixture-exclude-cleanup on the command line." - } - } - }, - "testPresetsFilterExcludeObject": { - "properties": { - "name": { - "type": "string", - "description": "An optional string specifying a regex for test names. Equivalent to passing --exclude-regex on the command line." - }, - "label": { - "type": "string", - "description": "An optional string specifying a regex for test labels. Equivalent to passing --label-exclude on the command line." - } - } - }, - "testPresetsExecutionV11": { - "type": "object", - "description": "An optional object specifying options for test execution.", - "allOf": [ - { "$ref": "#/definitions/testPresetsExecutionObject" }, - { "$ref": "#/definitions/commentAsProperty" }, - { - "properties": { - "repeat": { "$ref": "#/definitions/testPresetsExecutionRepeatV10" }, - "jobs": { "$ref": "#/definitions/testPresetsExecutionJobsV11" } - } - } - ], - "unevaluatedProperties": false - }, - "testPresetsExecutionV10": { - "type": "object", - "description": "An optional object specifying options for test execution.", - "allOf": [ - { "$ref": "#/definitions/testPresetsExecutionObject" }, - { "$ref": "#/definitions/commentAsProperty" }, - { - "properties": { - "repeat": { "$ref": "#/definitions/testPresetsExecutionRepeatV10" }, - "jobs": { "$ref": "#/definitions/testPresetsExecutionJobsV2" } - } - } - ], - "unevaluatedProperties": false - }, - "testPresetsExecutionV2": { - "type": "object", - "description": "An optional object specifying options for test execution.", - "allOf": [ - { "$ref": "#/definitions/testPresetsExecutionObject" }, - { - "properties": { - "repeat": { "$ref": "#/definitions/testPresetsExecutionRepeatV2" }, - "jobs": { "$ref": "#/definitions/testPresetsExecutionJobsV2" } - } - } - ], - "unevaluatedProperties": false - }, - "testPresetsExecutionObject": { - "properties": { - "stopOnFailure": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --stop-on-failure on the command line." - }, - "enableFailover": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing -F on the command line." - }, - "resourceSpecFile": { - "type": "string", - "description": "An optional string. Equivalent to passing --resource-spec-file on the command line." - }, - "testLoad": { - "type": "integer", - "description": "An optional integer. Equivalent to passing --test-load on the command line." - }, - "showOnly": { - "type": "string", - "description": "An optional string. Equivalent to passing --show-only on the command line. Value must be \"human\" or \"json-v1\".", - "enum": [ - "human", "json-v1" - ] - }, - "interactiveDebugging": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --interactive-debug-mode 1 on the command line. If false, equivalent to passing --interactive-debug-mode 0 on the command line." - }, - "scheduleRandom": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --schedule-random on the command line." - }, - "timeout": { - "type": "integer", - "description": "An optional integer. Equivalent to passing --timeout on the command line." - }, - "noTestsAction": { - "type": "string", - "description": "An optional string specifying the behavior if no tests are found. Must be one of the following values: \"default\" (equivalent to not passing any value on the command line), \"error\" (equivalent to passing --no-tests=error on the command line), or \"ignore\" (equivalent to passing --no-tests-ignore on the command line).", - "enum": [ - "default", "error", "ignore" - ] - } - } - }, - "testPresetsOutputJUnitFile": { - "type": "string", - "description": "An optional string specifying a path to a JUnit file. Equivalent to passing --output-junit on the command line." - }, - "testPresetsTestOutputTruncation": { - "type": "string", - "description": "An optional string specifying the test output truncation mode. Equivalent to passing --test-output-truncation on the command line. Must be one of the following values: \"tail\", \"middle\", or \"head\".", - "enum": [ - "tail", "middle", "head" - ] - }, - "testPresetsOutputV10": { - "type": "object", - "description": "An optional object specifying output options.", - "allOf": [ - { "$ref": "#/definitions/testPresetsOutputObject" }, - { - "properties": { - "outputJUnitFile": { "$ref": "#/definitions/testPresetsOutputJUnitFile" }, - "testOutputTruncation": { "$ref": "#/definitions/testPresetsTestOutputTruncation" }, - "$comment": { "$ref": "#/definitions/$comment" } - } - } - ], - "unevaluatedProperties": false - }, - "testPresetsOutputV6": { - "type": "object", - "description": "An optional object specifying output options.", - "allOf": [ - { "$ref": "#/definitions/testPresetsOutputObject" }, - { - "properties": { - "outputJUnitFile": { "$ref": "#/definitions/testPresetsOutputJUnitFile" }, - "testOutputTruncation": { "$ref": "#/definitions/testPresetsTestOutputTruncation" } - } - } - ], - "unevaluatedProperties": false - }, - "testPresetsOutputV5": { - "type": "object", - "description": "An optional object specifying output options.", - "allOf": [ - { "$ref": "#/definitions/testPresetsOutputObject" }, - { - "properties": { - "testOutputTruncation": { "$ref": "#/definitions/testPresetsTestOutputTruncation" } - } - } - ], - "unevaluatedProperties": false - }, - "testPresetsOutputV2": { - "type": "object", - "description": "An optional object specifying output options.", - "allOf": [ - { "$ref": "#/definitions/testPresetsOutputObject" } - ], - "unevaluatedProperties": false - }, - "testPresetsOutputObject": { - "properties": { - "shortProgress": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --progress on the command line." - }, - "verbosity": { - "type": "string", - "description": "An optional string specifying verbosity level. Valid values are \"default\" (equivalent to passing no verbosity flags on the command line), \"verbose\" (equivalent to passing --verbose on the command line), and \"extra\" (equivalent to passing --extra-verbose on the command line).", - "enum": [ - "default", "verbose", "extra" - ] - }, - "debug": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --debug on the command line." - }, - "outputOnFailure": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --output-on-failure on the command line." - }, - "quiet": { - "type": "boolean", - "description": "An optional boolean. If true, equivalent to passing --quiet on the command line." - }, - "outputLogFile": { - "type": "string", - "description": "An optional string specifying a path to a log file. Equivalent to passing --output-log on the command line." - }, - "labelSummary": { - "type": "boolean", - "description": "An optional boolean. If false, equivalent to passing --no-label-summary on the command line." - }, - "subprojectSummary": { - "type": "boolean", - "description": "An optional boolean. If false, equivalent to passing --no-subproject-summary on the command line." - }, - "maxPassedTestOutputSize": { - "type": "integer", - "description": "An optional integer specifying the maximum output for passed tests in bytes. Equivalent to passing --test-output-size-passed on the command line." - }, - "maxFailedTestOutputSize": { - "type": "integer", - "description": "An optional integer specifying the maximum output for failed tests in bytes. Equivalent to passing --test-output-size-failed on the command line." - }, - "maxTestNameWidth": { - "type": "integer", - "description": "An optional integer specifying the maximum width of a test name to output. Equivalent to passing --max-width on the command line." - } - } - }, - "testPresetsAdditionalPropertiesV11": { - "properties": { - "execution": { "$ref": "#/definitions/testPresetsExecutionV11" }, - "filter": { - "type": "object", - "description": "An optional object specifying how to filter the tests to run.", - "properties": { - "include": { "$ref": "#/definitions/testPresetsFilterIncludeV10" }, - "exclude": { "$ref": "#/definitions/testPresetsFilterExcludeV10" }, - "$comment": { "$ref": "#/definitions/$comment" } - } - }, - "output": { "$ref": "#/definitions/testPresetsOutputV6" }, - "condition": { "$ref": "#/definitions/topConditionV10" } - } - }, - "testPresetsAdditionalPropertiesV10": { - "properties": { - "execution": { "$ref": "#/definitions/testPresetsExecutionV10" }, - "filter": { - "type": "object", - "description": "An optional object specifying how to filter the tests to run.", - "properties": { - "include": { "$ref": "#/definitions/testPresetsFilterIncludeV10" }, - "exclude": { "$ref": "#/definitions/testPresetsFilterExcludeV10" }, - "$comment": { "$ref": "#/definitions/$comment" } - } - }, - "output": { "$ref": "#/definitions/testPresetsOutputV6" }, - "condition": { "$ref": "#/definitions/topConditionV10" } - } - }, - "testPresetsAdditionalPropertiesV6": { - "properties": { - "execution": { "$ref": "#/definitions/testPresetsExecutionV2" }, - "filter": { - "type": "object", - "description": "An optional object specifying how to filter the tests to run.", - "properties": { - "include": { "$ref": "#/definitions/testPresetsFilterIncludeV2" }, - "exclude": { "$ref": "#/definitions/testPresetsFilterExcludeV2" } - } - }, - "output": { "$ref": "#/definitions/testPresetsOutputV6" }, - "condition": { "$ref": "#/definitions/topConditionV3" } - } - }, - "testPresetsAdditionalPropertiesV5": { - "properties": { - "execution": { "$ref": "#/definitions/testPresetsExecutionV2" }, - "filter": { - "type": "object", - "description": "An optional object specifying how to filter the tests to run.", - "properties": { - "include": { "$ref": "#/definitions/testPresetsFilterIncludeV2" }, - "exclude": { "$ref": "#/definitions/testPresetsFilterExcludeV2" } - } - }, - "output": { "$ref": "#/definitions/testPresetsOutputV5" }, - "condition": { "$ref": "#/definitions/topConditionV3" } - } - }, - "testPresetsAdditionalPropertiesV3": { - "allOf": [ - { "$ref": "#/definitions/testPresetsAdditionalPropertiesV2" }, - { - "properties": { - "condition": { "$ref": "#/definitions/topConditionV3" } - } - } - ] - }, - "testPresetsAdditionalPropertiesV2": { - "properties": { - "execution": { "$ref": "#/definitions/testPresetsExecutionV2" }, - "filter": { - "type": "object", - "description": "An optional object specifying how to filter the tests to run.", - "properties": { - "include": { "$ref": "#/definitions/testPresetsFilterIncludeV2" }, - "exclude": { "$ref": "#/definitions/testPresetsFilterExcludeV2" } - } - }, - "output": { "$ref": "#/definitions/testPresetsOutputV2" } - } - }, - "testPresetsItemsV2": { - "allOf": [ - { "$ref": "#/definitions/commonPropertiesAll" }, - { "$ref": "#/definitions/configureBuildTestPackagePresetsEnvironmentProperty" }, - { - "properties": { - "hidden": { - "type": "boolean", - "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field." - }, - "inherits": { - "anyOf": [ - { - "type": "string", - "description": "An optional string representing the name of the test preset to inherit from.", - "minLength": 1 - }, - { - "type": "array", - "description": "An optional array of strings representing the names of test presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.", - "items": { - "type": "string", - "description": "An optional string representing the name of the preset to inherit from.", - "minLength": 1 - } - } - ] - }, - "configurePreset": { - "type": "string", - "description": "An optional string specifying the name of a configure preset to associate with this test preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build tree directory is inferred from the configure preset.", - "minLength": 1 - }, - "inheritConfigureEnvironment": { - "type": "boolean", - "description": "An optional boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited test preset environments, but before environment variables explicitly specified in this test preset." - }, - "configuration": { - "type": "string", - "description": "An optional string. Equivalent to passing --build-config on the command line." - }, - "overwriteConfigurationFile": { - "type": "array", - "description": "An optional array of configuration options to overwrite options specified in the CTest configuration file. Equivalent to passing ``--overwrite`` for each value in the array.", - "items": { - "type": "string", - "description": "An option written as a key-value pair in the form \"key=value\"." - } - } - } - } - ], - "required": [ - "name" - ] - }, - "testPresetsV11": { - "type": "array", - "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 11 and higher.", - "items": { - "type": "object", - "description": "A test preset object", - "allOf": [ - { "$ref": "#/definitions/testPresetsItemsV2" }, - { "$ref": "#/definitions/testPresetsAdditionalPropertiesV11" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - }, - "testPresetsV10": { - "type": "array", - "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 10 and higher.", - "items": { - "type": "object", - "description": "A test preset object", - "allOf": [ - { "$ref": "#/definitions/testPresetsItemsV2" }, - { "$ref": "#/definitions/testPresetsAdditionalPropertiesV10" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - }, - "testPresetsV6": { - "type": "array", - "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 6 and higher.", - "items": { - "type": "object", - "description": "A test preset object", - "allOf": [ - { "$ref": "#/definitions/testPresetsItemsV2" }, - { "$ref": "#/definitions/testPresetsAdditionalPropertiesV6" } - ], - "unevaluatedProperties": false - } - }, - "testPresetsV5": { - "type": "array", - "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 5 and higher.", - "items": { - "type": "object", - "description": "A test preset object", - "allOf": [ - { "$ref": "#/definitions/testPresetsItemsV2" }, - { "$ref": "#/definitions/testPresetsAdditionalPropertiesV5" } - ], - "unevaluatedProperties": false - } - }, - "testPresetsV3": { - "type": "array", - "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 3 and higher.", - "items": { - "type": "object", - "description": "A test preset object", - "allOf": [ - { "$ref": "#/definitions/testPresetsItemsV2" }, - { "$ref": "#/definitions/testPresetsAdditionalPropertiesV3" } - ], - "unevaluatedProperties": false - } - }, - "testPresetsV2": { - "type": "array", - "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 2 and higher.", - "items": { - "type": "object", - "description": "A test preset object", - "allOf": [ - { "$ref": "#/definitions/testPresetsItemsV2" }, - { "$ref": "#/definitions/testPresetsAdditionalPropertiesV2" } - ], - "unevaluatedProperties": false - } - }, - "packagePresetsOutputV10": { - "type": "object", - "description": "An optional object specifying output options.", - "allOf": [ - { "$ref": "#/definitions/packagePresetsOutputObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - "packagePresetsOutputV6": { - "type": "object", - "description": "An optional object specifying output options.", - "allOf": [ - { "$ref": "#/definitions/packagePresetsOutputObject" } - ], - "unevaluatedProperties": false - }, - "packagePresetsOutputObject": { - "properties": { - "debug": { - "type": "boolean", - "description": "An optional boolean specifying whether or not to print debug information. A value of true is equivalent to passing --debug on the command line." - }, - "verbose": { - "type": "boolean", - "description": "An optional boolean specifying whether or not to print verbosely. A value of true is equivalent to passing --verbose on the command line." - } - } - }, - "packagePresetsAdditionalPropertiesV10": { - "properties": { - "output": { "$ref": "#/definitions/packagePresetsOutputV10" }, - "condition": { "$ref": "#/definitions/topConditionV10" }, - "$comment": { "$ref": "#/definitions/$comment" } - } - }, - "packagePresetsAdditionalPropertiesV6": { - "properties": { - "output": { "$ref": "#/definitions/packagePresetsOutputV6" }, - "condition": { "$ref": "#/definitions/topConditionV3" } - } - }, - "packagePresetsItemsV6": { - "allOf": [ - { "$ref": "#/definitions/commonPropertiesAll" }, - { "$ref": "#/definitions/configureBuildTestPackagePresetsEnvironmentProperty" }, - { - "properties": { - "hidden": { - "type": "boolean", - "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field." - }, - "inherits": { - "anyOf": [ - { - "type": "string", - "description": "An optional string representing the name of the package preset to inherit from.", - "minLength": 1 - }, - { - "type": "array", - "description": "An optional array of strings representing the names of package presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.", - "items": { - "type": "string", - "description": "An optional string representing the name of the preset to inherit from.", - "minLength": 1 - } - } - ] - }, - "configurePreset": { - "type": "string", - "description": "An optional string specifying the name of a configure preset to associate with this package preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build tree directory is inferred from the configure preset.", - "minLength": 1 - }, - "inheritConfigureEnvironment": { - "type": "boolean", - "description": "An optional boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited package preset environments, but before environment variables explicitly specified in this package preset." - }, - "generators": { - "type": "array", - "description": "An optional list of strings representing generators for CPack to use.", - "items": { - "type": "string", - "description": "An optional string representing the name of the CPack generator to use." - } - }, - "configurations": { - "type": "array", - "description": "An optional list of strings representing build configurations for CPack to package.", - "items": { - "type": "string", - "description": "An optional string representing the name of the configuration to use." - } - }, - "variables": { - "type": "object", - "description": "An optional map of variables to pass to CPack, equivalent to -D arguments. Each key is the name of a variable, and the value is the string to assign to that variable.", - "items": { - "type": "string", - "description": "An optional string representing the value of the variable." - } - }, - "configFile": { - "type": "string", - "description": "An optional string representing the config file for CPack to use." - }, - "packageName": { - "type": "string", - "description": "An optional string representing the package name." - }, - "packageVersion": { - "type": "string", - "description": "An optional string representing the package version." - }, - "packageDirectory": { - "type": "string", - "description": "An optional string representing the directory in which to place the package." - }, - "vendorName": { - "type": "string", - "description": "An optional string representing the vendor name." - } - } - } - ], - "required": [ - "name" - ] - }, - "packagePresetsV10": { - "type": "array", - "description": "An optional array of package preset objects. Used to specify arguments to cpack. Available in version 10 and higher.", - "items": { - "type": "object", - "description": "An optional package preset object.", - "allOf": [ - { "$ref": "#/definitions/packagePresetsItemsV6" }, - { "$ref": "#/definitions/packagePresetsAdditionalPropertiesV10" } - ], - "unevaluatedProperties": false - } - }, - "packagePresetsV6": { - "type": "array", - "description": "An optional array of package preset objects. Used to specify arguments to cpack. Available in version 6 and higher.", - "items": { - "type": "object", - "description": "An optional package preset object.", - "allOf": [ - { "$ref": "#/definitions/packagePresetsItemsV6" }, - { "$ref": "#/definitions/packagePresetsAdditionalPropertiesV6" } - ], - "unevaluatedProperties": false - } - }, - "workflowPresetsStepsV10": { - "type": "array", - "description": "A required array of objects describing the steps of the workflow. The first step must be a configure preset, and all subsequent steps must be non-configure presets whose configurePreset field matches the starting configure preset.", - "items": { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/workflowPresetsStepsObject" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - } - }, - "workflowPresetsStepsV6": { - "type": "array", - "description": "A required array of objects describing the steps of the workflow. The first step must be a configure preset, and all subsequent steps must be non-configure presets whose configurePreset field matches the starting configure preset.", - "items": { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/workflowPresetsStepsObject" } - ], - "unevaluatedProperties": false - } - }, - "workflowPresetsStepsObject": { - "properties": { - "type": { - "type": "string", - "description": "A required string. The first step must be configure. Subsequent steps must be either build, test, or package.", - "enum": ["configure", "build", "test", "package"] - }, - "name": { - "type": "string", - "description": "A required string representing the name of the configure, build, test, or package preset to run as this workflow step.", - "minLength": 1 - } - }, - "required": [ - "type", - "name" - ] - }, - "workflowPresetsAdditionalPropertiesV10": { - "properties": { - "steps": { "$ref": "#/definitions/workflowPresetsStepsV10" }, - "$comment": { "$ref": "#/definitions/$comment" } - } - }, - "workflowPresetsAdditionalPropertiesV6": { - "properties": { - "steps": { "$ref": "#/definitions/workflowPresetsStepsV6" } - } - }, - "workflowPresetsV10": { - "type": "array", - "description": "An optional array of workflow preset objects. Used to execute configure, build, test, and package presets in order. Available in version 10 and higher.", - "items": { - "type": "object", - "description": "An optional workflow preset object", - "allOf": [ - { "$ref": "#/definitions/commonPropertiesAll" }, - { "$ref": "#/definitions/workflowPresetsAdditionalPropertiesV10" } - ], - "required": [ - "name", - "steps" - ], - "unevaluatedProperties": false - } - }, - "workflowPresetsV6": { - "type": "array", - "description": "An optional array of workflow preset objects. Used to execute configure, build, test, and package presets in order. Available in version 6 and higher.", - "items": { - "type": "object", - "description": "An optional workflow preset object", - "allOf": [ - { "$ref": "#/definitions/commonPropertiesAll" }, - { "$ref": "#/definitions/workflowPresetsAdditionalPropertiesV6" } - ], - "required": [ - "name", - "steps" - ], - "unevaluatedProperties": false - } - }, - "conditionAsBooleanV3": { - "type": "boolean", - "description": "A boolean which provides a constant value for the condition's evaluation." - }, - "conditionAsObjectConstV3": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "const" - }, - "value": { - "type": "boolean", - "description": "A required boolean which provides a constant value for the condition's evaluation." - } - }, - "required": [ - "type", - "value" - ] - }, - "conditionAsObjectEqualsV3": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "enum": [ "equals", "notEquals" ] - }, - "lhs": { - "type": "string", - "description": "First string to compare. This field supports macro expansion." - }, - "rhs": { - "type": "string", - "description": "Second string to compare. This field supports macro expansion." - } - }, - "required": [ - "type", - "lhs", - "rhs" - ] - }, - "conditionAsObjectInListV3": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "enum": [ "inList", "notInList" ] - }, - "string": { - "type": "string", - "description": "A required string to search for. This field supports macro expansion." - }, - "list": { - "type": "array", - "description": "A required list of strings to search. This field supports macro expansion, and uses short-circuit evaluation.", - "items": { - "type": "string" - } - } - }, - "required": [ - "type", - "string", - "list" - ] - }, - "conditionAsObjectMatchesV3": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "enum": [ "matches", "notMatches" ] - }, - "string": { - "type": "string", - "description": "A required string to search. This field supports macro expansion." - }, - "regex": { - "type": "string", - "description": "A required regular expression to search for. This field supports macro expansion." - } - }, - "required": [ - "type", - "string", - "regex" - ] - }, - "conditionAsObjectAggregationV3": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "enum": [ "anyOf", "allOf" ] - }, - "conditions": { - "type": "array", - "description": "A required array of condition objects. These conditions use short-circuit evaluation.", - "items": { - "properties": { - "condition": { "$ref": "#/definitions/conditionV3" } - } - } - } - }, - "required": [ - "type", - "conditions" - ] - }, - "conditionAsObjectNotV3": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "not" - }, - "condition": { "$ref": "#/definitions/conditionV3" } - }, - "required": [ - "type", - "condition" - ] - }, - "conditionV10": { - "anyOf": [ - { "$ref": "#/definitions/conditionAsBooleanV3" }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectConstV3" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectEqualsV3" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectInListV3" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectMatchesV3" }, - { "$ref": "#/definitions/commentAsProperty" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectAggregationV3" }, - { "$ref": "#/definitions/commentAsProperty" }, - { - "properties": { - "conditions": { - "type": "array", - "items": { "$ref": "#/definitions/conditionV10" } - } - } - } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectNotV3" }, - { "$ref": "#/definitions/commentAsProperty" }, - { - "properties": { - "condition": { "$ref": "#/definitions/conditionV10" } - } - } - ], - "unevaluatedProperties": false - } - ] - }, - "conditionV3": { - "anyOf": [ - { "$ref": "#/definitions/conditionAsBooleanV3" }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectConstV3" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectEqualsV3" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectInListV3" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectMatchesV3" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectAggregationV3" } - ], - "unevaluatedProperties": false - }, - { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/conditionAsObjectNotV3" } - ], - "unevaluatedProperties": false - } - ] - }, - "topConditionAsNullV3": { - "type": "null", - "description": "Null indicates that the condition always evaluates to true and is not inherited." - }, - "topConditionV10": { - "anyOf": [ - { "$ref": "#/definitions/conditionV10" }, - { "$ref": "#/definitions/topConditionAsNullV3" } - ] - }, - "topConditionV3": { - "anyOf": [ - { "$ref": "#/definitions/conditionV3" }, - { "$ref": "#/definitions/topConditionAsNullV3" } - ] - }, - "include": { + "include@v4..": { "type": "array", "description": "An optional array of strings representing files to include. If the filenames are not absolute, they are considered relative to the current file.", "items": { "type": "string" } + }, + "vendor@v1..": { + "type": "object", + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, the keys should be a vendor-specific domain name followed by a /-separated path. For example, the Example IDE 1.0 could use example.com/ExampleIDE/1.0. The value of each field can be anything desired by the vendor, though will typically be a map.", + "properties": {} + }, + "configurePresets@v1..v2": { + "type": "array", + "description": "An optional array of configure preset objects.", + "items": { + "type": "object", + "description": "A configure preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/configurePresets.name@v1.." + }, + "hidden": { + "$ref": "#/definitions/configurePresets.hidden@v1.." + }, + "inherits": { + "$ref": "#/definitions/configurePresets.inherits@v1.." + }, + "vendor": { + "$ref": "#/definitions/configurePresets.vendor@v1.." + }, + "displayName": { + "$ref": "#/definitions/configurePresets.displayName@v1.." + }, + "description": { + "$ref": "#/definitions/configurePresets.description@v1.." + }, + "generator": { + "$ref": "#/definitions/configurePresets.generator@v1..v2" + }, + "architecture": { + "$ref": "#/definitions/configurePresets.architecture@v1..v9" + }, + "toolset": { + "$ref": "#/definitions/configurePresets.toolset@v1..v9" + }, + "binaryDir": { + "$ref": "#/definitions/configurePresets.binaryDir@v1..v2" + }, + "cmakeExecutable": { + "$ref": "#/definitions/configurePresets.cmakeExecutable@v1.." + }, + "cacheVariables": { + "$ref": "#/definitions/configurePresets.cacheVariables@v1..v9" + }, + "environment": { + "$ref": "#/definitions/configurePresets.environment@v1.." + }, + "warnings": { + "$ref": "#/definitions/configurePresets.warnings@v1..v9" + }, + "errors": { + "$ref": "#/definitions/configurePresets.errors@v1..v9" + }, + "debug": { + "$ref": "#/definitions/configurePresets.debug@v1..v9" + } + }, + "required": [ + "name" + ] + } + }, + "configurePresets@v3..v6": { + "type": "array", + "description": "An optional array of configure preset objects.", + "items": { + "type": "object", + "description": "A configure preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/configurePresets.name@v1.." + }, + "hidden": { + "$ref": "#/definitions/configurePresets.hidden@v1.." + }, + "inherits": { + "$ref": "#/definitions/configurePresets.inherits@v1.." + }, + "condition": { + "$ref": "#/definitions/configurePresets.condition@v3..v9" + }, + "vendor": { + "$ref": "#/definitions/configurePresets.vendor@v1.." + }, + "displayName": { + "$ref": "#/definitions/configurePresets.displayName@v1.." + }, + "description": { + "$ref": "#/definitions/configurePresets.description@v1.." + }, + "generator": { + "$ref": "#/definitions/configurePresets.generator@v3.." + }, + "architecture": { + "$ref": "#/definitions/configurePresets.architecture@v1..v9" + }, + "toolset": { + "$ref": "#/definitions/configurePresets.toolset@v1..v9" + }, + "toolchainFile": { + "$ref": "#/definitions/configurePresets.toolchainFile@v3.." + }, + "binaryDir": { + "$ref": "#/definitions/configurePresets.binaryDir@v3.." + }, + "installDir": { + "$ref": "#/definitions/configurePresets.installDir@v3.." + }, + "cmakeExecutable": { + "$ref": "#/definitions/configurePresets.cmakeExecutable@v1.." + }, + "cacheVariables": { + "$ref": "#/definitions/configurePresets.cacheVariables@v1..v9" + }, + "environment": { + "$ref": "#/definitions/configurePresets.environment@v1.." + }, + "warnings": { + "$ref": "#/definitions/configurePresets.warnings@v1..v9" + }, + "errors": { + "$ref": "#/definitions/configurePresets.errors@v1..v9" + }, + "debug": { + "$ref": "#/definitions/configurePresets.debug@v1..v9" + } + }, + "required": [ + "name" + ] + } + }, + "configurePresets@v7..v9": { + "type": "array", + "description": "An optional array of configure preset objects.", + "items": { + "type": "object", + "description": "A configure preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/configurePresets.name@v1.." + }, + "hidden": { + "$ref": "#/definitions/configurePresets.hidden@v1.." + }, + "inherits": { + "$ref": "#/definitions/configurePresets.inherits@v1.." + }, + "condition": { + "$ref": "#/definitions/configurePresets.condition@v3..v9" + }, + "vendor": { + "$ref": "#/definitions/configurePresets.vendor@v1.." + }, + "displayName": { + "$ref": "#/definitions/configurePresets.displayName@v1.." + }, + "description": { + "$ref": "#/definitions/configurePresets.description@v1.." + }, + "generator": { + "$ref": "#/definitions/configurePresets.generator@v3.." + }, + "architecture": { + "$ref": "#/definitions/configurePresets.architecture@v1..v9" + }, + "toolset": { + "$ref": "#/definitions/configurePresets.toolset@v1..v9" + }, + "toolchainFile": { + "$ref": "#/definitions/configurePresets.toolchainFile@v3.." + }, + "binaryDir": { + "$ref": "#/definitions/configurePresets.binaryDir@v3.." + }, + "installDir": { + "$ref": "#/definitions/configurePresets.installDir@v3.." + }, + "cmakeExecutable": { + "$ref": "#/definitions/configurePresets.cmakeExecutable@v1.." + }, + "cacheVariables": { + "$ref": "#/definitions/configurePresets.cacheVariables@v1..v9" + }, + "environment": { + "$ref": "#/definitions/configurePresets.environment@v1.." + }, + "warnings": { + "$ref": "#/definitions/configurePresets.warnings@v1..v9" + }, + "errors": { + "$ref": "#/definitions/configurePresets.errors@v1..v9" + }, + "debug": { + "$ref": "#/definitions/configurePresets.debug@v1..v9" + }, + "trace": { + "$ref": "#/definitions/configurePresets.trace@v7..v9" + } + }, + "required": [ + "name" + ] + } + }, + "configurePresets@v10..v11": { + "type": "array", + "description": "An optional array of configure preset objects.", + "items": { + "type": "object", + "description": "A configure preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/configurePresets.name@v1.." + }, + "hidden": { + "$ref": "#/definitions/configurePresets.hidden@v1.." + }, + "inherits": { + "$ref": "#/definitions/configurePresets.inherits@v1.." + }, + "condition": { + "$ref": "#/definitions/configurePresets.condition@v10.." + }, + "vendor": { + "$ref": "#/definitions/configurePresets.vendor@v1.." + }, + "displayName": { + "$ref": "#/definitions/configurePresets.displayName@v1.." + }, + "description": { + "$ref": "#/definitions/configurePresets.description@v1.." + }, + "generator": { + "$ref": "#/definitions/configurePresets.generator@v3.." + }, + "architecture": { + "$ref": "#/definitions/configurePresets.architecture@v10.." + }, + "toolset": { + "$ref": "#/definitions/configurePresets.toolset@v10.." + }, + "toolchainFile": { + "$ref": "#/definitions/configurePresets.toolchainFile@v3.." + }, + "graphviz": { + "$ref": "#/definitions/configurePresets.graphviz@v10.." + }, + "binaryDir": { + "$ref": "#/definitions/configurePresets.binaryDir@v3.." + }, + "installDir": { + "$ref": "#/definitions/configurePresets.installDir@v3.." + }, + "cmakeExecutable": { + "$ref": "#/definitions/configurePresets.cmakeExecutable@v1.." + }, + "cacheVariables": { + "$ref": "#/definitions/configurePresets.cacheVariables@v10.." + }, + "environment": { + "$ref": "#/definitions/configurePresets.environment@v1.." + }, + "warnings": { + "$ref": "#/definitions/configurePresets.warnings@v10..v11" + }, + "errors": { + "$ref": "#/definitions/configurePresets.errors@v10..v11" + }, + "debug": { + "$ref": "#/definitions/configurePresets.debug@v10.." + }, + "trace": { + "$ref": "#/definitions/configurePresets.trace@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "name" + ] + } + }, + "configurePresets@v12": { + "type": "array", + "description": "An optional array of configure preset objects.", + "items": { + "type": "object", + "description": "A configure preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/configurePresets.name@v1.." + }, + "hidden": { + "$ref": "#/definitions/configurePresets.hidden@v1.." + }, + "inherits": { + "$ref": "#/definitions/configurePresets.inherits@v1.." + }, + "condition": { + "$ref": "#/definitions/configurePresets.condition@v10.." + }, + "vendor": { + "$ref": "#/definitions/configurePresets.vendor@v1.." + }, + "displayName": { + "$ref": "#/definitions/configurePresets.displayName@v1.." + }, + "description": { + "$ref": "#/definitions/configurePresets.description@v1.." + }, + "generator": { + "$ref": "#/definitions/configurePresets.generator@v3.." + }, + "architecture": { + "$ref": "#/definitions/configurePresets.architecture@v10.." + }, + "toolset": { + "$ref": "#/definitions/configurePresets.toolset@v10.." + }, + "toolchainFile": { + "$ref": "#/definitions/configurePresets.toolchainFile@v3.." + }, + "graphviz": { + "$ref": "#/definitions/configurePresets.graphviz@v10.." + }, + "binaryDir": { + "$ref": "#/definitions/configurePresets.binaryDir@v3.." + }, + "installDir": { + "$ref": "#/definitions/configurePresets.installDir@v3.." + }, + "cmakeExecutable": { + "$ref": "#/definitions/configurePresets.cmakeExecutable@v1.." + }, + "cacheVariables": { + "$ref": "#/definitions/configurePresets.cacheVariables@v10.." + }, + "environment": { + "$ref": "#/definitions/configurePresets.environment@v1.." + }, + "warnings": { + "$ref": "#/definitions/configurePresets.warnings@v12" + }, + "errors": { + "$ref": "#/definitions/configurePresets.errors@v12" + }, + "debug": { + "$ref": "#/definitions/configurePresets.debug@v10.." + }, + "trace": { + "$ref": "#/definitions/configurePresets.trace@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "name" + ] + } + }, + "configurePresets.name@v1..": { + "type": "string", + "minLength": 1, + "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the cmake --preset option. There must not be two configure presets in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name. However, a configure preset may have the same name as a build, test, package, or workflow preset." + }, + "configurePresets.hidden@v1..": { + "type": "boolean", + "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset= argument, will not show up in the CMake GUI, and does not have to have a valid generator or binaryDir, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field." + }, + "configurePresets.inherits@v1..": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "description": "An optional string representing the name of the preset from which to inherit." + }, + { + "type": "array", + "description": "An optional array of strings representing the names of presets from which to inherit. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.", + "items": { + "type": "string", + "minLength": 1, + "description": "An optional string representing the name of the preset from which to inherit." + } + } + ] + }, + "configurePresets.condition@v3..v9": { + "anyOf": [ + { + "$ref": "#/definitions/condition@v3..v9" + }, + { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + } + ] + }, + "configurePresets.condition@v10..": { + "anyOf": [ + { + "$ref": "#/definitions/condition@v10.." + }, + { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + } + ] + }, + "configurePresets.vendor@v1..": { + "type": "object", + "minLength": 1, + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field.", + "properties": {} + }, + "configurePresets.displayName@v1..": { + "type": "string", + "description": "An optional string with a human-friendly name of the preset." + }, + "configurePresets.description@v1..": { + "type": "string", + "description": "An optional string with a human-friendly description of the preset." + }, + "configurePresets.generator@v1..v2": { + "type": "string", + "description": "An optional string representing the generator to use for the preset. If generator is not specified, it must be inherited from the inherits preset (unless this preset is hidden). Note that for Visual Studio generators, unlike in the command line -G argument, you cannot include the platform name in the generator name. Use the architecture field instead." + }, + "configurePresets.generator@v3..": { + "type": "string", + "description": "An optional string representing the generator to use for the preset. If generator is not specified, the normal generator discovery procedure is used. Note that for Visual Studio generators, unlike in the command line -G argument, you cannot include the platform name in the generator name. Use the architecture field instead." + }, + "configurePresets.architecture@v1..v9": { + "anyOf": [ + { + "type": "string", + "description": "An optional string representing the platform for generators that support it." + }, + { + "type": "object", + "description": "An optional string representing the platform for generators that support it.", + "unevaluatedProperties": false, + "properties": { + "value": { + "$ref": "#/definitions/configurePresets.architecture.value@v1.." + }, + "strategy": { + "$ref": "#/definitions/configurePresets.architecture.strategy@v1.." + } + } + } + ] + }, + "configurePresets.architecture@v10..": { + "anyOf": [ + { + "type": "string", + "description": "An optional string representing the platform for generators that support it." + }, + { + "type": "object", + "description": "An optional string representing the platform for generators that support it.", + "unevaluatedProperties": false, + "properties": { + "value": { + "$ref": "#/definitions/configurePresets.architecture.value@v1.." + }, + "strategy": { + "$ref": "#/definitions/configurePresets.architecture.strategy@v1.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + } + ] + }, + "configurePresets.architecture.value@v1..": { + "type": "string", + "description": "An optional string representing the value." + }, + "configurePresets.architecture.strategy@v1..": { + "type": "string", + "enum": [ + "set", + "external" + ], + "description": "An optional string telling CMake how to handle the field. Valid values are: \"set\" Set the respective value. This will result in an error for generators that do not support the respective field. \"external\" Do not set the value, even if the generator supports it. This is useful if, for example, a preset uses the Ninja generator, and an IDE knows how to set up the Visual C++ environment from the architecture and toolset fields. In that case, CMake will ignore the field, but the IDE can use them to set up the environment before invoking CMake." + }, + "configurePresets.toolset@v1..v9": { + "anyOf": [ + { + "type": "string", + "description": "An optional string representing the toolset for generators that support it." + }, + { + "type": "object", + "description": "An optional string representing the toolset for generators that support it.", + "unevaluatedProperties": false, + "properties": { + "value": { + "$ref": "#/definitions/configurePresets.toolset.value@v1.." + }, + "strategy": { + "$ref": "#/definitions/configurePresets.toolset.strategy@v1.." + } + } + } + ] + }, + "configurePresets.toolset@v10..": { + "anyOf": [ + { + "type": "string", + "description": "An optional string representing the toolset for generators that support it." + }, + { + "type": "object", + "description": "An optional string representing the toolset for generators that support it.", + "unevaluatedProperties": false, + "properties": { + "value": { + "$ref": "#/definitions/configurePresets.toolset.value@v1.." + }, + "strategy": { + "$ref": "#/definitions/configurePresets.toolset.strategy@v1.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + } + ] + }, + "configurePresets.toolset.value@v1..": { + "type": "string", + "description": "An optional string representing the value." + }, + "configurePresets.toolset.strategy@v1..": { + "type": "string", + "enum": [ + "set", + "external" + ], + "description": "An optional string telling CMake how to handle the field. Valid values are: \"set\" Set the respective value. This will result in an error for generators that do not support the respective field. \"external\" Do not set the value, even if the generator supports it. This is useful if, for example, a preset uses the Ninja generator, and an IDE knows how to set up the Visual C++ environment from the architecture and toolset fields. In that case, CMake will ignore the field, but the IDE can use them to set up the environment before invoking CMake." + }, + "configurePresets.toolchainFile@v3..": { + "type": "string", + "description": "An optional string representing the path to the toolchain file. This field supports macro expansion. If a relative path is specified, it is calculated relative to the build directory, and if not found, relative to the source directory." + }, + "configurePresets.graphviz@v10..": { + "type": "string", + "description": "An optional string specifying the path to the graphviz dot file." + }, + "configurePresets.binaryDir@v1..v2": { + "type": "string", + "description": "An optional string representing the path to the output binary directory. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory. If binaryDir is not specified, it must be inherited from the inherits preset (unless this preset is hidden)." + }, + "configurePresets.binaryDir@v3..": { + "type": "string", + "description": "An optional string representing the path to the output binary directory. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory. If binaryDir is not specified, the path is calculated using regular methods." + }, + "configurePresets.installDir@v3..": { + "type": "string", + "description": "An optional string representing the path to the installation directory, which will be used as the CMAKE_INSTALL_PREFIX variable. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory." + }, + "configurePresets.cmakeExecutable@v1..": { + "type": "string", + "description": "An optional string representing the path to the CMake executable to use for this preset. This is reserved for use by IDEs, and is not used by CMake itself. IDEs that use this field should expand any macros in it." + }, + "configurePresets.cacheVariables@v1..v9": { + "type": "object", + "description": "An optional map of cache variables. The key is the variable name (which must not be an empty string). Cache variables are inherited through the inherits field, and the preset's variables will be the union of its own cacheVariables and the cacheVariables from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied.", + "propertyNames": { + "pattern": "^.+$" + }, + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "boolean", + "description": "A boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"" + }, + { + "type": "string", + "description": "A string representing the value of the variable (which supports macro expansion)." + }, + { + "type": "object", + "description": "An optional object representing the cache variables for generators that support it.", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/configurePresets.cacheVariables.type@v1.." + }, + "value": { + "$ref": "#/definitions/configurePresets.cacheVariables.value@v1.." + } + }, + "required": [ + "value" + ] + } + ] + } + }, + "configurePresets.cacheVariables@v10..": { + "type": "object", + "description": "An optional map of cache variables. The key is the variable name (which must not be an empty string). Cache variables are inherited through the inherits field, and the preset's variables will be the union of its own cacheVariables and the cacheVariables from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied.", + "propertyNames": { + "pattern": "^.+$" + }, + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "boolean", + "description": "A boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"" + }, + { + "type": "string", + "description": "A string representing the value of the variable (which supports macro expansion)." + }, + { + "type": "object", + "description": "An optional object representing the cache variables for generators that support it.", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/configurePresets.cacheVariables.type@v1.." + }, + "value": { + "$ref": "#/definitions/configurePresets.cacheVariables.value@v1.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "value" + ] + } + ] + } + }, + "configurePresets.cacheVariables.type@v1..": { + "type": "string", + "enum": [ + "BOOL", + "FILEPATH", + "PATH", + "STRING", + "INTERNAL" + ], + "description": "An optional string representing the type of the variable. It should be BOOL, FILEPATH, PATH, STRING, or INTERNAL." + }, + "configurePresets.cacheVariables.value@v1..": { + "anyOf": [ + { + "type": "boolean", + "description": "A boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"." + }, + { + "type": "string", + "description": "A string representing the value of the variable (which supports macro expansion)." + } + ] + }, + "configurePresets.environment@v1..": { + "type": "object", + "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.", + "propertyNames": { + "pattern": "^.+$" + }, + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "string", + "description": "A string representing the value of the variable." + } + ] + } + }, + "configurePresets.warnings@v1..v9": { + "type": "object", + "description": "An optional object specifying the warnings to enable.", + "unevaluatedProperties": false, + "properties": { + "deprecated": { + "$ref": "#/definitions/configurePresets.warnings.deprecated@v1.." + }, + "dev": { + "$ref": "#/definitions/configurePresets.warnings.dev@v1..v11" + }, + "uninitialized": { + "$ref": "#/definitions/configurePresets.warnings.uninitialized@v1.." + }, + "unusedCli": { + "$ref": "#/definitions/configurePresets.warnings.unusedCli@v1.." + }, + "systemVars": { + "$ref": "#/definitions/configurePresets.warnings.systemVars@v1.." + } + } + }, + "configurePresets.warnings@v10..v11": { + "type": "object", + "description": "An optional object specifying the warnings to enable.", + "unevaluatedProperties": false, + "properties": { + "deprecated": { + "$ref": "#/definitions/configurePresets.warnings.deprecated@v1.." + }, + "dev": { + "$ref": "#/definitions/configurePresets.warnings.dev@v1..v11" + }, + "uninitialized": { + "$ref": "#/definitions/configurePresets.warnings.uninitialized@v1.." + }, + "unusedCli": { + "$ref": "#/definitions/configurePresets.warnings.unusedCli@v1.." + }, + "systemVars": { + "$ref": "#/definitions/configurePresets.warnings.systemVars@v1.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "configurePresets.warnings@v12": { + "type": "object", + "description": "An optional object specifying the warnings to enable.", + "unevaluatedProperties": false, + "properties": { + "author": { + "$ref": "#/definitions/configurePresets.warnings.author@v12" + }, + "deprecated": { + "$ref": "#/definitions/configurePresets.warnings.deprecated@v1.." + }, + "experimental": { + "$ref": "#/definitions/configurePresets.warnings.experimental@v12" + }, + "installAbsoluteDestination": { + "$ref": "#/definitions/configurePresets.warnings.installAbsoluteDestination@v12" + }, + "policy": { + "$ref": "#/definitions/configurePresets.warnings.policy@v12" + }, + "uninitialized": { + "$ref": "#/definitions/configurePresets.warnings.uninitialized@v1.." + }, + "unusedCli": { + "$ref": "#/definitions/configurePresets.warnings.unusedCli@v1.." + }, + "systemVars": { + "$ref": "#/definitions/configurePresets.warnings.systemVars@v1.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "configurePresets.warnings.author@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wauthor or -Wno-author on the command line. This may not be set to false if errors.author is set to true." + }, + "configurePresets.warnings.deprecated@v1..": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wdeprecated or -Wno-deprecated on the command line. This may not be set to false if errors.deprecated is set to true." + }, + "configurePresets.warnings.dev@v1..v11": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wdev or -Wno-dev on the command line. This may not be set to false if errors.dev is set to true." + }, + "configurePresets.warnings.experimental@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wexperimental or -Wno-experimental on the command line. This may not be set to false if errors.experimental is set to true." + }, + "configurePresets.warnings.installAbsoluteDestination@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Winstall-absolute-destination or -Wno-install-absolute-destination on the command line. This may not be set to false if errors.installAbsoluteDestination is set to true." + }, + "configurePresets.warnings.policy@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wpolicy or -Wno-policy on the command line. This may not be set to false if errors.policy is set to true." + }, + "configurePresets.warnings.uninitialized@v1..": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wuninitialized or -Wno-uninitialized on the command line. This may not be set to false if errors.uninitialized is set to true." + }, + "configurePresets.warnings.unusedCli@v1..": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wunused-cli or -Wno-unused-cli on the command line. This may not be set to false if errors.unusedCli is set to true." + }, + "configurePresets.warnings.systemVars@v1..": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --check-system-vars on the command line." + }, + "configurePresets.errors@v1..v9": { + "type": "object", + "description": "An optional object specifying the errors to enable.", + "unevaluatedProperties": false, + "properties": { + "deprecated": { + "$ref": "#/definitions/configurePresets.errors.deprecated@v1.." + }, + "dev": { + "$ref": "#/definitions/configurePresets.errors.dev@v1..v11" + } + } + }, + "configurePresets.errors@v10..v11": { + "type": "object", + "description": "An optional object specifying the errors to enable.", + "unevaluatedProperties": false, + "properties": { + "deprecated": { + "$ref": "#/definitions/configurePresets.errors.deprecated@v1.." + }, + "dev": { + "$ref": "#/definitions/configurePresets.errors.dev@v1..v11" + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "configurePresets.errors@v12": { + "type": "object", + "description": "An optional object specifying the errors to enable.", + "unevaluatedProperties": false, + "properties": { + "author": { + "$ref": "#/definitions/configurePresets.errors.author@v12" + }, + "deprecated": { + "$ref": "#/definitions/configurePresets.errors.deprecated@v1.." + }, + "experimental": { + "$ref": "#/definitions/configurePresets.errors.experimental@v12" + }, + "installAbsoluteDestination": { + "$ref": "#/definitions/configurePresets.errors.installAbsoluteDestination@v12" + }, + "policy": { + "$ref": "#/definitions/configurePresets.errors.policy@v12" + }, + "uninitialized": { + "$ref": "#/definitions/configurePresets.errors.uninitialized@v12" + }, + "unusedCli": { + "$ref": "#/definitions/configurePresets.errors.unusedCli@v12" + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "configurePresets.errors.author@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=author or -Wno-error=author on the command line. This may not be set to true if warnings.author is set to false." + }, + "configurePresets.errors.deprecated@v1..": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=deprecated or -Wno-error=deprecated on the command line. This may not be set to true if warnings.deprecated is set to false." + }, + "configurePresets.errors.dev@v1..v11": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=dev or -Wno-error=dev on the command line. This may not be set to true if warnings.dev is set to false." + }, + "configurePresets.errors.experimental@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=experimental or -Wno-error=experimental on the command line. This may not be set to true if warnings.experimental is set to false." + }, + "configurePresets.errors.installAbsoluteDestination@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=install-absolute-destination or -Wno-error=install-absolute-destination on the command line. This may not be set to true if warnings.installAbsoluteDestination is set to false." + }, + "configurePresets.errors.policy@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=policy or -Wno-error=policy on the command line. This may not be set to true if warnings.policy is set to false." + }, + "configurePresets.errors.uninitialized@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=uninitialized or -Wno-error=uninitialized on the command line. This may not be set to true if warnings.uninitialized is set to false." + }, + "configurePresets.errors.unusedCli@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=unused-cli or -Wno-error=unused-cli on the command line. This may not be set to true if warnings.unusedCli is set to false." + }, + "configurePresets.debug@v1..v9": { + "type": "object", + "description": "An optional object specifying debug options.", + "unevaluatedProperties": false, + "properties": { + "output": { + "$ref": "#/definitions/configurePresets.debug.output@v1.." + }, + "tryCompile": { + "$ref": "#/definitions/configurePresets.debug.tryCompile@v1.." + }, + "find": { + "$ref": "#/definitions/configurePresets.debug.find@v1.." + } + } + }, + "configurePresets.debug@v10..": { + "type": "object", + "description": "An optional object specifying debug options.", + "unevaluatedProperties": false, + "properties": { + "output": { + "$ref": "#/definitions/configurePresets.debug.output@v1.." + }, + "tryCompile": { + "$ref": "#/definitions/configurePresets.debug.tryCompile@v1.." + }, + "find": { + "$ref": "#/definitions/configurePresets.debug.find@v1.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "configurePresets.debug.output@v1..": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --debug-output on the command line." + }, + "configurePresets.debug.tryCompile@v1..": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --debug-trycompile on the command line." + }, + "configurePresets.debug.find@v1..": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --debug-find on the command line." + }, + "configurePresets.trace@v7..v9": { + "type": "object", + "description": "An optional object specifying trace options.", + "unevaluatedProperties": false, + "properties": { + "mode": { + "$ref": "#/definitions/configurePresets.trace.mode@v7.." + }, + "format": { + "$ref": "#/definitions/configurePresets.trace.format@v7.." + }, + "source": { + "$ref": "#/definitions/configurePresets.trace.source@v7.." + }, + "redirect": { + "$ref": "#/definitions/configurePresets.trace.redirect@v7.." + } + } + }, + "configurePresets.trace@v10..": { + "type": "object", + "description": "An optional object specifying trace options.", + "unevaluatedProperties": false, + "properties": { + "mode": { + "$ref": "#/definitions/configurePresets.trace.mode@v7.." + }, + "format": { + "$ref": "#/definitions/configurePresets.trace.format@v7.." + }, + "source": { + "$ref": "#/definitions/configurePresets.trace.source@v7.." + }, + "redirect": { + "$ref": "#/definitions/configurePresets.trace.redirect@v7.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "configurePresets.trace.mode@v7..": { + "type": "string", + "enum": [ + "on", + "off", + "expand" + ], + "description": "An optional string that specifies the trace mode." + }, + "configurePresets.trace.format@v7..": { + "type": "string", + "enum": [ + "human", + "json-v1" + ], + "description": "An optional string that specifies the trace output format." + }, + "configurePresets.trace.source@v7..": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "description": "An optional string representing the path to one source file to be traced." + }, + { + "type": "array", + "description": "An optional array of strings representing the paths to source files to be traced.", + "items": { + "type": "string", + "minLength": 1, + "description": "A string representing the path to one source file to be traced." + } + } + ] + }, + "configurePresets.trace.redirect@v7..": { + "type": "string", + "description": "An optional string specifying a path to a trace output file." + }, + "buildPresets@v2": { + "type": "array", + "description": "An optional array of build preset objects.", + "items": { + "type": "object", + "description": "A build preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/buildPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/buildPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/buildPresets.inherits@v2.." + }, + "vendor": { + "$ref": "#/definitions/buildPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/buildPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/buildPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/buildPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/buildPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/buildPresets.inheritConfigureEnvironment@v2.." + }, + "jobs": { + "$ref": "#/definitions/buildPresets.jobs@v2.." + }, + "targets": { + "$ref": "#/definitions/buildPresets.targets@v2.." + }, + "configuration": { + "$ref": "#/definitions/buildPresets.configuration@v2.." + }, + "cleanFirst": { + "$ref": "#/definitions/buildPresets.cleanFirst@v2.." + }, + "verbose": { + "$ref": "#/definitions/buildPresets.verbose@v2.." + }, + "nativeToolOptions": { + "$ref": "#/definitions/buildPresets.nativeToolOptions@v2.." + } + }, + "required": [ + "name" + ] + } + }, + "buildPresets@v3": { + "type": "array", + "description": "An optional array of build preset objects.", + "items": { + "type": "object", + "description": "A build preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/buildPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/buildPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/buildPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/buildPresets.condition@v3..v9" + }, + "vendor": { + "$ref": "#/definitions/buildPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/buildPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/buildPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/buildPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/buildPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/buildPresets.inheritConfigureEnvironment@v2.." + }, + "jobs": { + "$ref": "#/definitions/buildPresets.jobs@v2.." + }, + "targets": { + "$ref": "#/definitions/buildPresets.targets@v2.." + }, + "configuration": { + "$ref": "#/definitions/buildPresets.configuration@v2.." + }, + "cleanFirst": { + "$ref": "#/definitions/buildPresets.cleanFirst@v2.." + }, + "verbose": { + "$ref": "#/definitions/buildPresets.verbose@v2.." + }, + "nativeToolOptions": { + "$ref": "#/definitions/buildPresets.nativeToolOptions@v2.." + } + }, + "required": [ + "name" + ] + } + }, + "buildPresets@v4..v9": { + "type": "array", + "description": "An optional array of build preset objects.", + "items": { + "type": "object", + "description": "A build preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/buildPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/buildPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/buildPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/buildPresets.condition@v3..v9" + }, + "vendor": { + "$ref": "#/definitions/buildPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/buildPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/buildPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/buildPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/buildPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/buildPresets.inheritConfigureEnvironment@v2.." + }, + "jobs": { + "$ref": "#/definitions/buildPresets.jobs@v2.." + }, + "targets": { + "$ref": "#/definitions/buildPresets.targets@v2.." + }, + "configuration": { + "$ref": "#/definitions/buildPresets.configuration@v2.." + }, + "cleanFirst": { + "$ref": "#/definitions/buildPresets.cleanFirst@v2.." + }, + "resolvePackageReferences": { + "$ref": "#/definitions/buildPresets.resolvePackageReferences@v4.." + }, + "verbose": { + "$ref": "#/definitions/buildPresets.verbose@v2.." + }, + "nativeToolOptions": { + "$ref": "#/definitions/buildPresets.nativeToolOptions@v2.." + } + }, + "required": [ + "name" + ] + } + }, + "buildPresets@v10..": { + "type": "array", + "description": "An optional array of build preset objects.", + "items": { + "type": "object", + "description": "A build preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/buildPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/buildPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/buildPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/buildPresets.condition@v10.." + }, + "vendor": { + "$ref": "#/definitions/buildPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/buildPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/buildPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/buildPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/buildPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/buildPresets.inheritConfigureEnvironment@v2.." + }, + "jobs": { + "$ref": "#/definitions/buildPresets.jobs@v2.." + }, + "targets": { + "$ref": "#/definitions/buildPresets.targets@v2.." + }, + "configuration": { + "$ref": "#/definitions/buildPresets.configuration@v2.." + }, + "cleanFirst": { + "$ref": "#/definitions/buildPresets.cleanFirst@v2.." + }, + "resolvePackageReferences": { + "$ref": "#/definitions/buildPresets.resolvePackageReferences@v4.." + }, + "verbose": { + "$ref": "#/definitions/buildPresets.verbose@v2.." + }, + "nativeToolOptions": { + "$ref": "#/definitions/buildPresets.nativeToolOptions@v2.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "name" + ] + } + }, + "buildPresets.name@v2..": { + "type": "string", + "minLength": 1, + "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the cmake --build --preset option. There must not be two build presets in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name. However, a build preset may have the same name as a configure, test, package, or workflow preset." + }, + "buildPresets.hidden@v2..": { + "type": "boolean", + "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. hidden presets are intended to be used as a base for other presets to inherit via the inherits field." + }, + "buildPresets.inherits@v2..": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "description": "An optional string representing the name of the preset from which to inherit." + }, + { + "type": "array", + "description": "An optional array of strings representing the names of presets from which to inherit. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.", + "items": { + "type": "string", + "minLength": 1, + "description": "An optional string representing the name of the preset from which to inherit." + } + } + ] + }, + "buildPresets.condition@v3..v9": { + "anyOf": [ + { + "$ref": "#/definitions/condition@v3..v9" + }, + { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + } + ] + }, + "buildPresets.condition@v10..": { + "anyOf": [ + { + "$ref": "#/definitions/condition@v10.." + }, + { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + } + ] + }, + "buildPresets.vendor@v2..": { + "type": "object", + "minLength": 1, + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field.", + "properties": {} + }, + "buildPresets.displayName@v2..": { + "type": "string", + "description": "An optional string with a human-friendly name of the preset." + }, + "buildPresets.description@v2..": { + "type": "string", + "description": "An optional string with a human-friendly description of the preset." + }, + "buildPresets.environment@v2..": { + "type": "object", + "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.", + "propertyNames": { + "pattern": "^.+$" + }, + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "string", + "description": "A string representing the value of the variable." + } + ] + } + }, + "buildPresets.configurePreset@v2..": { + "type": "string", + "minLength": 1, + "description": "An optional string specifying the name of a configure preset to associate with this build preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build tree directory is inferred from the configure preset." + }, + "buildPresets.inheritConfigureEnvironment@v2..": { + "type": "boolean", + "description": "An optional boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited build preset environments, but before environment variables explicitly specified in this build preset." + }, + "buildPresets.jobs@v2..": { + "type": "integer", + "description": "An optional non-negative integer. Equivalent to passing --parallel or -j on the command line.", + "minimum": 0 + }, + "buildPresets.targets@v2..": { + "anyOf": [ + { + "type": "string", + "description": "An optional string. Equivalent to passing --target or -t on the command line. Vendors may ignore the targets property or hide build presets that explicitly specify targets." + }, + { + "type": "array", + "description": "An optional array of strings. Equivalent to passing --target or -t on the command line. Vendors may ignore the targets property or hide build presets that explicitly specify targets.", + "items": { + "type": "string" + } + } + ] + }, + "buildPresets.configuration@v2..": { + "type": "string", + "description": "An optional string. Equivalent to passing --config on the command line." + }, + "buildPresets.cleanFirst@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --clean-first on the command line." + }, + "buildPresets.resolvePackageReferences@v4..": { + "type": "string", + "enum": [ + "on", + "off", + "only" + ], + "description": "An optional string specifying the package resolve behavior. Valid values are \"on\" (packages are resolved prior to the build), \"off\" (packages are not resolved prior to the build), and \"only\" (packages are resolved, but no build will be performed)." + }, + "buildPresets.verbose@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --verbose on the command line." + }, + "buildPresets.nativeToolOptions@v2..": { + "type": "array", + "description": "An optional array of strings. Equivalent to passing options after -- on the command line.", + "items": { + "type": "string" + } + }, + "testPresets@v2": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest.", + "items": { + "type": "object", + "description": "A test preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/testPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/testPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/testPresets.inherits@v2.." + }, + "vendor": { + "$ref": "#/definitions/testPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/testPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/testPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/testPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/testPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/testPresets.inheritConfigureEnvironment@v2.." + }, + "configuration": { + "$ref": "#/definitions/testPresets.configuration@v2.." + }, + "overwriteConfigurationFile": { + "$ref": "#/definitions/testPresets.overwriteConfigurationFile@v2.." + }, + "output": { + "$ref": "#/definitions/testPresets.output@v2..v4" + }, + "filter": { + "$ref": "#/definitions/testPresets.filter@v2..v9" + }, + "execution": { + "$ref": "#/definitions/testPresets.execution@v2..v9" + } + }, + "required": [ + "name" + ] + } + }, + "testPresets@v3..v4": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest.", + "items": { + "type": "object", + "description": "A test preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/testPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/testPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/testPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/testPresets.condition@v3..v9" + }, + "vendor": { + "$ref": "#/definitions/testPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/testPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/testPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/testPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/testPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/testPresets.inheritConfigureEnvironment@v2.." + }, + "configuration": { + "$ref": "#/definitions/testPresets.configuration@v2.." + }, + "overwriteConfigurationFile": { + "$ref": "#/definitions/testPresets.overwriteConfigurationFile@v2.." + }, + "output": { + "$ref": "#/definitions/testPresets.output@v2..v4" + }, + "filter": { + "$ref": "#/definitions/testPresets.filter@v2..v9" + }, + "execution": { + "$ref": "#/definitions/testPresets.execution@v2..v9" + } + }, + "required": [ + "name" + ] + } + }, + "testPresets@v5": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest.", + "items": { + "type": "object", + "description": "A test preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/testPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/testPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/testPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/testPresets.condition@v3..v9" + }, + "vendor": { + "$ref": "#/definitions/testPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/testPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/testPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/testPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/testPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/testPresets.inheritConfigureEnvironment@v2.." + }, + "configuration": { + "$ref": "#/definitions/testPresets.configuration@v2.." + }, + "overwriteConfigurationFile": { + "$ref": "#/definitions/testPresets.overwriteConfigurationFile@v2.." + }, + "output": { + "$ref": "#/definitions/testPresets.output@v5" + }, + "filter": { + "$ref": "#/definitions/testPresets.filter@v2..v9" + }, + "execution": { + "$ref": "#/definitions/testPresets.execution@v2..v9" + } + }, + "required": [ + "name" + ] + } + }, + "testPresets@v6..v9": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest.", + "items": { + "type": "object", + "description": "A test preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/testPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/testPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/testPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/testPresets.condition@v3..v9" + }, + "vendor": { + "$ref": "#/definitions/testPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/testPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/testPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/testPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/testPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/testPresets.inheritConfigureEnvironment@v2.." + }, + "configuration": { + "$ref": "#/definitions/testPresets.configuration@v2.." + }, + "overwriteConfigurationFile": { + "$ref": "#/definitions/testPresets.overwriteConfigurationFile@v2.." + }, + "output": { + "$ref": "#/definitions/testPresets.output@v6..v9" + }, + "filter": { + "$ref": "#/definitions/testPresets.filter@v2..v9" + }, + "execution": { + "$ref": "#/definitions/testPresets.execution@v2..v9" + } + }, + "required": [ + "name" + ] + } + }, + "testPresets@v10": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest.", + "items": { + "type": "object", + "description": "A test preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/testPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/testPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/testPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/testPresets.condition@v10.." + }, + "vendor": { + "$ref": "#/definitions/testPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/testPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/testPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/testPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/testPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/testPresets.inheritConfigureEnvironment@v2.." + }, + "configuration": { + "$ref": "#/definitions/testPresets.configuration@v2.." + }, + "overwriteConfigurationFile": { + "$ref": "#/definitions/testPresets.overwriteConfigurationFile@v2.." + }, + "output": { + "$ref": "#/definitions/testPresets.output@v10.." + }, + "filter": { + "$ref": "#/definitions/testPresets.filter@v10.." + }, + "execution": { + "$ref": "#/definitions/testPresets.execution@v10" + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "name" + ] + } + }, + "testPresets@v11": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest.", + "items": { + "type": "object", + "description": "A test preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/testPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/testPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/testPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/testPresets.condition@v10.." + }, + "vendor": { + "$ref": "#/definitions/testPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/testPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/testPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/testPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/testPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/testPresets.inheritConfigureEnvironment@v2.." + }, + "configuration": { + "$ref": "#/definitions/testPresets.configuration@v2.." + }, + "overwriteConfigurationFile": { + "$ref": "#/definitions/testPresets.overwriteConfigurationFile@v2.." + }, + "output": { + "$ref": "#/definitions/testPresets.output@v10.." + }, + "filter": { + "$ref": "#/definitions/testPresets.filter@v10.." + }, + "execution": { + "$ref": "#/definitions/testPresets.execution@v11" + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "name" + ] + } + }, + "testPresets@v12": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest.", + "items": { + "type": "object", + "description": "A test preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/testPresets.name@v2.." + }, + "hidden": { + "$ref": "#/definitions/testPresets.hidden@v2.." + }, + "inherits": { + "$ref": "#/definitions/testPresets.inherits@v2.." + }, + "condition": { + "$ref": "#/definitions/testPresets.condition@v10.." + }, + "vendor": { + "$ref": "#/definitions/testPresets.vendor@v2.." + }, + "displayName": { + "$ref": "#/definitions/testPresets.displayName@v2.." + }, + "description": { + "$ref": "#/definitions/testPresets.description@v2.." + }, + "environment": { + "$ref": "#/definitions/testPresets.environment@v2.." + }, + "configurePreset": { + "$ref": "#/definitions/testPresets.configurePreset@v2.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/testPresets.inheritConfigureEnvironment@v2.." + }, + "configuration": { + "$ref": "#/definitions/testPresets.configuration@v2.." + }, + "overwriteConfigurationFile": { + "$ref": "#/definitions/testPresets.overwriteConfigurationFile@v2.." + }, + "output": { + "$ref": "#/definitions/testPresets.output@v10.." + }, + "filter": { + "$ref": "#/definitions/testPresets.filter@v10.." + }, + "execution": { + "$ref": "#/definitions/testPresets.execution@v12" + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "name" + ] + } + }, + "testPresets.name@v2..": { + "type": "string", + "minLength": 1, + "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the ctest --preset option. There must not be two test presets in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name. However, a test preset may have the same name as a configure, build, package, or workflow preset." + }, + "testPresets.hidden@v2..": { + "type": "boolean", + "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. hidden presets are intended to be used as a base for other presets to inherit via the inherits field." + }, + "testPresets.inherits@v2..": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "description": "An optional string representing the name of the preset from which to inherit." + }, + { + "type": "array", + "description": "An optional array of strings representing the names of presets from which to inherit. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.", + "items": { + "type": "string", + "minLength": 1, + "description": "An optional string representing the name of the preset from which to inherit." + } + } + ] + }, + "testPresets.condition@v3..v9": { + "anyOf": [ + { + "$ref": "#/definitions/condition@v3..v9" + }, + { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + } + ] + }, + "testPresets.condition@v10..": { + "anyOf": [ + { + "$ref": "#/definitions/condition@v10.." + }, + { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + } + ] + }, + "testPresets.vendor@v2..": { + "type": "object", + "minLength": 1, + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field.", + "properties": {} + }, + "testPresets.displayName@v2..": { + "type": "string", + "description": "An optional string with a human-friendly name of the preset." + }, + "testPresets.description@v2..": { + "type": "string", + "description": "An optional string with a human-friendly description of the preset." + }, + "testPresets.environment@v2..": { + "type": "object", + "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.", + "propertyNames": { + "pattern": "^.+$" + }, + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "string", + "description": "A string representing the value of the variable." + } + ] + } + }, + "testPresets.configurePreset@v2..": { + "type": "string", + "minLength": 1, + "description": "An optional string specifying the name of a configure preset to associate with this test preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build directory is inferred from the configure preset." + }, + "testPresets.inheritConfigureEnvironment@v2..": { + "type": "boolean", + "description": "An optional boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited test preset environments, but before environment variables explicitly specified in this test preset." + }, + "testPresets.configuration@v2..": { + "type": "string", + "description": "An optional string. Equivalent to passing --build-config on the command line." + }, + "testPresets.overwriteConfigurationFile@v2..": { + "type": "array", + "description": "An optional array of configuration options to overwrite options specified in the CTest configuration file. Equivalent to passing ``--overwrite`` for each value in the array.", + "items": { + "type": "string", + "description": "An option written as a key-value pair in the form \"key=value\"." + } + }, + "testPresets.output@v2..v4": { + "type": "object", + "description": "An optional object specifying output options.", + "unevaluatedProperties": false, + "properties": { + "shortProgress": { + "$ref": "#/definitions/testPresets.output.shortProgress@v2.." + }, + "verbosity": { + "$ref": "#/definitions/testPresets.output.verbosity@v2.." + }, + "debug": { + "$ref": "#/definitions/testPresets.output.debug@v2.." + }, + "outputOnFailure": { + "$ref": "#/definitions/testPresets.output.outputOnFailure@v2.." + }, + "quiet": { + "$ref": "#/definitions/testPresets.output.quiet@v2.." + }, + "outputLogFile": { + "$ref": "#/definitions/testPresets.output.outputLogFile@v2.." + }, + "labelSummary": { + "$ref": "#/definitions/testPresets.output.labelSummary@v2.." + }, + "subprojectSummary": { + "$ref": "#/definitions/testPresets.output.subprojectSummary@v2.." + }, + "maxPassedTestOutputSize": { + "$ref": "#/definitions/testPresets.output.maxPassedTestOutputSize@v2.." + }, + "maxFailedTestOutputSize": { + "$ref": "#/definitions/testPresets.output.maxFailedTestOutputSize@v2.." + }, + "maxTestNameWidth": { + "$ref": "#/definitions/testPresets.output.maxTestNameWidth@v2.." + } + } + }, + "testPresets.output@v5": { + "type": "object", + "description": "An optional object specifying output options.", + "unevaluatedProperties": false, + "properties": { + "shortProgress": { + "$ref": "#/definitions/testPresets.output.shortProgress@v2.." + }, + "verbosity": { + "$ref": "#/definitions/testPresets.output.verbosity@v2.." + }, + "debug": { + "$ref": "#/definitions/testPresets.output.debug@v2.." + }, + "outputOnFailure": { + "$ref": "#/definitions/testPresets.output.outputOnFailure@v2.." + }, + "quiet": { + "$ref": "#/definitions/testPresets.output.quiet@v2.." + }, + "outputLogFile": { + "$ref": "#/definitions/testPresets.output.outputLogFile@v2.." + }, + "labelSummary": { + "$ref": "#/definitions/testPresets.output.labelSummary@v2.." + }, + "subprojectSummary": { + "$ref": "#/definitions/testPresets.output.subprojectSummary@v2.." + }, + "maxPassedTestOutputSize": { + "$ref": "#/definitions/testPresets.output.maxPassedTestOutputSize@v2.." + }, + "maxFailedTestOutputSize": { + "$ref": "#/definitions/testPresets.output.maxFailedTestOutputSize@v2.." + }, + "testOutputTruncation": { + "$ref": "#/definitions/testPresets.output.testOutputTruncation@v5.." + }, + "maxTestNameWidth": { + "$ref": "#/definitions/testPresets.output.maxTestNameWidth@v2.." + } + } + }, + "testPresets.output@v6..v9": { + "type": "object", + "description": "An optional object specifying output options.", + "unevaluatedProperties": false, + "properties": { + "shortProgress": { + "$ref": "#/definitions/testPresets.output.shortProgress@v2.." + }, + "verbosity": { + "$ref": "#/definitions/testPresets.output.verbosity@v2.." + }, + "debug": { + "$ref": "#/definitions/testPresets.output.debug@v2.." + }, + "outputOnFailure": { + "$ref": "#/definitions/testPresets.output.outputOnFailure@v2.." + }, + "quiet": { + "$ref": "#/definitions/testPresets.output.quiet@v2.." + }, + "outputLogFile": { + "$ref": "#/definitions/testPresets.output.outputLogFile@v2.." + }, + "outputJUnitFile": { + "$ref": "#/definitions/testPresets.output.outputJUnitFile@v6.." + }, + "labelSummary": { + "$ref": "#/definitions/testPresets.output.labelSummary@v2.." + }, + "subprojectSummary": { + "$ref": "#/definitions/testPresets.output.subprojectSummary@v2.." + }, + "maxPassedTestOutputSize": { + "$ref": "#/definitions/testPresets.output.maxPassedTestOutputSize@v2.." + }, + "maxFailedTestOutputSize": { + "$ref": "#/definitions/testPresets.output.maxFailedTestOutputSize@v2.." + }, + "testOutputTruncation": { + "$ref": "#/definitions/testPresets.output.testOutputTruncation@v5.." + }, + "maxTestNameWidth": { + "$ref": "#/definitions/testPresets.output.maxTestNameWidth@v2.." + } + } + }, + "testPresets.output@v10..": { + "type": "object", + "description": "An optional object specifying output options.", + "unevaluatedProperties": false, + "properties": { + "shortProgress": { + "$ref": "#/definitions/testPresets.output.shortProgress@v2.." + }, + "verbosity": { + "$ref": "#/definitions/testPresets.output.verbosity@v2.." + }, + "debug": { + "$ref": "#/definitions/testPresets.output.debug@v2.." + }, + "outputOnFailure": { + "$ref": "#/definitions/testPresets.output.outputOnFailure@v2.." + }, + "quiet": { + "$ref": "#/definitions/testPresets.output.quiet@v2.." + }, + "outputLogFile": { + "$ref": "#/definitions/testPresets.output.outputLogFile@v2.." + }, + "outputJUnitFile": { + "$ref": "#/definitions/testPresets.output.outputJUnitFile@v6.." + }, + "labelSummary": { + "$ref": "#/definitions/testPresets.output.labelSummary@v2.." + }, + "subprojectSummary": { + "$ref": "#/definitions/testPresets.output.subprojectSummary@v2.." + }, + "maxPassedTestOutputSize": { + "$ref": "#/definitions/testPresets.output.maxPassedTestOutputSize@v2.." + }, + "maxFailedTestOutputSize": { + "$ref": "#/definitions/testPresets.output.maxFailedTestOutputSize@v2.." + }, + "testOutputTruncation": { + "$ref": "#/definitions/testPresets.output.testOutputTruncation@v5.." + }, + "maxTestNameWidth": { + "$ref": "#/definitions/testPresets.output.maxTestNameWidth@v2.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "testPresets.output.shortProgress@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --progress on the command line." + }, + "testPresets.output.verbosity@v2..": { + "type": "string", + "enum": [ + "default", + "verbose", + "extra" + ], + "description": "An optional string specifying verbosity level. Valid values are \"default\" (equivalent to passing no verbosity flags on the command line), \"verbose\" (equivalent to passing --verbose on the command line), and \"extra\" (equivalent to passing --extra-verbose on the command line)." + }, + "testPresets.output.debug@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --debug on the command line." + }, + "testPresets.output.outputOnFailure@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --output-on-failure on the command line." + }, + "testPresets.output.quiet@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --quiet on the command line." + }, + "testPresets.output.outputLogFile@v2..": { + "type": "string", + "description": "An optional string specifying a path to a log file. Equivalent to passing --output-log on the command line." + }, + "testPresets.output.outputJUnitFile@v6..": { + "type": "string", + "description": "An optional string specifying a path to a JUnit file. Equivalent to passing --output-junit on the command line." + }, + "testPresets.output.labelSummary@v2..": { + "type": "boolean", + "description": "An optional boolean. If false, equivalent to passing --no-label-summary on the command line." + }, + "testPresets.output.subprojectSummary@v2..": { + "type": "boolean", + "description": "An optional boolean. If false, equivalent to passing --no-subproject-summary on the command line." + }, + "testPresets.output.maxPassedTestOutputSize@v2..": { + "type": "integer", + "description": "An optional integer specifying the maximum output for passed tests in bytes. Equivalent to passing --test-output-size-passed on the command line." + }, + "testPresets.output.maxFailedTestOutputSize@v2..": { + "type": "integer", + "description": "An optional integer specifying the maximum output for failed tests in bytes. Equivalent to passing --test-output-size-failed on the command line." + }, + "testPresets.output.testOutputTruncation@v5..": { + "type": "string", + "enum": [ + "tail", + "middle", + "head" + ], + "description": "An optional string specifying the test output truncation mode. Equivalent to passing --test-output-truncation on the command line. Must be one of the following values: \"tail\", \"middle\", or \"head\"." + }, + "testPresets.output.maxTestNameWidth@v2..": { + "type": "integer", + "description": "An optional integer specifying the maximum width of a test name to output. Equivalent to passing --max-width on the command line." + }, + "testPresets.filter@v2..v9": { + "type": "object", + "description": "An optional object specifying how to filter the tests to run.", + "properties": { + "include": { + "$ref": "#/definitions/testPresets.filter.include@v2..v9" + }, + "exclude": { + "$ref": "#/definitions/testPresets.filter.exclude@v2..v9" + } + } + }, + "testPresets.filter@v10..": { + "type": "object", + "description": "An optional object specifying how to filter the tests to run.", + "properties": { + "include": { + "$ref": "#/definitions/testPresets.filter.include@v10.." + }, + "exclude": { + "$ref": "#/definitions/testPresets.filter.exclude@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "testPresets.filter.include@v2..v9": { + "type": "object", + "description": "An optional object specifying which tests to include.", + "properties": { + "name": { + "$ref": "#/definitions/testPresets.filter.include.name@v2.." + }, + "label": { + "$ref": "#/definitions/testPresets.filter.include.label@v2.." + }, + "useUnion": { + "$ref": "#/definitions/testPresets.filter.include.useUnion@v2.." + }, + "index": { + "$ref": "#/definitions/testPresets.filter.include.index@v2..v9" + } + } + }, + "testPresets.filter.include@v10..": { + "type": "object", + "description": "An optional object specifying which tests to include.", + "properties": { + "name": { + "$ref": "#/definitions/testPresets.filter.include.name@v2.." + }, + "label": { + "$ref": "#/definitions/testPresets.filter.include.label@v2.." + }, + "useUnion": { + "$ref": "#/definitions/testPresets.filter.include.useUnion@v2.." + }, + "index": { + "$ref": "#/definitions/testPresets.filter.include.index@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "testPresets.filter.include.name@v2..": { + "type": "string", + "description": "An optional string specifying a regex for test names. Equivalent to passing --tests-regex on the command line." + }, + "testPresets.filter.include.label@v2..": { + "type": "string", + "description": "An optional string specifying a regex for test labels. Equivalent to passing --label-regex on the command line." + }, + "testPresets.filter.include.useUnion@v2..": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing --union on the command line." + }, + "testPresets.filter.include.index@v2..v9": { + "anyOf": [ + { + "type": "string", + "description": "An optional string specifying a file with the command line syntax for --tests-information. Available in version 2 and higher." + }, + { + "type": "object", + "description": "An optional object specifying test preset filters. Available in version 2 and higher.", + "unevaluatedProperties": false, + "properties": { + "start": { + "$ref": "#/definitions/testPresets.filter.include.index.start@v2.." + }, + "end": { + "$ref": "#/definitions/testPresets.filter.include.index.end@v2.." + }, + "stride": { + "$ref": "#/definitions/testPresets.filter.include.index.stride@v2.." + }, + "specificTests": { + "$ref": "#/definitions/testPresets.filter.include.index.specificTests@v2.." + } + } + } + ] + }, + "testPresets.filter.include.index@v10..": { + "anyOf": [ + { + "type": "string", + "description": "An optional string specifying a file with the command line syntax for --tests-information. Available in version 2 and higher." + }, + { + "type": "object", + "description": "An optional object specifying test preset filters. Available in version 2 and higher.", + "unevaluatedProperties": false, + "properties": { + "start": { + "$ref": "#/definitions/testPresets.filter.include.index.start@v2.." + }, + "end": { + "$ref": "#/definitions/testPresets.filter.include.index.end@v2.." + }, + "stride": { + "$ref": "#/definitions/testPresets.filter.include.index.stride@v2.." + }, + "specificTests": { + "$ref": "#/definitions/testPresets.filter.include.index.specificTests@v2.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + } + ] + }, + "testPresets.filter.include.index.start@v2..": { + "type": "integer", + "description": "An optional integer specifying a test index at which to start testing." + }, + "testPresets.filter.include.index.end@v2..": { + "type": "integer", + "description": "An optional integer specifying a test index at which to stop testing." + }, + "testPresets.filter.include.index.stride@v2..": { + "type": "integer", + "description": "An optional integer specifying the increment." + }, + "testPresets.filter.include.index.specificTests@v2..": { + "type": "array", + "description": "An optional array of integers specifying specific test indices to run.", + "items": { + "type": "integer", + "description": "An integer specifying the test to run by index." + } + }, + "testPresets.filter.exclude@v2..v9": { + "type": "object", + "description": "An optional object specifying which tests to exclude.", + "properties": { + "name": { + "$ref": "#/definitions/testPresets.filter.exclude.name@v2.." + }, + "label": { + "$ref": "#/definitions/testPresets.filter.exclude.label@v2.." + }, + "fixtures": { + "$ref": "#/definitions/testPresets.filter.exclude.fixtures@v2..v9" + } + } + }, + "testPresets.filter.exclude@v10..": { + "type": "object", + "description": "An optional object specifying which tests to exclude.", + "properties": { + "name": { + "$ref": "#/definitions/testPresets.filter.exclude.name@v2.." + }, + "label": { + "$ref": "#/definitions/testPresets.filter.exclude.label@v2.." + }, + "fixtures": { + "$ref": "#/definitions/testPresets.filter.exclude.fixtures@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "testPresets.filter.exclude.name@v2..": { + "type": "string", + "description": "An optional string specifying a regex for test names. Equivalent to passing --exclude-regex on the command line." + }, + "testPresets.filter.exclude.label@v2..": { + "type": "string", + "description": "An optional string specifying a regex for test labels. Equivalent to passing --label-exclude on the command line." + }, + "testPresets.filter.exclude.fixtures@v2..v9": { + "type": "object", + "description": "An optional object specifying which fixtures to exclude from adding tests.", + "properties": { + "any": { + "$ref": "#/definitions/testPresets.filter.exclude.fixtures.any@v2.." + }, + "setup": { + "$ref": "#/definitions/testPresets.filter.exclude.fixtures.setup@v2.." + }, + "cleanup": { + "$ref": "#/definitions/testPresets.filter.exclude.fixtures.cleanup@v2.." + } + } + }, + "testPresets.filter.exclude.fixtures@v10..": { + "type": "object", + "description": "An optional object specifying which fixtures to exclude from adding tests.", + "properties": { + "any": { + "$ref": "#/definitions/testPresets.filter.exclude.fixtures.any@v2.." + }, + "setup": { + "$ref": "#/definitions/testPresets.filter.exclude.fixtures.setup@v2.." + }, + "cleanup": { + "$ref": "#/definitions/testPresets.filter.exclude.fixtures.cleanup@v2.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "testPresets.filter.exclude.fixtures.any@v2..": { + "type": "string", + "description": "An optional string specifying a regex for text fixtures to exclude from adding any tests. Equivalent to passing --fixture-exclude-any on the command line." + }, + "testPresets.filter.exclude.fixtures.setup@v2..": { + "type": "string", + "description": "An optional string specifying a regex for text fixtures to exclude from adding setup tests. Equivalent to passing --fixture-exclude-setup on the command line." + }, + "testPresets.filter.exclude.fixtures.cleanup@v2..": { + "type": "string", + "description": "An optional string specifying a regex for text fixtures to exclude from adding cleanup tests. Equivalent to passing --fixture-exclude-cleanup on the command line." + }, + "testPresets.execution@v2..v9": { + "type": "object", + "description": "An optional object specifying options for test execution.", + "unevaluatedProperties": false, + "properties": { + "stopOnFailure": { + "$ref": "#/definitions/testPresets.execution.stopOnFailure@v2.." + }, + "enableFailover": { + "$ref": "#/definitions/testPresets.execution.enableFailover@v2.." + }, + "jobs": { + "$ref": "#/definitions/testPresets.execution.jobs@v2..v10" + }, + "resourceSpecFile": { + "$ref": "#/definitions/testPresets.execution.resourceSpecFile@v2.." + }, + "testLoad": { + "$ref": "#/definitions/testPresets.execution.testLoad@v2.." + }, + "showOnly": { + "$ref": "#/definitions/testPresets.execution.showOnly@v2.." + }, + "repeat": { + "$ref": "#/definitions/testPresets.execution.repeat@v2..v9" + }, + "interactiveDebugging": { + "$ref": "#/definitions/testPresets.execution.interactiveDebugging@v2.." + }, + "scheduleRandom": { + "$ref": "#/definitions/testPresets.execution.scheduleRandom@v2.." + }, + "timeout": { + "$ref": "#/definitions/testPresets.execution.timeout@v2.." + }, + "noTestsAction": { + "$ref": "#/definitions/testPresets.execution.noTestsAction@v2.." + } + } + }, + "testPresets.execution@v10": { + "type": "object", + "description": "An optional object specifying options for test execution.", + "unevaluatedProperties": false, + "properties": { + "stopOnFailure": { + "$ref": "#/definitions/testPresets.execution.stopOnFailure@v2.." + }, + "enableFailover": { + "$ref": "#/definitions/testPresets.execution.enableFailover@v2.." + }, + "jobs": { + "$ref": "#/definitions/testPresets.execution.jobs@v2..v10" + }, + "resourceSpecFile": { + "$ref": "#/definitions/testPresets.execution.resourceSpecFile@v2.." + }, + "testLoad": { + "$ref": "#/definitions/testPresets.execution.testLoad@v2.." + }, + "showOnly": { + "$ref": "#/definitions/testPresets.execution.showOnly@v2.." + }, + "repeat": { + "$ref": "#/definitions/testPresets.execution.repeat@v10.." + }, + "interactiveDebugging": { + "$ref": "#/definitions/testPresets.execution.interactiveDebugging@v2.." + }, + "scheduleRandom": { + "$ref": "#/definitions/testPresets.execution.scheduleRandom@v2.." + }, + "timeout": { + "$ref": "#/definitions/testPresets.execution.timeout@v2.." + }, + "noTestsAction": { + "$ref": "#/definitions/testPresets.execution.noTestsAction@v2.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "testPresets.execution@v11": { + "type": "object", + "description": "An optional object specifying options for test execution.", + "unevaluatedProperties": false, + "properties": { + "stopOnFailure": { + "$ref": "#/definitions/testPresets.execution.stopOnFailure@v2.." + }, + "enableFailover": { + "$ref": "#/definitions/testPresets.execution.enableFailover@v2.." + }, + "jobs": { + "$ref": "#/definitions/testPresets.execution.jobs@v11.." + }, + "resourceSpecFile": { + "$ref": "#/definitions/testPresets.execution.resourceSpecFile@v2.." + }, + "testLoad": { + "$ref": "#/definitions/testPresets.execution.testLoad@v2.." + }, + "showOnly": { + "$ref": "#/definitions/testPresets.execution.showOnly@v2.." + }, + "repeat": { + "$ref": "#/definitions/testPresets.execution.repeat@v10.." + }, + "interactiveDebugging": { + "$ref": "#/definitions/testPresets.execution.interactiveDebugging@v2.." + }, + "scheduleRandom": { + "$ref": "#/definitions/testPresets.execution.scheduleRandom@v2.." + }, + "timeout": { + "$ref": "#/definitions/testPresets.execution.timeout@v2.." + }, + "noTestsAction": { + "$ref": "#/definitions/testPresets.execution.noTestsAction@v2.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "testPresets.execution@v12": { + "type": "object", + "description": "An optional object specifying options for test execution.", + "unevaluatedProperties": false, + "properties": { + "stopOnFailure": { + "$ref": "#/definitions/testPresets.execution.stopOnFailure@v2.." + }, + "enableFailover": { + "$ref": "#/definitions/testPresets.execution.enableFailover@v2.." + }, + "jobs": { + "$ref": "#/definitions/testPresets.execution.jobs@v11.." + }, + "resourceSpecFile": { + "$ref": "#/definitions/testPresets.execution.resourceSpecFile@v2.." + }, + "testLoad": { + "$ref": "#/definitions/testPresets.execution.testLoad@v2.." + }, + "showOnly": { + "$ref": "#/definitions/testPresets.execution.showOnly@v2.." + }, + "repeat": { + "$ref": "#/definitions/testPresets.execution.repeat@v10.." + }, + "interactiveDebugging": { + "$ref": "#/definitions/testPresets.execution.interactiveDebugging@v2.." + }, + "scheduleRandom": { + "$ref": "#/definitions/testPresets.execution.scheduleRandom@v2.." + }, + "timeout": { + "$ref": "#/definitions/testPresets.execution.timeout@v2.." + }, + "noTestsAction": { + "$ref": "#/definitions/testPresets.execution.noTestsAction@v2.." + }, + "testPassthroughArguments": { + "$ref": "#/definitions/testPresets.execution.testPassthroughArguments@v12" + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "testPresets.execution.stopOnFailure@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --stop-on-failure on the command line." + }, + "testPresets.execution.enableFailover@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing -F on the command line." + }, + "testPresets.execution.jobs@v2..v10": { + "minimum": 0, + "type": "integer", + "description": "An optional non-negative integer. Equivalent to passing --parallel or -j on the command line." + }, + "testPresets.execution.jobs@v11..": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "description": "An optional non-negative integer. Equivalent to passing --parallel or -j on the command line." + }, + { + "type": "string", + "minLength": 0, + "maxLength": 0, + "description": "An optional string. Equivalent to passing --parallel or -j on the command line with the number of jobs omitted." + } + ] + }, + "testPresets.execution.resourceSpecFile@v2..": { + "type": "string", + "description": "An optional string. Equivalent to passing --resource-spec-file on the command line." + }, + "testPresets.execution.testLoad@v2..": { + "type": "integer", + "description": "An optional integer. Equivalent to passing --test-load on the command line." + }, + "testPresets.execution.showOnly@v2..": { + "type": "string", + "enum": [ + "human", + "json-v1" + ], + "description": "An optional string. Equivalent to passing --show-only on the command line. Value must be \"human\" or \"json-v1\"." + }, + "testPresets.execution.repeat@v2..v9": { + "type": "object", + "description": "An optional object specifying how to repeat tests. Equivalent to passing --repeat on the command line.", + "unevaluatedProperties": false, + "properties": { + "mode": { + "$ref": "#/definitions/testPresets.execution.repeat.mode@v2.." + }, + "count": { + "$ref": "#/definitions/testPresets.execution.repeat.count@v2.." + } + }, + "required": [ + "mode", + "count" + ] + }, + "testPresets.execution.repeat@v10..": { + "type": "object", + "description": "An optional object specifying how to repeat tests. Equivalent to passing --repeat on the command line.", + "unevaluatedProperties": false, + "properties": { + "mode": { + "$ref": "#/definitions/testPresets.execution.repeat.mode@v2.." + }, + "count": { + "$ref": "#/definitions/testPresets.execution.repeat.count@v2.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "mode", + "count" + ] + }, + "testPresets.execution.repeat.mode@v2..": { + "type": "string", + "enum": [ + "until-fail", + "until-pass", + "after-timeout" + ], + "description": "A required string. Must be one of the following values: \"until-fail\", \"until-pass\", or \"after-timeout\"." + }, + "testPresets.execution.repeat.count@v2..": { + "type": "integer", + "description": "A required integer." + }, + "testPresets.execution.interactiveDebugging@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --interactive-debug-mode 1 on the command line. If false, equivalent to passing --interactive-debug-mode 0 on the command line." + }, + "testPresets.execution.scheduleRandom@v2..": { + "type": "boolean", + "description": "An optional boolean. If true, equivalent to passing --schedule-random on the command line." + }, + "testPresets.execution.timeout@v2..": { + "type": "integer", + "description": "An optional integer. Equivalent to passing --timeout on the command line." + }, + "testPresets.execution.noTestsAction@v2..": { + "type": "string", + "enum": [ + "default", + "error", + "ignore" + ], + "description": "An optional string specifying the behavior if no tests are found. Must be one of the following values: \"default\" (equivalent to not passing any value on the command line), \"error\" (equivalent to passing --no-tests=error on the command line), or \"ignore\" (equivalent to passing --no-tests-ignore on the command line)." + }, + "testPresets.execution.testPassthroughArguments@v12": { + "type": "array", + "description": "An optional array of strings. Each element is forwarded as an argument to every test executable. Equivalent to passing arguments after -- on the ctest command line.", + "items": { + "type": "string" + } + }, + "packagePresets@v6..v9": { + "type": "array", + "description": "An optional array of package preset objects. Used to specify arguments to cpack.", + "items": { + "type": "object", + "description": "A package preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/packagePresets.name@v6.." + }, + "hidden": { + "$ref": "#/definitions/packagePresets.hidden@v6.." + }, + "inherits": { + "$ref": "#/definitions/packagePresets.inherits@v6.." + }, + "condition": { + "$ref": "#/definitions/packagePresets.condition@v6..v9" + }, + "vendor": { + "$ref": "#/definitions/packagePresets.vendor@v6.." + }, + "displayName": { + "$ref": "#/definitions/packagePresets.displayName@v6.." + }, + "description": { + "$ref": "#/definitions/packagePresets.description@v6.." + }, + "environment": { + "$ref": "#/definitions/packagePresets.environment@v6.." + }, + "configurePreset": { + "$ref": "#/definitions/packagePresets.configurePreset@v6.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/packagePresets.inheritConfigureEnvironment@v6.." + }, + "generators": { + "$ref": "#/definitions/packagePresets.generators@v6.." + }, + "configurations": { + "$ref": "#/definitions/packagePresets.configurations@v6.." + }, + "variables": { + "$ref": "#/definitions/packagePresets.variables@v6.." + }, + "configFile": { + "$ref": "#/definitions/packagePresets.configFile@v6.." + }, + "output": { + "$ref": "#/definitions/packagePresets.output@v6..v9" + }, + "packageName": { + "$ref": "#/definitions/packagePresets.packageName@v6.." + }, + "packageVersion": { + "$ref": "#/definitions/packagePresets.packageVersion@v6.." + }, + "packageDirectory": { + "$ref": "#/definitions/packagePresets.packageDirectory@v6.." + }, + "vendorName": { + "$ref": "#/definitions/packagePresets.vendorName@v6.." + } + }, + "required": [ + "name" + ] + } + }, + "packagePresets@v10..": { + "type": "array", + "description": "An optional array of package preset objects. Used to specify arguments to cpack.", + "items": { + "type": "object", + "description": "A package preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/packagePresets.name@v6.." + }, + "hidden": { + "$ref": "#/definitions/packagePresets.hidden@v6.." + }, + "inherits": { + "$ref": "#/definitions/packagePresets.inherits@v6.." + }, + "condition": { + "$ref": "#/definitions/packagePresets.condition@v10.." + }, + "vendor": { + "$ref": "#/definitions/packagePresets.vendor@v6.." + }, + "displayName": { + "$ref": "#/definitions/packagePresets.displayName@v6.." + }, + "description": { + "$ref": "#/definitions/packagePresets.description@v6.." + }, + "environment": { + "$ref": "#/definitions/packagePresets.environment@v6.." + }, + "configurePreset": { + "$ref": "#/definitions/packagePresets.configurePreset@v6.." + }, + "inheritConfigureEnvironment": { + "$ref": "#/definitions/packagePresets.inheritConfigureEnvironment@v6.." + }, + "generators": { + "$ref": "#/definitions/packagePresets.generators@v6.." + }, + "configurations": { + "$ref": "#/definitions/packagePresets.configurations@v6.." + }, + "variables": { + "$ref": "#/definitions/packagePresets.variables@v6.." + }, + "configFile": { + "$ref": "#/definitions/packagePresets.configFile@v6.." + }, + "output": { + "$ref": "#/definitions/packagePresets.output@v10.." + }, + "packageName": { + "$ref": "#/definitions/packagePresets.packageName@v6.." + }, + "packageVersion": { + "$ref": "#/definitions/packagePresets.packageVersion@v6.." + }, + "packageDirectory": { + "$ref": "#/definitions/packagePresets.packageDirectory@v6.." + }, + "vendorName": { + "$ref": "#/definitions/packagePresets.vendorName@v6.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "name" + ] + } + }, + "packagePresets.name@v6..": { + "type": "string", + "minLength": 1, + "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the cpack --preset option. There must not be two package presets in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name. However, a package preset may have the same name as a configure, build, test, or workflow preset." + }, + "packagePresets.hidden@v6..": { + "type": "boolean", + "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. hidden presets are intended to be used as a base for other presets to inherit via the inherits field." + }, + "packagePresets.inherits@v6..": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "description": "An optional string representing the name of the preset from which to inherit." + }, + { + "type": "array", + "description": "An optional array of strings representing the names of presets from which to inherit. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.", + "items": { + "type": "string", + "minLength": 1, + "description": "An optional string representing the name of the preset from which to inherit." + } + } + ] + }, + "packagePresets.condition@v6..v9": { + "anyOf": [ + { + "$ref": "#/definitions/condition@v3..v9" + }, + { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + } + ] + }, + "packagePresets.condition@v10..": { + "anyOf": [ + { + "$ref": "#/definitions/condition@v10.." + }, + { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + } + ] + }, + "packagePresets.vendor@v6..": { + "type": "object", + "minLength": 1, + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field.", + "properties": {} + }, + "packagePresets.displayName@v6..": { + "type": "string", + "description": "An optional string with a human-friendly name of the preset." + }, + "packagePresets.description@v6..": { + "type": "string", + "description": "An optional string with a human-friendly description of the preset." + }, + "packagePresets.environment@v6..": { + "type": "object", + "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.", + "propertyNames": { + "pattern": "^.+$" + }, + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "string", + "description": "A string representing the value of the variable." + } + ] + } + }, + "packagePresets.configurePreset@v6..": { + "type": "string", + "minLength": 1, + "description": "An optional string specifying the name of a configure preset to associate with this package preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build tree directory is inferred from the configure preset." + }, + "packagePresets.inheritConfigureEnvironment@v6..": { + "type": "boolean", + "description": "An optional boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited package preset environments, but before environment variables explicitly specified in this package preset." + }, + "packagePresets.generators@v6..": { + "type": "array", + "description": "An optional array of strings representing generators for CPack to use.", + "items": { + "type": "string", + "description": "An optional string representing the name of a CPack generator to use." + } + }, + "packagePresets.configurations@v6..": { + "type": "array", + "description": "An optional array of strings representing build configurations for CPack to package.", + "items": { + "type": "string", + "description": "An optional string representing the name of a configuration to package." + } + }, + "packagePresets.variables@v6..": { + "type": "object", + "description": "An optional map of variables to pass to CPack, equivalent to -D arguments. Each key is the name of a variable, and the value is the string to assign to that variable.", + "propertyNames": { + "pattern": "^.+$" + }, + "properties": {}, + "additionalProperties": { + "type": "string", + "description": "An optional string representing the value of the variable." + } + }, + "packagePresets.configFile@v6..": { + "type": "string", + "description": "An optional string representing the config file for CPack to use." + }, + "packagePresets.output@v6..v9": { + "type": "object", + "description": "An optional object specifying output options.", + "unevaluatedProperties": false, + "properties": { + "debug": { + "$ref": "#/definitions/packagePresets.output.debug@v6.." + }, + "verbose": { + "$ref": "#/definitions/packagePresets.output.verbose@v6.." + } + } + }, + "packagePresets.output@v10..": { + "type": "object", + "description": "An optional object specifying output options.", + "unevaluatedProperties": false, + "properties": { + "debug": { + "$ref": "#/definitions/packagePresets.output.debug@v6.." + }, + "verbose": { + "$ref": "#/definitions/packagePresets.output.verbose@v6.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + }, + "packagePresets.output.debug@v6..": { + "type": "boolean", + "description": "An optional boolean specifying whether or not to print debug information. A value of true is equivalent to passing --debug on the command line." + }, + "packagePresets.output.verbose@v6..": { + "type": "boolean", + "description": "An optional boolean specifying whether or not to print verbosely. A value of true is equivalent to passing --verbose on the command line." + }, + "packagePresets.packageName@v6..": { + "type": "string", + "description": "An optional string representing the package name." + }, + "packagePresets.packageVersion@v6..": { + "type": "string", + "description": "An optional string representing the package version." + }, + "packagePresets.packageDirectory@v6..": { + "type": "string", + "description": "An optional string representing the directory in which to place the package." + }, + "packagePresets.vendorName@v6..": { + "type": "string", + "description": "An optional string representing the vendor name." + }, + "workflowPresets@v6..v9": { + "type": "array", + "description": "An optional array of workflow preset objects. Used to execute configure, build, test, and package presets in order.", + "items": { + "type": "object", + "description": "A workflow preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/workflowPresets.name@v6.." + }, + "vendor": { + "$ref": "#/definitions/workflowPresets.vendor@v6.." + }, + "displayName": { + "$ref": "#/definitions/workflowPresets.displayName@v6.." + }, + "description": { + "$ref": "#/definitions/workflowPresets.description@v6.." + }, + "steps": { + "$ref": "#/definitions/workflowPresets.steps@v6..v9" + } + }, + "required": [ + "name", + "steps" + ] + } + }, + "workflowPresets@v10..": { + "type": "array", + "description": "An optional array of workflow preset objects. Used to execute configure, build, test, and package presets in order.", + "items": { + "type": "object", + "description": "A workflow preset object.", + "unevaluatedProperties": false, + "properties": { + "name": { + "$ref": "#/definitions/workflowPresets.name@v6.." + }, + "vendor": { + "$ref": "#/definitions/workflowPresets.vendor@v6.." + }, + "displayName": { + "$ref": "#/definitions/workflowPresets.displayName@v6.." + }, + "description": { + "$ref": "#/definitions/workflowPresets.description@v6.." + }, + "steps": { + "$ref": "#/definitions/workflowPresets.steps@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "name", + "steps" + ] + } + }, + "workflowPresets.name@v6..": { + "type": "string", + "minLength": 1, + "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the cmake --workflow --preset option. There must not be two presets (configure, build, test, package, or workflow) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name. However, a workflow preset may have the same name as a configure, build, test, or package preset." + }, + "workflowPresets.vendor@v6..": { + "type": "object", + "minLength": 1, + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field.", + "properties": {} + }, + "workflowPresets.displayName@v6..": { + "type": "string", + "description": "An optional string with a human-friendly name of the preset." + }, + "workflowPresets.description@v6..": { + "type": "string", + "description": "An optional string with a human-friendly description of the preset." + }, + "workflowPresets.steps@v6..v9": { + "type": "array", + "description": "A required array of objects describing the steps of the workflow. The first step must be a configure preset, and all subsequent steps must be non-configure presets whose configurePreset field matches the starting configure preset.", + "items": { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/workflowPresets.steps.type@v6.." + }, + "name": { + "$ref": "#/definitions/workflowPresets.steps.name@v6.." + } + } + } + }, + "workflowPresets.steps@v10..": { + "type": "array", + "description": "A required array of objects describing the steps of the workflow. The first step must be a configure preset, and all subsequent steps must be non-configure presets whose configurePreset field matches the starting configure preset.", + "items": { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/workflowPresets.steps.type@v6.." + }, + "name": { + "$ref": "#/definitions/workflowPresets.steps.name@v6.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + } + } + }, + "workflowPresets.steps.type@v6..": { + "type": "string", + "enum": [ + "configure", + "build", + "test", + "package" + ], + "description": "A required string. The first step must be configure. Subsequent steps must be either build, test, or package." + }, + "workflowPresets.steps.name@v6..": { + "type": "string", + "minLength": 1, + "description": "A required string representing the name of the configure, build, test, or package preset to run as this workflow step." + }, + "condition[1].type@v3..": { + "type": "string", + "const": "const", + "description": "A required string specifying the type of the condition." + }, + "condition[1].value@v3..": { + "type": "boolean", + "description": "A required boolean which provides a constant value for the condition's evaluation." + }, + "condition[2].type@v3..": { + "type": "string", + "enum": [ + "equals", + "notEquals" + ], + "description": "A required string specifying the type of the condition." + }, + "condition[2].lhs@v3..": { + "type": "string", + "description": "First string to compare. This field supports macro expansion." + }, + "condition[2].rhs@v3..": { + "type": "string", + "description": "Second string to compare. This field supports macro expansion." + }, + "condition[3].type@v3..": { + "type": "string", + "enum": [ + "inList", + "notInList" + ], + "description": "A required string specifying the type of the condition." + }, + "condition[3].string@v3..": { + "type": "string", + "description": "A required string to search for. This field supports macro expansion." + }, + "condition[3].list@v3..": { + "type": "array", + "description": "A required array of strings to search. This field supports macro expansion, and uses short-circuit evaluation.", + "items": { + "type": "string" + } + }, + "condition[4].type@v3..": { + "type": "string", + "enum": [ + "matches", + "notMatches" + ], + "description": "A required string specifying the type of the condition." + }, + "condition[4].string@v3..": { + "type": "string", + "description": "A required string to search. This field supports macro expansion." + }, + "condition[4].regex@v3..": { + "type": "string", + "description": "A required regular expression to search for. This field supports macro expansion." + }, + "condition[5].type@v3..": { + "type": "string", + "enum": [ + "anyOf", + "allOf" + ], + "description": "A required string specifying the type of the condition." + }, + "condition[5].conditions@v3..v9": { + "type": "array", + "description": "A required array of condition objects. These conditions use short-circuit evaluation.", + "items": { + "$ref": "#/definitions/condition@v3..v9" + } + }, + "condition[5].conditions@v10..": { + "type": "array", + "description": "A required array of condition objects. These conditions use short-circuit evaluation.", + "items": { + "$ref": "#/definitions/condition@v10.." + } + }, + "condition[6].type@v3..": { + "type": "string", + "const": "not", + "description": "A required string specifying the type of the condition." + }, + "condition[6].condition@v3..v9": { + "$ref": "#/definitions/condition@v3..v9" + }, + "condition[6].condition@v10..": { + "$ref": "#/definitions/condition@v10.." + }, + "condition@v3..v9": { + "anyOf": [ + { + "type": "boolean", + "description": "A boolean which provides a constant value for the condition's evaluation." + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[1].type@v3.." + }, + "value": { + "$ref": "#/definitions/condition[1].value@v3.." + } + }, + "required": [ + "type", + "value" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[2].type@v3.." + }, + "lhs": { + "$ref": "#/definitions/condition[2].lhs@v3.." + }, + "rhs": { + "$ref": "#/definitions/condition[2].rhs@v3.." + } + }, + "required": [ + "type", + "lhs", + "rhs" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[3].type@v3.." + }, + "string": { + "$ref": "#/definitions/condition[3].string@v3.." + }, + "list": { + "$ref": "#/definitions/condition[3].list@v3.." + } + }, + "required": [ + "type", + "string", + "list" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[4].type@v3.." + }, + "string": { + "$ref": "#/definitions/condition[4].string@v3.." + }, + "regex": { + "$ref": "#/definitions/condition[4].regex@v3.." + } + }, + "required": [ + "type", + "string", + "regex" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[5].type@v3.." + }, + "conditions": { + "$ref": "#/definitions/condition[5].conditions@v3..v9" + } + }, + "required": [ + "type", + "conditions" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[6].type@v3.." + }, + "condition": { + "$ref": "#/definitions/condition[6].condition@v3..v9" + } + }, + "required": [ + "type", + "condition" + ] + } + ] + }, + "condition@v10..": { + "anyOf": [ + { + "type": "boolean", + "description": "A boolean which provides a constant value for the condition's evaluation." + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[1].type@v3.." + }, + "value": { + "$ref": "#/definitions/condition[1].value@v3.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "type", + "value" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[2].type@v3.." + }, + "lhs": { + "$ref": "#/definitions/condition[2].lhs@v3.." + }, + "rhs": { + "$ref": "#/definitions/condition[2].rhs@v3.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "type", + "lhs", + "rhs" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[3].type@v3.." + }, + "string": { + "$ref": "#/definitions/condition[3].string@v3.." + }, + "list": { + "$ref": "#/definitions/condition[3].list@v3.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "type", + "string", + "list" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[4].type@v3.." + }, + "string": { + "$ref": "#/definitions/condition[4].string@v3.." + }, + "regex": { + "$ref": "#/definitions/condition[4].regex@v3.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "type", + "string", + "regex" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[5].type@v3.." + }, + "conditions": { + "$ref": "#/definitions/condition[5].conditions@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "type", + "conditions" + ] + }, + { + "type": "object", + "unevaluatedProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/condition[6].type@v3.." + }, + "condition": { + "$ref": "#/definitions/condition[6].condition@v10.." + }, + "$comment": { + "$ref": "#/definitions/$comment@v10.." + } + }, + "required": [ + "type", + "condition" + ] + } + ] } } }
diff --git a/Help/manual/presets/schema.yaml b/Help/manual/presets/schema.yaml new file mode 100644 index 0000000..7650fa4 --- /dev/null +++ b/Help/manual/presets/schema.yaml
@@ -0,0 +1,2039 @@ +--- +version: 12 +types: + - id: condition + type: variant + items: + - type: boolean + description: + A boolean which provides a constant value for the condition's + evaluation. + # 'const' condition + - type: object + unevaluatedProperties: false + properties: + type: + type: string + required: true + const: const + description: + A required string specifying the type of the condition. + value: + type: boolean + required: true + description: + A required boolean which provides a constant value for the + condition's evaluation. + # 'equals', 'notEquals' condition + - type: object + unevaluatedProperties: false + properties: + type: + type: string + required: true + enum: + - equals + - notEquals + description: + A required string specifying the type of the condition. + lhs: + type: string + required: true + description: + First string to compare. This field supports macro expansion. + rhs: + type: string + required: true + description: + Second string to compare. This field supports macro expansion. + # 'inList', 'notInList' condition + - type: object + properties: + type: + type: string + required: true + enum: + - inList + - notInList + description: + A required string specifying the type of the condition. + string: + type: string + required: true + description: + A required string to search for. This field supports macro + expansion. + list: + type: array + required: true + items: + type: string + description: + A required array of strings to search. This field supports macro + expansion, and uses short-circuit evaluation. + unevaluatedProperties: false + # 'matches', 'notMatches' condition + - type: object + properties: + type: + type: string + required: true + enum: + - matches + - notMatches + description: + A required string specifying the type of the condition. + string: + type: string + required: true + description: + A required string to search. This field supports macro expansion. + regex: + type: string + required: true + description: + A required regular expression to search for. This field supports + macro expansion. + unevaluatedProperties: false + # 'anyOf', 'allOf' condition + - type: object + properties: + type: + type: string + required: true + enum: + - anyOf + - allOf + description: + A required string specifying the type of the condition. + conditions: + type: array + required: true + description: + A required array of condition objects. These conditions use + short-circuit evaluation. + items: + type: ref + target: condition + unevaluatedProperties: false + # 'not' condition + - type: object + properties: + type: + type: string + required: true + const: not + description: + A required string specifying the type of the condition. + condition: + type: ref + required: true + target: condition + unevaluatedProperties: false +definitions: + inherits: &preset-inherits + anyOf: + - type: string + minLength: 1 + description: + An optional string representing the name of the preset from which to + inherit. + - type: array + description: + An optional array of strings representing the names of presets from + which to inherit. The preset will inherit all of the fields from the + inherits presets by default (except name, hidden, inherits, + description, and displayName), but can override them as desired. If + multiple inherits presets provide conflicting values for the same + field, the earlier preset in the inherits list will be preferred. + Presets in CMakePresets.json must not inherit from presets in + CMakeUserPresets.json. + items: + type: string + minLength: 1 + description: + An optional string representing the name of the preset from which + to inherit. + sphinxDescription: | + An optional array of strings representing the names of presets from which + to inherit. This field can also be a string, which is equivalent to an + array containing one string. + + The preset will inherit all of the fields from the ``inherits`` presets + by default (except ``name``, ``hidden``, ``inherits``, ``description``, + and ``displayName``), but can override them as desired. If multiple + ``inherits`` presets provide conflicting values for the same field, the + earlier preset in the ``inherits`` array will be preferred. + + A preset can only inherit from another preset that is defined in the same + file or in one of the files it includes (directly or indirectly). Presets + in ``CMakePresets.json`` may not inherit from presets in + ``CMakeUserPresets.json``. + vendor: &preset-vendor + type: object + minLength: 1 + description: + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, it should follow the same conventions as the + root-level vendor field. + sphinxDescription: | + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. + properties: {} + displayName: &preset-displayName + type: string + description: + An optional string with a human-friendly name of the preset. + description: &preset-description + type: string + description: + An optional string with a human-friendly description of the preset. + environment: &preset-environment + type: object + description: &environment-description + An optional map of environment variables. The key is the variable name + (which must not be an empty string). Each variable is set regardless of + whether or not a value was given to it by the process's environment. This + field supports macro expansion, and environment variables in this map may + reference each other, and may be listed in any order, as long as such + references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, + ENV_2 may not be $env{ENV_1}.) Environment variables are inherited + through the inherits field, and the preset's environment will be the + union of its own environment and the environment from all its parents. If + multiple presets in this union define the same variable, the standard + rules of inherits are applied. Setting a variable to null causes it to + not be set, even if a value was inherited from another preset. + sphinxDescription: &environment-sphinx-description | + An optional map of environment variables. The key is the variable name + (which may not be an empty string), and the value is either ``null`` or a + string representing the value of the variable. Each variable is set + regardless of whether or not a value was given to it by the process's + environment. + + This field supports `macro expansion`_, and environment variables in this + map may reference each other, and may be listed in any order, as long as + such references do not cause a cycle (for example, if ``ENV_1`` is + ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables + by accessing only values from the parent environment. + + Environment variables are inherited through the ``inherits`` field, and + the preset's environment will be the union of its own ``environment`` and + the ``environment`` from all its parents. If multiple presets in this + union define the same variable, the standard rules of ``inherits`` are + applied. Setting a variable to ``null`` causes it to not be set, even if + a value was inherited from another preset. + properties: {} + propertyNames: + pattern: '^.+$' + additionalProperties: &environment-additional-properties + anyOf: + - type: 'null' + description: + Setting a variable to null causes it to not be set, even if a value + was inherited from another preset. + - type: string + description: + A string representing the value of the variable. + architecture-or-toolset: &architecture-or-toolset-properties + value: + type: string + description: An optional string representing the value. + strategy: + type: string + enum: + - set + - external + description: ' + An optional string telling CMake how to handle the field. Valid values + are: "set" Set the respective value. This will result in an error for + generators that do not support the respective field. "external" Do not + set the value, even if the generator supports it. This is useful if, + for example, a preset uses the Ninja generator, and an IDE knows how to + set up the Visual C++ environment from the architecture and toolset + fields. In that case, CMake will ignore the field, but the IDE can use + them to set up the environment before invoking CMake. + ' + sphinxDescription: | + An optional string telling CMake how to handle the field. + Valid values are: + + ``"set"`` + Set the respective value. This will result in an error for generators + that do not support the respective field. + + ``"external"`` + Do not set the value, even if the generator supports it. This is + useful if, for example, a preset uses the Ninja generator, and an IDE + knows how to set up the Visual C++ environment from the architecture + and toolset fields. In that case, CMake will ignore the field, but + the IDE can use them to set up the environment before invoking CMake. + + If no ``strategy`` field is given, or if the field uses the string form + rather than the object form, the behavior is the same as ``"set"``. + condition: &condition + since: 3 + anyOf: + - type: ref + target: condition + - type: 'null' + description: + Null indicates that the condition always evaluates to true and is not + inherited. + sphinxDescription: | + An optional `Condition`_ object. +description: + The presets specify the generator and the build directory, and optionally an + array of variables and other arguments to pass to CMake. +properties: + $schema: + since: 8 + type: string + format: uri-reference + description: + 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. + version: + type: integer + required: true + description: + A required integer representing the version of the JSON schema. + sphinxDescription: | + A required integer representing the version of the JSON schema. See + `Versions`_ for discussion of the supported versions and the + corresponding version of CMake in which they were added. + cmakeMinimumRequired: + type: object + description: + An optional object representing the minimum version of CMake + needed to build this project. + sphinxDescription: | + An optional object representing the minimum version of CMake + needed to build this project. This object consists of the following + fields: + + .. include:: presets/cmakeMinimumRequired-properties.rst + properties: + major: + type: integer + description: An optional integer representing the major version. + minor: + type: integer + description: An optional integer representing the minor version. + patch: + type: integer + description: An optional integer representing the patch version. + unevaluatedProperties: false + include: + since: 4 + type: array + items: + type: string + description: + An optional array of strings representing files to include. If the + filenames are not absolute, they are considered relative to the current + file. + sphinxDescription: | + An optional array of strings representing files to include. If the + filenames are not absolute, they are considered relative to the current + file. See `Includes`_ for discussion of the constraints on included + files. + vendor: + type: object + description: + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, the keys should be a vendor-specific domain name + followed by a /-separated path. For example, the Example IDE 1.0 could + use example.com/ExampleIDE/1.0. The value of each field can be anything + desired by the vendor, though will typically be a map. + sphinxDescription: | + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, the keys should be a vendor-specific domain name + followed by a ``/``-separated path. For example, the Example IDE 1.0 + could use ``example.com/ExampleIDE/1.0``. The value of each field can be + anything desired by the vendor, though will typically be a map. + properties: {} + configurePresets: + type: array + description: + An optional array of configure preset objects. + sphinxDescription: | + An optional array of `Configure Preset`_ objects. + items: + type: object + description: + A configure preset object. + properties: + name: + type: string + required: true + minLength: 1 + description: + A required string representing the machine-friendly name of the + preset. This identifier is used in the cmake --preset option. + There must not be two configure presets in the union of + CMakePresets.json and CMakeUserPresets.json in the same directory + with the same name. However, a configure preset may have the same + name as a build, test, package, or workflow preset. + sphinxDescription: | + A required string representing the machine-friendly name of the + preset. This identifier is used in the :option:`cmake --preset` + option. There must not be two configure presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a configure preset may have + the same name as a build, test, package, or workflow preset. + hidden: + type: boolean + description: + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the --preset= + argument, will not show up in the CMake GUI, and does not have to + have a valid generator or binaryDir, even from inheritance. Hidden + presets are intended to be used as a base for other presets to + inherit via the inherits field. + sphinxDescription: | + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the + :cmake-option:`--preset` argument, will not show up in the + :manual:`CMake GUI <cmake-gui(1)>`, and does not have to have a + valid ``generator`` or ``binaryDir``, even from inheritance. + ``hidden`` presets are intended to be used as a base for other + presets to inherit via the ``inherits`` field. + inherits: *preset-inherits + condition: *condition + vendor: *preset-vendor + displayName: *preset-displayName + description: *preset-description + generator: + revisions: + - until: 3 + type: string + description: + An optional string representing the generator to use for the + preset. If generator is not specified, it must be inherited + from the inherits preset (unless this preset is hidden). Note + that for Visual Studio generators, unlike in the command line + -G argument, you cannot include the platform name in the + generator name. Use the architecture field instead. + - since: 3 + type: string + description: + An optional string representing the generator to use for the + preset. If generator is not specified, the normal generator + discovery procedure is used. Note that for Visual Studio + generators, unlike in the command line -G argument, you cannot + include the platform name in the generator name. Use the + architecture field instead. + sphinxDescription: | + An optional string representing the + :manual:`generator <cmake-generators(7)>` to use for the preset. + + .. presets-versionchanged:: 3 + + If omitted, CMake will fall back to regular generator discovery + procedure. In previous versions, if not specified, this field + must be inherited from the ``inherits`` preset (unless this + preset is ``hidden``). + + Note that for :ref:`Visual Studio generators`, unlike in the + command line :cmake-option:`-G` argument, you cannot include the + platform name in the generator name. Use the ``architecture`` + field instead. + architecture: + anyOf: + - type: string + description: &architectureDescription + An optional string representing the platform for generators + that support it. + - type: object + description: *architectureDescription + properties: *architecture-or-toolset-properties + unevaluatedProperties: false + sphinxDescription: | + Optional field representing the platform for + :manual:`generators <cmake-generators(7)>` that support it. + + See :option:`cmake -A` for possible values. + + ``architecture`` may be either a string or an object with the + following fields: + + .. include:: presets/architecture-properties.rst + toolset: + anyOf: + - type: string + description: &toolsetDescription + An optional string representing the toolset for generators that + support it. + - type: object + description: *toolsetDescription + properties: *architecture-or-toolset-properties + unevaluatedProperties: false + sphinxDescription: | + Optional field representing the toolset for + :manual:`generators <cmake-generators(7)>` that support it. + + See :option:`cmake -T` for possible values. + + ``toolset`` may be either a string or an object with the following + fields: + + .. include:: presets/toolset-properties.rst + toolchainFile: + since: 3 + type: string + description: + An optional string representing the path to the toolchain file. + This field supports macro expansion. If a relative path is + specified, it is calculated relative to the build directory, and if + not found, relative to the source directory. + sphinxDescription: | + An optional string representing the path to the toolchain file. + This field supports `macro expansion`_. If a relative path is + specified, it is calculated relative to the build directory, and if + not found, relative to the source directory. This field takes + precedence over any :variable:`CMAKE_TOOLCHAIN_FILE` value. + graphviz: + since: 10 + type: string + description: + An optional string specifying the path to the graphviz dot file. + sphinxDescription: | + An optional string representing the path to the graphviz input + file, that will contain all the library and executable dependencies + in the project. See the documentation for + :option:`cmake --graphviz` for more details. + + This field supports `macro expansion`_. If a relative path is + specified, it is calculated relative to the current working + directory. + binaryDir: + revisions: + - until: 3 + type: string + description: + An optional string representing the path to the output binary + directory. This field supports macro expansion. If a relative + path is specified, it is calculated relative to the source + directory. If binaryDir is not specified, it must be inherited + from the inherits preset (unless this preset is hidden). + - since: 3 + type: string + description: + An optional string representing the path to the output binary + directory. This field supports macro expansion. If a relative + path is specified, it is calculated relative to the source + directory. If binaryDir is not specified, the path is + calculated using regular methods. + sphinxDescription: | + An optional string representing the path to the output binary + directory. This field supports `macro expansion`_. If a relative + path is specified, it is calculated relative to the source + directory. + + .. presets-versionchanged:: 3 + + If omitted, CMake will calculate the path using regular methods. + In previous versions, if not specified, this field must be + inherited from the ``inherits`` preset (unless this preset is + ``hidden``). + installDir: + since: 3 + type: string + description: + An optional string representing the path to the installation + directory, which will be used as the CMAKE_INSTALL_PREFIX variable. + This field supports macro expansion. If a relative path is + specified, it is calculated relative to the source directory. + sphinxDescription: | + An optional string representing the path to the installation + directory, which will be used as the + :variable:`CMAKE_INSTALL_PREFIX` variable. This field supports + `macro expansion`_. If a relative path is specified, it is + calculated relative to the source directory. + cmakeExecutable: + type: string + description: + An optional string representing the path to the CMake executable to + use for this preset. This is reserved for use by IDEs, and is not + used by CMake itself. IDEs that use this field should expand any + macros in it. + cacheVariables: + type: object + description: + An optional map of cache variables. The key is the variable name + (which must not be an empty string). Cache variables are inherited + through the inherits field, and the preset's variables will be the + union of its own cacheVariables and the cacheVariables from all its + parents. If multiple presets in this union define the same + variable, the standard rules of inherits are applied. + sphinxDescription: | + An optional map of cache variables. The key is the variable name + (which may not be an empty string), and the value is either + ``null``, a boolean (which is equivalent to a value of ``"TRUE"`` + or ``"FALSE"`` and a type of ``BOOL``), a string representing the + value of the variable (which supports `macro expansion`_), or an + object with the following fields: + + ``type`` + An optional string representing the type of the variable. + + ``value`` + A required string or boolean representing the value of the + variable. A boolean is equivalent to ``"TRUE"`` or ``"FALSE"``. + This field supports `macro expansion`_. + + Cache variables are inherited through the ``inherits`` field, and + the preset's variables will be the union of its own + ``cacheVariables`` and the ``cacheVariables`` from all its parents. + If multiple presets in this union define the same variable, the + standard rules of ``inherits`` are applied. Setting a variable to + ``null`` causes it to not be set, even if a value was inherited + from another preset. + properties: {} + propertyNames: + pattern: '^.+$' + additionalProperties: + anyOf: + - type: 'null' + description: + Setting a variable to null causes it to not be set, even if a + value was inherited from another preset. + - type: boolean + description: + A boolean representing the value of the variable. Equivalent + to "TRUE" or "FALSE" + - type: string + description: + A string representing the value of the variable (which + supports macro expansion). + - type: object + description: + An optional object representing the cache variables for + generators that support it. + properties: + type: + type: string + enum: + - BOOL + - FILEPATH + - PATH + - STRING + - INTERNAL + description: + An optional string representing the type of the variable. + It should be BOOL, FILEPATH, PATH, STRING, or INTERNAL. + value: + required: true + anyOf: + - type: boolean + description: + A boolean representing the value of the variable. + Equivalent to "TRUE" or "FALSE". + - type: string + description: + A string representing the value of the variable + (which supports macro expansion). + unevaluatedProperties: false + environment: *preset-environment + warnings: + type: object + description: + An optional object specifying the warnings to enable. + sphinxDescription: | + An optional object specifying the warnings to enable. + The object may contain the following fields: + + .. include:: presets/warnings-properties.rst + properties: + # This should ONLY have diagnostics whose definitions require + # special handling (i.e. anything from preset version < 12). Others + # will be generated from cmDiagnostics.h. + deprecated: + type: boolean + description: + An optional boolean. Equivalent to passing -Wdeprecated or + -Wno-deprecated on the command line. This may not be set + to false if errors.deprecated is set to true. + sphinxDescription: | + An optional boolean. Equivalent to passing + :option:`-Wdeprecated <cmake -W>` or + :option:`-Wno-deprecated <cmake -Wno->` + on the command line. + This may not be set to ``false`` + if ``errors.deprecated`` is set to ``true``. + dev: + until: 12 + type: boolean + description: + An optional boolean. Equivalent to passing -Wdev or + -Wno-dev on the command line. This may not be set + to false if errors.dev is set to true. + sphinxDescription: | + An optional boolean. Equivalent to passing + :option:`-Wdev <cmake -W>` or + :option:`-Wno-dev <cmake -Wno->` + on the command line. + This may not be set to ``false`` + if ``errors.dev`` is set to ``true``. + uninitialized: + type: boolean + description: + An optional boolean. Equivalent to passing -Wuninitialized or + -Wno-uninitialized on the command line. This may not be set + to false if errors.uninitialized is set to true. + sphinxDescription: | + An optional boolean. Equivalent to passing + :option:`-Wuninitialized <cmake -W>` or + :option:`-Wno-uninitialized <cmake -Wno->` + on the command line. + This may not be set to ``false`` + if ``errors.uninitialized`` is set to ``true``. + unusedCli: + type: boolean + description: + An optional boolean. Equivalent to passing -Wunused-cli or + -Wno-unused-cli on the command line. This may not be set + to false if errors.unusedCli is set to true. + sphinxDescription: | + An optional boolean. Equivalent to passing + :option:`-Wunused-cli <cmake -W>` or + :option:`-Wno-unused-cli <cmake -Wno->` + on the command line. + This may not be set to ``false`` + if ``errors.unusedCli`` is set to ``true``. + systemVars: + sort: false + type: boolean + description: + An optional boolean. Setting this to true is equivalent to + passing --check-system-vars on the command line. + sphinxDescription: | + An optional boolean. Setting this to ``true`` is equivalent to + passing :cmake-option:`--check-system-vars` on the command + line. + unevaluatedProperties: false + errors: + type: object + description: + An optional object specifying the errors to enable. + sphinxDescription: | + An optional object specifying the errors to enable. + The object may contain the following fields: + + .. include:: presets/errors-properties.rst + properties: + # This should ONLY have diagnostics whose definitions require + # special handling (i.e. anything from preset version < 12). Others + # will be generated from cmDiagnostics.h. + deprecated: + type: boolean + description: + An optional boolean. Equivalent to passing + -Werror=deprecated or -Wno-error=deprecated + on the command line. This may not be set + to true if warnings.deprecated is set to false. + sphinxDescription: | + An optional boolean. Equivalent to passing + :cmake-option:`-Werror=deprecated` or + :cmake-option:`-Wno-error=deprecated` + on the command line. + This may not be set to ``true`` + if ``warnings.deprecated`` is set to ``false``. + dev: + until: 12 + type: boolean + description: + An optional boolean. Equivalent to passing + -Werror=dev or -Wno-error=dev + on the command line. This may not be set + to true if warnings.dev is set to false. + sphinxDescription: | + An optional boolean. Equivalent to passing + :cmake-option:`-Werror=dev` or + :cmake-option:`-Wno-error=dev` + on the command line. + This may not be set to ``true`` + if ``warnings.dev`` is set to ``false``. + uninitialized: + since: 12 + type: boolean + description: + An optional boolean. Equivalent to passing + -Werror=uninitialized or -Wno-error=uninitialized + on the command line. This may not be set + to true if warnings.uninitialized is set to false. + sphinxDescription: | + An optional boolean. Equivalent to passing + :cmake-option:`-Werror=uninitialized` or + :cmake-option:`-Wno-error=uninitialized` + on the command line. + This may not be set to ``true`` + if ``warnings.uninitialized`` is set to ``false``. + unusedCli: + since: 12 + type: boolean + description: + An optional boolean. Equivalent to passing + -Werror=unused-cli or -Wno-error=unused-cli + on the command line. This may not be set + to true if warnings.unusedCli is set to false. + sphinxDescription: | + An optional boolean. Equivalent to passing + :cmake-option:`-Werror=unused-cli` or + :cmake-option:`-Wno-error=unused-cli` + on the command line. + This may not be set to ``true`` + if ``warnings.unusedCli`` is set to ``false``. + unevaluatedProperties: false + debug: + type: object + description: + An optional object specifying debug options. + sphinxDescription: | + An optional object specifying debug options. + The object may contain the following fields: + + .. include:: presets/debug-properties.rst + properties: + output: + type: boolean + description: + An optional boolean. Setting this to true is equivalent to + passing --debug-output on the command line. + sphinxDescription: | + An optional boolean. Setting this to ``true`` is equivalent to + passing :cmake-option:`--debug-output` on the command line. + tryCompile: + type: boolean + description: + An optional boolean. Setting this to true is equivalent to + passing --debug-trycompile on the command line. + sphinxDescription: | + An optional boolean. Setting this to ``true`` is equivalent to + passing :cmake-option:`--debug-trycompile` on the command line. + find: + type: boolean + description: + An optional boolean. Setting this to true is equivalent to + passing --debug-find on the command line. + sphinxDescription: | + An optional boolean. Setting this to ``true`` is equivalent to + passing :cmake-option:`--debug-find` on the command line. + unevaluatedProperties: false + trace: + since: 7 + type: object + description: + An optional object specifying trace options. + sphinxDescription: | + An optional object specifying trace options. + The object may contain the following fields: + + .. include:: presets/trace-properties.rst + properties: + mode: + type: string + enum: + - 'on' + - 'off' + - 'expand' + description: + An optional string that specifies the trace mode. + sphinxDescription: | + An optional string that specifies the trace mode. + Valid values are: + + ``on`` + Causes a trace of all calls made and from where to be + printed. Equivalent to passing :cmake-option:`--trace` + on the command line. + + ``off`` + A trace of all calls will not be printed. + + ``expand`` + Causes a trace with variables expanded of all calls made + and from where to be printed. Equivalent to passing + :cmake-option:`--trace-expand` on the command line. + format: + type: string + enum: + - human + - json-v1 + description: + An optional string that specifies the trace output format. + sphinxDescription: | + An optional string that specifies the format output of the + trace. Valid values are: + + ``human`` + Prints each trace line in a human-readable format. + This is the default format. Equivalent to passing + :cmake-option:`--trace-format=human` + on the command line. + + ``json-v1`` + Prints each line as a separate JSON document. Equivalent + to passing :cmake-option:`--trace-format=json-v1` + on the command line. + source: + anyOf: + - type: string + minLength: 1 + description: + An optional string representing the path to one source file + to be traced. + - type: array + description: + An optional array of strings representing the paths to source + files to be traced. + items: + type: string + minLength: 1 + description: + A string representing the path to one source file to be + traced. + sphinxDescription: | + An optional array of strings representing the paths of source + files to be traced. This field can also be a string, which is + equivalent to an array containing one string. Equivalent to + passing :cmake-option:`--trace-source` on the command line. + redirect: + type: string + description: + An optional string specifying a path to a trace output file. + sphinxDescription: | + An optional string specifying a path to a trace output file. + Equivalent to passing :cmake-option:`--trace-redirect` on the + command line. + unevaluatedProperties: false + unevaluatedProperties: false + buildPresets: + since: 2 + type: array + description: + An optional array of build preset objects. + sphinxDescription: | + An optional array of `Build Preset`_ objects. + items: + type: object + description: + A build preset object. + properties: + name: + type: string + required: true + minLength: 1 + description: + A required string representing the machine-friendly name of the + preset. This identifier is used in the cmake --build --preset + option. There must not be two build presets in the union of + CMakePresets.json and CMakeUserPresets.json in the same directory + with the same name. However, a build preset may have the same name + as a configure, test, package, or workflow preset. + sphinxDescription: | + A required string representing the machine-friendly name of the + preset. This identifier is used in the + :option:`cmake --build --preset <cmake--build --preset>` option. + There must not be two build presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a build preset may have the + same name as a configure, test, package, or workflow preset. + hidden: + type: boolean + description: + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the --preset + argument and does not have to have a valid configurePreset, even + from inheritance. hidden presets are intended to be used as a base + for other presets to inherit via the inherits field. + sphinxDescription: | + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the + :cmake-build-option:`--preset` argument and does not have to have a + valid :preset:`buildPresets.configurePreset`, even from + inheritance. ``hidden`` presets are intended to be used as a base + for other presets to inherit via the + :preset:`buildPresets.inherits` field. + inherits: *preset-inherits + condition: *condition + vendor: *preset-vendor + displayName: *preset-displayName + description: *preset-description + environment: + type: object + description: *environment-description + sphinxDescription: *environment-sphinx-description + sphinxNote: | + For a CMake project using :module:`ExternalProject` with a + configuration preset having environment variables needed in the + ExternalProject, use a build preset that inherits that + configuration preset or the ExternalProject will not have the + environment variables set in the configuration preset. Example: + suppose the host defaults to one compiler (say Clang) and the user + wishes to use another compiler (say GCC). Set configuration preset + environment variables :envvar:`CC` and :envvar:`CXX` and use a + build preset that inherits that configuration preset. Otherwise the + ExternalProject may use a different (system default) compiler than + the top-level CMake project. + properties: {} + propertyNames: + pattern: '^.+$' + additionalProperties: *environment-additional-properties + configurePreset: + type: string + minLength: 1 + description: + An optional string specifying the name of a configure preset to + associate with this build preset. If configurePreset is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build tree directory is inferred from + the configure preset. + sphinxDescription: | + An optional string specifying the name of a configure preset to + associate with this build preset. If ``configurePreset`` is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build directory is inferred from the + configure preset, so the build will take place in the same + ``binaryDir`` that the configuration did. + inheritConfigureEnvironment: + type: boolean + description: + An optional boolean that defaults to true. If true, the environment + variables from the associated configure preset are inherited after + all inherited build preset environments, but before environment + variables explicitly specified in this build preset. + sphinxDescription: | + An optional boolean that defaults to ``true``. If ``true``, the + environment variables from the associated configure preset are + inherited after all inherited build preset environments, but before + environment variables explicitly specified in this build preset. + jobs: + type: integer + description: + An optional non-negative integer. Equivalent to passing --parallel + or -j on the command line. + sphinxDescription: | + An optional integer. Equivalent to passing + :cmake-build-option:`--parallel` or ``-j`` on the command line. + If the value is ``0``, it is equivalent to passing ``--parallel`` + with ``<jobs>`` omitted; alternatively, one can define the + environment variable :envvar:`CMAKE_BUILD_PARALLEL_LEVEL` as an + empty string using the ``environment`` field. + + .. versionchanged:: 4.3 + + This field does not accept negative integer values, regardless of + the version in the presets file. + minimum: 0 + targets: + anyOf: + - type: string + description: + An optional string. Equivalent to passing --target or -t on the + command line. Vendors may ignore the targets property or hide + build presets that explicitly specify targets. + - type: array + items: + type: string + description: + An optional array of strings. Equivalent to passing --target or + -t on the command line. Vendors may ignore the targets property + or hide build presets that explicitly specify targets. + sphinxDescription: | + An optional string or array of strings. Equivalent to passing + :cmake-build-option:`--target` or ``-t`` on the command line. + Vendors may ignore the targets property or hide build presets that + explicitly specify targets. This field supports `macro expansion`_. + configuration: + type: string + description: + An optional string. Equivalent to passing --config on the command + line. + sphinxDescription: | + An optional string. Equivalent to passing + :cmake-build-option:`--config` on the command line. + cleanFirst: + type: boolean + description: + An optional boolean. If true, equivalent to passing --clean-first + on the command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :cmake-build-option:`--clean-first` on the command line. + resolvePackageReferences: + since: 4 + type: string + enum: + - 'on' + - 'off' + - 'only' + description: + An optional string specifying the package resolve behavior. Valid + values are "on" (packages are resolved prior to the build), "off" + (packages are not resolved prior to the build), and "only" + (packages are resolved, but no build will be performed). + sphinxDescription: | + An optional string that specifies the package resolve mode. + + Package references are used to define dependencies to packages from + external package managers. Currently only NuGet in combination with + the :ref:`Visual Studio generators` is supported. If there are no + targets that define package references, this option does nothing. + Valid values are: + + ``on`` + Causes package references to be resolved before attempting a + build. + + ``off`` + Package references will not be resolved. Note that this may cause + errors in some build environments, such as .NET SDK style + projects. + + ``only`` + Only resolve package references, but do not perform a build. + + .. note:: + + The command line parameter + :cmake-build-option:`--resolve-package-references` + will take priority over this setting. If the command line + parameter is not provided and this setting is not specified, an + environment-specific cache variable will be evaluated to decide, + if package restoration should be performed. + + When using the :ref:`Visual Studio generators`, + package references are defined using the + :prop_tgt:`VS_PACKAGE_REFERENCES` property. Package references + are restored using NuGet. It can be disabled by setting the + :variable:`CMAKE_VS_NUGET_PACKAGE_RESTORE` variable to ``OFF``. + This can also be done from within a configure preset. + verbose: + type: boolean + description: + An optional boolean. If true, equivalent to passing --verbose on + the command line. + sphinxDescription: | + An optional boolean. If ``true``, equivalent to passing + :cmake-build-option:`--verbose` on the command line. + nativeToolOptions: + type: array + items: + type: string + description: + An optional array of strings. Equivalent to passing options after + -- on the command line. + sphinxDescription: | + An optional array of strings. Equivalent to passing options after + :cmake-build-option:`--` on the command line. The array values + support `macro expansion`_. + unevaluatedProperties: false + testPresets: + since: 2 + type: array + description: + An optional array of test preset objects. Used to specify arguments to + ctest. + sphinxDescription: | + An optional array of `Test Preset`_ objects. + items: + type: object + description: + A test preset object. + properties: + name: + type: string + required: true + minLength: 1 + description: + A required string representing the machine-friendly name of the + preset. This identifier is used in the ctest --preset option. There + must not be two test presets in the union of CMakePresets.json and + CMakeUserPresets.json in the same directory with the same name. + However, a test preset may have the same name as a configure, + build, package, or workflow preset. + sphinxDescription: | + A required string representing the machine-friendly name of the + preset. This identifier is used in the :option:`ctest --preset` + option. There must not be two test presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a test preset may have the + same name as a configure, build, package, or workflow preset. + hidden: + type: boolean + description: + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the --preset + argument and does not have to have a valid configurePreset, even + from inheritance. hidden presets are intended to be used as a base + for other presets to inherit via the inherits field. + sphinxDescription: | + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the + :ctest-option:`--preset` argument and does not have to have a valid + :preset:`testPresets.configurePreset`, even from + inheritance. ``hidden`` presets are intended to be used as a base + for other presets to inherit via the + :preset:`testPresets.inherits` field. + inherits: *preset-inherits + condition: *condition + vendor: *preset-vendor + displayName: *preset-displayName + description: *preset-description + environment: *preset-environment + configurePreset: + type: string + minLength: 1 + description: + An optional string specifying the name of a configure preset to + associate with this test preset. If configurePreset is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build directory is inferred from the + configure preset. + sphinxDescription: | + An optional string specifying the name of a configure preset to + associate with this test preset. If ``configurePreset`` is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build directory is inferred from the + configure preset, so tests will run in the same ``binaryDir`` that + the configuration and build used. + inheritConfigureEnvironment: + type: boolean + description: + An optional boolean that defaults to true. If true, the environment + variables from the associated configure preset are inherited after + all inherited test preset environments, but before environment + variables explicitly specified in this test preset. + sphinxDescription: | + An optional boolean that defaults to ``true``. If ``true``, the + environment variables from the associated configure preset are + inherited after all inherited test preset environments, but before + environment variables explicitly specified in this test preset. + configuration: + type: string + description: + An optional string. Equivalent to passing --build-config on the + command line. + sphinxDescription: | + An optional string. Equivalent to passing + :ctest-option:`--build-config` on the command line. + overwriteConfigurationFile: + type: array + description: + An optional array of configuration options to overwrite options + specified in the CTest configuration file. Equivalent to passing + ``--overwrite`` for each value in the array. + sphinxDescription: | + An optional array of configuration options to overwrite options + specified in the CTest configuration file. Equivalent to passing + :ctest-option:`--overwrite` for each value in the array. The array + values support `macro expansion`_. + items: + type: string + description: + An option written as a key-value pair in the form "key=value". + output: + id: testOutput + type: object + description: + An optional object specifying output options. + sphinxDescription: | + An optional object specifying output options. + The object may contain the following fields: + + .. include:: presets/testOutput-properties.rst + properties: + shortProgress: + type: boolean + description: + An optional boolean. If true, equivalent to passing --progress + on the command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--progress` on the command line. + verbosity: + type: string + enum: + - default + - verbose + - extra + description: ' + An optional string specifying verbosity level. Valid values are + "default" (equivalent to passing no verbosity flags on the + command line), "verbose" (equivalent to passing --verbose on + the command line), and "extra" (equivalent to passing + --extra-verbose on the command line). + ' + sphinxDescription: | + An optional string specifying verbosity level. Must be one of + the following: + + ``default`` + Equivalent to passing no verbosity flags on the command line. + + ``verbose`` + Equivalent to passing :ctest-option:`--verbose` on the + command line. + + ``extra`` + Equivalent to passing :ctest-option:`--extra-verbose` on the + command line. + debug: + type: boolean + description: + An optional boolean. If true, equivalent to passing --debug on + the command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--debug` on the command line. + outputOnFailure: + type: boolean + description: + An optional boolean. If true, equivalent to passing + --output-on-failure on the command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--output-on-failure` on the command line. + quiet: + type: boolean + description: + An optional boolean. If true, equivalent to passing --quiet on + the command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--quiet` on the command line. + outputLogFile: + type: string + description: + An optional string specifying a path to a log file. Equivalent + to passing --output-log on the command line. + sphinxDescription: | + An optional string specifying a path to a log file. Equivalent + to passing :ctest-option:`--output-log` on the command line. + This field supports `macro expansion`_. + outputJUnitFile: + since: 6 + type: string + description: + An optional string specifying a path to a JUnit file. + Equivalent to passing --output-junit on the command line. + sphinxDescription: | + An optional string specifying a path to a JUnit file. + Equivalent to passing :ctest-option:`--output-junit` on the + command line. This field supports `macro expansion`_. + labelSummary: + type: boolean + description: + An optional boolean. If false, equivalent to passing + --no-label-summary on the command line. + sphinxDescription: | + An optional bool. If ``false``, equivalent to passing + :ctest-option:`--no-label-summary` on the command line. + subprojectSummary: + type: boolean + description: + An optional boolean. If false, equivalent to passing + --no-subproject-summary on the command line. + sphinxDescription: | + An optional bool. If ``false``, equivalent to passing + :ctest-option:`--no-subproject-summary` on the command line. + maxPassedTestOutputSize: + type: integer + description: + An optional integer specifying the maximum output for passed + tests in bytes. Equivalent to passing --test-output-size-passed + on the command line. + sphinxDescription: | + An optional integer specifying the maximum output for passed + tests in bytes. Equivalent to passing + :ctest-option:`--test-output-size-passed` on the command line. + maxFailedTestOutputSize: + type: integer + description: + An optional integer specifying the maximum output for failed + tests in bytes. Equivalent to passing --test-output-size-failed + on the command line. + sphinxDescription: | + An optional integer specifying the maximum output for failed + tests in bytes. Equivalent to passing + :ctest-option:`--test-output-size-failed` on the command line. + testOutputTruncation: + since: 5 + type: string + enum: + - tail + - middle + - head + description: ' + An optional string specifying the test output truncation mode. + Equivalent to passing --test-output-truncation on the command + line. Must be one of the following values: "tail", "middle", or + "head". + ' + sphinxDescription: | + An optional string specifying the test output truncation mode. + Equivalent to passing :ctest-option:`--test-output-truncation` + on the command line. Must be one of the following values: + + * ``tail`` + + * ``middle`` + + * ``head`` + maxTestNameWidth: + type: integer + description: + An optional integer specifying the maximum width of a test name + to output. Equivalent to passing --max-width on the command + line. + sphinxDescription: | + An optional integer specifying the maximum width of a test name + to output. Equivalent to passing :ctest-option:`--max-width` on + the command line. + unevaluatedProperties: false + filter: + type: object + description: + An optional object specifying how to filter the tests to run. + sphinxDescription: | + An optional object specifying how to filter the tests to run. + The object may contain the following fields: + + .. include:: presets/filter-properties.rst + properties: + include: + type: object + description: + An optional object specifying which tests to include. + sphinxDescription: | + An optional object specifying which tests to include. + The object may contain the following fields: + + .. include:: presets/include-properties.rst + properties: + name: + type: string + description: + An optional string specifying a regex for test names. + Equivalent to passing --tests-regex on the command line. + sphinxDescription: | + An optional string specifying a regex for test names. + Equivalent to passing :ctest-option:`--tests-regex` on the + command line. This field supports `macro expansion`_. + + CMake regex syntax is described under + :ref:`string(REGEX) <Regex Specification>`. + label: + type: string + description: + An optional string specifying a regex for test labels. + Equivalent to passing --label-regex on the command line. + sphinxDescription: | + An optional string specifying a regex for test labels. + Equivalent to passing :ctest-option:`--label-regex` on the + command line. This field supports `macro expansion`_. + useUnion: + type: boolean + description: + An optional boolean. Equivalent to passing --union on the + command line. + sphinxDescription: | + An optional bool. Equivalent to passing + :ctest-option:`--union` on the command line. + index: + anyOf: + - type: string + description: + An optional string specifying a file with the command + line syntax for --tests-information. Available in + version 2 and higher. + - type: object + description: + An optional object specifying test preset filters. + Available in version 2 and higher. + properties: + start: + type: integer + description: + An optional integer specifying a test index at + which to start testing. + end: + type: integer + description: + An optional integer specifying a test index at + which to stop testing. + stride: + type: integer + description: + An optional integer specifying the increment. + specificTests: + type: array + description: + An optional array of integers specifying specific + test indices to run. + items: + type: integer + description: + An integer specifying the test to run by index. + unevaluatedProperties: false + sphinxDescription: | + An optional object specifying tests to include by test + index. The object may contain the following fields. Can + also be an optional string specifying a file with the + command line syntax for + :ctest-option:`--tests-information`. If specified as a + string, this field supports `macro expansion`_. + + .. include:: presets/index-properties.rst + exclude: + type: object + description: + An optional object specifying which tests to exclude. + sphinxDescription: | + An optional object specifying which tests to exclude. + The object may contain the following fields: + + .. include:: presets/exclude-properties.rst + properties: + name: + type: string + description: + An optional string specifying a regex for test names. + Equivalent to passing --exclude-regex on the command line. + sphinxDescription: | + An optional string specifying a regex for test names. + Equivalent to passing :ctest-option:`--exclude-regex` on + the command line. This field supports `macro expansion`_. + label: + type: string + description: + An optional string specifying a regex for test labels. + Equivalent to passing --label-exclude on the command line. + sphinxDescription: | + An optional string specifying a regex for test labels. + Equivalent to passing :ctest-option:`--label-exclude` on + the command line. This field supports `macro expansion`_. + fixtures: + type: object + description: + An optional object specifying which fixtures to exclude + from adding tests. + sphinxDescription: | + An optional object specifying which fixtures to exclude + from adding tests. The object may contain the following + fields: + + .. include:: presets/fixtures-properties.rst + properties: + any: + type: string + description: + An optional string specifying a regex for text fixtures + to exclude from adding any tests. Equivalent to passing + --fixture-exclude-any on the command line. + sphinxDescription: | + An optional string specifying a regex for text fixtures + to exclude from adding any tests. Equivalent to passing + :ctest-option:`--fixture-exclude-any` on the command + line. This field supports `macro expansion`_. + setup: + type: string + description: + An optional string specifying a regex for text fixtures + to exclude from adding setup tests. Equivalent to + passing --fixture-exclude-setup on the command line. + sphinxDescription: | + An optional string specifying a regex for text fixtures + to exclude from adding setup tests. Equivalent to + passing :ctest-option:`--fixture-exclude-setup` on the + command line. This field supports `macro expansion`_. + cleanup: + type: string + description: + An optional string specifying a regex for text fixtures + to exclude from adding cleanup tests. Equivalent to + passing --fixture-exclude-cleanup on the command line. + sphinxDescription: | + An optional string specifying a regex for text fixtures + to exclude from adding cleanup tests. Equivalent to + passing :ctest-option:`--fixture-exclude-cleanup` on + the command line. + This field supports `macro expansion`_. + execution: + type: object + description: + An optional object specifying options for test execution. + sphinxDescription: | + An optional object specifying options for test execution. + The object may contain the following fields: + + .. include:: presets/execution-properties.rst + properties: + stopOnFailure: + type: boolean + description: + An optional boolean. If true, equivalent to passing + --stop-on-failure on the command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--stop-on-failure` on the command line. + enableFailover: + type: boolean + description: + An optional boolean. If true, equivalent to passing -F on the + command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :ctest-option:`-F` on the command line. + jobs: + revisions: + - until: 11 + type: integer + minimum: 0 + description: + An optional non-negative integer. Equivalent to passing + --parallel or -j on the command line. + - since: 11 + anyOf: + - type: integer + minimum: 0 + description: + An optional non-negative integer. Equivalent to passing + --parallel or -j on the command line. + - type: string + minLength: 0 + maxLength: 0 + description: + An optional string. Equivalent to passing --parallel or + -j on the command line with the number of jobs omitted. + sphinxDescription: | + An optional integer. Equivalent to passing + :ctest-option:`--parallel` on the command line. If the value is + ``0``, it is equivalent to unbounded parallelism. + + .. presets-versionchanged:: 11 + + This field can also be a string, in which case it must be + empty, and is equivalent to passing ``--parallel`` with + ``<jobs>`` omitted. + + .. versionchanged:: 4.3 + + This field does not accept negative integer values, + regardless of the version in the presets file. + minimum: 0 + resourceSpecFile: + type: string + description: + An optional string. Equivalent to passing --resource-spec-file + on the command line. + sphinxDescription: | + An optional string. Equivalent to passing + :ctest-option:`--resource-spec-file` on the command line. This + field supports `macro expansion`_. + testLoad: + type: integer + description: + An optional integer. Equivalent to passing --test-load on the + command line. + sphinxDescription: | + An optional integer. Equivalent to passing + :ctest-option:`--test-load` on the command line. + showOnly: + type: string + enum: + - human + - json-v1 + description: + An optional string. Equivalent to passing --show-only on the + command line. Value must be "human" or "json-v1". + sphinxDescription: | + An optional string. Equivalent to passing + :ctest-option:`--show-only` on the command line. The string + must be one of the following values: + + * ``human`` + + * ``json-v1`` + repeat: + type: object + description: + An optional object specifying how to repeat tests. Equivalent + to passing --repeat on the command line. + sphinxDescription: | + An optional object specifying how to repeat tests. Equivalent + to passing :ctest-option:`--repeat` on the command line. + The object must have the following fields: + + .. include:: presets/repeat-properties.rst + properties: + mode: + type: string + required: true + enum: + - until-fail + - until-pass + - after-timeout + description: ' + A required string. Must be one of the following values: + "until-fail", "until-pass", or "after-timeout". + ' + sphinxDescription: | + A required string. Must be one of the following values: + + * ``until-fail`` + + * ``until-pass`` + + * ``after-timeout`` + count: + type: integer + required: true + description: + A required integer. + unevaluatedProperties: false + interactiveDebugging: + type: boolean + description: + An optional boolean. If true, equivalent to passing + --interactive-debug-mode 1 on the command line. If false, + equivalent to passing --interactive-debug-mode 0 on the + command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--interactive-debug-mode 1` on the command line. + If ``false``, equivalent to passing + :ctest-option:`--interactive-debug-mode 0` on the command line. + scheduleRandom: + type: boolean + description: + An optional boolean. If true, equivalent to passing + --schedule-random on the command line. + sphinxDescription: | + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--schedule-random` on the command line. + timeout: + type: integer + description: + An optional integer. Equivalent to passing --timeout on the + command line. + sphinxDescription: | + An optional integer. Equivalent to passing + :ctest-option:`--timeout` on the command line. + noTestsAction: + type: string + enum: + - default + - error + - ignore + description: ' + An optional string specifying the behavior if no tests are + found. Must be one of the following values: "default" + (equivalent to not passing any value on the command line), + "error" (equivalent to passing --no-tests=error on the command + line), or "ignore" (equivalent to passing --no-tests-ignore on + the command line). + ' + sphinxDescription: | + An optional string specifying the behavior if no tests are + found. Must be one of the following values: + + ``default`` + Equivalent to not passing any value on the command line. + + ``error`` + Equivalent to passing :ctest-option:`--no-tests=error` + on the command line. + + ``ignore`` + Equivalent to passing :ctest-option:`--no-tests=ignore` + on the command line. + testPassthroughArguments: + since: 12 + type: array + items: + type: string + description: + An optional array of strings. Each element is forwarded as an + argument to every test executable. Equivalent to passing + arguments after -- on the ctest command line. + sphinxDescription: | + An optional array of strings. Each element is forwarded as an + argument to every test executable, equivalent to passing + arguments after :option:`ctest --` on the command line. + + When both preset and command-line passthrough arguments are + specified, preset arguments appear first, followed by + command-line arguments. + unevaluatedProperties: false + unevaluatedProperties: false + packagePresets: + since: 6 + type: array + description: + An optional array of package preset objects. Used to specify arguments to + cpack. + sphinxDescription: | + An optional array of `Package Preset`_ objects. + items: + type: object + description: + A package preset object. + properties: + name: + type: string + required: true + minLength: 1 + description: + A required string representing the machine-friendly name of the + preset. This identifier is used in the cpack --preset option. There + must not be two package presets in the union of CMakePresets.json + and CMakeUserPresets.json in the same directory with the same name. + However, a package preset may have the same name as a configure, + build, test, or workflow preset. + sphinxDescription: | + A required string representing the machine-friendly name of the + preset. This identifier is used in the :option:`cpack --preset` + option. There must not be two package presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a package preset may have + the same name as a configure, build, test, or workflow preset. + hidden: + type: boolean + description: + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the --preset + argument and does not have to have a valid configurePreset, even + from inheritance. hidden presets are intended to be used as a base + for other presets to inherit via the inherits field. + sphinxDescription: | + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the + :cpack-option:`--preset` argument and does not have to have a valid + :preset:`packagePresets.configurePreset`, even from + inheritance. ``hidden`` presets are intended to be used as a base + for other presets to inherit via the + :preset:`packagePresets.inherits` field. + inherits: *preset-inherits + condition: *condition + vendor: *preset-vendor + displayName: *preset-displayName + description: *preset-description + environment: *preset-environment + configurePreset: + type: string + minLength: 1 + description: + An optional string specifying the name of a configure preset to + associate with this package preset. If configurePreset is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build tree directory is inferred from + the configure preset. + sphinxDescription: | + An optional string specifying the name of a configure preset to + associate with this package preset. If ``configurePreset`` is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build directory is inferred from the + configure preset, so packaging will run in the same ``binaryDir`` + that the configuration did and build did. + inheritConfigureEnvironment: + type: boolean + description: + An optional boolean that defaults to true. If true, the environment + variables from the associated configure preset are inherited after + all inherited package preset environments, but before environment + variables explicitly specified in this package preset. + sphinxDescription: | + An optional boolean that defaults to ``true``. If ``true``, the + environment variables from the associated configure preset are + inherited after all inherited package preset environments, but + before environment variables explicitly specified in this package + preset. + generators: + type: array + description: + An optional array of strings representing generators for CPack to + use. + items: + type: string + description: + An optional string representing the name of a CPack generator + to use. + configurations: + type: array + description: + An optional array of strings representing build configurations for + CPack to package. + items: + type: string + description: + An optional string representing the name of a configuration to + package. + variables: + type: object + description: + An optional map of variables to pass to CPack, equivalent to -D + arguments. Each key is the name of a variable, and the value is the + string to assign to that variable. + sphinxDescription: | + An optional map of variables to pass to CPack, equivalent to + :cpack-option:`-D` arguments. Each key is the name of a variable, + and the value is the string to assign to that variable. + properties: {} + propertyNames: + pattern: '^.+$' + additionalProperties: + type: string + description: + An optional string representing the value of the variable. + configFile: + type: string + description: + An optional string representing the config file for CPack to use. + output: + id: packageOutput + type: object + description: + An optional object specifying output options. + sphinxDescription: | + An optional object specifying output options. Valid keys are: + + .. include:: presets/packageOutput-properties.rst + properties: + debug: + type: boolean + description: + An optional boolean specifying whether or not to print debug + information. A value of true is equivalent to passing --debug + on the command line. + sphinxDescription: | + An optional boolean specifying whether or not to print debug + information. A value of ``true`` is equivalent to passing + :cpack-option:`--debug` on the command line. + verbose: + type: boolean + description: + An optional boolean specifying whether or not to print + verbosely. A value of true is equivalent to passing --verbose + on the command line. + sphinxDescription: | + An optional boolean specifying whether or not to print + verbosely. A value of ``true`` is equivalent to passing + :cpack-option:`--verbose` on the command line. + unevaluatedProperties: false + packageName: + type: string + description: + An optional string representing the package name. + sphinxNote: &package-naming-note | + Due to problems with the implementation, this field does not affect + the name of the final package file produced. However, other aspects + of the package may use the value, leading to inconsistencies. A + future CMake release may address this problem, but until then, it + is recommended that this field not be used. + packageVersion: + type: string + description: + An optional string representing the package version. + sphinxNote: *package-naming-note + packageDirectory: + type: string + description: + An optional string representing the directory in which to place the + package. + vendorName: + type: string + description: + An optional string representing the vendor name. + unevaluatedProperties: false + workflowPresets: + since: 6 + type: array + description: + An optional array of workflow preset objects. Used to execute configure, + build, test, and package presets in order. + sphinxDescription: | + An optional array of `Workflow Preset`_ objects. + items: + type: object + description: + A workflow preset object. + properties: + name: + type: string + required: true + minLength: 1 + description: + A required string representing the machine-friendly name of the + preset. This identifier is used in the cmake --workflow --preset + option. There must not be two presets (configure, build, test, + package, or workflow) in the union of CMakePresets.json and + CMakeUserPresets.json in the same directory with the same name. + However, a workflow preset may have the same name as a configure, + build, test, or package preset. + sphinxDescription: | + A required string representing the machine-friendly name of the + preset. This identifier is used in the + :option:`cmake --workflow --preset <cmake--workflow --preset>` + option. There must not be two workflow presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a workflow preset may have + the same name as a configure, build, test, or package preset. + vendor: *preset-vendor + displayName: *preset-displayName + description: *preset-description + steps: + type: array + required: true + description: + A required array of objects describing the steps of the workflow. + The first step must be a configure preset, and all subsequent steps + must be non-configure presets whose configurePreset field matches + the starting configure preset. + sphinxDescription: | + A required array of objects describing the steps of the workflow. + The first step must be a configure preset, and all subsequent steps + must be non-configure presets whose ``configurePreset`` field + matches the starting configure preset. Each object may contain the + following fields: + + .. include:: presets/steps-properties.rst + items: + type: object + properties: + type: + type: string + enum: + - configure + - build + - test + - package + description: + A required string. The first step must be configure. + Subsequent steps must be either build, test, or package. + sphinxDescription: | + A required string. The first step must be ``configure``. + Subsequent steps must be either ``build``, ``test``, or + ``package``. + name: + type: string + minLength: 1 + description: + A required string representing the name of the configure, + build, test, or package preset to run as this workflow step. + unevaluatedProperties: false + unevaluatedProperties: false +additionalProperties: false
diff --git a/Help/manual/presets/steps-properties.rst b/Help/manual/presets/steps-properties.rst new file mode 100644 index 0000000..6da4a51 --- /dev/null +++ b/Help/manual/presets/steps-properties.rst
@@ -0,0 +1,14 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.workflowPresets.steps.type`: + +``type`` + A required string. The first step must be ``configure``. + Subsequent steps must be either ``build``, ``test``, or + ``package``. + +.. _`CMakePresets.workflowPresets.steps.name`: + +``name`` + A required string representing the name of the configure, build, test, or package preset to run as this workflow step.
diff --git a/Help/manual/presets/testOutput-properties.rst b/Help/manual/presets/testOutput-properties.rst new file mode 100644 index 0000000..3e5a6df --- /dev/null +++ b/Help/manual/presets/testOutput-properties.rst
@@ -0,0 +1,107 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.output.shortProgress`: + +``shortProgress`` + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--progress` on the command line. + +.. _`CMakePresets.testPresets.output.verbosity`: + +``verbosity`` + An optional string specifying verbosity level. Must be one of + the following: + + ``default`` + Equivalent to passing no verbosity flags on the command line. + + ``verbose`` + Equivalent to passing :ctest-option:`--verbose` on the + command line. + + ``extra`` + Equivalent to passing :ctest-option:`--extra-verbose` on the + command line. + +.. _`CMakePresets.testPresets.output.debug`: + +``debug`` + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--debug` on the command line. + +.. _`CMakePresets.testPresets.output.outputOnFailure`: + +``outputOnFailure`` + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--output-on-failure` on the command line. + +.. _`CMakePresets.testPresets.output.quiet`: + +``quiet`` + An optional bool. If ``true``, equivalent to passing + :ctest-option:`--quiet` on the command line. + +.. _`CMakePresets.testPresets.output.outputLogFile`: + +``outputLogFile`` + An optional string specifying a path to a log file. Equivalent + to passing :ctest-option:`--output-log` on the command line. + This field supports `macro expansion`_. + +.. _`CMakePresets.testPresets.output.outputJUnitFile`: + +``outputJUnitFile`` + .. presets-versionadded:: 6 + + An optional string specifying a path to a JUnit file. + Equivalent to passing :ctest-option:`--output-junit` on the + command line. This field supports `macro expansion`_. + +.. _`CMakePresets.testPresets.output.labelSummary`: + +``labelSummary`` + An optional bool. If ``false``, equivalent to passing + :ctest-option:`--no-label-summary` on the command line. + +.. _`CMakePresets.testPresets.output.subprojectSummary`: + +``subprojectSummary`` + An optional bool. If ``false``, equivalent to passing + :ctest-option:`--no-subproject-summary` on the command line. + +.. _`CMakePresets.testPresets.output.maxPassedTestOutputSize`: + +``maxPassedTestOutputSize`` + An optional integer specifying the maximum output for passed + tests in bytes. Equivalent to passing + :ctest-option:`--test-output-size-passed` on the command line. + +.. _`CMakePresets.testPresets.output.maxFailedTestOutputSize`: + +``maxFailedTestOutputSize`` + An optional integer specifying the maximum output for failed + tests in bytes. Equivalent to passing + :ctest-option:`--test-output-size-failed` on the command line. + +.. _`CMakePresets.testPresets.output.testOutputTruncation`: + +``testOutputTruncation`` + .. presets-versionadded:: 5 + + An optional string specifying the test output truncation mode. + Equivalent to passing :ctest-option:`--test-output-truncation` + on the command line. Must be one of the following values: + + * ``tail`` + + * ``middle`` + + * ``head`` + +.. _`CMakePresets.testPresets.output.maxTestNameWidth`: + +``maxTestNameWidth`` + An optional integer specifying the maximum width of a test name + to output. Equivalent to passing :ctest-option:`--max-width` on + the command line.
diff --git a/Help/manual/presets/testPresets-properties.rst b/Help/manual/presets/testPresets-properties.rst new file mode 100644 index 0000000..6e71ab4 --- /dev/null +++ b/Help/manual/presets/testPresets-properties.rst
@@ -0,0 +1,147 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.testPresets.name`: + +``name`` + A required string representing the machine-friendly name of the + preset. This identifier is used in the :option:`ctest --preset` + option. There must not be two test presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a test preset may have the + same name as a configure, build, package, or workflow preset. + +.. _`CMakePresets.testPresets.hidden`: + +``hidden`` + An optional boolean specifying whether or not a preset should be + hidden. If a preset is hidden, it cannot be used in the + :ctest-option:`--preset` argument and does not have to have a valid + :preset:`testPresets.configurePreset`, even from + inheritance. ``hidden`` presets are intended to be used as a base + for other presets to inherit via the + :preset:`testPresets.inherits` field. + +.. _`CMakePresets.testPresets.inherits`: + +``inherits`` + An optional array of strings representing the names of presets from which + to inherit. This field can also be a string, which is equivalent to an + array containing one string. + + The preset will inherit all of the fields from the ``inherits`` presets + by default (except ``name``, ``hidden``, ``inherits``, ``description``, + and ``displayName``), but can override them as desired. If multiple + ``inherits`` presets provide conflicting values for the same field, the + earlier preset in the ``inherits`` array will be preferred. + + A preset can only inherit from another preset that is defined in the same + file or in one of the files it includes (directly or indirectly). Presets + in ``CMakePresets.json`` may not inherit from presets in + ``CMakeUserPresets.json``. + +.. _`CMakePresets.testPresets.condition`: + +``condition`` + .. presets-versionadded:: 3 + + An optional `Condition`_ object. + +.. _`CMakePresets.testPresets.vendor`: + +``vendor`` + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. + +.. _`CMakePresets.testPresets.displayName`: + +``displayName`` + An optional string with a human-friendly name of the preset. + +.. _`CMakePresets.testPresets.description`: + +``description`` + An optional string with a human-friendly description of the preset. + +.. _`CMakePresets.testPresets.environment`: + +``environment`` + An optional map of environment variables. The key is the variable name + (which may not be an empty string), and the value is either ``null`` or a + string representing the value of the variable. Each variable is set + regardless of whether or not a value was given to it by the process's + environment. + + This field supports `macro expansion`_, and environment variables in this + map may reference each other, and may be listed in any order, as long as + such references do not cause a cycle (for example, if ``ENV_1`` is + ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables + by accessing only values from the parent environment. + + Environment variables are inherited through the ``inherits`` field, and + the preset's environment will be the union of its own ``environment`` and + the ``environment`` from all its parents. If multiple presets in this + union define the same variable, the standard rules of ``inherits`` are + applied. Setting a variable to ``null`` causes it to not be set, even if + a value was inherited from another preset. + +.. _`CMakePresets.testPresets.configurePreset`: + +``configurePreset`` + An optional string specifying the name of a configure preset to + associate with this test preset. If ``configurePreset`` is not + specified, it must be inherited from the inherits preset (unless + this preset is hidden). The build directory is inferred from the + configure preset, so tests will run in the same ``binaryDir`` that + the configuration and build used. + +.. _`CMakePresets.testPresets.inheritConfigureEnvironment`: + +``inheritConfigureEnvironment`` + An optional boolean that defaults to ``true``. If ``true``, the + environment variables from the associated configure preset are + inherited after all inherited test preset environments, but before + environment variables explicitly specified in this test preset. + +.. _`CMakePresets.testPresets.configuration`: + +``configuration`` + An optional string. Equivalent to passing + :ctest-option:`--build-config` on the command line. + +.. _`CMakePresets.testPresets.overwriteConfigurationFile`: + +``overwriteConfigurationFile`` + An optional array of configuration options to overwrite options + specified in the CTest configuration file. Equivalent to passing + :ctest-option:`--overwrite` for each value in the array. The array + values support `macro expansion`_. + +.. _`CMakePresets.testPresets.output`: + +``output`` + An optional object specifying output options. + The object may contain the following fields: + + .. include:: presets/testOutput-properties.rst + +.. _`CMakePresets.testPresets.filter`: + +``filter`` + An optional object specifying how to filter the tests to run. + The object may contain the following fields: + + .. include:: presets/filter-properties.rst + +.. _`CMakePresets.testPresets.execution`: + +``execution`` + An optional object specifying options for test execution. + The object may contain the following fields: + + .. include:: presets/execution-properties.rst
diff --git a/Help/manual/presets/toolset-properties.rst b/Help/manual/presets/toolset-properties.rst new file mode 100644 index 0000000..1fc91ca --- /dev/null +++ b/Help/manual/presets/toolset-properties.rst
@@ -0,0 +1,27 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.configurePresets.toolset.value`: + +``value`` + An optional string representing the value. + +.. _`CMakePresets.configurePresets.toolset.strategy`: + +``strategy`` + An optional string telling CMake how to handle the field. + Valid values are: + + ``"set"`` + Set the respective value. This will result in an error for generators + that do not support the respective field. + + ``"external"`` + Do not set the value, even if the generator supports it. This is + useful if, for example, a preset uses the Ninja generator, and an IDE + knows how to set up the Visual C++ environment from the architecture + and toolset fields. In that case, CMake will ignore the field, but + the IDE can use them to set up the environment before invoking CMake. + + If no ``strategy`` field is given, or if the field uses the string form + rather than the object form, the behavior is the same as ``"set"``.
diff --git a/Help/manual/presets/trace-properties.rst b/Help/manual/presets/trace-properties.rst new file mode 100644 index 0000000..b7b8b94 --- /dev/null +++ b/Help/manual/presets/trace-properties.rst
@@ -0,0 +1,53 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.configurePresets.trace.mode`: + +``mode`` + An optional string that specifies the trace mode. + Valid values are: + + ``on`` + Causes a trace of all calls made and from where to be + printed. Equivalent to passing :cmake-option:`--trace` + on the command line. + + ``off`` + A trace of all calls will not be printed. + + ``expand`` + Causes a trace with variables expanded of all calls made + and from where to be printed. Equivalent to passing + :cmake-option:`--trace-expand` on the command line. + +.. _`CMakePresets.configurePresets.trace.format`: + +``format`` + An optional string that specifies the format output of the + trace. Valid values are: + + ``human`` + Prints each trace line in a human-readable format. + This is the default format. Equivalent to passing + :cmake-option:`--trace-format=human` + on the command line. + + ``json-v1`` + Prints each line as a separate JSON document. Equivalent + to passing :cmake-option:`--trace-format=json-v1` + on the command line. + +.. _`CMakePresets.configurePresets.trace.source`: + +``source`` + An optional array of strings representing the paths of source + files to be traced. This field can also be a string, which is + equivalent to an array containing one string. Equivalent to + passing :cmake-option:`--trace-source` on the command line. + +.. _`CMakePresets.configurePresets.trace.redirect`: + +``redirect`` + An optional string specifying a path to a trace output file. + Equivalent to passing :cmake-option:`--trace-redirect` on the + command line.
diff --git a/Help/manual/presets/warnings-properties.rst b/Help/manual/presets/warnings-properties.rst new file mode 100644 index 0000000..9c9b7bc --- /dev/null +++ b/Help/manual/presets/warnings-properties.rst
@@ -0,0 +1,87 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.configurePresets.warnings.author`: + +``author`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :option:`-Wauthor <cmake -W>` or + :option:`-Wno-author <cmake -Wno->` on the command line. + This may not be set to ``false`` if ``errors.author`` is set to ``true``. + +.. _`CMakePresets.configurePresets.warnings.deprecated`: + +``deprecated`` + An optional boolean. Equivalent to passing + :option:`-Wdeprecated <cmake -W>` or + :option:`-Wno-deprecated <cmake -Wno->` + on the command line. + This may not be set to ``false`` + if ``errors.deprecated`` is set to ``true``. + +.. _`CMakePresets.configurePresets.warnings.dev`: + +``dev`` + .. presets-versionremoved:: 12 + + An optional boolean. Equivalent to passing + :option:`-Wdev <cmake -W>` or + :option:`-Wno-dev <cmake -Wno->` + on the command line. + This may not be set to ``false`` + if ``errors.dev`` is set to ``true``. + +.. _`CMakePresets.configurePresets.warnings.experimental`: + +``experimental`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :option:`-Wexperimental <cmake -W>` or + :option:`-Wno-experimental <cmake -Wno->` on the command line. + This may not be set to ``false`` if ``errors.experimental`` is set to ``true``. + +.. _`CMakePresets.configurePresets.warnings.installAbsoluteDestination`: + +``installAbsoluteDestination`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :option:`-Winstall-absolute-destination <cmake -W>` or + :option:`-Wno-install-absolute-destination <cmake -Wno->` on the command line. + This may not be set to ``false`` if ``errors.installAbsoluteDestination`` is set to ``true``. + +.. _`CMakePresets.configurePresets.warnings.policy`: + +``policy`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :option:`-Wpolicy <cmake -W>` or + :option:`-Wno-policy <cmake -Wno->` on the command line. + This may not be set to ``false`` if ``errors.policy`` is set to ``true``. + +.. _`CMakePresets.configurePresets.warnings.uninitialized`: + +``uninitialized`` + An optional boolean. Equivalent to passing + :option:`-Wuninitialized <cmake -W>` or + :option:`-Wno-uninitialized <cmake -Wno->` + on the command line. + This may not be set to ``false`` + if ``errors.uninitialized`` is set to ``true``. + +.. _`CMakePresets.configurePresets.warnings.unusedCli`: + +``unusedCli`` + An optional boolean. Equivalent to passing + :option:`-Wunused-cli <cmake -W>` or + :option:`-Wno-unused-cli <cmake -Wno->` + on the command line. + This may not be set to ``false`` + if ``errors.unusedCli`` is set to ``true``. + +.. _`CMakePresets.configurePresets.warnings.systemVars`: + +``systemVars`` + An optional boolean. Setting this to ``true`` is equivalent to + passing :cmake-option:`--check-system-vars` on the command + line.
diff --git a/Help/manual/presets/workflowPresets-properties.rst b/Help/manual/presets/workflowPresets-properties.rst new file mode 100644 index 0000000..fdebe96 --- /dev/null +++ b/Help/manual/presets/workflowPresets-properties.rst
@@ -0,0 +1,44 @@ +.. This file was generated by Utilities/Scripts/regenerate-presets.py + from Help/manual/presets/schema.yaml. Do not edit. + +.. _`CMakePresets.workflowPresets.name`: + +``name`` + A required string representing the machine-friendly name of the + preset. This identifier is used in the + :option:`cmake --workflow --preset <cmake--workflow --preset>` + option. There must not be two workflow presets in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. However, a workflow preset may have + the same name as a configure, build, test, or package preset. + +.. _`CMakePresets.workflowPresets.vendor`: + +``vendor`` + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map if + it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. + +.. _`CMakePresets.workflowPresets.displayName`: + +``displayName`` + An optional string with a human-friendly name of the preset. + +.. _`CMakePresets.workflowPresets.description`: + +``description`` + An optional string with a human-friendly description of the preset. + +.. _`CMakePresets.workflowPresets.steps`: + +``steps`` + A required array of objects describing the steps of the workflow. + The first step must be a configure preset, and all subsequent steps + must be non-configure presets whose ``configurePreset`` field + matches the starting configure preset. Each object may contain the + following fields: + + .. include:: presets/steps-properties.rst
diff --git a/Help/policy/CMP0211.rst b/Help/policy/CMP0211.rst new file mode 100644 index 0000000..54ddf70 --- /dev/null +++ b/Help/policy/CMP0211.rst
@@ -0,0 +1,25 @@ +CMP0211 +------- + +.. versionadded:: 4.4 + +A file may belong to at most one non-``HEADERS`` :ref:`file set <file sets>` +in a target. + +In CMake 4.3 and below, it was possible to specify the same file +in multiple file sets in a target. In CMake 4.4 and above, a file +may belong to at most one file set in a target except for the ``HEADERS`` file +set type. This policy provides compatibility for projects that have not been +updated accordingly. + +The ``OLD`` behavior for this policy is to accept the same file in +multiple file sets in a target. The ``NEW`` behavior for this policy +is to accept a given file in at most one file set in a target except for the +``HEADERS`` file set type. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: + warns when a file is part of multiple file sets in a target +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0212.rst b/Help/policy/CMP0212.rst new file mode 100644 index 0000000..81d7828 --- /dev/null +++ b/Help/policy/CMP0212.rst
@@ -0,0 +1,31 @@ +CMP0212 +------- + +.. versionadded:: 4.4 + +:command:`add_custom_command` ``DEPENDS`` does not strip ``.exe`` suffixes. + +When searching for dependencies specified with the ``DEPENDS`` option of +:command:`add_custom_command`, CMake 4.3 and below applied a heuristic to +remove ``.exe`` from the end of ``DEPENDS`` argument values, which created a +target-level dependency on an executable with that stripped name if such a +target exists. This allowed using the name of the target output file as an +alternative spelling when naming dependencies. However, with the emergence of +CMake's target model, specifying the output file in this way is no longer +needed, and leads to conflicts in creating target- and file-level dependencies +when targets exist with the ``.exe`` suffix in the name itself. + +The ``OLD`` behavior of this policy strips the ``.exe`` suffix from arguments +to ``DEPENDS`` when searching for target-level dependencies. The ``NEW`` +behavior does not strip the ``.exe`` suffix in its search. CMake will otherwise +proceed normally with the other dependency search heuristics as specified in +:command:`add_custom_command` ``DEPENDS``. Users are encouraged to specify the +name of the executable target if a target-level dependency is desired, or use +the :genex:`TARGET_FILE` generator expression if a file-level dependency +is desired, rather than implying the target indirectly via its output file name. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0213.rst b/Help/policy/CMP0213.rst new file mode 100644 index 0000000..d8a8e25 --- /dev/null +++ b/Help/policy/CMP0213.rst
@@ -0,0 +1,22 @@ +CMP0213 +------- + +.. versionadded:: 4.4 + +:command:`file(ARCHIVE_CREATE)` and :command:`file(ARCHIVE_EXTRACT)` +encode archive paths as UTF-8 by default. + +CMake 4.3 and below always used the ``OEM`` (current locale) encoding for archive +paths. CMake 4.4 offer the ``ENCODING`` option, but prefer to use ``UTF-8`` +by default. This policy provides compatibility for projects that have not +been updated to specify the encoding or accept the new default. + +The ``OLD`` behavior for this policy is to use the current locale as the +default encoding, which is also known as an ``OEM`` encoding. +The ``NEW`` behavior uses ``UTF-8`` as the default encoding. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0214.rst b/Help/policy/CMP0214.rst new file mode 100644 index 0000000..f1e6db5 --- /dev/null +++ b/Help/policy/CMP0214.rst
@@ -0,0 +1,33 @@ +CMP0214 +------- + +.. versionadded:: 4.4 + +Honor ``CMAKE_EXE_LINKER_FLAGS`` for Swift executable targets. + +CMake 3.29 introduced :policy:`CMP0157`, which provides an abstraction +for selecting the Swift compilation mode. In CMake 3.30, +:variable:`CMAKE_SHARED_LINKER_FLAGS` was accidentally honored for +Swift shared library targets when :policy:`CMP0157` is ``NEW``, but +the change went unnoticed and was not documented. The corresponding +change was not made for Swift executable targets, leaving linker flag +handling inconsistent between Swift executables and Swift shared +libraries in CMake 3.30 through 4.3. + +CMake 4.4 and above prefer to honor :variable:`CMAKE_EXE_LINKER_FLAGS` +for Swift executable targets when :policy:`CMP0157` is set to ``NEW``, +restoring consistency with Swift shared library targets. This policy +provides compatibility for projects that have not been updated to +expect ``CMAKE_EXE_LINKER_FLAGS`` to be honored for Swift executables. + +The ``OLD`` behavior for this policy is to not honor +:variable:`CMAKE_EXE_LINKER_FLAGS` for Swift executable targets. +The ``NEW`` behavior for this policy is to honor +:variable:`CMAKE_EXE_LINKER_FLAGS` for Swift executable targets +when :policy:`CMP0157` is set to ``NEW``. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0215.rst b/Help/policy/CMP0215.rst new file mode 100644 index 0000000..acee88c --- /dev/null +++ b/Help/policy/CMP0215.rst
@@ -0,0 +1,41 @@ +CMP0215 +------- + +.. versionadded:: 4.4 + +:ref:`Ninja Generators` emit Swift modules separately from compilation. + +In CMake 4.3 and below, Swift targets built by :ref:`Ninja Generators` +emit ``.swiftmodule`` from the Swift compile edge together with object files. +Because Ninja treats a build edge as atomic, downstream Swift targets +must wait for compilation outputs to finish before they can use the module +interface. + +CMake 4.4 and above prefer to emit ``.swiftmodule`` from a dedicated +``-emit-module`` build edge so that downstream Swift targets can begin +compiling as soon as the module interface is available. This policy +provides compatibility with projects that have not been updated. + +The ``OLD`` behavior for this policy is to not emit modules separately. +The ``NEW`` behavior for this policy is to emit ``.swiftmodule`` from a +dedicated build edge for importable Swift targets by default. + +The :prop_tgt:`Swift_SEPARATE_MODULE_EMISSION` target property and +corresponding :variable:`CMAKE_Swift_SEPARATE_MODULE_EMISSION` variable +may be set to enable or disable the behavior on a per-target basis. + +The ``NEW`` behavior requires that policies :policy:`CMP0157` and +:policy:`CMP0195` are also set to ``NEW``. :policy:`CMP0157` selects the +split Swift build model that provides the dedicated ``.swiftmodule`` edge, +and must be set prior to the first :command:`project` or +:command:`enable_language` command that enables the Swift language. +:policy:`CMP0195` places the module in the Swift module directory structure +that the dedicated edge writes. If either policy is not ``NEW`` while +``CMP0215`` is ``NEW``, CMake issues a fatal error when the Swift language +is enabled. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0216.rst b/Help/policy/CMP0216.rst new file mode 100644 index 0000000..814440f --- /dev/null +++ b/Help/policy/CMP0216.rst
@@ -0,0 +1,32 @@ +CMP0216 +------- + +.. versionadded:: 4.4 + +Swift targets have a default project name. + +CMake 4.3 and below did not compile Swift targets with ``-package-name``. +The package name is useful because Swift package access control makes types, +functions, and properties visible to other libraries within the same package, +but not from sources outside of the package. CMake 4.4 and above prefer to +pass ``-package-name`` by default using the :variable:`PROJECT_NAME` variable +value. This policy provides compatibility for projects that have not been +updated to expect a package name. + +.. note:: + + Package access control was added in Swift 5.8 and Xcode 15. This policy has + no effect when using an earlier version of Swift or Xcode. + +The ``OLD`` behavior for this policy is to build Swift source files without +passing a package-name. The ``NEW`` behavior for this policy is to build +Swift source files with a default package name from :variable:`PROJECT_NAME`. + +Regardless of whether this policy is set, the :prop_tgt:`Swift_PACKAGE_NAME` +target property may be set to explicitly specify or suppress a package name. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0217.rst b/Help/policy/CMP0217.rst new file mode 100644 index 0000000..052b82c6 --- /dev/null +++ b/Help/policy/CMP0217.rst
@@ -0,0 +1,24 @@ +CMP0217 +------- + +.. versionadded:: 4.4 + +The :prop_dir:`MACROS` directory property does not exist anymore. + +In CMake 4.3 and below, the macros defined in a directory are registered in the +:prop_dir:`MACROS` directory property. However, the property does not have the +same scope as macros, so it does not represent the actual list of macros. +In CMake 4.4 and above, this property is not longer filled with the defined +macros. One may still use :command:`if(COMMAND myMacro) <if(COMMAND)>` to test +whether one exists. + +The ``OLD`` behavior for this policy is to store macros definition in the +:prop_dir:`MACROS` directory property. +The ``NEW`` behavior for this policy is to no longer manage the +:prop_dir:`MACROS` directory property. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0218.rst b/Help/policy/CMP0218.rst new file mode 100644 index 0000000..6e64d0c --- /dev/null +++ b/Help/policy/CMP0218.rst
@@ -0,0 +1,46 @@ +CMP0218 +------- + +.. versionadded:: 4.4 + +The :variable:`CMAKE_WARN_DEPRECATED` and :variable:`CMAKE_ERROR_DEPRECATED` +variables are ignored. + +In CMake 4.3 and below, these variables controlled whether deprecation messages +resulted in warnings, errors, or were suppressed. However, not all deprecation +messages considered the non-cache values. + +CMake 4.4 introduced a new, unified system for managing +:manual:`diagnostics <cmake-diagnostics(7)>` that prefers the +:command:`cmake_diagnostic` command for manipulating diagnostic actions. + +This policy provides compatibility with projects that relied on these variables +and have not been updated to be aware of the change. + +The ``OLD`` behavior for this policy is to use the variables to control +deprecation messages which respected the non-cache values of these variables +in CMake 4.3 and earlier. + +The ``NEW`` behavior does not ascribe any special meaning to the non-cache +values of these variables and uses the :diagnostic:`CMD_DEPRECATED` diagnostic +state to control all deprecation messages. + +.. note:: + + The :variable:`CMAKE_WARN_DEPRECATED` and :variable:`CMAKE_ERROR_DEPRECATED` + *cache* variables are synchronized with the initial state of the + :diagnostic:`CMD_DEPRECATED` diagnostic, regardless of this policy's setting. + (The synchronization happens at a point prior to when it is possible to + manipulate policies.) However, this behavior is deprecated and will be + removed in a future version of CMake. + +Any deprecation messages introduced in CMake 4.4 or later are controlled by the +:diagnostic:`CMD_DEPRECATED` diagnostic regardless of the setting of this +policy. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: + warns when either variable is accessed or modified +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0219.rst b/Help/policy/CMP0219.rst new file mode 100644 index 0000000..2ae3c24 --- /dev/null +++ b/Help/policy/CMP0219.rst
@@ -0,0 +1,103 @@ +CMP0219 +------- + +.. versionadded:: 4.4 + +:command:`macro` invocations preserve backslashes in arguments. + +In CMake 4.3 and below, macro argument references (``${ARGN}``, ``${ARGV}``, +``${ARGV<n>}``, and named macro arguments) are substituted textually and then +evaluated by the called command. Backslashes in substituted values are +re-evaluated as escape prefixes, and invalid escape sequences (such as +``\b`` in Windows paths) can cause errors. The same re-evaluation can also +occur for arguments passed to a :command:`variable_watch` callback command. + +CMake 4.4 and above prefer to preserve literal backslashes in those macro +argument references. + +The ``OLD`` behavior for this policy is to interpret escape sequences in macro +argument references and in arguments passed to ``variable_watch()`` callback +commands. The ``NEW`` behavior is to preserve backslashes in those values +before command invocation. + +This policy applies to macro argument references and to arguments passed to +``variable_watch()`` callback commands. Other variable references keep their +existing behavior. + +Mixed Policy Chains +^^^^^^^^^^^^^^^^^^^ + +Whether pre-escaping occurs is determined by the policy setting at each +call site. When a project and its dependencies use different ``CMP0219`` +settings, each macro call in the chain follows the policy where that call +appears. + +Consider a project (``CMP0219`` is ``OLD``) that calls a macro from +dependency A (upgraded to +:command:`cmake_minimum_required(VERSION 4.4) <cmake_minimum_required>`, +so ``CMP0219`` is ``NEW``), which in turn forwards arguments to a macro +from dependency B (not yet upgraded, so ``CMP0219`` is ``OLD``): + +.. code-block:: cmake + + # Dependency B: cmake_minimum_required(VERSION 3.24) + # CMP0219 is OLD inside dependency B. + macro(depB_store var_name) + set(${var_name} "${ARGN}") + endmacro() + + # Dependency A: cmake_minimum_required(VERSION 4.4) + # CMP0219 is NEW inside dependency A. + macro(depA_forward var_name) + depB_store(${var_name} ${ARGN}) # call site is NEW -> pre-escapes + endmacro() + + # Project: cmake_minimum_required(VERSION 3.24) + # CMP0219 is OLD in the project. + macro(my_wrapper var_name) + depA_forward(${var_name} ${ARGN}) # call site is OLD -> no pre-escaping + endmacro() + +In a fully ``OLD`` chain, each macro boundary re-evaluates backslash +escapes, so callers must add extra escaping layers to compensate. +To pass the Windows path ``C:\build\new\temp`` through three ``OLD`` +boundaries, the caller needs eight backslashes per separator: + +.. code-block:: cmake + + my_wrapper(result "C:\\\\\\\\build\\\\\\\\new\\\\\\\\temp") + message("${result}") # -> C:\build\new\temp + +When dependency A upgrades and the middle boundary becomes ``NEW``, +that boundary preserves backslashes instead of consuming a level. +The same input now retains an extra layer: + +.. code-block:: cmake + + # Same 8x-escaped input through OLD -> NEW -> OLD: + my_wrapper(result "C:\\\\\\\\build\\\\\\\\new\\\\\\\\temp") + message("${result}") # -> C:\\build\\new\\temp (one extra layer) + +The caller must then use half the escaping to reach the native path: + +.. code-block:: cmake + + # Only 4x-escaped input needed for OLD -> NEW -> OLD: + my_wrapper(result "C:\\\\build\\\\new\\\\temp") + message("${result}") # -> C:\build\new\temp + +When a dependency updates its :command:`cmake_minimum_required` version +to 4.4 or above, call sites inside that dependency begin pre-escaping. +Callers that previously added extra backslash layers to compensate for +multiple levels of ``OLD`` re-evaluation may then need fewer escape +layers. + +Where possible, converting paths to forward slashes with +:command:`cmake_path(CONVERT ... TO_CMAKE_PATH_LIST)` avoids the issue entirely, because +forward slashes require no escaping regardless of the policy setting. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns when backslashes are present +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/prop_dir/MACROS.rst b/Help/prop_dir/MACROS.rst index 245cc1b..7f1a7c5 100644 --- a/Help/prop_dir/MACROS.rst +++ b/Help/prop_dir/MACROS.rst
@@ -1,8 +1,4 @@ MACROS ------ -List of macro commands available in the current directory. - -This read-only property specifies the list of CMake macros currently -defined. It is intended for debugging purposes. See the :command:`macro` -command. +This directory property does not exist anymore. See policy :policy:`CMP0217`.
diff --git a/Help/prop_fs/BASE_DIRS.rst b/Help/prop_fs/BASE_DIRS.rst new file mode 100644 index 0000000..879d44d --- /dev/null +++ b/Help/prop_fs/BASE_DIRS.rst
@@ -0,0 +1,15 @@ +BASE_DIRS +--------- + +.. versionadded:: 4.4 + +List of base directories of the file set. The :command:`target_sources` command +sets or adds to the ``BASE_DIRS`` file set property and is the usual way to +manipulate it. + +Contents of ``BASE_DIRS`` may use +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +Any relative paths are considered relative to the target's source directory. No +two base directories for a file set may be sub-directories of each other. This +requirement must be met across all base directories added to a file set.
diff --git a/Help/prop_fs/COMPILE_DEFINITIONS.rst b/Help/prop_fs/COMPILE_DEFINITIONS.rst new file mode 100644 index 0000000..bc400bf --- /dev/null +++ b/Help/prop_fs/COMPILE_DEFINITIONS.rst
@@ -0,0 +1,37 @@ +COMPILE_DEFINITIONS +------------------- + +.. versionadded:: 4.4 + +Preprocessor definitions for compiling a source file. + +The ``COMPILE_DEFINITIONS`` property may be set to a semicolon-separated +list of preprocessor definitions using the syntax ``VAR`` or ``VAR=value``. +Function-style definitions are not supported. CMake will +automatically escape the value correctly for the native build system +(note that CMake language syntax may require escapes to specify some +values). + +CMake will automatically drop some definitions that are not supported +by the native build tool. :generator:`Xcode` does not support +per-configuration definitions on source files. + +.. include:: /include/COMPILE_DEFINITIONS_DISCLAIMER.rst + +Contents of ``COMPILE_DEFINITIONS`` may use :manual:`cmake-generator-expressions(7)` +with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` +manual for available expressions. However, :generator:`Xcode` +does not support per-config per-source settings, so expressions +that depend on the build configuration are not allowed with that +generator. + +Related properties: + +* Use :prop_fs:`COMPILE_OPTIONS` to pass additional compile flags. +* Use :prop_fs:`INCLUDE_DIRECTORIES` to pass additional include directories. + +Related commands: + +* :command:`add_compile_definitions` for directory-wide settings +* :command:`target_compile_definitions` for target-specific settings +* :command:`set_source_files_properties` for source-specific settings
diff --git a/Help/prop_fs/COMPILE_OPTIONS.rst b/Help/prop_fs/COMPILE_OPTIONS.rst new file mode 100644 index 0000000..70614b1 --- /dev/null +++ b/Help/prop_fs/COMPILE_OPTIONS.rst
@@ -0,0 +1,36 @@ +COMPILE_OPTIONS +--------------- + +.. versionadded:: 4.4 + +List of additional options to pass to the compiler. + +This property holds a :ref:`semicolon-separated list <CMake Language Lists>` +of options and will be added to the list of compile flags when the sources of +this file set are built. The options will be added after target-wide options +and source level ones. + +Contents of ``COMPILE_OPTIONS`` may use "generator expressions" with the +syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual +for available expressions. However, :generator:`Xcode` +does not support per-config per-source settings, so expressions +that depend on the build configuration are not allowed with that +generator. + +Usage example: + +.. code-block:: cmake + + set_property(FILE_SET SOURCES TARGET foo PROPERTY + COMPILE_OPTIONS "-Wno-unused-parameter;-Wno-missing-field-initializer") + +Related properties: + +* Use :prop_fs:`COMPILE_DEFINITIONS` to pass additional preprocessor definitions. +* Use :prop_fs:`INCLUDE_DIRECTORIES` to pass additional include directories. + +Related commands: + +* :command:`add_compile_options` for directory-wide settings +* :command:`target_compile_options` for target-specific settings +* :command:`set_source_files_properties` for source-specific settings
diff --git a/Help/prop_fs/CXX_SCAN_FOR_MODULES.rst b/Help/prop_fs/CXX_SCAN_FOR_MODULES.rst new file mode 100644 index 0000000..f4c92a0 --- /dev/null +++ b/Help/prop_fs/CXX_SCAN_FOR_MODULES.rst
@@ -0,0 +1,20 @@ +CXX_SCAN_FOR_MODULES +-------------------- + +.. versionadded:: 4.4 + +``CXX_SCAN_FOR_MODULES`` is a boolean specifying whether CMake will scan the +file set's sources for C++ module dependencies. See also the +:prop_sf:`CXX_SCAN_FOR_MODULES` source file property and the +:prop_tgt:`CXX_SCAN_FOR_MODULES` target property settings. + +When this property is set ``ON``, CMake will scan the file set's sources at +build time and add module dependency information to the compile line as +necessary. When this property is set ``OFF``, CMake will not scan the sources +at build time. When this property is unset, the +:prop_sf:`CXX_SCAN_FOR_MODULES` source file property and +:prop_tgt:`CXX_SCAN_FOR_MODULES` target property are consulted, in that order. + +Note that scanning is only performed if C++20 or higher is enabled for the +target and the source uses the ``CXX`` language. Scanning for modules in +sources belonging to file sets of type ``CXX_MODULES`` is always performed.
diff --git a/Help/prop_fs/INCLUDE_DIRECTORIES.rst b/Help/prop_fs/INCLUDE_DIRECTORIES.rst new file mode 100644 index 0000000..fd940ec --- /dev/null +++ b/Help/prop_fs/INCLUDE_DIRECTORIES.rst
@@ -0,0 +1,31 @@ +INCLUDE_DIRECTORIES +------------------- + +.. versionadded:: 4.4 + +List of preprocessor include file search directories. + +This property holds a :ref:`semicolon-separated list <CMake Language Lists>` of paths +and will be added to the list of include directories when the sources of this +file set are built. These directories will take precedence over directories +defined at target level and source level except for :generator:`Xcode` +generator due to technical limitations. + +Relative paths should not be added to this property directly. + +Contents of ``INCLUDE_DIRECTORIES`` may use "generator expressions" with +the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual +for available expressions. However, :generator:`Xcode` does not support +per-config per-source settings, so expressions that depend on the build +configuration are not allowed with that generator. + +Related properties: + +* Use :prop_fs:`COMPILE_DEFINITIONS` to pass additional preprocessor definitions. +* Use :prop_fs:`COMPILE_OPTIONS` to pass additional compile flags. + +Related commands: + +* :command:`include_directories` for directory-wide settings +* :command:`target_include_directories` for target-specific settings +* :command:`set_source_files_properties` for source-specific settings
diff --git a/Help/prop_fs/INDEPENDENT_FILES.rst b/Help/prop_fs/INDEPENDENT_FILES.rst new file mode 100644 index 0000000..40195ac --- /dev/null +++ b/Help/prop_fs/INDEPENDENT_FILES.rst
@@ -0,0 +1,34 @@ +INDEPENDENT_FILES +----------------- + +.. versionadded:: 4.4 + +``INDEPENDENT_FILES`` is a boolean specifying that any :prop_sf:`GENERATED` +sources in the file set are not necessary for the compilation of other sources +in the same target. Stated another way, these files are "independent" and their +presence is never necessary for compilation of other sources (e.g., via +``#include``). + +When this property is ``ON``, :ref:`Ninja Generators` will omit 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 independent from other sources in +the same target. +When this property is ``OFF``, :ref:`Ninja Generators` will apply 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 independent from other sources in +the same target. + +If this property is not defined, the following default will be applied: + +* ``ON`` for ``CXX_MODULES`` file set type. +* ``OFF`` for all other file set types. + +In the case of the ``CXX_MODULES`` file set type, the only supported mode is +``ON``. So if the property is set to ``OFF``, it will be ignored. + +In the case of the ``HEADERS`` file set type, if the property is ``ON``, an +author warning will be emitted because this mode is not supported. + +This property is effective only when the policy :policy:`CMP0154` is ``NEW``.
diff --git a/Help/prop_fs/INTERFACE_COMPILE_DEFINITIONS.rst b/Help/prop_fs/INTERFACE_COMPILE_DEFINITIONS.rst new file mode 100644 index 0000000..5c102ee --- /dev/null +++ b/Help/prop_fs/INTERFACE_COMPILE_DEFINITIONS.rst
@@ -0,0 +1,15 @@ +INTERFACE_COMPILE_DEFINITIONS +----------------------------- + +.. versionadded:: 4.4 + +.. |property_name| replace:: compile definitions +.. |command_name| replace:: :command:`set_property(FILE_SET)` +.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_COMPILE_DEFINITIONS`` +.. include:: include/INTERFACE_BUILD_PROPERTY.rst + +Related properties: + +* Use :prop_fs:`INTERFACE_COMPILE_OPTIONS` to pass additional compile flags. +* Use :prop_fs:`INTERFACE_INCLUDE_DIRECTORIES` to pass additional include + directories.
diff --git a/Help/prop_fs/INTERFACE_COMPILE_OPTIONS.rst b/Help/prop_fs/INTERFACE_COMPILE_OPTIONS.rst new file mode 100644 index 0000000..cd30776 --- /dev/null +++ b/Help/prop_fs/INTERFACE_COMPILE_OPTIONS.rst
@@ -0,0 +1,16 @@ +INTERFACE_COMPILE_OPTIONS +------------------------- + +.. versionadded:: 4.4 + +.. |property_name| replace:: compile options +.. |command_name| replace:: :command:`set_property(FILE_SET)` +.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_COMPILE_OPTIONS`` +.. include:: include/INTERFACE_BUILD_PROPERTY.rst + +Related properties: + +* Use :prop_fs:`INTERFACE_COMPILE_DEFINITIONS` to pass additional preprocessor + definitions. +* Use :prop_fs:`INTERFACE_INCLUDE_DIRECTORIES` to pass additional include + directories.
diff --git a/Help/prop_fs/INTERFACE_INCLUDE_DIRECTORIES.rst b/Help/prop_fs/INTERFACE_INCLUDE_DIRECTORIES.rst new file mode 100644 index 0000000..880a234 --- /dev/null +++ b/Help/prop_fs/INTERFACE_INCLUDE_DIRECTORIES.rst
@@ -0,0 +1,35 @@ +INTERFACE_INCLUDE_DIRECTORIES +----------------------------- + +.. versionadded:: 4.4 + +.. |property_name| replace:: include directories +.. |command_name| replace:: :command:`set_property(FILE_SET)` +.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_INCLUDE_DIRECTORIES`` +.. include:: include/INTERFACE_BUILD_PROPERTY.rst + +Include directories usage requirements commonly differ between the build-tree +and the install-tree. The ``BUILD_INTERFACE`` and ``INSTALL_INTERFACE`` +generator expressions can be used to describe separate usage requirements +based on the usage location. Relative paths are allowed within the +``INSTALL_INTERFACE`` expression and are interpreted relative to the +installation prefix. For example: + +.. code-block:: cmake + + set_property(FILE_SET myfile_set TARGET mylib PROPERTY INTERFACE_INCLUDE_DIRECTORIES + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib> + $<INSTALL_INTERFACE:include/mylib> # <prefix>/include/mylib + ) + +Related properties: + +* Use :prop_fs:`INTERFACE_COMPILE_DEFINITIONS` to pass additional preprocessor + definitions. +* Use :prop_fs:`INTERFACE_COMPILE_OPTIONS` to pass additional compile flags. + +Creating Relocatable Packages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. |INTERFACE_PROPERTY_LINK| replace:: ``INTERFACE_INCLUDE_DIRECTORIES`` +.. include:: /include/INTERFACE_INCLUDE_DIRECTORIES_WARNING.rst
diff --git a/Help/prop_fs/INTERFACE_SOURCES.rst b/Help/prop_fs/INTERFACE_SOURCES.rst new file mode 100644 index 0000000..ca9e1c1 --- /dev/null +++ b/Help/prop_fs/INTERFACE_SOURCES.rst
@@ -0,0 +1,34 @@ +INTERFACE_SOURCES +----------------- + +.. versionadded:: 4.4 + +List of interface sources to compile into consuming targets. +The :command:`target_sources` command sets or adds to the ``INTERFACE_SOURCES`` +file set property for the file sets defined with the ``PUBLIC`` or +``INTERFACE`` keyword and is the usual way to manipulate it. + +Contents of ``INTERFACE_SOURCES`` may use +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +Each file must be in one of the base directories, or a subdirectory of one of +the base directories. + +If relative paths are specified, they are considered relative to the target's +source directory. + +The following behavior applies for the :prop_fs:`SOURCES` and +``INTERFACE_SOURCES`` file set properties, dependent on the value of the +:prop_fs:`SCOPE` file set property: + +``PRIVATE`` + Only the :prop_fs:`SOURCES` property can be set. Any change to the + ``INTERFACE_SOURCES`` property will be ignored. + +``PUBLIC`` + :prop_fs:`SOURCES` and ``INTERFACE_SOURCES`` properties will always have the + same content. + +``INTERFACE`` + Only the ``INTERFACE_SOURCES`` property can be set. Any change to the + :prop_fs:`SOURCES` property will be ignored.
diff --git a/Help/prop_fs/JOB_POOL_COMPILE.rst b/Help/prop_fs/JOB_POOL_COMPILE.rst new file mode 100644 index 0000000..b460aff --- /dev/null +++ b/Help/prop_fs/JOB_POOL_COMPILE.rst
@@ -0,0 +1,28 @@ +JOB_POOL_COMPILE +---------------- + +.. versionadded:: 4.4 + +Ninja only: Pool used for compiling. + +The number of parallel compile processes could be limited by defining +pools with the global :prop_gbl:`JOB_POOLS` +property and then specifying here the pool name. + +This allows to override the :prop_sf:`JOB_POOL_COMPILE` and +:prop_tgt:`JOB_POOL_COMPILE` values for specific source files within a same +target. + +For instance: + +.. code-block:: cmake + + set_property(FILE_SET my_fileset TYPE SOURCE PROPERTY JOB_POOL_COMPILE two_jobs) + +This property is undefined by default. + +See Also +^^^^^^^^ + +* :prop_sf:`JOB_POOL_COMPILE` source file property +* :prop_tgt:`JOB_POOL_COMPILE` target property
diff --git a/Help/prop_fs/SCOPE.rst b/Help/prop_fs/SCOPE.rst new file mode 100644 index 0000000..e326e2e --- /dev/null +++ b/Help/prop_fs/SCOPE.rst
@@ -0,0 +1,10 @@ +SCOPE +----- + +.. versionadded:: 4.4 + +The scope of the file set. + +This read-only property can be used to retrieve the +:ref:`scope <Target Command Scope>` of the given file set. +It will be ``PRIVATE``, ``PUBLIC``, or ``INTERFACE``.
diff --git a/Help/prop_fs/SKIP_LINTING.rst b/Help/prop_fs/SKIP_LINTING.rst new file mode 100644 index 0000000..7c6546e --- /dev/null +++ b/Help/prop_fs/SKIP_LINTING.rst
@@ -0,0 +1,54 @@ +SKIP_LINTING +------------ + +.. versionadded:: 4.4 + +This property allows you to exclude a source files of the specific file set +from the linting process. The linting process involves running +tools such as :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`, +:prop_tgt:`<LANG>_CPPCHECK`, :prop_tgt:`<LANG>_ICSTAT`, +:prop_tgt:`<LANG>_PVS_STUDIO` and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE` on +the source files, as well as compiling header files as part of +:prop_tgt:`VERIFY_INTERFACE_HEADER_SETS`. By setting ``SKIP_LINTING`` on a +file set, the mentioned linting tools will not be executed for the files of +that particular file set. If the :prop_fs:`SKIP_LINTING` file set property +is set, it takes precedence over this source-file and target-wide properties. + +This is a convenience alternative to setting the :prop_sf:`SKIP_LINTING` +source file property individually on each source. + +Example +^^^^^^^ + +Consider a C++ project that includes multiple source files, +such as ``main.cpp``, ``things.cpp``, and ``generatedBindings.cpp``. +In this example, you want to exclude the ``things.cpp`` and +``generatedBindings.cpp`` files from the linting process. To achieve this, you +can use a file set and utilize the ``SKIP_LINTING`` property with the +:command:`set_property(FILE_SET) <set_property>` command as shown below: + +.. code-block:: cmake + + add_executable(MyApp main.cpp) + target_sources(MyApp PRIVATE FILE_SET skip_lint TYPE SOURCES + FILES things.cpp generatedBindings.cpp) + + set_property(FILE_SET skip_lint TARGET MyApp PROPERTY SKIP_LINTING ON) + +In the provided code snippet, the ``SKIP_LINTING`` property is set to true +for the ``skip_lint`` file set. As a result, when the linting +tools specified by :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`, +:prop_tgt:`<LANG>_CPPCHECK`, :prop_tgt:`<LANG>_ICSTAT` or +:prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE` are executed, they will skip analyzing +the ``things.cpp`` and ``generatedBindings.cpp`` files. + +By using the ``SKIP_LINTING`` file set property, you can selectively exclude +specific source files from the linting process. This allows you to focus the +linting tools on the relevant parts of your project, enhancing the efficiency +and effectiveness of the linting workflow. + +See Also +^^^^^^^^ + +* :prop_sf:`SKIP_LINTING` source file property +* :prop_tgt:`SKIP_LINTING` target property
diff --git a/Help/prop_fs/SKIP_PRECOMPILE_HEADERS.rst b/Help/prop_fs/SKIP_PRECOMPILE_HEADERS.rst new file mode 100644 index 0000000..cc635d2 --- /dev/null +++ b/Help/prop_fs/SKIP_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,20 @@ +SKIP_PRECOMPILE_HEADERS +----------------------- + +.. versionadded:: 4.4 + +The file set sources will be skipped by :prop_tgt:`PRECOMPILE_HEADERS` feature. + +This property helps with build problems that one would run into +when using the :prop_tgt:`PRECOMPILE_HEADERS` feature. + +One example would be the usage of Objective-C (``*.m``) files, and +Objective-C++ (``*.mm``) files, which lead to compilation failure +because they are treated (in case of Ninja / Makefile generator) +as C, and CXX respectively. The precompile headers are not +compatible between languages. + +See Also +^^^^^^^^ + +* :prop_sf:`SKIP_PRECOMPILE_HEADERS` source file property
diff --git a/Help/prop_fs/SKIP_UNITY_BUILD_INCLUSION.rst b/Help/prop_fs/SKIP_UNITY_BUILD_INCLUSION.rst new file mode 100644 index 0000000..5ea1daf --- /dev/null +++ b/Help/prop_fs/SKIP_UNITY_BUILD_INCLUSION.rst
@@ -0,0 +1,24 @@ +SKIP_UNITY_BUILD_INCLUSION +-------------------------- + +.. versionadded:: 4.4 + +Setting this property to true ensures the source files of the file set will be +skipped by unity builds when its associated target has its +:prop_tgt:`UNITY_BUILD` property set to true. The source files of the file set +will instead be compiled on their own in the same way as it would with unity +builds disabled. + +This property helps with "ODR (One definition rule)" problems where combining +a particular source file with others might lead to build errors or other +unintended side effects. + +Note that :ref:`file sets <File Sets>` of type ``HEADERS`` and ``CXX_MODULES`` +as well as the sources which are scanned for C++ modules (see +:manual:`cmake-cxxmodules(7)`) are not eligible for unity build inclusion and +will automatically be excluded. + +See Also +^^^^^^^^ + +* :prop_sf:`SKIP_UNITY_BUILD_INCLUSION` source file property
diff --git a/Help/prop_fs/SOURCES.rst b/Help/prop_fs/SOURCES.rst new file mode 100644 index 0000000..da97c59 --- /dev/null +++ b/Help/prop_fs/SOURCES.rst
@@ -0,0 +1,32 @@ +SOURCES +------- + +.. versionadded:: 4.4 + +This specifies the list of paths to sources for the file set. +The :command:`target_sources` command sets or adds to the ``SOURCES`` file set +property for the file sets defined with the ``PRIVATE`` or ``PUBLIC`` keyword +and is the usual way to manipulate it. + +Contents of ``SOURCES`` may use +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +Each file must be in one of the base directories, or a subdirectory of one of +the base directories. If relative paths are specified, they are considered +relative to the target's source directory. + +The following behavior applies for the ``SOURCES`` and +:prop_fs:`INTERFACE_SOURCES` file set properties, dependent on the value of the +:prop_fs:`SCOPE` file set property: + +``PRIVATE`` + Only the ``SOURCES`` property can be set. Any change to + the :prop_fs:`INTERFACE_SOURCES` property will be ignored. + +``PUBLIC`` + ``SOURCES`` and :prop_fs:`INTERFACE_SOURCES` properties will always have the + same content. + +``INTERFACE`` + Only the :prop_fs:`INTERFACE_SOURCES` property can be set. Any change to + ``SOURCES`` property will be ignored.
diff --git a/Help/prop_fs/TYPE.rst b/Help/prop_fs/TYPE.rst new file mode 100644 index 0000000..83a1005 --- /dev/null +++ b/Help/prop_fs/TYPE.rst
@@ -0,0 +1,10 @@ +TYPE +---- + +.. versionadded:: 4.4 + +The type of the file set. + +This read-only property can be used to retrieve the +:ref:`type <File Sets>` of the given file set. +Possible values are ``CXX_MODULES``, ``SOURCES``, or ``HEADERS``.
diff --git a/Help/prop_fs/include/INTERFACE_BUILD_PROPERTY.rst b/Help/prop_fs/include/INTERFACE_BUILD_PROPERTY.rst new file mode 100644 index 0000000..dba863c --- /dev/null +++ b/Help/prop_fs/include/INTERFACE_BUILD_PROPERTY.rst
@@ -0,0 +1,16 @@ +List of public |property_name| requirements for a file set. + +File sets may populate this property to publish the |property_name| +required to compile the sources for the target. The |command_name| +command populates this property. + +When target dependencies are specified using :command:`target_link_libraries`, +CMake will read this property from file sets of all target dependencies to +determine the build properties of the consumer. These build properties are only +applied to the sources of the file sets. The other sources of the consumer are +unaffected. + +Contents of |PROPERTY_INTERFACE_NAME| may use "generator expressions" +with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` +manual for available expressions. See the :manual:`cmake-buildsystem(7)` +-manual for more on defining buildsystem properties.
diff --git a/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst b/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst index d10969a..318a8e2 100644 --- a/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst +++ b/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst
@@ -5,12 +5,14 @@ ``CXX_SCAN_FOR_MODULES`` is a boolean specifying whether CMake will scan the source for C++ module dependencies. See also the -:prop_tgt:`CXX_SCAN_FOR_MODULES` for target-wide settings. +:prop_fs:`CXX_SCAN_FOR_MODULES` file set property which, if set, +overrides the per-source settings and the +:prop_tgt:`CXX_SCAN_FOR_MODULES` target property. When this property is set ``ON``, CMake will scan the source at build time and add module dependency information to the compile line as necessary. When this property is set ``OFF``, CMake will not scan the source at build time. When -this property is unset, the :prop_tgt:`CXX_SCAN_FOR_MODULES` property is +this property is unset, the :prop_tgt:`CXX_SCAN_FOR_MODULES` target property is consulted. Note that scanning is only performed if C++20 or higher is enabled for the
diff --git a/Help/prop_sf/HEADER_FILE_ONLY.rst b/Help/prop_sf/HEADER_FILE_ONLY.rst index 71d62ae..4575fab 100644 --- a/Help/prop_sf/HEADER_FILE_ONLY.rst +++ b/Help/prop_sf/HEADER_FILE_ONLY.rst
@@ -4,7 +4,7 @@ Is this source file only a header file. A property on a source file that indicates if the source file is a -header file with no associated implementation. This is set +header file that should not be compiled into an object file. This is set automatically based on the file extension and is used by CMake to determine if certain dependency information should be computed.
diff --git a/Help/prop_sf/JOB_POOL_COMPILE.rst b/Help/prop_sf/JOB_POOL_COMPILE.rst index 79409da..2c9a8d4 100644 --- a/Help/prop_sf/JOB_POOL_COMPILE.rst +++ b/Help/prop_sf/JOB_POOL_COMPILE.rst
@@ -19,3 +19,9 @@ set_property(SOURCE main.cc PROPERTY JOB_POOL_COMPILE two_jobs) This property is undefined by default. + +See Also +^^^^^^^^ + +* :prop_fs:`JOB_POOL_COMPILE` file set property +* :prop_tgt:`JOB_POOL_COMPILE` target property
diff --git a/Help/prop_sf/Rust_EMIT.rst b/Help/prop_sf/Rust_EMIT.rst new file mode 100644 index 0000000..548a485 --- /dev/null +++ b/Help/prop_sf/Rust_EMIT.rst
@@ -0,0 +1,39 @@ +Rust_EMIT +--------- + +.. versionadded:: 4.4 + +.. note:: + Experimental. Gated by ``CMAKE_EXPERIMENTAL_RUST``. + +This property controls the type of output generated by the Rust compiler. Can +be one of several values: + +``link`` + This is the default if the property is not set. The crate will be compiled + into an `rlib` file, e.g.: ``libfile.rs.rlib`` + + .. note:: + CMake will automatically prefix the generated ``rlib`` file + with ``lib`` as the Rust compiler always requires such a + prefix for external crates. + +``obj`` + Generate a native object file, e.g.: ``file.rs.o`` + +``asm`` + Generate an assembly file, e.g.: ``file.rs.s`` + +.. note:: + The ``obj`` and ``asm`` output types are known to have the following + limitations: + + * When enabled, the Rust compiler disables multiple codegen units and + ThinLTO. Depending on the situation, this can affect the generated code, + optimizations. It also reduce the internal parallelism inside and the + compiler and can reduce the effectiveness of incremental rebuilds. + * The generated ``obj`` files cannot be used as external crates, so other + Rust code can only use C-style API from them. + * The generated ``obj`` files cannot be linked as-is with native linkers, + additional crates from the Rust toolchain needs to linked too. The actual + crates to link depend on the code and compiler options.
diff --git a/Help/prop_sf/SKIP_LINTING.rst b/Help/prop_sf/SKIP_LINTING.rst index 5d2c0c4..07e5cd1 100644 --- a/Help/prop_sf/SKIP_LINTING.rst +++ b/Help/prop_sf/SKIP_LINTING.rst
@@ -43,7 +43,11 @@ linting tools on the relevant parts of your project, enhancing the efficiency and effectiveness of the linting workflow. +A convenient alternative, when multiple sources should be excluded from the +linting process, is to use the file set's :prop_fs:`SKIP_LINTING` property. + See Also ^^^^^^^^ +* :prop_fs:`SKIP_LINTING` file set property * :prop_tgt:`SKIP_LINTING` target property
diff --git a/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst b/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst index 660de3f..b92d5c8 100644 --- a/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst +++ b/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst
@@ -13,3 +13,8 @@ because they are treated (in case of Ninja / Makefile generator) as C, and CXX respectively. The precompile headers are not compatible between languages. + +See Also +^^^^^^^^ + +* :prop_fs:`SKIP_PRECOMPILE_HEADERS` file set property
diff --git a/Help/prop_sf/SKIP_UNITY_BUILD_INCLUSION.rst b/Help/prop_sf/SKIP_UNITY_BUILD_INCLUSION.rst index 38a0a78..77da455 100644 --- a/Help/prop_sf/SKIP_UNITY_BUILD_INCLUSION.rst +++ b/Help/prop_sf/SKIP_UNITY_BUILD_INCLUSION.rst
@@ -15,3 +15,8 @@ Note that sources which are scanned for C++ modules (see :manual:`cmake-cxxmodules(7)`) are not eligible for unity build inclusion and will automatically be excluded. + +See Also +^^^^^^^^ + +* :prop_fs:`SKIP_UNITY_BUILD_INCLUSION` file set property
diff --git a/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst b/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst index 4bf83eb..bc8029c 100644 --- a/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst +++ b/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst
@@ -8,10 +8,13 @@ .. note:: - If Qt 5.15 or later is used and the generator is either :generator:`Ninja` - or :ref:`Makefile Generators`, origin target dependencies are forwarded to - the :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` - target instead of :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`. + If the + :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` + target is created, origin target dependencies are forwarded to it instead + of :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`. When using the + :ref:`Visual Studio Generators`, the ``moc`` and ``uic`` step may be part + of the ``<ORIGIN>`` target itself, in which case they need no forwarding. + See the :manual:`cmake-qt(7)` manual for details. Targets which have their :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC` property ``ON`` have a corresponding :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target
diff --git a/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst b/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst index 27f1fd3..930ba33 100644 --- a/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst +++ b/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst
@@ -6,10 +6,13 @@ .. note:: - If Qt 5.15 or later is used and the generator is either :generator:`Ninja` - or :ref:`Makefile Generators`, additional target dependencies are added to - the :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` - target instead of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target. + If the + :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` + target is created, additional target dependencies are added to it instead + of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target. When using the + :ref:`Visual Studio Generators`, the ``moc`` and ``uic`` step may be part + of the ``<ORIGIN>`` target itself, in which case the dependencies are added + to ``<ORIGIN>``. See the :manual:`cmake-qt(7)` manual for details. Targets which have their :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC` property
diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst index 25c5632..1ec36f8 100644 --- a/Help/prop_tgt/AUTOMOC.rst +++ b/Help/prop_tgt/AUTOMOC.rst
@@ -228,8 +228,8 @@ The generation of this file can be enabled or disabled in this target property. :prop_tgt:`AUTOMOC_INCLUDE_DIRECTORIES`: -Specifies one or more include directories for ``AUTOMOC`` to pass explicitly to ``moc`` -instead of automatically discovering a target’s include directories. +Specifies one or more include directories for ``AUTOMOC`` to pass explicitly +to ``moc`` instead of automatically discovering a target's include directories. :prop_sf:`SKIP_AUTOMOC`: Sources and headers can be excluded from ``AUTOMOC`` processing by
diff --git a/Help/prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES.rst b/Help/prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES.rst index a280ce1..70e34e3 100644 --- a/Help/prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES.rst +++ b/Help/prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES.rst
@@ -4,7 +4,7 @@ .. versionadded:: 4.1 Specifies zero or more include directories for AUTOMOC to pass explicitly to -the Qt Meta‑Object Compiler (``moc``) instead of automatically discovering a +the Qt Meta-Object Compiler (``moc``) instead of automatically discovering a target's include directories. When this property is set on a target, only the directories listed here will be
diff --git a/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst b/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst index 791914e..d61846d 100644 --- a/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst +++ b/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst
@@ -5,7 +5,8 @@ ``CXX_SCAN_FOR_MODULES`` is a boolean specifying whether CMake will scan C++ sources in the target for module dependencies. See also the -:prop_sf:`CXX_SCAN_FOR_MODULES` for per-source settings which, if set, +:prop_sf:`CXX_SCAN_FOR_MODULES` for per-source settings and +:prop_fs:`CXX_SCAN_FOR_MODULES` for file set settings. If either is set, it overrides the target-wide settings. This property is initialized by the value of the
diff --git a/Help/prop_tgt/INTERFACE_SOURCE_SETS.rst b/Help/prop_tgt/INTERFACE_SOURCE_SETS.rst new file mode 100644 index 0000000..4aba928 --- /dev/null +++ b/Help/prop_tgt/INTERFACE_SOURCE_SETS.rst
@@ -0,0 +1,14 @@ +INTERFACE_SOURCE_SETS +--------------------- + +.. versionadded:: 4.4 + +Read-only list of the target's ``INTERFACE`` and ``PUBLIC`` source sets (i.e. +all file sets with the type ``SOURCES``). Files listed in these source sets +can be installed with :command:`install(TARGETS)` and exported with +:command:`install(EXPORT)` and :command:`export`. + +Source sets may be defined using the :command:`target_sources` command +``FILE_SET`` option with type ``SOURCES``. + +See also :prop_tgt:`SOURCE_SETS`.
diff --git a/Help/prop_tgt/JOB_POOL_COMPILE.rst b/Help/prop_tgt/JOB_POOL_COMPILE.rst index 5d8e940..d70b010 100644 --- a/Help/prop_tgt/JOB_POOL_COMPILE.rst +++ b/Help/prop_tgt/JOB_POOL_COMPILE.rst
@@ -15,3 +15,9 @@ This property is initialized by the value of :variable:`CMAKE_JOB_POOL_COMPILE`. + +See Also +^^^^^^^^ + +* :prop_fs:`JOB_POOL_COMPILE` file set property +* :prop_sf:`JOB_POOL_COMPILE` source file property
diff --git a/Help/prop_tgt/LINK_WARNING_AS_ERROR.rst b/Help/prop_tgt/LINK_WARNING_AS_ERROR.rst index c3f7566..cd812cd 100644 --- a/Help/prop_tgt/LINK_WARNING_AS_ERROR.rst +++ b/Help/prop_tgt/LINK_WARNING_AS_ERROR.rst
@@ -35,6 +35,7 @@ * ``MOLD`` * ``MSVC`` * ``Solaris`` +* ``WILD`` This property is initialized by the value of the variable :variable:`CMAKE_LINK_WARNING_AS_ERROR` if it is set when a target is
diff --git a/Help/prop_tgt/Rust_EDITION.rst b/Help/prop_tgt/Rust_EDITION.rst new file mode 100644 index 0000000..454b1b1 --- /dev/null +++ b/Help/prop_tgt/Rust_EDITION.rst
@@ -0,0 +1,25 @@ +Rust_EDITION +------------ + +.. versionadded:: 4.4 + +.. note:: + Experimental. Gated by ``CMAKE_EXPERIMENTAL_RUST``. + +The Rust edition required to build this target. It is initialized from the +:variable:`CMAKE_Rust_EDITION` variable if defined. Setting this property +results in adding a flag such as ``--edition=2018`` to the compile line. + +Supported values are: + +``2015`` + Rust 2015, the default edition, started with Rust 1.0. + +``2018`` + Rust 2018, available since Rust 1.31. + +``2021`` + Rust 2021, available since Rust 1.56. + +``2024`` + Rust 2024, available since Rust 1.85.
diff --git a/Help/prop_tgt/SKIP_LINTING.rst b/Help/prop_tgt/SKIP_LINTING.rst index 031f653..14437a2 100644 --- a/Help/prop_tgt/SKIP_LINTING.rst +++ b/Help/prop_tgt/SKIP_LINTING.rst
@@ -10,16 +10,19 @@ :prop_tgt:`<LANG>_CPPCHECK`, :prop_tgt:`<LANG>_ICSTAT` and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`) will not be invoked for source files compiled by the target. If the :prop_sf:`SKIP_LINTING` source-file property -is set on a specific source, it takes precedence over this target-wide property. +is set on a specific source or if the :prop_fs:`SKIP_LINTING` file set property +is set, it takes precedence over this target-wide property. This is a convenience alternative to setting the :prop_sf:`SKIP_LINTING` source file property individually on each source. If either the target's -:prop_tgt:`SKIP_LINTING` or a source’s :prop_sf:`SKIP_LINTING` is enabled, -that source will be excluded from linting. +:prop_tgt:`SKIP_LINTING` or file set's :prop_fs:`SKIP_LINTING` including this +source or a source's :prop_sf:`SKIP_LINTING` is enabled, that source will be +excluded from linting. The property has no effect on targets that do not have sources. See Also ^^^^^^^^ +* :prop_fs:`SKIP_LINTING` file set property * :prop_sf:`SKIP_LINTING` source file property
diff --git a/Help/prop_tgt/SOURCE_DIRS.rst b/Help/prop_tgt/SOURCE_DIRS.rst new file mode 100644 index 0000000..4442aa5 --- /dev/null +++ b/Help/prop_tgt/SOURCE_DIRS.rst
@@ -0,0 +1,14 @@ +SOURCE_DIRS +----------- + +.. versionadded:: 4.4 + +Semicolon-separated list of base directories of the target's default +source set (i.e. the file set with name and type ``SOURCES``). The property +supports :manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`SOURCE_DIRS_<NAME>` for the list of base directories in +other source sets.
diff --git a/Help/prop_tgt/SOURCE_DIRS_NAME.rst b/Help/prop_tgt/SOURCE_DIRS_NAME.rst new file mode 100644 index 0000000..22f198f --- /dev/null +++ b/Help/prop_tgt/SOURCE_DIRS_NAME.rst
@@ -0,0 +1,15 @@ +SOURCE_DIRS_<NAME> +------------------ + +.. versionadded:: 4.4 + +Semicolon-separated list of base directories of the target's ``<NAME>`` +source set, which has the set type ``SOURCES``. The property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`SOURCE_DIRS` for the list of base directories in the +default source set. See :prop_tgt:`SOURCE_SETS` for the file set names of all +source sets.
diff --git a/Help/prop_tgt/SOURCE_SET.rst b/Help/prop_tgt/SOURCE_SET.rst new file mode 100644 index 0000000..c7f517e --- /dev/null +++ b/Help/prop_tgt/SOURCE_SET.rst
@@ -0,0 +1,15 @@ +SOURCE_SET +---------- + +.. versionadded:: 4.4 + +Semicolon-separated list of files in the target's default source set, +(i.e. the file set with name and type ``SOURCES``). If any of the paths +are relative, they are computed relative to the target's source directory. +The property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`SOURCE_SET_<NAME>` for the list of files in other source sets.
diff --git a/Help/prop_tgt/SOURCE_SETS.rst b/Help/prop_tgt/SOURCE_SETS.rst new file mode 100644 index 0000000..b6fad22 --- /dev/null +++ b/Help/prop_tgt/SOURCE_SETS.rst
@@ -0,0 +1,14 @@ +SOURCE_SETS +----------- + +.. versionadded:: 4.4 + +Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` source sets (i.e. +all file sets with the type ``SOURCES``). Files listed in these file sets are +treated as source files. + +Source sets may be defined using the :command:`target_sources` command +``FILE_SET`` option with type ``SOURCES``. + +See also :prop_tgt:`SOURCE_SET_<NAME>`, :prop_tgt:`SOURCE_SET` and +:prop_tgt:`INTERFACE_SOURCE_SETS`.
diff --git a/Help/prop_tgt/SOURCE_SET_NAME.rst b/Help/prop_tgt/SOURCE_SET_NAME.rst new file mode 100644 index 0000000..9eef526 --- /dev/null +++ b/Help/prop_tgt/SOURCE_SET_NAME.rst
@@ -0,0 +1,15 @@ +SOURCE_SET_<NAME> +----------------- + +.. versionadded:: 4.4 + +Semicolon-separated list of files in the target's ``<NAME>`` source set, +which has the set type ``SOURCES``. If any of the paths are relative, +they are computed relative to the target's source directory. The property +supports :manual:`generator expressions <cmake-generator-expressions(7)>`. + +This property is normally only set by :command:`target_sources(FILE_SET)` +rather than being manipulated directly. + +See :prop_tgt:`SOURCE_SET` for the list of files in the default source set. +See :prop_tgt:`SOURCE_SETS` for the file set names of all source sets.
diff --git a/Help/prop_tgt/Swift_MODULE_NAME.rst b/Help/prop_tgt/Swift_MODULE_NAME.rst index d941b54..8e7ee6b 100644 --- a/Help/prop_tgt/Swift_MODULE_NAME.rst +++ b/Help/prop_tgt/Swift_MODULE_NAME.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.15 This property specifies the name of the Swift module. It is defaulted to the -name of the target. +name of the target, with hyphens replaced by underscores (hyphens are not valid +in Swift module identifiers).
diff --git a/Help/prop_tgt/Swift_PACKAGE_NAME.rst b/Help/prop_tgt/Swift_PACKAGE_NAME.rst new file mode 100644 index 0000000..539e033 --- /dev/null +++ b/Help/prop_tgt/Swift_PACKAGE_NAME.rst
@@ -0,0 +1,21 @@ +Swift_PACKAGE_NAME +------------------ + +.. versionadded:: 4.4 + +Specify the Swift package name for a target. + +This property sets the package name passed to the Swift compiler via the +``-package-name`` flag, which enables the package access control in Swift +code. + +.. note:: + + Package access control was added in Swift 5.8 and Xcode 15. This target + property has no effect when using an earlier version of Swift or Xcode. + +If the property is not set, the :variable:`PROJECT_NAME` variable is used +as the default package name. See policy :policy:`CMP0216`. + +Setting this property to the empty string will result in the target being +compiled without a package name.
diff --git a/Help/prop_tgt/Swift_SEPARATE_MODULE_EMISSION.rst b/Help/prop_tgt/Swift_SEPARATE_MODULE_EMISSION.rst new file mode 100644 index 0000000..81c61b6 --- /dev/null +++ b/Help/prop_tgt/Swift_SEPARATE_MODULE_EMISSION.rst
@@ -0,0 +1,24 @@ +Swift_SEPARATE_MODULE_EMISSION +------------------------------ + +.. versionadded:: 4.4 + +Emit Swift modules separately from compilation in the :ref:`Ninja Generators`. + +When this property is set to ``ON`` on an importable Swift target built by +:ref:`Ninja Generators`, CMake emits a separate ``-emit-module`` build edge +that produces the ``.swiftmodule`` independently of object compilation. +This allows downstream Swift targets to begin compiling as soon as the +module interface is available, rather than waiting for all upstream +compilation outputs. + +The property is initialized from the value of the +:variable:`CMAKE_Swift_SEPARATE_MODULE_EMISSION` variable, if it is set. +If this property is not set, separate module emission is enabled by default. +See policy :policy:`CMP0215`. + +.. note:: + + Separate module emission takes effect only when policy :policy:`CMP0157` + is set to ``NEW`` prior to the first :command:`project` or + :command:`enable_language` command that enables the Swift language.
diff --git a/Help/prop_tgt/UNITY_BUILD.rst b/Help/prop_tgt/UNITY_BUILD.rst index ee00171..4fc8d64 100644 --- a/Help/prop_tgt/UNITY_BUILD.rst +++ b/Help/prop_tgt/UNITY_BUILD.rst
@@ -11,8 +11,8 @@ CMake provides different algorithms for selecting which sources are grouped together into a *bucket*. Algorithm selection is decided by the -:prop_tgt:`UNITY_BUILD_MODE` target property, which has the following acceptable -values: +:prop_tgt:`UNITY_BUILD_MODE` target property, which has the following +acceptable values: * ``BATCH`` When in this mode CMake determines which files are grouped together. @@ -79,6 +79,11 @@ :prop_sf:`INCLUDE_DIRECTORIES` source property will not be combined into a unity source. +* .. versionadded:: 4.4 + Any file set that has a non-empty :prop_fs:`COMPILE_OPTIONS`, + :prop_fs:`COMPILE_DEFINITIONS`, or :prop_fs:`INCLUDE_DIRECTORIES` + file set property will not be combined into a unity source. + * Any source file which is scanned for C++ module sources via :prop_tgt:`CXX_SCAN_FOR_MODULES`, :prop_sf:`CXX_SCAN_FOR_MODULES`, or membership of a ``CXX_MODULES`` file set will not be combined into a unity @@ -90,6 +95,13 @@ problems with specific files than disabling unity builds for an entire target. +* .. versionadded:: 4.4 + Projects can prevent all the source files of a file set from being combined + into a unity source by setting its :prop_fs:`SKIP_UNITY_BUILD_INCLUSION` + file set property to true. This can be a more effective way to prevent + problems with specific files than disabling unity builds for an entire + target. + * Projects can set :prop_tgt:`UNITY_BUILD_UNIQUE_ID` to cause a valid C-identifier to be generated which is unique per file in a unity build. This can be used to avoid problems with anonymous namespaces
diff --git a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst index a14f7c8..59cd104 100644 --- a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst +++ b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst
@@ -3,16 +3,27 @@ .. versionadded:: 3.24 +.. versionchanged:: 4.4 + ``OBJC`` and ``OBJCXX`` languages are now supported in addition to ``C`` + and ``CXX``. + Used to verify that all headers in a target's ``PUBLIC`` and ``INTERFACE`` header sets can be included on their own. +.. versionchanged:: 4.4 + Previously, the verification target was only created when the target had + at least one matching header. Now it is always created when the property + is enabled. + When this property is set to true, and the target is an object library, static library, shared library, interface library, or executable (subject to policy -:policy:`CMP0209`) and the target has one or more ``PUBLIC`` or ``INTERFACE`` -header sets, an object library target named +:policy:`CMP0209`), an object library target named ``<target_name>_verify_interface_header_sets`` is created. This verification -target has one source file per header in the ``PUBLIC`` and ``INTERFACE`` -header sets. Each source file only includes its associated header file. +target has one source file per header in the target's ``PUBLIC`` and +``INTERFACE`` header sets. Each source file only includes its associated +header file. If the target has no matching header sets, a utility target is +created instead so that the target name always exists for build system +dependencies. The verification target links against the original target to get all of its usage requirements.
diff --git a/Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst b/Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst index df9d183..889a207 100644 --- a/Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst +++ b/Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst
@@ -3,16 +3,26 @@ .. versionadded:: 4.3 +.. versionchanged:: 4.4 + ``OBJC`` and ``OBJCXX`` languages are now supported in addition to ``C`` + and ``CXX``. + Used to verify that all headers in a target's ``PUBLIC`` and ``PRIVATE`` header sets can be included on their own. +.. versionchanged:: 4.4 + Previously, the verification target was only created when the target had + at least one matching header. Now it is always created when the property + is enabled. + When this property is set to true, and the target is an object library, static -library, shared library, module library, interface library, or executable, and -the target has one or more ``PUBLIC`` or ``PRIVATE`` header sets, an object -library target named ``<target_name>_verify_private_header_sets`` is created. -This verification target has one source file per header in the ``PUBLIC`` and -``PRIVATE`` header sets. Each source file only includes its associated header -file. +library, shared library, module library, interface library, or executable, an +object library target named ``<target_name>_verify_private_header_sets`` is +created. This verification target has one source file per header in the +target's ``PUBLIC`` and ``PRIVATE`` header sets. Each source file only +includes its associated header file. If the target has no matching header +sets, a utility target is created instead so that the target name always +exists for build system dependencies. Properties affecting compilation are copied from the original target to the verification target so that the headers will be interpreted the same way by
diff --git a/Help/prop_tgt/VS_SOLUTION_DEPLOY.rst b/Help/prop_tgt/VS_SOLUTION_DEPLOY.rst index e56f411..6c19012 100644 --- a/Help/prop_tgt/VS_SOLUTION_DEPLOY.rst +++ b/Help/prop_tgt/VS_SOLUTION_DEPLOY.rst
@@ -11,6 +11,10 @@ :manual:`Generator expressions <cmake-generator-expressions(7)>` are supported. +.. versionadded:: 4.4 + + Targets created by :command:`add_custom_target` now honor this property. + Examples ^^^^^^^^
diff --git a/Help/prop_tgt/include/MSVC_RUNTIME_LIBRARY-VALUES.rst b/Help/prop_tgt/include/MSVC_RUNTIME_LIBRARY-VALUES.rst index 5f8b82d..533c4f5 100644 --- a/Help/prop_tgt/include/MSVC_RUNTIME_LIBRARY-VALUES.rst +++ b/Help/prop_tgt/include/MSVC_RUNTIME_LIBRARY-VALUES.rst
@@ -1,12 +1,21 @@ +``""`` (set but empty) + No runtime library selection flag will be added explicitly by CMake. + + Note that with :ref:`Visual Studio Generators` the native build system + may choose to add its own default runtime library selection flag. + ``MultiThreaded`` Compile with ``-MT`` or equivalent flag(s) to use a multi-threaded statically-linked runtime library. + ``MultiThreadedDLL`` Compile with ``-MD`` or equivalent flag(s) to use a multi-threaded dynamically-linked runtime library. + ``MultiThreadedDebug`` Compile with ``-MTd`` or equivalent flag(s) to use a multi-threaded statically-linked runtime library. + ``MultiThreadedDebugDLL`` Compile with ``-MDd`` or equivalent flag(s) to use a multi-threaded dynamically-linked runtime library. @@ -14,8 +23,3 @@ The value is ignored on compilers not targeting the MSVC ABI, but an unsupported value will be rejected as an error when using a compiler targeting the MSVC ABI. - -The value may also be the empty string (``""``) in which case no runtime -library selection flag will be added explicitly by CMake. Note that with -:ref:`Visual Studio Generators` the native build system may choose to -add its own default runtime library selection flag.
diff --git a/Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst b/Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst index f190213..b8e8d07 100644 --- a/Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst +++ b/Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst
@@ -6,11 +6,16 @@ If the header's :prop_sf:`LANGUAGE` property is set, the value of that property is used to determine the language with which to compile the header file. -Otherwise, if the target has any C++ sources, the header is compiled as C++. -Otherwise, if the target has any C sources, the header is compiled as C. -Otherwise, if C++ is enabled globally, the header is compiled as C++. -Otherwise, if C is enabled globally, the header is compiled as C. Otherwise, -the header file is not compiled. +The supported languages are ``C``, ``CXX``, ``OBJC``, and ``OBJCXX``. +If the language is not one of those four, the header file is not compiled. + +Otherwise, the language is inferred from the target's sources using the +following precedence: ``OBJCXX`` takes priority over all others; if both +``CXX`` and ``OBJC`` sources are present, the header is compiled as +``OBJCXX``; otherwise ``CXX`` takes precedence over ``OBJC``, and ``OBJC`` +takes precedence over ``C``. If no matching sources are found, the same +precedence is applied to the globally enabled languages. If no supported +language is found, the header file is not compiled. If the header's :prop_sf:`SKIP_LINTING` property is set to true, the file is not compiled. @@ -21,11 +26,10 @@ different, leading to the compiler interpreting the contents of the header differently. -If any |xxx| file set verification targets are created, a top-level target -called |THIS_ALL_TARGET| is created which depends on all |xxx| verification -targets. Another target called ``all_verify_header_sets`` is also created -which depends on |THIS_ALL_TARGET|, and on |COMPLEMENTARY_ALL_TARGET| if it -exists (see |COMPLEMENTARY_PROPERTY|). +A top-level target called |THIS_ALL_TARGET| is created which depends on all +|xxx| verification targets. Another target called ``all_verify_header_sets`` +is also created which depends on |THIS_ALL_TARGET|, and on +|COMPLEMENTARY_ALL_TARGET| if it exists (see |COMPLEMENTARY_PROPERTY|). This property is initialized by the value of the |INIT_VARIABLE| variable if it is set when a target is created.
diff --git a/Help/release/3.23.rst b/Help/release/3.23.rst index 20e43c5..ece6941 100644 --- a/Help/release/3.23.rst +++ b/Help/release/3.23.rst
@@ -19,7 +19,7 @@ which allows the files to include other files. * :manual:`cmake-presets(7)` files now support a ``${fileDir}`` macro, which - contains the directory containing the preset file. + contains the directory containing the presets file. * :manual:`cmake-presets(7)` gained support for specifying the ``resolvePackageReferences`` command line option in a build preset to control
diff --git a/Help/release/3.29.rst b/Help/release/3.29.rst index 4916ca4..fcbb735 100644 --- a/Help/release/3.29.rst +++ b/Help/release/3.29.rst
@@ -133,7 +133,7 @@ CTest ----- -* :manual:`ctest(1)` gained a :option:`--http-header <ctest --http-header>` +* :manual:`ctest(1)` gained a :ctest-dashboard-option:`--http-header` option to add custom headers on submission to CDash. * :manual:`ctest(1)` gained the :option:`--tests-from-file <ctest
diff --git a/Help/release/4.4.rst b/Help/release/4.4.rst new file mode 100644 index 0000000..ff4e319 --- /dev/null +++ b/Help/release/4.4.rst
@@ -0,0 +1,461 @@ +CMake 4.4 Release Notes +*********************** + +.. only:: html + + .. contents:: + +Changes made since CMake 4.3 include the following. + +New Features +============ + +Diagnostics +----------- + +* CMake diagnostic actions are now tracked in a new state type managed by the + :command:`cmake_diagnostic` command. This replaces the old system which + sometimes used variables to manage reporting actions and sometimes did not + allow management beyond command-line options and presets. The new system + also introduces several new diagnostic categories and significantly reduces + the cost of adding additional categories in the future. See the + :manual:`cmake-diagnostics(7)` manual for a list of available categories. + +* The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic category + was added to diagnose :command:`install` commands that specify an + absolute ``DESTINATION``. This diagnostic may be controlled with the + :option:`-Winstall-absolute-destination <cmake -W>` command-line option, + the ``installAbsoluteDestination`` field in a + :manual:`CMake Presets <cmake-presets(7)>` + :preset:`configurePresets.warnings` object, + or the :command:`cmake_diagnostic` command. + +Presets +------- + +* :manual:`cmake-presets(7)` files now support schema version 12. + +* :manual:`cmake(1)`, :manual:`ctest(1)`, and :manual:`cpack(1)` gained + ``--presets-file`` arguments to support loading + :manual:`presets <cmake-presets(7)>` from the specified file instead of + ``CMakePresets.json`` and/or ``CMakeUserPresets.json``. See + :option:`cmake --presets-file`, + :option:`cmake --build --presets-file <cmake--build --presets-file>`, + :option:`cmake --workflow --presets-file <cmake--workflow --presets-file>`, + :option:`ctest --presets-file`, and :option:`cpack --presets-file` for details. + +* :manual:`cmake-presets(7)`, in schema version 12 and above, now expand the + :ref:`${fileDir} <CMakePresets fileDir>` macro to the directory of the + presets file containing the ``${fileDir}`` macro, regardless of whether + it is inherited by another preset in a different directory. + +* :manual:`cmake-presets(7)` gained support for a + ``testPassthroughArguments`` field in the + test preset ``execution`` object for + forwarding arguments to test executables via presets. + +File-Based API +-------------- + +* The :manual:`cmake-file-api(7)` "codemodel" version 2 object has been + updated to 2.11. + +* The :manual:`cmake-file-api(7)` "codemodel" version 2 "target" object's + "sources" and "interfaceSources" objects gained new ``fileSetIndexes`` fields + to support sources belonging to multiple file sets. The ``fileSetIndex`` + fields are maintained for backwards compatibility only, and users are advised + to port to the new fields. + +* The :manual:`cmake-file-api(7)` "codemodel" version 2 "target" object's + "sources" object gained a new ``backtraces`` field to represent multiple + command invocations which associate a single source for a given target with + multiple file sets. The ``backtrace`` field is maintained for backwards + compatibility only, and users are advised to port to the new field. + +* The :manual:`cmake-file-api(7)` "codemodel" version 2 "installers" object's + optional ``fileSetType`` field gained a new ``SOURCES`` value to incorporate + the new ``SOURCES`` :ref:`file set <file sets>` type in codemodel replies. + +Instrumentation +--------------- + +* The :manual:`cmake-instrumentation(7)` data version has been updated to 1.1. +* :manual:`cmake-instrumentation(7)` gained a ``captureOutput`` option which + collects the ``stdout`` and ``stderr`` of instrumented commands. +* :manual:`cmake-instrumentation(7)` gained a ``compileTrace`` option which + records compiler-generated trace files (e.g., from Clang's ``-ftime-trace`` + flag). + +Generators +---------- + +* The :ref:`Ninja Generators` now support generating a build target named + ``test_prep/<test-name>`` for each test added by :command:`add_test`, which + builds all dependencies for that test. Build dependencies are registered for + an executable target invoked by the test, targets referenced in generator + expressions in the test command, and explicit dependencies added using the + new ``BUILD_DEPENDS`` option. This behavior is enabled by the new + :variable:`CMAKE_TEST_BUILD_DEPENDS` variable. A ``test_prep/all`` + target is also generated to depend on every ``test_prep/<test-name>`` + target. Tests with names that are not valid target names are excluded from + this behavior. If multiple tests in different directories share the same + name, their dependencies are merged into one ``test_prep/<test-name>`` + target. + +Languages +--------- + +* CUDA language level 23 (corresponding to C++ 23) is now supported with + NVCC 13.3 and above. + +Compilers +--------- + +* C++ 20 named modules are now supported with ``clang-cl``, except with the + :ref:`Visual Studio Generators` (i.e., the ``ClangCl`` + :variable:`toolset <CMAKE_GENERATOR_TOOLSET>`). + See :manual:`cmake-cxxmodules(7)`. + +* The ``ibm-flang`` compiler is now supported with + :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` ``IBMFlang``. + +* The `Pelles C`_ toolchain is now supported with + :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` ``PellesC``. + + The ``ASM_POASM`` language was added to support the Pelles Macro Assembler. + +* The Open Watcom ``wlink`` linker is now identified with + :variable:`CMAKE_<LANG>_COMPILER_LINKER_ID` ``OpenWatcom``. + +* The `wild linker <https://github.com/wild-linker/wild>`_ is now + supported with :variable:`CMAKE_<LANG>_COMPILER_LINKER_ID` ``WILD``. + +.. _`Pelles C`: https://pellesc.se + +Command-Line +------------ + +* :manual:`cmake(1)` now supports ``--build <dir> --preset <pre>`` + regardless of the current working directory. + +* The :option:`cmake -D` command-line option now populates a + default :prop_cache:`HELPSTRING` for variables known to CMake. + +* :option:`cmake --install` now supports installing multiple components. + +* :option:`cmake --install` now accepts ``-j<N>`` in addition to ``-j <N>`` + for specifying the number of parallel jobs. + +* The :option:`cmake -E` commands + :option:`copy_if_different <cmake-E copy_if_different>`, + :option:`copy_if_newer <cmake-E copy_if_newer>`, + :option:`copy_directory <cmake-E copy_directory>`, + :option:`copy_directory_if_different <cmake-E copy_directory_if_different>`, + and :option:`copy_directory_if_newer <cmake-E copy_directory_if_newer>` + gained support for the ``-t`` argument. + +* The :option:`cmake -E tar <cmake-E tar>` command-line tool gained the + :option:`--cmake-tar-encoding <cmake-E_tar --cmake-tar-encoding>` flag + to specify the pathname encoding used in archives. + +Commands +-------- + +* The :command:`block` command can now create a ``DIAGNOSTICS`` scope. + +* The :command:`cmake_diagnostic` command was added to manipulate CMake's + diagnostic state. + +* The :command:`cmake_parse_arguments` command gained a new ``PARSE_ARGN`` + signature that starts parsing after the last named argument of the calling + function and works exactly like ``PARSE_ARGV`` with ``<N>`` being the number + of parameters in the function definition. + +* The :command:`discover_tests` command was added to support registering tests + discovered at test time by :manual:`ctest(1)`. It runs a user-provided + discovery command, parses its output, and generates test names, arguments, + and properties from regular-expression captures. + +* The :command:`execute_process` command gained + ``ENVIRONMENT`` and ``ENVIRONMENT_MODIFICATION`` options to run child + processes with a modified environment. + +* The :command:`file(ARCHIVE_CREATE)` and :command:`file(ARCHIVE_EXTRACT)` + commands gained an ``ENCODING`` option to specify the encoding of the + archive pathnames. The default is now ``UTF-8``. + See policy :policy:`CMP0213`. + +* The :command:`file(READ_SYMLINK)` command gained a ``RESULT`` + option to capture errors without failing. + +* The :command:`if` command gained a new ``DIAGNOSTIC`` operator that evaluates + to true if a given diagnostic category name is known to CMake. + +* The :command:`include` command now has a ``NO_DIAGNOSTIC_SCOPE`` option. + +* The :command:`list(FILTER)` command gained a new ``PREDICATE`` mode + that invokes a user-defined :command:`function` to decide which elements are + included or excluded, complementing the existing ``REGEX`` mode. + +* The :command:`list(SORT)` command gained a new ``COMPARATOR`` keyword + that invokes a user-defined :command:`function` to compare elements, complementing + the built-in ``COMPARE`` methods. + +* The :command:`list(TRANSFORM)` command gained a new ``APPLY`` action that + invokes a user-defined :command:`function` for each element of the list, + enabling arbitrary per-element transformations. + +* The :command:`list(TRANSFORM)` command gained a new ``PREDICATE`` selector + that invokes a user-defined :command:`function` to decide which elements are + transformed. + +* The :command:`string(JSON ... PARTIAL_EQUAL) <string(JSON-PARTIAL_EQUAL)>` + command was added to compare two JSON values using *partial* equality. + +File Sets +--------- + +* A ``SOURCES`` :ref:`file set <file sets>` type was added to specify + a target's source files via file sets. + +* The :prop_fs:`CXX_SCAN_FOR_MODULES` file set property was added to + control whether a file set's sources are scanned for C++ modules. + +* The :prop_fs:`INDEPENDENT_FILES` file set property was added to + specify that a file set's sources do not depend on any generated files. + +* The :prop_fs:`JOB_POOL_COMPILE` file set property was added to + assign a file set's sources to a specific Ninja job pool. + +* The :prop_fs:`SCOPE`, :prop_fs:`TYPE`, :prop_fs:`BASE_DIRS`, + :prop_fs:`SOURCES`, and :prop_fs:`INTERFACE_SOURCES` file set + properties were added to provide the declared details of a file set. + +* The :prop_fs:`SKIP_LINTING` file set property was added to exclude + a file set's sources from target-wide code checks. + +* The :prop_fs:`SKIP_PRECOMPILE_HEADERS` file set property was added + to avoid using precompiled headers for a file set's sources. + +* The :prop_fs:`SKIP_UNITY_BUILD_INCLUSION` file set property was added + to exclude a file set's sources from inclusion in unity builds. + +Variables +--------- + +* The :variable:`CMAKE_INSTALL_DEFAULT_COMPONENT_NAME` variable gained + support for a special ``<PROJECT_NAME>`` placeholder value. + It tells each :command:`install` invocation to take a default component + name from the current :variable:`PROJECT_NAME` variable value. + +Environment Variables +--------------------- + +* The :envvar:`CMAKE_DISABLE_PRECOMPILE_HEADERS` environment variable + was added to disable precompiled headers by default. + +Properties +---------- + +* The :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` and + :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS` target properties now always + create the per-target verification target and the aggregate + ``all_verify_interface_header_sets``, ``all_verify_private_header_sets``, + and ``all_verify_header_sets`` targets when the property is enabled, + even if the target has no matching header sets. + +* The :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` and + :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS` target properties now support + ``OBJC`` and ``OBJCXX`` languages in addition to ``C`` and ``CXX``. + +* The :prop_tgt:`VS_SOLUTION_DEPLOY` target property is now implemented + for targets created by :command:`add_custom_target`. + +Modules +------- + +* The :module:`CMakePackageConfigHelpers` module's + :command:`write_basic_package_version_file` command now supports + ``SamePatchVersion``, ``SameFullVersion``, and ``SemanticVersion`` + compatibility modes. The older ``ExactVersion`` mode is now documented as + deprecated in favor of the more explicit ``SamePatchVersion`` and + ``SameFullVersion`` modes. The ``SemanticVersion`` mode treats ``0.y.z`` + versions more strictly than ``1.y.z`` and later versions. + +* The :module:`ExternalData` module gained an + :variable:`ExternalData_LINK_MODE` variable to select an ordered list + of file materialization modes for hard links, symbolic links, and + copies. + +* The :module:`ExternalData` module gained an + :variable:`ExternalData_STATE_ROOT` variable to store hash records outside + :variable:`ExternalData_BINARY_ROOT`. + +* The :module:`ExternalProject` and :module:`FetchContent` modules gained + :variable:`CMAKE_EP_GIT_CLONE_RETRY_COUNT` and + :variable:`CMAKE_EP_GIT_CLONE_RETRY_DELAY` variables to control + ``git clone`` retries. + +* The :module:`FindCUDAToolkit` module now creates a ``CUDA::sanitizer`` + target for the :ref:`compute-sanitizer <FindCUDAToolkit_sanitizer>` + library. + +* The :module:`FindCUDAToolkit` module now provides an imported target + for ``cufilt`` in CUDA 11.4 and above. + +* :module:`FindPython` and :module:`FindPython3` modules gained the support of + the Stable ABI for Free Threaded Python (``SABIT``) as specified by + :pep:`803`. + +Generator Expressions +--------------------- + +* The :genex:`TARGET_OBJECTS` generator expression now has an optional + ``SOURCE_FILES:...`` argument, which can take one or more source files as + arguments, and will filter its output to only include objects built from + those source files. + +Swift +----- + +* The :ref:`Ninja Generators` gained the + :prop_tgt:`Swift_SEPARATE_MODULE_EMISSION` target property, initialized by + the :variable:`CMAKE_Swift_SEPARATE_MODULE_EMISSION` variable, to control + whether importable Swift targets emit ``.swiftmodule`` from a dedicated + build edge. Policy :policy:`CMP0215` enables this by default. The + behavior requires policies :policy:`CMP0157` and :policy:`CMP0195` to be + set to ``NEW``. + +* The default :prop_tgt:`Swift_MODULE_NAME` now replaces hyphens with + underscores, since hyphens are not valid in Swift module identifiers. + +* Swift targets now have a package name by default. + See policy :policy:`CMP0216`. + +* The :prop_tgt:`Swift_PACKAGE_NAME` target property was added to + specify a Swift package name. + +* Swift targets on Windows now support the ``$<TARGET_PDB_FILE:...>`` + family of generator expressions. + +CTest +----- + +* :manual:`ctest(1)` gained support for forwarding extra arguments to test + executables using a :option:`-- <ctest -->` separator. + +* :manual:`ctest(1)` gained a :ref:`CoverageTool <ctest-CoverageTool>` + option in :ref:`Dashboard Client` mode to configure tests for + coverage collection by ``llvm-cov``. + +* The :command:`ctest_configure`, :command:`ctest_build`, + :command:`ctest_test`, and :command:`ctest_memcheck` commands gained + ``PRESET`` and ``PRESETS_FILE`` arguments to support using + :manual:`presets <cmake-presets(7)>` for their :ref:`Dashboard Client` steps. + + The :variable:`CTEST_CONFIGURE_PRESET`, :variable:`CTEST_BUILD_PRESET`, + :variable:`CTEST_TEST_PRESET`, :variable:`CTEST_PRESET`, and + :variable:`CTEST_PRESETS_FILE` variables may be used to specify preset + arguments to the above commands in a :ref:`Dashboard Client` script or + on the :program:`ctest` command line via the + :ctest-dashboard-option:`-D` option. + +* :manual:`ctest(1)` gained a :ctest-dashboard-option:`--source-dir` option + to specify the source directory. When combined with + :ctest-option:`-T` Configure, this allows CTest to perform an + initial configure step for an empty binary directory. + +* :manual:`ctest(1)` gained support for a :variable:`CTEST_SUBMIT_PARTS` + variable that restricts which parts are uploaded when operating in + :ref:`Dashboard Client` mode. + +* The :command:`ctest_update` command gained ``VERSION_ONLY`` and + ``VERSION_OVERRIDE`` options to select or report a source revision + without requiring the corresponding + :variable:`CTEST_UPDATE_VERSION_ONLY` or + :variable:`CTEST_UPDATE_VERSION_OVERRIDE` dashboard script variables. + +CPack +----- + +* The :cpack_gen:`CPack Archive Generator` gained the + :variable:`CPACK_ARCHIVE_ENCODING` variable to specify the pathname + encoding used in package archives. + +* The :cpack_gen:`CPack NSIS Generator` gained a new variable + :variable:`CPACK_NSIS_IGNORE_COMPONENTS_PAGE` to ignore the + component selection page in the installer. + +* The :variable:`CPACK_RPM_SPEC_MORE_DEFINE` variable now supports + a list of definitions. + +Deprecated and Removed Features +=============================== + +* The ``-W[no-][error=]dev`` command-line options are deprecated. The new + spelling is :option:`-W[no-][error=]author <cmake -W>`. + +* The ``--warn-uninitialized`` command-line option is deprecated. The new + spelling is :option:`-Wuninitialized <cmake -W>`. + +* The ``--no-warn-unused-cli`` command-line option is deprecated. The new + spelling is :option:`-Wno-unused-cli <cmake -Wno->`. + +* The :variable:`CMAKE_WARN_DEPRECATED` and :variable:`CMAKE_ERROR_DEPRECATED` + variables are deprecated. The new :command:`cmake_diagnostic` command should + be used instead. + +* The :prop_dir:`MACROS` directory property does not exist anymore. + See policy :policy:`CMP0217`. + +Other Changes +============= + +* A file may belong to at most one non-``HEADERS`` :ref:`file set <file sets>` + in a target. See policy :policy:`CMP0211`. + +* :command:`macro` invocations now preserve backslashes in arguments. + See policy :policy:`CMP0219`. + +* :command:`variable_watch` callback command arguments preserve backslashes. + See policy :policy:`CMP0219`. + +* :manual:`ctest(1)` now exits with non-zero if invoked with no + arguments and no test input file exists. + +* Test drivers generated by the :command:`create_test_sourcelist` command now + support the ``-N`` option to list all tests, one per line. This is useful in + combination with the new :command:`discover_tests` command. + +* Names of executables given to the ``DEPENDS`` argument of + :command:`add_custom_command` no longer have ``.exe`` suffixes stripped to + establish target-level dependencies. See policy :policy:`CMP0212`. + +* Targets which provide C++ modules may now be consumed by targets with + different :prop_tgt:`compile features <COMPILE_FEATURES>` and + :prop_tgt:`options <COMPILE_OPTIONS>` than the provider. + +Updates +======= + +Changes made since CMake 4.4.0 include the following. + +4.4.1 +----- + +* The :module:`FindPython`, :module:`FindPython3` modules learned + to manage, on ``Windows`` systems, the ``pydebug`` ABI flag. + +4.4.2 +----- + +* This version made no changes to documented features or interfaces. + Some implementation updates were made to support ecosystem changes + and/or fix regressions. + +4.4.3 +----- + +* Swift policy :policy:`CMP0215`'s ``NEW`` behavior has been updated + to require policies :policy:`CMP0157` and :policy:`CMP0195` to also + be set to ``NEW``.
diff --git a/Help/release/index.rst b/Help/release/index.rst index c525705..462babd 100644 --- a/Help/release/index.rst +++ b/Help/release/index.rst
@@ -13,6 +13,7 @@ .. toctree:: :maxdepth: 1 + 4.4 <4.4> 4.3 <4.3> 4.2 <4.2> 4.1 <4.1>
diff --git a/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst b/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst index 139268b..c2e7e82 100644 --- a/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst +++ b/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst
@@ -8,10 +8,13 @@ .. note:: - If Qt 5.15 or later is used and the generator is either :generator:`Ninja` - or :ref:`Makefile Generators`, additional target dependencies are added to - the :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` - target instead of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target. + If the + :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` + target is created, additional target dependencies are added to it instead + of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target. When using the + :ref:`Visual Studio Generators`, the ``moc`` and ``uic`` step may be part + of the ``<ORIGIN>`` target itself, in which case the dependencies are added + to ``<ORIGIN>``. See the :manual:`cmake-qt(7)` manual for details. This variable is used to initialize the :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS` property on all the targets. See that target property for additional
diff --git a/Help/variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES.rst b/Help/variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES.rst index 2146f00..da55c60 100644 --- a/Help/variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES.rst +++ b/Help/variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES.rst
@@ -4,7 +4,7 @@ .. versionadded:: 4.1 Specifies zero or more include directories for AUTOMOC to pass explicitly to -the Qt Meta‑Object Compiler (``moc``) instead of automatically discovering +the Qt Meta-Object Compiler (``moc``) instead of automatically discovering each target's include directories. The directories listed here will replace any include paths discovered from
diff --git a/Help/variable/CMAKE_ERROR_DEPRECATED.rst b/Help/variable/CMAKE_ERROR_DEPRECATED.rst index f3a6738..0bf7d55 100644 --- a/Help/variable/CMAKE_ERROR_DEPRECATED.rst +++ b/Help/variable/CMAKE_ERROR_DEPRECATED.rst
@@ -1,7 +1,32 @@ CMAKE_ERROR_DEPRECATED ---------------------- +.. deprecated:: 4.4 + Whether to issue errors for deprecated functionality. -If ``TRUE``, use of deprecated functionality will issue fatal errors. -If this variable is not set, CMake behaves as if it were set to ``FALSE``. +This is a deprecated mechanism for interacting with the +:diagnostic:`CMD_DEPRECATED` :manual:`diagnostic <cmake-diagnostics(7)>`. +If policy :policy:`CMP0218` is set to ``NEW``, this variable is ignored. + +If policy :policy:`CMP0218` is not set to ``NEW``, the :command:`message` +command's ``DEPRECATION`` message type will use +:variable:`CMAKE_WARN_DEPRECATED` and ``CMAKE_ERROR_DEPRECATED`` +to determine the severity of a deprecation diagnostic. The severity will be: + +* ``IGNORE``, if ``CMAKE_WARN_DEPRECATED`` is ``OFF`` and + ``CMAKE_ERROR_DEPRECATED`` is unset or ``OFF``. + +* ``WARN``, if ``CMAKE_WARN_DEPRECATED`` is unset or ``ON`` and + ``CMAKE_ERROR_DEPRECATED`` is unset or ``OFF``. + +* ``FATAL_ERROR`` if ``CMAKE_ERROR_DEPRECATED`` is ``ON``. + +Setting ``CMAKE_ERROR_DEPRECATED`` in the cache will alter the default state of +the :diagnostic:`CMD_DEPRECATED` :manual:`diagnostic <cmake-diagnostics(7)>`; +however, the :option:`-W[no-][error=]deprecated <cmake -W>` option, and +:preset:`warnings.deprecated <configurePresets.warnings.deprecated>` and/or +:preset:`errors.deprecated <configurePresets.errors.deprecated>` preset +fields, will take precedence. CMake will also update the cached value of +``CMAKE_ERROR_DEPRECATED`` to reflect the actual state of the diagnostic as of +the start of script execution.
diff --git a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst index 38e9b7b..5ee7bf3 100644 --- a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst +++ b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst
@@ -8,3 +8,10 @@ file takes place. This variable is used by CMake-generated ``cmake_install.cmake`` scripts. If one sets this variable to ``ON`` while running the script, it may get fatal error messages from the script. + +.. versionadded:: 4.4 + + The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be used to + generate errors for absolute install destinations at generate time. + +See also :variable:`CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION`.
diff --git a/Help/variable/CMAKE_FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT.rst b/Help/variable/CMAKE_FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT.rst index 07da28d..3328e6b 100644 --- a/Help/variable/CMAKE_FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT.rst +++ b/Help/variable/CMAKE_FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT.rst
@@ -11,7 +11,7 @@ .. note:: When this variable is ``OFF`` (the default), ``BYPRODUCTS`` are treated - similarly to how Ninja handles them — as opaque side effects — and the + similarly to how Ninja handles them - as opaque side effects - and the generator emits ``ExecAlways`` nodes to ensure they always run. Defaults to ``OFF``.
diff --git a/Help/variable/CMAKE_FASTBUILD_USE_LIGHTCACHE.rst b/Help/variable/CMAKE_FASTBUILD_USE_LIGHTCACHE.rst index e3498d9..187a411 100644 --- a/Help/variable/CMAKE_FASTBUILD_USE_LIGHTCACHE.rst +++ b/Help/variable/CMAKE_FASTBUILD_USE_LIGHTCACHE.rst
@@ -3,7 +3,7 @@ .. versionadded:: 4.2 -Enables FASTBuild’s *light caching* mode, which accelerates cache lookups by +Enables FASTBuild's *light caching* mode, which accelerates cache lookups by parsing source files directly (instead of invoking the compiler preprocessor). See the `FASTBuild Compiler() documentation <https://www.fastbuild.org/docs/functions/compiler.html>`_
diff --git a/Help/variable/CMAKE_INSTALL_DEFAULT_COMPONENT_NAME.rst b/Help/variable/CMAKE_INSTALL_DEFAULT_COMPONENT_NAME.rst index 57160f1..15ec0cd 100644 --- a/Help/variable/CMAKE_INSTALL_DEFAULT_COMPONENT_NAME.rst +++ b/Help/variable/CMAKE_INSTALL_DEFAULT_COMPONENT_NAME.rst
@@ -7,3 +7,13 @@ these files will be grouped into a default component. The name of this default install component will be taken from this variable. It defaults to ``Unspecified``. + +There is a special component name: + +``<PROJECT_NAME>`` + .. versionadded:: 4.4 + + This literal placeholder will expand to the contents of the + :variable:`PROJECT_NAME` variable when the :command:`install` command is + evaluated. If no :command:`project` command has been called it defaults to + ``Unspecified``.
diff --git a/Help/variable/CMAKE_INSTALL_PREFIX.rst b/Help/variable/CMAKE_INSTALL_PREFIX.rst index 340bc35..f9fa96d 100644 --- a/Help/variable/CMAKE_INSTALL_PREFIX.rst +++ b/Help/variable/CMAKE_INSTALL_PREFIX.rst
@@ -60,6 +60,5 @@ * The :option:`--install-prefix <cmake --install-prefix>` option. * The :genex:`$<INSTALL_PREFIX>` generator expression. -* The :ref:`installDir <CMakePresets installDir>` field in CMake configure - presets. +* The :preset:`configurePresets.installDir` field in CMake configure presets. * The :variable:`CPACK_PACKAGING_INSTALL_PREFIX` variable.
diff --git a/Help/variable/CMAKE_LANG_COMPILER.rst b/Help/variable/CMAKE_LANG_COMPILER.rst index db8f4e1..a6ffd84 100644 --- a/Help/variable/CMAKE_LANG_COMPILER.rst +++ b/Help/variable/CMAKE_LANG_COMPILER.rst
@@ -3,13 +3,21 @@ The full path to the compiler for ``LANG``. -This is the command that will be used as the ``<LANG>`` compiler. Once -set, you can not change this variable. +This is the command that will be used as the ``<LANG>`` compiler. +Once set, you can not change this variable. + +.. note:: + + With :ref:`IDE Build Tool Generators`, including + :ref:`Visual Studio Generators` and :generator:`Xcode`, this variable is + computed automatically and should not be set. Instead, specify a toolset + using :option:`cmake -T`. Usage ^^^^^ -This variable can be set by the user during the first time a build tree is configured. +With :ref:`Command-Line Build Tool Generators` this variable can be +set by the user during the first time a build tree is configured. If a non-full path value is supplied then CMake will resolve the full path of the compiler.
diff --git a/Help/variable/CMAKE_LANG_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_ID.rst index 365e1d0..be57618 100644 --- a/Help/variable/CMAKE_LANG_COMPILER_ID.rst +++ b/Help/variable/CMAKE_LANG_COMPILER_ID.rst
@@ -39,6 +39,7 @@ ``NVIDIA`` `NVIDIA CUDA Compiler`_ ``OrangeC`` `OrangeC Compiler`_ ``OpenWatcom`` `Open Watcom`_ +``PellesC`` `Pelles C`_ ``PGI`` The Portland Group ``PathScale`` PathScale ``QCC`` QNX C/C++ compiler @@ -53,6 +54,7 @@ ``XL``, ``VisualAge``, ``zOS`` IBM XL ``XLClang`` IBM Clang-based XL ``IBMClang`` IBM LLVM-based Compiler +``IBMFlang`` IBM LLVM/Flang-based Fortran Compiler =============================== =============================================== This variable is not guaranteed to be defined for all compilers or @@ -71,6 +73,7 @@ .. _NVIDIA CUDA Compiler: https://developer.nvidia.com/cuda-llvm-compiler .. _Open Watcom: https://open-watcom.github.io .. _OrangeC Compiler: https://github.com/LADSoft/OrangeC +.. _Pelles C: https://pellesc.se .. _Renesas Compiler: https://www.renesas.com .. _Small Device C Compiler: https://sdcc.sourceforge.net .. _Tiny C Compiler: https://bellard.org/tcc
diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst index a57fd33..21b31c4 100644 --- a/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst +++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst
@@ -11,6 +11,7 @@ =============================== =============================================== Value Name =============================== =============================================== +``AIX`` AIX system linker ``AppleClang`` Apple Clang ``LLD`` `LLVM LLD`_ ``GNU`` `GNU Binutils - ld linker`_ (also known as @@ -19,8 +20,9 @@ ``MSVC`` `Microsoft Visual Studio`_ ``MOLD`` `mold: A Modern Linker`_, or on Apple the `sold`_ linker -``AIX`` AIX system linker ``Solaris`` SunOS system linker +``WILD`` `wild`_ linker +``OpenWatcom`` `OpenWatcom wlink`_ linker =============================== =============================================== This variable is not guaranteed to be defined for all linkers or languages. @@ -31,3 +33,5 @@ .. _Microsoft Visual Studio: https://visualstudio.microsoft.com .. _mold\: A Modern Linker: https://github.com/rui314/mold .. _sold: https://github.com/bluewhalesystems/sold +.. _wild: https://github.com/wild-linker/wild +.. _OpenWatcom wlink: https://www.openwatcom.com/
diff --git a/Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst b/Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst index 4539c90..8a3519c 100644 --- a/Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst +++ b/Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst
@@ -5,10 +5,9 @@ When set, this variable specifies the logging level used by the :command:`message` command. Valid values are the same as those for the -:option:`--log-level <cmake --log-level>` command line option of the -:manual:`cmake(1)` program. If this variable is set and the -:option:`--log-level <cmake --log-level>` command line option is -given, the command line option takes precedence. +:cmake-option:`--log-level` command line option of the :manual:`cmake(1)` +program. If this variable is set and the :cmake-option:`--log-level` command +line option is given, the command line option takes precedence. The main advantage to using this variable is to make a log level persist between CMake runs. Setting it as a cache variable will ensure that @@ -18,6 +17,5 @@ they may control the log level according to their own needs. .. versionadded:: 3.25 - See the :command:`cmake_language` - :ref:`cmake_language <query_message_log_level>` command for a way to query - the current message logging level. + See the :command:`cmake_language(GET_MESSAGE_LOG_LEVEL)` command for a way to + query the current message logging level.
diff --git a/Help/variable/CMAKE_Rust_EDITION.rst b/Help/variable/CMAKE_Rust_EDITION.rst new file mode 100644 index 0000000..273aea6 --- /dev/null +++ b/Help/variable/CMAKE_Rust_EDITION.rst
@@ -0,0 +1,10 @@ +CMAKE_Rust_EDITION +------------------ + +.. versionadded:: 4.4 + +.. note:: + Experimental. Gated by ``CMAKE_EXPERIMENTAL_RUST``. + +Default value for :prop_tgt:`Rust_EDITION` target property if set when a target +is created.
diff --git a/Help/variable/CMAKE_SYSTEM_NAME.rst b/Help/variable/CMAKE_SYSTEM_NAME.rst index de688b5..90b26ba 100644 --- a/Help/variable/CMAKE_SYSTEM_NAME.rst +++ b/Help/variable/CMAKE_SYSTEM_NAME.rst
@@ -28,91 +28,270 @@ The following is a list of possible values, each associated with corresponding operating systems or environments. -========================= ====================================================== -Value Name -========================= ====================================================== -``ADSP`` Analog Devices Audio Digital Signal Processing -``AIX`` IBM Unix operating system -``Android`` Android operating system -``ARTOS`` Operating system for microcontrollers -``BeOS`` Operating system for personal computers (discontinued) -``BlueGeneL`` Blue Gene/L static environment -``BlueGeneP-dynamic`` Blue Gene/P dynamic environment -``BlueGeneP-static`` Blue Gene/P static environment -``BlueGeneQ-dynamic`` Blue Gene/Q dynamic environment -``BlueGeneQ-static`` Blue Gene/Q static environment -``BSDOS`` BSD operating system (discontinued) -``Catamount`` Operating system for Cray XT series -``CrayLinuxEnvironment`` Cray Linux Environment -``CYGWIN`` Cygwin environment for Windows -``Darwin`` Apple stationary operating systems (macOS, OS X, etc.) -``DOS`` MS-DOS or compatible -``DragonFly`` BSD-derived operating system -``eCos`` Real-time embedded operating system -``Emscripten`` Compiler toolchain to WebAssembly -``Euros`` Real-time operating system for embedded devices -``FreeBSD`` FreeBSD operating system -``Fuchsia`` Operating system by Google based on the Zircon kernel -``Generic-ADSP`` Generic ADSP (Audio DSP) environment -``Generic-ELF`` Generic ELF (Executable and Linkable Format) environment -``Generic`` Some platforms, e.g. bare metal embedded devices -``GHS-MULTI`` Green Hills Software MULTI environment -``GNU`` GNU/Hurd-based operating system -``Haiku`` Unix operating system inspired by BeOS -``HP-UX`` Hewlett Packard Unix -``iOS`` Apple mobile phone operating system -``Linux`` All Linux-based distributions -``Midipix`` POSIX-compatible layer for Windows -``MirBSD`` MirOS BSD operating system -``MP-RAS`` MP-RAS UNIX operating system -``MSYS`` MSYS environment (MSYSTEM=MSYS) -``NetBSD`` NetBSD operating systems -``OpenBSD`` OpenBSD operating systems -``OpenVMS`` OpenVMS operating system by HP -``OS2`` OS/2 operating system -``OSF1`` Compaq Tru64 UNIX (formerly DEC OSF/1, Digital Unix) (discontinued) -``QNX`` Unix-like operating system by BlackBerry -``RISCos`` RISC OS operating system -``SCO_SV`` SCO OpenServer 5 -``SerenityOS`` Unix-like operating system -``SINIX`` SINIX operating system -``SunOS`` Oracle Solaris and all illumos operating systems -``syllable`` Syllable operating system -``Tru64`` Compaq Tru64 UNIX (formerly DEC OSF/1) operating system -``tvOS`` Apple TV operating system -``ULTRIX`` Unix operating system (discontinued) -``UNIX_SV`` SCO UnixWare (pre release 7) -``UnixWare`` SCO UnixWare 7 -``visionOS`` Apple mixed reality operating system -``WASI`` WebAssembly System Interface -``watchOS`` Apple watch operating system -``Windows`` Windows stationary operating systems -``WindowsCE`` Windows Embedded Compact -``WindowsPhone`` Windows mobile phone operating system -``WindowsStore`` Universal Windows Platform applications -``Xenix`` SCO Xenix Unix operating system (discontinued) -========================= ====================================================== +Apple Platforms +""""""""""""""" -Platform-specific notes: +``Darwin`` + Apple stationary operating systems (macOS, OS X, etc.) -* MSYS2's ``msys/cmake`` package (``/usr/bin/cmake``) works only under +``iOS`` + .. versionadded:: 3.14 + + Apple mobile phone operating system + +``tvOS`` + .. versionadded:: 3.14 + + Apple TV operating system + +``visionOS`` + .. versionadded:: 3.28 + + Apple mixed reality operating system + +``watchOS`` + .. versionadded:: 3.14 + + Apple watch operating system + +UNIX Platforms +"""""""""""""" + +``ADSP`` + Analog Devices Audio Digital Signal Processing + +``AIX`` + IBM Unix operating system + +``Android`` + .. versionadded:: 3.1 + + Android operating system + +``ARTOS`` + .. versionadded:: 3.4 + + Operating system for microcontrollers + +``BeOS`` + Operating system for personal computers (discontinued) + +``BlueGeneL`` + Blue Gene/L static environment + +``BlueGeneP-dynamic`` + Blue Gene/P dynamic environment + +``BlueGeneP-static`` + Blue Gene/P static environment + +``BlueGeneQ-dynamic`` + .. versionadded:: 3.3 + + Blue Gene/Q dynamic environment + +``BlueGeneQ-static`` + .. versionadded:: 3.3 + + Blue Gene/Q static environment + +``BSDOS`` + BSD operating system (discontinued) + +``Catamount`` + Operating system for Cray XT series + +``CrayLinuxEnvironment`` + .. versionadded:: 3.5 + + Cray Linux Environment + +``DragonFly`` + BSD-derived operating system + +``eCos`` + Real-time embedded operating system + +``Emscripten`` + .. versionadded:: 4.2 + + Compiler toolchain to WebAssembly. + +``Euros`` + .. versionadded:: 3.4 + + Real-time operating system for embedded devices + +``FreeBSD`` + FreeBSD operating system + +``Fuchsia`` + .. versionadded:: 3.8 + + Operating system by Google based on the Zircon kernel + +``Generic-ADSP`` + Generic ADSP (Audio DSP) environment + +``Generic-ELF`` + .. versionadded:: 3.23 + + Generic ELF (Executable and Linkable Format) environment + +``Generic`` + Some platforms, e.g. bare metal embedded devices + +``GHS-MULTI`` + .. versionadded:: 3.3 + + Green Hills Software MULTI environment + +``GNU`` + GNU/Hurd-based operating system + +``Haiku`` + Unix operating system inspired by BeOS + +``HP-UX`` + Hewlett Packard Unix (discontinued) + +``Linux`` + All Linux-based distributions + +``MirBSD`` + MirOS BSD operating system + +``MP-RAS`` + MP-RAS UNIX operating system + +``NetBSD`` + NetBSD operating systems + +``OHOS`` + .. versionadded:: 3.30 + + OpenHarmony family of operating systems (e.g., HarmonyOS) + + Toolchain file can set this value when targeting OpenHarmony operating + systems. + +``OpenBSD`` + OpenBSD operating systems + +``OpenVMS`` + OpenVMS operating system by HP + +``OS2`` + .. versionadded:: 3.18 + + OS/2 operating system + +``OSF1`` + Compaq Tru64 UNIX (formerly DEC OSF/1, Digital Unix) (discontinued) + +``QNX`` + Unix-like operating system by BlackBerry + +``RISCos`` + RISC OS operating system + +``SCO_SV`` + SCO OpenServer 5 + +``SerenityOS`` + .. versionadded:: 3.25 + + Unix-like operating system + +``SINIX`` + SINIX operating system + +``SunOS`` + Oracle Solaris and all illumos operating systems + +``syllable`` + Syllable operating system + +``Tru64`` + Compaq Tru64 UNIX (formerly DEC OSF/1) operating system + +``ULTRIX`` + Unix operating system (discontinued) + +``UNIX_SV`` + SCO UnixWare (pre release 7) + +``UnixWare`` + SCO UnixWare 7 + +``WASI`` + .. versionadded:: 3.31 + + WebAssembly System Interface + +``Xenix`` + SCO Xenix Unix operating system (discontinued) + +Windows Platforms +""""""""""""""""" + +``CYGWIN`` + Cygwin environment for Windows + + Cygwin's ``cmake`` package (``/usr/bin/cmake``) uses system name ``CYGWIN``. + A non-cygwin CMake on Windows (e.g. ``$PROGRAMFILES/CMake/bin/cmake``) + uses system name ``Windows`` even when it runs under a Cygwin environment. + +``DOS`` + MS-DOS or compatible + +``Midipix`` + .. versionadded:: 3.10 + + POSIX-compatible layer for Windows + +``MSYS`` + MSYS environment (MSYSTEM=MSYS) + + MSYS2's ``msys/cmake`` package (``/usr/bin/cmake``) works only under ``MSYSTEM=MSYS`` environments, with system name ``MSYS``. Under other environments like ``MSYSTEM=MINGW64``, use another package such as ``mingw64/mingw-w64-x86_64-cmake`` (``/mingw64/bin/cmake``), which targets ``MSYSTEM=MINGW64`` with system name ``Windows``. -* Cygwin's ``cmake`` package (``/usr/bin/cmake``) uses system name ``CYGWIN``. - A non-cygwin CMake on Windows (e.g. ``$PROGRAMFILES/CMake/bin/cmake``) - uses system name ``Windows`` even when it runs under a Cygwin environment. +``Windows`` + Windows stationary operating systems + +``WindowsCE`` + Windows Embedded Compact + +``WindowsKernelModeDriver`` + .. versionadded:: 4.1 + + Windows Kernel-Mode Driver + + When building drivers for Kernel-Mode Driver Framework on Windows, toolchain + file can set this value. See also the :variable:`CMAKE_WINDOWS_KMDF_VERSION` + variable. + +``WindowsPhone`` + .. versionadded:: 3.1 + + Windows mobile phone operating system + +``WindowsStore`` + .. versionadded:: 3.1 + + Universal Windows Platform applications Removed Platforms -^^^^^^^^^^^^^^^^^ +""""""""""""""""" The following platforms were once supported by CMake and got removed either due to platform's EOL, or other incompatibilities: -========================= ===================================== ================ -Value Name Removed in CMake -========================= ===================================== ================ -``kFreeBSD`` FreeBSD kernel with a GNU userland 4.1 -========================= ===================================== ================ +``kFreeBSD`` + .. versionchanged:: 4.1 + Removed from CMake. + + FreeBSD kernel with a GNU userland
diff --git a/Help/variable/CMAKE_Swift_SEPARATE_MODULE_EMISSION.rst b/Help/variable/CMAKE_Swift_SEPARATE_MODULE_EMISSION.rst new file mode 100644 index 0000000..d5ebcb0 --- /dev/null +++ b/Help/variable/CMAKE_Swift_SEPARATE_MODULE_EMISSION.rst
@@ -0,0 +1,24 @@ +CMAKE_Swift_SEPARATE_MODULE_EMISSION +------------------------------------- + +.. versionadded:: 4.4 + +Specify whether Swift targets emit ``.swiftmodule`` from a dedicated build +edge. This variable is used to initialize the +:prop_tgt:`Swift_SEPARATE_MODULE_EMISSION` property on targets as they are +created. + +When set to ``ON``, importable Swift targets built by :ref:`Ninja Generators` +produce the ``.swiftmodule`` from a separate ``-emit-module`` build edge, +allowing downstream Swift targets to compile in parallel with upstream +object compilation. + +If this variable is not set, separate module emission is enabled by default +in targets that do not set :prop_tgt:`Swift_SEPARATE_MODULE_EMISSION`. +See policy :policy:`CMP0215`. + +.. note:: + + Separate module emission takes effect only when policy :policy:`CMP0157` + is set to ``NEW`` prior to the first :command:`project` or + :command:`enable_language` command that enables the Swift language.
diff --git a/Help/variable/CMAKE_TEST_BUILD_DEPENDS.rst b/Help/variable/CMAKE_TEST_BUILD_DEPENDS.rst new file mode 100644 index 0000000..cdc5b18 --- /dev/null +++ b/Help/variable/CMAKE_TEST_BUILD_DEPENDS.rst
@@ -0,0 +1,17 @@ +CMAKE_TEST_BUILD_DEPENDS +------------------------ + +.. versionadded:: 4.4 + +Enable ``test_prep/<name>`` build targets for tests added by +:command:`add_test` when using the :ref:`Ninja Generators`. + +When this variable is set to a true value, CMake generates a +``test_prep/<name>`` target for each eligible test and a ``test_prep/all`` +target for all such tests. Building these targets ensures the test executable, +targets referenced by test command generator expressions, and explicit +``BUILD_DEPENDS`` entries are up-to-date before the test runs. + +Tests whose names are not valid target names are excluded from this behavior. +If multiple tests in different directories share the same name, their +dependencies are merged into a single ``test_prep/<name>`` target.
diff --git a/Help/variable/CMAKE_WARN_DEPRECATED.rst b/Help/variable/CMAKE_WARN_DEPRECATED.rst index c7b6159..482b982 100644 --- a/Help/variable/CMAKE_WARN_DEPRECATED.rst +++ b/Help/variable/CMAKE_WARN_DEPRECATED.rst
@@ -1,11 +1,32 @@ CMAKE_WARN_DEPRECATED --------------------- +.. deprecated:: 4.4 + Whether to issue warnings for deprecated functionality. -If not ``FALSE``, use of deprecated functionality will issue warnings. -If this variable is not set, CMake behaves as if it were set to ``TRUE``. +This is a deprecated mechanism for interacting with the +:diagnostic:`CMD_DEPRECATED` :manual:`diagnostic <cmake-diagnostics(7)>`. +If policy :policy:`CMP0218` is set to ``NEW``, this variable is ignored. -When running :manual:`cmake(1)`, this option can be enabled with the -:option:`-Wdeprecated <cmake -Wdeprecated>` option, or disabled with the -:option:`-Wno-deprecated <cmake -Wno-deprecated>` option. +If policy :policy:`CMP0218` is not set to ``NEW``, the :command:`message` +command's ``DEPRECATION`` message type will use +``CMAKE_WARN_DEPRECATED`` and :variable:`CMAKE_ERROR_DEPRECATED` +to determine the severity of a deprecation diagnostic. The severity will be: + +* ``IGNORE``, if ``CMAKE_WARN_DEPRECATED`` is ``OFF`` and + ``CMAKE_ERROR_DEPRECATED`` is unset or ``OFF``. + +* ``WARN``, if ``CMAKE_WARN_DEPRECATED`` is unset or ``ON`` and + ``CMAKE_ERROR_DEPRECATED`` is unset or ``OFF``. + +* ``FATAL_ERROR`` if ``CMAKE_ERROR_DEPRECATED`` is ``ON``. + +Setting ``CMAKE_WARN_DEPRECATED`` in the cache will alter the default state of +the :diagnostic:`CMD_DEPRECATED` :manual:`diagnostic <cmake-diagnostics(7)>`; +however, the :option:`-W[no-][error=]deprecated <cmake -W>` option, and +:preset:`warnings.deprecated <configurePresets.warnings.deprecated>` and/or +:preset:`errors.deprecated <configurePresets.errors.deprecated>` preset +fields, will take precedence. CMake will also update the cached value of +``CMAKE_WARN_DEPRECATED`` to reflect the actual state of the diagnostic as of +the start of script execution.
diff --git a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst index 81c1158..ac40b3d 100644 --- a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst +++ b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst
@@ -7,3 +7,10 @@ This variable is used by CMake-generated ``cmake_install.cmake`` scripts. If one sets this variable to ``ON`` while running the script, it may get warning messages from the script. + +.. versionadded:: 4.4 + + The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be used to + generate warnings for absolute install destinations at generate time. + +See also :variable:`CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION`.
diff --git a/Help/variable/CTEST_BINARY_DIRECTORY.rst b/Help/variable/CTEST_BINARY_DIRECTORY.rst index ec30f2d..572e58e 100644 --- a/Help/variable/CTEST_BINARY_DIRECTORY.rst +++ b/Help/variable/CTEST_BINARY_DIRECTORY.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``BuildDirectory`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_BUILD_COMMAND.rst b/Help/variable/CTEST_BUILD_COMMAND.rst index e4110c7..9e4ba42 100644 --- a/Help/variable/CTEST_BUILD_COMMAND.rst +++ b/Help/variable/CTEST_BUILD_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``MakeCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_BUILD_NAME.rst b/Help/variable/CTEST_BUILD_NAME.rst index d6b0636..76d7ef7 100644 --- a/Help/variable/CTEST_BUILD_NAME.rst +++ b/Help/variable/CTEST_BUILD_NAME.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``BuildName`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_BUILD_PRESET.rst b/Help/variable/CTEST_BUILD_PRESET.rst new file mode 100644 index 0000000..b531dab --- /dev/null +++ b/Help/variable/CTEST_BUILD_PRESET.rst
@@ -0,0 +1,10 @@ +CTEST_BUILD_PRESET +------------------ + +.. versionadded:: 4.4 + +Specify the ``PRESET`` option for the :command:`ctest_build` command +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. + +See also :variable:`CTEST_PRESET` and :variable:`CTEST_PRESETS_FILE`.
diff --git a/Help/variable/CTEST_BZR_COMMAND.rst b/Help/variable/CTEST_BZR_COMMAND.rst index 8927886..be2e804 100644 --- a/Help/variable/CTEST_BZR_COMMAND.rst +++ b/Help/variable/CTEST_BZR_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``BZRCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_BZR_UPDATE_OPTIONS.rst b/Help/variable/CTEST_BZR_UPDATE_OPTIONS.rst index 475dab2..667ebd7 100644 --- a/Help/variable/CTEST_BZR_UPDATE_OPTIONS.rst +++ b/Help/variable/CTEST_BZR_UPDATE_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``BZRUpdateOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_CHANGE_ID.rst b/Help/variable/CTEST_CHANGE_ID.rst index 5202020..66141a8 100644 --- a/Help/variable/CTEST_CHANGE_ID.rst +++ b/Help/variable/CTEST_CHANGE_ID.rst
@@ -4,7 +4,8 @@ .. versionadded:: 3.4 Specify the CTest ``ChangeId`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. This setting allows CTest to pass arbitrary information about this build up to CDash. One use of this feature is to allow CDash to
diff --git a/Help/variable/CTEST_CHECKOUT_COMMAND.rst b/Help/variable/CTEST_CHECKOUT_COMMAND.rst index 2203d6b..544751a 100644 --- a/Help/variable/CTEST_CHECKOUT_COMMAND.rst +++ b/Help/variable/CTEST_CHECKOUT_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Tell the :command:`ctest_start` command how to checkout or initialize -the source directory in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +the source directory in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_CONFIGURE_COMMAND.rst b/Help/variable/CTEST_CONFIGURE_COMMAND.rst index 90c50f4..a4c7841 100644 --- a/Help/variable/CTEST_CONFIGURE_COMMAND.rst +++ b/Help/variable/CTEST_CONFIGURE_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``ConfigureCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_CONFIGURE_PRESET.rst b/Help/variable/CTEST_CONFIGURE_PRESET.rst new file mode 100644 index 0000000..2d601b8 --- /dev/null +++ b/Help/variable/CTEST_CONFIGURE_PRESET.rst
@@ -0,0 +1,10 @@ +CTEST_CONFIGURE_PRESET +---------------------- + +.. versionadded:: 4.4 + +Specify the ``PRESET`` option for the :command:`ctest_configure` command +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. + +See also :variable:`CTEST_PRESET` and :variable:`CTEST_PRESETS_FILE`.
diff --git a/Help/variable/CTEST_COVERAGE_COMMAND.rst b/Help/variable/CTEST_COVERAGE_COMMAND.rst index 8168b33..a50a777 100644 --- a/Help/variable/CTEST_COVERAGE_COMMAND.rst +++ b/Help/variable/CTEST_COVERAGE_COMMAND.rst
@@ -4,7 +4,8 @@ .. versionadded:: 3.1 Specify the CTest ``CoverageCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. Cobertura '''''''''
diff --git a/Help/variable/CTEST_COVERAGE_EXTRA_FLAGS.rst b/Help/variable/CTEST_COVERAGE_EXTRA_FLAGS.rst index bae5c79..8c15c6e 100644 --- a/Help/variable/CTEST_COVERAGE_EXTRA_FLAGS.rst +++ b/Help/variable/CTEST_COVERAGE_EXTRA_FLAGS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``CoverageExtraFlags`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_CURL_OPTIONS.rst b/Help/variable/CTEST_CURL_OPTIONS.rst index 07557e9..1c0c702 100644 --- a/Help/variable/CTEST_CURL_OPTIONS.rst +++ b/Help/variable/CTEST_CURL_OPTIONS.rst
@@ -8,4 +8,5 @@ .. versionadded:: 3.1 Specify the CTest ``CurlOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_CVS_COMMAND.rst b/Help/variable/CTEST_CVS_COMMAND.rst index 797a4e7..e43c396 100644 --- a/Help/variable/CTEST_CVS_COMMAND.rst +++ b/Help/variable/CTEST_CVS_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``CVSCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_CVS_UPDATE_OPTIONS.rst b/Help/variable/CTEST_CVS_UPDATE_OPTIONS.rst index 6d9288a..7081d72 100644 --- a/Help/variable/CTEST_CVS_UPDATE_OPTIONS.rst +++ b/Help/variable/CTEST_CVS_UPDATE_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``CVSUpdateOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_DROP_LOCATION.rst b/Help/variable/CTEST_DROP_LOCATION.rst index 3955193..1c9062d8 100644 --- a/Help/variable/CTEST_DROP_LOCATION.rst +++ b/Help/variable/CTEST_DROP_LOCATION.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``DropLocation`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_DROP_METHOD.rst b/Help/variable/CTEST_DROP_METHOD.rst index 6a0d0a7..d76c977 100644 --- a/Help/variable/CTEST_DROP_METHOD.rst +++ b/Help/variable/CTEST_DROP_METHOD.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``DropMethod`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_DROP_SITE.rst b/Help/variable/CTEST_DROP_SITE.rst index be64d72..c9581ca 100644 --- a/Help/variable/CTEST_DROP_SITE.rst +++ b/Help/variable/CTEST_DROP_SITE.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``DropSite`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_DROP_SITE_PASSWORD.rst b/Help/variable/CTEST_DROP_SITE_PASSWORD.rst index b2f684e..f4e3b91 100644 --- a/Help/variable/CTEST_DROP_SITE_PASSWORD.rst +++ b/Help/variable/CTEST_DROP_SITE_PASSWORD.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``DropSitePassword`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_DROP_SITE_USER.rst b/Help/variable/CTEST_DROP_SITE_USER.rst index 07f115e..3a2eb36 100644 --- a/Help/variable/CTEST_DROP_SITE_USER.rst +++ b/Help/variable/CTEST_DROP_SITE_USER.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``DropSiteUser`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_EXTRA_SUBMIT_FILES.rst b/Help/variable/CTEST_EXTRA_SUBMIT_FILES.rst index a721ecf..2e10184 100644 --- a/Help/variable/CTEST_EXTRA_SUBMIT_FILES.rst +++ b/Help/variable/CTEST_EXTRA_SUBMIT_FILES.rst
@@ -2,4 +2,5 @@ ------------------------ Specify files for :command:`ctest_submit(PARTS ExtraFiles)` to submit -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_GIT_COMMAND.rst b/Help/variable/CTEST_GIT_COMMAND.rst index 01ffca7..445962f 100644 --- a/Help/variable/CTEST_GIT_COMMAND.rst +++ b/Help/variable/CTEST_GIT_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``GITCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_GIT_INIT_SUBMODULES.rst b/Help/variable/CTEST_GIT_INIT_SUBMODULES.rst index 6e4b9c1..127b377 100644 --- a/Help/variable/CTEST_GIT_INIT_SUBMODULES.rst +++ b/Help/variable/CTEST_GIT_INIT_SUBMODULES.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.6 Specify the CTest ``GITInitSubmodules`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_GIT_UPDATE_CUSTOM.rst b/Help/variable/CTEST_GIT_UPDATE_CUSTOM.rst index 1ae1099..d239022 100644 --- a/Help/variable/CTEST_GIT_UPDATE_CUSTOM.rst +++ b/Help/variable/CTEST_GIT_UPDATE_CUSTOM.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``GITUpdateCustom`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_GIT_UPDATE_OPTIONS.rst b/Help/variable/CTEST_GIT_UPDATE_OPTIONS.rst index a063ba3..324252b 100644 --- a/Help/variable/CTEST_GIT_UPDATE_OPTIONS.rst +++ b/Help/variable/CTEST_GIT_UPDATE_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``GITUpdateOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_HG_COMMAND.rst b/Help/variable/CTEST_HG_COMMAND.rst index 426cfd5..e553868 100644 --- a/Help/variable/CTEST_HG_COMMAND.rst +++ b/Help/variable/CTEST_HG_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``HGCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_HG_UPDATE_OPTIONS.rst b/Help/variable/CTEST_HG_UPDATE_OPTIONS.rst index 751b300..91c4097 100644 --- a/Help/variable/CTEST_HG_UPDATE_OPTIONS.rst +++ b/Help/variable/CTEST_HG_UPDATE_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``HGUpdateOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_LABELS_FOR_SUBPROJECTS.rst b/Help/variable/CTEST_LABELS_FOR_SUBPROJECTS.rst index 4c1ec4b..47aa60e 100644 --- a/Help/variable/CTEST_LABELS_FOR_SUBPROJECTS.rst +++ b/Help/variable/CTEST_LABELS_FOR_SUBPROJECTS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.10 Specify the CTest ``LabelsForSubprojects`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_MEMORYCHECK_COMMAND.rst b/Help/variable/CTEST_MEMORYCHECK_COMMAND.rst index 1c86357..2366ad5 100644 --- a/Help/variable/CTEST_MEMORYCHECK_COMMAND.rst +++ b/Help/variable/CTEST_MEMORYCHECK_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``MemoryCheckCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_MEMORYCHECK_COMMAND_OPTIONS.rst b/Help/variable/CTEST_MEMORYCHECK_COMMAND_OPTIONS.rst index f2c91e3..d6de142 100644 --- a/Help/variable/CTEST_MEMORYCHECK_COMMAND_OPTIONS.rst +++ b/Help/variable/CTEST_MEMORYCHECK_COMMAND_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``MemoryCheckCommandOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst b/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst index d9f3eea..ec30121 100644 --- a/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst +++ b/Help/variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS.rst
@@ -4,7 +4,8 @@ .. versionadded:: 3.1 Specify the CTest ``MemoryCheckSanitizerOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. CTest prepends correct sanitizer options ``*_OPTIONS`` environment variable to executed command. CTests adds
diff --git a/Help/variable/CTEST_MEMORYCHECK_SUPPRESSIONS_FILE.rst b/Help/variable/CTEST_MEMORYCHECK_SUPPRESSIONS_FILE.rst index fe54eb9..d8e124a 100644 --- a/Help/variable/CTEST_MEMORYCHECK_SUPPRESSIONS_FILE.rst +++ b/Help/variable/CTEST_MEMORYCHECK_SUPPRESSIONS_FILE.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``MemoryCheckSuppressionFile`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_MEMORYCHECK_TYPE.rst b/Help/variable/CTEST_MEMORYCHECK_TYPE.rst index d412e79..203f3a6 100644 --- a/Help/variable/CTEST_MEMORYCHECK_TYPE.rst +++ b/Help/variable/CTEST_MEMORYCHECK_TYPE.rst
@@ -4,7 +4,8 @@ .. versionadded:: 3.1 Specify the CTest ``MemoryCheckType`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. Valid values are ``Valgrind``, ``Purify``, ``BoundsChecker``, ``DrMemory``, ``CudaSanitizer``, ``ThreadSanitizer``, ``AddressSanitizer``, ``LeakSanitizer``, ``MemorySanitizer`` and ``UndefinedBehaviorSanitizer``.
diff --git a/Help/variable/CTEST_NIGHTLY_START_TIME.rst b/Help/variable/CTEST_NIGHTLY_START_TIME.rst index 3ccf50a..3f500c9 100644 --- a/Help/variable/CTEST_NIGHTLY_START_TIME.rst +++ b/Help/variable/CTEST_NIGHTLY_START_TIME.rst
@@ -4,7 +4,8 @@ .. versionadded:: 3.1 Specify the CTest ``NightlyStartTime`` setting in a :manual:`ctest(1)` -:ref:`Dashboard Client` script. +:ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. Note that this variable must always be set for a nightly build in a dashboard script. It is needed so that nightly builds can be properly grouped
diff --git a/Help/variable/CTEST_NOTES_FILES.rst b/Help/variable/CTEST_NOTES_FILES.rst index cc66345..a327a28 100644 --- a/Help/variable/CTEST_NOTES_FILES.rst +++ b/Help/variable/CTEST_NOTES_FILES.rst
@@ -2,4 +2,5 @@ ----------------- Specify files for :command:`ctest_submit(PARTS Notes)` to submit -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_P4_CLIENT.rst b/Help/variable/CTEST_P4_CLIENT.rst index 63c1739..b952c97 100644 --- a/Help/variable/CTEST_P4_CLIENT.rst +++ b/Help/variable/CTEST_P4_CLIENT.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``P4Client`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_P4_COMMAND.rst b/Help/variable/CTEST_P4_COMMAND.rst index 61cd4a1..fbe9291 100644 --- a/Help/variable/CTEST_P4_COMMAND.rst +++ b/Help/variable/CTEST_P4_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``P4Command`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_P4_OPTIONS.rst b/Help/variable/CTEST_P4_OPTIONS.rst index b4889f8..f1e92ed 100644 --- a/Help/variable/CTEST_P4_OPTIONS.rst +++ b/Help/variable/CTEST_P4_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``P4Options`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_P4_UPDATE_OPTIONS.rst b/Help/variable/CTEST_P4_UPDATE_OPTIONS.rst index 5dec345..eeaafe5 100644 --- a/Help/variable/CTEST_P4_UPDATE_OPTIONS.rst +++ b/Help/variable/CTEST_P4_UPDATE_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``P4UpdateOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_PRESET.rst b/Help/variable/CTEST_PRESET.rst new file mode 100644 index 0000000..e2af27b --- /dev/null +++ b/Help/variable/CTEST_PRESET.rst
@@ -0,0 +1,22 @@ +CTEST_PRESET +------------ + +.. versionadded:: 4.4 + +Specify a preset name to use for all relevant :manual:`ctest(1)` +:ref:`Dashboard Client` steps in a dashboard script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. + +This variable sets the ``PRESET`` option for :command:`ctest_configure`, +:command:`ctest_build`, :command:`ctest_test`, and :command:`ctest_memcheck` +when those commands are not given an explicit ``PRESET`` argument and the +corresponding command-specific variable +(:variable:`CTEST_CONFIGURE_PRESET`, :variable:`CTEST_BUILD_PRESET`, or +:variable:`CTEST_TEST_PRESET`) is also unset. + +For :command:`ctest_configure`, an error is raised if no configure preset +with this name exists. For :command:`ctest_build`, :command:`ctest_test`, +and :command:`ctest_memcheck`, a warning is emitted and the variable is +ignored if no preset of the matching type exists with this name. + +See also :variable:`CTEST_PRESETS_FILE`.
diff --git a/Help/variable/CTEST_PRESETS_FILE.rst b/Help/variable/CTEST_PRESETS_FILE.rst new file mode 100644 index 0000000..24c991d --- /dev/null +++ b/Help/variable/CTEST_PRESETS_FILE.rst
@@ -0,0 +1,16 @@ +CTEST_PRESETS_FILE +------------------ + +.. versionadded:: 4.4 + +Specify the ``PRESETS_FILE`` option for all relevant :manual:`ctest(1)` +:ref:`Dashboard Client` step commands in a dashboard script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. + +This variable sets the ``PRESETS_FILE`` option for +:command:`ctest_configure`, :command:`ctest_build`, :command:`ctest_test`, +and :command:`ctest_memcheck` when those commands are not given an explicit +``PRESETS_FILE`` argument. + +See also :variable:`CTEST_PRESET`, :variable:`CTEST_CONFIGURE_PRESET`, +:variable:`CTEST_BUILD_PRESET`, and :variable:`CTEST_TEST_PRESET`.
diff --git a/Help/variable/CTEST_RESOURCE_SPEC_FILE.rst b/Help/variable/CTEST_RESOURCE_SPEC_FILE.rst index 294e138..ff59d14 100644 --- a/Help/variable/CTEST_RESOURCE_SPEC_FILE.rst +++ b/Help/variable/CTEST_RESOURCE_SPEC_FILE.rst
@@ -4,7 +4,8 @@ .. versionadded:: 3.18 Specify the CTest ``ResourceSpecFile`` setting in a :manual:`ctest(1)` -:ref:`Dashboard Client` script. +:ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. This can also be used to specify the resource spec file from a CMake build. If no ``RESOURCE_SPEC_FILE`` is passed to :command:`ctest_test`, and
diff --git a/Help/variable/CTEST_SITE.rst b/Help/variable/CTEST_SITE.rst index 7875b30..df430bd 100644 --- a/Help/variable/CTEST_SITE.rst +++ b/Help/variable/CTEST_SITE.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``Site`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_SOURCE_DIRECTORY.rst b/Help/variable/CTEST_SOURCE_DIRECTORY.rst index 84fd1f8..ff80ffb 100644 --- a/Help/variable/CTEST_SOURCE_DIRECTORY.rst +++ b/Help/variable/CTEST_SOURCE_DIRECTORY.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``SourceDirectory`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_SUBMIT_INACTIVITY_TIMEOUT.rst b/Help/variable/CTEST_SUBMIT_INACTIVITY_TIMEOUT.rst index cdc4ff4..763d739 100644 --- a/Help/variable/CTEST_SUBMIT_INACTIVITY_TIMEOUT.rst +++ b/Help/variable/CTEST_SUBMIT_INACTIVITY_TIMEOUT.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.23 Specify the CTest ``SubmitInactivityTimeout`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_SUBMIT_PARTS.rst b/Help/variable/CTEST_SUBMIT_PARTS.rst new file mode 100644 index 0000000..ee712b3 --- /dev/null +++ b/Help/variable/CTEST_SUBMIT_PARTS.rst
@@ -0,0 +1,25 @@ +CTEST_SUBMIT_PARTS +------------------ + +.. versionadded:: 4.4 + +Specify a :ref:`semicolon-separated list <CMake Language Lists>` of parts +for the :command:`ctest_submit` command to submit in a +:manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` +option. + +Valid part names are the same as those accepted by the ``PARTS`` option of +:command:`ctest_submit`: ``Start``, ``Update``, ``Configure``, ``Build``, +``Test``, ``Coverage``, ``MemCheck``, ``Submit``, ``Notes``, ``ExtraFiles``, +``Upload``, and ``Done``. + +If the :command:`ctest_submit` command is given an explicit ``PARTS`` +argument, that argument takes precedence and this variable is ignored. + +When this variable is empty or unset (and no explicit ``PARTS`` argument is +given), all available parts are submitted (the default behavior). + +Example --- submit only the Configure result in a single ``ctest`` invocation:: + + ctest -T Configure -T Submit -D CTEST_SUBMIT_PARTS=Configure
diff --git a/Help/variable/CTEST_SUBMIT_URL.rst b/Help/variable/CTEST_SUBMIT_URL.rst index 3147b4a..c117302 100644 --- a/Help/variable/CTEST_SUBMIT_URL.rst +++ b/Help/variable/CTEST_SUBMIT_URL.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.14 Specify the CTest ``SubmitURL`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_SVN_COMMAND.rst b/Help/variable/CTEST_SVN_COMMAND.rst index 8d8b263..b22a0cd 100644 --- a/Help/variable/CTEST_SVN_COMMAND.rst +++ b/Help/variable/CTEST_SVN_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``SVNCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_SVN_OPTIONS.rst b/Help/variable/CTEST_SVN_OPTIONS.rst index 3d111cc..989e94a 100644 --- a/Help/variable/CTEST_SVN_OPTIONS.rst +++ b/Help/variable/CTEST_SVN_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``SVNOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_SVN_UPDATE_OPTIONS.rst b/Help/variable/CTEST_SVN_UPDATE_OPTIONS.rst index 0830c2f..8a2a945 100644 --- a/Help/variable/CTEST_SVN_UPDATE_OPTIONS.rst +++ b/Help/variable/CTEST_SVN_UPDATE_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``SVNUpdateOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_TEST_COVERAGE_TOOL.rst b/Help/variable/CTEST_TEST_COVERAGE_TOOL.rst new file mode 100644 index 0000000..05d62e1 --- /dev/null +++ b/Help/variable/CTEST_TEST_COVERAGE_TOOL.rst
@@ -0,0 +1,8 @@ +CTEST_TEST_COVERAGE_TOOL +------------------------ + +.. versionadded:: 4.4 + +Specify the CTest :ref:`CoverageTool <ctest-CoverageTool>` setting +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_TEST_LOAD.rst b/Help/variable/CTEST_TEST_LOAD.rst index fed4042..187303a 100644 --- a/Help/variable/CTEST_TEST_LOAD.rst +++ b/Help/variable/CTEST_TEST_LOAD.rst
@@ -4,6 +4,7 @@ .. versionadded:: 3.4 Specify the ``TestLoad`` setting in the :ref:`CTest Test Step` -of a :manual:`ctest(1)` :ref:`Dashboard Client` script. This sets the +of a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. This sets the default value for the ``TEST_LOAD`` option of the :command:`ctest_test` command.
diff --git a/Help/variable/CTEST_TEST_PRESET.rst b/Help/variable/CTEST_TEST_PRESET.rst new file mode 100644 index 0000000..6f57a38 --- /dev/null +++ b/Help/variable/CTEST_TEST_PRESET.rst
@@ -0,0 +1,11 @@ +CTEST_TEST_PRESET +----------------- + +.. versionadded:: 4.4 + +Specify the ``PRESET`` option for the :command:`ctest_test` and +:command:`ctest_memcheck` commands in a :manual:`ctest(1)` +:ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option. + +See also :variable:`CTEST_PRESET` and :variable:`CTEST_PRESETS_FILE`.
diff --git a/Help/variable/CTEST_TEST_TIMEOUT.rst b/Help/variable/CTEST_TEST_TIMEOUT.rst index f6a1daa..11a0bae 100644 --- a/Help/variable/CTEST_TEST_TIMEOUT.rst +++ b/Help/variable/CTEST_TEST_TIMEOUT.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``TimeOut`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_TLS_VERIFY.rst b/Help/variable/CTEST_TLS_VERIFY.rst index b283842..f3830d2 100644 --- a/Help/variable/CTEST_TLS_VERIFY.rst +++ b/Help/variable/CTEST_TLS_VERIFY.rst
@@ -5,7 +5,8 @@ Specify the CTest ``TLSVerify`` setting in a :manual:`ctest(1)` :ref:`Dashboard Client` script or in project ``CMakeLists.txt`` code -before including the :module:`CTest` module. The value is a boolean +before including the :module:`CTest` module, or on the :program:`ctest` +command line via the :ctest-dashboard-option:`-D` option. The value is a boolean indicating whether to verify the server certificate when submitting to a dashboard via ``https://`` URLs.
diff --git a/Help/variable/CTEST_TLS_VERSION.rst b/Help/variable/CTEST_TLS_VERSION.rst index b2aec5b..789b69e 100644 --- a/Help/variable/CTEST_TLS_VERSION.rst +++ b/Help/variable/CTEST_TLS_VERSION.rst
@@ -5,7 +5,8 @@ Specify the CTest ``TLSVersion`` setting in a :manual:`ctest(1)` :ref:`Dashboard Client` script or in project ``CMakeLists.txt`` code -before including the :module:`CTest` module. The value is a minimum +before including the :module:`CTest` module, or on the :program:`ctest` +command line via the :ctest-dashboard-option:`-D` option. The value is a minimum TLS version allowed when submitting to a dashboard via ``https://`` URLs. The value may be one of:
diff --git a/Help/variable/CTEST_UPDATE_COMMAND.rst b/Help/variable/CTEST_UPDATE_COMMAND.rst index 1084065..86052cd 100644 --- a/Help/variable/CTEST_UPDATE_COMMAND.rst +++ b/Help/variable/CTEST_UPDATE_COMMAND.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``UpdateCommand`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_UPDATE_OPTIONS.rst b/Help/variable/CTEST_UPDATE_OPTIONS.rst index ef2dad3..ea24061 100644 --- a/Help/variable/CTEST_UPDATE_OPTIONS.rst +++ b/Help/variable/CTEST_UPDATE_OPTIONS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``UpdateOptions`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_UPDATE_VERSION_ONLY.rst b/Help/variable/CTEST_UPDATE_VERSION_ONLY.rst index c778338..062a597 100644 --- a/Help/variable/CTEST_UPDATE_VERSION_ONLY.rst +++ b/Help/variable/CTEST_UPDATE_VERSION_ONLY.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest :ref:`UpdateVersionOnly <UpdateVersionOnly>` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_UPDATE_VERSION_OVERRIDE.rst b/Help/variable/CTEST_UPDATE_VERSION_OVERRIDE.rst index 9f4ecc3..aeeb5c0 100644 --- a/Help/variable/CTEST_UPDATE_VERSION_OVERRIDE.rst +++ b/Help/variable/CTEST_UPDATE_VERSION_OVERRIDE.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.15 Specify the CTest :ref:`UpdateVersionOverride <UpdateVersionOverride>` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/CTEST_USE_LAUNCHERS.rst b/Help/variable/CTEST_USE_LAUNCHERS.rst index 3326bad..db657a1 100644 --- a/Help/variable/CTEST_USE_LAUNCHERS.rst +++ b/Help/variable/CTEST_USE_LAUNCHERS.rst
@@ -4,4 +4,5 @@ .. versionadded:: 3.1 Specify the CTest ``UseLaunchers`` setting -in a :manual:`ctest(1)` :ref:`Dashboard Client` script. +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ctest-dashboard-option:`-D` option.
diff --git a/Help/variable/include/LINKER_PREDEFINED_TYPES.rst b/Help/variable/include/LINKER_PREDEFINED_TYPES.rst index 3c1b7b8..63ed955 100644 --- a/Help/variable/include/LINKER_PREDEFINED_TYPES.rst +++ b/Help/variable/include/LINKER_PREDEFINED_TYPES.rst
@@ -45,8 +45,8 @@ ``NVIDIA``, and ``Swift`` compilers. ``MOLD`` - Use the `mold linker <https://github.com/rui314/mold>`_. This type is - supported on the following platform-compiler combinations: + Use the `mold linker`_. This type is supported on the following + platform-compiler combinations: * Linux: ``GNU``, ``Clang``, ``LLVMFlang``, and ``NVIDIA`` compilers. * Apple platforms: ``Clang`` and ``AppleClang`` compilers (acts as an @@ -56,6 +56,12 @@ Use the `sold linker`_. This type is only supported on Apple platforms with ``Clang`` and ``AppleClang`` compilers. +``WILD`` + .. versionadded:: 4.4 + + Use the `wild linker`_. This type is only supported on Linux platforms + with ``Clang`` and ``GNU`` compilers. + ``APPLE_CLASSIC`` Use the Apple linker in the classic behavior (i.e. before ``Xcode 15.0``). This type is only supported on Apple platforms with ``GNU``, ``Clang``, @@ -66,4 +72,6 @@ platform with ``MSVC``, ``Clang`` with MSVC-like front-end, and ``Swift`` compilers. +.. _mold linker: https://github.com/rui314/mold .. _sold linker: https://github.com/bluewhalesystems/sold +.. _wild linker: https://github.com/wild-linker/wild
diff --git a/Help/variable/include/LINK_GROUP_PREDEFINED_FEATURES.rst b/Help/variable/include/LINK_GROUP_PREDEFINED_FEATURES.rst index 557886e..f83fefd 100644 --- a/Help/variable/include/LINK_GROUP_PREDEFINED_FEATURES.rst +++ b/Help/variable/include/LINK_GROUP_PREDEFINED_FEATURES.rst
@@ -20,3 +20,7 @@ This feature is available when using toolchains that target Linux, BSD, and SunOS. It can also be used when targeting Windows platforms if the GNU toolchain is used. + + .. versionadded:: 4.4 + ``RESCAN`` is additionally available when using toolchains targeting + Haiku.
diff --git a/Modules/AndroidTestUtilities/PushToAndroidDevice.cmake b/Modules/AndroidTestUtilities/PushToAndroidDevice.cmake index f6389d6..234fb5f 100644 --- a/Modules/AndroidTestUtilities/PushToAndroidDevice.cmake +++ b/Modules/AndroidTestUtilities/PushToAndroidDevice.cmake
@@ -41,7 +41,8 @@ set(${file_exists} "") execute_process( COMMAND ${adb_executable} shell ls ${device_file} - OUTPUT_VARIABLE out_var ERROR_VARIABLE out_var) + OUTPUT_VARIABLE out_var ERROR_VARIABLE out_var + RESULT_VARIABLE _result_check_file) if(NOT out_var) # when a directory exists but is empty the output is empty set(${file_exists} "YES") else()
diff --git a/Modules/BasicConfigVersion-ExactVersion.cmake.in b/Modules/BasicConfigVersion-ExactVersion.cmake.in deleted file mode 100644 index 8dac2af..0000000 --- a/Modules/BasicConfigVersion-ExactVersion.cmake.in +++ /dev/null
@@ -1,72 +0,0 @@ -# This is a basic version file for the Config-mode of find_package(). -# It is used by write_basic_package_version_file() as input file for configure_file() -# to create a version-file which can be installed along a config.cmake file. -# -# The created file sets PACKAGE_VERSION_EXACT if the current version string and -# the requested version string are exactly the same and it sets -# PACKAGE_VERSION_COMPATIBLE if the current version is equal to the requested version. -# The tweak version component is ignored. -# The variable CVF_VERSION must be set before calling configure_file(). - - -if (PACKAGE_FIND_VERSION_RANGE) - message(AUTHOR_WARNING - "`find_package()` specify a version range but the version strategy " - "(ExactVersion) of the module `${PACKAGE_FIND_NAME}` is incompatible " - "with this request. Only the lower endpoint of the range will be used.") -endif() - -set(PACKAGE_VERSION "@CVF_VERSION@") - -if("@CVF_VERSION@" MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") # strip the tweak version - set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") - set(CVF_VERSION_MINOR "${CMAKE_MATCH_2}") - set(CVF_VERSION_PATCH "${CMAKE_MATCH_3}") - - if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0) - string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}") - endif() - if(NOT CVF_VERSION_MINOR VERSION_EQUAL 0) - string(REGEX REPLACE "^0+" "" CVF_VERSION_MINOR "${CVF_VERSION_MINOR}") - endif() - if(NOT CVF_VERSION_PATCH VERSION_EQUAL 0) - string(REGEX REPLACE "^0+" "" CVF_VERSION_PATCH "${CVF_VERSION_PATCH}") - endif() - - set(CVF_VERSION_NO_TWEAK "${CVF_VERSION_MAJOR}.${CVF_VERSION_MINOR}.${CVF_VERSION_PATCH}") -else() - set(CVF_VERSION_NO_TWEAK "@CVF_VERSION@") -endif() - -if(PACKAGE_FIND_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") # strip the tweak version - set(REQUESTED_VERSION_MAJOR "${CMAKE_MATCH_1}") - set(REQUESTED_VERSION_MINOR "${CMAKE_MATCH_2}") - set(REQUESTED_VERSION_PATCH "${CMAKE_MATCH_3}") - - if(NOT REQUESTED_VERSION_MAJOR VERSION_EQUAL 0) - string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_MAJOR "${REQUESTED_VERSION_MAJOR}") - endif() - if(NOT REQUESTED_VERSION_MINOR VERSION_EQUAL 0) - string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_MINOR "${REQUESTED_VERSION_MINOR}") - endif() - if(NOT REQUESTED_VERSION_PATCH VERSION_EQUAL 0) - string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_PATCH "${REQUESTED_VERSION_PATCH}") - endif() - - set(REQUESTED_VERSION_NO_TWEAK - "${REQUESTED_VERSION_MAJOR}.${REQUESTED_VERSION_MINOR}.${REQUESTED_VERSION_PATCH}") -else() - set(REQUESTED_VERSION_NO_TWEAK "${PACKAGE_FIND_VERSION}") -endif() - -if(REQUESTED_VERSION_NO_TWEAK STREQUAL CVF_VERSION_NO_TWEAK) - set(PACKAGE_VERSION_COMPATIBLE TRUE) -else() - set(PACKAGE_VERSION_COMPATIBLE FALSE) -endif() - -if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) - set(PACKAGE_VERSION_EXACT TRUE) -endif() - -@CVF_ARCH_INDEPENDENT_CHECK@
diff --git a/Modules/BasicConfigVersion-SameFullVersion.cmake.in b/Modules/BasicConfigVersion-SameFullVersion.cmake.in new file mode 100644 index 0000000..e60325f --- /dev/null +++ b/Modules/BasicConfigVersion-SameFullVersion.cmake.in
@@ -0,0 +1,67 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version and requested version are +# equal after normalizing leading zeroes in each specified version component. +# The variable CVF_VERSION must be set before calling configure_file(). + + +if (PACKAGE_FIND_VERSION_RANGE) + message(AUTHOR_WARNING + "`find_package()` specify a version range but the version strategy " + "(SameFullVersion) of the module `${PACKAGE_FIND_NAME}` is incompatible " + "with this request. Only the lower endpoint of the range will be used.") +endif() + +set(PACKAGE_VERSION "@CVF_VERSION@") + +if("@CVF_VERSION@" MATCHES "^[0-9]+(\\.[0-9]+){0,3}$") + string(REPLACE "." ";" CVF_VERSION_COMPONENTS "@CVF_VERSION@") + unset(CVF_VERSION_NORMALIZED) + foreach(CVF_VERSION_COMPONENT IN LISTS CVF_VERSION_COMPONENTS) + set(CVF_VERSION_COMPONENT_VALUE "${CVF_VERSION_COMPONENT}") + if(NOT CVF_VERSION_COMPONENT_VALUE VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_COMPONENT_VALUE "${CVF_VERSION_COMPONENT_VALUE}") + endif() + if(DEFINED CVF_VERSION_NORMALIZED) + string(APPEND CVF_VERSION_NORMALIZED ".${CVF_VERSION_COMPONENT_VALUE}") + else() + set(CVF_VERSION_NORMALIZED "${CVF_VERSION_COMPONENT_VALUE}") + endif() + endforeach() +else() + set(CVF_VERSION_NORMALIZED "@CVF_VERSION@") +endif() + +if("${PACKAGE_FIND_VERSION}" MATCHES "^[0-9]+(\\.[0-9]+){0,3}$") + string(REPLACE "." ";" REQUESTED_VERSION_COMPONENTS "${PACKAGE_FIND_VERSION}") + unset(REQUESTED_VERSION_NORMALIZED) + foreach(REQUESTED_VERSION_COMPONENT IN LISTS REQUESTED_VERSION_COMPONENTS) + set(REQUESTED_VERSION_COMPONENT_VALUE "${REQUESTED_VERSION_COMPONENT}") + if(NOT REQUESTED_VERSION_COMPONENT_VALUE VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_COMPONENT_VALUE "${REQUESTED_VERSION_COMPONENT_VALUE}") + endif() + if(DEFINED REQUESTED_VERSION_NORMALIZED) + string(APPEND REQUESTED_VERSION_NORMALIZED ".${REQUESTED_VERSION_COMPONENT_VALUE}") + else() + set(REQUESTED_VERSION_NORMALIZED "${REQUESTED_VERSION_COMPONENT_VALUE}") + endif() + endforeach() +else() + set(REQUESTED_VERSION_NORMALIZED "${PACKAGE_FIND_VERSION}") +endif() + +if(REQUESTED_VERSION_NORMALIZED STREQUAL CVF_VERSION_NORMALIZED) + set(PACKAGE_VERSION_COMPATIBLE TRUE) +else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) +endif() + +if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) +endif() + +@CVF_ARCH_INDEPENDENT_CHECK@
diff --git a/Modules/BasicConfigVersion-SamePatchVersion.cmake.in b/Modules/BasicConfigVersion-SamePatchVersion.cmake.in new file mode 100644 index 0000000..6c0ac24 --- /dev/null +++ b/Modules/BasicConfigVersion-SamePatchVersion.cmake.in
@@ -0,0 +1,72 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version and requested version are +# equal through the patch version. The tweak version component is ignored. +# The variable CVF_VERSION must be set before calling configure_file(). + + +if (PACKAGE_FIND_VERSION_RANGE) + message(AUTHOR_WARNING + "`find_package()` specify a version range but the version strategy " + "(SamePatchVersion) of the module `${PACKAGE_FIND_NAME}` is incompatible " + "with this request. Only the lower endpoint of the range will be used.") +endif() + +set(PACKAGE_VERSION "@CVF_VERSION@") + +if("@CVF_VERSION@" MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") # strip the tweak version + set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(CVF_VERSION_MINOR "${CMAKE_MATCH_2}") + set(CVF_VERSION_PATCH "${CMAKE_MATCH_3}") + + if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}") + endif() + if(NOT CVF_VERSION_MINOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MINOR "${CVF_VERSION_MINOR}") + endif() + if(NOT CVF_VERSION_PATCH VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_PATCH "${CVF_VERSION_PATCH}") + endif() + + set(CVF_VERSION_NO_TWEAK "${CVF_VERSION_MAJOR}.${CVF_VERSION_MINOR}.${CVF_VERSION_PATCH}") +else() + set(CVF_VERSION_NO_TWEAK "@CVF_VERSION@") +endif() + +if(PACKAGE_FIND_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") # strip the tweak version + set(REQUESTED_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(REQUESTED_VERSION_MINOR "${CMAKE_MATCH_2}") + set(REQUESTED_VERSION_PATCH "${CMAKE_MATCH_3}") + + if(NOT REQUESTED_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_MAJOR "${REQUESTED_VERSION_MAJOR}") + endif() + if(NOT REQUESTED_VERSION_MINOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_MINOR "${REQUESTED_VERSION_MINOR}") + endif() + if(NOT REQUESTED_VERSION_PATCH VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_PATCH "${REQUESTED_VERSION_PATCH}") + endif() + + set(REQUESTED_VERSION_NO_TWEAK + "${REQUESTED_VERSION_MAJOR}.${REQUESTED_VERSION_MINOR}.${REQUESTED_VERSION_PATCH}") +else() + set(REQUESTED_VERSION_NO_TWEAK "${PACKAGE_FIND_VERSION}") +endif() + +if(REQUESTED_VERSION_NO_TWEAK STREQUAL CVF_VERSION_NO_TWEAK) + set(PACKAGE_VERSION_COMPATIBLE TRUE) +else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) +endif() + +if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) +endif() + +@CVF_ARCH_INDEPENDENT_CHECK@
diff --git a/Modules/BasicConfigVersion-SemanticVersion.cmake.in b/Modules/BasicConfigVersion-SemanticVersion.cmake.in new file mode 100644 index 0000000..e1962a3 --- /dev/null +++ b/Modules/BasicConfigVersion-SemanticVersion.cmake.in
@@ -0,0 +1,102 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version +# and stays within the semantic-version compatibility window. +# For versions with major > 0, compatibility requires the same major version. +# For versions with major = 0, compatibility requires the same major and minor +# versions. +# The variable CVF_VERSION must be set before calling configure_file(). + + +set(PACKAGE_VERSION "@CVF_VERSION@") + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + + if("@CVF_VERSION@" MATCHES "^([0-9]+)(\\.([0-9]+))?") + set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(CVF_VERSION_MINOR "${CMAKE_MATCH_3}") + + if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}") + endif() + if(NOT "${CVF_VERSION_MINOR}" STREQUAL "" AND NOT CVF_VERSION_MINOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MINOR "${CVF_VERSION_MINOR}") + endif() + else() + set(CVF_VERSION_MAJOR "@CVF_VERSION@") + set(CVF_VERSION_MINOR "") + endif() + + if(CVF_VERSION_MAJOR STREQUAL "0") + if(PACKAGE_FIND_VERSION_RANGE) + if(CVF_VERSION_MINOR STREQUAL "") + set(PACKAGE_VERSION_COMPATIBLE FALSE) + else() + math(EXPR CVF_VERSION_MINOR_NEXT "${CVF_VERSION_MINOR} + 1") + if (NOT (PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + AND PACKAGE_FIND_VERSION_MIN_MINOR STREQUAL CVF_VERSION_MINOR) + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" + AND NOT (PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR + AND PACKAGE_FIND_VERSION_MAX_MINOR STREQUAL CVF_VERSION_MINOR)) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" + AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL ${CVF_VERSION_MAJOR}.${CVF_VERSION_MINOR_NEXT}))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + AND PACKAGE_FIND_VERSION_MIN_MINOR STREQUAL CVF_VERSION_MINOR + AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + endif() + else() + if(NOT PACKAGE_FIND_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" PACKAGE_FIND_VERSION_MAJOR "${PACKAGE_FIND_VERSION_MAJOR}") + endif() + if(NOT "${PACKAGE_FIND_VERSION_MINOR}" STREQUAL "" AND NOT PACKAGE_FIND_VERSION_MINOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" PACKAGE_FIND_VERSION_MINOR "${PACKAGE_FIND_VERSION_MINOR}") + endif() + + if((PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR) AND + (PACKAGE_FIND_VERSION_MINOR STREQUAL CVF_VERSION_MINOR)) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + endif() + else() + if(PACKAGE_FIND_VERSION_RANGE) + math(EXPR CVF_VERSION_MAJOR_NEXT "${CVF_VERSION_MAJOR} + 1") + if (NOT PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL CVF_VERSION_MAJOR_NEXT))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + else() + if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + endif() + endif() + + if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() + +@CVF_ARCH_INDEPENDENT_CHECK@
diff --git a/Modules/BundleUtilities.cmake b/Modules/BundleUtilities.cmake index 40821e0..7b063f8 100644 --- a/Modules/BundleUtilities.cmake +++ b/Modules/BundleUtilities.cmake
@@ -506,18 +506,13 @@ ") #]=======================================================================] -function(_warn_cmp0080) - cmake_policy(GET_WARNING CMP0080 _cmp0080_warning) - message(AUTHOR_WARNING "${_cmp0080_warning}\n") -endfunction() - # Do not include this module at configure time! if(DEFINED CMAKE_GENERATOR) cmake_policy(GET CMP0080 _BundleUtilities_CMP0080) if(_BundleUtilities_CMP0080 STREQUAL "NEW") message(FATAL_ERROR "BundleUtilities cannot be included at configure time!") elseif(NOT _BundleUtilities_CMP0080 STREQUAL "OLD" AND NOT _CMP0080_SUPPRESS_WARNING) - _warn_cmp0080() + cmake_policy(ISSUE_WARNING CMP0080) endif() endif() @@ -695,6 +690,7 @@ -type f \( -perm -0100 -o -perm -0010 -o -perm -0001 \) OUTPUT_VARIABLE file_list OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_find ) string(REPLACE "\n" ";" file_list "${file_list}") else() @@ -991,7 +987,9 @@ if (NOT EXISTS "${target_dir}") file(MAKE_DIRECTORY "${target_dir}") endif() - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${symlink_target}" "${resolved_embedded_item}") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${symlink_target}" + "${resolved_embedded_item}" + RESULT_VARIABLE _result_symlink) endif() endfunction() @@ -1009,7 +1007,9 @@ message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...") else() #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" + "${resolved_embedded_item}" + RESULT_VARIABLE _result_copy) if(UNIX AND NOT APPLE) file(RPATH_REMOVE FILE "${resolved_embedded_item}") endif() @@ -1042,14 +1042,18 @@ else() # Framework lib itself: #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" + "${resolved_embedded_item}" + RESULT_VARIABLE _result_copy) # Plus Resources, if they exist: string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources" resolved_resources "${resolved_item}") string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources" resolved_embedded_resources "${resolved_embedded_item}") if(EXISTS "${resolved_resources}") #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_resources}' '${resolved_embedded_resources}'") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_resources}" "${resolved_embedded_resources}") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory + "${resolved_resources}" "${resolved_embedded_resources}" + RESULT_VARIABLE _result_copy_resources) endif() # Some frameworks e.g. Qt put Info.plist in wrong place, so when it is @@ -1060,7 +1064,9 @@ string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources/Info.plist" resolved_embedded_info_plist "${resolved_embedded_item}") if(EXISTS "${resolved_info_plist}") #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_info_plist}' '${resolved_embedded_info_plist}'") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_info_plist}" "${resolved_embedded_info_plist}") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy + "${resolved_info_plist}" "${resolved_embedded_info_plist}" + RESULT_VARIABLE _result_copy_info_plist) endif() endif() @@ -1071,17 +1077,25 @@ if(resolved_embedded_versions_basename STREQUAL "Versions") # Ensure Current symlink points to the framework version if(NOT EXISTS "${resolved_embedded_versions}/Current") - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${resolved_embedded_version}" "${resolved_embedded_versions}/Current") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + "${resolved_embedded_version}" + "${resolved_embedded_versions}/Current" + RESULT_VARIABLE _result_symlink_current) endif() # Restore symlinks in framework root pointing to current framework # binary and resources: string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1" resolved_embedded_root "${resolved_embedded_item}") string(REGEX REPLACE "^.*/([^/]+)$" "\\1" resolved_embedded_item_basename "${resolved_embedded_item}") if(NOT EXISTS "${resolved_embedded_root}/${resolved_embedded_item_basename}") - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "Versions/Current/${resolved_embedded_item_basename}" "${resolved_embedded_root}/${resolved_embedded_item_basename}") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + "Versions/Current/${resolved_embedded_item_basename}" + "${resolved_embedded_root}/${resolved_embedded_item_basename}" + RESULT_VARIABLE _result_symlink_item) endif() if(NOT EXISTS "${resolved_embedded_root}/Resources") - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "Versions/Current/Resources" "${resolved_embedded_root}/Resources") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + "Versions/Current/Resources" "${resolved_embedded_root}/Resources" + RESULT_VARIABLE _result_symlink_resources) endif() endif() endif() @@ -1156,6 +1170,7 @@ execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} OUTPUT_VARIABLE install_name_tool_usage ERROR_VARIABLE install_name_tool_usage + RESULT_VARIABLE _result_install_name_tool_usage ) if(install_name_tool_usage MATCHES ".*-delete_rpath.*") foreach(rpath ${${ikey}_RPATHS}) @@ -1177,7 +1192,8 @@ NOT file_contents MATCHES "^#!") set(cmd ${CMAKE_INSTALL_NAME_TOOL} ${changes} "${resolved_embedded_item}") if(BU_CHMOD_BUNDLE_ITEMS) - execute_process(COMMAND chmod u+w "${resolved_embedded_item}") + execute_process(COMMAND chmod u+w "${resolved_embedded_item}" + RESULT_VARIABLE _result_chmod) endif() execute_process(COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result) if(NOT install_name_tool_result EQUAL 0) @@ -1278,7 +1294,8 @@ function(copy_and_fixup_bundle src dst libs dirs) - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${src}" "${dst}") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${src}" "${dst}" + RESULT_VARIABLE _result_copy_directory) fixup_bundle("${dst}" "${libs}" "${dirs}") endfunction()
diff --git a/Modules/CMakeASMCompiler.cmake.in b/Modules/CMakeASMCompiler.cmake.in index 5d54390..81da7ec 100644 --- a/Modules/CMakeASMCompiler.cmake.in +++ b/Modules/CMakeASMCompiler.cmake.in
@@ -8,9 +8,10 @@ set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER "@_CMAKE_ASM_COMPILER_LINKER@") +set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_ARCHITECTURE_FLAGS "@_CMAKE_ASM_COMPILER_LINKER_ARCHITECTURE_FLAGS@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_ID "@_CMAKE_ASM_COMPILER_LINKER_ID@") -set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_VERSION @_CMAKE_ASM_COMPILER_LINKER_VERSION@) -set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_FRONTEND_VARIANT @_CMAKE_ASM_COMPILER_LINKER_FRONTEND_VARIANT@) +set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_VERSION "@_CMAKE_ASM_COMPILER_LINKER_VERSION@") +set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_FRONTEND_VARIANT "@_CMAKE_ASM_COMPILER_LINKER_FRONTEND_VARIANT@") set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1)
diff --git a/Modules/CMakeASM_NASMInformation.cmake b/Modules/CMakeASM_NASMInformation.cmake index 54e5153..c60aef0 100644 --- a/Modules/CMakeASM_NASMInformation.cmake +++ b/Modules/CMakeASM_NASMInformation.cmake
@@ -8,30 +8,34 @@ if(NOT CMAKE_ASM_NASM_OBJECT_FORMAT) if(WIN32) - if(DEFINED CMAKE_C_SIZEOF_DATA_PTR AND CMAKE_C_SIZEOF_DATA_PTR EQUAL 8) + if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) set(CMAKE_ASM_NASM_OBJECT_FORMAT win64) - elseif(DEFINED CMAKE_CXX_SIZEOF_DATA_PTR AND CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) - set(CMAKE_ASM_NASM_OBJECT_FORMAT win64) + elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 4) + set(CMAKE_ASM_NASM_OBJECT_FORMAT win32) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") set(CMAKE_ASM_NASM_OBJECT_FORMAT win64) else() set(CMAKE_ASM_NASM_OBJECT_FORMAT win32) endif() elseif(APPLE) - if(DEFINED CMAKE_C_SIZEOF_DATA_PTR AND CMAKE_C_SIZEOF_DATA_PTR EQUAL 8) + if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) set(CMAKE_ASM_NASM_OBJECT_FORMAT macho64) - elseif(DEFINED CMAKE_CXX_SIZEOF_DATA_PTR AND CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) - set(CMAKE_ASM_NASM_OBJECT_FORMAT macho64) + elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 4) + set(CMAKE_ASM_NASM_OBJECT_FORMAT macho32) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") set(CMAKE_ASM_NASM_OBJECT_FORMAT macho64) else() set(CMAKE_ASM_NASM_OBJECT_FORMAT macho) endif() else() - if(DEFINED CMAKE_C_SIZEOF_DATA_PTR AND CMAKE_C_SIZEOF_DATA_PTR EQUAL 8) + if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) - elseif(DEFINED CMAKE_CXX_SIZEOF_DATA_PTR AND CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) - set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) + elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 4) + if(";${CMAKE_C_COMPILER_ARCHITECTURE_ID};${CMAKE_CXX_COMPILER_ARCHITECTURE_ID};" MATCHES ";x86_64;") + set(CMAKE_ASM_NASM_OBJECT_FORMAT elfx32) + else() + set(CMAKE_ASM_NASM_OBJECT_FORMAT elf32) + endif() elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) else() @@ -41,7 +45,7 @@ endif() if(NOT CMAKE_ASM_NASM_COMPILE_OBJECT) - set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>") + set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <DEFINES> <INCLUDES> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} <FLAGS> -o <OBJECT> <SOURCE>") endif() if(NOT CMAKE_ASM_NASM_LINK_EXECUTABLE)
diff --git a/Modules/CMakeASM_POASMInformation.cmake b/Modules/CMakeASM_POASMInformation.cmake new file mode 100644 index 0000000..fad25a2 --- /dev/null +++ b/Modules/CMakeASM_POASMInformation.cmake
@@ -0,0 +1,7 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +set(ASM_DIALECT "_POASM") +set(CMAKE_ASM${ASM_DIALECT}_SOURCE_FILE_EXTENSIONS asm) +include(CMakeASMInformation) +set(ASM_DIALECT)
diff --git a/Modules/CMakeAddFortranSubdirectory.cmake b/Modules/CMakeAddFortranSubdirectory.cmake index 7eec1f2..3b45f17 100644 --- a/Modules/CMakeAddFortranSubdirectory.cmake +++ b/Modules/CMakeAddFortranSubdirectory.cmake
@@ -154,7 +154,8 @@ set(_mingw_target "Target:.*mingw32") endif() execute_process(COMMAND "${MINGW_GFORTRAN}" -v - ERROR_VARIABLE out ERROR_STRIP_TRAILING_WHITESPACE) + ERROR_VARIABLE out ERROR_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_mingw_gfortran_v) if(NOT "${out}" MATCHES "${_mingw_target}") string(REPLACE "\n" "\n " out " ${out}") message(FATAL_ERROR
diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in index 96e5c60..5d60020 100644 --- a/Modules/CMakeCCompiler.cmake.in +++ b/Modules/CMakeCCompiler.cmake.in
@@ -32,9 +32,10 @@ set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") set(CMAKE_C_COMPILER_LINKER "@CMAKE_C_COMPILER_LINKER@") +set(CMAKE_C_COMPILER_LINKER_ARCHITECTURE_FLAGS "@CMAKE_C_COMPILER_LINKER_ARCHITECTURE_FLAGS@") set(CMAKE_C_COMPILER_LINKER_ID "@CMAKE_C_COMPILER_LINKER_ID@") -set(CMAKE_C_COMPILER_LINKER_VERSION @CMAKE_C_COMPILER_LINKER_VERSION@) -set(CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT@) +set(CMAKE_C_COMPILER_LINKER_VERSION "@CMAKE_C_COMPILER_LINKER_VERSION@") +set(CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT "@CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT@") set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@)
diff --git a/Modules/CMakeCCompilerId.c.in b/Modules/CMakeCCompilerId.c.in index c79362e..165376d 100644 --- a/Modules/CMakeCCompilerId.c.in +++ b/Modules/CMakeCCompilerId.c.in
@@ -44,16 +44,17 @@ #define C_STD_17 201710L #define C_STD_23 202311L -#ifdef __STDC_VERSION__ -# define C_STD __STDC_VERSION__ +#if defined(__STDC_VERSION__) +# define C_STD __STDC_VERSION__ +#elif defined(__POCC_STDC_VERSION__) +# define C_STD __POCC_STDC_VERSION__ +#elif defined(__STDC__) || defined(__clang__) || defined(_MSC_VER) || \ + defined(__ibmxl__) || defined(__IBMC__) || defined(__RENESAS__) +# define C_STD 1 #endif -#if !defined(__STDC__) && !defined(__clang__) && !defined(__RENESAS__) -# if defined(_MSC_VER) || defined(__ibmxl__) || defined(__IBMC__) -# define C_VERSION "90" -# else -# define C_VERSION -# endif +#if !defined(C_STD) +# define C_VERSION #elif C_STD > C_STD_17 # define C_VERSION "23" #elif C_STD > C_STD_11 @@ -73,6 +74,8 @@ defined(__TI_COMPILER_VERSION__) || defined(__RENESAS__)) && \ !defined(__STRICT_ANSI__) "ON" +#elif defined(__POCC__) && defined(__POCC__EXTENSIONS) + "ON" #else "OFF" #endif
diff --git a/Modules/CMakeCUDACompiler.cmake.in b/Modules/CMakeCUDACompiler.cmake.in index ea31ea0..0643e2b 100644 --- a/Modules/CMakeCUDACompiler.cmake.in +++ b/Modules/CMakeCUDACompiler.cmake.in
@@ -82,9 +82,10 @@ set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") set(CMAKE_CUDA_COMPILER_LINKER "@CMAKE_CUDA_COMPILER_LINKER@") +set(CMAKE_CUDA_COMPILER_LINKER_ARCHITECTURE_FLAGS "@CMAKE_CUDA_COMPILER_LINKER_ARCHITECTURE_FLAGS@") set(CMAKE_CUDA_COMPILER_LINKER_ID "@CMAKE_CUDA_COMPILER_LINKER_ID@") -set(CMAKE_CUDA_COMPILER_LINKER_VERSION @CMAKE_CUDA_COMPILER_LINKER_VERSION@) -set(CMAKE_CUDA_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_CUDA_COMPILER_LINKER_FRONTEND_VARIANT@) +set(CMAKE_CUDA_COMPILER_LINKER_VERSION "@CMAKE_CUDA_COMPILER_LINKER_VERSION@") +set(CMAKE_CUDA_COMPILER_LINKER_FRONTEND_VARIANT "@CMAKE_CUDA_COMPILER_LINKER_FRONTEND_VARIANT@") set(CMAKE_AR "@CMAKE_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_MT "@CMAKE_MT@")
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in index 9e1db3b..52495f4 100644 --- a/Modules/CMakeCXXCompiler.cmake.in +++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -34,9 +34,10 @@ set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") set(CMAKE_CXX_COMPILER_LINKER "@CMAKE_CXX_COMPILER_LINKER@") +set(CMAKE_CXX_COMPILER_LINKER_ARCHITECTURE_FLAGS "@CMAKE_CXX_COMPILER_LINKER_ARCHITECTURE_FLAGS@") set(CMAKE_CXX_COMPILER_LINKER_ID "@CMAKE_CXX_COMPILER_LINKER_ID@") -set(CMAKE_CXX_COMPILER_LINKER_VERSION @CMAKE_CXX_COMPILER_LINKER_VERSION@) -set(CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT@) +set(CMAKE_CXX_COMPILER_LINKER_VERSION "@CMAKE_CXX_COMPILER_LINKER_VERSION@") +set(CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT "@CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT@") set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@)
diff --git a/Modules/CMakeCXXCompilerId.cpp.in b/Modules/CMakeCXXCompilerId.cpp.in index 4d78c2a..2e08785 100644 --- a/Modules/CMakeCXXCompilerId.cpp.in +++ b/Modules/CMakeCXXCompilerId.cpp.in
@@ -63,7 +63,9 @@ # define CXX_STD __cplusplus # endif #elif defined(__NVCOMPILER) -# if __cplusplus == CXX_STD_17 && defined(__cpp_aggregate_paren_init) +# if __cplusplus > CXX_STD_20 && defined(__cpp_pp_embed) +# define CXX_STD /*CXX_STD_26*/ (CXX_STD_23 + 1) +# elif __cplusplus == CXX_STD_17 && defined(__cpp_aggregate_paren_init) # define CXX_STD CXX_STD_20 # else # define CXX_STD __cplusplus
diff --git a/Modules/CMakeCompilerABI.h b/Modules/CMakeCompilerABI.h index bd90ea2..42342c9 100644 --- a/Modules/CMakeCompilerABI.h +++ b/Modules/CMakeCompilerABI.h
@@ -114,8 +114,6 @@ # define ARCHITECTURE_ID "riscv64" #elif defined(__riscv) && __riscv_xlen == 32 # define ARCHITECTURE_ID "riscv32" -#elif defined(__sw_64) -# define ARCHITECTURE_ID "sw_64" #elif defined(__s390x__) # define ARCHITECTURE_ID "s390x" #elif defined(__s390__) @@ -124,6 +122,8 @@ # define ARCHITECTURE_ID "sparcv9" #elif defined(__sparc) || defined(__sparc__) # define ARCHITECTURE_ID "sparc" +#elif defined(__sw_64) || defined(__sw_64__) +# define ARCHITECTURE_ID "sw_64" #elif defined(__hppa) || defined(__hppa__) # if defined(__LP64__) # define ARCHITECTURE_ID "parisc64"
diff --git a/Modules/CMakeCompilerIdDetection.cmake b/Modules/CMakeCompilerIdDetection.cmake index 68182b8..a8e652c 100644 --- a/Modules/CMakeCompilerIdDetection.cmake +++ b/Modules/CMakeCompilerIdDetection.cmake
@@ -74,6 +74,7 @@ list(APPEND ordered_compilers TinyCC Bruce + PellesC ) endif() list(APPEND ordered_compilers
diff --git a/Modules/CMakeDependentOption.cmake b/Modules/CMakeDependentOption.cmake index 0d840fc..a000f4c 100644 --- a/Modules/CMakeDependentOption.cmake +++ b/Modules/CMakeDependentOption.cmake
@@ -246,8 +246,7 @@ set(${option} "${${option}_ISSET}") endif() if("x${_CDO_CMP0127}x" STREQUAL "xx" AND "x${depends}x" MATCHES "[^A-Za-z0-9_.; ]") - cmake_policy(GET_WARNING CMP0127 _CDO_CMP0127_WARNING) - message(AUTHOR_WARNING "${_CDO_CMP0127_WARNING}") + cmake_policy(ISSUE_WARNING CMP0127) endif() unset(_CDO_CMP0127) endmacro()
diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake index 46f42e0..1ca5e73 100644 --- a/Modules/CMakeDetermineASMCompiler.cmake +++ b/Modules/CMakeDetermineASMCompiler.cmake
@@ -165,6 +165,10 @@ set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_Renesas "-v") set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_Renesas "(RX Family C/C\\+\\+ Compiler)|(RL78 Family Compiler)|(RH850 Family Compiler)") + list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS PellesC) + set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_PellesC ) + set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_PellesC "Pelles Macro Assembler") + include(CMakeDetermineCompilerId) set(userflags) CMAKE_DETERMINE_COMPILER_ID_VENDOR(ASM${ASM_DIALECT} "${userflags}") @@ -230,8 +234,7 @@ endif() if("ASM${ASM_DIALECT}" STREQUAL "ASM" AND CMAKE_ASM_COMPILER_ID STREQUAL "MSVC" AND _CMAKE_ASM_CMP0194 STREQUAL "") - cmake_policy(GET_WARNING CMP0194 _CMAKE_ASM_CMP0194_WARNING) - message(AUTHOR_WARNING "${_CMAKE_ASM_CMP0194_WARNING}") + cmake_policy(ISSUE_WARNING CMP0194) endif() # If we have a gas/as cross compiler, they have usually some prefix, like @@ -263,7 +266,7 @@ endif () -set(_CMAKE_PROCESSING_LANGUAGE "ASM") +set(_CMAKE_PROCESSING_LANGUAGE "ASM${ASM_DIALECT}") include(CMakeFindBinUtils) include(Compiler/${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}-FindBinUtils OPTIONAL) unset(_CMAKE_PROCESSING_LANGUAGE)
diff --git a/Modules/CMakeDetermineASM_POASMCompiler.cmake b/Modules/CMakeDetermineASM_POASMCompiler.cmake new file mode 100644 index 0000000..a46e96f --- /dev/null +++ b/Modules/CMakeDetermineASM_POASMCompiler.cmake
@@ -0,0 +1,7 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +set(ASM_DIALECT "_POASM") +set(CMAKE_ASM${ASM_DIALECT}_COMPILER_INIT poasm) +include(CMakeDetermineASMCompiler) +unset(ASM_DIALECT)
diff --git a/Modules/CMakeDetermineCSharpCompiler.cmake b/Modules/CMakeDetermineCSharpCompiler.cmake index 0528252..7512e69 100644 --- a/Modules/CMakeDetermineCSharpCompiler.cmake +++ b/Modules/CMakeDetermineCSharpCompiler.cmake
@@ -22,7 +22,10 @@ include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake) CMAKE_DETERMINE_COMPILER_ID(CSharp CSFLAGS CMakeCSharpCompilerId.cs) - execute_process(COMMAND "${CMAKE_CSharp_COMPILER}" "/help /preferreduilang:en-US" OUTPUT_VARIABLE output) + execute_process(COMMAND "${CMAKE_CSharp_COMPILER}" + "/help /preferreduilang:en-US" + OUTPUT_VARIABLE output + RESULT_VARIABLE _result_csharp_help) string(REPLACE "\n" ";" output "${output}") foreach(line ${output}) string(TOUPPER ${line} line)
diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake index e70742b..10b7eed 100644 --- a/Modules/CMakeDetermineCUDACompiler.cmake +++ b/Modules/CMakeDetermineCUDACompiler.cmake
@@ -249,6 +249,8 @@ set(_CUDA_LIBRARY_DIR "${_CUDA_TARGET_DIR}/lib64") elseif(EXISTS "${_CUDA_TARGET_DIR}/lib/x64") set(_CUDA_LIBRARY_DIR "${_CUDA_TARGET_DIR}/lib/x64") + elseif(EXISTS "${_CUDA_TARGET_DIR}/lib/arm64") + set(_CUDA_LIBRARY_DIR "${_CUDA_TARGET_DIR}/lib/arm64") elseif(EXISTS "${_CUDA_TARGET_DIR}/lib") set(_CUDA_LIBRARY_DIR "${_CUDA_TARGET_DIR}/lib") else()
diff --git a/Modules/CMakeDetermineCompiler.cmake b/Modules/CMakeDetermineCompiler.cmake index 097916e..af82bbc 100644 --- a/Modules/CMakeDetermineCompiler.cmake +++ b/Modules/CMakeDetermineCompiler.cmake
@@ -57,7 +57,7 @@ NO_DEFAULT_PATH DOC "${lang} compiler") endif() - if(CMAKE_HOST_WIN32 AND CMAKE_GENERATOR MATCHES "Ninja|MSYS Makefiles|MinGW Makefiles") + if(CMAKE_HOST_WIN32 AND CMAKE_GENERATOR MATCHES "Ninja|MSYS Makefiles|MinGW Makefiles|FASTBuild") # On Windows command-line builds, some generators imply a preferred compiler tool. # These generators do not, so use the compiler that occurs first in PATH. find_program(CMAKE_${lang}_COMPILER @@ -112,7 +112,7 @@ # if compiler (and arguments) comes from cache then synchronize cache with updated CMAKE_<LANG>_COMPILER get_filename_component(_CMAKE_USER_${lang}_COMPILER_PATH "${CMAKE_${lang}_COMPILER}" PATH) if(NOT _CMAKE_USER_${lang}_COMPILER_PATH) - find_program(CMAKE_${lang}_COMPILER_WITH_PATH NAMES ${CMAKE_${lang}_COMPILER}) + find_program(CMAKE_${lang}_COMPILER_WITH_PATH NO_CACHE NAMES ${CMAKE_${lang}_COMPILER}) if(CMAKE_${lang}_COMPILER_WITH_PATH) set(CMAKE_${lang}_COMPILER ${CMAKE_${lang}_COMPILER_WITH_PATH}) get_property(_CMAKE_${lang}_COMPILER_CACHED CACHE CMAKE_${lang}_COMPILER PROPERTY TYPE) @@ -121,7 +121,6 @@ endif() unset(_CMAKE_${lang}_COMPILER_CACHED) endif() - unset(CMAKE_${lang}_COMPILER_WITH_PATH CACHE) elseif (EXISTS ${CMAKE_${lang}_COMPILER}) get_property(_CMAKE_${lang}_COMPILER_CACHED CACHE CMAKE_${lang}_COMPILER PROPERTY TYPE) if(_CMAKE_${lang}_COMPILER_CACHED)
diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake index 2d89f9d..230eec8 100644 --- a/Modules/CMakeDetermineCompilerABI.cmake +++ b/Modules/CMakeDetermineCompilerABI.cmake
@@ -13,6 +13,46 @@ include(CMakeTestCompilerCommon) function(CMAKE_DETERMINE_COMPILER_ABI lang src) + set(abi_variables + CMAKE_${lang}_COMPILER_WORKS + CMAKE_${lang}_COMPILER_ARCHITECTURE_ID + CMAKE_${lang}_SIZEOF_DATA_PTR + CMAKE_${lang}_BYTE_ORDER + CMAKE_${lang}_COMPILER_ABI + CMAKE_${lang}_STANDARD_INCLUDE_DIRECTORIES + CMAKE_${lang}_COMPILER_LINKER + CMAKE_${lang}_IMPLICIT_LINK_LIBRARIES + CMAKE_${lang}_IMPLICIT_LINK_DIRECTORIES + CMAKE_${lang}_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES + CMAKE_${lang}_LIBRARY_ARCHITECTURE) + if (CMAKE_${lang}_COMPILER_LINKER) + list(APPEND abi_variables + CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS + CMAKE_${lang}_COMPILER_LINKER_ID + CMAKE_${lang}_COMPILER_LINKER_VERSION + CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT) + endif () + if (lang STREQUAL "Fortran") + list(APPEND abi_variables + CMAKE_${lang}_COMPILER_SUPPORTS_F90) + endif () + if (lang MATCHES "^(CUDA|HIP)$") + list(APPEND abi_variables + CMAKE_${lang}_ARCHITECTURES + CMAKE_${lang}_HOST_COMPILER_ID + CMAKE_${lang}_HOST_COMPILER_VERSION + CMAKE_${lang}_RUNTIME_LIBRARY) + endif () + set(all_abi_variables_defined 1) + foreach (abi_variable IN LISTS abi_variables) + if (NOT DEFINED "${abi_variable}") + set(all_abi_variables_defined 0) + break () + endif () + endforeach () + if (all_abi_variables_defined AND CMAKE_${lang}_COMPILER_WORKS) + set(CMAKE_${lang}_ABI_COMPILED 1) + endif () if(NOT DEFINED CMAKE_${lang}_ABI_COMPILED) message(CHECK_START "Detecting ${lang} compiler ABI info") @@ -86,6 +126,7 @@ # Ignore unused flags when we are just determining the ABI. "--no-warn-unused-cli" COMPILE_DEFINITIONS ${COMPILE_DEFINITIONS} + NO_CACHE OUTPUT_VARIABLE OUTPUT COPY_FILE "${BIN}" COPY_FILE_ERROR _copy_error @@ -97,9 +138,6 @@ set(ENV{LC_MESSAGES} ${_orig_lc_messages}) set(ENV{LANG} ${_orig_lang}) - # Move result from cache to normal variable. - set(CMAKE_${lang}_ABI_COMPILED ${CMAKE_${lang}_ABI_COMPILED}) - unset(CMAKE_${lang}_ABI_COMPILED CACHE) if(CMAKE_${lang}_ABI_COMPILED AND _copy_error) set(CMAKE_${lang}_ABI_COMPILED 0) endif() @@ -297,6 +335,11 @@ set(CMAKE_${lang}_COMPILER_LINKER "${linker_tool}" PARENT_SCOPE) cmake_determine_linker_id(${lang} "${linker_tool}") + if(CMAKE_${lang}_COMPILER_LINKER_ID MATCHES "GNU|LLD|MOLD|WILD") + set(CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS "${linker_tool_ARCH_FLAGS}" PARENT_SCOPE) + else() + set(CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS "" PARENT_SCOPE) + endif() set(CMAKE_${lang}_COMPILER_LINKER_ID "${CMAKE_${lang}_COMPILER_LINKER_ID}" PARENT_SCOPE) set(CMAKE_${lang}_COMPILER_LINKER_VERSION ${CMAKE_${lang}_COMPILER_LINKER_VERSION} PARENT_SCOPE) set(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT ${CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT} PARENT_SCOPE)
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index 0518128..f6fe7f5 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -72,6 +72,17 @@ endforeach() endif() + # Check if there is any vendor specific LLVMFlang + if(lang STREQUAL "Fortran" AND CMAKE_${lang}_COMPILER_ID STREQUAL "LLVMFlang") + set(_orig_compiler_id "${CMAKE_${lang}_COMPILER_ID}") + foreach(userflags "${CMAKE_${lang}_COMPILER_ID_FLAGS_LIST}" "") + CMAKE_DETERMINE_COMPILER_ID_VENDOR(${lang} "${userflags}") + if(NOT CMAKE_${lang}_COMPILER_ID STREQUAL _orig_compiler_id) + break() + endif() + endforeach() + endif() + # Check if compiler id detection gave us the compiler tool. if(CMAKE_${lang}_COMPILER_ID_TOOL) set(CMAKE_${lang}_COMPILER "${CMAKE_${lang}_COMPILER_ID_TOOL}") @@ -237,6 +248,22 @@ endif() endif() + # The IBM LLVM Flang compiler version is extracted from --version output + if(CMAKE_${lang}_COMPILER_ID STREQUAL "IBMFlang") + execute_process( + COMMAND "${CMAKE_${lang}_COMPILER}" --version + OUTPUT_VARIABLE output + ERROR_VARIABLE output + RESULT_VARIABLE result + TIMEOUT 10 + ) + if(result EQUAL 0) + if(output MATCHES [[version ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)]]) + set(CMAKE_${lang}_COMPILER_VERSION "${CMAKE_MATCH_1}") + endif() + endif() + endif() + # if the format is unknown after all files have been checked, put "Unknown" in the cache if(NOT CMAKE_EXECUTABLE_FORMAT) set(CMAKE_EXECUTABLE_FORMAT "Unknown" CACHE INTERNAL "Executable file format") @@ -324,6 +351,12 @@ else() set(CMAKE_${lang}_COMPILER_FRONTEND_VARIANT "GNU") endif() + elseif("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xIntel") + if(CMAKE_HOST_WIN32) + set(CMAKE_${lang}_COMPILER_FRONTEND_VARIANT "MSVC") + else() + set(CMAKE_${lang}_COMPILER_FRONTEND_VARIANT "GNU") + endif() elseif("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xGNU" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xAppleClang" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xFujitsuClang" @@ -465,7 +498,7 @@ #----------------------------------------------------------------------------- # Function to write the compiler id source file. function(CMAKE_DETERMINE_COMPILER_ID_WRITE lang src) - find_file(src_in ${src}.in PATHS ${CMAKE_ROOT}/Modules ${CMAKE_MODULE_PATH} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + find_file(src_in ${src}.in NO_CACHE PATHS ${CMAKE_ROOT}/Modules ${CMAKE_MODULE_PATH} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) file(READ ${src_in} ID_CONTENT_IN) compiler_id_detection(CMAKE_${lang}_COMPILER_ID_CONTENT ${lang} @@ -490,7 +523,6 @@ ) endif() - unset(src_in CACHE) string(CONFIGURE "${ID_CONTENT_IN}" ID_CONTENT_OUT @ONLY) file(WRITE ${CMAKE_${lang}_COMPILER_ID_DIR}/${src} "${ID_CONTENT_OUT}") endfunction()
diff --git a/Modules/CMakeDetermineCompilerSupport.cmake b/Modules/CMakeDetermineCompilerSupport.cmake index 512a1c4..ed80ba7 100644 --- a/Modules/CMakeDetermineCompilerSupport.cmake +++ b/Modules/CMakeDetermineCompilerSupport.cmake
@@ -5,6 +5,27 @@ function(cmake_determine_compiler_support lang) if("x${lang}" STREQUAL "xC" AND COMMAND cmake_record_c_compile_features) + set(compile_feature_vars + CMAKE_C_COMPILE_FEATURES + CMAKE_C90_COMPILE_FEATURES + CMAKE_C99_COMPILE_FEATURES + CMAKE_C11_COMPILE_FEATURES + CMAKE_C17_COMPILE_FEATURES + CMAKE_C23_COMPILE_FEATURES + ) + set(all_defined 1) + set(any_non_empty 0) + foreach (var IN LISTS compile_feature_vars) + if (NOT DEFINED "${var}") + set(all_defined 0) + elseif (${var}) + set(any_non_empty 1) + endif () + endforeach () + if (all_defined AND any_non_empty) + return () + endif () + message(CHECK_START "Detecting ${lang} compile features") set(CMAKE_C90_COMPILE_FEATURES) @@ -55,55 +76,81 @@ message(CHECK_PASS "done") elseif("x${lang}" STREQUAL "xCXX" AND COMMAND cmake_record_cxx_compile_features) - message(CHECK_START "Detecting ${lang} compile features") + set(compile_feature_vars + CMAKE_CXX_COMPILE_FEATURES + CMAKE_CXX98_COMPILE_FEATURES + CMAKE_CXX11_COMPILE_FEATURES + CMAKE_CXX14_COMPILE_FEATURES + CMAKE_CXX17_COMPILE_FEATURES + CMAKE_CXX20_COMPILE_FEATURES + CMAKE_CXX23_COMPILE_FEATURES + CMAKE_CXX26_COMPILE_FEATURES + ) + set(all_defined 1) + set(any_non_empty 0) + foreach (var IN LISTS compile_feature_vars) + if (NOT DEFINED "${var}") + set(all_defined 0) + elseif (${var}) + set(any_non_empty 1) + endif () + endforeach () + set(need_feature_detection 1) + if (all_defined AND any_non_empty) + set(need_feature_detection 0) + endif () - set(CMAKE_CXX98_COMPILE_FEATURES) - set(CMAKE_CXX11_COMPILE_FEATURES) - set(CMAKE_CXX14_COMPILE_FEATURES) - set(CMAKE_CXX17_COMPILE_FEATURES) - set(CMAKE_CXX20_COMPILE_FEATURES) - set(CMAKE_CXX23_COMPILE_FEATURES) - set(CMAKE_CXX26_COMPILE_FEATURES) + if (need_feature_detection) + message(CHECK_START "Detecting ${lang} compile features") - include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") + set(CMAKE_CXX98_COMPILE_FEATURES) + set(CMAKE_CXX11_COMPILE_FEATURES) + set(CMAKE_CXX14_COMPILE_FEATURES) + set(CMAKE_CXX17_COMPILE_FEATURES) + set(CMAKE_CXX20_COMPILE_FEATURES) + set(CMAKE_CXX23_COMPILE_FEATURES) + set(CMAKE_CXX26_COMPILE_FEATURES) - cmake_record_cxx_compile_features() + include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") - if(NOT _result EQUAL 0) - message(CHECK_FAIL "failed") - return() - endif() + cmake_record_cxx_compile_features() - if (CMAKE_CXX23_COMPILE_FEATURES AND CMAKE_CXX26_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES}) - endif() - if (CMAKE_CXX20_COMPILE_FEATURES AND CMAKE_CXX23_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES}) - endif() - if (CMAKE_CXX17_COMPILE_FEATURES AND CMAKE_CXX20_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CXX20_COMPILE_FEATURES ${CMAKE_CXX17_COMPILE_FEATURES}) - endif() - if (CMAKE_CXX14_COMPILE_FEATURES AND CMAKE_CXX17_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CXX17_COMPILE_FEATURES ${CMAKE_CXX14_COMPILE_FEATURES}) - endif() - if (CMAKE_CXX11_COMPILE_FEATURES AND CMAKE_CXX14_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CXX14_COMPILE_FEATURES ${CMAKE_CXX11_COMPILE_FEATURES}) - endif() - if (CMAKE_CXX98_COMPILE_FEATURES AND CMAKE_CXX11_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CXX11_COMPILE_FEATURES ${CMAKE_CXX98_COMPILE_FEATURES}) - endif() + if(NOT _result EQUAL 0) + message(CHECK_FAIL "failed") + return() + endif() - if(NOT CMAKE_CXX_COMPILE_FEATURES) - set(CMAKE_CXX_COMPILE_FEATURES - ${CMAKE_CXX98_COMPILE_FEATURES} - ${CMAKE_CXX11_COMPILE_FEATURES} - ${CMAKE_CXX14_COMPILE_FEATURES} - ${CMAKE_CXX17_COMPILE_FEATURES} - ${CMAKE_CXX20_COMPILE_FEATURES} - ${CMAKE_CXX23_COMPILE_FEATURES} - ${CMAKE_CXX26_COMPILE_FEATURES} - ) - endif() + if (CMAKE_CXX23_COMPILE_FEATURES AND CMAKE_CXX26_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES}) + endif() + if (CMAKE_CXX20_COMPILE_FEATURES AND CMAKE_CXX23_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES}) + endif() + if (CMAKE_CXX17_COMPILE_FEATURES AND CMAKE_CXX20_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CXX20_COMPILE_FEATURES ${CMAKE_CXX17_COMPILE_FEATURES}) + endif() + if (CMAKE_CXX14_COMPILE_FEATURES AND CMAKE_CXX17_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CXX17_COMPILE_FEATURES ${CMAKE_CXX14_COMPILE_FEATURES}) + endif() + if (CMAKE_CXX11_COMPILE_FEATURES AND CMAKE_CXX14_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CXX14_COMPILE_FEATURES ${CMAKE_CXX11_COMPILE_FEATURES}) + endif() + if (CMAKE_CXX98_COMPILE_FEATURES AND CMAKE_CXX11_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CXX11_COMPILE_FEATURES ${CMAKE_CXX98_COMPILE_FEATURES}) + endif() + + if(NOT CMAKE_CXX_COMPILE_FEATURES) + set(CMAKE_CXX_COMPILE_FEATURES + ${CMAKE_CXX98_COMPILE_FEATURES} + ${CMAKE_CXX11_COMPILE_FEATURES} + ${CMAKE_CXX14_COMPILE_FEATURES} + ${CMAKE_CXX17_COMPILE_FEATURES} + ${CMAKE_CXX20_COMPILE_FEATURES} + ${CMAKE_CXX23_COMPILE_FEATURES} + ${CMAKE_CXX26_COMPILE_FEATURES} + ) + endif() + endif () # Find the module metadata for import std set(CMAKE_CXX_COMPILER_IMPORT_STD "") @@ -111,7 +158,7 @@ foreach(_cmake_import_std_version IN ITEMS 23 26) if(CMAKE_CXX${_cmake_import_std_version}_COMPILE_FEATURES) # Modules JSON covers all versions, otherwise rely on toolchain targets - if(CMAKE_CXX_STDLIB_MODULES_JSON OR (TARGET "__CMAKE:CXX${_cmake_import_std_version}")) + if(CMAKE_CXX_STDLIB_MODULES_JSON) list(APPEND CMAKE_CXX_COMPILER_IMPORT_STD ${_cmake_import_std_version}) endif() endif() @@ -121,16 +168,18 @@ set(CMAKE_CXX_STDLIB_MODULES_JSON ${CMAKE_CXX_STDLIB_MODULES_JSON} PARENT_SCOPE) set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "${CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE}" PARENT_SCOPE) - set(CMAKE_CXX_COMPILE_FEATURES ${CMAKE_CXX_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CXX98_COMPILE_FEATURES ${CMAKE_CXX98_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CXX11_COMPILE_FEATURES ${CMAKE_CXX11_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CXX14_COMPILE_FEATURES ${CMAKE_CXX14_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CXX17_COMPILE_FEATURES ${CMAKE_CXX17_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CXX20_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX26_COMPILE_FEATURES} PARENT_SCOPE) + if (need_feature_detection) + set(CMAKE_CXX_COMPILE_FEATURES ${CMAKE_CXX_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CXX98_COMPILE_FEATURES ${CMAKE_CXX98_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CXX11_COMPILE_FEATURES ${CMAKE_CXX11_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CXX14_COMPILE_FEATURES ${CMAKE_CXX14_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CXX17_COMPILE_FEATURES ${CMAKE_CXX17_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CXX20_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX26_COMPILE_FEATURES} PARENT_SCOPE) - message(CHECK_PASS "done") + message(CHECK_PASS "done") + endif () elseif("x${lang}" STREQUAL "xCUDA" AND COMMAND cmake_record_cuda_compile_features) message(CHECK_START "Detecting ${lang} compile features")
diff --git a/Modules/CMakeDetermineFortranCompiler.cmake b/Modules/CMakeDetermineFortranCompiler.cmake index dc39577..67d8451 100644 --- a/Modules/CMakeDetermineFortranCompiler.cmake +++ b/Modules/CMakeDetermineFortranCompiler.cmake
@@ -134,6 +134,9 @@ list(APPEND CMAKE_Fortran_COMPILER_ID_VENDORS NAG) # Numerical Algorithms Group set(CMAKE_Fortran_COMPILER_ID_VENDOR_FLAGS_NAG "-V") set(CMAKE_Fortran_COMPILER_ID_VENDOR_REGEX_NAG "NAG Fortran Compiler") + list(APPEND CMAKE_Fortran_COMPILER_ID_VENDORS IBMFlang) + set(CMAKE_Fortran_COMPILER_ID_VENDOR_FLAGS_IBMFlang "--version") + set(CMAKE_Fortran_COMPILER_ID_VENDOR_REGEX_IBMFlang "IBM Open XL Fortran") # Match the link line from xcodebuild output of the form # Ld ...
diff --git a/Modules/CMakeDetermineOBJCCompiler.cmake b/Modules/CMakeDetermineOBJCCompiler.cmake index f8d334a..8aeadc1 100644 --- a/Modules/CMakeDetermineOBJCCompiler.cmake +++ b/Modules/CMakeDetermineOBJCCompiler.cmake
@@ -76,11 +76,10 @@ # if the C compiler already had a path, reuse it for searching the CXX compiler get_filename_component(_CMAKE_USER_OBJC_COMPILER_PATH "${CMAKE_OBJC_COMPILER}" PATH) if(NOT _CMAKE_USER_OBJC_COMPILER_PATH) - find_program(CMAKE_OBJC_COMPILER_WITH_PATH NAMES ${CMAKE_OBJC_COMPILER}) + find_program(CMAKE_OBJC_COMPILER_WITH_PATH NO_CACHE NAMES ${CMAKE_OBJC_COMPILER}) if(CMAKE_OBJC_COMPILER_WITH_PATH) set(CMAKE_OBJC_COMPILER ${CMAKE_OBJC_COMPILER_WITH_PATH} CACHE STRING "Objective-C compiler" FORCE) endif() - unset(CMAKE_OBJC_COMPILER_WITH_PATH CACHE) endif() endif() mark_as_advanced(CMAKE_OBJC_COMPILER)
diff --git a/Modules/CMakeDetermineOBJCXXCompiler.cmake b/Modules/CMakeDetermineOBJCXXCompiler.cmake index 0c03eea..b7df4e8 100644 --- a/Modules/CMakeDetermineOBJCXXCompiler.cmake +++ b/Modules/CMakeDetermineOBJCXXCompiler.cmake
@@ -78,11 +78,10 @@ # if the C compiler already had a path, reuse it for searching the CXX compiler get_filename_component(_CMAKE_USER_OBJCXX_COMPILER_PATH "${CMAKE_OBJCXX_COMPILER}" PATH) if(NOT _CMAKE_USER_OBJCXX_COMPILER_PATH) - find_program(CMAKE_OBJCXX_COMPILER_WITH_PATH NAMES ${CMAKE_OBJCXX_COMPILER}) + find_program(CMAKE_OBJCXX_COMPILER_WITH_PATH NO_CACHE NAMES ${CMAKE_OBJCXX_COMPILER}) if(CMAKE_OBJCXX_COMPILER_WITH_PATH) set(CMAKE_OBJCXX_COMPILER ${CMAKE_OBJCXX_COMPILER_WITH_PATH} CACHE STRING "Objective-C++ compiler" FORCE) endif() - unset(CMAKE_OBJCXX_COMPILER_WITH_PATH CACHE) endif() endif()
diff --git a/Modules/CMakeDetermineRustCompiler.cmake b/Modules/CMakeDetermineRustCompiler.cmake index 43441b0..4272e1b 100644 --- a/Modules/CMakeDetermineRustCompiler.cmake +++ b/Modules/CMakeDetermineRustCompiler.cmake
@@ -3,7 +3,7 @@ include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) -if(NOT "${CMAKE_GENERATOR}" MATCHES "^Ninja") +if(NOT "${CMAKE_GENERATOR}" MATCHES "^(Ninja)|(Unix Makefiles)") message(FATAL_ERROR "Rust language not supported by \"${CMAKE_GENERATOR}\" generator") endif() @@ -30,9 +30,10 @@ COMMAND ${RUSTUP_PATH} which rustc OUTPUT_VARIABLE REAL_RUSTC OUTPUT_STRIP_TRAILING_WHITESPACE - ) + RESULT_VARIABLE _rust_result + ) - if("${REAL_RUSTC}" STREQUAL "") + if(NOT _rust_result EQUAL 0 OR "${REAL_RUSTC}" STREQUAL "") message(FATAL_ERROR "Failed to find path to real rustc") endif()
diff --git a/Modules/CMakeDetermineSwiftCompiler.cmake b/Modules/CMakeDetermineSwiftCompiler.cmake index e9285b6..b8f69ee 100644 --- a/Modules/CMakeDetermineSwiftCompiler.cmake +++ b/Modules/CMakeDetermineSwiftCompiler.cmake
@@ -99,7 +99,8 @@ # Execute in dry-run mode so no compilation will be actually performed. execute_process(COMMAND ${_CMAKE_Swift_COMPILER_CHECK_COMMAND} WORKING_DIRECTORY "${CMAKE_Swift_COMPILER_DRIVER_TEST_DIR}" - OUTPUT_VARIABLE _CMAKE_Swift_COMPILER_CHECK_OUTPUT) + OUTPUT_VARIABLE _CMAKE_Swift_COMPILER_CHECK_OUTPUT + RESULT_VARIABLE _result_CMAKE_Swift_COMPILER) # Check the first frontend execution. It is on the first line of output. # The old driver treats all inputs as Swift sources while the new driver @@ -133,10 +134,15 @@ endif() execute_process( COMMAND ${target_info_command} - OUTPUT_VARIABLE swift_target_info) + OUTPUT_VARIABLE swift_target_info + RESULT_VARIABLE _result_swift_target_info) message(CONFIGURE_LOG "Swift target info:\n" "${swift_target_info}") string(JSON module_triple GET "${swift_target_info}" "target" "moduleTriple") set(CMAKE_Swift_MODULE_TRIPLE ${module_triple}) + + if(CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 6.2) + string(JSON CMAKE_Swift_SIZEOF_DATA_PTR GET "${swift_target_info}" "target" "pointerWidthInBytes") + endif() endif() if (NOT _CMAKE_TOOLCHAIN_LOCATION)
diff --git a/Modules/CMakeDetermineSystem.cmake b/Modules/CMakeDetermineSystem.cmake index dc26258..c70fea1 100644 --- a/Modules/CMakeDetermineSystem.cmake +++ b/Modules/CMakeDetermineSystem.cmake
@@ -15,19 +15,24 @@ execute_process(COMMAND ${CMAKE_UNAME} -v OUTPUT_VARIABLE _CMAKE_HOST_SYSTEM_MAJOR_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET) + ERROR_QUIET + RESULT_VARIABLE _uname_result) execute_process(COMMAND ${CMAKE_UNAME} -r OUTPUT_VARIABLE _CMAKE_HOST_SYSTEM_MINOR_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET) - set(CMAKE_HOST_SYSTEM_VERSION "${_CMAKE_HOST_SYSTEM_MAJOR_VERSION}.${_CMAKE_HOST_SYSTEM_MINOR_VERSION}") + ERROR_QUIET + RESULT_VARIABLE _uname_result2) + if(_uname_result EQUAL 0 AND _uname_result2 EQUAL 0) + set(CMAKE_HOST_SYSTEM_VERSION "${_CMAKE_HOST_SYSTEM_MAJOR_VERSION}.${_CMAKE_HOST_SYSTEM_MINOR_VERSION}") + endif() unset(_CMAKE_HOST_SYSTEM_MAJOR_VERSION) unset(_CMAKE_HOST_SYSTEM_MINOR_VERSION) elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Android") execute_process(COMMAND getprop ro.build.version.sdk OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET) + ERROR_QUIET + RESULT_VARIABLE _uname_result) if(NOT DEFINED CMAKE_SYSTEM_VERSION) set(_ANDROID_API_LEVEL_H $ENV{PREFIX}/include/android/api-level.h) @@ -49,7 +54,8 @@ execute_process(COMMAND ${CMAKE_UNAME} -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET) + ERROR_QUIET + RESULT_VARIABLE _uname_result) endif() if(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|CYGWIN.*|MSYS.*|^GNU$|Android") execute_process(COMMAND ${CMAKE_UNAME} -m
diff --git a/Modules/CMakeDetermineVSServicePack.cmake b/Modules/CMakeDetermineVSServicePack.cmake index c9a9fb9..4f14874 100644 --- a/Modules/CMakeDetermineVSServicePack.cmake +++ b/Modules/CMakeDetermineVSServicePack.cmake
@@ -121,6 +121,7 @@ COMMAND ${CMAKE_CXX_COMPILER} -? ERROR_VARIABLE _output OUTPUT_QUIET + RESULT_VARIABLE _result_cl_help ) if(_output MATCHES "Compiler Version (([0-9]+)\\.([0-9]+)\\.([0-9]+)(\\.([0-9]+))?)")
diff --git a/Modules/CMakeExtraGeneratorDetermineCompilerMacrosAndIncludeDirs.cmake b/Modules/CMakeExtraGeneratorDetermineCompilerMacrosAndIncludeDirs.cmake index 0b21e36..b1e65c9 100644 --- a/Modules/CMakeExtraGeneratorDetermineCompilerMacrosAndIncludeDirs.cmake +++ b/Modules/CMakeExtraGeneratorDetermineCompilerMacrosAndIncludeDirs.cmake
@@ -31,7 +31,8 @@ execute_process(COMMAND ${_compilerExecutable} ${_arg1} ${_stdver} ${_stdlib} -v -E -x ${_lang} -dD dummy WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/CMakeFiles ERROR_VARIABLE _gccOutput - OUTPUT_VARIABLE _gccStdout ) + OUTPUT_VARIABLE _gccStdout + RESULT_VARIABLE _result_gcc) file(REMOVE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy") # First find the system include dirs:
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake index 91543a6..3e77903 100644 --- a/Modules/CMakeFindBinUtils.cmake +++ b/Modules/CMakeFindBinUtils.cmake
@@ -34,7 +34,7 @@ if(NOT _CMAKE_USER_TOOL_PATH) # Find CMAKE_TOOL in the SEARCH_PATH directory by user-defined name. - find_program(_CMAKE_TOOL_WITH_PATH NAMES ${${CMAKE_TOOL}} HINTS ${SEARCH_PATH} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) + find_program(_CMAKE_TOOL_WITH_PATH NO_CACHE NAMES ${${CMAKE_TOOL}} HINTS ${SEARCH_PATH} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) if(_CMAKE_TOOL_WITH_PATH) # Overwrite CMAKE_TOOL with full path found in SEARCH_PATH. @@ -47,7 +47,6 @@ endif() endif() - unset(_CMAKE_TOOL_WITH_PATH CACHE) endif() @@ -108,6 +107,11 @@ set(_CMAKE_AR_NAMES "wlib") list(APPEND _CMAKE_TOOL_VARS LINKER AR) +elseif("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xPellesC") + set(_CMAKE_LINKER_NAMES "polink") + set(_CMAKE_AR_NAMES "polib") + list(APPEND _CMAKE_TOOL_VARS LINKER AR) + elseif("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" MATCHES "^xIAR$") # Detect the `<lang>` compiler name get_filename_component(__iar_selected_compiler "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" NAME)
diff --git a/Modules/CMakeFindJavaCommon.cmake b/Modules/CMakeFindJavaCommon.cmake index 95ca9b5..f3e3493 100644 --- a/Modules/CMakeFindJavaCommon.cmake +++ b/Modules/CMakeFindJavaCommon.cmake
@@ -22,6 +22,7 @@ OUTPUT_VARIABLE _CMD_JAVA_HOME OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET + RESULT_VARIABLE _result_java_home ) endif() if(_CMD_JAVA_HOME AND IS_DIRECTORY "${_CMD_JAVA_HOME}")
diff --git a/Modules/CMakeFindPackageMode.cmake b/Modules/CMakeFindPackageMode.cmake index 6e2762c..62e33e0 100644 --- a/Modules/CMakeFindPackageMode.cmake +++ b/Modules/CMakeFindPackageMode.cmake
@@ -71,7 +71,9 @@ find_program(FILE_EXECUTABLE file) if(FILE_EXECUTABLE) get_filename_component(FILE_ABSPATH "${FILE_EXECUTABLE}" ABSOLUTE) - execute_process(COMMAND "${FILE_ABSPATH}" "${FILE_ABSPATH}" OUTPUT_VARIABLE fileOutput ERROR_QUIET) + execute_process(COMMAND "${FILE_ABSPATH}" "${FILE_ABSPATH}" + OUTPUT_VARIABLE fileOutput ERROR_QUIET + RESULT_VARIABLE _result_file) if("${fileOutput}" MATCHES "64-bit") set(CMAKE_SIZEOF_VOID_P 8) endif()
diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in index bfcf3f4..ab6c260 100644 --- a/Modules/CMakeFortranCompiler.cmake.in +++ b/Modules/CMakeFortranCompiler.cmake.in
@@ -16,9 +16,10 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") set(CMAKE_Fortran_COMPILER_LINKER "@CMAKE_Fortran_COMPILER_LINKER@") +set(CMAKE_Fortran_COMPILER_LINKER_ARCHITECTURE_FLAGS "@CMAKE_Fortran_COMPILER_LINKER_ARCHITECTURE_FLAGS@") set(CMAKE_Fortran_COMPILER_LINKER_ID "@CMAKE_Fortran_COMPILER_LINKER_ID@") -set(CMAKE_Fortran_COMPILER_LINKER_VERSION @CMAKE_Fortran_COMPILER_LINKER_VERSION@) -set(CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT@) +set(CMAKE_Fortran_COMPILER_LINKER_VERSION "@CMAKE_Fortran_COMPILER_LINKER_VERSION@") +set(CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT "@CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT@") set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@)
diff --git a/Modules/CMakeFortranCompilerABI.F b/Modules/CMakeFortranCompilerABI.F index ed82730..d97ba2a 100644 --- a/Modules/CMakeFortranCompilerABI.F +++ b/Modules/CMakeFortranCompilerABI.F
@@ -10,7 +10,10 @@ PRINT *, 'INFO:sizeof_dptr[8]' #elif defined(_M_AMD64) PRINT *, 'INFO:sizeof_dptr[8]' -#elif defined(__x86_64__) +#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || \ + defined(__amd64__) + PRINT *, 'INFO:sizeof_dptr[8]' +#elif defined(__ppc64__) || defined(__powerpc64__) || defined(__PPC64__) PRINT *, 'INFO:sizeof_dptr[8]' #elif defined(__sparcv9) || defined(__sparcv9__) || defined(__sparc64__) PRINT *, 'INFO:sizeof_dptr[8]' @@ -19,7 +22,9 @@ PRINT *, 'INFO:sizeof_dptr[4]' #elif defined(_M_IX86) PRINT *, 'INFO:sizeof_dptr[4]' -#elif defined(__i386__) +#elif defined(__i386) || defined(__i386__) + PRINT *, 'INFO:sizeof_dptr[4]' +#elif defined(__ppc__) || defined(__powerpc__) || defined(__PPC__) PRINT *, 'INFO:sizeof_dptr[4]' #elif defined(__sparc) || defined(__sparc__) PRINT *, 'INFO:sizeof_dptr[4]' @@ -113,8 +118,6 @@ PRINT *, 'INFO:arch[riscv64]' #elif defined(__riscv) && __riscv_xlen == 32 PRINT *, 'INFO:arch[riscv32]' -#elif defined(__sw_64) || defined(__sw_64__) - PRINT *, 'INFO:arch[sw_64]' #elif defined(__s390x__) PRINT *, 'INFO:arch[s390x]' #elif defined(__s390__) @@ -123,6 +126,8 @@ PRINT *, 'INFO:arch[sparcv9]' #elif defined(__sparc) || defined(__sparc__) PRINT *, 'INFO:arch[sparc]' +#elif defined(__sw_64) || defined(__sw_64__) + PRINT *, 'INFO:arch[sw_64]' #elif defined(__hppa) || defined(__hppa__) # if defined(__LP64__) PRINT *, 'INFO:arch[parisc64]'
diff --git a/Modules/CMakeFortranCompilerABI.F90 b/Modules/CMakeFortranCompilerABI.F90 index 1dfd560..24725d8 100644 --- a/Modules/CMakeFortranCompilerABI.F90 +++ b/Modules/CMakeFortranCompilerABI.F90
@@ -18,7 +18,10 @@ PRINT *, 'INFO:sizeof_dptr[8]' #elif defined(_M_AMD64) PRINT *, 'INFO:sizeof_dptr[8]' -#elif defined(__x86_64__) +#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || \ + defined(__amd64__) +PRINT *, 'INFO:sizeof_dptr[8]' +#elif defined(__ppc64__) || defined(__powerpc64__) || defined(__PPC64__) PRINT *, 'INFO:sizeof_dptr[8]' #elif defined(__sparcv9) || defined(__sparcv9__) || defined(__sparc64__) PRINT *, 'INFO:sizeof_dptr[8]' @@ -27,7 +30,9 @@ PRINT *, 'INFO:sizeof_dptr[4]' #elif defined(_M_IX86) PRINT *, 'INFO:sizeof_dptr[4]' -#elif defined(__i386__) +#elif defined(__i386) || defined(__i386__) +PRINT *, 'INFO:sizeof_dptr[4]' +#elif defined(__ppc__) || defined(__powerpc__) || defined(__PPC__) PRINT *, 'INFO:sizeof_dptr[4]' #elif defined(__sparc) || defined(__sparc__) PRINT *, 'INFO:sizeof_dptr[4]' @@ -105,8 +110,6 @@ PRINT *, 'INFO:arch[loongarch32]' #elif defined(__m68k__) PRINT *, 'INFO:arch[m68k]' -#elif defined(__sw_64) -PRINT *, 'INFO:arch[sw_64]' #elif defined(__mips64) || defined(__mips64__) # if defined(_MIPSEL) PRINT *, 'INFO:arch[mips64el]' @@ -131,6 +134,8 @@ PRINT *, 'INFO:arch[sparcv9]' #elif defined(__sparc) || defined(__sparc__) PRINT *, 'INFO:arch[sparc]' +#elif defined(__sw_64) || defined(__sw_64__) +PRINT *, 'INFO:arch[sw_64]' #elif defined(__hppa) || defined(__hppa__) # if defined(__LP64__) PRINT *, 'INFO:arch[parisc64]'
diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in index 2c762b8..3244eef 100644 --- a/Modules/CMakeHIPCompiler.cmake.in +++ b/Modules/CMakeHIPCompiler.cmake.in
@@ -84,8 +84,9 @@ set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") set(CMAKE_HIP_COMPILER_LINKER "@CMAKE_HIP_COMPILER_LINKER@") +set(CMAKE_HIP_COMPILER_LINKER_ARCHITECTURE_FLAGS "@CMAKE_HIP_COMPILER_LINKER_ARCHITECTURE_FLAGS@") set(CMAKE_HIP_COMPILER_LINKER_ID "@CMAKE_HIP_COMPILER_LINKER_ID@") -set(CMAKE_HIP_COMPILER_LINKER_VERSION @CMAKE_HIP_COMPILER_LINKER_VERSION@) -set(CMAKE_HIP_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_HIP_COMPILER_LINKER_FRONTEND_VARIANT@) +set(CMAKE_HIP_COMPILER_LINKER_VERSION "@CMAKE_HIP_COMPILER_LINKER_VERSION@") +set(CMAKE_HIP_COMPILER_LINKER_FRONTEND_VARIANT "@CMAKE_HIP_COMPILER_LINKER_FRONTEND_VARIANT@") set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@")
diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in index 2eb5a55..a6aca6f 100644 --- a/Modules/CMakeOBJCCompiler.cmake.in +++ b/Modules/CMakeOBJCCompiler.cmake.in
@@ -29,9 +29,10 @@ set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") set(CMAKE_OBJC_COMPILER_LINKER "@CMAKE_OBJC_COMPILER_LINKER@") +set(CMAKE_OBJC_COMPILER_LINKER_ARCHITECTURE_FLAGS "@CMAKE_OBJC_COMPILER_LINKER_ARCHITECTURE_FLAGS@") set(CMAKE_OBJC_COMPILER_LINKER_ID "@CMAKE_OBJC_COMPILER_LINKER_ID@") -set(CMAKE_OBJC_COMPILER_LINKER_VERSION @CMAKE_OBJC_COMPILER_LINKER_VERSION@) -set(CMAKE_OBJC_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_OBJC_COMPILER_LINKER_FRONTEND_VARIANT@) +set(CMAKE_OBJC_COMPILER_LINKER_VERSION "@CMAKE_OBJC_COMPILER_LINKER_VERSION@") +set(CMAKE_OBJC_COMPILER_LINKER_FRONTEND_VARIANT "@CMAKE_OBJC_COMPILER_LINKER_FRONTEND_VARIANT@") set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@)
diff --git a/Modules/CMakeOBJCCompilerId.m.in b/Modules/CMakeOBJCCompilerId.m.in index 3c8a82a..5292ea2 100644 --- a/Modules/CMakeOBJCCompilerId.m.in +++ b/Modules/CMakeOBJCCompilerId.m.in
@@ -25,16 +25,14 @@ #define C_STD_17 201710L #define C_STD_23 202311L -#ifdef __STDC_VERSION__ +#if defined(__STDC_VERSION__) # define C_STD __STDC_VERSION__ +#elif defined(__STDC__) || defined(__ibmxl__) || defined(__IBMC__) +# define C_STD 1 #endif -#if !defined(__STDC__) -# if defined(__ibmxl__) || defined(__IBMC__) -# define C_VERSION "90" -# else -# define C_VERSION -# endif +#if !defined(C_STD) +# define C_VERSION #elif C_STD > C_STD_17 # define C_VERSION "23" #elif C_STD > C_STD_11
diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in index 94fcee3..e81dca1 100644 --- a/Modules/CMakeOBJCXXCompiler.cmake.in +++ b/Modules/CMakeOBJCXXCompiler.cmake.in
@@ -30,9 +30,10 @@ set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") set(CMAKE_OBJCXX_COMPILER_LINKER "@CMAKE_OBJCXX_COMPILER_LINKER@") +set(CMAKE_OBJCXX_COMPILER_LINKER_ARCHITECTURE_FLAGS "@CMAKE_OBJCXX_COMPILER_LINKER_ARCHITECTURE_FLAGS@") set(CMAKE_OBJCXX_COMPILER_LINKER_ID "@CMAKE_OBJCXX_COMPILER_LINKER_ID@") -set(CMAKE_OBJCXX_COMPILER_LINKER_VERSION @CMAKE_OBJCXX_COMPILER_LINKER_VERSION@) -set(CMAKE_OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT@) +set(CMAKE_OBJCXX_COMPILER_LINKER_VERSION "@CMAKE_OBJCXX_COMPILER_LINKER_VERSION@") +set(CMAKE_OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT "@CMAKE_OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT@") set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@)
diff --git a/Modules/CMakePackageConfigHelpers.cmake b/Modules/CMakePackageConfigHelpers.cmake index c253296..1bb2b9f 100644 --- a/Modules/CMakePackageConfigHelpers.cmake +++ b/Modules/CMakePackageConfigHelpers.cmake
@@ -147,7 +147,9 @@ write_basic_package_version_file(<filename> [VERSION <major.minor.patch>] - COMPATIBILITY <AnyNewerVersion|SameMajorVersion|SameMinorVersion|ExactVersion> + COMPATIBILITY <AnyNewerVersion|SameMajorVersion|SameMinorVersion| + SamePatchVersion|SameFullVersion|SemanticVersion| + ExactVersion> [ARCH_INDEPENDENT] ) @@ -173,11 +175,25 @@ If ``SameMinorVersion`` is used, the behavior is the same as ``SameMajorVersion``, but both major and minor version must be the same as requested, e.g version 0.2 will not be compatible if 0.1 is requested. -If ``ExactVersion`` is used, then the package is only considered compatible if -the requested version matches exactly its own version number (not considering -the tweak version). For example, version 1.2.3 of a package is only -considered compatible to requested version 1.2.3. This mode is for packages -without compatibility guarantees. +If ``SamePatchVersion`` is used, the package is only considered compatible if +the requested version matches exactly through the patch version, ignoring any +tweak version. For example, version 1.2.3.4 of a package is considered +compatible with requested version 1.2.3.5, but not with requested version +1.2.4. +If ``SameFullVersion`` is used, the package is only considered compatible if +the requested version matches exactly through all specified version components. +For example, version 1.2.3.4 of a package is only considered compatible with +requested version 1.2.3.4. Missing version components are not treated as +implicit zeroes, so version 1.2.3 is not considered compatible with requested +version 1.2.3.0. +If ``SemanticVersion`` is used, the behavior follows the usual semantic +versioning interpretation: when the installed package version has a major +component greater than zero, the behavior is the same as +``SameMajorVersion``. When the installed package version has a major +component zero, the behavior is the same as ``SameMinorVersion``. +If ``ExactVersion`` is used, the behavior is the same as +``SamePatchVersion``. + If your project has more elaborate version matching rules, you will need to write your own custom ``<PackageName>ConfigVersion.cmake`` file instead of using this macro. @@ -205,6 +221,20 @@ ``ExactVersion`` mode is incompatible with version ranges and will display an author warning if one is specified. +.. versionadded:: 4.4 + The ``SamePatchVersion``, ``SameFullVersion``, and ``SemanticVersion`` + compatibility modes. + + The version file generated by ``SemanticVersion`` handles version ranges if + one is specified (see :command:`find_package` command for the details). + ``SamePatchVersion`` and ``SameFullVersion`` modes are incompatible with + version ranges and will display an author warning if one is specified. + +.. deprecated:: 4.4 + The ``ExactVersion`` compatibility mode. Use ``SamePatchVersion`` or + ``SameFullVersion`` instead, depending on whether the tweak version should + be ignored. + Internally, this macro executes :command:`configure_file()` to create the resulting version file. Depending on the ``COMPATIBILITY``, the corresponding ``BasicConfigVersion-<COMPATIBILITY>.cmake.in`` file is used.
diff --git a/Modules/CMakeParseImplicitLinkInfo.cmake b/Modules/CMakeParseImplicitLinkInfo.cmake index 1b16a68..4e00970 100644 --- a/Modules/CMakeParseImplicitLinkInfo.cmake +++ b/Modules/CMakeParseImplicitLinkInfo.cmake
@@ -46,12 +46,18 @@ cmake_parse_arguments(EXTRA_PARSE "${keywordArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(is_lfortran_less_0_40 0) + set(is_clang 0) + set(is_gnu 0) set(is_msvc 0) set(is_cray 0) if(EXTRA_PARSE_LANGUAGE) if("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xMSVC" OR "x${CMAKE_${EXTRA_PARSE_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC") set(is_msvc 1) + elseif("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xGNU") + set(is_gnu 1) + elseif("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xClang") + set(is_clang 1) elseif("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xLFortran" AND CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_VERSION VERSION_LESS "0.40") set(is_lfortran_less_0_40 1) @@ -65,6 +71,9 @@ # lfortran < 0.40 has no way to pass -v to clang/gcc driver. string(APPEND linker "|clang|gcc") endif() + if(is_clang OR is_gnu) + string(APPEND linker "|wild") + endif() if(is_msvc) string(APPEND linker "|link\\.exe|lld-link(\\.exe)?") endif() @@ -99,6 +108,7 @@ string(APPEND linker_tool_exclude_regex "|^clang |^gcc ") endif() set(linker_tool "NOTFOUND") + set(linker_tool_arch) set(linker_tool_fallback "") set(link_line_parsed 0) string(APPEND log " link line regex: [${linker_regex}]\n") @@ -111,18 +121,31 @@ NOT linker_tool AND NOT "${line}" MATCHES "${linker_tool_exclude_regex}") if("${line}" MATCHES "exec: ([^()]*/(${linker}))") # IBM XL as nvcc host compiler set(linker_tool "${CMAKE_MATCH_1}") + string(APPEND log " link line: [${line}] ==> linker [${linker_tool}]\n") elseif("${line}" MATCHES "^export XL_LINKER=(.*/${linker})[ \t]*$") # IBM XL set(linker_tool "${CMAKE_MATCH_1}") + string(APPEND log " link line: [${line}] ==> linker [${linker_tool}]\n") elseif("${line}" MATCHES "--with-ld=") # GNU # The GNU compiler reports how it was configured. # This does not account for -fuse-ld= so use it only as a fallback. if("${line}" MATCHES " --with-ld=([^ ]+/${linker})( |$)") set(linker_tool_fallback "${CMAKE_MATCH_1}") + string(APPEND log " link line: [${line}] ==> linker (fallback) [${linker_tool_fallback}]\n") endif() - elseif("${line}" MATCHES "vs_link.*-- +([^\"]*[/\\](${linker})) ") # cmake -E vs_link_exe + elseif("${line}" MATCHES "vs_link.*-- +\"?([^\"]*[/\\](${linker}))\"? ") # cmake -E vs_link_exe set(linker_tool "${CMAKE_MATCH_1}") + string(APPEND log " link line: [${line}] ==> linker [${linker_tool}]\n") elseif("${line}" MATCHES "${linker_tool_regex}") set(linker_tool "${CMAKE_MATCH_2}") + string(APPEND log " link line: [${line}] ==> linker [${linker_tool}]\n") + endif() + endif() + if(linker_tool) + # pick-up useful flags influencing linker behavior + # these flags are meaningful only for LLVM and GNU linkers. Check will be done later + if("${line}" MATCHES "-m ([a-zA-Z0-9]+)") + list(APPEND linker_tool_arch -m "${CMAKE_MATCH_1}") + string(APPEND log " link line: [${line}] ==> linker architecture flags [${linker_tool_arch}]\n") endif() endif() if(NOT (EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS OR EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS @@ -296,7 +319,7 @@ if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") cmake_path(NORMAL_PATH linker_tool) endif() - string(APPEND log " linker tool for '${EXTRA_PARSE_LANGUAGE}': ${linker_tool}\n") + string(APPEND log " linker tool for '${EXTRA_PARSE_LANGUAGE}': ${linker_tool} ${linker_tool_arch}\n") endif() # Look for library search paths reported by linker. @@ -392,6 +415,7 @@ # Return results. if(EXTRA_PARSE_COMPUTE_LINKER) set(${EXTRA_PARSE_COMPUTE_LINKER} "${linker_tool}" PARENT_SCOPE) + set(${EXTRA_PARSE_COMPUTE_LINKER}_ARCH_FLAGS "${linker_tool_arch}" PARENT_SCOPE) endif() set(${log_var} "${log}" PARENT_SCOPE)
diff --git a/Modules/CMakeRCInformation.cmake b/Modules/CMakeRCInformation.cmake index cd991a0..b0a724f 100644 --- a/Modules/CMakeRCInformation.cmake +++ b/Modules/CMakeRCInformation.cmake
@@ -39,7 +39,9 @@ # now define the following rule variables # CMAKE_RC_COMPILE_OBJECT -set(CMAKE_INCLUDE_FLAG_RC "-I ") +if(NOT CMAKE_INCLUDE_FLAG_RC) + set(CMAKE_INCLUDE_FLAG_RC "-I ") +endif() # compile a Resource file into an object file if(NOT CMAKE_RC_COMPILE_OBJECT) set(CMAKE_RC_COMPILE_OBJECT
diff --git a/Modules/CMakeRustCompiler.cmake.in b/Modules/CMakeRustCompiler.cmake.in index deb19ba..5da83e4 100644 --- a/Modules/CMakeRustCompiler.cmake.in +++ b/Modules/CMakeRustCompiler.cmake.in
@@ -3,3 +3,8 @@ set(CMAKE_Rust_SOURCE_FILE_EXTENSIONS rs) set(CMAKE_Rust_COMPILER_LOADED 1) set(CMAKE_Rust_COMPILER_WORKS @CMAKE_Rust_COMPILER_WORKS@) + +# Make sure CMake prefers to link with Rust when Rust source files are present +set(CMAKE_Rust_LINKER_PREFERENCE 60) +# Once compiled by Rust, static libraries can be linked by anyone. +set(CMAKE_Rust_LINKER_PREFERENCE_PROPAGATES 0)
diff --git a/Modules/CMakeRustInformation.cmake b/Modules/CMakeRustInformation.cmake index 2224d01..e7d41fd 100644 --- a/Modules/CMakeRustInformation.cmake +++ b/Modules/CMakeRustInformation.cmake
@@ -3,11 +3,20 @@ include(CMakeLanguageInformation) -if(UNIX) - set(CMAKE_Rust_OUTPUT_EXTENSION .o) -else() - set(CMAKE_Rust_OUTPUT_EXTENSION .obj) -endif() +set(CMAKE_Rust_OUTPUT_EXTENSION .rlib) + +# Other values are supported to generate various outputs (LLVM bitcode, or IR, +# crate metadata, Rust MIR). However, CMake cannot do anything with those +# outputs, so we list output which can be reused in later stages of the build. +# See: https://doc.rust-lang.org/rustc/command-line-arguments.html#--emit-specifies-the-types-of-output-files-to-generate +set(CMAKE_Rust_EMIT_VALUES link obj asm) + +# The output extension for each supported emit value. +set(CMAKE_Rust_EMIT_link_OUTPUT_EXTENSION .rlib) +set(CMAKE_Rust_EMIT_asm_OUTPUT_EXTENSION .s) +# Might be switched to .obj on Windows when using MSVC target triple, see: +# https://github.com/rust-lang/rust/issues/37207 +set(CMAKE_Rust_EMIT_obj_OUTPUT_EXTENSION .o) set(CMAKE_Rust_LIBRARY_PATH_FLAG "-L ") set(CMAKE_Rust_LINK_LIBRARY_FILE_FLAG "-C link-arg=") @@ -19,23 +28,62 @@ set(CMAKE_Rust_FLAGS_RELWITHDEBINFO_INIT "-O -g") set(CMAKE_Rust_FLAGS_MINSIZEREL_INIT "-C opt-level=z") +block( + PROPAGATE + CMAKE_Rust_LINK_PIE_SUPPORTED + CMAKE_Rust_LINK_NO_PIE_SUPPORTED + CMAKE_Rust_COMPILE_OPTIONS_PIE + CMAKE_Rust_COMPILE_OPTIONS_PIC + CMAKE_Rust_LINK_OPTIONS_PIE + CMAKE_Rust_LINK_OPTIONS_NO_PIE + ) + execute_process( + COMMAND "${CMAKE_Rust_COMPILER}" --print relocation-models + OUTPUT_VARIABLE RUSTC_OUTPUT + ERROR_VARIABLE RUSTC_ERROR + RESULT_VARIABLE RUSTC_EXITCODE + ) + if(RUSTC_EXITCODE EQUAL "0") + string(REPLACE "\n" ";" RUSTC_OUTPUT_LINES "${RUSTC_OUTPUT}") + list(TRANSFORM RUSTC_OUTPUT_LINES STRIP) + if("pic" IN_LIST RUSTC_OUTPUT_LINES) + set(CMAKE_Rust_COMPILE_OPTIONS_PIC -C relocation-model=pic) + endif() + if("pie" IN_LIST RUSTC_OUTPUT_LINES) + set(CMAKE_Rust_LINK_PIE_SUPPORTED TRUE) + set(CMAKE_Rust_COMPILE_OPTIONS_PIE -C relocation-model=pie) + set(CMAKE_Rust_LINK_OPTIONS_PIE -C relocation-model=pie) + else() + set(CMAKE_Rust_LINK_PIE_SUPPORTED FALSE) + endif() + if("static" IN_LIST RUSTC_OUTPUT_LINES) + set(CMAKE_Rust_LINK_NO_PIE_SUPPORTED TRUE) + set(CMAKE_Rust_LINK_OPTIONS_NO_PIE -C relocation-model=static) + else() + set(CMAKE_Rust_LINK_NO_PIE_SUPPORTED FALSE) + endif() + else() + string(REPLACE "\n" "\n " RUSTC_ERROR " ${RUSTC_ERROR}") + message(FATAL_ERROR "Failed to check PIC/PIE support in rustc:\n${RUSTC_ERROR}") + endif() +endblock() + cmake_initialize_per_config_variable(CMAKE_Rust_FLAGS "Flags used by the Rust compiler") +if(NOT CMAKE_Rust_COMPILE_OBJECT) + set(CMAKE_Rust_COMPILE_OBJECT "<CMAKE_Rust_COMPILER> --crate-type=rlib <FLAGS> --emit=<RUST_EMIT>,dep-info=<DEP_FILE> -o <OBJECT> <SOURCE>") +endif() + if(NOT CMAKE_Rust_CREATE_STATIC_LIBRARY) - set(CMAKE_Rust_CREATE_STATIC_LIBRARY "${CMAKE_Rust_COMPILER} <LANGUAGE_COMPILE_FLAGS> --crate-type=staticlib <RUST_SOURCES> -o <TARGET> -C link-args=\"<RUST_OBJECT_DEPS>\"") + set(CMAKE_Rust_CREATE_STATIC_LIBRARY "${CMAKE_Rust_COMPILER} <LANGUAGE_COMPILE_FLAGS> --crate-type=staticlib --emit=link,dep-info=<DEP_FILE> <RUST_MAIN_CRATE_ROOT> -o <TARGET> <RUST_LINK_CRATES> <RUST_NATIVE_OBJECTS>") endif() if(NOT CMAKE_Rust_CREATE_SHARED_LIBRARY) - set(CMAKE_Rust_CREATE_SHARED_LIBRARY "${CMAKE_Rust_COMPILER} <LANGUAGE_COMPILE_FLAGS> --crate-type=cdylib <RUST_SOURCES> -o <TARGET> <LINK_FLAGS> <LINK_LIBRARIES> -C link-args=\"<RUST_OBJECT_DEPS>\"") -endif() - -# Deadcode warnings are not useful when generating object files. -if(NOT CMAKE_Rust_COMPILE_OBJECT) - set(CMAKE_Rust_COMPILE_OBJECT "${CMAKE_Rust_COMPILER} <FLAGS> -A dead_code --crate-type=lib --emit=obj=<OBJECT>,dep-info=<DEP_FILE> <SOURCE>") + set(CMAKE_Rust_CREATE_SHARED_LIBRARY "${CMAKE_Rust_COMPILER} <LANGUAGE_COMPILE_FLAGS> --crate-type=cdylib --emit=link,dep-info=<DEP_FILE> <RUST_MAIN_CRATE_ROOT> -o <TARGET> <RUST_LINK_CRATES> <RUST_NATIVE_OBJECTS> <LINK_FLAGS> <LINK_LIBRARIES>") endif() if(NOT CMAKE_Rust_LINK_EXECUTABLE) - set(CMAKE_Rust_LINK_EXECUTABLE "${CMAKE_Rust_COMPILER} <FLAGS> --crate-type=bin <RUST_SOURCES> -o <TARGET> <LINK_FLAGS> <LINK_LIBRARIES> -C link-args=\"<RUST_OBJECT_DEPS>\"") + set(CMAKE_Rust_LINK_EXECUTABLE "${CMAKE_Rust_COMPILER} <FLAGS> --crate-type=bin --emit=link,dep-info=<DEP_FILE> <RUST_MAIN_CRATE_ROOT> -o <TARGET> <RUST_LINK_CRATES> <RUST_NATIVE_OBJECTS> <LINK_FLAGS> <LINK_LIBRARIES>") endif() set(CMAKE_Rust_INFORMATION_LOADED 1)
diff --git a/Modules/CMakeSwiftCompiler.cmake.in b/Modules/CMakeSwiftCompiler.cmake.in index 5e2483a..27139d6 100644 --- a/Modules/CMakeSwiftCompiler.cmake.in +++ b/Modules/CMakeSwiftCompiler.cmake.in
@@ -18,3 +18,8 @@ set(CMAKE_Swift_IMPLICIT_INCLUDE_DIRECTORIES "@CMAKE_Swift_IMPLICIT_INCLUDE_DIRECTORIES@") set(CMAKE_Swift_MODULE_TRIPLE "@CMAKE_Swift_MODULE_TRIPLE@") +set(CMAKE_Swift_SIZEOF_DATA_PTR "@CMAKE_Swift_SIZEOF_DATA_PTR@") + +if(NOT CMAKE_SIZEOF_VOID_P AND CMAKE_Swift_SIZEOF_DATA_PTR) + set(CMAKE_SIZEOF_VOID_P "${CMAKE_Swift_SIZEOF_DATA_PTR}") +endif()
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index bafc6ce..a7b053c 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake
@@ -59,6 +59,7 @@ set(CMAKE_Swift_RESPONSE_FILE_FLAG @) set(CMAKE_Swift_COMPILE_OPTIONS_COLOR_DIAGNOSTICS -color-diagnostics) set(CMAKE_Swift_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF -no-color-diagnostics) +set(CMAKE_Swift_PACKAGE_NAME_FLAG -package-name) set(CMAKE_Swift_LINKER_PREFERENCE 50) set(CMAKE_Swift_LINKER_PREFERENCE_PROPAGATES 1) @@ -108,6 +109,47 @@ endif() unset(__SWIFT_COMP_MODE_CMP0157) +# Separate Swift module emission (see policy CMP0215) is available only in the +# split build model selected by policy CMP0157, and it writes the module into +# the Swift module directory structure selected by policy CMP0195. When +# CMP0215 is NEW, require both to also be NEW. This is a Ninja-only feature, +# so only enforce the requirement for Ninja generators. +if(CMAKE_GENERATOR MATCHES "Ninja") + cmake_policy(GET CMP0215 __SWIFT_SEPARATE_MODULE_CMP0215) + if(__SWIFT_SEPARATE_MODULE_CMP0215 STREQUAL "NEW") + set(__SWIFT_SEPARATE_MODULE_UNMET "") + cmake_policy(GET CMP0157 __SWIFT_SEPARATE_MODULE_CMP0157) + if(NOT __SWIFT_SEPARATE_MODULE_CMP0157 STREQUAL "NEW") + list(APPEND __SWIFT_SEPARATE_MODULE_UNMET CMP0157) + endif() + cmake_policy(GET CMP0195 __SWIFT_SEPARATE_MODULE_CMP0195) + if(NOT __SWIFT_SEPARATE_MODULE_CMP0195 STREQUAL "NEW") + list(APPEND __SWIFT_SEPARATE_MODULE_UNMET CMP0195) + endif() + if(__SWIFT_SEPARATE_MODULE_UNMET) + list(LENGTH __SWIFT_SEPARATE_MODULE_UNMET __SWIFT_SEPARATE_MODULE_UNMET_COUNT) + list(JOIN __SWIFT_SEPARATE_MODULE_UNMET " and " __SWIFT_SEPARATE_MODULE_UNMET_STR) + if(__SWIFT_SEPARATE_MODULE_UNMET_COUNT GREATER 1) + set(__SWIFT_SEPARATE_MODULE_VERB "are") + else() + set(__SWIFT_SEPARATE_MODULE_VERB "is") + endif() + message(FATAL_ERROR + "Policy CMP0215 is set to 'NEW', which requires policies CMP0157 and " + "CMP0195 to be set to 'NEW', but ${__SWIFT_SEPARATE_MODULE_UNMET_STR} " + "${__SWIFT_SEPARATE_MODULE_VERB} not. Note that CMP0157 must be set " + "before the Swift language is enabled.") + endif() + unset(__SWIFT_SEPARATE_MODULE_CMP0157) + unset(__SWIFT_SEPARATE_MODULE_CMP0195) + unset(__SWIFT_SEPARATE_MODULE_UNMET) + unset(__SWIFT_SEPARATE_MODULE_UNMET_COUNT) + unset(__SWIFT_SEPARATE_MODULE_UNMET_STR) + unset(__SWIFT_SEPARATE_MODULE_VERB) + endif() + unset(__SWIFT_SEPARATE_MODULE_CMP0215) +endif() + cmake_initialize_per_config_variable(CMAKE_Swift_FLAGS "Swift Compiler Flags") if(NOT CMAKE_Swift_NUM_THREADS MATCHES "^[0-9]+$") @@ -124,8 +166,12 @@ set(CMAKE_Swift_COMPILE_OBJECT "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -c <DEFINES> <FLAGS> <INCLUDES> <SOURCE>") endif() + if(NOT CMAKE_Swift_EMIT_MODULE) + set(CMAKE_Swift_EMIT_MODULE "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-module <DEFINES> <FLAGS> <INCLUDES> <SOURCE>") + endif() + if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY) - set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-library <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>") + set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-library <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> -o <TARGET> ${CMAKE_Swift_PDB_LINKER_FLAGS} <OBJECTS> <LINK_LIBRARIES>") endif() if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) @@ -133,7 +179,7 @@ endif() if(NOT CMAKE_Swift_LINK_EXECUTABLE) - set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-executable -o <TARGET> <FLAGS> <OBJECTS> <LINK_FLAGS> <LINK_LIBRARIES>") + set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-executable -o <TARGET> <FLAGS> <OBJECTS> <LINK_FLAGS> ${CMAKE_Swift_PDB_LINKER_FLAGS} <LINK_LIBRARIES>") endif() if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY) @@ -149,7 +195,7 @@ endif() if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY) - set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>") + set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} ${CMAKE_Swift_PDB_LINKER_FLAGS} <LINK_LIBRARIES>") endif() if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) @@ -157,7 +203,7 @@ endif() if(NOT CMAKE_Swift_LINK_EXECUTABLE) - set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>") + set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> ${CMAKE_Swift_PDB_LINKER_FLAGS} <LINK_LIBRARIES>") endif() if(NOT CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS)
diff --git a/Modules/CMakeTestASM_POASMCompiler.cmake b/Modules/CMakeTestASM_POASMCompiler.cmake new file mode 100644 index 0000000..c4e0e3d --- /dev/null +++ b/Modules/CMakeTestASM_POASMCompiler.cmake
@@ -0,0 +1,12 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +# This file is used by EnableLanguage in cmGlobalGenerator to +# determine that the selected ASM_POASM "compiler" (should be poasm) +# works. For assembler this can only check whether the compiler has been found, +# because otherwise there would have to be a separate assembler source file +# for each assembler on every architecture. + +set(ASM_DIALECT "_POASM") +include(CMakeTestASMCompiler) +set(ASM_DIALECT)
diff --git a/Modules/CMakeTestCCompiler.cmake b/Modules/CMakeTestCCompiler.cmake index d32fb6c..62225c6 100644 --- a/Modules/CMakeTestCCompiler.cmake +++ b/Modules/CMakeTestCCompiler.cmake
@@ -55,11 +55,9 @@ # Puts test result in cache variable. try_compile(CMAKE_C_COMPILER_WORKS SOURCE_FROM_VAR testCCompiler.c __TestCompiler_testCCompilerSource + NO_CACHE OUTPUT_VARIABLE __CMAKE_C_COMPILER_OUTPUT) unset(__TestCompiler_testCCompilerSource) - # Move result from cache to normal variable. - set(CMAKE_C_COMPILER_WORKS ${CMAKE_C_COMPILER_WORKS}) - unset(CMAKE_C_COMPILER_WORKS CACHE) __TestCompiler_restoreTryCompileTargetType() if(NOT CMAKE_C_COMPILER_WORKS) PrintTestCompilerResult(CHECK_FAIL "broken")
diff --git a/Modules/CMakeTestCSharpCompiler.cmake b/Modules/CMakeTestCSharpCompiler.cmake index d34ae92..30b0faf 100644 --- a/Modules/CMakeTestCSharpCompiler.cmake +++ b/Modules/CMakeTestCSharpCompiler.cmake
@@ -33,12 +33,10 @@ # Puts test result in cache variable. try_compile(CMAKE_CSharp_COMPILER_WORKS SOURCE_FROM_VAR testCSharpCompiler.cs __TestCompiler_testCSharpCompilerSource + NO_CACHE OUTPUT_VARIABLE __CMAKE_CSharp_COMPILER_OUTPUT ) unset(__TestCompiler_testCSharpCompilerSource) - # Move result from cache to normal variable. - set(CMAKE_CSharp_COMPILER_WORKS ${CMAKE_CSharp_COMPILER_WORKS}) - unset(CMAKE_CSharp_COMPILER_WORKS CACHE) set(CSharp_TEST_WAS_RUN 1) endif()
diff --git a/Modules/CMakeTestCUDACompiler.cmake b/Modules/CMakeTestCUDACompiler.cmake index fbeadf7..91bada0 100644 --- a/Modules/CMakeTestCUDACompiler.cmake +++ b/Modules/CMakeTestCUDACompiler.cmake
@@ -14,6 +14,19 @@ # We now store this in CMakeCUDACompiler.cmake. unset(CMAKE_CUDA_COMPILER_WORKS CACHE) +# If the compiler was not identified, required compiler-specific variables +# such as _CMAKE_CUDA_WHOLE_FLAG will not be set. Issue a clear diagnostic +# rather than the cryptic "required internal CMake variable not set" message +# that the generator would otherwise emit. +if(NOT CMAKE_CUDA_COMPILER_ID) + PrintTestCompilerStatus("CUDA") + PrintTestCompilerResult(CHECK_FAIL "broken") + message(FATAL_ERROR "The CUDA compiler\n \"${CMAKE_CUDA_COMPILER}\"\n" + "is not able to compile a simple test program.\nThe compiler could not " + "be identified as a supported CUDA compiler.\n\n" + "CMake will not be able to correctly generate this project.") +endif() + # Try to identify the ABI and configure it into CMakeCUDACompiler.cmake include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerABI.cmake) CMAKE_DETERMINE_COMPILER_ABI(CUDA ${CMAKE_ROOT}/Modules/CMakeCUDACompilerABI.cu) @@ -47,12 +60,10 @@ # Puts test result in cache variable. try_compile(CMAKE_CUDA_COMPILER_WORKS SOURCE_FROM_VAR main.cu __TestCompiler_testCudaCompilerSource + NO_CACHE OUTPUT_VARIABLE __CMAKE_CUDA_COMPILER_OUTPUT) unset(__TestCompiler_testCudaCompilerSource) - # Move result from cache to normal variable. - set(CMAKE_CUDA_COMPILER_WORKS ${CMAKE_CUDA_COMPILER_WORKS}) - unset(CMAKE_CUDA_COMPILER_WORKS CACHE) if(NOT CMAKE_CUDA_COMPILER_WORKS) PrintTestCompilerResult(CHECK_FAIL "broken") string(REPLACE "\n" "\n " _output "${__CMAKE_CUDA_COMPILER_OUTPUT}")
diff --git a/Modules/CMakeTestCXXCompiler.cmake b/Modules/CMakeTestCXXCompiler.cmake index 5c35426..c005011 100644 --- a/Modules/CMakeTestCXXCompiler.cmake +++ b/Modules/CMakeTestCXXCompiler.cmake
@@ -55,6 +55,7 @@ # Puts test result in cache variable. try_compile(CMAKE_CXX_COMPILER_WORKS SOURCE_FROM_VAR testCXXCompiler.cxx __TestCompiler_testCXXCompilerSource + NO_CACHE OUTPUT_VARIABLE __CMAKE_CXX_COMPILER_OUTPUT) if(DEFINED __CMAKE_SAVED_CXX_SCAN_FOR_MODULES) set(CMAKE_CXX_SCAN_FOR_MODULES "${__CMAKE_SAVED_CXX_SCAN_FOR_MODULES}") @@ -63,9 +64,6 @@ unset(CMAKE_CXX_SCAN_FOR_MODULES) endif() unset(__TestCompiler_testCXXCompilerSource) - # Move result from cache to normal variable. - set(CMAKE_CXX_COMPILER_WORKS ${CMAKE_CXX_COMPILER_WORKS}) - unset(CMAKE_CXX_COMPILER_WORKS CACHE) __TestCompiler_restoreTryCompileTargetType() if(NOT CMAKE_CXX_COMPILER_WORKS) PrintTestCompilerResult(CHECK_FAIL "broken")
diff --git a/Modules/CMakeTestFortranCompiler.cmake b/Modules/CMakeTestFortranCompiler.cmake index bf4100e..a9f68b1 100644 --- a/Modules/CMakeTestFortranCompiler.cmake +++ b/Modules/CMakeTestFortranCompiler.cmake
@@ -21,7 +21,9 @@ if(CMAKE_Fortran_ABI_COMPILED) # The compiler worked so skip dedicated test below. set(CMAKE_Fortran_COMPILER_WORKS TRUE) - set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 1) + if (NOT DEFINED CMAKE_Fortran_COMPILER_SUPPORTS_F90) + set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 1) + endif () message(STATUS "Check for working Fortran compiler: ${CMAKE_Fortran_COMPILER} - skipped") else() cmake_determine_compiler_abi(Fortran ${CMAKE_ROOT}/Modules/CMakeFortranCompilerABI.F) @@ -48,11 +50,9 @@ # Puts test result in cache variable. try_compile(CMAKE_Fortran_COMPILER_WORKS SOURCE_FROM_VAR testFortranCompiler.f __TestCompiler_testFortranCompilerSource + NO_CACHE OUTPUT_VARIABLE OUTPUT) unset(__TestCompiler_testFortranCompilerSource) - # Move result from cache to normal variable. - set(CMAKE_Fortran_COMPILER_WORKS ${CMAKE_Fortran_COMPILER_WORKS}) - unset(CMAKE_Fortran_COMPILER_WORKS CACHE) if(NOT CMAKE_Fortran_COMPILER_WORKS) PrintTestCompilerResult(CHECK_FAIL "broken") string(REPLACE "\n" "\n " _output "${OUTPUT}") @@ -67,7 +67,7 @@ # Test for Fortran 90 support by using an f90-specific construct. if(NOT DEFINED CMAKE_Fortran_COMPILER_SUPPORTS_F90) message(CHECK_START "Checking whether ${CMAKE_Fortran_COMPILER} supports Fortran 90") - set(__TestCompiler_testFortranCompilerSource " + set(__TestCompiler_testFortranCompilerF90Source " PROGRAM TESTFortran90 integer stop ; stop = 1 ; do while ( stop .eq. 0 ) ; end do END PROGRAM TESTFortran90
diff --git a/Modules/CMakeTestHIPCompiler.cmake b/Modules/CMakeTestHIPCompiler.cmake index ed1df10..57994b9 100644 --- a/Modules/CMakeTestHIPCompiler.cmake +++ b/Modules/CMakeTestHIPCompiler.cmake
@@ -61,11 +61,9 @@ # Puts test result in cache variable. try_compile(CMAKE_HIP_COMPILER_WORKS SOURCE_FROM_VAR testHIPCompiler.hip __TestCompiler_testHIPCompilerSource + NO_CACHE OUTPUT_VARIABLE __CMAKE_HIP_COMPILER_OUTPUT) unset(__TestCompiler_testHIPCompilerSource) - # Move result from cache to normal variable. - set(CMAKE_HIP_COMPILER_WORKS ${CMAKE_HIP_COMPILER_WORKS}) - unset(CMAKE_HIP_COMPILER_WORKS CACHE) __TestCompiler_restoreTryCompileTargetType() if(NOT CMAKE_HIP_COMPILER_WORKS) PrintTestCompilerResult(CHECK_FAIL "broken")
diff --git a/Modules/CMakeTestOBJCCompiler.cmake b/Modules/CMakeTestOBJCCompiler.cmake index 8003415..056c4ee 100644 --- a/Modules/CMakeTestOBJCCompiler.cmake +++ b/Modules/CMakeTestOBJCCompiler.cmake
@@ -52,11 +52,9 @@ # Puts test result in cache variable. try_compile(CMAKE_OBJC_COMPILER_WORKS SOURCE_FROM_VAR testObjCCompiler.m __TestCompiler_testObjCCompilerSource + NO_CACHE OUTPUT_VARIABLE __CMAKE_OBJC_COMPILER_OUTPUT) unset(__TestCompiler_testObjCCompilerSource) - # Move result from cache to normal variable. - set(CMAKE_OBJC_COMPILER_WORKS ${CMAKE_OBJC_COMPILER_WORKS}) - unset(CMAKE_OBJC_COMPILER_WORKS CACHE) __TestCompiler_restoreTryCompileTargetType() if(NOT CMAKE_OBJC_COMPILER_WORKS) PrintTestCompilerResult(CHECK_FAIL "broken")
diff --git a/Modules/CMakeTestOBJCXXCompiler.cmake b/Modules/CMakeTestOBJCXXCompiler.cmake index 1b4bf60..dbdd2ba 100644 --- a/Modules/CMakeTestOBJCXXCompiler.cmake +++ b/Modules/CMakeTestOBJCXXCompiler.cmake
@@ -51,11 +51,9 @@ # Puts test result in cache variable. try_compile(CMAKE_OBJCXX_COMPILER_WORKS SOURCE_FROM_VAR testObjCXXCompiler.mm __TestCompiler_testObjCXXCompilerSource + NO_CACHE OUTPUT_VARIABLE __CMAKE_OBJCXX_COMPILER_OUTPUT) unset(__TestCompiler_testObjCXXCompilerSource) - # Move result from cache to normal variable. - set(CMAKE_OBJCXX_COMPILER_WORKS ${CMAKE_OBJCXX_COMPILER_WORKS}) - unset(CMAKE_OBJCXX_COMPILER_WORKS CACHE) __TestCompiler_restoreTryCompileTargetType() if(NOT CMAKE_OBJCXX_COMPILER_WORKS) PrintTestCompilerResult(CHECK_FAIL "broken")
diff --git a/Modules/CMakeTestSwiftCompiler.cmake b/Modules/CMakeTestSwiftCompiler.cmake index 0abaabe..64751eb 100644 --- a/Modules/CMakeTestSwiftCompiler.cmake +++ b/Modules/CMakeTestSwiftCompiler.cmake
@@ -30,10 +30,8 @@ "}") try_compile(CMAKE_Swift_COMPILER_WORKS SOURCE_FROM_VAR main.swift __CMAKE_Swift_TEST_SOURCE + NO_CACHE OUTPUT_VARIABLE __CMAKE_Swift_COMPILER_OUTPUT) - # Move result from cache to normal variable. - set(CMAKE_Swift_COMPILER_WORKS ${CMAKE_Swift_COMPILER_WORKS}) - unset(CMAKE_Swift_COMPILER_WORKS CACHE) set(Swift_TEST_WAS_RUN 1) endif()
diff --git a/Modules/CPack.cmake b/Modules/CPack.cmake index b8e7049..4104f87 100644 --- a/Modules/CPack.cmake +++ b/Modules/CPack.cmake
@@ -899,10 +899,9 @@ cmake_policy(GET CMP0133 _CPack_CMP0133) if(NOT "x${_CPack_CMP0133}x" STREQUAL "xNEWx") if(NOT "x${_CPack_CMP0133}x" STREQUAL "xOLDx" AND CMAKE_POLICY_WARNING_CMP0133) - cmake_policy(GET_WARNING CMP0133 _CMP0133_warning) - message(AUTHOR_WARNING - "${_CMP0133_warning}\n" - "For compatibility, CMake will enable the SLA in the CPack DragNDrop Generator." + cmake_policy(ISSUE_WARNING CMP0133 POST + "For compatibility, CMake will enable the SLA " + "in the CPack DragNDrop Generator." ) unset(_CMP0133_warning) endif() @@ -925,10 +924,9 @@ if("x${_CPack_CMP0172}x" STREQUAL "xNEWx") _cpack_set_default(CPACK_WIX_INSTALL_SCOPE perMachine) elseif(NOT "x${_CPack_CMP0172}x" STREQUAL "xOLDx" AND CMAKE_POLICY_WARNING_CMP0172) - cmake_policy(GET_WARNING CMP0172 _CMP0172_warning) - message(AUTHOR_WARNING - "${_CMP0172_warning}\n" - "For compatibility, CMake will not enable per-machine installation by default in the CPack WIX Generator." + cmake_policy(ISSUE_WARNING CMP0172 POST + "For compatibility, CMake will not enable per-machine installation " + "by default in the CPack WIX Generator." ) unset(_CMP0172_warning) endif() @@ -942,13 +940,11 @@ elseif(APPLE AND CPACK_BINARY_PRODUCTBUILD AND NOT DEFINED CPACK_PRODUCTBUILD_DOMAINS AND NOT "x${_CPack_CMP0161}x" STREQUAL "xOLDx") - cmake_policy(GET_WARNING CMP0161 _CMP0161_warning) - message(AUTHOR_WARNING - "${_CMP0161_warning}\n" + cmake_policy(ISSUE_WARNING CMP0161 POST "For compatibility, CPACK_PRODUCTBUILD_DOMAINS will remain unset. " "Explicitly setting CPACK_PRODUCTBUILD_DOMAINS or setting policy CMP0161 " "to NEW will prevent this warning." - ) + ) unset(_CMP0161_warning) endif() unset(_CPack_CMP0161) @@ -958,9 +954,7 @@ cmake_policy(GET CMP0206 _CPack_CMP0206) if(NOT "x${_CPack_CMP0206}x" STREQUAL "xNEWx") if(NOT "x${_CPack_CMP0206}x" STREQUAL "xOLDx" AND CMAKE_POLICY_WARNING_CMP0206) - cmake_policy(GET_WARNING CMP0206 _CMP0206_warning) - message(AUTHOR_WARNING - "${_CMP0206_warning}\n" + cmake_policy(ISSUE_WARNING CMP0206 POST "For compatibility, CMake will set archive UID/GID to -1/-1." ) unset(_CMP0206_warning)
diff --git a/Modules/CTestCoverageCollectGCOV.cmake b/Modules/CTestCoverageCollectGCOV.cmake index f36c022..acde687 100644 --- a/Modules/CTestCoverageCollectGCOV.cmake +++ b/Modules/CTestCoverageCollectGCOV.cmake
@@ -419,7 +419,9 @@ "--mtime=1970-01-01 0:0:0 UTC" "--format=gnutar" --files-from=${coverage_dir}/coverage_file_list.txt - WORKING_DIRECTORY ${binary_dir}) + WORKING_DIRECTORY ${binary_dir} + RESULT_VARIABLE _result_tar + ) if (GCOV_DELETE) foreach(gcov_file ${unfiltered_gcov_files})
diff --git a/Modules/CTestTargets.cmake b/Modules/CTestTargets.cmake index 2840875..3759253 100644 --- a/Modules/CTestTargets.cmake +++ b/Modules/CTestTargets.cmake
@@ -35,6 +35,19 @@ set(CTEST_TLS_VERIFY "$ENV{CMAKE_TLS_VERIFY}") endif() endif() + + if(CTEST_TEST_COVERAGE_TOOL STREQUAL "LLVM-COV") + find_program(CTEST_TEST_COVERAGE_MERGE_EXECUTABLE llvm-profdata) + if(NOT CTEST_TEST_COVERAGE_MERGE_EXECUTABLE) + set(CTEST_TEST_COVERAGE_MERGE_EXECUTABLE "llvm-profdata") + endif() + + find_program(CTEST_TEST_COVERAGE_MERGE_EXECUTABLE llvm-cov) + if(NOT CTEST_TEST_COVERAGE_DATA_EXEUCUTABLE) + set(CTEST_TEST_COVERAGE_DATA_EXECUTABLE "llvm-cov") + endif() + endif() + if(CTEST_NEW_FORMAT) configure_file( ${CMAKE_ROOT}/Modules/DartConfiguration.tcl.in
diff --git a/Modules/CheckFunctionExists.cmake b/Modules/CheckFunctionExists.cmake index 5d13f2b..899e937 100644 --- a/Modules/CheckFunctionExists.cmake +++ b/Modules/CheckFunctionExists.cmake
@@ -123,38 +123,15 @@ #]=======================================================================] include_guard(GLOBAL) +include(Internal/CheckCommon) macro(CHECK_FUNCTION_EXISTS FUNCTION VARIABLE) if(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}") - set(MACRO_CHECK_FUNCTION_DEFINITIONS - "-DCHECK_FUNCTION_EXISTS=${FUNCTION} ${CMAKE_REQUIRED_FLAGS}") if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Looking for ${FUNCTION}") endif() - if(CMAKE_REQUIRED_LINK_OPTIONS) - set(CHECK_FUNCTION_EXISTS_ADD_LINK_OPTIONS - LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - else() - set(CHECK_FUNCTION_EXISTS_ADD_LINK_OPTIONS) - endif() - if(CMAKE_REQUIRED_LIBRARIES) - set(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES - LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - else() - set(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES) - endif() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CFE_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CFE_LINK_DIRECTORIES) - endif() - if(CMAKE_REQUIRED_INCLUDES) - set(CHECK_FUNCTION_EXISTS_ADD_INCLUDES - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - else() - set(CHECK_FUNCTION_EXISTS_ADD_INCLUDES) - endif() + + cmake_check_common_init_args(_CFE) if(CMAKE_C_COMPILER_LOADED) set(_cfe_source CheckFunctionExists.c) @@ -166,15 +143,19 @@ try_compile(${VARIABLE} SOURCE_FROM_FILE "${_cfe_source}" "${CMAKE_ROOT}/Modules/CheckFunctionExists.c" - COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - ${CHECK_FUNCTION_EXISTS_ADD_LINK_OPTIONS} - ${CHECK_FUNCTION_EXISTS_ADD_LIBRARIES} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} - "${CHECK_FUNCTION_EXISTS_ADD_INCLUDES}" - "${_CFE_LINK_DIRECTORIES}" + COMPILE_DEFINITIONS + -DCHECK_FUNCTION_EXISTS=${FUNCTION} + ${CMAKE_REQUIRED_DEFINITIONS} + ${_CFE_ADD_LINK_OPTIONS} + ${_CFE_ADD_LINK_LIBRARIES} + CMAKE_FLAGS + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + ${_CSC_EXTRA_CMAKE_ARGUMENTS} + "${_CFE_INCLUDE_DIRECTORIES}" + "${_CFE_LINK_DIRECTORIES}" ) unset(_cfe_source) - unset(_CFE_LINK_DIRECTORIES) + cmake_check_common_cleanup(_CFE) if(${VARIABLE}) set(${VARIABLE} 1 CACHE INTERNAL "Have function ${FUNCTION}")
diff --git a/Modules/CheckIPOSupported.cmake b/Modules/CheckIPOSupported.cmake index ced2a81..f6a4e8b 100644 --- a/Modules/CheckIPOSupported.cmake +++ b/Modules/CheckIPOSupported.cmake
@@ -199,14 +199,13 @@ PROJECT "${TRY_COMPILE_PROJECT_NAME}" SOURCE_DIR "${srcdir}" BINARY_DIR "${bindir}" + NO_CACHE CMAKE_FLAGS "-DCMAKE_VERBOSE_MAKEFILE=ON" "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON" ${_CMAKE_LANG_FLAGS} OUTPUT_VARIABLE output ) - set(_IPO_LANGUAGE_CHECK_RESULT "${_IPO_LANGUAGE_CHECK_RESULT}") - unset(_IPO_LANGUAGE_CHECK_RESULT CACHE) if(NOT _IPO_LANGUAGE_CHECK_RESULT) _ipo_not_supported("check failed to compile")
diff --git a/Modules/CheckIncludeFile.cmake b/Modules/CheckIncludeFile.cmake index 74f0edb..0569796 100644 --- a/Modules/CheckIncludeFile.cmake +++ b/Modules/CheckIncludeFile.cmake
@@ -116,15 +116,10 @@ #]=======================================================================] include_guard(GLOBAL) +include(Internal/CheckCommon) macro(CHECK_INCLUDE_FILE INCLUDE VARIABLE) if(NOT DEFINED "${VARIABLE}") - if(CMAKE_REQUIRED_INCLUDES) - set(CHECK_INCLUDE_FILE_C_INCLUDE_DIRS "-DINCLUDE_DIRECTORIES=${CMAKE_REQUIRED_INCLUDES}") - else() - set(CHECK_INCLUDE_FILE_C_INCLUDE_DIRS) - endif() - set(MACRO_CHECK_INCLUDE_FILE_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(CHECK_INCLUDE_FILE_VAR ${INCLUDE}) file(READ ${CMAKE_ROOT}/Modules/CheckIncludeFile.c.in _CIF_SOURCE_CONTENT) string(CONFIGURE "${_CIF_SOURCE_CONTENT}" _CIF_SOURCE_CONTENT) @@ -136,26 +131,21 @@ string(APPEND CMAKE_C_FLAGS " ${ARGV2}") endif() - set(_CIF_LINK_OPTIONS) - if(CMAKE_REQUIRED_LINK_OPTIONS) - set(_CIF_LINK_OPTIONS LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - endif() + cmake_check_common_init_compile_args(_CIF) + cmake_check_common_init_link_options(_CIF) + cmake_check_common_init_link_directories(_CIF) - set(_CIF_LINK_LIBRARIES "") + set(_CIF_ADD_LINK_LIBRARIES "") if(CMAKE_REQUIRED_LIBRARIES) cmake_policy(GET CMP0075 _CIF_CMP0075 PARENT_SCOPE # undocumented, do not use outside of CMake ) if("x${_CIF_CMP0075}x" STREQUAL "xNEWx") - set(_CIF_LINK_LIBRARIES LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + set(_CIF_ADD_LINK_LIBRARIES LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) elseif("x${_CIF_CMP0075}x" STREQUAL "xOLDx") elseif(NOT _CIF_CMP0075_WARNED) set(_CIF_CMP0075_WARNED 1) - message(AUTHOR_WARNING - "Policy CMP0075 is not set: Include file check macros honor CMAKE_REQUIRED_LIBRARIES. " - "Run \"cmake --help-policy CMP0075\" for policy details. " - "Use the cmake_policy command to set the policy and suppress this warning." - "\n" + cmake_policy(ISSUE_WARNING CMP0075 POST "CMAKE_REQUIRED_LIBRARIES is set to:\n" " ${CMAKE_REQUIRED_LIBRARIES}\n" "For compatibility with CMake 3.11 and below this check is ignoring it." @@ -164,26 +154,17 @@ unset(_CIF_CMP0075) endif() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CIF_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CIF_LINK_DIRECTORIES) - endif() - try_compile(${VARIABLE} SOURCE_FROM_VAR CheckIncludeFile.c _CIF_SOURCE_CONTENT COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - ${_CIF_LINK_OPTIONS} - ${_CIF_LINK_LIBRARIES} + ${_CIF_ADD_LINK_OPTIONS} + ${_CIF_ADD_LINK_LIBRARIES} CMAKE_FLAGS - -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILE_FLAGS} - "${CHECK_INCLUDE_FILE_C_INCLUDE_DIRS}" - "${_CIF_LINK_DIRECTORIES}" + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + "${_CIF_INCLUDE_DIRECTORIES}" + "${_CIF_LINK_DIRECTORIES}" ) - unset(_CIF_LINK_OPTIONS) - unset(_CIF_LINK_LIBRARIES) - unset(_CIF_LINK_DIRECTORIES) + cmake_check_common_cleanup(_CIF) if(${ARGC} EQUAL 3) set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_SAVE})
diff --git a/Modules/CheckIncludeFileCXX.cmake b/Modules/CheckIncludeFileCXX.cmake index 3d487a4..bea5f08 100644 --- a/Modules/CheckIncludeFileCXX.cmake +++ b/Modules/CheckIncludeFileCXX.cmake
@@ -85,15 +85,10 @@ #]=======================================================================] include_guard(GLOBAL) +include(Internal/CheckCommon) macro(CHECK_INCLUDE_FILE_CXX INCLUDE VARIABLE) if(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}") - if(CMAKE_REQUIRED_INCLUDES) - set(CHECK_INCLUDE_FILE_CXX_INCLUDE_DIRS "-DINCLUDE_DIRECTORIES=${CMAKE_REQUIRED_INCLUDES}") - else() - set(CHECK_INCLUDE_FILE_CXX_INCLUDE_DIRS) - endif() - set(MACRO_CHECK_INCLUDE_FILE_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(CHECK_INCLUDE_FILE_VAR ${INCLUDE}) file(READ ${CMAKE_ROOT}/Modules/CheckIncludeFile.cxx.in _CIF_SOURCE_CONTENT) string(CONFIGURE "${_CIF_SOURCE_CONTENT}" _CIF_SOURCE_CONTENT) @@ -105,26 +100,21 @@ string(APPEND CMAKE_CXX_FLAGS " ${ARGV2}") endif() - set(_CIF_LINK_OPTIONS) - if(CMAKE_REQUIRED_LINK_OPTIONS) - set(_CIF_LINK_OPTIONS LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - endif() + cmake_check_common_init_compile_args(_CIF) + cmake_check_common_init_link_options(_CIF) + cmake_check_common_init_link_directories(_CIF) - set(_CIF_LINK_LIBRARIES "") + set(_CIF_ADD_LINK_LIBRARIES "") if(CMAKE_REQUIRED_LIBRARIES) cmake_policy(GET CMP0075 _CIF_CMP0075 PARENT_SCOPE # undocumented, do not use outside of CMake ) if("x${_CIF_CMP0075}x" STREQUAL "xNEWx") - set(_CIF_LINK_LIBRARIES LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + set(_CIF_ADD_LINK_LIBRARIES LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) elseif("x${_CIF_CMP0075}x" STREQUAL "xOLDx") elseif(NOT _CIF_CMP0075_WARNED) set(_CIF_CMP0075_WARNED 1) - message(AUTHOR_WARNING - "Policy CMP0075 is not set: Include file check macros honor CMAKE_REQUIRED_LIBRARIES. " - "Run \"cmake --help-policy CMP0075\" for policy details. " - "Use the cmake_policy command to set the policy and suppress this warning." - "\n" + cmake_policy(ISSUE_WARNING CMP0075 POST "CMAKE_REQUIRED_LIBRARIES is set to:\n" " ${CMAKE_REQUIRED_LIBRARIES}\n" "For compatibility with CMake 3.11 and below this check is ignoring it." @@ -133,26 +123,17 @@ unset(_CIF_CMP0075) endif() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CIF_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CIF_LINK_DIRECTORIES) - endif() - try_compile(${VARIABLE} SOURCE_FROM_VAR CheckIncludeFile.cxx _CIF_SOURCE_CONTENT COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - ${_CIF_LINK_OPTIONS} - ${_CIF_LINK_LIBRARIES} + ${_CIF_ADD_LINK_OPTIONS} + ${_CIF_ADD_LINK_LIBRARIES} CMAKE_FLAGS - -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILE_FLAGS} - "${CHECK_INCLUDE_FILE_CXX_INCLUDE_DIRS}" - "${_CIF_LINK_DIRECTORIES}" + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + "${_CIF_INCLUDE_DIRECTORIES}" + "${_CIF_LINK_DIRECTORIES}" ) - unset(_CIF_LINK_OPTIONS) - unset(_CIF_LINK_LIBRARIES) - unset(_CIF_LINK_DIRECTORIES) + cmake_check_common_cleanup(_CIF) if(${ARGC} EQUAL 3) set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS_SAVE})
diff --git a/Modules/CheckIncludeFiles.cmake b/Modules/CheckIncludeFiles.cmake index 9870340..57e6bed 100644 --- a/Modules/CheckIncludeFiles.cmake +++ b/Modules/CheckIncludeFiles.cmake
@@ -106,6 +106,7 @@ #]=======================================================================] include_guard(GLOBAL) +include(Internal/CheckCommon) macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE) if(NOT DEFINED "${VARIABLE}") @@ -136,13 +137,7 @@ message(FATAL_ERROR "Unknown language:\n ${_lang}\nSupported languages: C, CXX.\n") endif() - if(CMAKE_REQUIRED_INCLUDES) - set(CHECK_INCLUDE_FILES_INCLUDE_DIRS "-DINCLUDE_DIRECTORIES=${CMAKE_REQUIRED_INCLUDES}") - else() - set(CHECK_INCLUDE_FILES_INCLUDE_DIRS) - endif() set(CHECK_INCLUDE_FILES_CONTENT "/* */\n") - set(MACRO_CHECK_INCLUDE_FILES_FLAGS ${CMAKE_REQUIRED_FLAGS}) foreach(FILE ${INCLUDE}) string(APPEND _src_content "#include <${FILE}>\n") @@ -160,26 +155,21 @@ set(_description "include file ${_INCLUDE}") endif() - set(_CIF_LINK_OPTIONS) - if(CMAKE_REQUIRED_LINK_OPTIONS) - set(_CIF_LINK_OPTIONS LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - endif() + cmake_check_common_init_compile_args(_CIF) + cmake_check_common_init_link_options(_CIF) + cmake_check_common_init_link_directories(_CIF) - set(_CIF_LINK_LIBRARIES "") + set(_CIF_ADD_LINK_LIBRARIES "") if(CMAKE_REQUIRED_LIBRARIES) cmake_policy(GET CMP0075 _CIF_CMP0075 PARENT_SCOPE # undocumented, do not use outside of CMake ) if("x${_CIF_CMP0075}x" STREQUAL "xNEWx") - set(_CIF_LINK_LIBRARIES LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + set(_CIF_ADD_LINK_LIBRARIES LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) elseif("x${_CIF_CMP0075}x" STREQUAL "xOLDx") elseif(NOT _CIF_CMP0075_WARNED) set(_CIF_CMP0075_WARNED 1) - message(AUTHOR_WARNING - "Policy CMP0075 is not set: Include file check macros honor CMAKE_REQUIRED_LIBRARIES. " - "Run \"cmake --help-policy CMP0075\" for policy details. " - "Use the cmake_policy command to set the policy and suppress this warning." - "\n" + cmake_policy(ISSUE_WARNING CMP0075 POST "CMAKE_REQUIRED_LIBRARIES is set to:\n" " ${CMAKE_REQUIRED_LIBRARIES}\n" "For compatibility with CMake 3.11 and below this check is ignoring it." @@ -188,29 +178,21 @@ unset(_CIF_CMP0075) endif() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CIF_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CIF_LINK_DIRECTORIES) - endif() - if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Looking for ${_description}") endif() try_compile(${VARIABLE} SOURCE_FROM_VAR "${src}" _src_content COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - ${_CIF_LINK_OPTIONS} - ${_CIF_LINK_LIBRARIES} + ${_CIF_ADD_LINK_OPTIONS} + ${_CIF_ADD_LINK_LIBRARIES} CMAKE_FLAGS - -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILES_FLAGS} - "${CHECK_INCLUDE_FILES_INCLUDE_DIRS}" - "${_CIF_LINK_DIRECTORIES}" + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + "${_CIF_INCLUDE_DIRECTORIES}" + "${_CIF_LINK_DIRECTORIES}" ) - unset(_CIF_LINK_OPTIONS) - unset(_CIF_LINK_LIBRARIES) - unset(_CIF_LINK_DIRECTORIES) + cmake_check_common_cleanup(_CIF) + if(${VARIABLE}) if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_PASS "found")
diff --git a/Modules/CheckLibraryExists.cmake b/Modules/CheckLibraryExists.cmake index bd75bd1..4864908 100644 --- a/Modules/CheckLibraryExists.cmake +++ b/Modules/CheckLibraryExists.cmake
@@ -118,30 +118,18 @@ #]=======================================================================] include_guard(GLOBAL) +include(Internal/CheckCommon) macro(CHECK_LIBRARY_EXISTS LIBRARY FUNCTION LOCATION VARIABLE) if(NOT DEFINED "${VARIABLE}") - set(MACRO_CHECK_LIBRARY_EXISTS_DEFINITION - "-DCHECK_FUNCTION_EXISTS=${FUNCTION} ${CMAKE_REQUIRED_FLAGS}") if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Looking for ${FUNCTION} in ${LIBRARY}") endif() - set(CHECK_LIBRARY_EXISTS_LINK_OPTIONS) - if(CMAKE_REQUIRED_LINK_OPTIONS) - set(CHECK_LIBRARY_EXISTS_LINK_OPTIONS - LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - endif() - set(CHECK_LIBRARY_EXISTS_LIBRARIES ${LIBRARY}) - if(CMAKE_REQUIRED_LIBRARIES) - set(CHECK_LIBRARY_EXISTS_LIBRARIES - ${CHECK_LIBRARY_EXISTS_LIBRARIES} ${CMAKE_REQUIRED_LIBRARIES}) - endif() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CLE_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${LOCATION};${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CLE_LINK_DIRECTORIES "-DLINK_DIRECTORIES:STRING=${LOCATION}") - endif() + + cmake_check_common_init_compile_flags(_CLE) + cmake_check_common_init_link_options(_CLE) + cmake_check_common_init_link_libraries(_CLE "${LIBRARY}") + cmake_check_common_init_link_directories(_CLE "${LOCATION}") if(CMAKE_C_COMPILER_LOADED) set(_cle_source CheckFunctionExists.c) @@ -153,15 +141,17 @@ try_compile(${VARIABLE} SOURCE_FROM_FILE "${_cle_source}" "${CMAKE_ROOT}/Modules/CheckFunctionExists.c" - COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - ${CHECK_LIBRARY_EXISTS_LINK_OPTIONS} - LINK_LIBRARIES ${CHECK_LIBRARY_EXISTS_LIBRARIES} + COMPILE_DEFINITIONS + -DCHECK_FUNCTION_EXISTS=${FUNCTION} + ${CMAKE_REQUIRED_DEFINITIONS} + ${_CLE_ADD_LINK_OPTIONS} + ${_CLE_ADD_LINK_LIBRARIES} CMAKE_FLAGS - -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_LIBRARY_EXISTS_DEFINITION} - "${_CLE_LINK_DIRECTORIES}" + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + "${_CLE_LINK_DIRECTORIES}" ) unset(_cle_source) - unset(_CLE_LINK_DIRECTORIES) + cmake_check_common_cleanup(_CLE) if(${VARIABLE}) if(NOT CMAKE_REQUIRED_QUIET)
diff --git a/Modules/CheckPrototypeDefinition.cmake b/Modules/CheckPrototypeDefinition.cmake index d77e81c..84f0b6d 100644 --- a/Modules/CheckPrototypeDefinition.cmake +++ b/Modules/CheckPrototypeDefinition.cmake
@@ -94,6 +94,7 @@ #]=======================================================================] include_guard(GLOBAL) +include(Internal/CheckCommon) function(check_prototype_definition _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIABLE) @@ -103,33 +104,6 @@ endif() set(CHECK_PROTOTYPE_DEFINITION_CONTENT "/* */\n") - set(CHECK_PROTOTYPE_DEFINITION_FLAGS ${CMAKE_REQUIRED_FLAGS}) - if (CMAKE_REQUIRED_LINK_OPTIONS) - set(CHECK_PROTOTYPE_DEFINITION_LINK_OPTIONS - LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - else() - set(CHECK_PROTOTYPE_DEFINITION_LINK_OPTIONS) - endif() - if (CMAKE_REQUIRED_LIBRARIES) - set(CHECK_PROTOTYPE_DEFINITION_LIBS - LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - else() - set(CHECK_PROTOTYPE_DEFINITION_LIBS) - endif() - if (CMAKE_REQUIRED_INCLUDES) - set(CMAKE_SYMBOL_EXISTS_INCLUDES - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - else() - set(CMAKE_SYMBOL_EXISTS_INCLUDES) - endif() - - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CPD_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CPD_LINK_DIRECTORIES) - endif() - foreach(_FILE ${_HEADER}) string(APPEND CHECK_PROTOTYPE_DEFINITION_HEADER "#include <${_FILE}>\n") @@ -142,16 +116,19 @@ file(READ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CheckPrototypeDefinition.c.in _SOURCE) string(CONFIGURE "${_SOURCE}" _SOURCE @ONLY) + cmake_check_common_init_args(_CPD) + try_compile(${_VARIABLE} SOURCE_FROM_VAR CheckPrototypeDefinition.c _SOURCE COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - ${CHECK_PROTOTYPE_DEFINITION_LINK_OPTIONS} - ${CHECK_PROTOTYPE_DEFINITION_LIBS} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CHECK_PROTOTYPE_DEFINITION_FLAGS} - "${CMAKE_SYMBOL_EXISTS_INCLUDES}" - "${_CPD_LINK_DIRECTORIES}" + ${_CPD_ADD_LINK_OPTIONS} + ${_CPD_ADD_LINK_LIBRARIES} + CMAKE_FLAGS + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + ${_CPD_EXTRA_CMAKE_ARGUMENTS} + "${_CPD_INCLUDE_DIRECTORIES}" + "${_CPD_LINK_DIRECTORIES}" ) - unset(_CPD_LINK_DIRECTORIES) if (${_VARIABLE}) set(${_VARIABLE} 1 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") @@ -165,5 +142,4 @@ set(${_VARIABLE} 0 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") endif () endif() - endfunction()
diff --git a/Modules/CheckSymbolExists.cmake b/Modules/CheckSymbolExists.cmake index 46d1ab3..3f49dfc 100644 --- a/Modules/CheckSymbolExists.cmake +++ b/Modules/CheckSymbolExists.cmake
@@ -91,6 +91,7 @@ #]=======================================================================] include_guard(GLOBAL) +include(Internal/CheckCommon) macro(CHECK_SYMBOL_EXISTS SYMBOL FILES VARIABLE) if(CMAKE_C_COMPILER_LOADED) @@ -134,32 +135,7 @@ macro(__CHECK_SYMBOL_EXISTS_IMPL SOURCEFILE SYMBOL FILES VARIABLE) if(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}") set(_CSE_SOURCE "/* */\n") - set(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) - if(CMAKE_REQUIRED_LINK_OPTIONS) - set(CHECK_SYMBOL_EXISTS_LINK_OPTIONS - LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - else() - set(CHECK_SYMBOL_EXISTS_LINK_OPTIONS) - endif() - if(CMAKE_REQUIRED_LIBRARIES) - set(CHECK_SYMBOL_EXISTS_LIBS - LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - else() - set(CHECK_SYMBOL_EXISTS_LIBS) - endif() - if(CMAKE_REQUIRED_INCLUDES) - set(CMAKE_SYMBOL_EXISTS_INCLUDES - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - else() - set(CMAKE_SYMBOL_EXISTS_INCLUDES) - endif() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CSE_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CSE_LINK_DIRECTORIES) - endif() foreach(FILE ${FILES}) string(APPEND _CSE_SOURCE "#include <${FILE}>\n") @@ -187,20 +163,25 @@ }\n") unset(_CSE_CHECK_NON_MACRO) + cmake_check_common_init_args(_CSE) + if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Looking for ${SYMBOL}") endif() try_compile(${VARIABLE} SOURCE_FROM_VAR "${SOURCEFILE}" _CSE_SOURCE COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - ${CHECK_SYMBOL_EXISTS_LINK_OPTIONS} - ${CHECK_SYMBOL_EXISTS_LIBS} + ${_CSE_ADD_LINK_OPTIONS} + ${_CSE_ADD_LINK_LIBRARIES} CMAKE_FLAGS - -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_SYMBOL_EXISTS_FLAGS} - "${CMAKE_SYMBOL_EXISTS_INCLUDES}" - "${_CSE_LINK_DIRECTORIES}" + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + ${_CSE_EXTRA_CMAKE_ARGUMENTS} + -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH} + "${_CSE_INCLUDE_DIRECTORIES}" + "${_CSE_LINK_DIRECTORIES}" ) - unset(_CSE_LINK_DIRECTORIES) + cmake_check_common_cleanup(_CSE) + if(${VARIABLE}) if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_PASS "found")
diff --git a/Modules/CheckTypeSize.cmake b/Modules/CheckTypeSize.cmake index 1f5a622..c0c9dbb 100644 --- a/Modules/CheckTypeSize.cmake +++ b/Modules/CheckTypeSize.cmake
@@ -282,6 +282,7 @@ include(CheckIncludeFileCXX) include_guard(GLOBAL) +include(Internal/CheckCommon) block(SCOPE_FOR POLICIES) cmake_policy(SET CMP0140 NEW) @@ -334,12 +335,7 @@ string(APPEND headers "#include \"${h}\"\n") endforeach() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CTS_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CTS_LINK_DIRECTORIES) - endif() + cmake_check_common_init_args(_CTS) # Perform the check. set(bin ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.bin) @@ -347,15 +343,14 @@ string(CONFIGURE "${src_content}" src_content @ONLY) try_compile(${result_var} SOURCE_FROM_VAR "${src}" src_content COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS} - LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} + ${_CTS_ADD_LINK_OPTIONS} + ${_CTS_ADD_LINK_LIBRARIES} CMAKE_FLAGS - "-DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS}" - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}" + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + "${_CTS_INCLUDE_DIRECTORIES}" "${_CTS_LINK_DIRECTORIES}" COPY_FILE ${bin} ) - unset(_CTS_LINK_DIRECTORIES) if(${result_var}) # The check compiled. Load information from the binary.
diff --git a/Modules/Compiler/ARMClang.cmake b/Modules/Compiler/ARMClang.cmake index 2f0ee91..a3c7086 100644 --- a/Modules/Compiler/ARMClang.cmake +++ b/Modules/Compiler/ARMClang.cmake
@@ -25,9 +25,14 @@ function(__armclang_set_processor_list lang out_var) execute_process(COMMAND "${CMAKE_${lang}_COMPILER}" --target=${CMAKE_${lang}_COMPILER_TARGET} -mcpu=list OUTPUT_VARIABLE processor_list - ERROR_VARIABLE processor_list) - string(REGEX MATCHALL "-mcpu=([^ \n]*)" processor_list "${processor_list}") - string(REGEX REPLACE "-mcpu=" "" processor_list "${processor_list}") + ERROR_VARIABLE processor_list + RESULT_VARIABLE _res + ) + set(processor_list) + if(_res EQUAL 0) + string(REGEX MATCHALL "-mcpu=([^ \n]*)" processor_list "${processor_list}") + string(REGEX REPLACE "-mcpu=" "" processor_list "${processor_list}") + endif() set(${out_var} "${processor_list}" PARENT_SCOPE) endfunction() @@ -45,9 +50,14 @@ function(__armclang_set_arch_list lang out_var) execute_process(COMMAND "${CMAKE_${lang}_COMPILER}" --target=${CMAKE_${lang}_COMPILER_TARGET} -march=list OUTPUT_VARIABLE arch_list - ERROR_VARIABLE arch_list) - string(REGEX MATCHALL "-march=([^ \n]*)" arch_list "${arch_list}") - string(REGEX REPLACE "-march=" "" arch_list "${arch_list}") + ERROR_VARIABLE arch_list + RESULT_VARIABLE _res + ) + set(arch_list) + if(_res EQUAL 0) + string(REGEX MATCHALL "-march=([^ \n]*)" arch_list "${arch_list}") + string(REGEX REPLACE "-march=" "" arch_list "${arch_list}") + endif() set(${out_var} "${arch_list}" PARENT_SCOPE) endfunction() @@ -61,9 +71,14 @@ execute_process(COMMAND "${CMAKE_LINKER}" ${__linker_wrapper_flags} --cpu=list OUTPUT_VARIABLE cpu_list - ERROR_VARIABLE cpu_list) - string(REGEX MATCHALL "--cpu=([^ \n]*)" cpu_list "${cpu_list}") - string(REGEX REPLACE "--cpu=" "" cpu_list "${cpu_list}") + ERROR_VARIABLE cpu_list + RESULT_VARIABLE _res + ) + set(cpu_list) + if(_res EQUAL 0) + string(REGEX MATCHALL "--cpu=([^ \n]*)" cpu_list "${cpu_list}") + string(REGEX REPLACE "--cpu=" "" cpu_list "${cpu_list}") + endif() set(${out_var} "${cpu_list}" PARENT_SCOPE) endfunction() @@ -84,9 +99,7 @@ cmake_policy(GET CMP0123 policy_CMP0123) if(NOT "x${CMAKE_ARMClang_CMP0123}x" STREQUAL "xNEWx") if(NOT "x${CMAKE_ARMClang_CMP0123}x" STREQUAL "xOLDx") - cmake_policy(GET_WARNING CMP0123 _cmp0123_warning) - message(AUTHOR_WARNING - "${_cmp0123_warning}\n" + cmake_policy(ISSUE_WARNING CMP0123 POST "For compatibility, CMake will automatically add cpu/arch flags based " "on the CMAKE_SYSTEM_PROCESSOR and/or CMAKE_SYSTEM_ARCH variables." )
diff --git a/Modules/Compiler/Clang-CXX-CXXImportStd.cmake b/Modules/Compiler/Clang-CXX-CXXImportStd.cmake index 9b66b56..5d637b7 100644 --- a/Modules/Compiler/Clang-CXX-CXXImportStd.cmake +++ b/Modules/Compiler/Clang-CXX-CXXImportStd.cmake
@@ -1,14 +1,14 @@ function (_cmake_cxx_find_modules_json) - if (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libc++") - set(_clang_modules_json_impl "libc++") - elseif (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libstdc++") - set(_clang_modules_json_impl "libstdc++") - else () - set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Only `libc++` and `libstdc++` are supported" PARENT_SCOPE) - return () - endif () - if (NOT CMAKE_CXX_STDLIB_MODULES_JSON) + if (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libc++") + set(_clang_modules_json_impl "libc++") + elseif (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libstdc++") + set(_clang_modules_json_impl "libstdc++") + else () + set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Only `libc++` and `libstdc++` are supported" PARENT_SCOPE) + return () + endif () + execute_process( COMMAND "${CMAKE_CXX_COMPILER}" @@ -32,7 +32,7 @@ # The original PR had a key spelling mismatch internally. Do not support it # and instead require a release known to have the fix. # https://github.com/llvm/llvm-project/pull/83036 - set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "LLVM 18.1.2 is required for `${_clang_modules_json_impl}.modules.json` format fix" PARENT_SCOPE) + set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "LLVM 18.1.2 is required for 'import std' json format fix" PARENT_SCOPE) return () endif () endfunction ()
diff --git a/Modules/Compiler/Clang-CXX.cmake b/Modules/Compiler/Clang-CXX.cmake index 205c5f7..5a8bc3f 100644 --- a/Modules/Compiler/Clang-CXX.cmake +++ b/Modules/Compiler/Clang-CXX.cmake
@@ -20,43 +20,52 @@ set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl") endif() -if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) - if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU") - if (CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR) - set(_clang_scan_deps_resource_dir - " -resource-dir \"${CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR}\"") - else() - set(_clang_scan_deps_resource_dir "") - endif () - if (CMAKE_HOST_WIN32) - # `rename` doesn't overwrite and doesn't retry in case of "target file is - # busy". - set(_clang_scan_deps_mv "\"${CMAKE_COMMAND}\" -E rename") - else () - set(_clang_scan_deps_mv "mv") - endif () - string(CONCAT CMAKE_CXX_SCANDEP_SOURCE - "\"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}\"" - " -format=p1689" - " --" - " <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>" - " -x c++ <SOURCE> -c -o <OBJECT>" - "${_clang_scan_deps_resource_dir}" - " -MT <DYNDEP_FILE>" - " -MD -MF <DEP_FILE>" - # Write to a temporary file. If the scan fails, we do not want to update - # the actual output file as `ninja` (at least) assumes that failed - # commands either delete or leave output files alone. See Issue#25419. - " > <DYNDEP_FILE>.tmp" - # We cannot use `copy_if_different` as the rule does not have a feature - # analogous to `ninja`'s `restat = 1`. It would also leave behind the - # `.tmp` file. - " && ${_clang_scan_deps_mv} <DYNDEP_FILE>.tmp <DYNDEP_FILE>") - unset(_clang_scan_deps_resource_dir) - unset(_clang_scan_deps_mv) - set(CMAKE_CXX_MODULE_MAP_FORMAT "clang") - set(CMAKE_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>") - set(CMAKE_CXX_COMPILE_BMI - "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> --precompile <SOURCE>") +if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0 AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU") OR + (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.1 AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")) + if (CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR) + set(_clang_scan_deps_resource_dir + " -resource-dir \"${CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR}\"") + else() + set(_clang_scan_deps_resource_dir "") + endif () + if (CMAKE_HOST_WIN32) + # `rename` doesn't overwrite and doesn't retry in case of "target file is + # busy". + set(_clang_scan_deps_mv "\"${CMAKE_COMMAND}\" -E rename") + else () + set(_clang_scan_deps_mv "mv") + endif () + if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + set(_clang "-clang:") + set(_srcsep "-- ") + else() + set(_clang "") + set(_srcsep "") endif() + string(CONCAT CMAKE_CXX_SCANDEP_SOURCE + "\"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}\"" + " -format=p1689" + " --" + " <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>" + " -x c++ -c ${_clang}-o ${_clang}<OBJECT>" + "${_clang_scan_deps_resource_dir}" + " ${_clang}-MT ${_clang}<DYNDEP_FILE>" + " ${_clang}-MD ${_clang}-MF ${_clang}<DEP_FILE>" + " ${_srcsep}<SOURCE>" + # Write to a temporary file. If the scan fails, we do not want to update + # the actual output file as `ninja` (at least) assumes that failed + # commands either delete or leave output files alone. See Issue#25419. + " > <DYNDEP_FILE>.tmp" + # We cannot use `copy_if_different` as the rule does not have a feature + # analogous to `ninja`'s `restat = 1`. It would also leave behind the + # `.tmp` file. + " && ${_clang_scan_deps_mv} <DYNDEP_FILE>.tmp <DYNDEP_FILE>") + unset(_clang_scan_deps_resource_dir) + unset(_clang_scan_deps_mv) + set(CMAKE_CXX_MODULE_MAP_FORMAT "clang") + set(CMAKE_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>") + set(CMAKE_CXX_COMPILE_BMI + "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> ${_clang}-o ${_clang}<OBJECT> --precompile ${_srcsep}<SOURCE>") + unset(_clang) + unset(_srcsep) endif()
diff --git a/Modules/Compiler/Clang-FindBinUtils.cmake b/Modules/Compiler/Clang-FindBinUtils.cmake index f4f35e6..48195e2 100644 --- a/Modules/Compiler/Clang-FindBinUtils.cmake +++ b/Modules/Compiler/Clang-FindBinUtils.cmake
@@ -52,6 +52,12 @@ "${_CMAKE_TOOLCHAIN_PREFIX}clang-scan-deps-${__version_x}" "${_CMAKE_TOOLCHAIN_PREFIX}clang-scan-deps${__version_x}" "${_CMAKE_TOOLCHAIN_PREFIX}clang-scan-deps" + # Gentoo, at least, installs associated tools without the prefix. Upstream + # builds also seem to not add toolchain prefixes to `clang-scan-deps`. + "clang-scan-deps-${__version_x_y}" + "clang-scan-deps-${__version_x}" + "clang-scan-deps${__version_x}" + "clang-scan-deps" HINTS ${__clang_hints} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH DOC "`clang-scan-deps` dependency scanner"
diff --git a/Modules/Compiler/IAR-C.cmake b/Modules/Compiler/IAR-C.cmake index df8f003..99609d6 100644 --- a/Modules/Compiler/IAR-C.cmake +++ b/Modules/Compiler/IAR-C.cmake
@@ -1,12 +1,5 @@ -# This file is processed when the IAR C Compiler is used -# -# C Language Specification support -# - Newer versions of the IAR C Compiler require the --c89 flag to build a file under the C90 standard. -# - Earlier versions of the compiler had C90 by default, not requiring the backward-compatibility flag. -# -# The IAR Language Extensions -# - The IAR Language Extensions can be enabled by -e flag -# +# This file is processed when the IAR C Compiler is used. + include(Compiler/IAR) include(Compiler/CMakeCommonCompilerMacros) @@ -17,16 +10,35 @@ # Unused after CMP0128 set(CMAKE_C_EXTENSION_COMPILE_OPTION -e) -if(CMAKE_C_COMPILER_VERSION_INTERNAL VERSION_GREATER 7) +# Stubs for standard/extended compile options +if((NOT CMAKE_C_STANDARD_COMPUTED_DEFAULT EQUAL 99) AND + (NOT CMAKE_C_STANDARD_COMPUTED_DEFAULT EQUAL 90)) + if(CMAKE_C_STANDARD_COMPUTED_DEFAULT GREATER_EQUAL 17) + set(CMAKE_C17_STANDARD_COMPILE_OPTION "") + set(CMAKE_C17_EXTENSION_COMPILE_OPTION -e) + endif() + if(CMAKE_C_STANDARD_COMPUTED_DEFAULT GREATER_EQUAL 11) + set(CMAKE_C11_STANDARD_COMPILE_OPTION "") + set(CMAKE_C11_EXTENSION_COMPILE_OPTION -e) + endif() + set(CMAKE_C99_STANDARD_COMPILE_OPTION "") + set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e) +elseif(CMAKE_C_STANDARD_COMPUTED_DEFAULT EQUAL 99) + set(CMAKE_C99_STANDARD_COMPILE_OPTION "") + set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e) +endif() + +# C90 Mode +# - IAR Compilers with *internal* version >= v8.0.0 require the --c89 flag. +# - IAR Compilers with *internal* version < v8.0.0 has C90 mode by default. +if(CMAKE_C_COMPILER_VERSION_INTERNAL GREATER_EQUAL 0x00080000) set(CMAKE_C90_STANDARD_COMPILE_OPTION --c89) set(CMAKE_C90_EXTENSION_COMPILE_OPTION --c89 -e) -else() +elseif(CMAKE_C_COMPILER_VERSION_INTERNAL LESS 0x00080000) set(CMAKE_C90_STANDARD_COMPILE_OPTION "") set(CMAKE_C90_EXTENSION_COMPILE_OPTION -e) endif() -set(CMAKE_C${CMAKE_C_STANDARD_COMPUTED_DEFAULT}_STANDARD_COMPILE_OPTION "") -set(CMAKE_C${CMAKE_C_STANDARD_COMPUTED_DEFAULT}_EXTENSION_COMPILE_OPTION -e) # Architecture specific if("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
diff --git a/Modules/Compiler/IAR-CXX.cmake b/Modules/Compiler/IAR-CXX.cmake index bd74e2b..2b794a6 100644 --- a/Modules/Compiler/IAR-CXX.cmake +++ b/Modules/Compiler/IAR-CXX.cmake
@@ -14,23 +14,48 @@ message(FATAL_ERROR "Could not detect CMAKE_CXX_COMPILER_VERSION. This should be automatic. Check your product license.\n") endif() -# Whenever needed, override this default behavior using CMAKE_IAR_CXX_FLAG in your toolchain file. +# The CMAKE_IAR_CXX_FLAG variable can be used in your toolchain file +# for overriding the default flag for compiling C++ source files. if(NOT CMAKE_IAR_CXX_FLAG) - set(_CMAKE_IAR_MODERNCXX_LIST 14 17) - if(${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT} IN_LIST _CMAKE_IAR_MODERNCXX_LIST OR - ("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM" AND ${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT} EQUAL 98)) + if((NOT CMAKE_CXX_STANDARD_COMPUTED_DEFAULT EQUAL 98) OR + ("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")) set(CMAKE_IAR_CXX_FLAG --c++) else() set(CMAKE_IAR_CXX_FLAG --eec++) endif() - unset(_CMAKE_IAR_MODERNCXX_LIST) endif() set(CMAKE_CXX_STANDARD_COMPILE_OPTION "") set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e) # Unused after CMP0128 -set(CMAKE_CXX${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT}_STANDARD_COMPILE_OPTION "") -set(CMAKE_CXX${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT}_EXTENSION_COMPILE_OPTION -e) +# Stubs for standard/extended compiler options +if(NOT CMAKE_CXX_STANDARD_COMPUTED_DEFAULT EQUAL 98) + if(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT GREATER_EQUAL 20) + set(CMAKE_CXX20_STANDARD_COMPILE_OPTION --libc++) + set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION --libc++ -e) + endif() + if(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT GREATER_EQUAL 17) + set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION -e) + endif() + if(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT GREATER_EQUAL 14) + set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION -e) + endif() + if(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT GREATER_EQUAL 11) + set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION -e) + endif() + if(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT EQUAL 03) + set(CMAKE_CXX03_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX03_EXTENSION_COMPILE_OPTION -e) + set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -e) + endif() +else() + set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -e) +endif() # Architecture specific if("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") @@ -39,7 +64,7 @@ message(FATAL_ERROR "IAR C++ Compiler for Arm version ${CMAKE_CXX_COMPILER_VERSION} not supported by CMake.") endif() __compiler_iar_ilink(CXX) - __compiler_check_default_language_standard(CXX 5.10 98 8.10 14 8.40 17) + __compiler_check_default_language_standard(CXX 5.10 98 8.10 14 8.40 17 10.10 20) elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") __compiler_iar_ilink(CXX)
diff --git a/Modules/Compiler/IAR-DetermineCompiler.cmake b/Modules/Compiler/IAR-DetermineCompiler.cmake index b03fd1f..45ee8cf 100644 --- a/Modules/Compiler/IAR-DetermineCompiler.cmake +++ b/Modules/Compiler/IAR-DetermineCompiler.cmake
@@ -1,26 +1,38 @@ # IAR C/C++ Compiler (https://www.iar.com) -# CPU <arch> supported in CMake: 8051, Arm, AVR, MSP430, RH850, RISC-V, RL78, RX and V850 # -# IAR C/C++ Compiler for <arch> internal integer symbols used in CMake: +# CPU target architectures (<arch>) supported in CMake: +# 8051, Arm, AVR, MSP430, RH850, RISC-V, RL78, RX, STM8 and V850 +# +# IAR C/C++ Compiler for <arch> internal macros used by CMake: +# +# __IAR_COMPILERBASE__ +# Provides a fine-grained compiler internal version, +# available as integer, and preferred for detection. +# Format: +# <__IAR_SYSTEMS_ICC__+1> & 0x00FF0000 +# <major_version> & 0x0000FF00 +# <minor_version> & 0x000000FF # # __IAR_SYSTEMS_ICC__ -# Provides the compiler internal platform version -# __ICC<arch>__ -# Provides 1 for the current <arch> in use -# __VER__ -# Provides the current version in use -# The semantic version of the compiler is architecture-dependent -# When <arch> is ARM: -# CMAKE_<LANG>_COMPILER_VERSION_MAJOR = (__VER__ / 1E6) -# CMAKE_<LANG>_COMPILER_VERSION_MINOR = (__VER__ / 1E3) % 1E3 -# CMAKE_<LANG>_COMPILER_VERSION_PATCH = (__VER__ % 1E3) -# When <arch> is non-ARM: -# CMAKE_<LANG>_COMPILER_VERSION_MAJOR = (__VER__ / 1E2) -# CMAKE_<LANG>_COMPILER_VERSION_MINOR = (__VER__ - ((__VER__/ 1E2) * 1E2)) -# CMAKE_<LANG>_COMPILER_VERSION_PATCH = (__SUBVERSION__) -# __SUBVERSION__ -# Provides the version's patch level for non-ARM <arch> +# Provides the compiler internal platform version. +# In CMake, it is used as a fallback detection option for legacy compilers, +# when __IAR_COMPILERBASE__ is not available. +# Format: +# <iar_platform_version> & 0x000000FF +# In use: +# (<iar_platform_version> << 16) & 0x00FF0000 # +# __ICC<arch>__ +# Provides 1 for the current <arch> in use. +# +# __VER__ +# Provides the compiler version, which is composed differently +# between Arm and non-Arm target architectures. +# +# __SUBVERSION__ +# Provides the compiler patchlevel version for non-Arm target architectures. +# + set(_compiler_id_pp_test "defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)") set(_compiler_id_version_compute " @@ -28,10 +40,13 @@ # define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@((__VER__) / 1000000) # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(((__VER__) / 1000) % 1000) # define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@((__VER__) % 1000) -# define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__IAR_SYSTEMS_ICC__) # elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__) || defined(__ICCSTM8__)) # define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@((__VER__) / 100) # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@((__VER__) - (((__VER__) / 100)*100)) # define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__SUBVERSION__) -# define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__IAR_SYSTEMS_ICC__) +# endif +# if defined(__IAR_COMPILERBASE__) +# define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__IAR_COMPILERBASE__) +# else +# define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@((__IAR_SYSTEMS_ICC__ << 16)) # endif")
diff --git a/Modules/Compiler/IAR.cmake b/Modules/Compiler/IAR.cmake index 97e1adb..14c6cf2 100644 --- a/Modules/Compiler/IAR.cmake +++ b/Modules/Compiler/IAR.cmake
@@ -23,6 +23,8 @@ string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG") set(CMAKE_${lang}_LINK_MODE LINKER) + + set(CMAKE_${lang}_COMPILE_OPTIONS_WARNING_AS_ERROR "--warnings_are_errors") endif() set(CMAKE_${lang}_OUTPUT_EXTENSION_REPLACE 1)
diff --git a/Modules/Compiler/IBMFlang-Fortran.cmake b/Modules/Compiler/IBMFlang-Fortran.cmake new file mode 100644 index 0000000..b2d4e30 --- /dev/null +++ b/Modules/Compiler/IBMFlang-Fortran.cmake
@@ -0,0 +1,3 @@ +# IBM LLVM Flang Compiler + +include(Compiler/LLVMFlang-Fortran)
diff --git a/Modules/Compiler/LLVMFlang-Fortran.cmake b/Modules/Compiler/LLVMFlang-Fortran.cmake index 636ab07..914e40d 100644 --- a/Modules/Compiler/LLVMFlang-Fortran.cmake +++ b/Modules/Compiler/LLVMFlang-Fortran.cmake
@@ -25,7 +25,26 @@ set(CMAKE_Fortran_LINK_MODE DRIVER) + set(CMAKE_Fortran_COMPILE_OPTIONS_PIC "-fPIC") + set(CMAKE_Fortran_COMPILE_OPTIONS_PIE "-fPIE") + string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " -O0 -g") string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -O2 -g") string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " -O3") endif() + +set(CMAKE_SHARED_LIBRARY_Fortran_FLAGS -fPIC) +set(CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS "-shared") + +# Determine LTO support +set(_CMAKE_Fortran_IPO_SUPPORTED_BY_CMAKE NO) +set(_CMAKE_Fortran_IPO_MAY_BE_SUPPORTED_BY_COMPILER NO) +if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 18.0) + set(_CMAKE_Fortran_IPO_SUPPORTED_BY_CMAKE YES) + set(_CMAKE_Fortran_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES) + if(CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 23.0) + set(CMAKE_Fortran_COMPILE_OPTIONS_IPO "-flto") + else() + set(CMAKE_Fortran_COMPILE_OPTIONS_IPO "-flto=thin") + endif() +endif()
diff --git a/Modules/Compiler/NAG-Fortran.cmake b/Modules/Compiler/NAG-Fortran.cmake index 50b2991..9d12677 100644 --- a/Modules/Compiler/NAG-Fortran.cmake +++ b/Modules/Compiler/NAG-Fortran.cmake
@@ -6,10 +6,11 @@ COMMAND ${CMAKE_Fortran_COMPILER} dummy.o -dryrun OUTPUT_VARIABLE _dryrun ERROR_VARIABLE _dryrun + RESULT_VARIABLE _dryrun_result ) # Match an object file. string(REGEX MATCH "/[^ ]*/[^ /][^ /]*\\.o" _nag_obj "${_dryrun}") - if(_nag_obj) + if(_nag_obj AND _dryrun_result EQUAL 0) # Parse object directory and convert to a regex. string(REGEX REPLACE "/[^/]*$" "" _nag_dir "${_nag_obj}") string(REGEX REPLACE "([][+.*()^])" "\\\\\\1" _nag_regex "${_nag_dir}") @@ -44,7 +45,13 @@ set(CMAKE_Fortran_FORMAT_FREE_FLAG "-free") set(CMAKE_Fortran_COMPILE_OPTIONS_PIC "-PIC") set(CMAKE_Fortran_COMPILE_OPTIONS_PIE "-PIC") -set(CMAKE_Fortran_RESPONSE_FILE_LINK_FLAG "-Wl,@") +if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 6.2) + set(CMAKE_Fortran_RESPONSE_FILE_LINK_FLAG "-xldarg @") + set(CMAKE_Fortran_RESPONSE_FILE_ARCHIVE_FLAG "@") + set(CMAKE_Fortran_USE_RESPONSE_FILE_FOR_LIBRARIES 0) +else() + set(CMAKE_Fortran_RESPONSE_FILE_LINK_FLAG "@") +endif() set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-fpp") set(CMAKE_Fortran_LINK_MODE DRIVER)
diff --git a/Modules/Compiler/NVHPC-C.cmake b/Modules/Compiler/NVHPC-C.cmake index 51a62b6..5f8708d 100644 --- a/Modules/Compiler/NVHPC-C.cmake +++ b/Modules/Compiler/NVHPC-C.cmake
@@ -22,4 +22,10 @@ set(CMAKE_C_DEPENDS_EXTRA_COMMANDS "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -x c -M <SOURCE> -MT <OBJECT> -MD<DEP_FILE>") endif() +if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 26.3) + set(CMAKE_C23_STANDARD_COMPILE_OPTION -std=c23) + set(CMAKE_C23_EXTENSION_COMPILE_OPTION -std=gnu23) + set(CMAKE_C_STANDARD_LATEST 23) +endif() + __compiler_nvhpc(C)
diff --git a/Modules/Compiler/NVHPC-CXX.cmake b/Modules/Compiler/NVHPC-CXX.cmake index 56daffb..5907b29 100644 --- a/Modules/Compiler/NVHPC-CXX.cmake +++ b/Modules/Compiler/NVHPC-CXX.cmake
@@ -29,4 +29,11 @@ set(CMAKE_CXX_STANDARD_LATEST 23) endif() +if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 26.3) + set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION -std=gnu++23) # -std=gnu++23 is now available + set(CMAKE_CXX26_STANDARD_COMPILE_OPTION -std=c++26) + set(CMAKE_CXX26_EXTENSION_COMPILE_OPTION -std=gnu++26) + set(CMAKE_CXX_STANDARD_LATEST 26) +endif() + __compiler_nvhpc(CXX)
diff --git a/Modules/Compiler/NVIDIA.cmake b/Modules/Compiler/NVIDIA.cmake index 0d0f03a..5e27124 100644 --- a/Modules/Compiler/NVIDIA.cmake +++ b/Modules/Compiler/NVIDIA.cmake
@@ -40,12 +40,20 @@ endif() if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 12.0) - if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.11.25505) + if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.12.25835) set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std=c++20") set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std=c++20") set(CMAKE_${lang}_STANDARD_LATEST 20) endif() endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 13.3) + if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.29.30129) + set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std=c++23") + set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std=c++23") + set(CMAKE_${lang}_STANDARD_LATEST 23) + endif() + endif() else() set(CMAKE_${lang}03_STANDARD_COMPILE_OPTION "") set(CMAKE_${lang}03_EXTENSION_COMPILE_OPTION "") @@ -75,6 +83,12 @@ set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std=c++20") set(CMAKE_${lang}_STANDARD_LATEST 20) endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 13.3) + set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std=c++23") + set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std=c++23") + set(CMAKE_${lang}_STANDARD_LATEST 23) + endif() endif() __compiler_check_default_language_standard(${lang} 6.0 03)
diff --git a/Modules/Compiler/OrangeC-C.cmake b/Modules/Compiler/OrangeC-C.cmake index f0dfcd1..edf0da5 100644 --- a/Modules/Compiler/OrangeC-C.cmake +++ b/Modules/Compiler/OrangeC-C.cmake
@@ -17,6 +17,13 @@ set(CMAKE_C_STANDARD_LATEST 11) +if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "6.0.71") + set(CMAKE_C23_STANDARD_COMPILE_OPTION -2) + set(CMAKE_C23_EXTENSION_COMPILE_OPTION -2) + + set(CMAKE_C_STANDARD_LATEST 23) +endif() + __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 index 575c09d..4827d7f 100644 --- a/Modules/Compiler/OrangeC-CXX.cmake +++ b/Modules/Compiler/OrangeC-CXX.cmake
@@ -22,6 +22,14 @@ set(CMAKE_CXX_STANDARD_LATEST 14) +if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0") + set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") + set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=c++17") + set(CMAKE_CXX17_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_CXX_STANDARD_LATEST 17) +endif() + __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.cmake b/Modules/Compiler/OrangeC.cmake index 69628b8..27f0c8d 100644 --- a/Modules/Compiler/OrangeC.cmake +++ b/Modules/Compiler/OrangeC.cmake
@@ -22,13 +22,14 @@ 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> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES>") + set(CMAKE_${lang}_LINK_EXECUTABLE + "<CMAKE_${lang}_COMPILER> -! <FLAGS> -o <TARGET> --out-implib <TARGET_IMPLIB> <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> <OBJECTS> <LINK_LIBRARIES>") + "<CMAKE_${lang}_COMPILER> -! <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> -o <TARGET> --out-implib <TARGET_IMPLIB> <LINK_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_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared") set(CMAKE_${lang}_RESPONSE_FILE_FLAG "@") set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "@")
diff --git a/Modules/Compiler/PellesC-ASM_POASM.cmake b/Modules/Compiler/PellesC-ASM_POASM.cmake new file mode 100644 index 0000000..93091a6 --- /dev/null +++ b/Modules/Compiler/PellesC-ASM_POASM.cmake
@@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include (Compiler/PellesC) +__compiler_pellesc(ASM_POASM)
diff --git a/Modules/Compiler/PellesC-C.cmake b/Modules/Compiler/PellesC-C.cmake new file mode 100644 index 0000000..4cf97a9 --- /dev/null +++ b/Modules/Compiler/PellesC-C.cmake
@@ -0,0 +1,30 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include (Compiler/PellesC) +__compiler_pellesc(C) + +set(CMAKE_C_OUTPUT_EXTENSION ".obj") +set(CMAKE_C_VERBOSE_FLAG "-V2") + +include (Compiler/CMakeCommonCompilerMacros) + +set(CMAKE_C90_STANDARD_COMPILE_OPTION "") # No C90-only mode. +set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_C99_STANDARD_COMPILE_OPTION -std=c99) +set(CMAKE_C99_EXTENSION_COMPILE_OPTION -std=c99 -Zx) +set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_C11_STANDARD_COMPILE_OPTION -std=c11) +set(CMAKE_C11_EXTENSION_COMPILE_OPTION -std=c11 -Zx) +set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_C17_STANDARD_COMPILE_OPTION -std=c17) +set(CMAKE_C17_EXTENSION_COMPILE_OPTION -std=c17 -Zx) +set(CMAKE_C_STANDARD_LATEST 17) + +if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12) + set(CMAKE_C23_STANDARD_COMPILE_OPTION -std=c23) + set(CMAKE_C23_EXTENSION_COMPILE_OPTION -std=c23 -Zx) + set(CMAKE_C_STANDARD_LATEST 23) +endif() + +__compiler_check_default_language_standard(C 9.0 17 12.0 23)
diff --git a/Modules/Compiler/PellesC-DetermineCompiler.cmake b/Modules/Compiler/PellesC-DetermineCompiler.cmake new file mode 100644 index 0000000..c53e5e6 --- /dev/null +++ b/Modules/Compiler/PellesC-DetermineCompiler.cmake
@@ -0,0 +1,8 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +set(_compiler_id_pp_test "defined(__POCC__)") + +set(_compiler_id_version_compute " +# define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__POCC__/100) +# define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__POCC__%100)")
diff --git a/Modules/Compiler/PellesC.cmake b/Modules/Compiler/PellesC.cmake new file mode 100644 index 0000000..c53162b --- /dev/null +++ b/Modules/Compiler/PellesC.cmake
@@ -0,0 +1,7 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. +include_guard() + +macro(__compiler_pellesc lang) + set(CMAKE_${lang}_LINK_MODE LINKER) +endmacro ()
diff --git a/Modules/Dart.cmake b/Modules/Dart.cmake index e7967a0..93a9381 100644 --- a/Modules/Dart.cmake +++ b/Modules/Dart.cmake
@@ -43,8 +43,7 @@ # which will not warn. Warn again just in case. cmake_policy(GET CMP0145 cmp0145) if(cmp0145 STREQUAL "") - cmake_policy(GET_WARNING CMP0145 _cmp0145_warning) - message(AUTHOR_WARNING "${_cmp0145_warning}") + cmake_policy(ISSUE_WARNING CMP0145) endif() option(BUILD_TESTING "Build the testing tree." ON)
diff --git a/Modules/DartConfiguration.tcl.in b/Modules/DartConfiguration.tcl.in index 67d9f8f..2931583 100644 --- a/Modules/DartConfiguration.tcl.in +++ b/Modules/DartConfiguration.tcl.in
@@ -107,3 +107,8 @@ # specify behavior for retrying the submission CTestSubmitRetryDelay: @CTEST_SUBMIT_RETRY_DELAY@ CTestSubmitRetryCount: @CTEST_SUBMIT_RETRY_COUNT@ + +# Invoke each test with environment variables configuring tool's collection. +CTestTestCoverageTool: @CTEST_TEST_COVERAGE_TOOL@ +CTestTestCoverageMergeExecutable: @CTEST_TEST_COVERAGE_MERGE_EXECUTABLE@ +CTestTestCoverageDataExecutable: @CTEST_TEST_COVERAGE_DATA_EXECUTABLE@
diff --git a/Modules/Documentation.cmake b/Modules/Documentation.cmake index bd30068..fdde7d7 100644 --- a/Modules/Documentation.cmake +++ b/Modules/Documentation.cmake
@@ -26,9 +26,7 @@ # Ignore the warning if the project is detected as VTK itself. if (NOT CMAKE_PROJECT_NAME STREQUAL "VTK" AND NOT PROJECT_NAME STREQUAL "VTK") - cmake_policy(GET_WARNING CMP0106 _Documentation_policy_warning) - message(AUTHOR_WARNING - "${_Documentation_policy_warning}\n" + cmake_policy(ISSUE_WARNING CMP0106 POST "Documentation.cmake is VTK-specific code and should not be used in " "non-VTK projects. This logic in this module is best shipped with the " "project using it rather than with CMake.")
diff --git a/Modules/ExternalData.cmake b/Modules/ExternalData.cmake index 39266e3..e70ba33 100644 --- a/Modules/ExternalData.cmake +++ b/Modules/ExternalData.cmake
@@ -169,6 +169,46 @@ some platforms. The ``ExternalData_NO_SYMLINKS`` variable may be set to disable use of symbolic links and enable use of copies instead. +.. variable:: ExternalData_STATE_ROOT + + .. versionadded:: 4.4 + + The ``ExternalData_STATE_ROOT`` variable may be set to place module-managed + metadata outside ``ExternalData_BINARY_ROOT``. When set, ``ExternalData`` + stores its hash records under this directory while still materializing real + data files under ``ExternalData_BINARY_ROOT``. By default, hash records + continue to be placed next to the materialized data. + +.. variable:: ExternalData_LINK_MODE + + .. versionadded:: 4.4 + + The ``ExternalData_LINK_MODE`` variable selects how real data files are + exposed under ``ExternalData_BINARY_ROOT``. + + The value may be one of: + + ``auto`` + Try each of the supported modes until one succeeds. + The default order is ``hardlink -> symlink -> copy`` on Windows + and ``symlink -> hardlink -> copy`` on other platforms. + + ``hardlink`` + Materialize paths as hard links to the real files. + + ``symlink`` + Materialize paths as symbolic links to the real files. + + ``copy`` + Materialize paths as copies of the real files. + + Or, the value may be a :ref:`semicolon-separated list <CMake Language Lists>` + of ``hardlink``, ``symlink``, and ``copy`` to try the modes in the order + specified until one succeeds. + + When ``ExternalData_NO_SYMLINKS`` is set, the ``symlink`` mode is removed + from automatic selection and cannot be requested explicitly. + .. variable:: ExternalData_OBJECT_STORES The ``ExternalData_OBJECT_STORES`` variable may be set to a list of local @@ -540,7 +580,7 @@ list(GET tuple 2 exts) string(REPLACE "+" ";" exts_list "${exts}") list(GET exts_list 0 first_ext) - set(stamp "-hash-stamp") + _ExternalData_compute_stamp_file("${file}" stamp_file) if(NOT DEFINED "_ExternalData_FILE_${file}") set("_ExternalData_FILE_${file}" 1) get_property(added DIRECTORY PROPERTY "_ExternalData_FILE_${file}") @@ -553,10 +593,11 @@ # List the real file as a second output in case it is a broken link. # The files must be listed in this order so CMake can hide from the # make tool that a symlink target may not be newer than the input. - OUTPUT "${file}${stamp}" "${file}" + OUTPUT "${stamp_file}" "${file}" # Run the data fetch/update script. COMMAND ${CMAKE_COMMAND} -Drelative_top=${CMAKE_BINARY_DIR} -Dfile=${file} -Dname=${name} -Dexts=${exts} + -Dstamp_file=${stamp_file} -DExternalData_ACTION=fetch -DExternalData_SHOW_PROGRESS=${_ExternalData_add_target_SHOW_PROGRESS} -DExternalData_CONFIG=${config} @@ -565,7 +606,7 @@ MAIN_DEPENDENCY "${name}${first_ext}" ) endif() - list(APPEND files "${file}${stamp}") + list(APPEND files "${stamp_file}") endif() endforeach() @@ -635,12 +676,40 @@ endfunction() function(_ExternalData_atomic_write file content) + get_filename_component(dir "${file}" DIRECTORY) + file(MAKE_DIRECTORY "${dir}") _ExternalData_random(random) set(tmp "${file}.tmp${random}") file(WRITE "${tmp}" "${content}") file(RENAME "${tmp}" "${file}") endfunction() +function(_ExternalData_compute_stamp_file file var_stamp) + if(DEFINED ExternalData_STATE_ROOT AND NOT "${ExternalData_STATE_ROOT}" STREQUAL "") + _ExternalData_compute_state_relative_path("${file}" relfile) + set(stamp_file "${ExternalData_STATE_ROOT}/${relfile}-hash-stamp") + else() + set(stamp_file "${file}-hash-stamp") + endif() + set(${var_stamp} "${stamp_file}" PARENT_SCOPE) +endfunction() + +function(_ExternalData_compute_state_relative_path file var_relfile) + if(NOT ExternalData_BINARY_ROOT) + set(top_bin "${CMAKE_BINARY_DIR}") + else() + set(top_bin "${ExternalData_BINARY_ROOT}") + endif() + file(RELATIVE_PATH relfile "${top_bin}" "${file}") + if(IS_ABSOLUTE "${relfile}" OR "${relfile}" MATCHES "^\\.\\./") + message(FATAL_ERROR + "File path is not under ExternalData_BINARY_ROOT:\n" + " file = ${file}\n" + " ExternalData_BINARY_ROOT = ${top_bin}") + endif() + set(${var_relfile} "${relfile}" PARENT_SCOPE) +endfunction() + function(_ExternalData_link_content name var_ext) if("${ExternalData_LINK_CONTENT}" MATCHES "^(${_ExternalData_REGEX_ALGO})$") set(algo "${ExternalData_LINK_CONTENT}") @@ -970,33 +1039,103 @@ "Neither ExternalData_URL_TEMPLATES nor ExternalData_OBJECT_STORES is set!") endif() +function(_ExternalData_compute_link_target src dst var_tgt) + get_filename_component(dst_dir "${dst}" PATH) + set(tgt "${src}") + if(relative_top) + # Use relative path if files are close enough. + file(RELATIVE_PATH relsrc "${relative_top}" "${src}") + file(RELATIVE_PATH reldst "${relative_top}" "${dst}") + if(NOT IS_ABSOLUTE "${relsrc}" AND NOT "${relsrc}" MATCHES "^\\.\\./" AND + NOT IS_ABSOLUTE "${reldst}" AND NOT "${reldst}" MATCHES "^\\.\\./") + file(RELATIVE_PATH tgt "${dst_dir}" "${src}") + endif() + endif() + set(${var_tgt} "${tgt}" PARENT_SCOPE) +endfunction() + +function(_ExternalData_try_link_mode mode src dst var_result) + if(mode STREQUAL "copy") + file(COPY_FILE "${src}" "${dst}" RESULT result INPUT_MAY_BE_RECENT) + elseif(mode STREQUAL "hardlink") + file(CREATE_LINK "${src}" "${dst}" RESULT result) + elseif(mode STREQUAL "symlink") + _ExternalData_compute_link_target("${src}" "${dst}" tgt) + file(CREATE_LINK "${tgt}" "${dst}" RESULT result SYMBOLIC) + else() + set(result "Unsupported ExternalData_LINK_MODE `${mode}`") + endif() + set(${var_result} "${result}" PARENT_SCOPE) +endfunction() + +function(_ExternalData_get_auto_link_modes var_modes) + if(CMAKE_HOST_WIN32) + set(modes hardlink) + if(NOT ExternalData_NO_SYMLINKS) + list(APPEND modes symlink) + endif() + list(APPEND modes copy) + else() + if(NOT ExternalData_NO_SYMLINKS) + list(APPEND modes symlink) + endif() + list(APPEND modes hardlink copy) + endif() + set(${var_modes} "${modes}" PARENT_SCOPE) +endfunction() + +function(_ExternalData_get_requested_link_modes var_modes) + if(DEFINED ExternalData_LINK_MODE AND NOT "${ExternalData_LINK_MODE}" STREQUAL "") + set(requested_modes "${ExternalData_LINK_MODE}") + else() + set(requested_modes auto) + endif() + string(TOLOWER "${requested_modes}" requested_modes_lower) + + if(requested_modes_lower STREQUAL "auto") + _ExternalData_get_auto_link_modes(modes) + else() + set(modes) + foreach(mode IN LISTS requested_modes) + string(TOLOWER "${mode}" mode_lower) + if(mode_lower STREQUAL "copy" OR mode_lower STREQUAL "hardlink") + list(APPEND modes "${mode_lower}") + elseif(mode_lower STREQUAL "symlink") + if(ExternalData_NO_SYMLINKS) + message(FATAL_ERROR + "ExternalData_LINK_MODE with `symlink` conflicts with NO_SYMLINKS") + endif() + list(APPEND modes symlink) + else() + message(FATAL_ERROR + "ExternalData_LINK_MODE must be `auto` or list: hardlink, symlink, copy") + endif() + endforeach() + endif() + + set(${var_modes} "${modes}" PARENT_SCOPE) +endfunction() + function(_ExternalData_link_or_copy src dst) # Create a temporary file first. get_filename_component(dst_dir "${dst}" PATH) file(MAKE_DIRECTORY "${dst_dir}") _ExternalData_random(random) set(tmp "${dst}.tmp${random}") - if(UNIX AND NOT ExternalData_NO_SYMLINKS) - # Create a symbolic link. - set(tgt "${src}") - if(relative_top) - # Use relative path if files are close enough. - file(RELATIVE_PATH relsrc "${relative_top}" "${src}") - file(RELATIVE_PATH relfile "${relative_top}" "${dst}") - if(NOT IS_ABSOLUTE "${relsrc}" AND NOT "${relsrc}" MATCHES "^\\.\\./" AND - NOT IS_ABSOLUTE "${reldst}" AND NOT "${reldst}" MATCHES "^\\.\\./") - file(RELATIVE_PATH tgt "${dst_dir}" "${src}") - endif() + _ExternalData_get_requested_link_modes(_ExternalData_link_modes) + + unset(result) + foreach(_ExternalData_mode IN LISTS _ExternalData_link_modes) + _ExternalData_try_link_mode("${_ExternalData_mode}" "${src}" "${tmp}" result) + if(NOT result) + break() endif() - # Create link (falling back to copying if there's a problem). - file(CREATE_LINK "${tgt}" "${tmp}" RESULT result COPY_ON_ERROR SYMBOLIC) - else() - # Create a copy. - file(COPY_FILE "${src}" "${tmp}" RESULT result INPUT_MAY_BE_RECENT) - endif() + file(REMOVE "${tmp}") + endforeach() + if(result) file(REMOVE "${tmp}") - message(FATAL_ERROR "Failed to create:\n \"${tmp}\"\nfrom:\n \"${obj}\"\nwith error:\n ${result}") + message(FATAL_ERROR "Failed to create:\n \"${tmp}\"\nfrom:\n \"${src}\"\nwith error:\n ${result}") endif() # Atomically create/replace the real destination. @@ -1234,10 +1373,16 @@ message(FATAL_ERROR "${errorMsg}") endif() # Check if file already corresponds to the object. - set(stamp "-hash-stamp") + if(NOT DEFINED stamp_file OR "${stamp_file}" STREQUAL "") + if(DEFINED ExternalData_STATE_ROOT AND NOT "${ExternalData_STATE_ROOT}" STREQUAL "") + message(FATAL_ERROR + "stamp_file must be provided when ExternalData_STATE_ROOT is set") + endif() + set(stamp_file "${file}-hash-stamp") + endif() set(file_up_to_date 0) - if(EXISTS "${file}" AND EXISTS "${file}${stamp}") - file(READ "${file}${stamp}" f_hash) + if(EXISTS "${file}" AND EXISTS "${stamp_file}") + file(READ "${stamp_file}" f_hash) string(STRIP "${f_hash}" f_hash) if("${f_hash}" STREQUAL "${hash}") set(file_up_to_date 1) @@ -1252,7 +1397,7 @@ endif() # Atomically update the hash/timestamp file to record the object referenced. - _ExternalData_atomic_write("${file}${stamp}" "${hash}\n") + _ExternalData_atomic_write("${stamp_file}" "${hash}\n") elseif("${ExternalData_ACTION}" STREQUAL "local") foreach(v file name) if(NOT DEFINED "${v}")
diff --git a/Modules/ExternalData_config.cmake.in b/Modules/ExternalData_config.cmake.in index 18be6b3..69cf0f5 100644 --- a/Modules/ExternalData_config.cmake.in +++ b/Modules/ExternalData_config.cmake.in
@@ -3,4 +3,6 @@ set(ExternalData_TIMEOUT_INACTIVITY "@ExternalData_TIMEOUT_INACTIVITY@") set(ExternalData_TIMEOUT_ABSOLUTE "@ExternalData_TIMEOUT_ABSOLUTE@") set(ExternalData_NO_SYMLINKS "@ExternalData_NO_SYMLINKS@") +set(ExternalData_STATE_ROOT "@ExternalData_STATE_ROOT@") +set(ExternalData_LINK_MODE "@ExternalData_LINK_MODE@") @_ExternalData_CONFIG_CODE@
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 6cd0a8a..c02fdbe 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake
@@ -446,6 +446,31 @@ unintended changes or failed builds resulting from conflicts during rebase operations. +The following variables can be set to control the retry behavior for +``git clone`` operations: + +.. variable:: CMAKE_EP_GIT_CLONE_RETRY_COUNT + + .. versionadded:: 4.4 + + Specifies the number of times to retry a ``git clone`` operation if it + fails. The default is ``2`` retries. The first attempt is not counted + as a retry, so the total number of attempts is the retry count plus one. + This variable should not be set by a project, it is intended for the user + to set. It is primarily useful when dealing with intermittent network + issues or rate limiting from git hosting services. + +.. variable:: CMAKE_EP_GIT_CLONE_RETRY_DELAY + + .. versionadded:: 4.4 + + Specifies the delay in seconds between retry attempts for git clone + operations. The default is ``0`` seconds (no delay between retries). + This variable should not be set by a project, it is intended for the + user to set. Setting a delay can be helpful when dealing with rate + limiting from git hosting services, where immediate retries would + also be rejected. + Subversion ~~~~~~~~~~ @@ -2128,8 +2153,7 @@ ) endif() elseif(cmp0114 STREQUAL "") - cmake_policy(GET_WARNING CMP0114 _cmp0114_warning) - string(APPEND _cmp0114_warning "\n" + string(JOIN "" _cmp0114_warning "ExternalProject target '${name}' would depend on the targets for " "step(s) '${steps}' under policy CMP0114, but this is being left out " "for compatibility since the policy is not set." @@ -2139,7 +2163,7 @@ "Also, the NO_DEPENDS option is deprecated in favor of policy CMP0114." ) endif() - message(AUTHOR_WARNING "${_cmp0114_warning}") + cmake_policy(ISSUE_WARNING CMP0114 POST "${_cmp0114_warning}") endif() foreach(step IN LISTS steps) _ep_step_add_target("${name}" "${step}" "${no_deps}") @@ -2482,14 +2506,11 @@ set_property(TARGET ${name} PROPERTY _EP_CMP0114_WARNED_INDEPENDENT_STEP_TARGETS 1 ) - cmake_policy(GET_WARNING CMP0114 _cmp0114_warning) - string(APPEND _cmp0114_warning - "\n" + cmake_policy(ISSUE_WARNING CMP0114 POST "ExternalProject '${name}' option INDEPENDENT_STEP_TARGETS is set to" "\n ${independent_step_targets}\n" "but the option is deprecated in favor of policy CMP0114." ) - message(AUTHOR_WARNING "${_cmp0114_warning}") endif() endif() foreach(st IN LISTS independent_step_targets)
diff --git a/Modules/ExternalProject/gitclone.cmake.in b/Modules/ExternalProject/gitclone.cmake.in index 449bcb3..19a7eb4 100644 --- a/Modules/ExternalProject/gitclone.cmake.in +++ b/Modules/ExternalProject/gitclone.cmake.in
@@ -30,10 +30,15 @@ message(FATAL_ERROR "Failed to remove directory: '@source_dir@'") endif() -# try the clone 3 times in case there is an odd git clone issue +# try the clone 1 + N times in case there is an odd git clone issue set(error_code 1) set(number_of_tries 0) -while(error_code AND number_of_tries LESS 3) +math(EXPR max_tries "1 + @git_clone_retries@") +while(error_code AND number_of_tries LESS ${max_tries}) + if(number_of_tries GREATER 0 AND @git_clone_retry_delay@ GREATER 0) + message(STATUS "Retry #${number_of_tries}, waiting @git_clone_retry_delay@ seconds before next attempt...") + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep @git_clone_retry_delay@) + endif() execute_process( COMMAND "@git_EXECUTABLE@" clone @git_clone_options@ "@git_repository@" "@src_name@" @@ -47,7 +52,7 @@ message(NOTICE "Had to git clone more than once: ${number_of_tries} times.") endif() if(error_code) - message(FATAL_ERROR "Failed to clone repository: '@git_repository@'") + message(FATAL_ERROR "Failed to clone repository:\n '@git_repository@'") endif() execute_process(
diff --git a/Modules/ExternalProject/shared_internal_commands.cmake b/Modules/ExternalProject/shared_internal_commands.cmake index b7077ef..1ee0153 100644 --- a/Modules/ExternalProject/shared_internal_commands.cmake +++ b/Modules/ExternalProject/shared_internal_commands.cmake
@@ -13,6 +13,7 @@ OUTPUT_VARIABLE git_symbolic_ref OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET + RESULT_VARIABLE _result_git_symbolic_ref ) if(NOT git_symbolic_ref STREQUAL "") @@ -24,6 +25,7 @@ OUTPUT_VARIABLE git_remote_name OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET + RESULT_VARIABLE _result_git_branch_remote ) endif() @@ -36,6 +38,7 @@ OUTPUT_VARIABLE git_remote_list OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET + RESULT_VARIABLE _result_git_remote_list ) string(REPLACE "\n" ";" git_remote_list "${git_remote_list}") list(LENGTH git_remote_list git_remote_list_length) @@ -70,6 +73,7 @@ OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL LAST ENCODING UTF-8 # Needed to handle non-ascii characters in local paths + RESULT_VARIABLE _result_git_remote_url ) set("${output_variable}" "${git_remote_url}" PARENT_SCOPE) @@ -171,9 +175,7 @@ set("${output_variable}" "${_resolved_git_remote_url}" PARENT_SCOPE) return() elseif(cmp0150 STREQUAL "") - cmake_policy(GET_WARNING CMP0150 _cmp0150_warning) - message(AUTHOR_WARNING - "${_cmp0150_warning}\n" + cmake_policy(ISSUE_WARNING CMP0150 POST "A relative GIT_REPOSITORY path was detected. " "This will be interpreted as a local path to where the project is being cloned. " "Set GIT_REPOSITORY to an absolute path or set policy CMP0150 to NEW to avoid " @@ -397,6 +399,8 @@ gitclone_stampfile tls_version tls_verify + git_clone_retries + git_clone_retry_delay ) if(NOT Git_VERSION VERSION_LESS 1.8.5) @@ -979,6 +983,16 @@ get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) + # Get retry configuration from variables with defaults + set(git_clone_retries "${CMAKE_EP_GIT_CLONE_RETRY_COUNT}") + if("${git_clone_retries}" STREQUAL "") + set(git_clone_retries 2) + endif() + set(git_clone_retry_delay "${CMAKE_EP_GIT_CLONE_RETRY_DELAY}") + if("${git_clone_retry_delay}" STREQUAL "") + set(git_clone_retry_delay 0) + endif() + # Since git clone doesn't succeed if the non-empty source_dir exists, # create a cmake script to invoke as download command. # The script will delete the source directory and then call git clone. @@ -1003,6 +1017,8 @@ ${stamp_dir}/${name}-gitclone-lastrun.txt "${tls_version}" "${tls_verify}" + "${git_clone_retries}" + "${git_clone_retry_delay}" ) set(comment "Performing download step (git clone) for '${name}'") set(cmd ${CMAKE_COMMAND}
diff --git a/Modules/FeatureSummary.cmake b/Modules/FeatureSummary.cmake index 6b83903..9c41224 100644 --- a/Modules/FeatureSummary.cmake +++ b/Modules/FeatureSummary.cmake
@@ -966,8 +966,7 @@ set_property(GLOBAL PROPERTY _CMAKE_${_name}_DESCRIPTION "${_desc}" ) if("x${_CDO_CMP0183}x" STREQUAL "xx" AND "x${_depends}x" MATCHES "[^A-Za-z0-9_.; ]") - cmake_policy(GET_WARNING CMP0183 _CDO_CMP0183_WARNING) - message(AUTHOR_WARNING "${_CDO_CMP0183_WARNING}") + cmake_policy(ISSUE_WARNING CMP0183) endif() unset(_CDO_CMP0183) endfunction()
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake index 80152f6..6cec11a 100644 --- a/Modules/FetchContent.cmake +++ b/Modules/FetchContent.cmake
@@ -258,7 +258,7 @@ :command:`FetchContent_Declare` for each dependency, and the first such call will control how that dependency will be made available, as described below. - If ``<lowercaseName>_SOURCE_DIR`` is not set: + If ``FETCHCONTENT_SOURCE_DIR_<uppercaseName>`` is not set: * .. versionadded:: 3.24 @@ -1866,6 +1866,8 @@ set(__FETCHCONTENT_CACHED_INFO "") set(__passthrough_vars CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY + CMAKE_EP_GIT_CLONE_RETRY_COUNT + CMAKE_EP_GIT_CLONE_RETRY_DELAY CMAKE_TLS_VERSION CMAKE_TLS_VERIFY CMAKE_TLS_CAINFO
diff --git a/Modules/FindArmadillo.cmake b/Modules/FindArmadillo.cmake index 901ea22..861540b 100644 --- a/Modules/FindArmadillo.cmake +++ b/Modules/FindArmadillo.cmake
@@ -133,7 +133,7 @@ set(Armadillo_VERSION_NAME "EARLY RELEASE") if(EXISTS "${ARMADILLO_INCLUDE_DIR}/armadillo_bits/arma_version.hpp") - # Read and parse armdillo version header file for version number + # Read and parse Armadillo version header file for version number. file(STRINGS "${ARMADILLO_INCLUDE_DIR}/armadillo_bits/arma_version.hpp" _ARMA_HEADER_CONTENTS REGEX "#define ARMA_VERSION_[A-Z]+ ") string(REGEX REPLACE ".*#define ARMA_VERSION_MAJOR ([0-9]+).*" "\\1" ARMADILLO_VERSION_MAJOR "${_ARMA_HEADER_CONTENTS}") string(REGEX REPLACE ".*#define ARMA_VERSION_MINOR ([0-9]+).*" "\\1" ARMADILLO_VERSION_MINOR "${_ARMA_HEADER_CONTENTS}") @@ -176,8 +176,6 @@ ) mark_as_advanced(ARMADILLO_LIBRARY) set(_ARMA_REQUIRED_VARS ARMADILLO_LIBRARY) -else() - set(ARMADILLO_LIBRARY "") endif() # Transitive linking with the wrapper does not work with MSVC,
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake index df36f9a..20ca4a4 100644 --- a/Modules/FindCUDAToolkit.cmake +++ b/Modules/FindCUDAToolkit.cmake
@@ -138,6 +138,7 @@ - `nvtx3`_ - `OpenCL`_ - `cuLIBOS`_ +- `cufilt`_ CUDA Runtime Library """""""""""""""""""" @@ -537,6 +538,36 @@ - ``CUDA::bin2c`` +.. _`FindCUDAToolkit_sanitizer`: + +compute-sanitizer +""""""""""""""""" + +.. versionadded:: 4.4 + +The `NVIDIA Compute Sanitizer`_ library, which allows the tracing of CUDA +runtime and driver calls. + +Target Created: + +- ``CUDA::sanitizer`` + +.. _`NVIDIA Compute Sanitizer`: https://docs.nvidia.com/compute-sanitizer + +cufilt +"""""" + +.. versionadded:: 4.4 + +The `cu++filt`_ binary utility also provides a static library for demangling +CUDA C++ symbols. + +Targets Created: + +- ``CUDA::cufilt`` starting in CUDA 11.4 + +.. _`cu++filt`: https://docs.nvidia.com/cuda/cuda-binary-utilities/index.html#cu-filt + Result Variables ^^^^^^^^^^^^^^^^ @@ -1006,7 +1037,9 @@ endif() elseif(CUDAToolkit_NVCC_EXECUTABLE) # Compute the version by invoking nvcc - execute_process(COMMAND ${CUDAToolkit_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE NVCC_OUT) + execute_process(COMMAND ${CUDAToolkit_NVCC_EXECUTABLE} "--version" + OUTPUT_VARIABLE NVCC_OUT + RESULT_VARIABLE _nvcc_version_result) if(NVCC_OUT MATCHES [=[ V([0-9]+)\.([0-9]+)\.([0-9]+)]=]) set(CUDAToolkit_VERSION_MAJOR "${CMAKE_MATCH_1}") set(CUDAToolkit_VERSION_MINOR "${CMAKE_MATCH_2}") @@ -1020,19 +1053,24 @@ endif() endif() -# Figure out the target directory when either crosscompiling +# Figure out the target processor which is required to determine +# our target dir ( linux ) or our lib dir ( windows ) +# +# When a language is enabled we can use its compiler's target architecture. +if(CMAKE_CUDA_COMPILER_LOADED AND CMAKE_CUDA_COMPILER_ARCHITECTURE_ID) + set(_CUDA_TARGET_PROCESSOR "${CMAKE_CUDA_COMPILER_ARCHITECTURE_ID}") +elseif(CMAKE_CXX_COMPILER_LOADED AND CMAKE_CXX_COMPILER_ARCHITECTURE_ID) + set(_CUDA_TARGET_PROCESSOR "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}") +elseif(CMAKE_C_COMPILER_LOADED AND CMAKE_C_COMPILER_ARCHITECTURE_ID) + set(_CUDA_TARGET_PROCESSOR "${CMAKE_C_COMPILER_ARCHITECTURE_ID}") +elseif(CMAKE_SYSTEM_PROCESSOR) + set(_CUDA_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}") +endif() + +# Figure out our crosscompiling targets dir # or if we don't have `nvcc` and need to deduce the target arch if(CMAKE_CROSSCOMPILING OR NOT CUDAToolkit_NVCC_EXECUTABLE) - # When a language is enabled we can use its compiler's target architecture. - if(CMAKE_CUDA_COMPILER_LOADED AND CMAKE_CUDA_COMPILER_ARCHITECTURE_ID) - set(_CUDA_TARGET_PROCESSOR "${CMAKE_CUDA_COMPILER_ARCHITECTURE_ID}") - elseif(CMAKE_CXX_COMPILER_LOADED AND CMAKE_CXX_COMPILER_ARCHITECTURE_ID) - set(_CUDA_TARGET_PROCESSOR "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}") - elseif(CMAKE_C_COMPILER_LOADED AND CMAKE_C_COMPILER_ARCHITECTURE_ID) - set(_CUDA_TARGET_PROCESSOR "${CMAKE_C_COMPILER_ARCHITECTURE_ID}") - elseif(CMAKE_SYSTEM_PROCESSOR) - set(_CUDA_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}") - elseif(CMAKE_CROSSCOMPILING) + if(CMAKE_CROSSCOMPILING AND NOT _CUDA_TARGET_PROCESSOR) message(FATAL_ERROR "Cross-compiling with the CUDA toolkit requires CMAKE_SYSTEM_PROCESSOR to be set.") endif() # Keep in sync with equivalent table in CMakeDetermineCUDACompiler and FindCUDA! @@ -1052,7 +1090,6 @@ elseif(_CUDA_TARGET_PROCESSOR STREQUAL "x86_64") set(CUDAToolkit_TARGET_NAMES "x86_64-linux") endif() - unset(_CUDA_TARGET_PROCESSOR) foreach(CUDAToolkit_TARGET_NAME IN LISTS CUDAToolkit_TARGET_NAMES) if(EXISTS "${CUDAToolkit_ROOT_DIR}/targets/${CUDAToolkit_TARGET_NAME}") @@ -1077,12 +1114,20 @@ # Determine windows search path suffix for libraries if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") + if(_CUDA_TARGET_PROCESSOR STREQUAL "AMD64" OR + _CUDA_TARGET_PROCESSOR STREQUAL "x64" OR + _CUDA_TARGET_PROCESSOR STREQUAL "ARM64EC" # Can link x64, but not arm64. + ) set(_CUDAToolkit_win_search_dirs lib/x64) set(_CUDAToolkit_win_stub_search_dirs lib/x64/stubs) + elseif(_CUDA_TARGET_PROCESSOR STREQUAL "ARM64") + set(_CUDAToolkit_win_search_dirs lib/arm64) + set(_CUDAToolkit_win_stub_search_dirs lib/arm64/stubs) endif() endif() +unset(_CUDA_TARGET_PROCESSOR) + # We don't need to verify the cuda_runtime header when we are using `nvcc` include paths # as the compiler being enabled means the header was found if(NOT CUDAToolkit_INCLUDE_DIRECTORIES) @@ -1212,12 +1257,22 @@ unset(CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES) unset(CUDAToolkit_INCLUDE_DIRECTORIES) +# CUDAToolkit_LIBRARY_ROOT is accidentally set to the target directory in some environments +# when the CUDA language is enabled, so patch it out +if(CUDAToolkit_LIBRARY_ROOT MATCHES "^(.*)/targets/([^/]*)$") + set(CUDAToolkit_LIBRARY_ROOT "${CMAKE_MATCH_1}") +endif() + #----------------------------------------------------------------------------- # Construct import targets if(CUDAToolkit_FOUND) function(_CUDAToolkit_find_and_add_import_lib lib_name) - cmake_parse_arguments(arg "" "" "ALT;DEPS;EXTRA_PATH_SUFFIXES;EXTRA_INCLUDE_DIRS;ONLY_SEARCH_FOR" ${ARGN}) + cmake_parse_arguments(arg "" "" "ALT;DEPS;EXTRA_PATH_SUFFIXES;EXTRA_INCLUDE_DIRS;ONLY_SEARCH_FOR;LIBRARY_SEARCH_DIRS" ${ARGN}) + + if(NOT arg_LIBRARY_SEARCH_DIRS) + set(arg_LIBRARY_SEARCH_DIRS "${CUDAToolkit_LIBRARY_SEARCH_DIRS}") + endif() if(arg_ONLY_SEARCH_FOR) set(search_names ${arg_ONLY_SEARCH_FOR}) @@ -1227,7 +1282,7 @@ find_library(CUDA_${lib_name}_LIBRARY NAMES ${search_names} - HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS} + HINTS ${arg_LIBRARY_SEARCH_DIRS} ENV CUDA_PATH PATH_SUFFIXES nvidia/current lib64 ${_CUDAToolkit_win_search_dirs} lib # Support NVHPC splayed math library layout @@ -1242,7 +1297,7 @@ if(NOT CUDA_${lib_name}_LIBRARY) find_library(CUDA_${lib_name}_LIBRARY NAMES ${search_names} - HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS} + HINTS ${arg_LIBRARY_SEARCH_DIRS} ENV CUDA_PATH PATH_SUFFIXES lib64/stubs ${_CUDAToolkit_win_stub_search_dirs} lib/stubs stubs ) @@ -1489,7 +1544,7 @@ NAMES nvToolsExt64_1 nvToolsExt64 nvToolsExt PATHS ENV NVTOOLSEXT_PATH ENV CUDA_PATH - PATH_SUFFIXES lib/x64 lib + PATH_SUFFIXES ${_CUDAToolkit_win_search_dirs} lib ) endif() _CUDAToolkit_find_and_add_import_lib(nvToolsExt ALT nvToolsExt64) @@ -1514,6 +1569,30 @@ add_executable(CUDA::bin2c IMPORTED) set_property(TARGET CUDA::bin2c PROPERTY IMPORTED_LOCATION "${CUDA_bin2c_EXECUTABLE}") endif() + + if(NOT TARGET CUDA::sanitizer) + _CUDAToolkit_find_and_add_import_lib( + sanitizer + ONLY_SEARCH_FOR sanitizer-public + EXTRA_PATH_SUFFIXES + "../compute-sanitizer" + "../../../compute-sanitizer" + "../Sanitizer" + "../../../Sanitizer" + "../extras/Sanitizer" + "../../../extras/Sanitizer" + EXTRA_INCLUDE_DIRS "${CUDAToolkit_CUPTI_INCLUDE_DIR}" + ) + if(TARGET CUDA::sanitizer) + get_property(loc TARGET CUDA::sanitizer PROPERTY IMPORTED_LOCATION) + get_filename_component(sanitizer_dir "${loc}" DIRECTORY) + target_include_directories(CUDA::sanitizer INTERFACE "${sanitizer_dir}/include") + endif() + endif() +endif() + +if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.4) + _CUDAToolkit_find_and_add_import_lib(cufilt) endif() if(_CUDAToolkit_Pop_ROOT_PATH)
diff --git a/Modules/FindCURL.cmake b/Modules/FindCURL.cmake index 9af420f..d5ca65e 100644 --- a/Modules/FindCURL.cmake +++ b/Modules/FindCURL.cmake
@@ -257,16 +257,19 @@ execute_process(COMMAND ${CURL_CONFIG_EXECUTABLE} --version OUTPUT_VARIABLE CURL_CONFIG_VERSION_STRING ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _curl_config_version_result) execute_process(COMMAND ${CURL_CONFIG_EXECUTABLE} --feature OUTPUT_VARIABLE CURL_CONFIG_FEATURES_STRING ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _curl_config_features_result) string(REPLACE "\n" ";" CURL_SUPPORTED_FEATURES "${CURL_CONFIG_FEATURES_STRING}") execute_process(COMMAND ${CURL_CONFIG_EXECUTABLE} --protocols OUTPUT_VARIABLE CURL_CONFIG_PROTOCOLS_STRING ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _curl_config_protocols_result) string(REPLACE "\n" ";" CURL_SUPPORTED_PROTOCOLS "${CURL_CONFIG_PROTOCOLS_STRING}") endif() endif()
diff --git a/Modules/FindDoxygen.cmake b/Modules/FindDoxygen.cmake index e72110c..888fd2d 100644 --- a/Modules/FindDoxygen.cmake +++ b/Modules/FindDoxygen.cmake
@@ -743,6 +743,7 @@ OUTPUT_VARIABLE full_doxygen_version OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE version_result + ERROR_QUIET ) # Ignore any commit hashes, etc. @@ -1029,6 +1030,7 @@ COMMAND "${DOXYGEN_EXECUTABLE}" -s -g "${_Doxygen_tpl}" OUTPUT_QUIET RESULT_VARIABLE _Doxygen_tpl_result + ERROR_QUIET ) if(_Doxygen_tpl_result) message(FATAL_ERROR
diff --git a/Modules/FindEnvModules.cmake b/Modules/FindEnvModules.cmake index bb19d04..40f0875 100644 --- a/Modules/FindEnvModules.cmake +++ b/Modules/FindEnvModules.cmake
@@ -229,6 +229,7 @@ execute_process( COMMAND mktemp -t module.cmake.XXXXXXXXXXXX OUTPUT_VARIABLE tempfile_name + RESULT_VARIABLE _tempfile_result ) string(STRIP "${tempfile_name}" tempfile_name)
diff --git a/Modules/FindFLTK.cmake b/Modules/FindFLTK.cmake index eed585c..9b4605f 100644 --- a/Modules/FindFLTK.cmake +++ b/Modules/FindFLTK.cmake
@@ -289,7 +289,9 @@ find_program(FLTK_CONFIG_SCRIPT fltk-config PATHS ${FLTK_BIN_DIR}) if(FLTK_CONFIG_SCRIPT) if(NOT FLTK_INCLUDE_DIR) - execute_process(COMMAND ${FLTK_CONFIG_SCRIPT} --cxxflags OUTPUT_VARIABLE FLTK_CXXFLAGS) + execute_process(COMMAND ${FLTK_CONFIG_SCRIPT} --cxxflags + OUTPUT_VARIABLE FLTK_CXXFLAGS + RESULT_VARIABLE _fltk_cxxflags_result) if(FLTK_CXXFLAGS) string(REGEX MATCHALL "-I[^ ]*" _fltk_temp_dirs ${FLTK_CXXFLAGS}) string(REPLACE "-I" "" _fltk_temp_dirs "${_fltk_temp_dirs}") @@ -313,7 +315,9 @@ # Try to find FLTK library if(UNIX) if(FLTK_CONFIG_SCRIPT) - execute_process(COMMAND ${FLTK_CONFIG_SCRIPT} --libs OUTPUT_VARIABLE _FLTK_POSSIBLE_LIBS) + execute_process(COMMAND ${FLTK_CONFIG_SCRIPT} --libs + OUTPUT_VARIABLE _FLTK_POSSIBLE_LIBS + RESULT_VARIABLE _fltk_config_libs_result) if(_FLTK_POSSIBLE_LIBS) get_filename_component(_FLTK_POSSIBLE_LIBRARY_DIR ${_FLTK_POSSIBLE_LIBS} PATH) endif() @@ -350,7 +354,8 @@ if(UNIX) if(FLTK_CONFIG_SCRIPT) execute_process(COMMAND ${FLTK_CONFIG_SCRIPT} --use-images --ldflags - OUTPUT_VARIABLE FLTK_IMAGES_LDFLAGS) + OUTPUT_VARIABLE FLTK_IMAGES_LDFLAGS + RESULT_VARIABLE _fltk_images_ldflags_result) set(FLTK_LIBS_EXTRACT_REGEX ".*-lfltk_images (.*) -lfltk.*") if("${FLTK_IMAGES_LDFLAGS}" MATCHES "${FLTK_LIBS_EXTRACT_REGEX}") string(REGEX REPLACE " +" ";" FLTK_IMAGES_LIBS "${CMAKE_MATCH_1}")
diff --git a/Modules/FindGSL.cmake b/Modules/FindGSL.cmake index 041e9d1..0d30ffb 100644 --- a/Modules/FindGSL.cmake +++ b/Modules/FindGSL.cmake
@@ -171,7 +171,8 @@ execute_process( COMMAND "${GSL_CONFIG_EXECUTABLE}" --version OUTPUT_VARIABLE GSL_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE ) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _gsl_config_version_result) endif() # 2. If gsl-config is not available, try looking in gsl/gsl_version.h
diff --git a/Modules/FindGettext.cmake b/Modules/FindGettext.cmake index 347d30d..1330580 100644 --- a/Modules/FindGettext.cmake +++ b/Modules/FindGettext.cmake
@@ -413,7 +413,8 @@ execute_process(COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --version OUTPUT_VARIABLE gettext_version ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _gettext_msgmerge_version_result) get_filename_component(msgmerge_name ${GETTEXT_MSGMERGE_EXECUTABLE} NAME) get_filename_component(msgmerge_namewe ${GETTEXT_MSGMERGE_EXECUTABLE} NAME_WE) if(gettext_version MATCHES "^(${msgmerge_name}|${msgmerge_namewe}) \\([^\\)]*\\) ([0-9\\.]+[^ \n]*)")
diff --git a/Modules/FindGit.cmake b/Modules/FindGit.cmake index 978746b..878e1a5 100644 --- a/Modules/FindGit.cmake +++ b/Modules/FindGit.cmake
@@ -162,8 +162,9 @@ execute_process(COMMAND ${GIT_EXECUTABLE} --version OUTPUT_VARIABLE git_version ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (git_version MATCHES "^git version [0-9]") + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _git_res) + if (_git_res EQUAL 0 AND git_version MATCHES "^git version [0-9]") string(REPLACE "git version " "" Git_VERSION "${git_version}") set(GIT_VERSION_STRING "${Git_VERSION}") set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
diff --git a/Modules/FindGnuplot.cmake b/Modules/FindGnuplot.cmake index f5bf309..5a22735 100644 --- a/Modules/FindGnuplot.cmake +++ b/Modules/FindGnuplot.cmake
@@ -87,7 +87,8 @@ execute_process(COMMAND "${GNUPLOT_EXECUTABLE}" --version OUTPUT_VARIABLE GNUPLOT_OUTPUT_VARIABLE ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _gnuplot_version_result) string(REGEX REPLACE "^gnuplot ([0-9\\.]+)( patchlevel )?" "\\1." Gnuplot_VERSION "${GNUPLOT_OUTPUT_VARIABLE}") string(REGEX REPLACE "\\.$" "" Gnuplot_VERSION "${Gnuplot_VERSION}")
diff --git a/Modules/FindIce.cmake b/Modules/FindIce.cmake index a0be791..f672ba8 100644 --- a/Modules/FindIce.cmake +++ b/Modules/FindIce.cmake
@@ -662,7 +662,8 @@ execute_process(COMMAND ${Ice_SLICE2CPP_EXECUTABLE} --version ERROR_VARIABLE Ice_VERSION_SLICE2CPP_FULL - ERROR_STRIP_TRAILING_WHITESPACE) + ERROR_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _slice2cpp_version_result) # restore the previous LC_ALL set(ENV{LC_ALL} ${_Ice_SAVED_LC_ALL})
diff --git a/Modules/FindIcotool.cmake b/Modules/FindIcotool.cmake index ea9cb04..b4eeb7e 100644 --- a/Modules/FindIcotool.cmake +++ b/Modules/FindIcotool.cmake
@@ -81,6 +81,7 @@ OUTPUT_VARIABLE Icotool_VERSION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _icotool_version_result ) if("${Icotool_VERSION}" MATCHES "^icotool \\([^\\)]*\\) ([0-9\\.]+[^ \n]*)") set(Icotool_VERSION "${CMAKE_MATCH_1}")
diff --git a/Modules/FindImageMagick.cmake b/Modules/FindImageMagick.cmake index c34ebdb..d1b482c 100644 --- a/Modules/FindImageMagick.cmake +++ b/Modules/FindImageMagick.cmake
@@ -285,6 +285,7 @@ OUTPUT_VARIABLE version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _version_result ) if(version MATCHES "^Version: ImageMagick ([-0-9.]+)")
diff --git a/Modules/FindJNI.cmake b/Modules/FindJNI.cmake index a70b00f..5ad8727 100644 --- a/Modules/FindJNI.cmake +++ b/Modules/FindJNI.cmake
@@ -417,6 +417,18 @@ # SuSE specific paths for default JVM /usr/lib64/jvm/java /usr/lib64/jvm/jre + /usr/lib64/jvm/java-26-openjdk + /usr/lib64/jvm/jre-26-openjdk + /usr/lib64/jvm/java-25-openjdk + /usr/lib64/jvm/jre-25-openjdk + /usr/lib64/jvm/java-21-openjdk + /usr/lib64/jvm/jre-21-openjdk + /usr/lib64/jvm/java-17-openjdk + /usr/lib64/jvm/jre-17-openjdk + /usr/lib64/jvm/java-11-openjdk + /usr/lib64/jvm/jre-11-openjdk + /usr/lib64/jvm/java-1.8.0-openjdk + /usr/lib64/jvm/jre-1.8.0-openjdk ) set(_JNI_JAVA_AWT_LIBRARY_TRIES)
diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake index 231700a..5f7c83b 100644 --- a/Modules/FindMatlab.cmake +++ b/Modules/FindMatlab.cmake
@@ -1083,6 +1083,7 @@ OUTPUT_VARIABLE _matlab_mex_extension ERROR_VARIABLE _matlab_mex_extension_error OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _matlab_mex_extension_result ${devnull}) if(_matlab_mex_extension_error) @@ -1326,6 +1327,33 @@ set(${prefix}_OUTPUT_NAME ${${prefix}_NAME}) endif() + if(NOT ${prefix}_SRC) + message(FATAL_ERROR "[MATLAB] The MEX source files cannot be empty") + endif() + + set(languages "") + foreach(src_file IN LISTS ${prefix}_SRC) + get_filename_component(ext ${src_file} EXT) + if(ext STREQUAL ".c") + list(APPEND languages "C") + elseif(ext STREQUAL ".cpp" OR ext STREQUAL ".cxx" OR ext STREQUAL ".c++" OR ext STREQUAL ".cc") + list(APPEND languages "CXX") + elseif(ext MATCHES "\\.[fF](90|95|03|08)?$" OR ext STREQUAL ".for") + list(APPEND languages "Fortran") + endif() + endforeach() + + if("CXX" IN_LIST languages) + set(language "CXX") + elseif("C" IN_LIST languages) + set(language "C") + elseif("Fortran" IN_LIST languages) + set(language "Fortran") + else() + set(language "CXX") + message(STATUS "No standard C, CXX or Fortran source files, the target language is set to CXX") + endif() + if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, add version source file # Compilers officially supported by Matlab 9.1 (R2016b): # MinGW 4.9, MSVC 2012, Intel C++ 2013, Xcode 6, GCC 4.9 @@ -1333,25 +1361,26 @@ # Other compilers (Clang) may support the -w flag and can be added here. set(_Matlab_silenceable_compilers AppleClang Clang GNU Intel IntelLLVM MSVC) - # Add the correct version file depending on which languages are enabled in the project - if(CMAKE_C_COMPILER_LOADED) - # If C is enabled, use the .c file as it will work fine also with C++ + # Add the correct version file depending on the languages of the source files + if(language STREQUAL "C") + # For C source files, use the .c file set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/c_mexapi_version.c") # Silence warnings for version source file if("${CMAKE_C_COMPILER_ID}" IN_LIST _Matlab_silenceable_compilers) set_source_files_properties("${MEX_VERSION_FILE}" PROPERTIES COMPILE_OPTIONS -w) endif() - elseif(CMAKE_CXX_COMPILER_LOADED) - # If C is not enabled, check if CXX is enabled and use the .cpp file - # to avoid that the .c file is silently ignored + elseif(language STREQUAL "CXX") + # For CXX source files, use the .cpp file set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/cpp_mexapi_version.cpp") if("${CMAKE_CXX_COMPILER_ID}" IN_LIST _Matlab_silenceable_compilers) set_source_files_properties("${MEX_VERSION_FILE}" PROPERTIES COMPILE_OPTIONS -w) endif() + elseif(language STREQUAL "Fortran") + # For Fortran source files, use the .F file + set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/fortran_mexapi_version.F") else() - # If neither C or CXX is enabled, warn because we cannot add the source. - # TODO: add support for fortran mex files - message(WARNING "[MATLAB] matlab_add_mex requires that at least C or CXX are enabled languages") + # If neither C, CXX or Fortran source files are provided, warn because we cannot add the source. + message(WARNING "[MATLAB] matlab_add_mex requires that the input source files are written in C, CXX or Fortran") endif() endif() @@ -1433,9 +1462,16 @@ if (MSVC) - string(APPEND _link_flags " /EXPORT:mexFunction") - if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, export version - string(APPEND _link_flags " /EXPORT:mexfilerequiredapiversion") + if(language STREQUAL "C" OR language STREQUAL "CXX") + string(APPEND _link_flags " /EXPORT:mexFunction") + if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, export version + string(APPEND _link_flags " /EXPORT:mexfilerequiredapiversion") + endif() + elseif(language STREQUAL "Fortran") + string(APPEND _link_flags " /EXPORT:MEXFUNCTION") + if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, export version + string(APPEND _link_flags " /EXPORT:MEXFILEREQUIREDAPIVERSION") + endif() endif() set_property(TARGET ${${prefix}_NAME} APPEND PROPERTY LINK_FLAGS ${_link_flags}) @@ -1448,9 +1484,17 @@ else() if(Matlab_VERSION_STRING VERSION_LESS "9.1") # For versions prior to 9.1 (R2016b) - set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/mexFunction.map) + if(language STREQUAL "C" OR language STREQUAL "CXX") + set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/mexFunction.map) + elseif(language STREQUAL "Fortran") + set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/fexport.map) + endif() else() # For 9.1 (R2016b) and newer - set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/c_exportsmexfileversion.map) + if(language STREQUAL "C" OR language STREQUAL "CXX") + set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/c_exportsmexfileversion.map) + elseif(language STREQUAL "Fortran") + set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/fortran_exportsmexfileversion.map) + endif() endif() if(NOT Matlab_VERSION_STRING VERSION_LESS "9.5") # For 9.5 (R2018b) (and newer?) @@ -1573,6 +1617,7 @@ find_program( _matlab_current_program + NO_CACHE NAMES matlab ${_find_matlab_options} DOC "Matlab main program" @@ -1588,14 +1633,12 @@ set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version") set(Matlab_OR_MCR_INTERNAL ${matlab_or_mcr} CACHE INTERNAL "Whether Matlab root contains MATLAB or MCR") unset(_matlab_current_program) - unset(_matlab_current_program CACHE) return() endif() # full real path for path comparison get_filename_component(_matlab_main_real_path_tmp "${_matlab_current_program}" REALPATH) unset(_matlab_current_program) - unset(_matlab_current_program CACHE) # is it the same as the previous one? if(_matlab_main_real_path_tmp STREQUAL Matlab_PROG_VERSION_STRING_AUTO_DETECT) @@ -1747,6 +1790,7 @@ # If set by the user, this won't change. find_program( _matlab_main_tmp + NO_CACHE NAMES matlab) if(_matlab_main_tmp) @@ -1766,11 +1810,11 @@ # We found the Matlab program list(APPEND _matlab_possible_roots "MATLAB" "NOTFOUND" ${_matlab_current_location}) - # we remove this from the CACHE - unset(_matlab_main_tmp CACHE) + unset(_matlab_main_tmp) else() find_program( _matlab_mex_tmp + NO_CACHE NAMES mex) if(_matlab_mex_tmp) # we then populate the list of roots, with empty version @@ -1789,7 +1833,7 @@ # We found the Matlab program list(APPEND _matlab_possible_roots "MCR" "NOTFOUND" ${_mex_current_location}) - unset(_matlab_mex_tmp CACHE) + unset(_matlab_mex_tmp) else() if(MATLAB_FIND_DEBUG) message(STATUS "[MATLAB] mex compiler not found")
diff --git a/Modules/FindODBC.cmake b/Modules/FindODBC.cmake index e8692bd..a668072 100644 --- a/Modules/FindODBC.cmake +++ b/Modules/FindODBC.cmake
@@ -163,9 +163,11 @@ if (UNIX AND ODBC_CONFIG) # unixODBC and iODBC accept unified command line options execute_process(COMMAND ${ODBC_CONFIG} --cflags - OUTPUT_VARIABLE _cflags OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_VARIABLE _cflags OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _odbc_config_cflags_result) execute_process(COMMAND ${ODBC_CONFIG} --libs - OUTPUT_VARIABLE _libs OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_VARIABLE _libs OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _odbc_config_libs_result) # Collect paths of include directories from CFLAGS separate_arguments(_cflags NATIVE_COMMAND "${_cflags}") @@ -219,13 +221,14 @@ foreach(_lib IN LISTS _odbc_required_libs_names) find_library(_lib_path + NO_CACHE NAMES ${_lib} PATHS ${_odbc_lib_paths} # system paths or collected from ODBC_CONFIG PATH_SUFFIXES odbc) if(_lib_path) list(APPEND _odbc_required_libs_paths ${_lib_path}) endif() - unset(_lib_path CACHE) + unset(_lib_path) endforeach() endif()
diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake index 6362edf..101dbad 100644 --- a/Modules/FindOpenGL.cmake +++ b/Modules/FindOpenGL.cmake
@@ -529,16 +529,8 @@ list(APPEND _OpenGL_CACHE_VARS OPENGL_gl_LIBRARY) endif() - # When preferring legacy, linking OpenGL and GLX should behave the same as linking legacy GL. - if(OpenGL_GL_PREFERENCE STREQUAL "LEGACY") - set(OpenGL_glx_LIBRARY "${OPENGL_gl_LIBRARY}") - set(OpenGL_opengl_LIBRARY "${OPENGL_gl_LIBRARY}") - endif() - if(_OpenGL_GL_POLICY_WARN AND OPENGL_gl_LIBRARY AND OPENGL_opengl_LIBRARY AND OPENGL_glx_LIBRARY) - cmake_policy(GET_WARNING CMP0072 _cmp0072_warning) - message(AUTHOR_WARNING - "${_cmp0072_warning}\n" + cmake_policy(ISSUE_WARNING CMP0072 POST "FindOpenGL found both a legacy GL library:\n" " OPENGL_gl_LIBRARY: ${OPENGL_gl_LIBRARY}\n" "and GLVND libraries for OpenGL and GLX:\n" @@ -550,6 +542,12 @@ endif() unset(_OpenGL_GL_POLICY_WARN) + # When preferring legacy, linking OpenGL and GLX should behave the same as linking legacy GL. + if(OpenGL_GL_PREFERENCE STREQUAL "LEGACY") + set(OPENGL_glx_LIBRARY "${OPENGL_gl_LIBRARY}") + set(OPENGL_opengl_LIBRARY "${OPENGL_gl_LIBRARY}") + endif() + # FPHSA cannot handle "this OR that is required", so we conditionally set what # it must look for. First clear any previous config we might have done: set(_OpenGL_REQUIRED_VARS)
diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index 6ef1152..11236f9 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake
@@ -237,6 +237,7 @@ set(OMP_FLAG_CrayClang "-fopenmp") set(OMP_FLAG_Fujitsu "-Kopenmp" "-KOMP") set(OMP_FLAG_FujitsuClang "-fopenmp" "-Kopenmp") + set(OMP_FLAG_OrangeC "") if(CMAKE_${LANG}_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_${LANG}_HOST_COMPILER_ID) set(compiler_id "${CMAKE_${LANG}_HOST_COMPILER_ID}") @@ -365,7 +366,8 @@ COMMAND ${CMAKE_${LANG}_COMPILER} -print-search-dirs OUTPUT_VARIABLE output_lines COMMAND_ERROR_IS_FATAL ANY - ERROR_QUIET) + ERROR_QUIET + RESULT_VARIABLE _result_lcc_output) if("${output_lines}" MATCHES ".*\nlibraries:[ \t]+(.*:)\n.*") string(REPLACE ":" ";" implicit_dirs_addon "${CMAKE_MATCH_1}") list(PREPEND OpenMP_${LANG}_IMPLICIT_LINK_DIRS ${implicit_dirs_addon})
diff --git a/Modules/FindProtobuf.cmake b/Modules/FindProtobuf.cmake index 62baba1..cec56ac 100644 --- a/Modules/FindProtobuf.cmake +++ b/Modules/FindProtobuf.cmake
@@ -992,7 +992,8 @@ if(Protobuf_PROTOC_EXECUTABLE) # Check Protobuf compiler version to be aligned with libraries version execute_process(COMMAND ${Protobuf_PROTOC_EXECUTABLE} --version - OUTPUT_VARIABLE _PROTOBUF_PROTOC_EXECUTABLE_VERSION) + OUTPUT_VARIABLE _PROTOBUF_PROTOC_EXECUTABLE_VERSION + RESULT_VARIABLE _PROTOBUF_PROTOC_EXECUTABLE_VERSION_RESULT) if("${_PROTOBUF_PROTOC_EXECUTABLE_VERSION}" MATCHES "libprotoc ([0-9.]+)") set(_PROTOBUF_PROTOC_EXECUTABLE_VERSION "${CMAKE_MATCH_1}")
diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake index 46a8cbb..381147d 100644 --- a/Modules/FindPython.cmake +++ b/Modules/FindPython.cmake
@@ -182,6 +182,13 @@ * Canopy * IronPython * PyPy + +``Python_FREE_THREADED`` + .. versionadded:: 4.4 + + Boolean value, which is ``true`` if the python is free threaded (i.e. + config var ``Py_GIL_DISABLED`` has value ``1``), ``false`` otherwise. + ``Python_STDLIB`` Standard platform independent installation directory. @@ -215,8 +222,8 @@ Information computed from ``importlib.machinery.EXTENSION_SUFFIXES`` if the COMPONENT ``Interpreter`` was specified. Otherwise, the extension is ``abi3`` - except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this is an empty - string. + or ``abi3t`` except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this + is an empty string. ``Python_Compiler_FOUND`` Boolean indicating whether system has the Python compiler. @@ -356,6 +363,10 @@ threaded python), is added and is optional. If not specified, the value is ``OFF``. + .. versionadded:: 4.4.1 + The ``pydebug`` flag is now supported on ``Windows`` systems for ``Python`` + 3.14+. + Each element can be set to one of the following: * ``ON``: Corresponding flag is selected. @@ -364,8 +375,10 @@ .. note:: - If ``Python3_FIND_ABI`` is not defined, any ABI, excluding the - ``gil_disabled`` flag, will be searched. + If ``Python_FIND_ABI`` is not defined, the following default apply: + + * On ``POSIX`` systems: ``ANY``, ``ANY``, ``ANY``, ``OFF``. + * On ``Windows`` systems: ``OFF``, ``OFF``, ``OFF``, ``OFF``. From this 4-tuple, various ABIs will be searched starting from the most specialized to the most general. Moreover, when ``ANY`` is specified for @@ -379,7 +392,10 @@ set (Python_FIND_ABI "ON" "ANY" "ANY" "ON") The following flags combinations will be appended, in that order, to the - artifact names: ``tdmu``, ``tdm``, ``tdu``, and ``td``. + artifact names: + + * On ``POSIX`` systems: ``tdmu``, ``tdm``, ``tdu``, and ``td``. + * On ``Windows`` systems: ``tmu_d``, ``tm_d``, ``tu_d``, and ``t_d``. And to search any possible ABIs: @@ -387,18 +403,24 @@ set (Python_FIND_ABI "ANY" "ANY" "ANY" "ANY") - The following combinations, in that order, will be used: ``mu``, ``m``, - ``u``, ``<empty>``, ``dmu``, ``dm``, ``du``, ``d``, ``tmu``, ``tm``, ``tu``, - ``t``, ``tdmu``, ``tdm``, ``tdu``, and ``td``. + The following combinations, in that order, will be used: + + * On ``POSIX`` systems: ``mu``, ``m``, ``u``, ``<empty>``, ``dmu``, ``dm``, + ``du``, ``d``, ``tmu``, ``tm``, ``tu``, ``t``, ``tdmu``, ``tdm``, ``tdu``, + and ``td``. + * On ``Windows`` systems: ``mu``, ``m``, ``u``, ``<empty>``, ``mu_d``, + ``m_d``, ``u_d``, ``_d``, ``tmu``, ``tm``, ``tu``, ``t``, ``tmu_d``, ``tm_d``, + ``tu_d``, and ``t_d``. .. note:: - This hint is useful only on ``POSIX`` systems except for the - ``gil_disabled`` flag. So, on ``Windows`` systems, - when ``Python_FIND_ABI`` is defined, ``Python`` distributions from - `python.org <https://www.python.org/>`_ will be found only if the value for - each flag is ``OFF`` or ``ANY`` except for the fourth one - (``gil_disabled``). + This hint is fully usable on ``POSIX`` systems. On ``Windows`` systems, + only the ``gil_disabled`` and ``pydebug`` flags are supported. Moreover, + the ``pydebug`` flag is only supported for ``Python`` 3.14+. So, on + ``Windows`` systems, when ``Python_FIND_ABI`` is defined, ``Python`` + distributions from `python.org <https://www.python.org/>`_ will be found + only if the value for the flags ``pymalloc`` and ``unicode`` is ``OFF`` or + ``ANY``. ``Python_FIND_STRATEGY`` .. versionadded:: 3.15 @@ -651,6 +673,12 @@ `Stable Application Binary Interface <https://docs.python.org/3/c-api/stable.html>`_ was introduced. Specifying only major version ``3`` is equivalent to ``3.2``. +.. versionchanged:: 4.4 + When ``USE_SABI`` is specified, if ``Python_FREE_THREADED`` is ``true`` and + Python version is ``3.15`` or upper, ``Py_TARGET_ABI3T`` preprocessor + definition will be defined rather than ``Py_LIMITED_API`` (see :pep:`803` for + more details). + When option ``WITH_SOABI`` is also specified, the module suffix will include the ``Python_SOSABI`` value, if any.
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake index 455260c..de9fba3 100644 --- a/Modules/FindPython/Support.cmake +++ b/Modules/FindPython/Support.cmake
@@ -238,20 +238,30 @@ foreach (implementation IN LISTS _PGR_IMPLEMENTATIONS) if (implementation STREQUAL "CPython") + set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") + list (TRANSFORM abi REPLACE "^<none>$" "") foreach (version IN LISTS _PGR_VERSION) string (REPLACE "." "" version_no_dots ${version}) - list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths) - list (APPEND registries ${reg_paths}) - if (version VERSION_GREATER_EQUAL "3.5") - # cmake_host_system_information is not usable in bootstrap - get_filename_component (arch "[HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\${version};SysArchitecture]" NAME) - string (REPLACE "bit" "" arch "${arch}") - if (arch IN_LIST _${_PYTHON_PREFIX}_ARCH) - list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath]) - endif() + if (abi) + set (abiversions "${abi}") + list (TRANSFORM abiversions PREPEND "${version}") else() - list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath]) + set (abiversions "${version}") endif() + foreach (abiversion IN LISTS abiversions) + list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${abiversion}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths) + list (APPEND registries ${reg_paths}) + if (version VERSION_GREATER_EQUAL "3.5") + # cmake_host_system_information is not usable in bootstrap + get_filename_component (arch "[HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\${abiversion};SysArchitecture]" NAME) + string (REPLACE "bit" "" arch "${arch}") + if (arch IN_LIST _${_PYTHON_PREFIX}_ARCH) + list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${abiversion}/InstallPath]) + endif() + else() + list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${abiversion}/InstallPath]) + endif() + endforeach() list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths) list (APPEND registries ${reg_paths}) list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths) @@ -302,18 +312,36 @@ endif() if (pydebug STREQUAL "ON") - if (abiflags) - list (TRANSFORM abiflags PREPEND "d") + if (WIN32) + if (NOT abiflags STREQUAL "<none>") + list (TRANSFORM abiflags APPEND "_d") + else() + set (abiflags "_d") + endif() else() - set (abiflags "d") + if (NOT abiflags STREQUAL "<none>") + list (TRANSFORM abiflags PREPEND "d") + else() + set (abiflags "d") + endif() endif() elseif (pydebug STREQUAL "ANY") - if (abiflags) - set (flags "${abiflags}") - list (TRANSFORM flags PREPEND "d") - list (APPEND abiflags "${flags}") + if (WIN32) + if (NOT abiflags STREQUAL "<none>") + set (flags "${abiflags}") + list (TRANSFORM flags APPEND "_d") + list (APPEND abiflags "${flags}") + else() + set (abiflags "<none>" "_d") + endif() else() - set (abiflags "<none>" "d") + if (NOT abiflags STREQUAL "<none>") + set (flags "${abiflags}") + list (TRANSFORM flags PREPEND "d") + list (APPEND abiflags "${flags}") + else() + set (abiflags "<none>" "d") + endif() endif() endif() @@ -424,12 +452,26 @@ foreach (implementation IN LISTS _PGN_IMPLEMENTATIONS) if (implementation STREQUAL "CPython") + set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") + list (TRANSFORM abi REPLACE "^<none>$" "") + + set (unversionednames python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "FIRST") - if (_PGN_DEBUG) - list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}_d python_d) + if (abi) + unset (fullnames) + foreach (name IN LISTS unversionednames) + set (abinames "${abi}") + list (TRANSFORM abinames PREPEND "${name}") + list(APPEND fullnames "${abinames}") + endforeach() else() - list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + set (fullnames ${unversionednames}) endif() + if (_PGN_DEBUG AND NOT abi MATCHES "_d") + list (TRANSFORM fullnames APPEND "_d") + endif() + list (APPEND names ${fullnames}) endif() foreach (version IN LISTS _PGN_VERSION) if (_PGN_WIN32) @@ -438,24 +480,19 @@ else() string (REPLACE "." "" name_version ${version}) endif() - set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") - list (TRANSFORM abi REPLACE "^<none>$" "") if (abi) set (abinames "${abi}") list (TRANSFORM abinames PREPEND "python${name_version}") else() set (abinames "python${name_version}") endif() - if (_PGN_DEBUG) + if (_PGN_DEBUG AND NOT abi MATCHES "_d") list (TRANSFORM abinames APPEND "_d") endif() list (APPEND names ${abinames}) endif() if (_PGN_POSIX) - set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") - list (TRANSFORM abi REPLACE "^<none>$" "") - if (abi) if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) set (abinames "${abi}") @@ -483,11 +520,20 @@ endif() endforeach() if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "LAST") - if (_PGN_DEBUG) - list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}_d python_d) + if (abi) + unset (fullnames) + foreach (name IN LISTS unversionednames) + set (abinames "${abi}") + list (TRANSFORM abinames PREPEND "${name}") + list(APPEND fullnames "${abinames}") + endforeach() else() - list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + set (fullnames ${unversionednames}) endif() + if (_PGN_DEBUG AND NOT abi MATCHES "_d") + list (TRANSFORM fullnames APPEND "_d") + endif() + list(APPEND names ${fullnames}) endif() elseif (implementation STREQUAL "IronPython") if (_PGN_INTERPRETER) @@ -549,7 +595,11 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR CMAKE_SYSTEM_NAME MATCHES "MSYS|CYGWIN") set (_values "") else() - set (_values "abi${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}") + if (${_PYTHON_PREFIX}_FREE_THREADED) + set (_values "abi${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}t") + else() + set (_values "abi${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}") + endif() endif() elseif (_${_PYTHON_PREFIX}_CONFIG) if (NAME STREQUAL "SOABI") @@ -580,9 +630,6 @@ endif() endif() endif() - if (NAME STREQUAL "ABIFLAGS" AND NOT _values) - set (_values "<none>") - endif() endif() if ("Interpreter" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS AND _${_PYTHON_PREFIX}_EXECUTABLE @@ -773,12 +820,13 @@ if (_result) unset (_values) endif() - if (NAME STREQUAL "ABIFLAGS" AND NOT _values) - set (_values "<none>") - endif() endif() endif() + if (NAME STREQUAL "ABIFLAGS" AND NOT _values) + set (_values "<none>") + endif() + if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI" OR NAME STREQUAL "SOSABI" OR NAME STREQUAL "POSTFIX") set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE) return() @@ -819,7 +867,7 @@ if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) get_filename_component (library_name "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" NAME) # extract version from library name - if (library_name MATCHES "python([23])([0-9]+)([tdmu]*)") + if (library_name MATCHES "python([23])([0-9]+)([tdmu_]*)") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) @@ -855,7 +903,7 @@ if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) get_filename_component (library_name "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" NAME) # extract version from library name - if (library_name MATCHES "python([23])([tdmu]*)") + if (library_name MATCHES "python([23])([tdmu_]*)") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) set (abi "${CMAKE_MATCH_2}") @@ -972,7 +1020,7 @@ endif() if (_${_PYTHON_PREFIX}_CROSSCOMPILING) - set (${_PYTHON_PGL_NAME} "${CMAKE_CROSSCOMPILING_EMULATOR}" PARENT_SCOPE) + set (${_PYTHON_PGL_NAME} "${_${_PYTHON_PREFIX}_CROSSCOMPILING_EMULATOR}" PARENT_SCOPE) return() endif() @@ -1581,15 +1629,22 @@ ## handle cross-compiling constraints for components: ## If Interpreter and/or Compiler are specified with Development components ## the CMAKE_CROSSCOMPILING_EMULATOR variable should be defined -cmake_policy (GET CMP0190 _${_PYTHON_PREFIX}_CROSSCOMPILING_POLICY) unset (_${_PYTHON_PREFIX}_CROSSCOMPILING) -if (CMAKE_CROSSCOMPILING AND _${_PYTHON_PREFIX}_CROSSCOMPILING_POLICY STREQUAL "NEW") +unset (_${_PYTHON_PREFIX}_CROSSCOMPILING_EMULATOR) +if (CMAKE_CROSSCOMPILING) if (${_PYTHON_BASE}_FIND_COMPONENTS MATCHES "Interpreter|Compiler" AND ${_PYTHON_BASE}_FIND_COMPONENTS MATCHES "Development") - if (CMAKE_CROSSCOMPILING_EMULATOR) - set (_${_PYTHON_PREFIX}_CROSSCOMPILING TRUE) + cmake_policy (GET CMP0190 _${_PYTHON_PREFIX}_CROSSCOMPILING_POLICY) + if (_${_PYTHON_PREFIX}_CROSSCOMPILING_POLICY STREQUAL "NEW") + if (CMAKE_CROSSCOMPILING_EMULATOR) + set (_${_PYTHON_PREFIX}_CROSSCOMPILING TRUE) + set (_${_PYTHON_PREFIX}_CROSSCOMPILING_EMULATOR "${CMAKE_CROSSCOMPILING_EMULATOR}") + else() + _python_display_failure ("${_PYTHON_PREFIX}: When cross-compiling, Interpreter and/or Compiler components cannot be searched when CMAKE_CROSSCOMPILING_EMULATOR variable is not specified (see policy CMP0190)." FATAL) + endif() else() - _python_display_failure ("${_PYTHON_PREFIX}: When cross-compiling, Interpreter and/or Compiler components cannot be searched when CMAKE_CROSSCOMPILING_EMULATOR variable is not specified (see policy CMP0190)." FATAL) + # CMP0190 == OLD : no condition regarding crosscompiling environment + set (_${_PYTHON_PREFIX}_CROSSCOMPILING TRUE) endif() endif() endif() @@ -1667,17 +1722,20 @@ message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_ABI}: invalid value for '${_PYTHON_PREFIX}_FIND_ABI'. Ignore it") unset (_${_PYTHON_PREFIX}_FIND_ABI) endif() - _python_get_abiflags ("${${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_ABIFLAGS) - else() + endif() + if (NOT DEFINED _${_PYTHON_PREFIX}_FIND_ABI) + # set defaults if (WIN32) - _python_get_abiflags ("OFF;OFF;OFF;OFF" _${_PYTHON_PREFIX}_ABIFLAGS) + set (_${_PYTHON_PREFIX}_FIND_ABI "OFF;OFF;OFF;OFF") else() - _python_get_abiflags ("ANY;ANY;ANY;OFF" _${_PYTHON_PREFIX}_ABIFLAGS) + set (_${_PYTHON_PREFIX}_FIND_ABI "ANY;ANY;ANY;OFF") endif() endif() + _python_get_abiflags ("${_${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_ABIFLAGS) endif() unset (${_PYTHON_PREFIX}_SOABI) unset (${_PYTHON_PREFIX}_SOSABI) +unset (${_PYTHON_PREFIX}_FREE_THREADED) # Windows CPython implementation may be requiring a postfix in debug mode unset (${_PYTHON_PREFIX}_DEBUG_POSTFIX) @@ -1725,6 +1783,9 @@ set (_${_PYTHON_PREFIX}_ARCHITECTURE_ID "${CMAKE_SYSTEM_PROCESSOR}") break() endwhile() + # normalize architecture name + string(REPLACE "aarch" "ARM" _${_PYTHON_PREFIX}_ARCHITECTURE_ID "${_${_PYTHON_PREFIX}_ARCHITECTURE_ID}") + string(TOUPPER "${_${_PYTHON_PREFIX}_ARCHITECTURE_ID}" _${_PYTHON_PREFIX}_ARCHITECTURE_ID) endblock() # Python and Anaconda distributions: define which architectures can be used @@ -2536,6 +2597,13 @@ unset (${_PYTHON_PREFIX}_SITEARCH) endif() + # Identify if the python interpreter is free threaded or not + if (_${_PYTHON_PREFIX}_ABIFLAGS MATCHES "t") + set (${_PYTHON_PREFIX}_FREE_THREADED ON) + else() + set (${_PYTHON_PREFIX}_FREE_THREADED OFF) + endif() + _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI) _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI) @@ -3134,7 +3202,7 @@ OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if (_${_PYTHON_PREFIX}_RESULT OR NOT __${_PYTHON_PREFIX}_ABIFLAGS ) + if (_${_PYTHON_PREFIX}_RESULT OR NOT __${_PYTHON_PREFIX}_ABIFLAGS) # assume ABI is not supported or not used set (__${_PYTHON_PREFIX}_ABIFLAGS "<none>") endif() @@ -3261,7 +3329,7 @@ OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if (_${_PYTHON_PREFIX}_RESULT) + if (_${_PYTHON_PREFIX}_RESULT OR NOT __${_PYTHON_PREFIX}_ABIFLAGS) # assume ABI is not supported set (__${_PYTHON_PREFIX}_ABIFLAGS "<none>") endif() @@ -3500,6 +3568,14 @@ endif() endif() + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT DEFINED ${_PYTHON_PREFIX}_FREE_THREADED) + if (_${_PYTHON_PREFIX}_ABI MATCHES "t") + set (${_PYTHON_PREFIX}_FREE_THREADED ON) + else() + set (${_PYTHON_PREFIX}_FREE_THREADED OFF) + endif() + endif() + set (${_PYTHON_PREFIX}_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") @@ -3742,7 +3818,7 @@ ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) - if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) break() endif() endforeach() @@ -3753,14 +3829,26 @@ # finalize library version information # ABI library does not have the full version information - if (${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) - # update from interpreter - set (_${_PYTHON_PREFIX}_VERSION ${${_PYTHON_PREFIX}_VERSION}) - set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${${_PYTHON_PREFIX}_VERSION_MAJOR}) - set (_${_PYTHON_PREFIX}_VERSION_MINOR ${${_PYTHON_PREFIX}_VERSION_MINOR}) - set (_${_PYTHON_PREFIX}_VERSION_PATCH ${${_PYTHON_PREFIX}_VERSION_PATCH}) - elseif(NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) _python_get_version (SABI_LIBRARY PREFIX _${_PYTHON_PREFIX}_) + if (_${_PYTHON_PREFIX}_VERSION EQUAL "${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}") + # not able to extract full version from library name + if (${_PYTHON_PREFIX}_Interpreter_FOUND) + # update from interpreter + set (_${_PYTHON_PREFIX}_VERSION ${${_PYTHON_PREFIX}_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${${_PYTHON_PREFIX}_VERSION_MAJOR}) + set (_${_PYTHON_PREFIX}_VERSION_MINOR ${${_PYTHON_PREFIX}_VERSION_MINOR}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${${_PYTHON_PREFIX}_VERSION_PATCH}) + endif() + endif() + endif() + + if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE AND NOT DEFINED ${_PYTHON_PREFIX}_FREE_THREADED) + if (_${_PYTHON_PREFIX}_ABI MATCHES "t") + set (${_PYTHON_PREFIX}_FREE_THREADED ON) + else() + set (${_PYTHON_PREFIX}_FREE_THREADED OFF) + endif() endif() set (${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}") @@ -3979,6 +4067,14 @@ set (_${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}) set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) endif() + + if (NOT DEFINED ${_PYTHON_PREFIX}_FREE_THREADED) + if (_${_PYTHON_PREFIX}_INC_ABI MATCHES "t") + set (${_PYTHON_PREFIX}_FREE_THREADED ON) + else() + set (${_PYTHON_PREFIX}_FREE_THREADED OFF) + endif() + endif() endif() endif() @@ -4018,14 +4114,6 @@ _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) endif() - - if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "t${CMAKE_IMPORT_LIBRARY_SUFFIX}$") - # On windows, header file is shared between the different implementations - # So Py_GIL_DISABLED should be set explicitly - set (${_PYTHON_PREFIX}_DEFINITIONS Py_GIL_DISABLED=1) - else() - unset (${_PYTHON_PREFIX}_DEFINITIONS) - endif() endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) @@ -4055,14 +4143,14 @@ _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG) endif() + endif() - if (WIN32 AND _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "t${CMAKE_IMPORT_LIBRARY_SUFFIX}$") - # On windows, header file is shared between the different implementations - # So Py_GIL_DISABLED should be set explicitly - set (${_PYTHON_PREFIX}_DEFINITIONS Py_GIL_DISABLED=1) - else() - unset (${_PYTHON_PREFIX}_DEFINITIONS) - endif() + if (WIN32 AND ${_PYTHON_PREFIX}_FREE_THREADED) + # On windows, header file is shared between the different implementations + # So Py_GIL_DISABLED should be set explicitly + set (${_PYTHON_PREFIX}_DEFINITIONS Py_GIL_DISABLED=1) + else() + unset (${_PYTHON_PREFIX}_DEFINITIONS) endif() if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_INCLUDE_DIR) @@ -4081,13 +4169,20 @@ _python_set_development_module_found (Embed) endif() if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI) - if ((_${_PYTHON_PREFIX}_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + if (((("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS + OR "LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) + AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + OR ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS + AND _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)) AND NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) set (${_PYTHON_PREFIX}_Development.Module_FOUND FALSE) set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE) set (${_PYTHON_PREFIX}_Development.Embed_FOUND FALSE) endif() - if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND + if ((("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS + OR "INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS + OR "INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) + AND _${_PYTHON_PREFIX}_INCLUDE_DIR) AND NOT WIN32 AND NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) set (${_PYTHON_PREFIX}_Development.Module_FOUND FALSE) set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE) @@ -4586,7 +4681,12 @@ if (type STREQUAL "MODULE_LIBRARY") if (PYTHON_ADD_LIBRARY_USE_SABI) - target_compile_definitions (${name} PRIVATE Py_LIMITED_API=${Py_LIMITED_API}) + if (${prefix}_FREE_THREADED AND + "${major_version}.${minor_version}" VERSION_GREATER_EQUAL "3.15") + target_compile_definitions (${name} PRIVATE Py_TARGET_ABI3T=${Py_LIMITED_API}) + else() + target_compile_definitions (${name} PRIVATE Py_LIMITED_API=${Py_LIMITED_API}) + endif() target_link_libraries (${name} PRIVATE ${prefix}::SABIModule) else() target_link_libraries (${name} PRIVATE ${prefix}::Module)
diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake index ee6715d..657587e 100644 --- a/Modules/FindPython3.cmake +++ b/Modules/FindPython3.cmake
@@ -184,6 +184,13 @@ * Canopy * IronPython * PyPy + +``Python3_FREE_THREADED`` + .. versionadded:: 4.4 + + Boolean value, which is ``true`` if the python is free threaded (i.e. + config var ``Py_GIL_DISABLED`` has value ``1``), ``false`` otherwise. + ``Python3_STDLIB`` Standard platform independent installation directory. @@ -217,8 +224,8 @@ Information computed from ``importlib.machinery.EXTENSION_SUFFIXES`` if the COMPONENT ``Interpreter`` was specified. Otherwise, the extension is ``abi3`` - except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this is an empty - string. + or ``abi3t`` except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this + is an empty string. ``Python3_Compiler_FOUND`` Boolean indicating whether system has the Python 3 compiler. @@ -355,6 +362,10 @@ threaded python), is added and is optional. If not specified, the value is ``OFF``. + .. versionadded:: 4.4.1 + The ``pydebug`` flag is now supported on ``Windows`` systems for ``Python`` + 3.14+. + Each element can be set to one of the following: * ``ON``: Corresponding flag is selected. @@ -363,8 +374,10 @@ .. note:: - If ``Python3_FIND_ABI`` is not defined, any ABI, excluding the - ``gil_disabled`` flag, will be searched. + If ``Python3_FIND_ABI`` is not defined, the following default apply: + + * On ``POSIX`` systems: ``ANY``, ``ANY``, ``ANY``, ``OFF``. + * On ``Windows`` systems: ``OFF``, ``OFF``, ``OFF``, ``OFF``. From this 4-tuple, various ABIs will be searched starting from the most specialized to the most general. Moreover, when ``ANY`` is specified for @@ -378,7 +391,10 @@ set (Python3_FIND_ABI "ON" "ANY" "ANY" "ON") The following flags combinations will be appended, in that order, to the - artifact names: ``tdmu``, ``tdm``, ``tdu``, and ``td``. + artifact names: + + * On ``POSIX`` systems: ``tdmu``, ``tdm``, ``tdu``, and ``td``. + * On ``Windows`` systems: ``tmu_d``, ``tm_d``, ``tu_d``, and ``t_d``. And to search any possible ABIs: @@ -386,18 +402,24 @@ set (Python3_FIND_ABI "ANY" "ANY" "ANY" "ANY") - The following combinations, in that order, will be used: ``mu``, ``m``, - ``u``, ``<empty>``, ``dmu``, ``dm``, ``du``, ``d``, ``tmu``, ``tm``, ``tu``, - ``t``, ``tdmu``, ``tdm``, ``tdu``, and ``td``. + The following combinations, in that order, will be used: + + * On ``POSIX`` systems: ``mu``, ``m``, ``u``, ``<empty>``, ``dmu``, ``dm``, + ``du``, ``d``, ``tmu``, ``tm``, ``tu``, ``t``, ``tdmu``, ``tdm``, ``tdu``, + and ``td``. + * On ``Windows`` systems: ``mu``, ``m``, ``u``, ``<empty>``, ``mu_d``, + ``m_d``, ``u_d``, ``_d``, ``tmu``, ``tm``, ``tu``, ``t``, ``tmu_d``, ``tm_d``, + ``tu_d``, and ``t_d``. .. note:: - This hint is useful only on ``POSIX`` systems except for the - ``gil_disabled`` flag. So, on ``Windows`` systems, - when ``Python_FIND_ABI`` is defined, ``Python`` distributions from - `python.org <https://www.python.org/>`_ will be found only if the value for - each flag is ``OFF`` or ``ANY`` except for the fourth one - (``gil_disabled``). + This hint is fully usable on ``POSIX`` systems. On ``Windows`` systems, + only the ``gil_disabled`` and ``pydebug`` flags are supported. Moreover, + the ``pydebug`` flag is only supported for ``Python`` 3.14+. So, on + ``Windows`` systems, when ``Python3_FIND_ABI`` is defined, ``Python`` + distributions from `python.org <https://www.python.org/>`_ will be found + only if the value for the flags ``pymalloc`` and ``unicode`` is ``OFF`` or + ``ANY``. ``Python3_FIND_STRATEGY`` .. versionadded:: 3.15 @@ -650,6 +672,12 @@ `Stable Application Binary Interface <https://docs.python.org/3/c-api/stable.html>`_ was introduced. Specifying only major version ``3`` is equivalent to ``3.2``. +.. versionchanged:: 4.4 + When ``USE_SABI`` is specified, if ``Python3_FREE_THREADED`` is ``true`` and + Python version is ``3.15`` or upper, ``Py_TARGET_ABI3T`` preprocessor + definition will be defined rather than ``Py_LIMITED_API`` (see :pep:`803` for + more details). + When option ``WITH_SOABI`` is also specified, the module suffix will include the ``Python3_SOSABI`` value, if any.
diff --git a/Modules/FindPythonLibs.cmake b/Modules/FindPythonLibs.cmake index 272b176..f8e9705 100644 --- a/Modules/FindPythonLibs.cmake +++ b/Modules/FindPythonLibs.cmake
@@ -483,7 +483,10 @@ file(APPEND ${_filenameTmp} "#ifndef EXCLUDE_LOAD_ALL_FUNCTION\nvoid CMakeLoadAllPythonModules(void)\n{\n ${_name}_LoadAllPythonModules();\n}\n#endif\n\n#endif\n") # with configure_file() cmake complains that you may not use a file created using file(WRITE) as input file for configure_file() - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_filenameTmp}" "${_filename}" OUTPUT_QUIET ERROR_QUIET) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${_filenameTmp}" "${_filename}" + OUTPUT_QUIET ERROR_QUIET + RESULT_VARIABLE _result_copy) endfunction()
diff --git a/Modules/FindSquish.cmake b/Modules/FindSquish.cmake index f405e45..9622499 100644 --- a/Modules/FindSquish.cmake +++ b/Modules/FindSquish.cmake
@@ -299,7 +299,8 @@ set(SQUISH_CLIENT_EXECUTABLE_FOUND 1) execute_process(COMMAND "${SQUISH_CLIENT_EXECUTABLE}" --version OUTPUT_VARIABLE _squishVersionOutput - ERROR_QUIET ) + ERROR_QUIET + RESULT_VARIABLE _squishVersionResult) if("${_squishVersionOutput}" MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)") set(SQUISH_VERSION_MAJOR "${CMAKE_MATCH_1}") set(SQUISH_VERSION_MINOR "${CMAKE_MATCH_2}")
diff --git a/Modules/FindTCL.cmake b/Modules/FindTCL.cmake index d5931f3..103a231 100644 --- a/Modules/FindTCL.cmake +++ b/Modules/FindTCL.cmake
@@ -116,8 +116,10 @@ ) set(TCLTK_POSSIBLE_LIB_PATH_SUFFIXES - lib/tcl/tcl8.7 - lib/tcl/tk8.7 + lib/tcl/tcl9.1 + lib/tcl/tk9.1 + lib/tcl/tcl9.0 + lib/tcl/tk9.0 lib/tcl/tcl8.6 lib/tcl/tk8.6 lib/tcl/tcl8.5 @@ -133,12 +135,6 @@ NAME) set(TCLTK_POSSIBLE_LIB_PATHS ${TCLTK_POSSIBLE_LIB_PATHS} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ActiveState\\ActiveTcl\\${ActiveTcl_CurrentVersion}]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.6;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.5;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.4;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.3;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.2;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.0;Root]/lib" "$ENV{ProgramFiles}/Tcl/Lib" "C:/Program Files/Tcl/lib" "C:/Tcl/lib" @@ -149,7 +145,8 @@ NAMES tcl tcl${TCL_LIBRARY_VERSION} tcl${TCL_TCLSH_VERSION} tcl${TK_WISH_VERSION} - tcl87 tcl8.7 tcl87t tcl8.7t + tcl91 tcl9.1 tcl91t tcl9.1t + tcl90 tcl9.0 tcl90t tcl9.0t tcl86 tcl8.6 tcl86t tcl8.6t tcl85 tcl8.5 tcl84 tcl8.4 @@ -164,7 +161,8 @@ NAMES tk tk${TK_LIBRARY_VERSION} tk${TCL_TCLSH_VERSION} tk${TK_WISH_VERSION} - tk87 tk8.7 tk87t tk8.7t + tk91 tk9.1 tk91t tk9.1t + tk90 tk9.0 tk90t tk9.0t tk86 tk8.6 tk86t tk8.6t tk85 tk8.5 tk84 tk8.4 @@ -211,8 +209,10 @@ set(TCLTK_POSSIBLE_INCLUDE_PATH_SUFFIXES include/tcl${TK_LIBRARY_VERSION} include/tcl${TCL_LIBRARY_VERSION} - include/tcl8.7 - include/tk8.7 + include/tcl9.1 + include/tk9.1 + include/tcl9.0 + include/tk9.0 include/tcl8.6 include/tk8.6 include/tcl8.5 @@ -227,12 +227,6 @@ if(WIN32) set(TCLTK_POSSIBLE_INCLUDE_PATHS ${TCLTK_POSSIBLE_INCLUDE_PATHS} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ActiveState\\ActiveTcl\\${ActiveTcl_CurrentVersion}]/include" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.6;Root]/include" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.5;Root]/include" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.4;Root]/include" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.3;Root]/include" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.2;Root]/include" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.0;Root]/include" "$ENV{ProgramFiles}/Tcl/include" "C:/Program Files/Tcl/include" "C:/Tcl/include"
diff --git a/Modules/FindTclStub.cmake b/Modules/FindTclStub.cmake index cf57b9e..764e067 100644 --- a/Modules/FindTclStub.cmake +++ b/Modules/FindTclStub.cmake
@@ -109,12 +109,6 @@ NAME) set(TCLTK_POSSIBLE_LIB_PATHS ${TCLTK_POSSIBLE_LIB_PATHS} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ActiveState\\ActiveTcl\\${ActiveTcl_CurrentVersion}]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.6;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.5;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.4;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.3;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.2;Root]/lib" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.0;Root]/lib" "$ENV{ProgramFiles}/Tcl/Lib" "C:/Program Files/Tcl/lib" "C:/Tcl/lib" @@ -125,7 +119,8 @@ NAMES tclstub tclstub${TK_LIBRARY_VERSION} tclstub${TCL_TCLSH_VERSION} tclstub${TK_WISH_VERSION} - tclstub87 tclstub8.7 + tclstub91 tclstub9.1 + tclstub90 tclstub9.0 tclstub86 tclstub8.6 tclstub85 tclstub8.5 tclstub84 tclstub8.4 @@ -139,7 +134,8 @@ NAMES tkstub tkstub${TCL_LIBRARY_VERSION} tkstub${TCL_TCLSH_VERSION} tkstub${TK_WISH_VERSION} - tkstub87 tkstub8.7 + tkstub91 tkstub9.1 + tkstub90 tkstub9.0 tkstub86 tkstub8.6 tkstub85 tkstub8.5 tkstub84 tkstub8.4 @@ -153,8 +149,8 @@ NAMES ttkstub ttkstub${TCL_LIBRARY_VERSION} ttkstub${TCL_TCLSH_VERSION} ttkstub${TK_WISH_VERSION} - ttkstub88 ttkstub8.8 - ttkstub87 ttkstub8.7 + ttkstub91 ttkstub9.1 + ttkstub90 ttkstub9.0 ttkstub86 ttkstub8.6 ttkstub85 ttkstub8.5 PATHS ${TCLTK_POSSIBLE_LIB_PATHS}
diff --git a/Modules/FindTclsh.cmake b/Modules/FindTclsh.cmake index 81a2cbf..2d9d0fb 100644 --- a/Modules/FindTclsh.cmake +++ b/Modules/FindTclsh.cmake
@@ -108,19 +108,14 @@ NAME) set(TCLTK_POSSIBLE_BIN_PATHS ${TCLTK_POSSIBLE_BIN_PATHS} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ActiveState\\ActiveTcl\\${ActiveTcl_CurrentVersion}]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.6;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.5;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.4;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.3;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.2;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.0;Root]/bin" ) endif() set(TCL_TCLSH_NAMES tclsh tclsh${TCL_LIBRARY_VERSION} tclsh${TK_LIBRARY_VERSION} tclsh${TK_WISH_VERSION} - tclsh87 tclsh8.7 + tclsh91 tclsh9.1 + tclsh90 tclsh9.0 tclsh86 tclsh8.6 tclsh85 tclsh8.5 tclsh84 tclsh8.4 @@ -139,7 +134,8 @@ COMMAND "${TCL_TCLSH}" OUTPUT_VARIABLE Tclsh_VERSION ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _tclsh_version_result) set(TCLSH_VERSION_STRING "${Tclsh_VERSION}") endif()
diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake index 5a40718..244a536 100644 --- a/Modules/FindVulkan.cmake +++ b/Modules/FindVulkan.cmake
@@ -277,6 +277,51 @@ The path to the DirectX shader compiler CLI tool. +``Vulkan_Layer_API_DUMP_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `api_dump` Vulkan Layer library. + +``Vulkan_Layer_CRASH_DIAGNOSTIC_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `crash_diagnostic` Vulkan Layer library. + +``Vulkan_Layer_GFXRECONSTRUCT_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `gfxreconstruct` Vulkan Layer library. + +``Vulkan_Layer_KHRONOS_PROFILES_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `khronos_profiles` Vulkan Layer library. + +``Vulkan_Layer_KHRONOS_SHADER_OBJECT_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `khronos_shader_object` Vulkan Layer library. + +``Vulkan_Layer_KHRONOS_SYNCHRONIZATION2_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `khronos_synchronization2` Vulkan Layer library. + +``Vulkan_Layer_KHRONOS_VALIDATION_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `khronos_validation` Vulkan Layer library. + +``Vulkan_Layer_MONITOR_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `monitor` Vulkan Layer library. + +``Vulkan_Layer_SCREENSHOT_LIBRARY`` + .. versionadded:: 4.4 + + Path to the `screenshot` Vulkan Layer library. + Hints ^^^^^ @@ -419,6 +464,31 @@ ) mark_as_advanced(Vulkan_LIBRARY) +# On iOS search for Validation Layers, which must be explicitly linked. +if (IOS) + foreach(layer IN ITEMS + api_dump + crash_diagnostic + gfxreconstruct + khronos_profiles + khronos_shader_object + khronos_synchronization2 + khronos_validation + monitor + screenshot + ) + string(TOUPPER "${layer}" LAYER) + find_library(Vulkan_Layer_${LAYER}_LIBRARY + NAMES VkLayer_${layer} + HINTS + ${_Vulkan_hint_library_search_paths} + OPTIONAL + ) + mark_as_advanced(Vulkan_Layer_${LAYER}_LIBRARY) + endforeach() +endif() + +# Search for requested components. if(glslc IN_LIST Vulkan_FIND_COMPONENTS) find_program(Vulkan_GLSLC_EXECUTABLE NAMES glslc
diff --git a/Modules/FindWish.cmake b/Modules/FindWish.cmake index 7e9038f..35d72cc 100644 --- a/Modules/FindWish.cmake +++ b/Modules/FindWish.cmake
@@ -84,18 +84,14 @@ NAME) set(TCLTK_POSSIBLE_BIN_PATHS ${TCLTK_POSSIBLE_BIN_PATHS} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ActiveState\\ActiveTcl\\${ActiveTcl_CurrentVersion}]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.6;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.5;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.4;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.3;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.2;Root]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.0;Root]/bin" ) endif() set(TK_WISH_NAMES wish wish${TCL_LIBRARY_VERSION} wish${TK_LIBRARY_VERSION} wish${TCL_TCLSH_VERSION} + wish91 wish9.1 + wish90 wish9.0 wish86 wish8.6 wish85 wish8.5 wish84 wish8.4
diff --git a/Modules/FindXCTest.cmake b/Modules/FindXCTest.cmake index 7b4cc41..4ffcf8a 100644 --- a/Modules/FindXCTest.cmake +++ b/Modules/FindXCTest.cmake
@@ -150,7 +150,8 @@ execute_process( COMMAND xcrun --find xctest OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_VARIABLE _xcrun_err) + ERROR_VARIABLE _xcrun_err + RESULT_VARIABLE _xcrun_result) if(_xcrun_out) set(XCTest_EXECUTABLE "${_xcrun_out}" CACHE FILEPATH "XCTest executable") mark_as_advanced(XCTest_EXECUTABLE)
diff --git a/Modules/FindwxWindows.cmake b/Modules/FindwxWindows.cmake index 90cb0b7..f869cce 100644 --- a/Modules/FindwxWindows.cmake +++ b/Modules/FindwxWindows.cmake
@@ -708,7 +708,8 @@ # rpath: execute_process(COMMAND ${CMAKE_WXWINDOWS_WXCONFIG_EXECUTABLE} ${WX_CONFIG_ARGS_LIBS} - OUTPUT_VARIABLE WX_CONFIG_LIBS ) + OUTPUT_VARIABLE WX_CONFIG_LIBS + RESULT_VARIABLE _wx_config_libs_result) ## extract linkdirs (-L) for rpath ## use regular expression to match wildcard equivalent "-L*<endchar>"
diff --git a/Modules/FortranCInterface.cmake b/Modules/FortranCInterface.cmake index d559ab7..a90425f 100644 --- a/Modules/FortranCInterface.cmake +++ b/Modules/FortranCInterface.cmake
@@ -420,6 +420,7 @@ TARGET VerifyFortranC SOURCE_DIR ${FortranCInterface_SOURCE_DIR}/Verify BINARY_DIR ${FortranCInterface_BINARY_DIR}/Verify${lang} + NO_CACHE CMAKE_FLAGS -DVERIFY_CXX=${verify_cxx} -DCMAKE_VERBOSE_MAKEFILE=ON "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}" @@ -443,7 +444,6 @@ message(CHECK_FAIL "Failed") set(FortranCInterface_VERIFIED_${lang} 0 CACHE INTERNAL "Fortran/${lang} compatibility") endif() - unset(FortranCInterface_VERIFY_${lang}_COMPILED CACHE) endif() # Error if compilers are incompatible.
diff --git a/Modules/FortranCInterface/Detect.cmake b/Modules/FortranCInterface/Detect.cmake index a4ee521..4f8311e 100644 --- a/Modules/FortranCInterface/Detect.cmake +++ b/Modules/FortranCInterface/Detect.cmake
@@ -45,6 +45,7 @@ TARGET FortranCInterface SOURCE_DIR ${FortranCInterface_SOURCE_DIR} BINARY_DIR ${FortranCInterface_BINARY_DIR} + NO_CACHE LOG_DESCRIPTION "Fortran/C interface test project" CMAKE_FLAGS "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}" @@ -54,8 +55,6 @@ ${_FortranCInterface_OSX_ARCH} ${_FortranCInterface_EXE_LINKER_FLAGS} ) -set(FortranCInterface_COMPILED ${FortranCInterface_COMPILED}) -unset(FortranCInterface_COMPILED CACHE) unset(_FortranCInterface_EXE_LINKER_FLAGS) unset(_FortranCInterface_OSX_ARCH)
diff --git a/Modules/GNUInstallDirs.cmake b/Modules/GNUInstallDirs.cmake index c2aa29a..d626688 100644 --- a/Modules/GNUInstallDirs.cmake +++ b/Modules/GNUInstallDirs.cmake
@@ -84,8 +84,11 @@ ``LIBDIR`` object code libraries (``lib`` or ``lib64``) + On Linux, when not cross-compiling, the default is the distro's + arch-specific library directory, such as ``lib`` or ``lib64``. On Debian, this may be ``lib/<multiarch-tuple>`` when :variable:`CMAKE_INSTALL_PREFIX` is ``/usr``. + When cross-compiling, the default is ``lib``. .. note:: @@ -441,31 +444,28 @@ function(_GNUInstallDirs_LIBDIR_get_default out_var install_prefix) set(${out_var} "${_GNUInstallDirs_LIBDIR_DEFAULT}") - # Override this default 'lib' with 'lib64' iff: - # - we are on Linux system but NOT cross-compiling - # - we are NOT on debian - # - we are NOT building for conda - # - we are on a 64 bits system - # reason is: amd64 ABI: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI - # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if - # CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu" - # and CMAKE_INSTALL_PREFIX is "/usr" - # See http://wiki.debian.org/Multiarch if (NOT DEFINED CMAKE_SYSTEM_NAME OR NOT DEFINED CMAKE_SIZEOF_VOID_P) message(AUTHOR_WARNING "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. " "Please enable at least one language before including GNUInstallDirs.") endif() + + # When not cross-compiling, detect the host distro's arch-specific 'lib'. + # When cross-compiling, the host's distro is meaningless to the target, + # so it is up to the caller to specify the correct 'lib' directory. if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|GNU)$" AND NOT CMAKE_CROSSCOMPILING) _GNUInstallDirs_get_system_type_for_install(system_type) if(system_type STREQUAL "debian") + # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if + # CMAKE_INSTALL_PREFIX is "/usr". See http://wiki.debian.org/Multiarch if(CMAKE_LIBRARY_ARCHITECTURE) if("${install_prefix}" MATCHES "^/usr/?$") set(${out_var} "lib/${CMAKE_LIBRARY_ARCHITECTURE}") endif() endif() elseif(NOT DEFINED system_type) - # not debian, alpine, arch, or conda so rely on CMAKE_SIZEOF_VOID_P: + # This host system uses a 'lib64' directory for 64-bit architectures. + # See https://gitlab.com/x86-psABIs/x86-64-ABI if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") set(${out_var} "lib64") endif()
diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake index a0e621a..9f836f0 100644 --- a/Modules/GoogleTest.cmake +++ b/Modules/GoogleTest.cmake
@@ -381,12 +381,10 @@ # the latter. set(__newArgs ${arg_new_EXTRA_ARGS}) if(NOT "${arg_EXTRA_ARGS}" STREQUAL "${__newArgs}") - cmake_policy(GET_WARNING CMP0178 cmp0178_warning) - message(AUTHOR_WARNING + cmake_policy(ISSUE_WARNING CMP0178 PRE "The EXTRA_ARGS contain one or more empty values. Those empty " "values are being silently discarded to preserve backward " - "compatibility.\n" - "${cmp0178_warning}" + "compatibility." ) endif() endblock() @@ -588,33 +586,15 @@ endif() set(arg_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE}) endif() - - get_property( - has_counter - TARGET ${target} - PROPERTY CTEST_DISCOVERED_TEST_COUNTER - SET - ) - if(has_counter) - get_property( - counter - TARGET ${target} - PROPERTY CTEST_DISCOVERED_TEST_COUNTER - ) - math(EXPR counter "${counter} + 1") - else() - set(counter 1) + if(arg_PROPERTIES) + list(LENGTH arg_PROPERTIES _len_PROPERTIES) + math(EXPR _odd_PROPERTIES "${_len_PROPERTIES} % 2") + if(_odd_PROPERTIES) + list(POP_BACK arg_PROPERTIES _back_PROPERTY) + message(AUTHOR_WARNING "PROPERTIES should be key-value pairs. Ignoring unpaired key '${_back_PROPERTY}'.") + endif() endif() - set_property( - TARGET ${target} - PROPERTY CTEST_DISCOVERED_TEST_COUNTER - ${counter} - ) - # Define rule to generate test list for aforementioned test executable - set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${target}[${counter}]") - set(ctest_include_file "${ctest_file_base}_include.cmake") - set(ctest_tests_file "${ctest_file_base}_tests.cmake") get_property(test_launcher TARGET ${target} PROPERTY TEST_LAUNCHER @@ -660,123 +640,134 @@ set(test_executor ${test_executor}) if(NOT cmp0178 STREQUAL "OLD") if(NOT "${test_executor}" STREQUAL "${test_executor_orig}") - cmake_policy(GET_WARNING CMP0178 cmp0178_warning) - message(AUTHOR_WARNING + cmake_policy(ISSUE_WARNING CMP0178 PRE "The '${target}' target's TEST_LAUNCHER or CROSSCOMPILING_EMULATOR " "test properties contain one or more empty values. Those empty " "values are being silently discarded to preserve backward " - "compatibility.\n" - "${cmp0178_warning}" + "compatibility." ) endif() # Unescape semicolons from the PARSE_ARGV form's value before comparing string(REPLACE [[\;]] ";" new_arg_EXTRA_ARGS "${new_arg_EXTRA_ARGS}") if(NOT "${old_arg_EXTRA_ARGS}" STREQUAL "${new_arg_EXTRA_ARGS}") - cmake_policy(GET_WARNING CMP0178 cmp0178_warning) - message(AUTHOR_WARNING + cmake_policy(ISSUE_WARNING CMP0178 PRE "The EXTRA_ARGS value contains one or more empty values. " "Those empty values are being silently discarded to preserve " - "backward compatibility.\n" - "${cmp0178_warning}" + "backward compatibility." ) endif() endif() endif() + string(SHA256 argn_hash "${ARGN}") + string(SUBSTRING "${argn_hash}" 0 8 argn_hash) + string(MAKE_C_IDENTIFIER "${target}_${argn_hash}" ctest_file_base) + string(PREPEND ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/") + + get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + string(APPEND ctest_file_base "_$<CONFIG>") + endif() + + set(discovery_file "${ctest_file_base}_discovery.cmake") + set(ctest_include_file "${ctest_file_base}_include.cmake") + set(ctest_tests_file "${ctest_file_base}_tests.cmake") + + list(JOIN test_executor "]==] [==[" test_executor) + list(JOIN arg_EXTRA_ARGS "]==] [==[" arg_EXTRA_ARGS) + list(JOIN arg_PROPERTIES "]==] [==[" arg_PROPERTIES) + list(JOIN arg_DISCOVERY_EXTRA_ARGS "]==] [==[" arg_DISCOVERY_EXTRA_ARGS) + + # Resolve alias to support custom target properties. + get_target_property(_gt_real_target ${target} ALIASED_TARGET) + if(NOT _gt_real_target) + set(_gt_real_target ${target}) + endif() + + get_target_property( + _current_gt_post_build_discovery ${_gt_real_target} _GT_POST_BUILD_DISCOVERY + ) + if("${discovery_file}" IN_LIST _current_gt_post_build_discovery) + message(AUTHOR_WARNING + "gtest_discover_tests() invoked multiple times with the same arguments " + "for the same target '${target}'. This invocation will be ignored." + ) + return() + endif() + + set_property( + TARGET ${_gt_real_target} + APPEND PROPERTY _GT_POST_BUILD_DISCOVERY "${discovery_file}" + ) + + string(CONCAT discovery_content + "include(\"${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake\")" "\n" + "gtest_discover_tests_impl(" "\n" + " TEST_TARGET" " [==[${target}]==]" "\n" + " TEST_EXECUTABLE" " [==[$<TARGET_FILE:${target}>]==]" "\n" + " TEST_EXECUTOR" " [==[${test_executor}]==]" "\n" + " TEST_WORKING_DIR" " [==[${arg_WORKING_DIRECTORY}]==]" "\n" + " TEST_EXTRA_ARGS" " [==[${arg_EXTRA_ARGS}]==]" "\n" + " TEST_PROPERTIES" " [==[${arg_PROPERTIES}]==]" "\n" + " TEST_PREFIX" " [==[${arg_TEST_PREFIX}]==]" "\n" + " TEST_SUFFIX" " [==[${arg_TEST_SUFFIX}]==]" "\n" + " TEST_FILTER" " [==[${arg_TEST_FILTER}]==]" "\n" + " NO_PRETTY_TYPES" " [==[${arg_NO_PRETTY_TYPES}]==]" "\n" + " NO_PRETTY_VALUES" " [==[${arg_NO_PRETTY_VALUES}]==]" "\n" + " TEST_LIST" " [==[${arg_TEST_LIST}]==]" "\n" + " CTEST_FILE" " [==[${ctest_tests_file}]==]" "\n" + " TEST_DISCOVERY_TIMEOUT" " [==[${arg_DISCOVERY_TIMEOUT}]==]" "\n" + " TEST_DISCOVERY_EXTRA_ARGS [==[${arg_DISCOVERY_EXTRA_ARGS}]==]" "\n" + " TEST_XML_OUTPUT_DIR" " [==[${arg_XML_OUTPUT_DIR}]==]" "\n" + " TEST_JSON_OUTPUT_DIR" " [==[${CMAKE_CURRENT_BINARY_DIR}]==]" "\n" + ")" "\n" + ) + file(GENERATE OUTPUT "${discovery_file}" CONTENT "${discovery_content}") + + set(ctest_include_content) if(arg_DISCOVERY_MODE STREQUAL "POST_BUILD") + # Make sure that TEST_LAUNCHER and CROSSCOMPILING_EMULATOR appear on the + # command line, so that CMake can add them as implicit dependencies in the + # case where they are executable targets. add_custom_command( TARGET ${target} POST_BUILD BYPRODUCTS "${ctest_tests_file}" - COMMAND "${CMAKE_COMMAND}" - -D "TEST_TARGET=${target}" - -D "TEST_EXECUTABLE=$<TARGET_FILE:${target}>" - -D "TEST_EXECUTOR=${test_executor}" - -D "TEST_WORKING_DIR=${arg_WORKING_DIRECTORY}" - -D "TEST_EXTRA_ARGS=${arg_EXTRA_ARGS}" - -D "TEST_PROPERTIES=${arg_PROPERTIES}" - -D "TEST_PREFIX=${arg_TEST_PREFIX}" - -D "TEST_SUFFIX=${arg_TEST_SUFFIX}" - -D "TEST_FILTER=${arg_TEST_FILTER}" - -D "NO_PRETTY_TYPES=${arg_NO_PRETTY_TYPES}" - -D "NO_PRETTY_VALUES=${arg_NO_PRETTY_VALUES}" - -D "TEST_LIST=${arg_TEST_LIST}" - -D "CTEST_FILE=${ctest_tests_file}" - -D "TEST_DISCOVERY_TIMEOUT=${arg_DISCOVERY_TIMEOUT}" - -D "TEST_DISCOVERY_EXTRA_ARGS=${arg_DISCOVERY_EXTRA_ARGS}" - -D "TEST_XML_OUTPUT_DIR=${arg_XML_OUTPUT_DIR}" - -D "TEST_JSON_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}" - -P "${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake" - VERBATIM - ) - - file(WRITE "${ctest_include_file}" - "if(EXISTS \"${ctest_tests_file}\")\n" - " include(\"${ctest_tests_file}\")\n" - "else()\n" - " add_test(${target}_NOT_BUILT ${target}_NOT_BUILT)\n" - "endif()\n" + COMMAND "${CMAKE_COMMAND}" -P "${discovery_file}" -- "${test_executor}" ) elseif(arg_DISCOVERY_MODE STREQUAL "PRE_TEST") - - get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL - PROPERTY GENERATOR_IS_MULTI_CONFIG - ) - - if(GENERATOR_IS_MULTI_CONFIG) - set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake") - endif() - - string(CONCAT ctest_include_content - "if(EXISTS \"$<TARGET_FILE:${target}>\")" "\n" - " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" + string(APPEND ctest_include_content + "if(EXISTS \"$<TARGET_FILE:${target}>\")" "\n" + " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${target}>\" OR\n" " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n" - " include(\"${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake\")" "\n" - " gtest_discover_tests_impl(" "\n" - " TEST_TARGET" " [==[${target}]==]" "\n" - " TEST_EXECUTABLE" " [==[$<TARGET_FILE:${target}>]==]" "\n" - " TEST_EXECUTOR" " [==[${test_executor}]==]" "\n" - " TEST_WORKING_DIR" " [==[${arg_WORKING_DIRECTORY}]==]" "\n" - " TEST_EXTRA_ARGS" " [==[${arg_EXTRA_ARGS}]==]" "\n" - " TEST_PROPERTIES" " [==[${arg_PROPERTIES}]==]" "\n" - " TEST_PREFIX" " [==[${arg_TEST_PREFIX}]==]" "\n" - " TEST_SUFFIX" " [==[${arg_TEST_SUFFIX}]==]" "\n" - " TEST_FILTER" " [==[${arg_TEST_FILTER}]==]" "\n" - " NO_PRETTY_TYPES" " [==[${arg_NO_PRETTY_TYPES}]==]" "\n" - " NO_PRETTY_VALUES" " [==[${arg_NO_PRETTY_VALUES}]==]" "\n" - " TEST_LIST" " [==[${arg_TEST_LIST}]==]" "\n" - " CTEST_FILE" " [==[${ctest_tests_file}]==]" "\n" - " TEST_DISCOVERY_TIMEOUT" " [==[${arg_DISCOVERY_TIMEOUT}]==]" "\n" - " TEST_DISCOVERY_EXTRA_ARGS [==[${arg_DISCOVERY_EXTRA_ARGS}]==]" "\n" - " TEST_XML_OUTPUT_DIR" " [==[${arg_XML_OUTPUT_DIR}]==]" "\n" - " TEST_JSON_OUTPUT_DIR" " [==[${CMAKE_CURRENT_BINARY_DIR}]==]" "\n" - " )" "\n" - " endif()" "\n" - " include(\"${ctest_tests_file}\")" "\n" - "else()" "\n" - " add_test(${target}_NOT_BUILT ${target}_NOT_BUILT)" "\n" - "endif()" "\n" + " include(\"${discovery_file}\")" "\n" + " endif()" "\n" + "endif()" "\n" ) - - if(GENERATOR_IS_MULTI_CONFIG) - file(GENERATE - OUTPUT "${ctest_file_base}_include-$<CONFIG>.cmake" - CONTENT "${ctest_include_content}" - ) - file(WRITE "${ctest_include_file}" - "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" - ) - else() - file(GENERATE - OUTPUT "${ctest_include_file}" - CONTENT "${ctest_include_content}" - ) - endif() - else() message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${arg_DISCOVERY_MODE}") endif() + string(APPEND ctest_include_content + "if(EXISTS \"${ctest_tests_file}\")" "\n" + " include(\"${ctest_tests_file}\")" "\n" + "else()" "\n" + " add_test(${target}_NOT_BUILT ${target}_NOT_BUILT)" "\n" + "endif()" "\n" + ) + file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_content}") + + if(is_multi_config) + string(REPLACE [[_$<CONFIG>]] [[_${CTEST_CONFIGURATION_TYPE}]] + include_file_cfg "${ctest_include_file}") + string(REPLACE [[_$<CONFIG>]] "" ctest_include_file "${ctest_include_file}") + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${include_file_cfg}\")" "\n" + " include(\"${include_file_cfg}\")" "\n" + "endif()" "\n" + ) + endif() + # Add discovered tests to directory TEST_INCLUDE_FILES set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
diff --git a/Modules/GoogleTest/ParseTestList.cmake b/Modules/GoogleTest/ParseTestList.cmake new file mode 100644 index 0000000..077e874 --- /dev/null +++ b/Modules/GoogleTest/ParseTestList.cmake
@@ -0,0 +1,145 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include_guard(GLOBAL) + +function(generate_testname_guards output open_guard_var close_guard_var) + set(open_guard "[=[") + set(close_guard "]=]") + set(counter 1) + while("${output}" MATCHES "${close_guard}") + math(EXPR counter "${counter} + 1") + string(REPEAT "=" ${counter} equals) + set(open_guard "[${equals}[") + set(close_guard "]${equals}]") + endwhile() + set(${open_guard_var} "${open_guard}" PARENT_SCOPE) + set(${close_guard_var} "${close_guard}" PARENT_SCOPE) +endfunction() + +function(escape_square_brackets output bracket placeholder placeholder_var output_var) + if("${output}" MATCHES "\\${bracket}") + set(placeholder "${placeholder}") + while("${output}" MATCHES "${placeholder}") + set(placeholder "${placeholder}_") + endwhile() + string(REPLACE "${bracket}" "${placeholder}" output "${output}") + set(${placeholder_var} "${placeholder}" PARENT_SCOPE) + set(${output_var} "${output}" PARENT_SCOPE) + endif() +endfunction() + +macro(parse_tests_from_output per_test_callback) + generate_testname_guards("${output}" open_guard close_guard) + escape_square_brackets("${output}" "[" "__osb" open_sb output) + escape_square_brackets("${output}" "]" "__csb" close_sb output) + + # Preserve semicolon in test-parameters + string(REPLACE [[;]] [[\;]] output "${output}") + string(REPLACE "\n" ";" output "${output}") + + # Command line output doesn't contain information about the file and line number of the tests + set(current_test_file "") + set(current_test_line "") + + # Parse output + foreach(line ${output}) + # Skip header + if(line MATCHES "gtest_main\\.cc") + continue() + endif() + + if(line STREQUAL "") + continue() + endif() + + # Do we have a module name or a test name? + if(NOT line MATCHES "^ ") + set(current_test_type_param "") + + # Module; remove trailing '.' to get just the name... + string(REGEX REPLACE "\\.( *#.*)?$" "" current_test_suite "${line}") + if(line MATCHES "# *TypeParam = (.*)$") + set(current_test_type_param "${CMAKE_MATCH_1}") + endif() + else() + string(STRIP "${line}" test) + string(REGEX REPLACE " ( *#.*)?$" "" current_test_name "${test}") + + set(current_test_value_param "") + if(line MATCHES "# *GetParam\\(\\) = (.*)$") + set(current_test_value_param "${CMAKE_MATCH_1}") + endif() + + cmake_language(CALL ${per_test_callback}) + endif() + endforeach() +endmacro() + +macro(get_json_member_with_default json_variable member_name out_variable) + string(JSON ${out_variable} + ERROR_VARIABLE error_param + GET "${${json_variable}}" "${member_name}" + ) + if(error_param) + # Member not present + set(${out_variable} "") + endif() +endmacro() + +macro(parse_tests_from_json json_file per_test_callback) + if(NOT EXISTS "${json_file}") + message(FATAL_ERROR "Missing expected JSON file with test list: ${json_file}") + endif() + + file(READ "${json_file}" test_json) + string(JSON test_suites_json GET "${test_json}" "testsuites") + + # Return if there are no testsuites + string(JSON len_test_suites LENGTH "${test_suites_json}") + if(len_test_suites GREATER 0) + set(open_sb) + set(close_sb) + + math(EXPR upper_limit_test_suite_range "${len_test_suites} - 1") + + foreach(index_test_suite RANGE ${upper_limit_test_suite_range}) + string(JSON test_suite_json GET "${test_suites_json}" ${index_test_suite}) + + # "suite" is expected to be set in write_test_to_file(). When parsing the + # plain text output, "suite" is expected to be the original suite name + # before accounting for pretty names. This may be used to construct the + # name of XML output results files. + string(JSON current_test_suite GET "${test_suite_json}" "name") + string(JSON tests_json GET "${test_suite_json}" "testsuite") + + # Skip test suites without tests + string(JSON len_tests LENGTH "${tests_json}") + if(len_tests LESS_EQUAL 0) + continue() + endif() + + math(EXPR upper_limit_test_range "${len_tests} - 1") + foreach(index_test RANGE ${upper_limit_test_range}) + string(JSON test_json GET "${tests_json}" ${index_test}) + + string(JSON len_test_parameters LENGTH "${test_json}") + if(len_test_parameters LESS_EQUAL 0) + continue() + endif() + + get_json_member_with_default(test_json "name" current_test_name) + get_json_member_with_default(test_json "file" current_test_file) + get_json_member_with_default(test_json "line" current_test_line) + get_json_member_with_default(test_json "value_param" current_test_value_param) + get_json_member_with_default(test_json "type_param" current_test_type_param) + + generate_testname_guards( + "${current_test_suite}${current_test_name}${current_test_value_param}${current_test_type_param}" + open_guard close_guard + ) + cmake_language(CALL ${per_test_callback}) + endforeach() + endforeach() + endif() +endmacro()
diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake index 04c7123..0774792 100644 --- a/Modules/GoogleTestAddTests.cmake +++ b/Modules/GoogleTestAddTests.cmake
@@ -2,45 +2,7 @@ # file LICENSE.rst or https://cmake.org/licensing for details. cmake_minimum_required(VERSION 4.2) - -function(add_command name test_name) - set(args "") - foreach(arg ${ARGN}) - if(arg MATCHES "[^-./:a-zA-Z0-9_]") - string(APPEND args " [==[${arg}]==]") - else() - string(APPEND args " ${arg}") - endif() - endforeach() - string(APPEND script "${name}(${test_name} ${args})\n") - set(script "${script}" PARENT_SCOPE) -endfunction() - -function(generate_testname_guards output open_guard_var close_guard_var) - set(open_guard "[=[") - set(close_guard "]=]") - set(counter 1) - while("${output}" MATCHES "${close_guard}") - math(EXPR counter "${counter} + 1") - string(REPEAT "=" ${counter} equals) - set(open_guard "[${equals}[") - set(close_guard "]${equals}]") - endwhile() - set(${open_guard_var} "${open_guard}" PARENT_SCOPE) - set(${close_guard_var} "${close_guard}" PARENT_SCOPE) -endfunction() - -function(escape_square_brackets output bracket placeholder placeholder_var output_var) - if("${output}" MATCHES "\\${bracket}") - set(placeholder "${placeholder}") - while("${output}" MATCHES "${placeholder}") - set(placeholder "${placeholder}_") - endwhile() - string(REPLACE "${bracket}" "${placeholder}" output "${output}") - set(${placeholder_var} "${placeholder}" PARENT_SCOPE) - set(${output_var} "${output}" PARENT_SCOPE) - endif() -endfunction() +include("${CMAKE_CURRENT_LIST_DIR}/GoogleTest/ParseTestList.cmake") macro(write_test_to_file) # Store the gtest test name before messing with these strings @@ -52,7 +14,7 @@ # Handle disabled tests set(maybe_DISABLED "") if(pretty_test_suite MATCHES "^DISABLED_" OR pretty_test_name MATCHES "^DISABLED_") - set(maybe_DISABLED DISABLED YES) + set(maybe_DISABLED "DISABLED YES") string(REGEX REPLACE "^DISABLED_" "" pretty_test_suite "${pretty_test_suite}") string(REGEX REPLACE "^DISABLED_" "" pretty_test_name "${pretty_test_name}") endif() @@ -98,9 +60,6 @@ string(REPLACE "${close_sb}" "]" testname "${testname}") endif() set(guarded_testname "${open_guard}${testname}${close_guard}") - # Add to script. Do not use add_command() here because it messes up the - # handling of empty values when forwarding arguments, and we need to - # preserve those carefully for arg_TEST_EXECUTOR and arg_EXTRA_ARGS. string(APPEND script "add_test(${guarded_testname} ${launcherArgs}") foreach(arg IN ITEMS "${arg_TEST_EXECUTABLE}" @@ -124,17 +83,24 @@ set(maybe_LOCATION "") if(NOT current_test_file STREQUAL "" AND NOT current_test_line STREQUAL "") - set(maybe_LOCATION DEF_SOURCE_LINE "${current_test_file}:${current_test_line}") + set(maybe_LOCATION "DEF_SOURCE_LINE [==[${current_test_file}:${current_test_line}]==]") endif() - add_command(set_tests_properties - "${guarded_testname}" - PROPERTIES - ${maybe_DISABLED} - ${maybe_LOCATION} - WORKING_DIRECTORY "${arg_TEST_WORKING_DIR}" - SKIP_REGULAR_EXPRESSION "\\[ SKIPPED \\]" - ${arg_TEST_PROPERTIES} + set(maybe_properties) + if(arg_TEST_PROPERTIES) + list(JOIN arg_TEST_PROPERTIES "]] [[" maybe_properties) + set(maybe_properties "[[${maybe_properties}]]") + endif() + + string(APPEND script + "set_tests_properties(${guarded_testname}\n" + " PROPERTIES\n" + " ${maybe_DISABLED}\n" + " ${maybe_LOCATION}\n" + " WORKING_DIRECTORY [==[${arg_TEST_WORKING_DIR}]==]\n" + " SKIP_REGULAR_EXPRESSION [==[\\[ SKIPPED \\]]==]\n" + " ${maybe_properties}\n" + ")\n" ) # possibly unbalanced square brackets render lists invalid so skip such @@ -163,123 +129,7 @@ endif() endmacro() -macro(parse_tests_from_output) - generate_testname_guards("${output}" open_guard close_guard) - escape_square_brackets("${output}" "[" "__osb" open_sb output) - escape_square_brackets("${output}" "]" "__csb" close_sb output) - - # Preserve semicolon in test-parameters - string(REPLACE [[;]] [[\;]] output "${output}") - string(REPLACE "\n" ";" output "${output}") - - # Command line output doesn't contain information about the file and line number of the tests - set(current_test_file "") - set(current_test_line "") - - # Parse output - foreach(line ${output}) - # Skip header - if(line MATCHES "gtest_main\\.cc") - continue() - endif() - - if(line STREQUAL "") - continue() - endif() - - # Do we have a module name or a test name? - if(NOT line MATCHES "^ ") - set(current_test_type_param "") - - # Module; remove trailing '.' to get just the name... - string(REGEX REPLACE "\\.( *#.*)?$" "" current_test_suite "${line}") - if(line MATCHES "# *TypeParam = (.*)$") - set(current_test_type_param "${CMAKE_MATCH_1}") - endif() - else() - string(STRIP "${line}" test) - string(REGEX REPLACE " ( *#.*)?$" "" current_test_name "${test}") - - set(current_test_value_param "") - if(line MATCHES "# *GetParam\\(\\) = (.*)$") - set(current_test_value_param "${CMAKE_MATCH_1}") - endif() - - write_test_to_file() - endif() - endforeach() -endmacro() - -macro(get_json_member_with_default json_variable member_name out_variable) - string(JSON ${out_variable} - ERROR_VARIABLE error_param - GET "${${json_variable}}" "${member_name}" - ) - if(error_param) - # Member not present - set(${out_variable} "") - endif() -endmacro() - -macro(parse_tests_from_json json_file) - if(NOT EXISTS "${json_file}") - message(FATAL_ERROR "Missing expected JSON file with test list: ${json_file}") - endif() - - file(READ "${json_file}" test_json) - string(JSON test_suites_json GET "${test_json}" "testsuites") - - # Return if there are no testsuites - string(JSON len_test_suites LENGTH "${test_suites_json}") - if(len_test_suites GREATER 0) - set(open_sb) - set(close_sb) - - math(EXPR upper_limit_test_suite_range "${len_test_suites} - 1") - - foreach(index_test_suite RANGE ${upper_limit_test_suite_range}) - string(JSON test_suite_json GET "${test_suites_json}" ${index_test_suite}) - - # "suite" is expected to be set in write_test_to_file(). When parsing the - # plain text output, "suite" is expected to be the original suite name - # before accounting for pretty names. This may be used to construct the - # name of XML output results files. - string(JSON current_test_suite GET "${test_suite_json}" "name") - string(JSON tests_json GET "${test_suite_json}" "testsuite") - - # Skip test suites without tests - string(JSON len_tests LENGTH "${tests_json}") - if(len_tests LESS_EQUAL 0) - continue() - endif() - - math(EXPR upper_limit_test_range "${len_tests} - 1") - foreach(index_test RANGE ${upper_limit_test_range}) - string(JSON test_json GET "${tests_json}" ${index_test}) - - string(JSON len_test_parameters LENGTH "${test_json}") - if(len_test_parameters LESS_EQUAL 0) - continue() - endif() - - get_json_member_with_default(test_json "name" current_test_name) - get_json_member_with_default(test_json "file" current_test_file) - get_json_member_with_default(test_json "line" current_test_line) - get_json_member_with_default(test_json "value_param" current_test_value_param) - get_json_member_with_default(test_json "type_param" current_test_type_param) - - generate_testname_guards( - "${current_test_suite}${current_test_name}${current_test_value_param}${current_test_type_param}" - open_guard close_guard - ) - write_test_to_file() - endforeach() - endforeach() - endif() -endmacro() - function(gtest_discover_tests_impl) - set(options "") set(oneValueArgs NO_PRETTY_TYPES # These two take a value, unlike gtest_discover_tests() @@ -294,16 +144,14 @@ TEST_DISCOVERY_TIMEOUT TEST_XML_OUTPUT_DIR TEST_JSON_OUTPUT_DIR - # The following are all multi-value arguments in gtest_discover_tests(), - # but they are each given to us as a single argument. We parse them that - # way to avoid problems with preserving empty list values and escaping. TEST_FILTER + ) + set(multiValueArgs TEST_EXTRA_ARGS TEST_DISCOVERY_EXTRA_ARGS TEST_PROPERTIES TEST_EXECUTOR ) - set(multiValueArgs "") cmake_parse_arguments(PARSE_ARGV 0 arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ) @@ -394,7 +242,7 @@ endif() if(EXISTS "${json_file}") - parse_tests_from_json("${json_file}") + parse_tests_from_json("${json_file}" write_test_to_file) else() # gtest < 1.8.1, and all gtest compiled with GTEST_HAS_FILE_SYSTEM=0, don't # recognize the --gtest_output=json option, and issue a warning or error on @@ -406,7 +254,7 @@ # an argument because it messes up the contents passed through due to the # different escaping, etc. that gets applied. We rely on it picking up the # "output" variable we have already set here. - parse_tests_from_output() + parse_tests_from_output(write_test_to_file) endif() if(NOT tests_buffer STREQUAL "") @@ -415,30 +263,9 @@ # Create a list of all discovered tests, which users may use to e.g. set # properties on the tests - add_command(set "" ${arg_TEST_LIST} "${tests}") + list(JOIN tests "]==] [==[" tests) + string(APPEND script "set(${arg_TEST_LIST} [==[${tests}]==])\n") # Write remaining content to the CTest script file(APPEND "${arg_CTEST_FILE}" "${script}") endfunction() - -if(CMAKE_SCRIPT_MODE_FILE) - gtest_discover_tests_impl( - NO_PRETTY_TYPES ${NO_PRETTY_TYPES} - NO_PRETTY_VALUES ${NO_PRETTY_VALUES} - TEST_TARGET ${TEST_TARGET} - TEST_EXECUTABLE ${TEST_EXECUTABLE} - TEST_EXECUTOR "${TEST_EXECUTOR}" - TEST_WORKING_DIR ${TEST_WORKING_DIR} - TEST_PREFIX ${TEST_PREFIX} - TEST_SUFFIX ${TEST_SUFFIX} - TEST_FILTER ${TEST_FILTER} - TEST_LIST ${TEST_LIST} - CTEST_FILE ${CTEST_FILE} - TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT} - TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR} - TEST_JSON_OUTPUT_DIR ${TEST_JSON_OUTPUT_DIR} - TEST_EXTRA_ARGS "${TEST_EXTRA_ARGS}" - TEST_DISCOVERY_EXTRA_ARGS "${TEST_DISCOVERY_EXTRA_ARGS}" - TEST_PROPERTIES "${TEST_PROPERTIES}" - ) -endif()
diff --git a/Modules/InstallRequiredSystemLibraries.cmake b/Modules/InstallRequiredSystemLibraries.cmake index 8f1543b..591bb67 100644 --- a/Modules/InstallRequiredSystemLibraries.cmake +++ b/Modules/InstallRequiredSystemLibraries.cmake
@@ -141,6 +141,10 @@ set(CMAKE_MSVC_ARCH amd64) endif() endif() + if(CMAKE_MSVC_ARCH STREQUAL "arm64ec") + set(CMAKE_MSVC_ARCH "arm64") + endif() + get_filename_component(devenv_dir "${CMAKE_MAKE_PROGRAM}" PATH) get_filename_component(base_dir "${devenv_dir}/../.." ABSOLUTE)
diff --git a/Modules/Internal/CMakeASM_POASMLinkerInformation.cmake b/Modules/Internal/CMakeASM_POASMLinkerInformation.cmake new file mode 100644 index 0000000..4f8e9f5 --- /dev/null +++ b/Modules/Internal/CMakeASM_POASMLinkerInformation.cmake
@@ -0,0 +1,9 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +# Support for the PellesC assembler, poasm + +# Load the generic ASMInformation file: +set(ASM_DIALECT "_POASM") +include(Internal/CMakeASMLinkerInformation) +set(ASM_DIALECT)
diff --git a/Modules/Internal/CMakeCUDAFindToolkit.cmake b/Modules/Internal/CMakeCUDAFindToolkit.cmake index 8fd4089..ff67ef0 100644 --- a/Modules/Internal/CMakeCUDAFindToolkit.cmake +++ b/Modules/Internal/CMakeCUDAFindToolkit.cmake
@@ -110,7 +110,8 @@ # 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) + OUTPUT_VARIABLE _CUDA_NVCC_OUT ERROR_VARIABLE _CUDA_NVCC_OUT + RESULT_VARIABLE _result_nvcc_out) if(_CUDA_NVCC_OUT MATCHES "\\#\\$ TOP=([^\r\n]*)") get_filename_component(${lang_var_}TOOLKIT_ROOT "${CMAKE_MATCH_1}" ABSOLUTE) else() @@ -161,7 +162,9 @@ # 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) + execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "--version" + OUTPUT_VARIABLE CMAKE_${lang}_COMPILER_ID_OUTPUT + RESULT_VARIABLE _result_nvcc_version) endif() if(CMAKE_${lang}_COMPILER_ID_OUTPUT MATCHES [=[V([0-9]+\.[0-9]+\.[0-9]+)]=])
diff --git a/Modules/Internal/CMakeChipStarHIP.cmake b/Modules/Internal/CMakeChipStarHIP.cmake index 1249220..4e03b76 100644 --- a/Modules/Internal/CMakeChipStarHIP.cmake +++ b/Modules/Internal/CMakeChipStarHIP.cmake
@@ -42,7 +42,8 @@ set(_hip_path "$ENV{HIP_PATH}") if(NOT IS_DIRECTORY "${_hip_path}") execute_process(COMMAND hipconfig --rocmpath - OUTPUT_VARIABLE _hip_path OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_VARIABLE _hip_path OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_hipconfig) endif() if(IS_DIRECTORY "${_hip_path}") file(TO_CMAKE_PATH "${_hip_path}" _hip_path)
diff --git a/Modules/Internal/CMakeDetermineLinkerId.cmake b/Modules/Internal/CMakeDetermineLinkerId.cmake index df2ddbe..7e2e433 100644 --- a/Modules/Internal/CMakeDetermineLinkerId.cmake +++ b/Modules/Internal/CMakeDetermineLinkerId.cmake
@@ -21,7 +21,7 @@ # Compute the linker ID and version. foreach(flags IN ITEMS - "-v" # AppleClang, GNU, GNUgold, MOLD + "-v" # AppleClang, GNU, GNUgold, MOLD, WILD "-V" # AIX, Solaris "--version" # LLD ) @@ -31,7 +31,10 @@ OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL NONE + RESULT_VARIABLE _res ) + # We capture "_res" but don't check it because it's expected that + # linkers will exit with non-zero on flags they do not have. string(JOIN "\" \"" flags_string ${flags}) string(REGEX REPLACE "\n\n.*" "" linker_desc_head "${linker_desc}") @@ -45,6 +48,11 @@ set(linker_frontend "GNU") set(linker_version "${CMAKE_MATCH_1}") break() + elseif(linker_desc MATCHES "Wild version ([0-9.]+)") + set(linker_id "WILD") + set(linker_frontend "GNU") + set(linker_version "${CMAKE_MATCH_1}") + break() elseif(linker_desc MATCHES "mold \\(sold\\) ([0-9.]+)") set(linker_id "MOLD") set(linker_frontend "GNU") @@ -78,6 +86,11 @@ set(linker_frontend "MSVC") set(linker_version "${CMAKE_MATCH_1}") break() + elseif(linker_desc MATCHES "Open Watcom Linker Version ([0-9.]+)") + set(linker_id "OpenWatcom") + set(linker_frontend "OpenWatcom") + set(linker_version "${CMAKE_MATCH_1}") + break() elseif (CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND linker_desc MATCHES "Solaris Link Editors: ([0-9.-]+)") set(linker_id "Solaris") set(linker_version "${CMAKE_MATCH_1}")
diff --git a/Modules/Internal/CMakeInspectASM_POASMLinker.cmake b/Modules/Internal/CMakeInspectASM_POASMLinker.cmake new file mode 100644 index 0000000..1fba36a --- /dev/null +++ b/Modules/Internal/CMakeInspectASM_POASMLinker.cmake
@@ -0,0 +1,7 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +# Load the generic ASM test file: +set(ASM_DIALECT "_POASM") +include(Internal/CMakeInspectASMLinker) +set(ASM_DIALECT)
diff --git a/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake b/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake index ea07a22..076ab1c 100644 --- a/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake +++ b/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake
@@ -108,6 +108,7 @@ else() string(APPEND _nvcc_log " extracted link launcher name: [${_nvcc_host_link_launcher}]\n") find_program(_nvcc_find_host_link_launcher + NO_CACHE NAMES ${_nvcc_host_link_launcher} PATHS ${_nvcc_path} NO_DEFAULT_PATH) find_program(_nvcc_find_host_link_launcher @@ -119,7 +120,7 @@ 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) + unset(_nvcc_find_host_link_launcher) endif() endif()
diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake index 046adc2..499331c 100644 --- a/Modules/Internal/CPack/CPackDeb.cmake +++ b/Modules/Internal/CPack/CPackDeb.cmake
@@ -14,9 +14,9 @@ set(FALLBACK_VAR_NAMES ${ARGN}) foreach(variable_name IN LISTS FALLBACK_VAR_NAMES) - if(${variable_name}) - set(${OUTPUT_VAR_NAME} "${${variable_name}}" PARENT_SCOPE) - break() + if(DEFINED ${variable_name}) + set(${OUTPUT_VAR_NAME} "${${variable_name}}") + return(PROPAGATE ${OUTPUT_VAR_NAME}) endif() endforeach() endfunction() @@ -29,53 +29,60 @@ string(TOLOWER "${CPACK_DEBIAN_PACKAGE_NAME}-${component}" package_name) endif() - set("${var}" "${package_name}" PARENT_SCOPE) + set(${var} "${package_name}") + return(PROPAGATE ${var}) endfunction() #extract library name and version for given shared object function(extract_so_info shared_object libname version) - if(CPACK_READELF_EXECUTABLE) - execute_process(COMMAND "${CPACK_READELF_EXECUTABLE}" -d "${shared_object}" - WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" - RESULT_VARIABLE result - OUTPUT_VARIABLE output - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(result EQUAL 0) - string(REGEX MATCH "\\(?SONAME\\)?[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}") - set(${libname} "${CMAKE_MATCH_1}" PARENT_SCOPE) - set(${version} "${CMAKE_MATCH_2}" PARENT_SCOPE) - else() - message(WARNING "Error running readelf for \"${shared_object}\"") - endif() - else() + if(NOT CPACK_READELF_EXECUTABLE) message(FATAL_ERROR "Readelf utility is not available.") + return() + endif() + + execute_process( + COMMAND "${CPACK_READELF_EXECUTABLE}" -d "${shared_object}" + WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" + RESULT_VARIABLE result + OUTPUT_VARIABLE output + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(result EQUAL 0) + string(REGEX MATCH "\\(?SONAME\\)?[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}") + set(${libname} "${CMAKE_MATCH_1}" PARENT_SCOPE) + set(${version} "${CMAKE_MATCH_2}" PARENT_SCOPE) + else() + message(WARNING "Error running readelf for \"${shared_object}\"") endif() endfunction() #extract RUNPATH and RPATH for given shared object or executable function(extract_runpath_and_rpath shared_object_or_executable runpath rpath) - if(CPACK_READELF_EXECUTABLE) - execute_process(COMMAND "${CPACK_READELF_EXECUTABLE}" -d "${shared_object_or_executable}" - WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" - RESULT_VARIABLE result - OUTPUT_VARIABLE output - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(result EQUAL 0) - string(REGEX MATCH "\\(?RUNPATH\\)?[^\n]*\\[([^\n]+)\\]" found_runpath "${output}") - string(REPLACE ":" ";" found_runpath "${CMAKE_MATCH_1}") - list(REMOVE_DUPLICATES found_runpath) - string(REGEX MATCH "\\(?RPATH\\)?[^\n]*\\[([^\n]+)\\]" found_rpath "${output}") - string(REPLACE ":" ";" found_rpath "${CMAKE_MATCH_1}") - list(REMOVE_DUPLICATES found_rpath) - set(${runpath} "${found_runpath}" PARENT_SCOPE) - set(${rpath} "${found_rpath}" PARENT_SCOPE) - else() - message(WARNING "Error running readelf for \"${shared_object_or_executable}\"") - endif() - else() + if(NOT CPACK_READELF_EXECUTABLE) message(FATAL_ERROR "Readelf utility is not available.") + return() + endif() + + execute_process( + COMMAND "${CPACK_READELF_EXECUTABLE}" -d "${shared_object_or_executable}" + WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" + RESULT_VARIABLE result + OUTPUT_VARIABLE output + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(result EQUAL 0) + string(REGEX MATCH "\\(?RUNPATH\\)?[^\n]*\\[([^\n]+)\\]" found_runpath "${output}") + string(REPLACE ":" ";" found_runpath "${CMAKE_MATCH_1}") + list(REMOVE_DUPLICATES found_runpath) + string(REGEX MATCH "\\(?RPATH\\)?[^\n]*\\[([^\n]+)\\]" found_rpath "${output}") + string(REPLACE ":" ";" found_rpath "${CMAKE_MATCH_1}") + list(REMOVE_DUPLICATES found_rpath) + set(${runpath} "${found_runpath}" PARENT_SCOPE) + set(${rpath} "${found_rpath}" PARENT_SCOPE) + else() + message(WARNING "Error running readelf for \"${shared_object_or_executable}\"") endif() endfunction() @@ -84,12 +91,13 @@ # NOTE: This pattern has to stay in sync with the 'prohibited_chars' variable # defined in the C++ function `CPackGenerator::GetSanitizedDirOrFileName`! set(prohibited_chars_pattern "[<]|[>]|[\"]|[/]|[\\]|[|]|[?]|[*]|[`]") - if("${dirname}" MATCHES "${prohibited_chars_pattern}") + if(dirname MATCHES "${prohibited_chars_pattern}") string(MD5 sanitized_dirname "${dirname}") - set(${outvar} "${sanitized_dirname}" PARENT_SCOPE) + set(${outvar} "${sanitized_dirname}") else() - set(${outvar} "${dirname}" PARENT_SCOPE) + set(${outvar} "${dirname}") endif() + return(PROPAGATE ${outvar}) endfunction() #retrieve packaging directories of components the current component depends on @@ -97,7 +105,9 @@ function(get_packaging_dirs_of_dependencies outvar) if(CPACK_DEB_PACKAGE_COMPONENT) if(NOT DEFINED WDIR OR NOT DEFINED _local_component_name) - message(FATAL_ERROR "CPackDeb: Function '${CMAKE_CURRENT_FUNCTION}' not called from correct function scope!") + message( + FATAL_ERROR "CPackDeb: Function '${CMAKE_CURRENT_FUNCTION}' not called from correct function scope!" + ) endif() set(result_list) foreach(dependency_name IN LISTS CPACK_COMPONENT_${_local_component_name}_DEPENDS) @@ -106,30 +116,28 @@ cmake_path(NORMAL_PATH dependency_packaging_dir) list(APPEND result_list "${dependency_packaging_dir}") endforeach() - set(${outvar} "${result_list}" PARENT_SCOPE) # Set return variable. + set(${outvar} "${result_list}") # Set return variable. else() - set(${outvar} "" PARENT_SCOPE) # Clear return variable. + set(${outvar} "") # Clear return variable. endif() + return(PROPAGATE ${outvar}) endfunction() -function(cpack_deb_check_description SUMMARY LINES RESULT_VARIABLE) - set(_result TRUE) - +function(cpack_deb_check_description summary lines result_variable) # Get the summary line - if(NOT SUMMARY MATCHES "^[^\\s].*$") - set(_result FALSE) - set(${RESULT_VARIABLE} ${_result} PARENT_SCOPE) - return() + if(NOT summary MATCHES "^[^\\s].*$") + set(${result_variable} FALSE) + else() + set(${result_variable} TRUE) + foreach(_line IN LISTS lines) + if(NOT _line MATCHES "^ +[^ ]+.*$") + set(${result_variable} FALSE) + break() + endif() + endforeach() endif() - foreach(_line IN LISTS LINES) - if(NOT _line MATCHES "^ +[^ ]+.*$") - set(_result FALSE) - break() - endif() - endforeach() - - set(${RESULT_VARIABLE} ${_result} PARENT_SCOPE) + return(PROPAGATE ${result_variable}) endfunction() function(cpack_deb_format_package_description TEXT OUTPUT_VAR) @@ -153,8 +161,8 @@ cpack_deb_check_description("${_summary}" "${_lines}" _result) if(_result) # Ok, no formatting required - set(${OUTPUT_VAR} "${TEXT}" PARENT_SCOPE) - return() + set(${OUTPUT_VAR} "${TEXT}") + return(PROPAGATE ${OUTPUT_VAR}) endif() # Format the summary line @@ -180,7 +188,8 @@ list(PREPEND _result "${_summary}") list(JOIN _result "\n" _result) string(REPLACE "${uuid}" ";" _result "${_result}") - set(${OUTPUT_VAR} "${_result}" PARENT_SCOPE) + set(${OUTPUT_VAR} "${_result}") + return(PROPAGATE ${OUTPUT_VAR}) endfunction() function(cpack_deb_prepare_package_vars) @@ -190,8 +199,11 @@ set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF) endif() - set(WDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}") - set(DBGSYMDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}-dbgsym") + cmake_path( + APPEND CPACK_TOPLEVEL_DIRECTORY "${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}" + OUTPUT_VARIABLE WDIR + ) + cmake_path(APPEND_STRING WDIR "-dbgsym" OUTPUT_VARIABLE DBGSYMDIR) file(REMOVE_RECURSE "${DBGSYMDIR}") # per component automatic discover: some of the component might not have @@ -204,32 +216,47 @@ if(DEFINED ${_component_shlibdeps_var}) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS "${${_component_shlibdeps_var}}") if(CPACK_DEBIAN_PACKAGE_DEBUG) - message("CPackDeb Debug: component '${CPACK_DEB_PACKAGE_COMPONENT}' dpkg-shlibdeps set to ${CPACK_DEBIAN_PACKAGE_SHLIBDEPS}") + message( + "CPackDeb Debug: component '${CPACK_DEB_PACKAGE_COMPONENT}' dpkg-shlibdeps set to " + "${CPACK_DEBIAN_PACKAGE_SHLIBDEPS}" + ) endif() endif() endif() - cpack_deb_variable_fallback("CPACK_DEBIAN_DEBUGINFO_PACKAGE" - "CPACK_DEBIAN_${_local_component_name}_DEBUGINFO_PACKAGE" - "CPACK_DEBIAN_DEBUGINFO_PACKAGE") + cpack_deb_variable_fallback( + CPACK_DEBIAN_DEBUGINFO_PACKAGE + CPACK_DEBIAN_${_local_component_name}_DEBUGINFO_PACKAGE + CPACK_DEBIAN_DEBUGINFO_PACKAGE + ) if(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OR CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS OR CPACK_DEBIAN_DEBUGINFO_PACKAGE) # Generating binary list - Get type of all install files file(GLOB_RECURSE FILE_PATHS_ LIST_DIRECTORIES false RELATIVE "${WDIR}" "${WDIR}/*") find_program(FILE_EXECUTABLE file) if(NOT FILE_EXECUTABLE) - message(FATAL_ERROR "CPackDeb: file utility is not available. CPACK_DEBIAN_PACKAGE_SHLIBDEPS and CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS options are not available.") + message( + FATAL_ERROR + "CPackDeb: file utility is not available. CPACK_DEBIAN_PACKAGE_SHLIBDEPS and " + "CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS options are not available." + ) endif() # get file info so that we can determine if file is executable or not unset(CPACK_DEB_INSTALL_FILES) foreach(FILE_ IN LISTS FILE_PATHS_) - execute_process(COMMAND ${CMAKE_COMMAND} -E env LC_ALL=C ${FILE_EXECUTABLE} "./${FILE_}" + execute_process( + COMMAND "${CMAKE_COMMAND}" -E env LC_ALL=C "${FILE_EXECUTABLE}" "./${FILE_}" WORKING_DIRECTORY "${WDIR}" RESULT_VARIABLE FILE_RESULT_ - OUTPUT_VARIABLE INSTALL_FILE_) + OUTPUT_VARIABLE INSTALL_FILE_ + ) if(NOT FILE_RESULT_ EQUAL 0) - message(FATAL_ERROR "CPackDeb: execution of command: '${FILE_EXECUTABLE} ./${FILE_}' failed with exit code: ${FILE_RESULT_}") + message( + FATAL_ERROR + "CPackDeb: execution of command: '${FILE_EXECUTABLE} ./${FILE_}' failed with " + "exit code: ${FILE_RESULT_}" + ) endif() list(APPEND CPACK_DEB_INSTALL_FILES "${INSTALL_FILE_}") endforeach() @@ -265,16 +292,22 @@ message(FATAL_ERROR "debuginfo packages require the readelf tool") endif() - file(RELATIVE_PATH _DBGSYM_ROOT "${CPACK_TEMPORARY_DIRECTORY}" "${DBGSYMDIR}") + cmake_path( + RELATIVE_PATH DBGSYMDIR + BASE_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" + OUTPUT_VARIABLE _DBGSYM_ROOT + ) foreach(_FILE IN LISTS CPACK_DEB_UNSTRIPPED_FILES) # Get the file's Build ID - execute_process(COMMAND env LC_ALL=C ${CPACK_READELF_EXECUTABLE} -n "${_FILE}" + execute_process( + COMMAND env LC_ALL=C "${CPACK_READELF_EXECUTABLE}" -n "${_FILE}" WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" OUTPUT_VARIABLE READELF_OUTPUT RESULT_VARIABLE READELF_RESULT ERROR_VARIABLE READELF_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE ) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) if(NOT READELF_RESULT EQUAL 0) message(FATAL_ERROR "CPackDeb: readelf: '${READELF_ERROR}';\n" "executed command: '${CPACK_READELF_EXECUTABLE} -n ${_FILE}'") @@ -288,38 +321,52 @@ endif() # Split out the debug symbols from the binaries - set(_FILE_DBGSYM ${_DBGSYM_ROOT}/usr/lib/debug/.build-id/${_BUILD_ID_START}/${_BUILD_ID_REMAINING}.debug) - get_filename_component(_OUT_DIR "${_FILE_DBGSYM}" DIRECTORY) + cmake_path( + APPEND _DBGSYM_ROOT "usr/lib/debug/.build-id/${_BUILD_ID_START}/${_BUILD_ID_REMAINING}.debug" + OUTPUT_VARIABLE _FILE_DBGSYM + ) + cmake_path(GET _FILE_DBGSYM PARENT_PATH _OUT_DIR) file(MAKE_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}/${_OUT_DIR}") - execute_process(COMMAND ${CPACK_OBJCOPY_EXECUTABLE} --only-keep-debug "${_FILE}" "${_FILE_DBGSYM}" + execute_process( + COMMAND "${CPACK_OBJCOPY_EXECUTABLE}" --only-keep-debug "${_FILE}" "${_FILE_DBGSYM}" WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" OUTPUT_VARIABLE OBJCOPY_OUTPUT RESULT_VARIABLE OBJCOPY_RESULT ERROR_VARIABLE OBJCOPY_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE ) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) if(NOT OBJCOPY_RESULT EQUAL 0) - message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n" - "executed command: '${CPACK_OBJCOPY_EXECUTABLE} --only-keep-debug ${_FILE} ${_FILE_DBGSYM}'") + message( + FATAL_ERROR + "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n" + "executed command: '${CPACK_OBJCOPY_EXECUTABLE} --only-keep-debug ${_FILE} ${_FILE_DBGSYM}'" + ) endif() - execute_process(COMMAND ${CPACK_OBJCOPY_EXECUTABLE} --strip-unneeded ${_FILE} + execute_process( + COMMAND "${CPACK_OBJCOPY_EXECUTABLE}" --strip-unneeded ${_FILE} WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" OUTPUT_VARIABLE OBJCOPY_OUTPUT RESULT_VARIABLE OBJCOPY_RESULT ERROR_VARIABLE OBJCOPY_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE ) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) if(NOT OBJCOPY_RESULT EQUAL 0) message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n" "executed command: '${CPACK_OBJCOPY_EXECUTABLE} --strip-debug ${_FILE}'") endif() - execute_process(COMMAND ${CPACK_OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE} + execute_process( + COMMAND "${CPACK_OBJCOPY_EXECUTABLE}" --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE} WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" OUTPUT_VARIABLE OBJCOPY_OUTPUT RESULT_VARIABLE OBJCOPY_RESULT ERROR_VARIABLE OBJCOPY_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE ) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) if(NOT OBJCOPY_RESULT EQUAL 0) - message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n" - "executed command: '${CPACK_OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE}'") + message( + FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n" + "executed command: '${CPACK_OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE}'" + ) endif() endforeach() endif() @@ -330,10 +377,13 @@ if(SHLIBDEPS_EXECUTABLE) # Check version of the dpkg-shlibdeps tool using CPackDEB method - execute_process(COMMAND ${CMAKE_COMMAND} -E env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --version + execute_process( + COMMAND "${CMAKE_COMMAND}" -E env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --version OUTPUT_VARIABLE _TMP_VERSION ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_shlibdeps_version + ) if(_TMP_VERSION MATCHES "dpkg-shlibdeps version ([0-9]+\\.[0-9]+\\.[0-9]+)") set(SHLIBDEPS_EXECUTABLE_VERSION "${CMAKE_MATCH_1}") else() @@ -351,17 +401,21 @@ # Create blank control file for running dpkg-shlibdeps # There might be some other way to invoke dpkg-shlibdeps without creating this file # but standard debian package should not have anything that can collide with this file or directory - file(MAKE_DIRECTORY ${CPACK_TEMPORARY_DIRECTORY}/debian) - file(WRITE ${CPACK_TEMPORARY_DIRECTORY}/debian/control "") + cmake_path(APPEND CPACK_TEMPORARY_DIRECTORY debian OUTPUT_VARIABLE _debian_path) + file(MAKE_DIRECTORY ${_debian_path}) + file(WRITE ${_debian_path}/control "") # Create a DEBIAN directory so that dpkg-shlibdeps can find the package dir when resolving $ORIGIN. file(MAKE_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}/DEBIAN") # Add --ignore-missing-info if the tool supports it - execute_process(COMMAND ${CMAKE_COMMAND} -E env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --help + execute_process( + COMMAND "${CMAKE_COMMAND}" -E env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --help OUTPUT_VARIABLE _TMP_HELP ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_shlibdeps_help + ) if(_TMP_HELP MATCHES "--ignore-missing-info") set(IGNORE_MISSING_INFO_FLAG "--ignore-missing-info") endif() @@ -382,15 +436,15 @@ get_packaging_dirs_of_dependencies(deps_packaging_dirs) foreach(exe IN LISTS CPACK_DEB_BINARY_FILES) cmake_path(GET exe PARENT_PATH exe_dir) - extract_runpath_and_rpath(${exe} runpath rpath) + extract_runpath_and_rpath("${exe}" runpath rpath) # If RUNPATH is available, RPATH will be ignored. Therefore we have to do the same here! - if (NOT "${runpath}" STREQUAL "") + if(runpath) set(selected_rpath "${runpath}") else() set(selected_rpath "${rpath}") endif() foreach(search_path IN LISTS selected_rpath) - if ("${search_path}" MATCHES "^[$]ORIGIN" OR "${search_path}" MATCHES "^[$][{]ORIGIN[}]") + if(search_path MATCHES "^[$]ORIGIN" OR search_path MATCHES "^[$][{]ORIGIN[}]") foreach(deps_pkgdir IN LISTS deps_packaging_dirs) string(REPLACE "\$ORIGIN" "${deps_pkgdir}/${exe_dir}" path "${search_path}") string(REPLACE "\${ORIGIN}" "${deps_pkgdir}/${exe_dir}" path "${path}") @@ -403,44 +457,60 @@ list(REMOVE_DUPLICATES PRIVATE_SEARCH_DIR_OPTIONS) elseif(NOT "${CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS}" STREQUAL "") - message(WARNING "CPackDeb: dkpg-shlibdeps is too old. \"CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS\" is therefore ignored.") + message( + WARNING + "CPackDeb: dkpg-shlibdeps is too old. \"CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS\" " + "is therefore ignored." + ) endif() # Execute dpkg-shlibdeps # --ignore-missing-info : allow dpkg-shlibdeps to run even if some libs do not belong to a package # -l<dir>: make dpkg-shlibdeps also search in this directory for (private) shared library dependencies # -O : print to STDOUT - execute_process(COMMAND ${SHLIBDEPS_EXECUTABLE} ${PRIVATE_SEARCH_DIR_OPTIONS} ${IGNORE_MISSING_INFO_FLAG} -O ${CPACK_DEB_BINARY_FILES} + execute_process( + COMMAND "${SHLIBDEPS_EXECUTABLE}" ${PRIVATE_SEARCH_DIR_OPTIONS} ${IGNORE_MISSING_INFO_FLAG} -O ${CPACK_DEB_BINARY_FILES} WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" OUTPUT_VARIABLE SHLIBDEPS_OUTPUT RESULT_VARIABLE SHLIBDEPS_RESULT ERROR_VARIABLE SHLIBDEPS_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE ) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) # E2K OSL 6.0.1 and prior has broken dpkg-shlibdeps. CPack will deal with that (mocking SHLIBDEPS_OUTPUT), but inform user of this. - if("${SHLIBDEPS_ERROR}" MATCHES "unknown gcc system type e2k.*, falling back to default") - message(WARNING "CPackDeb: broken dpkg-shlibdeps on E2K detected, will fall back to minimal dependencies.\n" - "You should expect that dependencies list in the package will be incomplete.") + if(SHLIBDEPS_ERROR MATCHES "unknown gcc system type e2k.*, falling back to default") + message( + WARNING + "CPackDeb: broken dpkg-shlibdeps on E2K detected, will fall back to minimal dependencies.\n" + "You should expect that dependencies list in the package will be incomplete." + ) set(SHLIBDEPS_OUTPUT "shlibs:Depends=libc6, lcc-libs") endif() if(CPACK_DEBIAN_PACKAGE_DEBUG) # dpkg-shlibdeps will throw some warnings if some input files are not binary - message( "CPackDeb Debug: dpkg-shlibdeps warnings \n${SHLIBDEPS_ERROR}") + message("CPackDeb Debug: dpkg-shlibdeps warnings \n${SHLIBDEPS_ERROR}") endif() if(NOT SHLIBDEPS_RESULT EQUAL 0) - message(FATAL_ERROR "CPackDeb: dpkg-shlibdeps: '${SHLIBDEPS_ERROR}';\n" - "executed command: '${SHLIBDEPS_EXECUTABLE} ${PRIVATE_SEARCH_DIR_OPTIONS} ${IGNORE_MISSING_INFO_FLAG} -O ${CPACK_DEB_BINARY_FILES}';\n" - "found files: '${INSTALL_FILE_}';\n" - "files info: '${CPACK_DEB_INSTALL_FILES}';\n" - "binary files: '${CPACK_DEB_BINARY_FILES}'") + message( + FATAL_ERROR + "CPackDeb: dpkg-shlibdeps: '${SHLIBDEPS_ERROR}';\n" + "executed command: '${SHLIBDEPS_EXECUTABLE} ${PRIVATE_SEARCH_DIR_OPTIONS} " + "${IGNORE_MISSING_INFO_FLAG} -O ${CPACK_DEB_BINARY_FILES}';\n" + "found files: '${INSTALL_FILE_}';\n" + "files info: '${CPACK_DEB_INSTALL_FILES}';\n" + "binary files: '${CPACK_DEB_BINARY_FILES}'" + ) endif() #Get rid of prefix generated by dpkg-shlibdeps string(REGEX REPLACE "^.*Depends=" "" CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS "${SHLIBDEPS_OUTPUT}") if(CPACK_DEBIAN_PACKAGE_DEBUG) - message("CPackDeb Debug: Found dependency: ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS} from output ${SHLIBDEPS_OUTPUT}") + message(" + CPackDeb Debug: Found dependency: ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS} from " + "output ${SHLIBDEPS_OUTPUT}" + ) endif() # Remove blank control file @@ -451,11 +521,17 @@ file(REMOVE_RECURSE "${CPACK_TEMPORARY_DIRECTORY}/DEBIAN") else() if(CPACK_DEBIAN_PACKAGE_DEBUG) - message(AUTHOR_WARNING "CPackDeb Debug: Using only user-provided depends because package does not contain executable files that link to shared libraries.") + message( + AUTHOR_WARNING + "CPackDeb Debug: Using only user-provided depends because package does not " + "contain executable files that link to shared libraries." + ) endif() endif() else() - message("CPackDeb: Using only user-provided dependencies because dpkg-shlibdeps is not found.") + message( + "CPackDeb: Using only user-provided dependencies because dpkg-shlibdeps is not found." + ) endif() else() @@ -515,8 +591,11 @@ # CMake 3.10 did not check for version format so we have to preserve # backward compatibility if(CPACK_DEBIAN_PACKAGE_VERSION MATCHES ".*-.*") - message(FATAL_ERROR - "CPackDeb: Debian package version must not contain hyphens when CPACK_DEBIAN_PACKAGE_RELEASE is not provided!") + message( + FATAL_ERROR + "CPackDeb: Debian package version must not contain hyphens when " + "CPACK_DEBIAN_PACKAGE_RELEASE is not provided!" + ) endif() endif() @@ -529,21 +608,24 @@ "${CPACK_DEBIAN_PACKAGE_EPOCH}:${CPACK_DEBIAN_PACKAGE_VERSION}") endif() + find_program(DPKG_EXECUTABLE dpkg) + # Architecture: (mandatory) if(CPACK_DEB_PACKAGE_COMPONENT AND CPACK_DEBIAN_${_local_component_name}_PACKAGE_ARCHITECTURE) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CPACK_DEBIAN_${_local_component_name}_PACKAGE_ARCHITECTURE}") elseif(NOT CPACK_DEBIAN_PACKAGE_ARCHITECTURE) # There is no such thing as i686 architecture on debian, you should use i386 instead # $ dpkg --print-architecture - find_program(DPKG_CMD dpkg) - if(NOT DPKG_CMD) + if(NOT DPKG_EXECUTABLE) message(STATUS "CPackDeb: Can not find dpkg in your path, default to i386.") set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE i386) endif() - execute_process(COMMAND "${DPKG_CMD}" --print-architecture + execute_process( + COMMAND "${DPKG_EXECUTABLE}" --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE - ) + RESULT_VARIABLE _result_dpkg_arch + ) endif() # Source: (optional) @@ -564,7 +646,13 @@ # if per-component variable, overrides the global CPACK_DEBIAN_PACKAGE_${variable_type_} # automatic dependency discovery will be performed afterwards. if(CPACK_DEB_PACKAGE_COMPONENT) - foreach(value_type_ IN ITEMS DEPENDS RECOMMENDS SUGGESTS PREDEPENDS ENHANCES BREAKS CONFLICTS PROVIDES REPLACES MULTIARCH SOURCE SECTION PRIORITY NAME) + set( + _value_types + DEPENDS RECOMMENDS SUGGESTS PREDEPENDS ENHANCES BREAKS + CONFLICTS PROVIDES REPLACES MULTIARCH SOURCE SECTION + PRIORITY NAME + ) + foreach(value_type_ IN LISTS _value_types) set(_component_var "CPACK_DEBIAN_${_local_component_name}_PACKAGE_${value_type_}") # if set, overrides the global variable @@ -605,7 +693,11 @@ # Maintainer: (mandatory) if(NOT CPACK_DEBIAN_PACKAGE_MAINTAINER) if(NOT CPACK_PACKAGE_CONTACT) - message(FATAL_ERROR "CPackDeb: Debian package requires a maintainer for a package, set CPACK_PACKAGE_CONTACT or CPACK_DEBIAN_PACKAGE_MAINTAINER") + message( + FATAL_ERROR + "CPackDeb: Debian package requires a maintainer for a package, set " + "CPACK_PACKAGE_CONTACT or CPACK_DEBIAN_PACKAGE_MAINTAINER" + ) endif() set(CPACK_DEBIAN_PACKAGE_MAINTAINER ${CPACK_PACKAGE_CONTACT}) endif() @@ -618,13 +710,17 @@ set(_desc_fallback "CPACK_PACKAGE_DESCRIPTION") endif() if(CPACK_DEB_PACKAGE_COMPONENT) - cpack_deb_variable_fallback("CPACK_DEBIAN_PACKAGE_DESCRIPTION" - "CPACK_DEBIAN_${_local_component_name}_DESCRIPTION" - "CPACK_COMPONENT_${_local_component_name}_DESCRIPTION") + cpack_deb_variable_fallback( + CPACK_DEBIAN_PACKAGE_DESCRIPTION + CPACK_DEBIAN_${_local_component_name}_DESCRIPTION + CPACK_COMPONENT_${_local_component_name}_DESCRIPTION + ) else() - cpack_deb_variable_fallback("CPACK_DEBIAN_PACKAGE_DESCRIPTION" - "CPACK_DEBIAN_PACKAGE_DESCRIPTION" - ${_desc_fallback}) + cpack_deb_variable_fallback( + CPACK_DEBIAN_PACKAGE_DESCRIPTION + CPACK_DEBIAN_PACKAGE_DESCRIPTION + ${_desc_fallback} + ) endif() # Still no description? ... and description file has set ... @@ -643,7 +739,8 @@ else() # Giving up! Report an error... set(_description_failure_message - "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION") + "CPackDeb: Debian package requires a summary for a package, set " + "CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION") if(CPACK_DEB_PACKAGE_COMPONENT) string(APPEND _description_failure_message " or CPACK_DEBIAN_${_local_component_name}_DESCRIPTION") @@ -684,12 +781,16 @@ if(CPACK_DEBIAN_ARCHIVE_TYPE) if(CPACK_DEBIAN_ARCHIVE_TYPE STREQUAL "paxr") - message(DEPRECATION "CPACK_DEBIAN_ARCHIVE_TYPE set to old and invalid " - "type 'paxr', mapping to 'gnutar'") + message( + DEPRECATION + "CPACK_DEBIAN_ARCHIVE_TYPE set to old and invalid type 'paxr', mapping to 'gnutar'" + ) set(CPACK_DEBIAN_ARCHIVE_TYPE "gnutar") elseif(NOT CPACK_DEBIAN_ARCHIVE_TYPE STREQUAL "gnutar") - message(FATAL_ERROR "CPACK_DEBIAN_ARCHIVE_TYPE set to unsupported" - "type ${CPACK_DEBIAN_ARCHIVE_TYPE}") + message( + FATAL_ERROR + "CPACK_DEBIAN_ARCHIVE_TYPE set to unsupported type ${CPACK_DEBIAN_ARCHIVE_TYPE}" + ) endif() else() set(CPACK_DEBIAN_ARCHIVE_TYPE "gnutar") @@ -739,58 +840,107 @@ foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES) extract_so_info("${_FILE}" libname soversion) if(libname AND DEFINED soversion) - list(APPEND CPACK_DEBIAN_PACKAGE_SHLIBS_LIST - "${libname} ${soversion} ${CPACK_DEBIAN_PACKAGE_NAME} (${CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY} ${CPACK_DEBIAN_PACKAGE_VERSION})") + list( + APPEND CPACK_DEBIAN_PACKAGE_SHLIBS_LIST + "${libname} ${soversion} ${CPACK_DEBIAN_PACKAGE_NAME} (${CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY} ${CPACK_DEBIAN_PACKAGE_VERSION})" + ) else() - message(AUTHOR_WARNING "Shared library '${_FILE}' is missing soname or soversion. Library will not be added to DEBIAN/shlibs control file.") + message( + AUTHOR_WARNING + "Shared library '${_FILE}' is missing soname or soversion. " + "Library will not be added to DEBIAN/shlibs control file." + ) endif() endforeach() list(JOIN CPACK_DEBIAN_PACKAGE_SHLIBS_LIST "\n" CPACK_DEBIAN_PACKAGE_SHLIBS_LIST) else() - message(FATAL_ERROR "Readelf utility is not available. CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS option is not available.") + message( + FATAL_ERROR + "Readelf utility is not available. CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS option is not available." + ) endif() endif() # add ldconfig call in default postrm and postint - set(CPACK_ADD_LDCONFIG_CALL 0) + set(_PACKAGE_HAS_SHARED_LIBS 0) # all files in CPACK_DEB_SHARED_OBJECT_FILES have dot at the beginning set(_LDCONF_DEFAULTS "./lib" "./usr/lib") foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES) - get_filename_component(_DIR ${_FILE} DIRECTORY) - get_filename_component(_PARENT_DIR ${_DIR} DIRECTORY) + cmake_path(GET _FILE PARENT_PATH _DIR) + cmake_path(GET _DIR PARENT_PATH _PARENT_DIR) if(_DIR IN_LIST _LDCONF_DEFAULTS OR _PARENT_DIR IN_LIST _LDCONF_DEFAULTS) - set(CPACK_ADD_LDCONFIG_CALL 1) + set(_PACKAGE_HAS_SHARED_LIBS 1) endif() endforeach() - if(CPACK_ADD_LDCONFIG_CALL) - set(CPACK_DEBIAN_GENERATE_POSTINST 1) - set(CPACK_DEBIAN_GENERATE_POSTRM 1) - foreach(f IN LISTS CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA) - get_filename_component(n "${f}" NAME) - if(n STREQUAL "postinst") + if(_PACKAGE_HAS_SHARED_LIBS) + # Expecting a modern distro to generate `debian/triggers` instead + # of legacy `ldconfig` calls from postinst/postrm + set(_dpkg_is_old FALSE) + if(DPKG_EXECUTABLE) + execute_process( + COMMAND "${DPKG_EXECUTABLE}" --version + OUTPUT_VARIABLE DPKG_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_dpkg_version + ) + set(_dpkg_version_re "Debian 'dpkg' package management program version ([0-9]+\.[0-9]+\.[0-9]+).*") + if(DPKG_VERSION MATCHES "${_dpkg_version_re}") + set(DPKG_VERSION "${CMAKE_MATCH_1}") + if(DPKG_VERSION VERSION_LESS 1.18.3) + set(_dpkg_is_old TRUE) + endif() + else() + set(_dpkg_is_old TRUE) + endif() + else() + set(_dpkg_is_old TRUE) + endif() + if(_dpkg_is_old) + set(CPACK_DEBIAN_GENERATE_LDCONFIG_TRIGGERS 0) + set(CPACK_DEBIAN_GENERATE_POSTINST 1) + set(CPACK_DEBIAN_GENERATE_POSTRM 1) + message(STATUS "CPackDeb: Providing legacy postinst/postrm.") + else() + set(CPACK_DEBIAN_GENERATE_LDCONFIG_TRIGGERS 1) + set(CPACK_DEBIAN_GENERATE_POSTINST 0) + set(CPACK_DEBIAN_GENERATE_POSTRM 0) + endif() + foreach(control_file IN LISTS CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA) + cmake_path(GET control_file FILENAME name) + if(name STREQUAL "postinst") set(CPACK_DEBIAN_GENERATE_POSTINST 0) endif() - if(n STREQUAL "postrm") + if(name STREQUAL "postrm") set(CPACK_DEBIAN_GENERATE_POSTRM 0) endif() + if(name STREQUAL "triggers") + set(CPACK_DEBIAN_GENERATE_LDCONFIG_TRIGGERS 0) + endif() endforeach() else() + set(CPACK_DEBIAN_GENERATE_LDCONFIG_TRIGGERS 0) set(CPACK_DEBIAN_GENERATE_POSTINST 0) set(CPACK_DEBIAN_GENERATE_POSTRM 0) endif() - cpack_deb_variable_fallback("CPACK_DEBIAN_FILE_NAME" - "CPACK_DEBIAN_${_local_component_name}_FILE_NAME" - "CPACK_DEBIAN_FILE_NAME") + cpack_deb_variable_fallback( + CPACK_DEBIAN_FILE_NAME + CPACK_DEBIAN_${_local_component_name}_FILE_NAME + CPACK_DEBIAN_FILE_NAME + ) if(CPACK_DEBIAN_FILE_NAME) if(CPACK_DEBIAN_FILE_NAME STREQUAL "DEB-DEFAULT") # Patch package file name to be in correct debian format: # <foo>_<VersionNumber>-<DebianRevisionNumber>_<DebianArchitecture>.deb - set(CPACK_OUTPUT_FILE_NAME - "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb") - set(CPACK_DBGSYM_OUTPUT_FILE_NAME - "${CPACK_DEBIAN_PACKAGE_NAME}-dbgsym_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.ddeb") + set( + CPACK_OUTPUT_FILE_NAME + "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb" + ) + set( + CPACK_DBGSYM_OUTPUT_FILE_NAME + "${CPACK_DEBIAN_PACKAGE_NAME}-dbgsym_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.ddeb" + ) else() if(NOT CPACK_DEBIAN_FILE_NAME MATCHES ".*\\.(deb|ipk)") set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb") @@ -800,9 +950,12 @@ string(REGEX REPLACE "\.deb$" "-dbgsym.ddeb" CPACK_DBGSYM_OUTPUT_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}") endif() - set(CPACK_TEMPORARY_PACKAGE_FILE_NAME "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_OUTPUT_FILE_NAME}") - get_filename_component(BINARY_DIR "${CPACK_OUTPUT_FILE_PATH}" DIRECTORY) - set(CPACK_OUTPUT_FILE_PATH "${BINARY_DIR}/${CPACK_OUTPUT_FILE_NAME}") + cmake_path( + APPEND CPACK_TOPLEVEL_DIRECTORY "${CPACK_OUTPUT_FILE_NAME}" + OUTPUT_VARIABLE CPACK_TEMPORARY_PACKAGE_FILE_NAME + ) + cmake_path(GET CPACK_OUTPUT_FILE_PATH PARENT_PATH BINARY_DIR) + cmake_path(APPEND BINARY_DIR "${CPACK_OUTPUT_FILE_NAME}" OUTPUT_VARIABLE CPACK_OUTPUT_FILE_PATH) else() # back compatibility - don't change the name string(REGEX REPLACE "\.deb$" "-dbgsym.ddeb" CPACK_DBGSYM_OUTPUT_FILE_NAME "${CPACK_OUTPUT_FILE_NAME}") @@ -876,6 +1029,8 @@ "${CPACK_DEBIAN_PACKAGE_SOURCE}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_GENERATE_POSTINST "${CPACK_DEBIAN_GENERATE_POSTINST}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_GENERATE_POSTRM "${CPACK_DEBIAN_GENERATE_POSTRM}" PARENT_SCOPE) + set(GEN_CPACK_DEBIAN_GENERATE_LDCONFIG_TRIGGERS + "${CPACK_DEBIAN_GENERATE_LDCONFIG_TRIGGERS}" PARENT_SCOPE) set(GEN_WDIR "${WDIR}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE "${CPACK_DEBIAN_DEBUGINFO_PACKAGE}" PARENT_SCOPE)
diff --git a/Modules/Internal/CPack/CPackRPM.cmake b/Modules/Internal/CPack/CPackRPM.cmake index d0454e1..8466550 100644 --- a/Modules/Internal/CPack/CPackRPM.cmake +++ b/Modules/Internal/CPack/CPackRPM.cmake
@@ -21,32 +21,32 @@ # on shell set_spec_script_if_enabled( - "post" + post "${PACKAGE_NAME}" "${RPM_SYMLINK_POSTINSTALL}\n${CPACK_RPM_SPEC_POSTINSTALL}") set_spec_script_if_enabled( - "posttrans" + posttrans "${PACKAGE_NAME}" "${CPACK_RPM_SPEC_POSTTRANS}") set_spec_script_if_enabled( - "postun" + postun "${PACKAGE_NAME}" "${CPACK_RPM_SPEC_POSTUNINSTALL}") set_spec_script_if_enabled( - "pre" + pre "${PACKAGE_NAME}" "${CPACK_RPM_SPEC_PREINSTALL}") set_spec_script_if_enabled( - "pretrans" + pretrans "${PACKAGE_NAME}" "${CPACK_RPM_SPEC_PRETRANS}") set_spec_script_if_enabled( - "preun" + preun "${PACKAGE_NAME}" "${CPACK_RPM_SPEC_PREUNINSTALL}") endmacro() @@ -54,38 +54,41 @@ function(make_rpm_spec_path var path) # RPM supports either whitespace with quoting or globbing without quoting. if(path MATCHES "[ \t]") - set("${var}" "\"${path}\"" PARENT_SCOPE) + set(${var} "\"${path}\"") else() - set("${var}" "${path}" PARENT_SCOPE) + set(${var} "${path}") endif() + return(PROPAGATE ${var}) endfunction() function(get_file_permissions FILE RETURN_VAR) - execute_process(COMMAND ls -l ${FILE} - OUTPUT_VARIABLE permissions_ - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process( + COMMAND stat --format=%A "${FILE}" + OUTPUT_VARIABLE permissions_str + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_stat_permissions + ) - string(REPLACE " " ";" permissions_ "${permissions_}") - list(GET permissions_ 0 permissions_) + string(REGEX REPLACE "(.)" "\\1;" permissions "${permissions_str}") + list(POP_FRONT permissions _) # drop the very first char (entry type) - unset(text_notation_) - set(any_chars_ ".") - foreach(PERMISSION_TYPE "OWNER" "GROUP" "WORLD") - if(permissions_ MATCHES "${any_chars_}r.*") - list(APPEND text_notation_ "${PERMISSION_TYPE}_READ") + unset(result) + foreach(PERMISSION_TYPE IN ITEMS OWNER GROUP WORLD) + list(POP_FRONT permissions r_ w_ x_) + if(r_ STREQUAL "r") + list(APPEND result ${PERMISSION_TYPE}_READ) endif() - string(APPEND any_chars_ ".") - if(permissions_ MATCHES "${any_chars_}w.*") - list(APPEND text_notation_ "${PERMISSION_TYPE}_WRITE") + if(w_ STREQUAL "w") + list(APPEND result ${PERMISSION_TYPE}_WRITE) endif() - string(APPEND any_chars_ ".") - if(permissions_ MATCHES "${any_chars_}x.*") - list(APPEND text_notation_ "${PERMISSION_TYPE}_EXECUTE") + if(x_ STREQUAL "x") + list(APPEND result ${PERMISSION_TYPE}_EXECUTE) endif() endforeach() - set(${RETURN_VAR} "${text_notation_}" PARENT_SCOPE) + set(${RETURN_VAR} "${result}") + return(PROPAGATE ${RETURN_VAR}) endfunction() function(get_unix_permissions_octal_notation PERMISSIONS_VAR RETURN_VAR) @@ -98,15 +101,15 @@ message(FATAL_ERROR "${PERMISSIONS_VAR} contains duplicate values.") endif() - foreach(PERMISSION_TYPE "OWNER" "GROUP" "WORLD") + foreach(PERMISSION_TYPE IN ITEMS OWNER GROUP WORLD) set(${PERMISSION_TYPE}_PERMISSIONS 0) - foreach(PERMISSION ${PERMISSIONS}) - if("${PERMISSION}" STREQUAL "${PERMISSION_TYPE}_READ") + foreach(PERMISSION IN LISTS PERMISSIONS) + if(PERMISSION STREQUAL "${PERMISSION_TYPE}_READ") math(EXPR ${PERMISSION_TYPE}_PERMISSIONS "${${PERMISSION_TYPE}_PERMISSIONS} + 4") - elseif("${PERMISSION}" STREQUAL "${PERMISSION_TYPE}_WRITE") + elseif(PERMISSION STREQUAL "${PERMISSION_TYPE}_WRITE") math(EXPR ${PERMISSION_TYPE}_PERMISSIONS "${${PERMISSION_TYPE}_PERMISSIONS} + 2") - elseif("${PERMISSION}" STREQUAL "${PERMISSION_TYPE}_EXECUTE") + elseif(PERMISSION STREQUAL "${PERMISSION_TYPE}_EXECUTE") math(EXPR ${PERMISSION_TYPE}_PERMISSIONS "${${PERMISSION_TYPE}_PERMISSIONS} + 1") elseif(PERMISSION MATCHES "${PERMISSION_TYPE}.*") message(FATAL_ERROR "${PERMISSIONS_VAR} contains invalid values.") @@ -114,22 +117,24 @@ endforeach() endforeach() - set(${RETURN_VAR} "${OWNER_PERMISSIONS}${GROUP_PERMISSIONS}${WORLD_PERMISSIONS}" PARENT_SCOPE) -endfunction() - -function(cpack_rpm_exact_regex regex_var string) - string(REGEX REPLACE "([][+.*()^])" "\\\\\\1" regex "${string}") - set("${regex_var}" "${regex}" PARENT_SCOPE) + set(${RETURN_VAR} "${OWNER_PERMISSIONS}${GROUP_PERMISSIONS}${WORLD_PERMISSIONS}") + return(PROPAGATE ${RETURN_VAR}) endfunction() function(cpack_rpm_prepare_relocation_paths) # set appropriate prefix, remove possible trailing slash and convert backslashes to slashes if(CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_PREFIX) - file(TO_CMAKE_PATH "${CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_PREFIX}" PATH_PREFIX) + cmake_path( + CONVERT "${CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_PREFIX}" + TO_CMAKE_PATH_LIST PATH_PREFIX NORMALIZE + ) elseif(CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_PREFIX) - file(TO_CMAKE_PATH "${CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_PREFIX}" PATH_PREFIX) + cmake_path( + CONVERT "${CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_PREFIX}" + TO_CMAKE_PATH_LIST PATH_PREFIX NORMALIZE + ) else() - file(TO_CMAKE_PATH "${CPACK_PACKAGING_INSTALL_PREFIX}" PATH_PREFIX) + cmake_path(CONVERT "${CPACK_PACKAGING_INSTALL_PREFIX}" TO_CMAKE_PATH_LIST PATH_PREFIX NORMALIZE ) endif() set(RPM_RELOCATION_PATHS "${CPACK_RPM_RELOCATION_PATHS}") @@ -150,24 +155,19 @@ endif() # set other path prefixes - foreach(RELOCATION_PATH ${RPM_RELOCATION_PATHS}) + foreach(RELOCATION_PATH IN LISTS RPM_RELOCATION_PATHS) if(IS_ABSOLUTE "${RELOCATION_PATH}") set(PREPARED_RELOCATION_PATH "${RELOCATION_PATH}") - elseif(PATH_PREFIX STREQUAL "/") - # don't prefix path with a second slash as "//" is treated as network path - # by get_filename_component() so it remains in path even inside rpm - # package where it may cause problems with relocation - set(PREPARED_RELOCATION_PATH "/${RELOCATION_PATH}") else() set(PREPARED_RELOCATION_PATH "${PATH_PREFIX}/${RELOCATION_PATH}") endif() # handle cases where path contains extra slashes (e.g. /a//b/ instead of # /a/b) - get_filename_component(PREPARED_RELOCATION_PATH - "${PREPARED_RELOCATION_PATH}" ABSOLUTE) + cmake_path(ABSOLUTE_PATH PREPARED_RELOCATION_PATH NORMALIZE) if(EXISTS "${WDIR}/${PREPARED_RELOCATION_PATH}") + string(REGEX REPLACE "/*$" "" PREPARED_RELOCATION_PATH "${PREPARED_RELOCATION_PATH}") string(APPEND TMP_RPM_PREFIXES "Prefix: ${PREPARED_RELOCATION_PATH}\n") list(APPEND RPM_USED_PACKAGE_PREFIXES "${PREPARED_RELOCATION_PATH}") endif() @@ -175,35 +175,36 @@ # warn about all the paths that are not relocatable file(GLOB_RECURSE FILE_PATHS_ "${WDIR}/*") - foreach(TMP_PATH ${FILE_PATHS_}) + foreach(TMP_PATH IN LISTS FILE_PATHS_) string(LENGTH "${WDIR}" WDIR_LEN) string(SUBSTRING "${TMP_PATH}" ${WDIR_LEN} -1 TMP_PATH) unset(TMP_PATH_FOUND_) - foreach(RELOCATION_PATH ${RPM_USED_PACKAGE_PREFIXES}) - file(RELATIVE_PATH REL_PATH_ "${RELOCATION_PATH}" "${TMP_PATH}") + foreach(RELOCATION_PATH IN LISTS RPM_USED_PACKAGE_PREFIXES) + cmake_path(RELATIVE_PATH TMP_PATH BASE_DIRECTORY "${RELOCATION_PATH}" OUTPUT_VARIABLE REL_PATH_) string(SUBSTRING "${REL_PATH_}" 0 2 PREFIX_) - if(NOT "${PREFIX_}" STREQUAL "..") + if(NOT PREFIX_ STREQUAL "..") set(TMP_PATH_FOUND_ TRUE) break() endif() endforeach() if(NOT TMP_PATH_FOUND_) - message(AUTHOR_WARNING "CPackRPM:Warning: Path ${TMP_PATH} is not on one of the relocatable paths! Package will be partially relocatable.") + message( + AUTHOR_WARNING + "CPackRPM:Warning: Path ${TMP_PATH} is not on one of the relocatable paths! " + "Package will be partially relocatable." + ) endif() endforeach() - - set(RPM_USED_PACKAGE_PREFIXES "${RPM_USED_PACKAGE_PREFIXES}" PARENT_SCOPE) - set(TMP_RPM_PREFIXES "${TMP_RPM_PREFIXES}" PARENT_SCOPE) + return(PROPAGATE RPM_USED_PACKAGE_PREFIXES TMP_RPM_PREFIXES) endfunction() function(cpack_rpm_prepare_content_list) # get files list file(GLOB_RECURSE CPACK_RPM_INSTALL_FILES LIST_DIRECTORIES true RELATIVE "${WDIR}" "${WDIR}/*") - set(CPACK_RPM_INSTALL_FILES "/${CPACK_RPM_INSTALL_FILES}") - string(REPLACE ";" ";/" CPACK_RPM_INSTALL_FILES "${CPACK_RPM_INSTALL_FILES}") + list(TRANSFORM CPACK_RPM_INSTALL_FILES PREPEND "/") # if we are creating a relocatable package, omit parent directories of # CPACK_RPM_PACKAGE_PREFIX. This is achieved by building a "filter list" @@ -214,8 +215,8 @@ # final element (so the install-prefix dir itself is not omitted # from the RPM's content-list) list(SORT RPM_USED_PACKAGE_PREFIXES) - set(_DISTINCT_PATH "NOT_SET") - foreach(_RPM_RELOCATION_PREFIX ${RPM_USED_PACKAGE_PREFIXES}) + set(_DISTINCT_PATH NOT_SET) + foreach(_RPM_RELOCATION_PREFIX IN LISTS RPM_USED_PACKAGE_PREFIXES) if(NOT "${_RPM_RELOCATION_PREFIX}" MATCHES "${_DISTINCT_PATH}/.*") set(_DISTINCT_PATH "${_RPM_RELOCATION_PREFIX}") @@ -223,9 +224,9 @@ list(REMOVE_AT _CPACK_RPM_PACKAGE_PREFIX_ELEMS -1) unset(_TMP_LIST) # Now generate all of the parent dirs of the relocation path - foreach(_PREFIX_PATH_ELEM ${_CPACK_RPM_PACKAGE_PREFIX_ELEMS}) + foreach(_PREFIX_PATH_ELEM IN LISTS _CPACK_RPM_PACKAGE_PREFIX_ELEMS) list(APPEND _TMP_LIST "${_PREFIX_PATH_ELEM}") - string(REPLACE ";" "/" _OMIT_DIR "${_TMP_LIST}") + list(JOIN _TMP_LIST "/" _OMIT_DIR) separate_arguments(_OMIT_DIR) list(APPEND _RPM_DIRS_TO_OMIT ${_OMIT_DIR}) endforeach() @@ -298,18 +299,18 @@ endforeach() endforeach() - set(CPACK_RPM_INSTALL_FILES "${CPACK_RPM_INSTALL_FILES}" PARENT_SCOPE) + return(PROPAGATE CPACK_RPM_INSTALL_FILES) endfunction() function(cpack_rpm_symlink_get_relocation_prefixes LOCATION PACKAGE_PREFIXES RETURN_VARIABLE) + unset(${RETURN_VARIABLE}) foreach(PKG_PREFIX IN LISTS PACKAGE_PREFIXES) - string(REGEX MATCH "^${PKG_PREFIX}/.*" FOUND_ "${LOCATION}") - if(FOUND_) - list(APPEND TMP_PREFIXES "${PKG_PREFIX}") + if(LOCATION MATCHES "^${PKG_PREFIX}/.*") + list(APPEND ${RETURN_VARIABLE} "${PKG_PREFIX}") endif() endforeach() - set(${RETURN_VARIABLE} "${TMP_PREFIXES}" PARENT_SCOPE) + return(PROPAGATE ${RETURN_VARIABLE}) endfunction() function(cpack_rpm_symlink_create_relocation_script PACKAGE_PREFIXES) @@ -334,7 +335,7 @@ string(LENGTH "${POINT_PATH}" POINT_PATH_LEN) if(_RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX}) - if("${SYMLINK_INDEX}" EQUAL "${POINT_INDEX}") + if(SYMLINK_INDEX EQUAL POINT_INDEX) set(INDENT "") else() string(APPEND SCRIPT_PART " if [ \"$RPM_INSTALL_PREFIX${POINT_INDEX}\" != \"${POINT_PATH}\" ]; then\n") @@ -354,13 +355,16 @@ math(EXPR POINT_PATH_START ${SPLIT_INDEX}+1+${POINT_PATH_LEN}) string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${POINT_PATH_START} -1 POINT_) - string(APPEND SCRIPT_PART " ${INDENT}if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n") - string(APPEND SCRIPT_PART " ${INDENT}ln -s \"$RPM_INSTALL_PREFIX${POINT_INDEX}${POINT_}\" \"$RPM_INSTALL_PREFIX${SYMLINK_INDEX}${SYMLINK_}\"\n") - string(APPEND SCRIPT_PART " ${INDENT}CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n") - string(APPEND SCRIPT_PART " ${INDENT}fi\n") + string( + APPEND SCRIPT_PART + " ${INDENT}if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n" + " ${INDENT}ln -s \"$RPM_INSTALL_PREFIX${POINT_INDEX}${POINT_}\" \"$RPM_INSTALL_PREFIX${SYMLINK_INDEX}${SYMLINK_}\"\n" + " ${INDENT}CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n" + " ${INDENT}fi\n" + ) endforeach() - if(NOT "${SYMLINK_INDEX}" EQUAL "${POINT_INDEX}") + if(NOT SYMLINK_INDEX EQUAL POINT_INDEX) string(APPEND SCRIPT_PART " fi\n") endif() endif() @@ -381,10 +385,13 @@ math(EXPR POINT_PATH_START ${SPLIT_INDEX}+1) string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${POINT_PATH_START} -1 POINT_) - string(APPEND SCRIPT_PART " if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n") - string(APPEND SCRIPT_PART " ln -s \"${POINT_}\" \"$RPM_INSTALL_PREFIX${SYMLINK_INDEX}${SYMLINK_}\"\n") - string(APPEND SCRIPT_PART " CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n") - string(APPEND SCRIPT_PART " fi\n") + string( + APPEND SCRIPT_PART + " if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n" + " ln -s \"${POINT_}\" \"$RPM_INSTALL_PREFIX${SYMLINK_INDEX}${SYMLINK_}\"\n" + " CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n" + " fi\n" + ) endforeach() endif() @@ -413,10 +420,13 @@ math(EXPR POINT_PATH_START ${SPLIT_INDEX}+1+${POINT_PATH_LEN}) string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${POINT_PATH_START} -1 POINT_) - string(APPEND SCRIPT " if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n") - string(APPEND SCRIPT " ln -s \"$RPM_INSTALL_PREFIX${POINT_INDEX}${POINT_}\" \"${SYMLINK_}\"\n") - string(APPEND SCRIPT " CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n") - string(APPEND SCRIPT " fi\n") + string( + APPEND SCRIPT + " if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n" + " ln -s \"$RPM_INSTALL_PREFIX${POINT_INDEX}${POINT_}\" \"${SYMLINK_}\"\n" + " CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n" + " fi\n" + ) endforeach() string(APPEND SCRIPT "fi\n") @@ -435,13 +445,17 @@ math(EXPR POINT_PATH_START ${SPLIT_INDEX}+1) string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${POINT_PATH_START} -1 POINT_) - string(APPEND SCRIPT "if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n") - string(APPEND SCRIPT " ln -s \"${POINT_}\" \"${SYMLINK_}\"\n") - string(APPEND SCRIPT "fi\n") + string( + APPEND SCRIPT + "if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n" + " ln -s \"${POINT_}\" \"${SYMLINK_}\"\n" + "fi\n" + ) endforeach() endif() - set(RPM_SYMLINK_POSTINSTALL "${SCRIPT}" PARENT_SCOPE) + set(RPM_SYMLINK_POSTINSTALL "${SCRIPT}") + return(PROPAGATE RPM_SYMLINK_POSTINSTALL) endfunction() function(cpack_rpm_symlink_add_for_relocation_script PACKAGE_PREFIXES SYMLINK SYMLINK_RELOCATION_PATHS POINT POINT_RELOCATION_PATHS) @@ -457,18 +471,18 @@ # source path relocated list(APPEND _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_X "${PAIR_NO}") - list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_X") + list(APPEND RELOCATION_VARS _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_X) foreach(POINT_RELOC_PATH IN LISTS POINT_RELOCATION_PATHS) list(FIND PACKAGE_PREFIXES "${POINT_RELOC_PATH}" POINT_INDEX) # both paths relocated - list(APPEND _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX} "${PAIR_NO}") - list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX}") + list(APPEND _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX} ${PAIR_NO}) + list(APPEND RELOCATION_VARS _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX}) # point path relocated - list(APPEND _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX} "${PAIR_NO}") - list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_X_${POINT_INDEX}") + list(APPEND _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX} ${PAIR_NO}) + list(APPEND RELOCATION_VARS _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX}) endforeach() endforeach() elseif(POINT_PATHS_COUNT) @@ -476,22 +490,18 @@ list(FIND PACKAGE_PREFIXES "${POINT_RELOC_PATH}" POINT_INDEX) # point path relocated - list(APPEND _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX} "${PAIR_NO}") - list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_X_${POINT_INDEX}") + list(APPEND _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX} ${PAIR_NO}) + list(APPEND RELOCATION_VARS _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX}) endforeach() endif() # no path relocated - list(APPEND _RPM_RELOCATION_SCRIPT_X_X "${PAIR_NO}") - list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_X_X") + list(APPEND _RPM_RELOCATION_SCRIPT_X_X ${PAIR_NO}) + list(APPEND RELOCATION_VARS _RPM_RELOCATION_SCRIPT_X_X) - # place variables into parent scope - foreach(VAR IN LISTS RELOCATION_VARS) - set(${VAR} "${${VAR}}" PARENT_SCOPE) - endforeach() - set(_RPM_RELOCATION_SCRIPT_PAIRS "${_RPM_RELOCATION_SCRIPT_PAIRS}" PARENT_SCOPE) - set(REQUIRES_SYMLINK_RELOCATION_SCRIPT "true" PARENT_SCOPE) - set(DIRECTIVE "%ghost " PARENT_SCOPE) + set(REQUIRES_SYMLINK_RELOCATION_SCRIPT "true") + set(DIRECTIVE "%ghost ") + return(PROPAGATE _RPM_RELOCATION_SCRIPT_PAIRS REQUIRES_SYMLINK_RELOCATION_SCRIPT DIRECTIVE ${RELOCATION_VARS}) endfunction() function(cpack_rpm_prepare_install_files INSTALL_FILES_LIST WDIR PACKAGE_PREFIXES IS_RELOCATABLE) @@ -514,40 +524,26 @@ if(IS_SYMLINK "${WDIR}/${F}") if(IS_RELOCATABLE) # check that symlink has relocatable format - get_filename_component(SYMLINK_LOCATION_ "${WDIR}/${F}" DIRECTORY) - execute_process(COMMAND ls -la "${WDIR}/${F}" - WORKING_DIRECTORY "${WDIR}" - OUTPUT_VARIABLE SYMLINK_POINT_ - OUTPUT_STRIP_TRAILING_WHITESPACE) - - string(FIND "${SYMLINK_POINT_}" "->" SYMLINK_POINT_INDEX_ REVERSE) - math(EXPR SYMLINK_POINT_INDEX_ ${SYMLINK_POINT_INDEX_}+3) - string(LENGTH "${SYMLINK_POINT_}" SYMLINK_POINT_LENGTH_) - - # get destination path - string(SUBSTRING "${SYMLINK_POINT_}" ${SYMLINK_POINT_INDEX_} ${SYMLINK_POINT_LENGTH_} SYMLINK_POINT_) + cmake_path(APPEND_STRING WDIR "${F}" OUTPUT_VARIABLE SYMLINK_LOCATION_) + cmake_path(GET SYMLINK_LOCATION_ PARENT_PATH SYMLINK_LOCATION_) + file(READ_SYMLINK "${WDIR}/${F}" SYMLINK_POINT_) # check if path is relative or absolute - string(SUBSTRING "${SYMLINK_POINT_}" 0 1 SYMLINK_IS_ABSOLUTE_) - - if(${SYMLINK_IS_ABSOLUTE_} STREQUAL "/") + if(IS_ABSOLUTE "${SYMLINK_POINT_}") # prevent absolute paths from having /../ or /./ section inside of them - get_filename_component(SYMLINK_POINT_ "${SYMLINK_POINT_}" ABSOLUTE) + cmake_path(NORMAL_PATH SYMLINK_POINT_) else() # handle relative path - get_filename_component(SYMLINK_POINT_ "${SYMLINK_LOCATION_}/${SYMLINK_POINT_}" ABSOLUTE) + cmake_path(ABSOLUTE_PATH SYMLINK_POINT_ BASE_DIRECTORY "${SYMLINK_LOCATION_}" NORMALIZE) endif() - # recalculate path length after conversion to canonical form - string(LENGTH "${SYMLINK_POINT_}" SYMLINK_POINT_LENGTH_) - - cpack_rpm_exact_regex(IN_SYMLINK_POINT_REGEX "${WDIR}") + string(REGEX QUOTE IN_SYMLINK_POINT_REGEX "${WDIR}") string(APPEND IN_SYMLINK_POINT_REGEX "/.*") if(SYMLINK_POINT_ MATCHES "${IN_SYMLINK_POINT_REGEX}") # only symlinks that are pointing inside the packaging structure should be checked for relocation string(SUBSTRING "${SYMLINK_POINT_}" ${WDR_LEN_} -1 SYMLINK_POINT_WD_) - cpack_rpm_symlink_get_relocation_prefixes("${F}" "${PACKAGE_PREFIXES}" "SYMLINK_RELOCATIONS") - cpack_rpm_symlink_get_relocation_prefixes("${SYMLINK_POINT_WD_}" "${PACKAGE_PREFIXES}" "POINT_RELOCATIONS") + cpack_rpm_symlink_get_relocation_prefixes("${F}" "${PACKAGE_PREFIXES}" SYMLINK_RELOCATIONS) + cpack_rpm_symlink_get_relocation_prefixes("${SYMLINK_POINT_WD_}" "${PACKAGE_PREFIXES}" POINT_RELOCATIONS) list(LENGTH SYMLINK_RELOCATIONS SYMLINK_RELOCATIONS_COUNT) list(LENGTH POINT_RELOCATIONS POINT_RELOCATIONS_COUNT) @@ -560,25 +556,35 @@ unset(SYMLINK_RELOCATIONS_COUNT) unset(POINT_RELOCATIONS_COUNT) - message(AUTHOR_WARNING "CPackRPM:Warning: Symbolic link '${F}' points to location that is outside packaging path! Link will possibly not be relocatable.") + message( + AUTHOR_WARNING + "CPackRPM:Warning: Symbolic link '${F}' points to location that is outside packaging path! " + "Link will possibly not be relocatable." + ) endif() if(SYMLINK_RELOCATIONS_COUNT AND POINT_RELOCATIONS_COUNT) # find matching foreach(SYMLINK_RELOCATION_PREFIX IN LISTS SYMLINK_RELOCATIONS) list(FIND POINT_RELOCATIONS "${SYMLINK_RELOCATION_PREFIX}" FOUND_INDEX) - if(NOT ${FOUND_INDEX} EQUAL -1) + if(NOT FOUND_INDEX EQUAL -1) break() endif() endforeach() - if(NOT ${FOUND_INDEX} EQUAL -1) + if(NOT FOUND_INDEX EQUAL -1) # symlinks have the same subpath - if(${SYMLINK_RELOCATIONS_COUNT} EQUAL 1 AND ${POINT_RELOCATIONS_COUNT} EQUAL 1) + if(SYMLINK_RELOCATIONS_COUNT EQUAL 1 AND POINT_RELOCATIONS_COUNT EQUAL 1) # permanent symlink - get_filename_component(SYMLINK_LOCATION_ "${F}" DIRECTORY) - file(RELATIVE_PATH FINAL_PATH_ ${SYMLINK_LOCATION_} ${SYMLINK_POINT_WD_}) - execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${FINAL_PATH_}" "${WDIR}/${F}") + cmake_path(GET F PARENT_PATH SYMLINK_LOCATION_) + cmake_path( + RELATIVE_PATH SYMLINK_POINT_WD_ + BASE_DIRECTORY "${SYMLINK_LOCATION_}" + OUTPUT_VARIABLE FINAL_PATH_ + ) + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink + "${FINAL_PATH_}" "${WDIR}/${F}" + RESULT_VARIABLE _result_symlink_creation) else() # relocation subpaths cpack_rpm_symlink_add_for_relocation_script("${PACKAGE_PREFIXES}" "${F}" "${SYMLINK_RELOCATIONS}" @@ -595,7 +601,9 @@ "${SYMLINK_POINT_WD_}" "${POINT_RELOCATIONS}") else() # is not relocatable or points to non relocatable path - permanent symlink - execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${SYMLINK_POINT_WD_}" "${WDIR}/${F}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink + "${SYMLINK_POINT_WD_}" "${WDIR}/${F}" + RESULT_VARIABLE _result_symlink_creation) endif() endif() elseif(IS_DIRECTORY "${WDIR}/${F}") @@ -610,8 +618,8 @@ cpack_rpm_symlink_create_relocation_script("${PACKAGE_PREFIXES}") endif() - set(RPM_SYMLINK_POSTINSTALL "${RPM_SYMLINK_POSTINSTALL}" PARENT_SCOPE) - set(CPACK_RPM_INSTALL_FILES "${INSTALL_FILES}" PARENT_SCOPE) + set(CPACK_RPM_INSTALL_FILES "${INSTALL_FILES}") + return(PROPAGATE RPM_SYMLINK_POSTINSTALL CPACK_RPM_INSTALL_FILES) endfunction() if(CMAKE_BINARY_DIR) @@ -656,15 +664,14 @@ continue() endif() - execute_process(COMMAND "${CPACK_OBJDUMP_EXECUTABLE}" -h ${WORKING_DIR}/${F} + execute_process(COMMAND "${CPACK_OBJDUMP_EXECUTABLE}" -h "${WORKING_DIR}/${F}" WORKING_DIRECTORY "${CPACK_TOPLEVEL_DIRECTORY}" RESULT_VARIABLE OBJDUMP_EXEC_RESULT OUTPUT_VARIABLE OBJDUMP_OUT ERROR_QUIET) # Check if the given file is an executable or not if(NOT OBJDUMP_EXEC_RESULT) - string(FIND "${OBJDUMP_OUT}" "debug" FIND_RESULT) - if(FIND_RESULT GREATER -1) + if(OBJDUMP_OUT MATCHES "debug") set(index_ 0) foreach(source_dir_ IN LISTS CPACK_BUILD_SOURCE_DIRS) string(LENGTH "${source_dir_}" source_dir_len_) @@ -679,23 +686,37 @@ " CPACK_RPM_DEBUGINFO_PACKAGE variable for details.") endif() - file(REMOVE "${CPACK_RPM_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}/debugsources_add.list") - execute_process(COMMAND "${DEBUGEDIT_EXECUTABLE}" -b "${source_dir_}" -d "${CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX}/src_${index_}" -i -l "${CPACK_RPM_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}/debugsources_add.list" "${WORKING_DIR}/${F}" + cmake_path( + APPEND CPACK_RPM_DIRECTORY "${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}/debugsources_add.list" + OUTPUT_VARIABLE debugsources_file_ + ) + file(REMOVE "${debugsources_file_}") + execute_process( + COMMAND "${DEBUGEDIT_EXECUTABLE}" + -b "${source_dir_}" + -d "${CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX}/src_${index_}" + -i + -l "${debugsources_file_}" + "${WORKING_DIR}/${F}" RESULT_VARIABLE res_ OUTPUT_VARIABLE opt_ ERROR_VARIABLE err_ ) - file(STRINGS - "${CPACK_RPM_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}/debugsources_add.list" - sources_) + file(STRINGS "${debugsources_file_}" sources_) list(REMOVE_DUPLICATES sources_) foreach(source_ IN LISTS sources_) if(EXISTS "${source_dir_}/${source_}" AND NOT IS_DIRECTORY "${source_dir_}/${source_}") - get_filename_component(path_part_ "${source_}" DIRECTORY) - list(APPEND mkdir_list_ "%{buildroot}${CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX}/src_${index_}/${path_part_}") - list(APPEND cp_list_ "cp \"${source_dir_}/${source_}\" \"%{buildroot}${CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX}/src_${index_}/${path_part_}\"") + cmake_path(GET source_ PARENT_PATH path_part_) + list( + APPEND mkdir_list_ + "%{buildroot}${CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX}/src_${index_}/${path_part_}" + ) + list( + APPEND cp_list_ + "cp \"${source_dir_}/${source_}\" \"%{buildroot}${CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX}/src_${index_}/${path_part_}\"" + ) list(APPEND additional_sources_ "${CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX}/src_${index_}/${source_}") endif() @@ -704,7 +725,11 @@ math(EXPR index_ "${index_} + 1") endforeach() else() - message(WARNING "CPackRPM: File: ${F} does not contain debug symbols. They will possibly be missing from debuginfo package!") + message( + WARNING + "CPackRPM: File: ${F} does not contain debug symbols. " + "They will possibly be missing from debuginfo package!" + ) endif() get_file_permissions("${WORKING_DIR}/${F}" permissions_) @@ -712,7 +737,7 @@ NOT "GROUP_EXECUTE" IN_LIST permissions_ AND NOT "WORLD_EXECUTE" IN_LIST permissions_) if(CPACK_RPM_INSTALL_WITH_EXEC) - execute_process(COMMAND chmod a+x ${WORKING_DIR}/${F} + execute_process(COMMAND chmod a+x "${WORKING_DIR}/${F}" RESULT_VARIABLE res_ ERROR_VARIABLE err_ OUTPUT_QUIET) @@ -788,9 +813,7 @@ endforeach() endif() - set(TMP_RPM_DEBUGINFO_INSTALL "${TMP_RPM_DEBUGINFO_INSTALL}" PARENT_SCOPE) - set(TMP_DEBUGINFO_ADDITIONAL_SOURCES "${TMP_DEBUGINFO_ADDITIONAL_SOURCES}" - PARENT_SCOPE) + return(PROPAGATE TMP_RPM_DEBUGINFO_INSTALL TMP_DEBUGINFO_ADDITIONAL_SOURCES) endfunction() function(cpack_rpm_variable_fallback OUTPUT_VAR_NAME) @@ -798,8 +821,8 @@ foreach(variable_name IN LISTS FALLBACK_VAR_NAMES) if(DEFINED ${variable_name}) - set(${OUTPUT_VAR_NAME} "${${variable_name}}" PARENT_SCOPE) - break() + set(${OUTPUT_VAR_NAME} "${${variable_name}}") + return(PROPAGATE ${OUTPUT_VAR_NAME}) endif() endforeach() endfunction() @@ -822,7 +845,8 @@ execute_process(COMMAND ${RPMBUILD_EXECUTABLE} --version OUTPUT_VARIABLE _TMP_VERSION ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _TMP_VERSION_RESULT) string(REGEX REPLACE "^.* " "" RPMBUILD_EXECUTABLE_VERSION ${_TMP_VERSION}) @@ -839,11 +863,12 @@ execute_process(COMMAND ${LSB_RELEASE_EXECUTABLE} -a OUTPUT_VARIABLE _TMP_LSB_RELEASE_OUTPUT ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _TMP_LSB_RELEASE_RESULT) string(REGEX REPLACE "\n" ", " LSB_RELEASE_OUTPUT ${_TMP_LSB_RELEASE_OUTPUT}) - else () + else() set(LSB_RELEASE_OUTPUT "lsb_release not installed/found!") endif() message("CPackRPM:Debug: LSB_RELEASE = ${LSB_RELEASE_OUTPUT}") @@ -853,16 +878,23 @@ # to shut down warning about space in buildtree # some recent RPM version should support space in different places. # not checked [yet]. - if(CPACK_TOPLEVEL_DIRECTORY MATCHES ".* .*") - message(FATAL_ERROR "${RPMBUILD_EXECUTABLE} can't handle paths with spaces, use a build directory without spaces for building RPMs.") + if(CPACK_TOPLEVEL_DIRECTORY MATCHES " ") + message( + FATAL_ERROR + "${RPMBUILD_EXECUTABLE} can't handle paths with spaces, use a build directory " + "without spaces for building RPMs." + ) endif() # Are we packaging components ? if(CPACK_RPM_PACKAGE_COMPONENT) - string(TOUPPER ${CPACK_RPM_PACKAGE_COMPONENT} CPACK_RPM_PACKAGE_COMPONENT_UPPER) + string(TOUPPER "${CPACK_RPM_PACKAGE_COMPONENT}" CPACK_RPM_PACKAGE_COMPONENT_UPPER) endif() - set(WDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}") + cmake_path( + APPEND CPACK_TOPLEVEL_DIRECTORY "${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}" + OUTPUT_VARIABLE WDIR + ) # # Use user-defined RPM specific variables value @@ -876,9 +908,9 @@ # CPACK_RPM_PACKAGE_SUMMARY (mandatory) if(CPACK_RPM_PACKAGE_COMPONENT) - cpack_rpm_variable_fallback("CPACK_RPM_PACKAGE_SUMMARY" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_SUMMARY" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_SUMMARY") + cpack_rpm_variable_fallback(CPACK_RPM_PACKAGE_SUMMARY + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_SUMMARY + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_SUMMARY) endif() if(NOT CPACK_RPM_PACKAGE_SUMMARY) @@ -900,15 +932,14 @@ endif() if(CPACK_RPM_PACKAGE_COMPONENT) - string(TOUPPER "${CPACK_RPM_MAIN_COMPONENT}" - CPACK_RPM_MAIN_COMPONENT_UPPER) + string(TOUPPER "${CPACK_RPM_MAIN_COMPONENT}" CPACK_RPM_MAIN_COMPONENT_UPPER) if(NOT CPACK_RPM_MAIN_COMPONENT_UPPER STREQUAL CPACK_RPM_PACKAGE_COMPONENT_UPPER) string(APPEND CPACK_RPM_PACKAGE_NAME "-${CPACK_RPM_PACKAGE_COMPONENT_PART_NAME}") - cpack_rpm_variable_fallback("CPACK_RPM_PACKAGE_NAME" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_NAME" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_NAME") + cpack_rpm_variable_fallback(CPACK_RPM_PACKAGE_NAME + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_NAME + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_NAME) endif() endif() @@ -929,7 +960,8 @@ if(NOT CPACK_RPM_PACKAGE_ARCHITECTURE) execute_process(COMMAND uname "-m" OUTPUT_VARIABLE CPACK_RPM_PACKAGE_ARCHITECTURE - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _TMP_UNAME_RESULT) else() if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: using user-specified build arch = ${CPACK_RPM_PACKAGE_ARCHITECTURE}") @@ -937,9 +969,9 @@ endif() if(CPACK_RPM_PACKAGE_COMPONENT) - cpack_rpm_variable_fallback("CPACK_RPM_PACKAGE_ARCHITECTURE" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_ARCHITECTURE" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_ARCHITECTURE") + cpack_rpm_variable_fallback(CPACK_RPM_PACKAGE_ARCHITECTURE + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_ARCHITECTURE + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_ARCHITECTURE) if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: using component build arch = ${CPACK_RPM_PACKAGE_ARCHITECTURE}") @@ -961,7 +993,7 @@ # This is the case when the packaging is buggy (not) the software :=) # If not set, 1 is a good candidate if(NOT CPACK_RPM_PACKAGE_RELEASE) - set(CPACK_RPM_PACKAGE_RELEASE "1") + set(CPACK_RPM_PACKAGE_RELEASE 1) endif() if(CPACK_RPM_PACKAGE_RELEASE_DIST) @@ -970,18 +1002,18 @@ # CPACK_RPM_PACKAGE_LICENSE if(NOT CPACK_RPM_PACKAGE_LICENSE) - set(CPACK_RPM_PACKAGE_LICENSE "unknown") + set(CPACK_RPM_PACKAGE_LICENSE unknown) endif() # CPACK_RPM_PACKAGE_GROUP if(CPACK_RPM_PACKAGE_COMPONENT) - cpack_rpm_variable_fallback("CPACK_RPM_PACKAGE_GROUP" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_GROUP" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_GROUP") + cpack_rpm_variable_fallback(CPACK_RPM_PACKAGE_GROUP + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_GROUP + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_GROUP) endif() if(NOT CPACK_RPM_PACKAGE_GROUP) - set(CPACK_RPM_PACKAGE_GROUP "unknown") + set(CPACK_RPM_PACKAGE_GROUP unknown) endif() # CPACK_RPM_PACKAGE_VENDOR @@ -989,7 +1021,7 @@ if(CPACK_PACKAGE_VENDOR) set(CPACK_RPM_PACKAGE_VENDOR "${CPACK_PACKAGE_VENDOR}") else() - set(CPACK_RPM_PACKAGE_VENDOR "unknown") + set(CPACK_RPM_PACKAGE_VENDOR unknown) endif() endif() @@ -1005,23 +1037,23 @@ # if(CPACK_RPM_PACKAGE_COMPONENT) - cpack_rpm_variable_fallback("CPACK_RPM_PACKAGE_DESCRIPTION" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_DESCRIPTION" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_DESCRIPTION" - "CPACK_COMPONENT_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DESCRIPTION") + cpack_rpm_variable_fallback(CPACK_RPM_PACKAGE_DESCRIPTION + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_DESCRIPTION + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_DESCRIPTION + CPACK_COMPONENT_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DESCRIPTION) endif() if(NOT CPACK_RPM_PACKAGE_DESCRIPTION) if(CPACK_PACKAGE_DESCRIPTION_FILE) - file(READ ${CPACK_PACKAGE_DESCRIPTION_FILE} CPACK_RPM_PACKAGE_DESCRIPTION) - else () + file(READ "${CPACK_PACKAGE_DESCRIPTION_FILE}" CPACK_RPM_PACKAGE_DESCRIPTION) + else() set(CPACK_RPM_PACKAGE_DESCRIPTION "no package description available") - endif () - endif () + endif() + endif() # CPACK_RPM_COMPRESSION_TYPE # - if (CPACK_RPM_COMPRESSION_TYPE) + if(CPACK_RPM_COMPRESSION_TYPE) if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: User Specified RPM compression type: ${CPACK_RPM_COMPRESSION_TYPE}") endif() @@ -1056,7 +1088,11 @@ message("CPackRPM:Debug: Trying to build a relocatable package") endif() if(CPACK_SET_DESTDIR AND (NOT CPACK_SET_DESTDIR STREQUAL "I_ON")) - message("CPackRPM:Warning: CPACK_SET_DESTDIR is set (=${CPACK_SET_DESTDIR}) while requesting a relocatable package (CPACK_RPM_PACKAGE_RELOCATABLE is set): this is not supported, the package won't be relocatable.") + message( + "CPackRPM:Warning: CPACK_SET_DESTDIR is set (=${CPACK_SET_DESTDIR}) while requesting a " + "relocatable package (CPACK_RPM_PACKAGE_RELOCATABLE is set): this is not supported, " + "the package won't be relocatable." + ) set(CPACK_RPM_PACKAGE_RELOCATABLE FALSE) else() set(CPACK_RPM_PACKAGE_PREFIX ${CPACK_PACKAGING_INSTALL_PREFIX}) # kept for back compatibility (provided external RPM spec files) @@ -1085,7 +1121,8 @@ execute_process( COMMAND "${RPM_EXECUTABLE}" --querytags OUTPUT_VARIABLE RPMBUILD_TAG_LIST - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_rpm) endif() string(REPLACE "\n" ";" RPMBUILD_TAG_LIST "${RPMBUILD_TAG_LIST}") @@ -1098,7 +1135,7 @@ ERROR_QUIET RESULT_VARIABLE RPM_SUGGESTS_RESULT) if(NOT RPM_SUGGESTS_RESULT EQUAL 0) - foreach(_WEAK_DEP SUGGESTS RECOMMENDS SUPPLEMENTS ENHANCES) + foreach(_WEAK_DEP IN ITEMS SUGGESTS RECOMMENDS SUPPLEMENTS ENHANCES) list(REMOVE_ITEM RPMBUILD_TAG_LIST ${_WEAK_DEP}) endforeach() endif() @@ -1112,16 +1149,22 @@ # There may be some COMPONENT specific variables as well # If component specific var is not provided we use the global one # for each component - foreach(_RPM_SPEC_HEADER URL REQUIRES SUGGESTS RECOMMENDS SUPPLEMENTS ENHANCES PROVIDES OBSOLETES PREFIX CONFLICTS AUTOPROV AUTOREQ AUTOREQPROV REQUIRES_PRE REQUIRES_POST REQUIRES_PREUN REQUIRES_POSTUN) + set( + tags_ + URL REQUIRES SUGGESTS RECOMMENDS SUPPLEMENTS ENHANCES PROVIDES + OBSOLETES PREFIX CONFLICTS AUTOPROV AUTOREQ AUTOREQPROV + REQUIRES_PRE REQUIRES_POST REQUIRES_PREUN REQUIRES_POSTUN + ) + foreach(_RPM_SPEC_HEADER IN LISTS tags_) if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: processing ${_RPM_SPEC_HEADER}") endif() if(CPACK_RPM_PACKAGE_COMPONENT) - cpack_rpm_variable_fallback("CPACK_RPM_PACKAGE_${_RPM_SPEC_HEADER}" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_${_RPM_SPEC_HEADER}" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_${_RPM_SPEC_HEADER}") + cpack_rpm_variable_fallback(CPACK_RPM_PACKAGE_${_RPM_SPEC_HEADER} + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_${_RPM_SPEC_HEADER} + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_${_RPM_SPEC_HEADER}) endif() if(DEFINED CPACK_RPM_PACKAGE_${_RPM_SPEC_HEADER}) @@ -1153,8 +1196,7 @@ string(APPEND _PACKAGE_HEADER_NAME "${_PACKAGE_HEADER_TAIL}") # The following keywords require parentheses around the "pre" or "post" suffix in the final RPM spec file. set(SCRIPTS_REQUIREMENTS_LIST REQUIRES_PRE REQUIRES_POST REQUIRES_PREUN REQUIRES_POSTUN) - list(FIND SCRIPTS_REQUIREMENTS_LIST ${_RPM_SPEC_HEADER} IS_SCRIPTS_REQUIREMENT_FOUND) - if(NOT ${IS_SCRIPTS_REQUIREMENT_FOUND} EQUAL -1) + if(_RPM_SPEC_HEADER IN_LIST SCRIPTS_REQUIREMENTS_LIST) string(REPLACE "_" "(" _PACKAGE_HEADER_NAME "${_PACKAGE_HEADER_NAME}") string(APPEND _PACKAGE_HEADER_NAME ")") endif() @@ -1200,29 +1242,32 @@ # May be used to embed a pre installation/uninstallation/transaction script in the spec file. # The referred script file(s) will be read and directly # put after the %pre or %preun or %pretrans section - foreach(RPM_SCRIPT_FILE_TYPE_ "INSTALL" "UNINSTALL" "TRANS") - foreach(RPM_SCRIPT_FILE_TIME_ "PRE" "POST") - set("CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE" + foreach(RPM_SCRIPT_FILE_TYPE_ IN ITEMS INSTALL UNINSTALL TRANS) + foreach(RPM_SCRIPT_FILE_TIME_ IN ITEMS PRE POST) + set(CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE "${CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_SCRIPT_FILE}") if(CPACK_RPM_PACKAGE_COMPONENT) - cpack_rpm_variable_fallback("CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_SCRIPT_FILE" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_SCRIPT_FILE") + cpack_rpm_variable_fallback(CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_SCRIPT_FILE + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_SCRIPT_FILE) endif() # Handle file if it has been specified if(CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE) if(EXISTS ${CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE}) - file(READ ${CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE} - "CPACK_RPM_SPEC_${RPM_SCRIPT_FILE_TIME_}${RPM_SCRIPT_FILE_TYPE_}") + file(READ "${CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE}" + CPACK_RPM_SPEC_${RPM_SCRIPT_FILE_TIME_}${RPM_SCRIPT_FILE_TYPE_}) else() - message("CPackRPM:Warning: CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_SCRIPT_FILE <${CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE}> does not exist - ignoring") + message( + "CPackRPM:Warning: CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_SCRIPT_FILE " + "<${CPACK_RPM_${RPM_SCRIPT_FILE_TIME_}_${RPM_SCRIPT_FILE_TYPE_}_READ_FILE}> does not exist - ignoring" + ) endif() else() # reset SPEC var value if no file has been specified # (either globally or component-wise) - set("CPACK_RPM_SPEC_${RPM_SCRIPT_FILE_TIME_}${RPM_SCRIPT_FILE_TYPE_}" "") + set(CPACK_RPM_SPEC_${RPM_SCRIPT_FILE_TIME_}${RPM_SCRIPT_FILE_TYPE_} "") endif() endforeach() endforeach() @@ -1232,17 +1277,26 @@ # The referred file will be read and directly put after the %changelog section if(CPACK_RPM_CHANGELOG_FILE) if(EXISTS ${CPACK_RPM_CHANGELOG_FILE}) - file(READ ${CPACK_RPM_CHANGELOG_FILE} CPACK_RPM_SPEC_CHANGELOG) + file(READ "${CPACK_RPM_CHANGELOG_FILE}" CPACK_RPM_SPEC_CHANGELOG) else() message(SEND_ERROR "CPackRPM:Warning: CPACK_RPM_CHANGELOG_FILE <${CPACK_RPM_CHANGELOG_FILE}> does not exist - ignoring") endif() else() - set(CPACK_RPM_SPEC_CHANGELOG "* Sun Jul 4 2010 Eric Noulard <eric.noulard@gmail.com> - ${CPACK_RPM_PACKAGE_VERSION}-${CPACK_RPM_PACKAGE_RELEASE}\n Generated by CPack RPM (no Changelog file were provided)") + set( + CPACK_RPM_SPEC_CHANGELOG + "* Sun Jul 4 2010 Eric Noulard <eric.noulard@gmail.com> - ${CPACK_RPM_PACKAGE_VERSION}-${CPACK_RPM_PACKAGE_RELEASE}" + " Generated by CPack RPM (no Changelog file were provided)" + ) + list(JOIN CPACK_RPM_SPEC_CHANGELOG "\n" CPACK_RPM_SPEC_CHANGELOG) endif() # CPACK_RPM_SPEC_MORE_DEFINE # This is a generated spec rpm file spaceholder if(CPACK_RPM_SPEC_MORE_DEFINE) + list(LENGTH CPACK_RPM_SPEC_MORE_DEFINE _defines_size) + if(_defines_size GREATER 1) + list(JOIN CPACK_RPM_SPEC_MORE_DEFINE "\n" CPACK_RPM_SPEC_MORE_DEFINE) + endif() if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: User defined more define spec line specified:\n ${CPACK_RPM_SPEC_MORE_DEFINE}") endif() @@ -1254,13 +1308,15 @@ message("CPackRPM:Debug: Using CPACK_RPM_ROOTDIR=${CPACK_RPM_ROOTDIR}") endif() # Prepare RPM build tree - file(MAKE_DIRECTORY ${CPACK_RPM_ROOTDIR}) - file(MAKE_DIRECTORY ${CPACK_RPM_ROOTDIR}/tmp) - file(MAKE_DIRECTORY ${CPACK_RPM_ROOTDIR}/BUILD) - file(MAKE_DIRECTORY ${CPACK_RPM_ROOTDIR}/RPMS) - file(MAKE_DIRECTORY ${CPACK_RPM_ROOTDIR}/SOURCES) - file(MAKE_DIRECTORY ${CPACK_RPM_ROOTDIR}/SPECS) - file(MAKE_DIRECTORY ${CPACK_RPM_ROOTDIR}/SRPMS) + file( + MAKE_DIRECTORY + ${CPACK_RPM_ROOTDIR}/tmp + ${CPACK_RPM_ROOTDIR}/BUILD + ${CPACK_RPM_ROOTDIR}/RPMS + ${CPACK_RPM_ROOTDIR}/SOURCES + ${CPACK_RPM_ROOTDIR}/SPECS + ${CPACK_RPM_ROOTDIR}/SRPMS + ) # it seems rpmbuild can't handle spaces in the path # neither escaping (as below) nor putting quotes around the path seem to help @@ -1275,9 +1331,9 @@ # This must be done BEFORE the CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL handling if(CPACK_RPM_PACKAGE_COMPONENT) if(CPACK_ABSOLUTE_DESTINATION_FILES) - cpack_rpm_variable_fallback("COMPONENT_FILES_TAG" - "CPACK_ABSOLUTE_DESTINATION_FILES_${CPACK_RPM_PACKAGE_COMPONENT}" - "CPACK_ABSOLUTE_DESTINATION_FILES_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}") + cpack_rpm_variable_fallback(COMPONENT_FILES_TAG + CPACK_ABSOLUTE_DESTINATION_FILES_${CPACK_RPM_PACKAGE_COMPONENT} + CPACK_ABSOLUTE_DESTINATION_FILES_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}) set(CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL "${${COMPONENT_FILES_TAG}}") if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: Handling Absolute Destination Files: <${CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL}>") @@ -1293,9 +1349,9 @@ # In component case, set CPACK_RPM_USER_FILELIST_INTERNAL with CPACK_RPM_<COMPONENT>_USER_FILELIST. set(CPACK_RPM_USER_FILELIST_INTERNAL "") if(CPACK_RPM_PACKAGE_COMPONENT) - cpack_rpm_variable_fallback("CPACK_RPM_USER_FILELIST_INTERNAL" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_USER_FILELIST" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_USER_FILELIST") + cpack_rpm_variable_fallback(CPACK_RPM_USER_FILELIST_INTERNAL + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_USER_FILELIST + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_USER_FILELIST) if(CPACK_RPM_PACKAGE_DEBUG AND CPACK_RPM_USER_FILELIST_INTERNAL) message("CPackRPM:Debug: Handling User Filelist: <${CPACK_RPM_USER_FILELIST_INTERNAL}>") @@ -1340,7 +1396,7 @@ # Remove from CPACK_RPM_INSTALL_FILES and CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL list(REMOVE_ITEM CPACK_RPM_INSTALL_FILES_LIST ${F_PATH}) # ABSOLUTE destination files list may not exists at all - if (CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL) + if(CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL) list(REMOVE_ITEM CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL ${F_PATH}) endif() endforeach() @@ -1355,7 +1411,7 @@ set(CPACK_RPM_USER_INSTALL_FILES "") endif() - if (CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL) + if(CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL) if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: Handling Absolute Destination Files: ${CPACK_ABSOLUTE_DESTINATION_FILES_INTERNAL}") endif() @@ -1388,24 +1444,22 @@ set(CPACK_RPM_ABSOLUTE_INSTALL_FILES "") endif() - cpack_rpm_variable_fallback("CPACK_RPM_DEBUGINFO_PACKAGE" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_DEBUGINFO_PACKAGE" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEBUGINFO_PACKAGE" - "CPACK_RPM_DEBUGINFO_PACKAGE") + cpack_rpm_variable_fallback(CPACK_RPM_DEBUGINFO_PACKAGE + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_DEBUGINFO_PACKAGE + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEBUGINFO_PACKAGE + CPACK_RPM_DEBUGINFO_PACKAGE) if(CPACK_RPM_DEBUGINFO_PACKAGE OR (CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE AND NOT GENERATE_SPEC_PARTS)) - cpack_rpm_variable_fallback("CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_BUILD_SOURCE_DIRS_PREFIX" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_BUILD_SOURCE_DIRS_PREFIX" - "CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX") + cpack_rpm_variable_fallback(CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_BUILD_SOURCE_DIRS_PREFIX + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_BUILD_SOURCE_DIRS_PREFIX + CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX) if(NOT CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX) set(CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX "/usr/src/debug/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}") endif() # handle cases where path contains extra slashes (e.g. /a//b/ instead of # /a/b) - get_filename_component(CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX - "${CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX}" ABSOLUTE) - + cmake_path(ABSOLUTE_PATH CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX NORMALIZE) if(CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE AND GENERATE_SPEC_PARTS) file(WRITE "${CPACK_RPM_ROOTDIR}/SPECS/${CPACK_RPM_PACKAGE_COMPONENT}.files" "${CPACK_RPM_INSTALL_FILES}") @@ -1456,7 +1510,7 @@ continue() endif() - get_filename_component(dir_path_ "${f_}" DIRECTORY) + cmake_path(GET f_ PARENT_PATH dir_path_) # check that we are not overriding an existing file that doesn't # match the file that we want to copy @@ -1473,8 +1527,7 @@ endif() file(MAKE_DIRECTORY "${WDIR}/${dir_path_}") - file(RENAME "${src_file_}" - "${WDIR}/${f_}") + file(RENAME "${src_file_}" "${WDIR}/${f_}") endforeach() endforeach() @@ -1527,31 +1580,40 @@ ) # set default user and group - foreach(_PERM_TYPE "USER" "GROUP") + foreach(_PERM_TYPE IN ITEMS USER GROUP) if(CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEFAULT_${_PERM_TYPE}) set(TMP_DEFAULT_${_PERM_TYPE} "${CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEFAULT_${_PERM_TYPE}}") elseif(CPACK_RPM_DEFAULT_${_PERM_TYPE}) set(TMP_DEFAULT_${_PERM_TYPE} "${CPACK_RPM_DEFAULT_${_PERM_TYPE}}") else() - set(TMP_DEFAULT_${_PERM_TYPE} "root") + set(TMP_DEFAULT_${_PERM_TYPE} root) endif() endforeach() # set default file and dir permissions - foreach(_PERM_TYPE "FILE" "DIR") + foreach(_PERM_TYPE IN ITEMS FILE DIR) if(CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEFAULT_${_PERM_TYPE}_PERMISSIONS) - get_unix_permissions_octal_notation("CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEFAULT_${_PERM_TYPE}_PERMISSIONS" "TMP_DEFAULT_${_PERM_TYPE}_PERMISSIONS") - set(_PERMISSIONS_VAR "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEFAULT_${_PERM_TYPE}_PERMISSIONS") + get_unix_permissions_octal_notation( + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEFAULT_${_PERM_TYPE}_PERMISSIONS + TMP_DEFAULT_${_PERM_TYPE}_PERMISSIONS + ) + set(_PERMISSIONS_VAR CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEFAULT_${_PERM_TYPE}_PERMISSIONS) elseif(CPACK_RPM_DEFAULT_${_PERM_TYPE}_PERMISSIONS) - get_unix_permissions_octal_notation("CPACK_RPM_DEFAULT_${_PERM_TYPE}_PERMISSIONS" "TMP_DEFAULT_${_PERM_TYPE}_PERMISSIONS") - set(_PERMISSIONS_VAR "CPACK_RPM_DEFAULT_${_PERM_TYPE}_PERMISSIONS") + get_unix_permissions_octal_notation( + CPACK_RPM_DEFAULT_${_PERM_TYPE}_PERMISSIONS + TMP_DEFAULT_${_PERM_TYPE}_PERMISSIONS + ) + set(_PERMISSIONS_VAR CPACK_RPM_DEFAULT_${_PERM_TYPE}_PERMISSIONS) else() - set(TMP_DEFAULT_${_PERM_TYPE}_PERMISSIONS "-") + set(TMP_DEFAULT_${_PERM_TYPE}_PERMISSIONS -) endif() endforeach() # The name of the final spec file to be used by rpmbuild - set(CPACK_RPM_BINARY_SPECFILE "${CPACK_RPM_ROOTDIR}/SPECS/${CPACK_RPM_PACKAGE_NAME}.spec") + cmake_path( + APPEND CPACK_RPM_ROOTDIR "SPECS/${CPACK_RPM_PACKAGE_NAME}.spec" + OUTPUT_VARIABLE CPACK_RPM_BINARY_SPECFILE + ) # Print out some debug information if we were asked for that if(CPACK_RPM_PACKAGE_DEBUG) @@ -1572,15 +1634,15 @@ # We can have a component specific spec file. if(CPACK_RPM_PACKAGE_COMPONENT) - cpack_rpm_variable_fallback("CPACK_RPM_USER_BINARY_SPECFILE" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_USER_BINARY_SPECFILE" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_USER_BINARY_SPECFILE") + cpack_rpm_variable_fallback(CPACK_RPM_USER_BINARY_SPECFILE + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_USER_BINARY_SPECFILE + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_USER_BINARY_SPECFILE) endif() - cpack_rpm_variable_fallback("CPACK_RPM_FILE_NAME" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_FILE_NAME" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_FILE_NAME" - "CPACK_RPM_FILE_NAME") + cpack_rpm_variable_fallback(CPACK_RPM_FILE_NAME + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_FILE_NAME + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_FILE_NAME + CPACK_RPM_FILE_NAME) if(NOT CPACK_RPM_FILE_NAME STREQUAL "RPM-DEFAULT") if(CPACK_RPM_FILE_NAME) if(NOT CPACK_RPM_FILE_NAME MATCHES ".*\\.rpm") @@ -1588,9 +1650,7 @@ endif() else() # old file name format for back compatibility - string(TOUPPER "${CPACK_RPM_MAIN_COMPONENT}" - CPACK_RPM_MAIN_COMPONENT_UPPER) - + string(TOUPPER "${CPACK_RPM_MAIN_COMPONENT}" CPACK_RPM_MAIN_COMPONENT_UPPER) if(CPACK_RPM_MAIN_COMPONENT_UPPER STREQUAL CPACK_RPM_PACKAGE_COMPONENT_UPPER) # this is the main component so ignore the component filename part set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}.rpm") @@ -1617,6 +1677,7 @@ execute_process( COMMAND ${CMAKE_COMMAND} -E tar "cfvz" "${CPACK_RPM_DIRECTORY}/SOURCES/${archive_name_}.tar.gz" "${CPACK_PACKAGE_FILE_NAME}" WORKING_DIRECTORY ${CPACK_RPM_DIRECTORY} + RESULT_VARIABLE _result_tar ) set(TMP_RPM_SOURCE "Source: ${archive_name_}.tar.gz") @@ -1650,7 +1711,7 @@ ) set(TMP_RPM_PREP "%setup -c") - set(RPMBUILD_FLAGS "-bs") + set(RPMBUILD_FLAGS -bs) file(WRITE ${CPACK_RPM_BINARY_SPECFILE}.in "# Restore old style debuginfo creation for rpm >= 4.14. @@ -1700,8 +1761,6 @@ %install \@TMP_RPM_INSTALL\@ -%clean - %changelog \@CPACK_RPM_SPEC_CHANGELOG\@ " @@ -1773,7 +1832,7 @@ # We should generate a USER spec file template: # - either because the user asked for it : CPACK_RPM_GENERATE_USER_BINARY_SPECFILE_TEMPLATE # - or the user did not provide one : NOT CPACK_RPM_USER_BINARY_SPECFILE - set(RPMBUILD_FLAGS "-bb") + set(RPMBUILD_FLAGS -bb) if(CPACK_RPM_GENERATE_USER_BINARY_SPECFILE_TEMPLATE OR NOT CPACK_RPM_USER_BINARY_SPECFILE) set_spec_scripts("") @@ -1843,8 +1902,6 @@ \@TMP_RPM_DEBUGINFO_INSTALL\@ -%clean - \@post_\@ \@posttrans_\@ \@postun_\@ @@ -1911,10 +1968,10 @@ endif() # find generated rpm files and take their names - file(GLOB_RECURSE GENERATED_FILES "${CPACK_RPM_DIRECTORY}/RPMS/*.rpm" + file(GLOB_RECURSE GEN_CPACK_OUTPUT_FILES "${CPACK_RPM_DIRECTORY}/RPMS/*.rpm" "${CPACK_RPM_DIRECTORY}/SRPMS/*.rpm") - if(NOT GENERATED_FILES) + if(NOT GEN_CPACK_OUTPUT_FILES) message(FATAL_ERROR "RPM package was not generated! ${CPACK_RPM_DIRECTORY}") endif() @@ -1927,10 +1984,10 @@ endif() if(CPACK_RPM_DEBUGINFO_PACKAGE) - cpack_rpm_variable_fallback("CPACK_RPM_DEBUGINFO_FILE_NAME" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_DEBUGINFO_FILE_NAME" - "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEBUGINFO_FILE_NAME" - "CPACK_RPM_DEBUGINFO_FILE_NAME") + cpack_rpm_variable_fallback(CPACK_RPM_DEBUGINFO_FILE_NAME + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_DEBUGINFO_FILE_NAME + CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEBUGINFO_FILE_NAME + CPACK_RPM_DEBUGINFO_FILE_NAME) if(CPACK_RPM_DEBUGINFO_FILE_NAME AND NOT CPACK_RPM_DEBUGINFO_FILE_NAME STREQUAL "RPM-DEFAULT") @@ -1955,16 +2012,16 @@ endif() if(expected_filenames_) - foreach(F IN LISTS GENERATED_FILES) - unset(matched_) + foreach(F IN LISTS GEN_CPACK_OUTPUT_FILES) + set(matched_ FALSE) foreach(expected_ IN LISTS expected_filenames_) if(F MATCHES ".*/${expected_}") list(FIND expected_filenames_ "${expected_}" idx_) list(GET filenames_ ${idx_} filename_) - get_filename_component(FILE_PATH "${F}" DIRECTORY) + cmake_path(GET F PARENT_PATH FILE_PATH) file(RENAME "${F}" "${FILE_PATH}/${filename_}") list(APPEND new_files_list_ "${FILE_PATH}/${filename_}") - set(matched_ "YES") + set(matched_ TRUE) break() endif() @@ -1975,15 +2032,15 @@ endif() endforeach() - set(GENERATED_FILES "${new_files_list_}") + set(GEN_CPACK_OUTPUT_FILES "${new_files_list_}") endif() endif() - set(GEN_CPACK_OUTPUT_FILES "${GENERATED_FILES}" PARENT_SCOPE) - if(CPACK_RPM_PACKAGE_DEBUG) - message("CPackRPM:Debug: GEN_CPACK_OUTPUT_FILES = ${GENERATED_FILES}") + message("CPackRPM:Debug: GEN_CPACK_OUTPUT_FILES = ${GEN_CPACK_OUTPUT_FILES}") endif() + + return(PROPAGATE GEN_CPACK_OUTPUT_FILES) endfunction() cpack_rpm_generate_package()
diff --git a/Modules/Internal/CheckCommon.cmake b/Modules/Internal/CheckCommon.cmake new file mode 100644 index 0000000..df9b3a1 --- /dev/null +++ b/Modules/Internal/CheckCommon.cmake
@@ -0,0 +1,123 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + + +# Do NOT include this module directly into any of your code. It is meant as +# a library for Check*CompilerFlag.cmake and Check*LinkerFlag.cma modules. +# It's content may change in any way between releases. + +include_guard(GLOBAL) + +macro(CMAKE_CHECK_COMMON_INIT_COMPILE_FLAGS _PREFIX) + set(${_PREFIX}_EXTRA_CMAKE_ARGUMENTS) + string(REPLACE "\\;" "\\\\;" CMAKE_REQUIRED_FLAGS + "${CMAKE_REQUIRED_FLAGS}") + #[[ + cmake_polify(GET CMP0999 ${_PREFIX}_CMP0999) + if(${_PREFIX}_CMP0999 STREQUAL "NEW") + # Join multiple list arguments into the space-separated string that we + # want. This ensures that the entirety of the value gets passed as + # compile arguments, which is what the user almost surely intended. + # + # FIXME(#27901): This needs a policy to be implemented. This will be + # available in a future version of CMake. + list(JOIN CMAKE_REQUIRED_FLAGS " " CMAKE_REQUIRED_FLAGS) + else() + #]] + # If CMAKE_REQUIRED_FLAGS contains an unescaped semicolon, anything after + # gets passed as flags to 'cmake' itself. This is probably not intended, + # but we preserve it for compatibility. + set(${_PREFIX}_EXTRA_CMAKE_ARGUMENTS "${CMAKE_REQUIRED_FLAGS}") + list(POP_FRONT ${_PREFIX}_EXTRA_CMAKE_ARGUMENTS CMAKE_REQUIRED_FLAGS) + if(NOT ${_PREFIX}_EXTRA_CMAKE_ARGUMENTS STREQUAL "") + # cmake_policy(ISSUE_WARNING CMP0999) TODO + # There are known instances of users accidentally passing '-W' arguments + # intended for the compiler as arguments to CMake. Since CMake complains + # about unknown '-W' arguments starting with CMake 4.4, we need to strip + # these for compatibility. + string(REPLACE "\\;" "\\\\;" ${_PREFIX}_EXTRA_CMAKE_ARGUMENTS + "${${_PREFIX}_EXTRA_CMAKE_ARGUMENTS}") + list(FILTER ${_PREFIX}_EXTRA_CMAKE_ARGUMENTS EXCLUDE REGEX "^-W") + endif() + #endif() +endmacro() + +macro(CMAKE_CHECK_COMMON_INIT_INCLUDE_DIRECTORIES _PREFIX) + if(CMAKE_REQUIRED_INCLUDES) + if(${ARGC} GREATER 1) + set(${_PREFIX}_INCLUDE_DIRECTORIES + "-DINCLUDE_DIRECTORIES:STRING=${ARGN};${CMAKE_REQUIRED_INCLUDES}") + else() + set(${_PREFIX}_INCLUDE_DIRECTORIES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + endif() + elseif(${ARGC} GREATER 1) + set(${_PREFIX}_INCLUDE_DIRECTORIES + "-DINCLUDE_DIRECTORIES:STRING=${ARGN}") + else() + set(${_PREFIX}_INCLUDE_DIRECTORIES) + endif() +endmacro() + +macro(CMAKE_CHECK_COMMON_INIT_LINK_OPTIONS _PREFIX) + if(CMAKE_REQUIRED_LINK_OPTIONS) + set(${_PREFIX}_ADD_LINK_OPTIONS + LINK_OPTIONS ${ARGN} ${CMAKE_REQUIRED_LINK_OPTIONS}) + elseif(${ARGC} GREATER 1) + set(${_PREFIX}_ADD_LINK_OPTIONS LINK_OPTIONS ${ARGN}) + else() + set(${_PREFIX}_ADD_LINK_OPTIONS) + endif() +endmacro() + +macro(CMAKE_CHECK_COMMON_INIT_LINK_LIBRARIES _PREFIX) + if(CMAKE_REQUIRED_LIBRARIES) + set(${_PREFIX}_ADD_LINK_LIBRARIES + LINK_LIBRARIES ${ARGN} ${CMAKE_REQUIRED_LIBRARIES}) + elseif(${ARGC} GREATER 1) + set(${_PREFIX}_ADD_LINK_LIBRARIES LINK_LIBRARIES ${ARGN}) + else() + set(${_PREFIX}_ADD_LINK_LIBRARIES) + endif() +endmacro() + +macro(CMAKE_CHECK_COMMON_INIT_LINK_DIRECTORIES _PREFIX) + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + if(${ARGC} GREATER 1) + set(${_PREFIX}_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${ARGN};${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(${_PREFIX}_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + endif() + elseif(${ARGC} GREATER 1) + set(${_PREFIX}_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${ARGN}") + else() + set(${_PREFIX}_LINK_DIRECTORIES) + endif() +endmacro() + +macro(CMAKE_CHECK_COMMON_INIT_COMPILE_ARGS _PREFIX) + cmake_check_common_init_compile_flags(${_PREFIX}) + cmake_check_common_init_include_directories(${_PREFIX}) +endmacro() + +macro(CMAKE_CHECK_COMMON_INIT_LINK_ARGS _PREFIX) + cmake_check_common_init_link_options(${_PREFIX}) + cmake_check_common_init_link_libraries(${_PREFIX}) + cmake_check_common_init_link_directories(${_PREFIX}) +endmacro() + +macro(CMAKE_CHECK_COMMON_INIT_ARGS _PREFIX) + cmake_check_common_init_compile_args(${_PREFIX}) + cmake_check_common_init_link_args(${_PREFIX}) +endmacro() + +macro(CMAKE_CHECK_COMMON_CLEANUP _PREFIX) + unset(${_PREFIX}_ADD_LINK_OPTIONS) + unset(${_PREFIX}_ADD_LINK_LIBRARIES) + unset(${_PREFIX}_LINK_DIRECTORIES) + unset(${_PREFIX}_INCLUDE_DIRECTORIES) + unset(${_PREFIX}_EXTRA_CMAKE_ARGUMENTS) +endmacro()
diff --git a/Modules/Internal/CheckSourceCompiles.cmake b/Modules/Internal/CheckSourceCompiles.cmake index e5c0ef1..733b62e 100644 --- a/Modules/Internal/CheckSourceCompiles.cmake +++ b/Modules/Internal/CheckSourceCompiles.cmake
@@ -2,6 +2,7 @@ # file LICENSE.rst or https://cmake.org/licensing for details. include_guard(GLOBAL) +include(Internal/CheckCommon) function(CMAKE_CHECK_SOURCE_COMPILES _lang _source _var) if(NOT DEFINED "${_var}") @@ -71,31 +72,7 @@ set(_SRC_EXT ${_lang_ext}) endif() - if(CMAKE_REQUIRED_LINK_OPTIONS) - set(CHECK_${LANG}_SOURCE_COMPILES_ADD_LINK_OPTIONS - LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - else() - set(CHECK_${LANG}_SOURCE_COMPILES_ADD_LINK_OPTIONS) - endif() - if(CMAKE_REQUIRED_LIBRARIES) - set(CHECK_${LANG}_SOURCE_COMPILES_ADD_LIBRARIES - LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - else() - set(CHECK_${LANG}_SOURCE_COMPILES_ADD_LIBRARIES) - endif() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CSC_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CSC_LINK_DIRECTORIES) - endif() - - if(CMAKE_REQUIRED_INCLUDES) - set(CHECK_${LANG}_SOURCE_COMPILES_ADD_INCLUDES - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - else() - set(CHECK_${LANG}_SOURCE_COMPILES_ADD_INCLUDES) - endif() + cmake_check_common_init_args(_CSC) if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Performing Test ${_var}") @@ -104,13 +81,14 @@ try_compile(${_var} SOURCE_FROM_VAR "${_lang_filename}.${_SRC_EXT}" _source COMPILE_DEFINITIONS -D${_var} ${CMAKE_REQUIRED_DEFINITIONS} - ${CHECK_${LANG}_SOURCE_COMPILES_ADD_LINK_OPTIONS} - ${CHECK_${LANG}_SOURCE_COMPILES_ADD_LIBRARIES} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} - "${CHECK_${LANG}_SOURCE_COMPILES_ADD_INCLUDES}" - "${_CSC_LINK_DIRECTORIES}" + ${_CSC_ADD_LINK_OPTIONS} + ${_CSC_ADD_LINK_LIBRARIES} + CMAKE_FLAGS + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + ${_CSC_EXTRA_CMAKE_ARGUMENTS} + "${_CSC_INCLUDE_DIRECTORIES}" + "${_CSC_LINK_DIRECTORIES}" OUTPUT_VARIABLE OUTPUT) - unset(_CSC_LINK_DIRECTORIES) foreach(_regex ${_FAIL_REGEX}) if("${OUTPUT}" MATCHES "${_regex}")
diff --git a/Modules/Internal/CheckSourceRuns.cmake b/Modules/Internal/CheckSourceRuns.cmake index 5df37e0..eaf9632 100644 --- a/Modules/Internal/CheckSourceRuns.cmake +++ b/Modules/Internal/CheckSourceRuns.cmake
@@ -2,6 +2,7 @@ # file LICENSE.rst or https://cmake.org/licensing for details. include_guard(GLOBAL) +include(Internal/CheckCommon) function(CMAKE_CHECK_SOURCE_RUNS _lang _source _var) if(NOT DEFINED "${_var}") @@ -63,30 +64,7 @@ set(_SRC_EXT ${_lang_ext}) endif() - if(CMAKE_REQUIRED_LINK_OPTIONS) - set(CHECK_${_lang}_SOURCE_COMPILES_ADD_LINK_OPTIONS - LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) - else() - set(CHECK_${_lang}_SOURCE_COMPILES_ADD_LINK_OPTIONS) - endif() - if(CMAKE_REQUIRED_LIBRARIES) - set(CHECK_${_lang}_SOURCE_COMPILES_ADD_LIBRARIES - LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - else() - set(CHECK_${_lang}_SOURCE_COMPILES_ADD_LIBRARIES) - endif() - if(CMAKE_REQUIRED_LINK_DIRECTORIES) - set(_CSR_LINK_DIRECTORIES - "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") - else() - set(_CSR_LINK_DIRECTORIES) - endif() - if(CMAKE_REQUIRED_INCLUDES) - set(CHECK_${_lang}_SOURCE_COMPILES_ADD_INCLUDES - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - else() - set(CHECK_${_lang}_SOURCE_COMPILES_ADD_INCLUDES) - endif() + cmake_check_common_init_args(_CSR) if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Performing Test ${_var}") @@ -95,14 +73,16 @@ try_run(${_var}_EXITCODE ${_var}_COMPILED SOURCE_FROM_VAR "src.${_SRC_EXT}" _source COMPILE_DEFINITIONS -D${_var} ${CMAKE_REQUIRED_DEFINITIONS} - ${CHECK_${_lang}_SOURCE_COMPILES_ADD_LINK_OPTIONS} - ${CHECK_${_lang}_SOURCE_COMPILES_ADD_LIBRARIES} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} - -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH} - "${CHECK_${_lang}_SOURCE_COMPILES_ADD_INCLUDES}" - "${_CSR_LINK_DIRECTORIES}" + ${_CSR_ADD_LINK_OPTIONS} + ${_CSR_ADD_LINK_LIBRARIES} + CMAKE_FLAGS + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + ${_CSR_EXTRA_CMAKE_ARGUMENTS} + -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH} + "${_CSR_INCLUDE_DIRECTORIES}" + "${_CSR_LINK_DIRECTORIES}" ) - unset(_CSR_LINK_DIRECTORIES) + # if it did not compile make the return value fail code of 1 if(NOT ${_var}_COMPILED) set(${_var}_EXITCODE 1)
diff --git a/Modules/Internal/FeatureTesting.cmake b/Modules/Internal/FeatureTesting.cmake index 64d477b..85f7217 100644 --- a/Modules/Internal/FeatureTesting.cmake +++ b/Modules/Internal/FeatureTesting.cmake
@@ -37,6 +37,7 @@ LINK_LIBRARIES "${compile_flags_for_link}" COPY_FILE "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.bin" COPY_FILE_ERROR _copy_error + NO_CACHE __CMAKE_INTERNAL FEATURE_TESTING ) if(NOT CMAKE_${lang}_FEATURE_TEST) @@ -47,7 +48,7 @@ else() set(_result 0) endif() - unset(CMAKE_${lang}_FEATURE_TEST CACHE) + unset(CMAKE_${lang}_FEATURE_TEST) unset(compile_flags_for_link) if (_result EQUAL 0)
diff --git a/Modules/Linker/GNU.cmake b/Modules/Linker/GNU.cmake index b56608e..de8f8e2 100644 --- a/Modules/Linker/GNU.cmake +++ b/Modules/Linker/GNU.cmake
@@ -24,11 +24,13 @@ # Ninja 1.10 or upper is required execute_process(COMMAND "${CMAKE_MAKE_PROGRAM}" --version OUTPUT_VARIABLE _ninja_version - ERROR_VARIABLE _ninja_version) + ERROR_VARIABLE _ninja_version + RESULT_VARIABLE _res + ) if (_ninja_version MATCHES "[0-9]+(\\.[0-9]+)*") set (_ninja_version "${CMAKE_MATCH_0}") endif() - if (_ninja_version VERSION_LESS "1.10") + if (NOT _res EQUAL 0 OR _ninja_version VERSION_LESS "1.10") set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) endif() endif() @@ -38,8 +40,9 @@ if (CMAKE_${lang}_COMPILER_LINKER AND CMAKE_${lang}_COMPILER_LINKER_ID MATCHES "GNU|LLD|MOLD") execute_process(COMMAND "${CMAKE_${lang}_COMPILER_LINKER}" --help OUTPUT_VARIABLE _linker_capabilities - ERROR_VARIABLE _linker_capabilities) - if(_linker_capabilities MATCHES "--dependency-file") + ERROR_VARIABLE _linker_capabilities + RESULT_VARIABLE _res) + if(_res EQUAL 0 AND _linker_capabilities MATCHES "--dependency-file") set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE) else() set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE)
diff --git a/Modules/Linker/WILD-ASM.cmake b/Modules/Linker/WILD-ASM.cmake new file mode 100644 index 0000000..8dec3f4 --- /dev/null +++ b/Modules/Linker/WILD-ASM.cmake
@@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include(Linker/WILD) + +__linker_wild(ASM)
diff --git a/Modules/Linker/WILD-C.cmake b/Modules/Linker/WILD-C.cmake new file mode 100644 index 0000000..f96cc6b --- /dev/null +++ b/Modules/Linker/WILD-C.cmake
@@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include(Linker/WILD) + +__linker_wild(C)
diff --git a/Modules/Linker/WILD-CXX.cmake b/Modules/Linker/WILD-CXX.cmake new file mode 100644 index 0000000..c77f96e --- /dev/null +++ b/Modules/Linker/WILD-CXX.cmake
@@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include(Linker/WILD) + +__linker_wild(CXX)
diff --git a/Modules/Linker/WILD-Fortran.cmake b/Modules/Linker/WILD-Fortran.cmake new file mode 100644 index 0000000..58042bf --- /dev/null +++ b/Modules/Linker/WILD-Fortran.cmake
@@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include(Linker/WILD) + +__linker_wild(Fortran)
diff --git a/Modules/Linker/WILD.cmake b/Modules/Linker/WILD.cmake new file mode 100644 index 0000000..e68911e --- /dev/null +++ b/Modules/Linker/WILD.cmake
@@ -0,0 +1,12 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + + +# This module is shared by multiple linkers; use include blocker. +include_guard() + +macro(__linker_wild lang) + include(Linker/GNU) + + __linker_gnu(${lang}) +endmacro()
diff --git a/Modules/Platform/AIX-IBMFlang-Fortran.cmake b/Modules/Platform/AIX-IBMFlang-Fortran.cmake new file mode 100644 index 0000000..07772a7 --- /dev/null +++ b/Modules/Platform/AIX-IBMFlang-Fortran.cmake
@@ -0,0 +1,2 @@ +include(Platform/AIX-GNU) +__aix_compiler_gnu(Fortran)
diff --git a/Modules/Platform/AIX/ExportImportList b/Modules/Platform/AIX/ExportImportList index 064024b..ef6eada 100755 --- a/Modules/Platform/AIX/ExportImportList +++ b/Modules/Platform/AIX/ExportImportList
@@ -43,7 +43,13 @@ # need to be handled differently. # # [0]: https://www.ibm.com/docs/en/openxl-c-and-cpp-aix/17.1.0?topic=compatibility-link-time-optimization-lto -NM="$(dirname "$compiler")/../libexec/ibm-llvm-nm" +if "$compiler" --version 2>/dev/null | grep -qi "IBM Open XL"; then + NM="$(dirname "$compiler")/../libexec/ibm-llvm-nm" +elif "$compiler" --version 2>/dev/null | grep -qi "[cf]lang version"; then + NM="$(dirname "$compiler")/../bin/llvm-nm" +else + NM="nm" +fi function IsBitcode { # N4 = first 4 bytes, -tx = output in hexadecimal, -An = don't display offset
diff --git a/Modules/Platform/Android-Determine.cmake b/Modules/Platform/Android-Determine.cmake index bd0b301..bc73ecb 100644 --- a/Modules/Platform/Android-Determine.cmake +++ b/Modules/Platform/Android-Determine.cmake
@@ -285,6 +285,8 @@ elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "linux-x86_64") + elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64") + set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "linux-arm64") else() set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "linux-x86") endif()
diff --git a/Modules/Platform/Android/Determine-Compiler-Standalone.cmake b/Modules/Platform/Android/Determine-Compiler-Standalone.cmake index 2da1ad3..30a0125 100644 --- a/Modules/Platform/Android/Determine-Compiler-Standalone.cmake +++ b/Modules/Platform/Android/Determine-Compiler-Standalone.cmake
@@ -30,8 +30,9 @@ OUTPUT_VARIABLE _gcc_version ERROR_VARIABLE _gcc_error OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _gcc_result ) -if(_gcc_version MATCHES "^([0-9]+\\.[0-9]+)") +if(_gcc_result EQUAL 0 AND _gcc_version MATCHES "^([0-9]+\\.[0-9]+)") set(_ANDROID_TOOL_C_TOOLCHAIN_VERSION "${CMAKE_MATCH_1}") else() message(FATAL_ERROR
diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake index 69ccb40..1852c6e 100644 --- a/Modules/Platform/Apple-GNU.cmake +++ b/Modules/Platform/Apple-GNU.cmake
@@ -31,8 +31,9 @@ COMMAND ${CMAKE_${lang}_COMPILER} "-v" "--help" OUTPUT_VARIABLE _gcc_help ERROR_VARIABLE _gcc_help + RESULT_VARIABLE _gcc_result ) - if("${_gcc_help}" MATCHES "isysroot") + if(_gcc_result EQUAL 0 AND "${_gcc_help}" MATCHES "isysroot") message(CHECK_PASS "yes") set(CMAKE_${lang}_SYSROOT_FLAG "-isysroot") else() @@ -51,8 +52,9 @@ COMMAND ${CMAKE_${lang}_COMPILER} "-v" "--help" OUTPUT_VARIABLE _gcc_help ERROR_VARIABLE _gcc_help + RESULT_VARIABLE _gcc_result ) - if("${_gcc_help}" MATCHES "macosx-version-min") + if(_gcc_result EQUAL 0 AND "${_gcc_help}" MATCHES "macosx-version-min") message(CHECK_PASS "yes") set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mmacosx-version-min=") else()
diff --git a/Modules/Platform/CrayLinuxEnvironment.cmake b/Modules/Platform/CrayLinuxEnvironment.cmake index daff647..56717e9 100644 --- a/Modules/Platform/CrayLinuxEnvironment.cmake +++ b/Modules/Platform/CrayLinuxEnvironment.cmake
@@ -40,9 +40,16 @@ # The rest of this file is based on UnixPaths.cmake, adjusted for Cray # add the install directory of the running cmake to the search directories -# CMAKE_ROOT is CMAKE_INSTALL_PREFIX/share/cmake, so we need to go two levels up +# CMAKE_ROOT is CMAKE_INSTALL_PREFIX/share/cmake, so we need to go two levels up. +# When CMake runs from its build tree, CMAKE_ROOT is the source tree instead. get_filename_component(__cmake_install_dir "${CMAKE_ROOT}" PATH) get_filename_component(__cmake_install_dir "${__cmake_install_dir}" PATH) +get_property(__cmake_running_in_build_tree GLOBAL PROPERTY + _CMAKE_RUNNING_IN_BUILD_TREE) +if(__cmake_running_in_build_tree) + set(__cmake_install_dir "") +endif() +unset(__cmake_running_in_build_tree) # Note: Some Cray's have the SYSROOT_DIR variable defined, pointing to a copy # of the NIDs userland. If so, then we'll use it. Otherwise, just assume @@ -53,10 +60,13 @@ list(APPEND CMAKE_SYSTEM_PREFIX_PATH # Standard $ENV{SYSROOT_DIR}/usr/local $ENV{SYSROOT_DIR}/usr $ENV{SYSROOT_DIR}/ - - # CMake install location - "${__cmake_install_dir}" ) +if(__cmake_install_dir) + list(APPEND CMAKE_SYSTEM_PREFIX_PATH + # CMake install location + "${__cmake_install_dir}" + ) +endif() if (NOT CMAKE_FIND_NO_INSTALL_PREFIX) list(APPEND CMAKE_SYSTEM_PREFIX_PATH # Project install destination.
diff --git a/Modules/Platform/Darwin-Initialize.cmake b/Modules/Platform/Darwin-Initialize.cmake index fe42de1..c9c88b3 100644 --- a/Modules/Platform/Darwin-Initialize.cmake +++ b/Modules/Platform/Darwin-Initialize.cmake
@@ -292,10 +292,11 @@ if(NOT CMAKE_CROSSCOMPILING) execute_process(COMMAND sw_vers -productVersion OUTPUT_VARIABLE _CMAKE_HOST_OSX_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result) endif() -if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) +if(_result EQUAL 0 AND CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) set(_CMAKE_OSX_DEPLOYMENT_TARGET_DEFAULT "$ENV{MACOSX_DEPLOYMENT_TARGET}") # Xcode chooses a default macOS deployment target based on the macOS SDK
diff --git a/Modules/Platform/Haiku.cmake b/Modules/Platform/Haiku.cmake index 3610389..bf0e0fc 100644 --- a/Modules/Platform/Haiku.cmake +++ b/Modules/Platform/Haiku.cmake
@@ -8,6 +8,12 @@ set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") set(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") +# Features for LINK_GROUP generator expression +## RESCAN: request the linker to rescan static libraries until there is +## no pending undefined symbols +set(CMAKE_LINK_GROUP_USING_RESCAN "LINKER:--start-group" "LINKER:--end-group") +set(CMAKE_LINK_GROUP_USING_RESCAN_SUPPORTED TRUE) + # Determine, if the C or C++ compiler is configured for a secondary # architecture. If so, that will change the search paths we set below. We check # whether the compiler's library search paths contain a
diff --git a/Modules/Platform/Linker/BSD-Linker-Initialize.cmake b/Modules/Platform/Linker/BSD-Linker-Initialize.cmake index b28dc06..2b5d598 100644 --- a/Modules/Platform/Linker/BSD-Linker-Initialize.cmake +++ b/Modules/Platform/Linker/BSD-Linker-Initialize.cmake
@@ -7,7 +7,7 @@ RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_VARIABLE output) - if(result OR NOT output MATCHES "LLD") + if(NOT result EQUAL 0 OR NOT output MATCHES "LLD") # assume GNU as default linker set(_CMAKE_SYSTEM_LINKER_TYPE GNU CACHE INTERNAL "System linker type") else()
diff --git a/Modules/Platform/Linker/CYGWIN-GNU.cmake b/Modules/Platform/Linker/CYGWIN-GNU.cmake index 7a7e6f8..4984af6 100644 --- a/Modules/Platform/Linker/CYGWIN-GNU.cmake +++ b/Modules/Platform/Linker/CYGWIN-GNU.cmake
@@ -10,6 +10,8 @@ macro(__cygwin_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/FreeBSD-GNU.cmake b/Modules/Platform/Linker/FreeBSD-GNU.cmake index 8912c36..e146b3e 100644 --- a/Modules/Platform/Linker/FreeBSD-GNU.cmake +++ b/Modules/Platform/Linker/FreeBSD-GNU.cmake
@@ -10,6 +10,8 @@ macro(__freebsd_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/GNU-GNU.cmake b/Modules/Platform/Linker/GNU-GNU.cmake index 9038f03..b5b9274 100644 --- a/Modules/Platform/Linker/GNU-GNU.cmake +++ b/Modules/Platform/Linker/GNU-GNU.cmake
@@ -10,6 +10,8 @@ macro(__gnu_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/GNU.cmake b/Modules/Platform/Linker/GNU.cmake index 3e665fa..5ab2983 100644 --- a/Modules/Platform/Linker/GNU.cmake +++ b/Modules/Platform/Linker/GNU.cmake
@@ -7,10 +7,10 @@ # WHOLE_ARCHIVE Feature for LINK_LIBRARY generator expression ## check linker capabilities -function(__cmake_set_whole_archive_feature __linker) +function(__cmake_set_whole_archive_feature __linker __linker_options) unset(__lang) - if(ARGC EQUAL "2") - set(__lang "${ARGV1}_") + if(ARGC EQUAL "3") + set(__lang "${ARGV2}_") endif() if(NOT __linker) @@ -19,11 +19,10 @@ if(NOT DEFINED CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED) # launch linker to check if push_state/pop_state options are supported - execute_process(COMMAND "${__linker}" --push-state --pop-state + execute_process(COMMAND "${__linker}" ${__linker_options} --push-state --pop-state OUTPUT_VARIABLE __linker_log ERROR_VARIABLE __linker_log - COMMAND_ERROR_IS_FATAL NONE - ) + COMMAND_ERROR_IS_FATAL NONE) if(__linker_log MATCHES "--push-state" OR __linker_log MATCHES "--pop-state") set(CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED FALSE) else() @@ -47,4 +46,4 @@ ## Configure system linker -__cmake_set_whole_archive_feature("${CMAKE_LINKER}") +__cmake_set_whole_archive_feature("${CMAKE_LINKER}" "")
diff --git a/Modules/Platform/Linker/Linux-GNU.cmake b/Modules/Platform/Linker/Linux-GNU.cmake index 67099b9..ed953bd 100644 --- a/Modules/Platform/Linker/Linux-GNU.cmake +++ b/Modules/Platform/Linker/Linux-GNU.cmake
@@ -10,6 +10,8 @@ macro(__linux_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/Linux-WILD-ASM.cmake b/Modules/Platform/Linker/Linux-WILD-ASM.cmake new file mode 100644 index 0000000..b9115cb --- /dev/null +++ b/Modules/Platform/Linker/Linux-WILD-ASM.cmake
@@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-WILD) + +__linux_linker_wild(ASM)
diff --git a/Modules/Platform/Linker/Linux-WILD-C.cmake b/Modules/Platform/Linker/Linux-WILD-C.cmake new file mode 100644 index 0000000..7b87b36 --- /dev/null +++ b/Modules/Platform/Linker/Linux-WILD-C.cmake
@@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-WILD) + +__linux_linker_wild(C)
diff --git a/Modules/Platform/Linker/Linux-WILD-CXX.cmake b/Modules/Platform/Linker/Linux-WILD-CXX.cmake new file mode 100644 index 0000000..d66ca0c --- /dev/null +++ b/Modules/Platform/Linker/Linux-WILD-CXX.cmake
@@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-WILD) + +__linux_linker_wild(CXX)
diff --git a/Modules/Platform/Linker/Linux-WILD-Fortran.cmake b/Modules/Platform/Linker/Linux-WILD-Fortran.cmake new file mode 100644 index 0000000..671d23d --- /dev/null +++ b/Modules/Platform/Linker/Linux-WILD-Fortran.cmake
@@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-WILD) + +__linux_linker_wild(Fortran)
diff --git a/Modules/Platform/Linker/Linux-WILD.cmake b/Modules/Platform/Linker/Linux-WILD.cmake new file mode 100644 index 0000000..230df4e --- /dev/null +++ b/Modules/Platform/Linker/Linux-WILD.cmake
@@ -0,0 +1,18 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + + +include(Platform/Linker/Linux-GNU) + + +macro(__linux_linker_wild lang) + __linux_linker_gnu(${lang}) + + + set(CMAKE_C_PLATFORM_LINKER_ID WILD) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) +endmacro()
diff --git a/Modules/Platform/Linker/NetBSD-GNU.cmake b/Modules/Platform/Linker/NetBSD-GNU.cmake index 9c26654..3e4c85e 100644 --- a/Modules/Platform/Linker/NetBSD-GNU.cmake +++ b/Modules/Platform/Linker/NetBSD-GNU.cmake
@@ -10,6 +10,8 @@ macro(__netbsd_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/OpenBSD-GNU.cmake b/Modules/Platform/Linker/OpenBSD-GNU.cmake index 64086e7..b104fe9 100644 --- a/Modules/Platform/Linker/OpenBSD-GNU.cmake +++ b/Modules/Platform/Linker/OpenBSD-GNU.cmake
@@ -10,6 +10,8 @@ macro(__openbsd_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/SerenityOS-GNU.cmake b/Modules/Platform/Linker/SerenityOS-GNU.cmake index 0e7013c..3f6667d 100644 --- a/Modules/Platform/Linker/SerenityOS-GNU.cmake +++ b/Modules/Platform/Linker/SerenityOS-GNU.cmake
@@ -10,6 +10,8 @@ macro(__serenityos_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/SunOS-GNU.cmake b/Modules/Platform/Linker/SunOS-GNU.cmake index 0c3b230..b4c2e2d 100644 --- a/Modules/Platform/Linker/SunOS-GNU.cmake +++ b/Modules/Platform/Linker/SunOS-GNU.cmake
@@ -10,6 +10,8 @@ macro(__sunos_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/Windows-GNU.cmake b/Modules/Platform/Linker/Windows-GNU.cmake index c5a968f..20387c9 100644 --- a/Modules/Platform/Linker/Windows-GNU.cmake +++ b/Modules/Platform/Linker/Windows-GNU.cmake
@@ -10,6 +10,8 @@ macro(__windows_linker_gnu lang) set(CMAKE_${lang}_PLATFORM_LINKER_ID GNU) if(CMAKE_${lang}_COMPILER_LINKER) - __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro()
diff --git a/Modules/Platform/Linker/Windows-MSVC.cmake b/Modules/Platform/Linker/Windows-MSVC.cmake index d4e819c..1facec5 100644 --- a/Modules/Platform/Linker/Windows-MSVC.cmake +++ b/Modules/Platform/Linker/Windows-MSVC.cmake
@@ -18,7 +18,7 @@ set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD DEDUPLICATION=ALL) # Features for LINK_LIBRARY generator expression - if(DEFINED CMAKE_${lang}_COMPILER_LINKER_VERSION) + if(NOT CMAKE_${lang}_COMPILER_LINKER_VERSION STREQUAL "") if (CMAKE_${lang}_COMPILER_LINKER_VERSION GREATER_EQUAL "14.10") ## WHOLE_ARCHIVE: Force loading all members of an archive set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:<LIBRARY>")
diff --git a/Modules/Platform/Linux-GNU.cmake b/Modules/Platform/Linux-GNU.cmake index 66e3a7e..8ebefd5 100644 --- a/Modules/Platform/Linux-GNU.cmake +++ b/Modules/Platform/Linux-GNU.cmake
@@ -24,4 +24,10 @@ OR CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1") set(CMAKE_${lang}_USING_LINKER_MOLD "-fuse-ld=mold") endif() + if(CMAKE_${lang}_COMPILER_ID STREQUAL "Clang") + set(CMAKE_${lang}_USING_LINKER_WILD "--ld-path=wild") + elseif(CMAKE_${lang}_COMPILER_ID STREQUAL "GNU" + AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "16.0") + set(CMAKE_${lang}_USING_LINKER_WILD "-fuse-ld=wild") + endif() endmacro()
diff --git a/Modules/Platform/Linux-NAG-Fortran.cmake b/Modules/Platform/Linux-NAG-Fortran.cmake index 353bae6..3bcd9b7 100644 --- a/Modules/Platform/Linux-NAG-Fortran.cmake +++ b/Modules/Platform/Linux-NAG-Fortran.cmake
@@ -8,3 +8,16 @@ set(CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG "-Wl,-Xlinker,-rpath-link,-Xlinker,") set(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG "-Wl,-Xlinker,-soname,-Xlinker,") set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-Wl,-rdynamic") + +set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,-Xlinker,") +set(CMAKE_Fortran_LINKER_WRAPPER_FLAG_SEP ",") + +set(CMAKE_Fortran_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_Fortran_LINK_LIBRARY_USING_WHOLE_ARCHIVE + "LINKER:--whole-archive" + "<LINK_ITEM>" + "LINKER:--no-whole-archive") +set(CMAKE_Fortran_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES + LIBRARY_TYPE=STATIC + OVERRIDE=DEFAULT + DEDUPLICATION=YES)
diff --git a/Modules/Platform/Linux-SunPro-CXX.cmake b/Modules/Platform/Linux-SunPro-CXX.cmake index a07f1ec..fe25deb 100644 --- a/Modules/Platform/Linux-SunPro-CXX.cmake +++ b/Modules/Platform/Linux-SunPro-CXX.cmake
@@ -1,7 +1,10 @@ # Sun C++ 5.9 does not support -Wl, but Sun C++ 5.11 does not work without it. # Query the compiler flags to detect whether to use -Wl. -execute_process(COMMAND ${CMAKE_CXX_COMPILER} -flags OUTPUT_VARIABLE _cxx_flags ERROR_VARIABLE _cxx_error) -if("${_cxx_flags}" MATCHES "\n-W[^\n]*component") +execute_process(COMMAND ${CMAKE_CXX_COMPILER} -flags +OUTPUT_VARIABLE _cxx_flags +ERROR_VARIABLE _cxx_error +RESULT_VARIABLE _cxx_result) +if(_cxx_result EQUAL 0 AND "${_cxx_flags}" MATCHES "\n-W[^\n]*component") set(CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG "-Wl,-rpath-link,") else() set(CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG "-rpath-link ")
diff --git a/Modules/Platform/OpenBSD.cmake b/Modules/Platform/OpenBSD.cmake index 97e2a6a..71bb99e 100644 --- a/Modules/Platform/OpenBSD.cmake +++ b/Modules/Platform/OpenBSD.cmake
@@ -7,12 +7,15 @@ if(NOT CMAKE_PLATFORM_RUNTIME_PATH) execute_process(COMMAND /sbin/ldconfig -r OUTPUT_VARIABLE LDCONFIG_HINTS - ERROR_QUIET) - string(REGEX REPLACE ".*search\\ directories:\\ ([^\n]*).*" "\\1" - LDCONFIG_HINTS "${LDCONFIG_HINTS}") - string(REPLACE ":" ";" - CMAKE_PLATFORM_RUNTIME_PATH - "${LDCONFIG_HINTS}") + ERROR_QUIET + RESULT_VARIABLE _res) + if(_res EQUAL 0) + string(REGEX REPLACE ".*search\\ directories:\\ ([^\n]*).*" "\\1" + LDCONFIG_HINTS "${LDCONFIG_HINTS}") + string(REPLACE ":" ";" + CMAKE_PLATFORM_RUNTIME_PATH + "${LDCONFIG_HINTS}") + endif() endif() # OpenBSD requires -z origin to enable $ORIGIN expansion in RPATH.
diff --git a/Modules/Platform/UnixPaths.cmake b/Modules/Platform/UnixPaths.cmake index e95da44..9aa659e 100644 --- a/Modules/Platform/UnixPaths.cmake +++ b/Modules/Platform/UnixPaths.cmake
@@ -18,9 +18,16 @@ set(UNIX 1) # also add the install directory of the running cmake to the search directories -# CMAKE_ROOT is CMAKE_INSTALL_PREFIX/share/cmake, so we need to go two levels up +# CMAKE_ROOT is CMAKE_INSTALL_PREFIX/share/cmake, so we need to go two levels up. +# When CMake runs from its build tree, CMAKE_ROOT is the source tree instead. get_filename_component(_CMAKE_INSTALL_DIR "${CMAKE_ROOT}" PATH) get_filename_component(_CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" PATH) +get_property(_cmake_running_in_build_tree GLOBAL PROPERTY + _CMAKE_RUNNING_IN_BUILD_TREE) +if(_cmake_running_in_build_tree) + set(_CMAKE_INSTALL_DIR "") +endif() +unset(_cmake_running_in_build_tree) # List common installation prefixes. These will be used for all # search types. @@ -31,10 +38,13 @@ list(APPEND CMAKE_SYSTEM_PREFIX_PATH # Standard /usr/local /usr / - - # CMake install location - "${_CMAKE_INSTALL_DIR}" ) +if(_CMAKE_INSTALL_DIR) + list(APPEND CMAKE_SYSTEM_PREFIX_PATH + # CMake install location + "${_CMAKE_INSTALL_DIR}" + ) +endif() if (NOT CMAKE_FIND_NO_INSTALL_PREFIX) list(APPEND CMAKE_SYSTEM_PREFIX_PATH # Project install destination.
diff --git a/Modules/Platform/Windows-Apple-Swift.cmake b/Modules/Platform/Windows-Apple-Swift.cmake index 3e34985..56082b5 100644 --- a/Modules/Platform/Windows-Apple-Swift.cmake +++ b/Modules/Platform/Windows-Apple-Swift.cmake
@@ -1,4 +1,6 @@ set(CMAKE_Swift_IMPLIB_LINKER_FLAGS "-Xlinker -implib:<TARGET_IMPLIB>") +set(CMAKE_Swift_PDB_LINKER_FLAGS "-Xlinker -pdb:<TARGET_PDB>") +set(CMAKE_Swift_LINKER_SUPPORTS_PDB ON) set(CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS "-Xlinker -debug") set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_LINKER_FLAGS "-Xlinker -debug")
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake index 0c002dd..e33d795 100644 --- a/Modules/Platform/Windows-Clang.cmake +++ b/Modules/Platform/Windows-Clang.cmake
@@ -62,9 +62,9 @@ set(CMAKE_${lang}_SHARED_LIBRARY_COMPILE_DEFINITIONS "_WINDLL") # linker selection - set(CMAKE_${lang}_USING_LINKER_DEFAULT "-fuse-ld=lld-link") + set(CMAKE_${lang}_USING_LINKER_DEFAULT "-fuse-ld=lld") set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=link") - set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld-link") + set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld") set(CMAKE_${lang}_USING_LINKER_MSVC "-fuse-ld=link") set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 1) @@ -207,19 +207,19 @@ if(NOT CMAKE_RC_COMPILER_INIT) # Check if rc is already in the path # This may happen in cases where the user is already in a visual studio environment when CMake is invoked - find_program(__RC_COMPILER_PATH NAMES rc) + find_program(__RC_COMPILER_PATH NO_CACHE NAMES rc) # Default to rc if it's available, otherwise fall back to llvm-rc if(__RC_COMPILER_PATH) set(CMAKE_RC_COMPILER_INIT rc) else() - find_program(__RC_COMPILER_PATH NAMES llvm-rc) + find_program(__RC_COMPILER_PATH NO_CACHE NAMES llvm-rc) if(__RC_COMPILER_PATH) set(CMAKE_RC_COMPILER_INIT llvm-rc) endif() endif() - unset(__RC_COMPILER_PATH CACHE) + unset(__RC_COMPILER_PATH) endif() if ( "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC"
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake index 962b57a..c50b13c 100644 --- a/Modules/Platform/Windows-GNU.cmake +++ b/Modules/Platform/Windows-GNU.cmake
@@ -58,8 +58,8 @@ # Check if GNU ld is too old to support @FILE syntax. set(__WINDOWS_GNU_LD_RESPONSE 1) -execute_process(COMMAND ld -v OUTPUT_VARIABLE _help ERROR_VARIABLE _help) -if("${_help}" MATCHES "GNU ld .* 2\\.1[1-6]") +execute_process(COMMAND ld -v OUTPUT_VARIABLE _help ERROR_VARIABLE _help RESULT_VARIABLE _res) +if(_res EQUAL 0 AND "${_help}" MATCHES "GNU ld .* 2\\.1[1-6]") set(__WINDOWS_GNU_LD_RESPONSE 0) endif() @@ -107,8 +107,8 @@ set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 1) # We prefer "@" for response files but it is not supported by gcc 3. - execute_process(COMMAND ${CMAKE_${lang}_COMPILER} --version OUTPUT_VARIABLE _ver ERROR_VARIABLE _ver) - if("${_ver}" MATCHES "\\(GCC\\) 3\\.") + execute_process(COMMAND ${CMAKE_${lang}_COMPILER} --version OUTPUT_VARIABLE _ver ERROR_VARIABLE _ver RESULT_VARIABLE _res) + if(_res EQUAL 0 AND "${_ver}" MATCHES "\\(GCC\\) 3\\.") if("${lang}" STREQUAL "Fortran") # The GNU Fortran compiler reports an error: # no input files; unwilling to write output files
diff --git a/Modules/Platform/Windows-Intel.cmake b/Modules/Platform/Windows-Intel.cmake index 12598a0..46a934d 100644 --- a/Modules/Platform/Windows-Intel.cmake +++ b/Modules/Platform/Windows-Intel.cmake
@@ -16,7 +16,7 @@ RESULT_VARIABLE _CMAKE_NINJA_RESULT OUTPUT_VARIABLE _CMAKE_NINJA_VERSION ERROR_VARIABLE _CMAKE_NINJA_VERSION) - if (NOT _CMAKE_NINJA_RESULT AND _CMAKE_NINJA_VERSION MATCHES "[0-9]+(\\.[0-9]+)*") + if (_CMAKE_NINJA_RESULT EQUAL 0 AND _CMAKE_NINJA_VERSION MATCHES "[0-9]+(\\.[0-9]+)*") set (_CMAKE_NINJA_VERSION "${CMAKE_MATCH_0}") endif() unset(_CMAKE_NINJA_RESULT)
diff --git a/Modules/Platform/Windows-LLVMFlang-Fortran.cmake b/Modules/Platform/Windows-LLVMFlang-Fortran.cmake index c2ccec6..0f753df 100644 --- a/Modules/Platform/Windows-LLVMFlang-Fortran.cmake +++ b/Modules/Platform/Windows-LLVMFlang-Fortran.cmake
@@ -5,6 +5,11 @@ include(Platform/Windows-MSVC) __windows_compiler_msvc(Fortran) + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "") + if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 18.0) set(_LLVMFlang_LINK_RUNTIME "") set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "-fms-runtime-lib=static")
diff --git a/Modules/Platform/Windows-OrangeC.cmake b/Modules/Platform/Windows-OrangeC.cmake index 4f66e0e..d9498b3 100644 --- a/Modules/Platform/Windows-OrangeC.cmake +++ b/Modules/Platform/Windows-OrangeC.cmake
@@ -7,4 +7,11 @@ macro(__windows_compiler_orangec lang) set(CMAKE_${lang}_CREATE_WIN32_EXE "-Wg") set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-Wc") + + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0") + if(NOT CMAKE_RC_COMPILER_INIT) + set(CMAKE_RC_COMPILER_INIT orc) + endif() + enable_language(RC) + endif() endmacro()
diff --git a/Modules/Platform/Windows-PellesC-ASM_POASM.cmake b/Modules/Platform/Windows-PellesC-ASM_POASM.cmake new file mode 100644 index 0000000..2466246 --- /dev/null +++ b/Modules/Platform/Windows-PellesC-ASM_POASM.cmake
@@ -0,0 +1,23 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include (Platform/Windows-PellesC) +__windows_compiler_pellesc(ASM_POASM) + +if(_PellesC_ARCH STREQUAL "x64") + set(_PellesC_ASM_POASM_FLAGS " -Aamd64") +elseif(_PellesC_ARCH STREQUAL "x86") + set(_PellesC_ASM_POASM_FLAGS " -Aia32") +else() + set(_PellesC_ASM_POASM_FLAGS "") +endif() + +set(CMAKE_ASM_POASM_COMPILE_OBJECT + "<CMAKE_ASM_POASM_COMPILER> <DEFINES> <INCLUDES>${_PellesC_ASM_POASM_FLAGS} <FLAGS> -Fo<OBJECT> <SOURCE>") + +string(APPEND CMAKE_ASM_POASM_FLAGS_DEBUG_INIT " -Zi") +string(APPEND CMAKE_ASM_POASM_FLAGS_RELEASE_INIT "") +string(APPEND CMAKE_ASM_POASM_FLAGS_RELWITHDEBINFO_INIT " -Zi") +string(APPEND CMAKE_ASM_POASM_FLAGS_MINSIZEREL_INIT "") + +unset(_PellesC_ASM_POASM_FLAGS)
diff --git a/Modules/Platform/Windows-PellesC-C.cmake b/Modules/Platform/Windows-PellesC-C.cmake new file mode 100644 index 0000000..c8cb4af --- /dev/null +++ b/Modules/Platform/Windows-PellesC-C.cmake
@@ -0,0 +1,41 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + +include (Platform/Windows-PellesC) +__windows_compiler_pellesc(C) + +if(_PellesC_ARCH) + set(_PellesC_C_OBJ_FLAGS " -T${_PellesC_ARCH}-coff") +else() + set(_PellesC_C_OBJ_FLAGS "") +endif() +if(_PellesC_ARCH STREQUAL "x64") + set(_PellesC_C_ASM_FLAGS " -Tx64-asm") +elseif(_PellesC_ARCH STREQUAL "x86") + set(_PellesC_C_ASM_FLAGS " -Tx86-asm") +else() + set(_PellesC_C_ASM_FLAGS "") +endif() + +set(CMAKE_C_COMPILE_OBJECT + "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES>${_PellesC_C_OBJ_FLAGS} <FLAGS> -Fo<OBJECT> -c <SOURCE>") + +set(CMAKE_C_CREATE_PREPROCESSED_SOURCE + "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES>${_PellesC_C_OBJ_FLAGS} <FLAGS> -E <SOURCE> ><PREPROCESSED_SOURCE>") + +set(CMAKE_C_CREATE_ASSEMBLY_SOURCE + "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES>${_PellesC_C_ASM_FLAGS} <FLAGS> -Fo<ASSEMBLY_SOURCE> <SOURCE>") + +# Enable Pelles C's Microsoft extensions to use Windows APIs. +# The flag also disables standard definitions, so add them. +string(APPEND CMAKE_C_FLAGS_INIT " -Ze -D__STDC__=1 -D__STDC_VERSION__=__POCC_STDC_VERSION__") + +string(APPEND CMAKE_C_FLAGS_DEBUG_INIT " -Zi -Ob0") +string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " -Ot -Ob2 -DNDEBUG=1") +string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO_INIT " -Zi -Ot -Ob1 -DNDEBUG=1") +string(APPEND CMAKE_C_FLAGS_MINSIZEREL_INIT " -Os -Ob1 -DNDEBUG=1") + +set(CMAKE_C_STANDARD_LIBRARIES_INIT "kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib") + +unset(_PellesC_C_OBJ_FLAGS) +unset(_PellesC_C_ASM_FLAGS)
diff --git a/Modules/Platform/Windows-PellesC.cmake b/Modules/Platform/Windows-PellesC.cmake new file mode 100644 index 0000000..513cce4 --- /dev/null +++ b/Modules/Platform/Windows-PellesC.cmake
@@ -0,0 +1,55 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. +include_guard() + +set(CMAKE_LIBRARY_PATH_FLAG "-libpath:") +set(CMAKE_LINK_DEF_FILE_FLAG "-def:") +set(CMAKE_LINK_LIBRARY_FLAG "") + +set(CMAKE_BUILD_TYPE_INIT Debug) + +# Select a default architecture based on the LIB search path. +string(TOLOWER "$ENV{LIB}" _PellesC_LIB) +if(_PellesC_LIB MATCHES [[[\/]+lib[\/]+win64[\/]*(;|$)]]) + set(_PellesC_ARCH "x64") +elseif(_PellesC_LIB MATCHES [[[\/]+lib[\/]+win[\/]*(;|$)]]) + set(_PellesC_ARCH "x86") +else() + set(_PellesC_ARCH "") +endif() + +macro(__windows_compiler_pellesc lang) + if(_PellesC_ARCH) + set(_PellesC_LINK_FLAGS " -machine:${_PellesC_ARCH}") + else() + set(_PellesC_LINK_FLAGS "") + endif() + + set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> <LINK_FLAGS> -out:<TARGET> <OBJECTS>") + + set(CMAKE_${lang}_CREATE_SHARED_LIBRARY + "<CMAKE_LINKER> <OBJECTS> ${CMAKE_START_TEMP_FILE} -out:<TARGET> -implib:<TARGET_IMPLIB> -dll -version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PellesC_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}") + set(CMAKE_${lang}_CREATE_SHARED_MODULE "${CMAKE_${lang}_CREATE_SHARED_LIBRARY}") + set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "") + + set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 1) + set(CMAKE_${lang}_LINK_EXECUTABLE + "<CMAKE_LINKER> <OBJECTS> ${CMAKE_START_TEMP_FILE} -out:<TARGET> -implib:<TARGET_IMPLIB> -version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PellesC_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}") + + set(CMAKE_${lang}_LINK_DEF_FILE_FLAG "${CMAKE_LINK_DEF_FILE_FLAG}") + + set(CMAKE_${lang}_CREATE_WIN32_EXE "-subsystem:windows") + set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-subsystem:console") + + if(NOT CMAKE_RC_COMPILER_INIT) + set(CMAKE_RC_COMPILER_INIT porc) + endif() + enable_language(RC) + + unset(_PellesC_LINK_FLAGS) +endmacro() + +foreach (t IN ITEMS EXE SHARED MODULE) + string(APPEND CMAKE_${t}_LINKER_FLAGS_DEBUG_INIT " -debug -debugtype:both -dbg") + string(APPEND CMAKE_${t}_LINKER_FLAGS_RELWITHDEBINFO_INIT " -debug -debugtype:both -dbg") +endforeach ()
diff --git a/Modules/Platform/Windows-orc.cmake b/Modules/Platform/Windows-orc.cmake new file mode 100644 index 0000000..1f8b08e --- /dev/null +++ b/Modules/Platform/Windows-orc.cmake
@@ -0,0 +1,2 @@ +set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -! <DEFINES> <INCLUDES> <FLAGS> -o<OBJECT> <SOURCE>") +set(CMAKE_INCLUDE_FLAG_RC "-i ")
diff --git a/Modules/Platform/Windows-porc.cmake b/Modules/Platform/Windows-porc.cmake new file mode 100644 index 0000000..a251a13 --- /dev/null +++ b/Modules/Platform/Windows-porc.cmake
@@ -0,0 +1 @@ +set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -Fo<OBJECT> <SOURCE>")
diff --git a/Modules/Platform/WindowsPaths.cmake b/Modules/Platform/WindowsPaths.cmake index c56dfaf..0f19b95 100644 --- a/Modules/Platform/WindowsPaths.cmake +++ b/Modules/Platform/WindowsPaths.cmake
@@ -50,9 +50,19 @@ unset(_programfiles) # Add the CMake install location. +# CMAKE_ROOT is CMAKE_INSTALL_PREFIX/share/cmake, so we need to go two levels up. +# When CMake runs from its build tree, CMAKE_ROOT is the source tree instead. get_filename_component(_CMAKE_INSTALL_DIR "${CMAKE_ROOT}" PATH) get_filename_component(_CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" PATH) -list(APPEND CMAKE_SYSTEM_PREFIX_PATH "${_CMAKE_INSTALL_DIR}") +get_property(_cmake_running_in_build_tree GLOBAL PROPERTY + _CMAKE_RUNNING_IN_BUILD_TREE) +if(_cmake_running_in_build_tree) + set(_CMAKE_INSTALL_DIR "") +endif() +unset(_cmake_running_in_build_tree) +if(_CMAKE_INSTALL_DIR) + list(APPEND CMAKE_SYSTEM_PREFIX_PATH "${_CMAKE_INSTALL_DIR}") +endif() if (NOT CMAKE_FIND_NO_INSTALL_PREFIX) # Add other locations. @@ -88,8 +98,12 @@ ) endif() endif() +if(_CMAKE_INSTALL_DIR) + list(APPEND CMAKE_SYSTEM_LIBRARY_PATH + "${_CMAKE_INSTALL_DIR}/bin" + ) +endif() list(APPEND CMAKE_SYSTEM_LIBRARY_PATH - "${_CMAKE_INSTALL_DIR}/bin" /bin )
diff --git a/Modules/ProcessorCount.cmake b/Modules/ProcessorCount.cmake index 260b663..b8fa1fa 100644 --- a/Modules/ProcessorCount.cmake +++ b/Modules/ProcessorCount.cmake
@@ -94,12 +94,13 @@ execute_process(COMMAND ${ProcessorCount_cmd_sysctl} -n hw.ncpu ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE count) + OUTPUT_VARIABLE count + RESULT_VARIABLE _count_ok) #message("ProcessorCount: trying sysctl '${ProcessorCount_cmd_sysctl}'") endif() endif() - if(NOT count) + if(NOT (_count_ok EQUAL 0 AND count GREATER 0)) # Linux (systems with nproc): # Prefer nproc to getconf if available as getconf may return the host CPU count in Linux containers find_program(ProcessorCount_cmd_nproc nproc) @@ -108,12 +109,13 @@ execute_process(COMMAND ${ProcessorCount_cmd_nproc} ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE count) + OUTPUT_VARIABLE count + RESULT_VARIABLE _count_ok) #message("ProcessorCount: trying nproc '${ProcessorCount_cmd_nproc}'") endif() endif() - if(NOT count) + if(NOT (_count_ok EQUAL 0 AND count GREATER 0)) # Linux (systems with getconf): find_program(ProcessorCount_cmd_getconf getconf) mark_as_advanced(ProcessorCount_cmd_getconf) @@ -121,12 +123,13 @@ execute_process(COMMAND ${ProcessorCount_cmd_getconf} _NPROCESSORS_ONLN ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE count) + OUTPUT_VARIABLE count + RESULT_VARIABLE _count_ok) #message("ProcessorCount: trying getconf '${ProcessorCount_cmd_getconf}'") endif() endif() - if(NOT count) + if(NOT (_count_ok EQUAL 0 AND count GREATER 0)) # HPUX (systems with machinfo): find_program(ProcessorCount_cmd_machinfo machinfo PATHS /usr/contrib/bin) @@ -150,15 +153,18 @@ execute_process(COMMAND ${ProcessorCount_cmd_mpsched} -s OUTPUT_QUIET ERROR_STRIP_TRAILING_WHITESPACE - ERROR_VARIABLE mpsched_output) - string(REGEX MATCHALL "Processor Count *: *([0-9]+)" procs "${mpsched_output}") - set(count "${CMAKE_MATCH_1}") + ERROR_VARIABLE mpsched_output + RESULT_VARIABLE _mpsched_ok) + if(_mpsched_ok EQUAL 0) + string(REGEX MATCHALL "Processor Count *: *([0-9]+)" procs "${mpsched_output}") + set(count "${CMAKE_MATCH_1}") + endif() #message("ProcessorCount: trying mpsched -s '${ProcessorCount_cmd_mpsched}'") endif() endif() endif() - if(NOT count) + if(NOT count GREATER 0) # AIX (systems with lsconf): find_program(ProcessorCount_cmd_lsconf lsconf PATHS /usr/sbin) @@ -167,14 +173,17 @@ execute_process(COMMAND ${ProcessorCount_cmd_lsconf} ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE lsconf_output) - string(REGEX MATCHALL "Number Of Processors: ([0-9]+)" procs "${lsconf_output}") - set(count "${CMAKE_MATCH_1}") + OUTPUT_VARIABLE lsconf_output + RESULT_VARIABLE _lsconf_ok) + if(_lsconf_ok EQUAL 0) + string(REGEX MATCHALL "Number Of Processors: ([0-9]+)" procs "${lsconf_output}") + set(count "${CMAKE_MATCH_1}") + endif() #message("ProcessorCount: trying lsconf '${ProcessorCount_cmd_lsconf}'") endif() endif() - if(NOT count) + if(NOT count GREATER 0) # QNX (systems with pidin): find_program(ProcessorCount_cmd_pidin pidin) mark_as_advanced(ProcessorCount_cmd_pidin) @@ -182,14 +191,17 @@ execute_process(COMMAND ${ProcessorCount_cmd_pidin} info ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE pidin_output) - string(REGEX MATCHALL "Processor[0-9]+: " procs "${pidin_output}") - list(LENGTH procs count) + OUTPUT_VARIABLE pidin_output + RESULT_VARIABLE _pidin_ok) + if(_pidin_ok EQUAL 0) + string(REGEX MATCHALL "Processor[0-9]+: " procs "${pidin_output}") + list(LENGTH procs count) + endif() #message("ProcessorCount: trying pidin '${ProcessorCount_cmd_pidin}'") endif() endif() - if(NOT count) + if(NOT count GREATER 0) # Sun (systems where psrinfo tool is available) find_program(ProcessorCount_cmd_psrinfo psrinfo PATHS /usr/sbin /sbin) mark_as_advanced(ProcessorCount_cmd_psrinfo) @@ -197,13 +209,16 @@ execute_process(COMMAND ${ProcessorCount_cmd_psrinfo} -p -v ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE psrinfo_output) - string(REGEX MATCHALL "has [0-9]+ virtual processor" procs "${psrinfo_output}") - set(count "") - foreach(proc ${procs}) - string(REGEX MATCH "has ([0-9]+) virtual" res ${proc}) - math(EXPR count "${count} + ${CMAKE_MATCH_1}") - endforeach() + OUTPUT_VARIABLE psrinfo_output + RESULT_VARIABLE _psrinfo_ok) + if(_psrinfo_ok EQUAL 0) + string(REGEX MATCHALL "has [0-9]+ virtual processor" procs "${psrinfo_output}") + set(count "") + foreach(proc IN LISTS procs) + string(REGEX MATCH "has ([0-9]+) virtual" res ${proc}) + math(EXPR count "${count} + ${CMAKE_MATCH_1}") + endforeach() + endif() #message("ProcessorCount: trying '${ProcessorCount_cmd_psrinfo}' -p -v") else() # Sun (systems where uname -X emits "NumCPU" in its output): @@ -213,9 +228,12 @@ execute_process(COMMAND ${ProcessorCount_cmd_uname} -X ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE uname_X_output) - string(REGEX MATCHALL "NumCPU = ([0-9]+)" procs "${uname_X_output}") - set(count "${CMAKE_MATCH_1}") + OUTPUT_VARIABLE uname_X_output + RESULT_VARIABLE _uname_X_ok) + if(_uname_X_ok EQUAL 0) + string(REGEX MATCHALL "NumCPU = ([0-9]+)" procs "${uname_X_output}") + set(count "${CMAKE_MATCH_1}") + endif() #message("ProcessorCount: trying uname -X '${ProcessorCount_cmd_uname}'") endif() endif() @@ -224,7 +242,7 @@ # Execute this code when all previously attempted methods return empty # output: # - if(NOT count) + if(NOT count GREATER 0) # Systems with /proc/cpuinfo: set(cpuinfo_file /proc/cpuinfo) if(EXISTS "${cpuinfo_file}") @@ -237,7 +255,7 @@ endif() endif() - if(NOT count) + if(NOT count GREATER 0) # Haiku find_program(ProcessorCount_cmd_sysinfo sysinfo) mark_as_advanced(ProcessorCount_cmd_sysinfo) @@ -245,9 +263,12 @@ execute_process(COMMAND ${ProcessorCount_cmd_sysinfo} ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE sysinfo_X_output) - string(REGEX MATCHALL "\nCPU #[0-9]+:" procs "\n${sysinfo_X_output}") - list(LENGTH procs count) + OUTPUT_VARIABLE sysinfo_X_output + RESULT_VARIABLE _sysinfo_ok) + if(_sysinfo_ok EQUAL 0) + string(REGEX MATCHALL "\nCPU #[0-9]+:" procs "\n${sysinfo_X_output}") + list(LENGTH procs count) + endif() #message("ProcessorCount: trying sysinfo '${ProcessorCount_cmd_sysinfo}'") endif() endif() @@ -255,7 +276,7 @@ # Since cygwin builds of CMake do not define WIN32 anymore, but they still # run on Windows, and will still have this env var defined: # - if(NOT count) + if(NOT count GREATER 0) set(count "$ENV{NUMBER_OF_PROCESSORS}") #message("ProcessorCount: last fallback, trying environment variable") endif()
diff --git a/Modules/SquishTestScript.cmake b/Modules/SquishTestScript.cmake index 9575084..d74b03e 100644 --- a/Modules/SquishTestScript.cmake +++ b/Modules/SquishTestScript.cmake
@@ -45,7 +45,7 @@ if(squish_pre_command) message(STATUS "Executing pre command: ${squish_pre_command}") - execute_process(COMMAND "${squish_pre_command}") + execute_process(COMMAND "${squish_pre_command}" RESULT_VARIABLE _result_pre_command) endif() # run the test @@ -71,7 +71,7 @@ if(squish_post_command) message(STATUS "Executing post command: ${squish_post_command}") - execute_process(COMMAND "${squish_post_command}") + execute_process(COMMAND "${squish_post_command}" RESULT_VARIABLE _result_post_command) endif() # check for an error with running the test
diff --git a/Modules/UseJava/Symlinks.cmake b/Modules/UseJava/Symlinks.cmake index 3142dca..6281067 100644 --- a/Modules/UseJava/Symlinks.cmake +++ b/Modules/UseJava/Symlinks.cmake
@@ -13,6 +13,7 @@ execute_process( COMMAND ${LN_EXECUTABLE} -sf "${_JAVA_TARGET_OUTPUT_NAME}" "${_JAVA_TARGET_OUTPUT_LINK}" WORKING_DIRECTORY ${_JAVA_TARGET_DIR} + RESULT_VARIABLE _result_symlink_creation ) else () message(SEND_ERROR "FATAL: Can't find _JAVA_TARGET_OUTPUT_NAME")
diff --git a/Modules/UseJava/javaTargets.cmake.in b/Modules/UseJava/javaTargets.cmake.in index 7a88344..157a60e 100644 --- a/Modules/UseJava/javaTargets.cmake.in +++ b/Modules/UseJava/javaTargets.cmake.in
@@ -1,5 +1,5 @@ cmake_policy(PUSH) -cmake_policy(VERSION 2.8.12...4.1) +cmake_policy(VERSION 2.8.12...4.2) #---------------------------------------------------------------- # Generated CMake Java target import file.
diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake index a469c4f..3d901dd 100644 --- a/Modules/UseSWIG.cmake +++ b/Modules/UseSWIG.cmake
@@ -668,8 +668,7 @@ endif() else() if (NOT module_name_policy) - cmake_policy(GET_WARNING CMP0086 _cmp0086_warning) - message(AUTHOR_WARNING "${_cmp0086_warning}\n") + cmake_policy(ISSUE_WARNING CMP0086) endif() endif() @@ -839,8 +838,7 @@ set (UseSWIG_TARGET_NAME_PREFERENCE STANDARD) else() if (NOT target_name_policy) - cmake_policy(GET_WARNING CMP0078 _cmp0078_warning) - message(AUTHOR_WARNING "${_cmp0078_warning}\n") + cmake_policy(ISSUE_WARNING CMP0078) endif() if (NOT DEFINED UseSWIG_TARGET_NAME_PREFERENCE) set (UseSWIG_TARGET_NAME_PREFERENCE LEGACY) @@ -1057,8 +1055,7 @@ # Do *not* override the target's library prefix else() if (NOT csharp_naming_policy) - cmake_policy(GET_WARNING CMP0122 _cmp0122_warning) - message(AUTHOR_WARNING "${_cmp0122_warning}\n") + cmake_policy(ISSUE_WARNING CMP0122) endif() set_target_properties (${target_name} PROPERTIES PREFIX "") endif()
diff --git a/Modules/WriteBasicConfigVersionFile.cmake b/Modules/WriteBasicConfigVersionFile.cmake index 4a90aea..b37ebaf 100644 --- a/Modules/WriteBasicConfigVersionFile.cmake +++ b/Modules/WriteBasicConfigVersionFile.cmake
@@ -14,7 +14,9 @@ WRITE_BASIC_CONFIG_VERSION_FILE(filename [VERSION major.minor.patch] - COMPATIBILITY (AnyNewerVersion|SameMajorVersion|SameMinorVersion|ExactVersion) + COMPATIBILITY (AnyNewerVersion|SameMajorVersion|SameMinorVersion| + SamePatchVersion|SameFullVersion|ExactVersion| + SemanticVersion) [ARCH_INDEPENDENT] ) @@ -33,7 +35,12 @@ message(FATAL_ERROR "Unknown keywords given to WRITE_BASIC_CONFIG_VERSION_FILE(): \"${CVF_UNPARSED_ARGUMENTS}\"") endif() - set(versionTemplateFile "${CMAKE_ROOT}/Modules/BasicConfigVersion-${CVF_COMPATIBILITY}.cmake.in") + set(_compatibilityTemplate "${CVF_COMPATIBILITY}") + if(CVF_COMPATIBILITY STREQUAL "ExactVersion") + set(_compatibilityTemplate "SamePatchVersion") + endif() + + set(versionTemplateFile "${CMAKE_ROOT}/Modules/BasicConfigVersion-${_compatibilityTemplate}.cmake.in") if(NOT EXISTS "${versionTemplateFile}") message(FATAL_ERROR "Bad COMPATIBILITY value used for WRITE_BASIC_CONFIG_VERSION_FILE(): \"${CVF_COMPATIBILITY}\"") endif()
diff --git a/README.rst b/README.rst index 3eefaf9..a10da9c 100644 --- a/README.rst +++ b/README.rst
@@ -89,6 +89,12 @@ $ mkdir build && cd build $ ../bootstrap && make +By default the ``bootstrap`` script configures CMake to build with vendored +dependencies. However, CMake does not vendor OpenSSL. To bootstrap in a +minimal environment, disable use of OpenSSL:: + + $ ./bootstrap -- -DCMAKE_USE_OPENSSL=OFF + Windows ^^^^^^^
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 0be5788..b601fe4 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt
@@ -125,6 +125,7 @@ cmCLocaleEnvironmentScope.cxx cmCMakePath.h cmCMakePath.cxx + cmCMakePresetsArgs.h cmCMakePresetsErrors.cxx cmCMakePresetsErrors.h cmCMakePresetsGraph.cxx @@ -136,6 +137,7 @@ cmCMakePresetsGraphReadJSONPackagePresets.cxx cmCMakePresetsGraphReadJSONTestPresets.cxx cmCMakePresetsGraphReadJSONWorkflowPresets.cxx + cmCMakePresetsGraphResolve.cxx cmCMakeString.hxx cmCMakeString.cxx cmCommandLineArgument.h @@ -187,12 +189,18 @@ cmDependsJavaParserHelper.h cmDependsCompiler.cxx cmDependsCompiler.h + cmDiagnosticContext.h + cmDiagnosticContext.cxx + cmDiagnostics.h + cmDiagnostics.cxx cmDocumentation.cxx cmDocumentationFormatter.cxx cmDyndepCollation.cxx cmDyndepCollation.h cmELF.h cmELF.cxx + cmEnvironment.cxx + cmEnvironment.h cmEvaluatedTargetProperty.cxx cmEvaluatedTargetProperty.h cmExprParserHelper.cxx @@ -206,8 +214,10 @@ cmExportBuildFileGenerator.cxx cmExportBuildPackageInfoGenerator.h cmExportBuildPackageInfoGenerator.cxx - cmExportBuildSbomGenerator.h - cmExportBuildSbomGenerator.cxx + cmBuildSbomGenerator.h + cmBuildSbomGenerator.cxx + cmBuildSbomBuilder.h + cmBuildSbomBuilder.cxx cmExportCMakeConfigGenerator.h cmExportCMakeConfigGenerator.cxx cmExportFileGenerator.h @@ -220,14 +230,12 @@ cmExportInstallFileGenerator.cxx cmExportInstallPackageInfoGenerator.h cmExportInstallPackageInfoGenerator.cxx - cmExportInstallSbomGenerator.h - cmExportInstallSbomGenerator.cxx cmExportPackageInfoGenerator.h cmExportPackageInfoGenerator.cxx cmExportTryCompileFileGenerator.h cmExportTryCompileFileGenerator.cxx - cmExportSbomGenerator.h - cmExportSbomGenerator.cxx + cmSbomBuilder.h + cmSbomBuilder.cxx cmExportSet.h cmExportSet.cxx cmExternalMakefileProjectGenerator.cxx @@ -270,6 +278,8 @@ cmFilePathChecksum.h cmFileSet.cxx cmFileSet.h + cmFileSetMetadata.cxx + cmFileSetMetadata.h cmFileTime.cxx cmFileTime.h cmFileTimeCache.cxx @@ -302,15 +312,19 @@ cmGeneratorExpressionParser.h cmGeneratorExpression.cxx cmGeneratorExpression.h + cmGeneratorFileSet.cxx + cmGeneratorFileSet.h + cmGeneratorFileSets.cxx + cmGeneratorFileSets.h cmGeneratorTarget.cxx cmGeneratorTarget.h cmGeneratorTarget_CompatibleInterface.cxx + cmGeneratorTarget_HeaderSetVerification.cxx cmGeneratorTarget_IncludeDirectories.cxx cmGeneratorTarget_Link.cxx cmGeneratorTarget_LinkDirectories.cxx cmGeneratorTarget_Options.cxx cmGeneratorTarget_Sources.cxx - cmGeneratorTarget_TargetPropertyEntry.cxx cmGeneratorTarget_TransitiveProperty.cxx cmLinkItemGraphVisitor.cxx cmLinkItemGraphVisitor.h @@ -334,6 +348,8 @@ cmInstallAndroidMKExportGenerator.h cmInstallCMakeConfigExportGenerator.cxx cmInstallCMakeConfigExportGenerator.h + cmInstallDirs.cxx + cmInstallDirs.h cmInstallGenerator.h cmInstallGenerator.cxx cmInstallGetRuntimeDependenciesGenerator.h @@ -353,8 +369,10 @@ cmInstallRuntimeDependencySet.cxx cmInstallRuntimeDependencySetGenerator.h cmInstallRuntimeDependencySetGenerator.cxx - cmInstallSbomExportGenerator.h - cmInstallSbomExportGenerator.cxx + cmInstallSbomGenerator.h + cmInstallSbomGenerator.cxx + cmInstallSbomBuilder.h + cmInstallSbomBuilder.cxx cmInstallScriptGenerator.h cmInstallScriptGenerator.cxx cmInstallSubdirectoryGenerator.h @@ -496,13 +514,14 @@ cmStdIoTerminal.cxx cmStringAlgorithms.cxx cmStringAlgorithms.h - cmSyntheticTargetCache.h cmSystemTools.cxx cmSystemTools.h cmTarget.cxx cmTarget.h cmTargetPropertyComputer.cxx cmTargetPropertyComputer.h + cmTargetPropertyEntry.cxx + cmTargetPropertyEntry.h cmTargetExport.h cmTargetTraceDependencies.cxx cmTargetTraceDependencies.h @@ -571,6 +590,8 @@ cmBreakCommand.h cmBuildCommand.cxx cmBuildCommand.h + cmCMakeDiagnosticCommand.cxx + cmCMakeDiagnosticCommand.h cmCMakeHostSystemInformationCommand.cxx cmCMakeHostSystemInformationCommand.h cmCMakeLanguageCommand.cxx @@ -595,6 +616,8 @@ cmCreateTestSourceList.h cmDefinePropertyCommand.cxx cmDefinePropertyCommand.h + cmDiscoverTestsCommand.cxx + cmDiscoverTestsCommand.h cmEnableLanguageCommand.cxx cmEnableLanguageCommand.h cmEnableTestingCommand.cxx @@ -857,6 +880,22 @@ llpkgc::llpkgc ) +# Built-in cache variable documentation table. This is a committed, +# hand-edited translation unit consumed by cmCacheManager to provide +# fallback HELPSTRING values for cache entries that ship without one. +# See the header comment in cmCacheDocumentationTable.cxx for the scope +# and maintenance discipline. The cmCachePatternTable sibling provides +# the same service for placeholder-shaped variable names (CMAKE_<LANG>_*, +# CMAKE_POLICY_DEFAULT_CMP<NNNN>, etc.) and is consulted as a fallback +# inside cmCacheDocumentationTable::Get. No code generation occurs at +# build time. +target_sources(CMakeLib PRIVATE + cmCacheDocumentationTable.h + cmCacheDocumentationTable.cxx + cmCachePatternTable.h + cmCachePatternTable.cxx +) + if(CMake_ENABLE_DEBUGGER) target_sources( CMakeLib @@ -1127,6 +1166,7 @@ CTest/cmCTestCoverageCommand.cxx CTest/cmCTestCoverageHandler.cxx CTest/cmCTestCurl.cxx + CTest/cmCTestDiscoverTests.cxx CTest/cmParseMumpsCoverage.cxx CTest/cmParseCacheCoverage.cxx CTest/cmParseGTMCoverage.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index c98d009..3c68498 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 4) -set(CMake_VERSION_MINOR 3) -set(CMake_VERSION_PATCH 5) +set(CMake_VERSION_MINOR 4) +set(CMake_VERSION_PATCH 3) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx index e440b84..8b1e9b2 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -22,7 +22,6 @@ #include "cmList.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmUuid.h" #include "cmValue.h" #include "cmWIXDirectoriesSourceWriter.h" #include "cmWIXFeaturesSourceWriter.h"
diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx index 9581d9f..821b00d 100644 --- a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
@@ -12,7 +12,6 @@ #include "cmCMakeToWixPath.h" #include "cmInstalledFile.h" #include "cmSystemTools.h" -#include "cmUuid.h" #include "cmWIXAccessControlList.h" #ifdef _WIN32
diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx index 8ee5637..e937fe0 100644 --- a/Source/CPack/cmCPackArchiveGenerator.cxx +++ b/Source/CPack/cmCPackArchiveGenerator.cxx
@@ -361,7 +361,8 @@ } else if (status == DeduplicateStatus::Error) { cmCPackLogger(cmCPackLog::LOG_ERROR, "ERROR The data in files with the " - "same filename is different."); + "same filename is different: " + << rp << std::endl); return 0; } else { cmCPackLogger(cmCPackLog::LOG_DEBUG, @@ -393,7 +394,7 @@ return 0; \ } \ cmArchiveWrite archive(gf, this->Compress, this->ArchiveFormat, \ - this->GetCompressionLevel(), \ + this->GetEncoding(), this->GetCompressionLevel(), \ this->GetThreadCount()); \ if (this->UID >= 0 && this->GID >= 0) { \ archive.SetUIDAndGID(this->UID, this->GID); \ @@ -610,3 +611,14 @@ return level; } + +std::string cmCPackArchiveGenerator::GetEncoding() const +{ + std::string encoding = "UTF-8"; + + if (cmValue v = this->GetOptionIfSet("CPACK_ARCHIVE_ENCODING")) { + encoding = *v; + } + + return encoding; +}
diff --git a/Source/CPack/cmCPackArchiveGenerator.h b/Source/CPack/cmCPackArchiveGenerator.h index 22c8bba..e295561 100644 --- a/Source/CPack/cmCPackArchiveGenerator.h +++ b/Source/CPack/cmCPackArchiveGenerator.h
@@ -106,6 +106,7 @@ int GetThreadCount() const; int GetCompressionLevel() const; + std::string GetEncoding() const; private: cmArchiveWrite::Compress Compress;
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 523a293..63e58a4 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx
@@ -37,7 +37,8 @@ std::map<std::string, std::string> controlValues, bool genShLibs, std::string shLibsFilename, bool genPostInst, std::string postInst, bool genPostRm, std::string postRm, - cmValue controlExtra, bool permissionStrctPolicy, + bool genTriggers, std::string triggers, cmValue controlExtra, + bool permissionStrctPolicy, std::vector<std::string> packageFiles); bool generate() const; @@ -65,6 +66,8 @@ std::string const PostInst; bool const GenPostRm; std::string const PostRm; + bool const GenTriggers; + std::string const Triggers; cmValue ControlExtra; bool const PermissionStrictPolicy; std::vector<std::string> const PackageFiles; @@ -78,8 +81,8 @@ cmValue debCompressionType, cmValue debCompressionLevel, cmValue numThreads, cmValue debianArchiveType, std::map<std::string, std::string> controlValues, bool genShLibs, std::string shLibsFilename, bool genPostInst, - std::string postInst, bool genPostRm, std::string postRm, - cmValue controlExtra, bool permissionStrictPolicy, + std::string postInst, bool genPostRm, std::string postRm, bool genTriggers, + std::string triggers, cmValue controlExtra, bool permissionStrictPolicy, std::vector<std::string> packageFiles) : Logger(logger) , OutputName(std::move(outputName)) @@ -94,6 +97,8 @@ , PostInst(std::move(postInst)) , GenPostRm(genPostRm) , PostRm(std::move(postRm)) + , GenTriggers(genTriggers) + , Triggers(std::move(triggers)) , ControlExtra(controlExtra) , PermissionStrictPolicy(permissionStrictPolicy) , PackageFiles(std::move(packageFiles)) @@ -206,9 +211,9 @@ << filename_data_tar << "\" for writing" << std::endl); return false; } - cmArchiveWrite data_tar(fileStream_data_tar, this->TarCompressionType, - this->DebianArchiveType, this->CompressionLevel, - static_cast<int>(this->NumThreads)); + cmArchiveWrite data_tar( + fileStream_data_tar, this->TarCompressionType, this->DebianArchiveType, + "UTF-8", this->CompressionLevel, static_cast<int>(this->NumThreads)); if (!data_tar.Open()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error opening the archive \"" @@ -297,7 +302,7 @@ cmGeneratedFileStream out; out.Open(md5filename, false, true); - std::string topLevelWithTrailingSlash = cmStrCat(this->TemporaryDir, '/'); + std::string topLevelWithTrailingSlash = cmStrCat(this->WorkDir, '/'); for (std::string const& file : this->PackageFiles) { // hash only regular files if (cmSystemTools::FileIsDirectory(file) || @@ -339,7 +344,8 @@ return false; } cmArchiveWrite control_tar(fileStream_control_tar, this->TarCompressionType, - this->DebianArchiveType, this->CompressionLevel); + this->DebianArchiveType, "UTF-8", + this->CompressionLevel); if (!control_tar.Open()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error opening the archive \"" @@ -429,14 +435,28 @@ control_tar.SetPermissions(permission644); } + if (this->GenTriggers) { + if (!control_tar.Add(this->Triggers, this->WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:\n" + "#top level directory: " + << this->WorkDir + << "\n" + "#file: \"triggers\"\n" + "#error:" + << control_tar.GetError() << std::endl); + return false; + } + } + // for the other files, we use // -either the original permission on the files // -either a permission strictly defined by the Debian policies if (this->ControlExtra) { // permissions are now controlled by the original file permissions - static char const* strictFiles[] = { "config", "postinst", "postrm", - "preinst", "prerm" }; + static char const* strictFiles[] = { "config", "postinst", "postrm", + "preinst", "prerm", "triggers" }; std::set<std::string> setStrictFiles( strictFiles, strictFiles + sizeof(strictFiles) / sizeof(strictFiles[0])); @@ -837,6 +857,7 @@ std::string const postinst = strGenWDIR + "/postinst"; std::string const postrm = strGenWDIR + "/postrm"; + std::string const triggers = strGenWDIR + "/triggers"; if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST")) { cmGeneratedFileStream out; out.Open(postinst, false, true); @@ -855,6 +876,11 @@ "\tldconfig\n" "fi\n"; } + if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_LDCONFIG_TRIGGERS")) { + cmGeneratedFileStream out; + out.Open(triggers, false, true); + out << "activate-noawait ldconfig\n"; + } DebGenerator gen( this->Logger, this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"), strGenWDIR, @@ -866,6 +892,7 @@ this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues, gen_shibs, shlibsfilename, this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST"), postinst, this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM"), postrm, + this->IsOn("GEN_CPACK_DEBIAN_GENERATE_LDCONFIG_TRIGGERS"), triggers, this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"), this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"), this->packageFiles); @@ -920,7 +947,7 @@ this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_LEVEL"), this->GetOption("CPACK_THREADS"), this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues, false, "", - false, "", false, "", nullptr, + false, "", false, "", false, "", nullptr, this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"), this->packageFiles);
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index aa07d83..bc95f5b 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx
@@ -1158,6 +1158,9 @@ return 0; } this->files = gl.GetFiles(); + + // Add files in a reproducible order + std::stable_sort(this->files.begin(), this->files.end()); } // Determine and store internally the directory that shall be packaged.
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index 538222d..551a93e 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -4,7 +4,6 @@ #include <algorithm> #include <cstdlib> -#include <cstring> #include <map> #include <sstream> #include <utility> @@ -88,7 +87,7 @@ "Uninstall Files: " << str.str() << std::endl); this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str()); std::vector<std::string> dirs; - this->GetListOfSubdirectories(this->toplevel.c_str(), dirs); + this->GetListOfSubdirectories(this->toplevel, dirs); std::ostringstream dstr; for (std::string const& dir : dirs) { std::string componentName; @@ -376,8 +375,10 @@ } this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", installTypesCode); - this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", - "!insertmacro MUI_PAGE_COMPONENTS"); + if (!this->IsSet("CPACK_NSIS_IGNORE_COMPONENTS_PAGE")) { + this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", + "!insertmacro MUI_PAGE_COMPONENTS"); + } this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", ""); this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", componentCode); this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", sectionList); @@ -677,18 +678,17 @@ } bool cmCPackNSISGenerator::GetListOfSubdirectories( - char const* topdir, std::vector<std::string>& dirs) + std::string const& topdir, std::vector<std::string>& dirs) { cmsys::Directory dir; dir.Load(topdir); for (unsigned long i = 0; i < dir.GetNumberOfFiles(); ++i) { - char const* fileName = dir.GetFile(i); - if (strcmp(fileName, ".") != 0 && strcmp(fileName, "..") != 0) { - std::string const fullPath = - std::string(topdir).append("/").append(fileName); + std::string const& fileName = dir.GetFileName(i); + if (fileName != "." && fileName != "..") { + std::string const fullPath = cmStrCat(topdir, '/', fileName); if (cmsys::SystemTools::FileIsDirectory(fullPath) && !cmsys::SystemTools::FileIsSymlink(fullPath)) { - if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs)) { + if (!this->GetListOfSubdirectories(fullPath, dirs)) { return false; } }
diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h index 5aca90d..d9a37bc 100644 --- a/Source/CPack/cmCPackNSISGenerator.h +++ b/Source/CPack/cmCPackNSISGenerator.h
@@ -44,7 +44,7 @@ char const* GetOutputExtension() override { return ".exe"; } char const* GetOutputPostfix() override { return "win32"; } - bool GetListOfSubdirectories(char const* dir, + bool GetListOfSubdirectories(std::string const& dir, std::vector<std::string>& dirs); enum cmCPackGenerator::CPackSetDestdirSupport SupportsSetDestdir()
diff --git a/Source/CPack/cmCPackProductBuildGenerator.cxx b/Source/CPack/cmCPackProductBuildGenerator.cxx index aafa92b..cfa9a3b 100644 --- a/Source/CPack/cmCPackProductBuildGenerator.cxx +++ b/Source/CPack/cmCPackProductBuildGenerator.cxx
@@ -85,18 +85,9 @@ std::string version = this->GetOption("CPACK_PACKAGE_VERSION"); std::string productbuild = this->GetOption("CPACK_COMMAND_PRODUCTBUILD"); - std::string identityName; - if (cmValue n = this->GetOption("CPACK_PRODUCTBUILD_IDENTITY_NAME")) { - identityName = n; - } - std::string keychainPath; - if (cmValue p = this->GetOption("CPACK_PRODUCTBUILD_KEYCHAIN_PATH")) { - keychainPath = p; - } - std::string identifier; - if (cmValue i = this->GetOption("CPACK_PRODUCTBUILD_IDENTIFIER")) { - identifier = i; - } + cmValue identityName = this->GetOption("CPACK_PRODUCTBUILD_IDENTITY_NAME"); + cmValue keychainPath = this->GetOption("CPACK_PRODUCTBUILD_KEYCHAIN_PATH"); + cmValue identifier = this->GetOption("CPACK_PRODUCTBUILD_IDENTIFIER"); pkgCmd << productbuild << " --distribution \"" << packageDirFileName << "/Contents/distribution.dist\"" @@ -108,16 +99,17 @@ << resDir << "\"" " --version \"" - << version << "\"" - << (identifier.empty() - ? std::string{} - : cmStrCat(" --identifier \"", identifier, '"')) - << (identityName.empty() ? std::string{} - : cmStrCat(" --sign \"", identityName, '"')) - << (keychainPath.empty() - ? std::string{} - : cmStrCat(" --keychain \"", keychainPath, '"')) - << " \"" << packageFileNames[0] << '"'; + << version << '"'; + if (identifier) { + pkgCmd << " --identifier \"" << identifier << '"'; + } + if (identityName) { + pkgCmd << " --sign \"" << identityName << '"'; + } + if (keychainPath) { + pkgCmd << " --keychain \"" << keychainPath << '"'; + } + pkgCmd << " \"" << packageFileNames[0] << '"'; // Run ProductBuild return RunProductBuild(pkgCmd.str()); @@ -205,10 +197,10 @@ // then copy them into the script directory and make // them executable if (preflight) { - this->CopyInstallScript(scriptDir, preflight, "preinstall"); + this->CopyInstallScript(scriptDir, *preflight, "preinstall"); } if (postflight) { - this->CopyInstallScript(scriptDir, postflight, "postinstall"); + this->CopyInstallScript(scriptDir, *postflight, "postinstall"); } // The command that will be used to run ProductBuild @@ -216,7 +208,7 @@ std::string pkgId; if (cmValue n = this->GetOption("CPACK_PRODUCTBUILD_IDENTIFIER")) { - pkgId = n; + pkgId = *n; } else { pkgId = cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"), '.', this->GetOption("CPACK_PACKAGE_NAME")); @@ -228,14 +220,8 @@ std::string version = this->GetOption("CPACK_PACKAGE_VERSION"); std::string pkgbuild = this->GetOption("CPACK_COMMAND_PKGBUILD"); - std::string identityName; - if (cmValue n = this->GetOption("CPACK_PKGBUILD_IDENTITY_NAME")) { - identityName = n; - } - std::string keychainPath; - if (cmValue p = this->GetOption("CPACK_PKGBUILD_KEYCHAIN_PATH")) { - keychainPath = p; - } + cmValue identityName = this->GetOption("CPACK_PKGBUILD_IDENTITY_NAME"); + cmValue keychainPath = this->GetOption("CPACK_PKGBUILD_KEYCHAIN_PATH"); pkgCmd << pkgbuild << " --root \"" << packageDir << "\"" @@ -248,16 +234,17 @@ " --version \"" << version << "\"" - " --install-location \"/\"" - << (identityName.empty() ? std::string{} - : cmStrCat(" --sign \"", identityName, '"')) - << (keychainPath.empty() - ? std::string{} - : cmStrCat(" --keychain \"", keychainPath, '"')) - << " \"" << packageFile << '"'; + " --install-location \"/\""; + if (identityName) { + pkgCmd << " --sign \"" << identityName << '"'; + } + if (keychainPath) { + pkgCmd << " --keychain \"" << keychainPath << '"'; + } + pkgCmd << " \"" << packageFile << '"'; if (component && !component->Plist.empty()) { - pkgCmd << " --component-plist \"" << component->Plist << "\""; + pkgCmd << " --component-plist \"" << component->Plist << '"'; } // Run ProductBuild
diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index 3a79367..c595378 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx
@@ -19,6 +19,7 @@ #include "cmsys/Encoding.hxx" +#include "cmCMakePresetsArgs.h" #include "cmCMakePresetsGraph.h" #include "cmCPackGenerator.h" #include "cmCPackGeneratorFactory.h" @@ -46,7 +47,7 @@ cmDocumentationEntry const cmDocumentationUsage = { {}, " cpack [options]" }; -cmDocumentationEntry const cmDocumentationOptions[14] = { +cmDocumentationEntry const cmDocumentationOptions[] = { { "-G <generators>", "Override/define CPACK_GENERATOR" }, { "-C <Configurations>", "Specify the project configuration(s)" }, { "-D <var>=<value>", "Set a CPack variable." }, @@ -60,6 +61,7 @@ { "-B <packageDirectory>", "Override/define CPACK_PACKAGE_DIRECTORY" }, { "--vendor <vendorName>", "Override/define CPACK_PACKAGE_VENDOR" }, { "--preset", "Read arguments from a package preset" }, + { "--presets-file", "Load package presets from the given file" }, { "--list-presets", "List available package presets" } }; @@ -130,8 +132,7 @@ std::string cpackProjectVendor; std::string cpackConfigFile; - std::string preset; - bool listPresets = false; + cmCMakePresetsArgs presetsArgs; std::map<std::string, std::string> definitions; @@ -162,6 +163,12 @@ return true; }; + auto const presetFileLambda = [&presetsArgs](std::string const& value, + cmake*, cmMakefile*) -> bool { + presetsArgs.PresetsFile = cmSystemTools::ToNormalizedPathOnDisk(value); + return true; + }; + using CommandArgument = cmCommandLineArgument<bool(std::string const&, cmake*, cmMakefile*)>; @@ -199,10 +206,13 @@ CommandArgument::setToValue(cpackProjectPatch) }, CommandArgument{ "--vendor", CommandArgument::Values::One, CommandArgument::setToValue(cpackProjectVendor) }, - CommandArgument{ "--preset", CommandArgument::Values::One, - CommandArgument::setToValue(preset) }, + CommandArgument{ "--preset", "No preset specified for --preset", + CommandArgument::Values::One, + CommandArgument::setToValue(presetsArgs.PresetName) }, CommandArgument{ "--list-presets", CommandArgument::Values::Zero, - CommandArgument::setToTrue(listPresets) }, + CommandArgument::setToTrue(presetsArgs.ListPresets) }, + CommandArgument{ "--presets-file", "No file specified for --presets-file", + CommandArgument::Values::One, presetFileLambda }, CommandArgument{ "-D", CommandArgument::Values::One, CommandArgument::RequiresSeparator::No, [&log, &definitions](std::string const& arg, cmake*, @@ -248,7 +258,7 @@ generators.SetLogger(&log); // Set up presets - if (!preset.empty() || listPresets) { + if (presetsArgs.HasPresetsArg()) { auto const workingDirectory = cmSystemTools::GetLogicalWorkingDirectory(); auto const presetGeneratorsPresent = @@ -261,7 +271,8 @@ }; cmCMakePresetsGraph presetsGraph; - auto result = presetsGraph.ReadProjectPresets(workingDirectory); + auto result = presetsGraph.ReadProjectPresets(workingDirectory, + presetsArgs.PresetsFile); if (result != true) { cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "Could not read presets from " @@ -270,46 +281,25 @@ return 1; } - if (listPresets) { + if (presetsArgs.ListPresets) { presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); return 0; } - auto presetPair = presetsGraph.PackagePresets.find(preset); - if (presetPair == presetsGraph.PackagePresets.end()) { - cmCPack_Log(&log, cmCPackLog::LOG_ERROR, - "No such package preset in " << workingDirectory << ": \"" - << preset << "\"\n"); + auto resolveResult = presetsGraph.ResolvePreset( + presetsArgs.PresetName, presetsGraph.PackagePresets); + auto resolveError = cmCMakePresetsGraph::FormatPresetError< + cmCMakePresetsGraph::PackagePreset>(resolveResult.StatusCode, + resolveResult.ErrorPresetName, + workingDirectory); + if (resolveError) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, *resolveError << "\n"); presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); return 1; } + auto const* expandedPreset = resolveResult.Preset; - if (presetPair->second.Unexpanded.Hidden) { - cmCPack_Log(&log, cmCPackLog::LOG_ERROR, - "Cannot use hidden package preset in " - << workingDirectory << ": \"" << preset << "\"\n"); - presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); - return 1; - } - - auto const& expandedPreset = presetPair->second.Expanded; - if (!expandedPreset) { - cmCPack_Log(&log, cmCPackLog::LOG_ERROR, - "Could not evaluate package preset \"" - << preset << "\": Invalid macro expansion\n"); - presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); - return 1; - } - - if (!expandedPreset->ConditionResult) { - cmCPack_Log(&log, cmCPackLog::LOG_ERROR, - "Cannot use disabled package preset in " - << workingDirectory << ": \"" << preset << "\"\n"); - presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); - return 1; - } - - if (!presetGeneratorsPresent(presetPair->second.Unexpanded)) { + if (!presetGeneratorsPresent(*expandedPreset)) { cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "Cannot use preset"); presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); return 1;
diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index d74ab55..f55ef65 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx
@@ -6,14 +6,17 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmCMakePresetsGraph.h" #include "cmCTest.h" #include "cmCTestBuildHandler.h" #include "cmCTestGenericHandler.h" #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" +#include "cmJSONState.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" @@ -21,6 +24,8 @@ #include "cmValue.h" #include "cmake.h" +using BuildPreset = cmCMakePresetsGraph::BuildPreset; + bool cmCTestBuildCommand::InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) const { @@ -32,7 +37,9 @@ .Bind("CONFIGURATION"_s, &BuildArguments::Configuration) .Bind("FLAGS"_s, &BuildArguments::Flags) .Bind("PROJECT_NAME"_s, &BuildArguments::ProjectName) - .Bind("PARALLEL_LEVEL"_s, &BuildArguments::ParallelLevel); + .Bind("PARALLEL_LEVEL"_s, &BuildArguments::ParallelLevel) + .Bind("PRESET"_s, &BuildArguments::Preset) + .Bind("PRESETS_FILE"_s, &BuildArguments::PresetsFile); return this->Invoke(parser, args, status, [&](BuildArguments& a) { return this->ExecuteHandlerCommand(a, status); @@ -46,32 +53,142 @@ auto const& args = static_cast<BuildArguments&>(arguments); auto handler = cm::make_unique<cmCTestBuildHandler>(this->CTest); + // Build configuration is set according to the following priority order: + // 1) The CONFIGURATION option to ctest_build() + // 2) CTEST_BUILD_CONFIGURATION script variable + // 3) CTEST_CONFIGURATION_TYPE script variable + // 4) The ctest -C command line argument + // 5) The configuration entry from the build preset + cmValue ctestBuildConfiguration = + mf.GetDefinition("CTEST_BUILD_CONFIGURATION"); + std::string cmakeBuildConfiguration = cmNonempty(args.Configuration) + ? args.Configuration + : cmNonempty(ctestBuildConfiguration) ? *ctestBuildConfiguration + : this->CTest->GetConfigType(); + + std::string const& cmakeBuildAdditionalFlags = cmNonempty(args.Flags) + ? args.Flags + : mf.GetSafeDefinition("CTEST_BUILD_FLAGS"); + std::string const& cmakeBuildTarget = cmNonempty(args.Target) + ? args.Target + : mf.GetSafeDefinition("CTEST_BUILD_TARGET"); + + // Preset name is set according to the following priority order: + // 1) The PRESET option to ctest_build() + // 2) CTEST_BUILD_PRESET script variable + // 3) CTEST_PRESET script variable (a warning is emitted if no build preset + // exists with this name) + std::string const sourceDirectory = + mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); + + // Presets file is set according to the following priority order: + // 1) The PRESETS_FILE option to ctest_build() + // 2) CTEST_PRESETS_FILE script variable + std::string const rawPresetsFile = !args.PresetsFile.empty() + ? args.PresetsFile + : mf.GetSafeDefinition("CTEST_PRESETS_FILE"); + + std::string const presetsFile = rawPresetsFile.empty() + ? "" + : cmSystemTools::CollapseFullPath(rawPresetsFile, sourceDirectory); + + std::string effectivePreset = !args.Preset.empty() ? args.Preset + : cmNonempty(mf.GetDefinition("CTEST_BUILD_PRESET")) + ? *mf.GetDefinition("CTEST_BUILD_PRESET") + : ""; + if (effectivePreset.empty()) { + cmValue v = mf.GetDefinition("CTEST_PRESET"); + if (cmNonempty(v)) { + std::string presetError; + auto presetCheck = + BuildPresetExists(*v, sourceDirectory, presetsFile, presetError); + if (presetCheck == PresetCheckResult::ReadError) { + status.SetError(cmStrCat('\n', presetError)); + return nullptr; + } + if (presetCheck == PresetCheckResult::Found) { + effectivePreset = *v; + } else { + cmCTestLog(this->CTest, WARNING, + "No build preset named \"" + << *v << "\" found, ignoring CTEST_PRESET." << std::endl); + } + } + } + + // Skip checking CTEST_BUILD_COMMAND when a preset is specified. + // We do this to avoid using a stale MakeCommand from DartConfiguration.tcl + // that would cause us to silently ignore the requested preset. cmValue ctestBuildCommand = mf.GetDefinition("CTEST_BUILD_COMMAND"); - if (cmNonempty(ctestBuildCommand)) { + if (cmNonempty(ctestBuildCommand) && effectivePreset.empty()) { this->CTest->SetCTestConfiguration("MakeCommand", *ctestBuildCommand, args.Quiet); + } else if (!effectivePreset.empty()) { + if (cmNonempty(ctestBuildCommand)) { + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Ignoring CTEST_BUILD_COMMAND because preset \"" + << effectivePreset << "\" is in use.\n", + args.Quiet); + } + cmCMakePresetsGraph presetsGraph; + if (!presetsGraph.ReadProjectPresets(sourceDirectory, presetsFile)) { + status.SetError(cmStrCat("\n Could not read presets from \"", + sourceDirectory, "\":\n ", + presetsGraph.parseState.GetErrorMessage())); + return nullptr; + } + + auto resolveResult = + presetsGraph.ResolvePreset(effectivePreset, presetsGraph.BuildPresets); + auto resolveError = cmCMakePresetsGraph::FormatPresetError<BuildPreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + sourceDirectory); + if (resolveError) { + status.SetError(*resolveError); + return nullptr; + } + + std::string buildCommand = + cmStrCat('"', cmSystemTools::GetCMakeCommand(), '"'); + buildCommand += " --build . --preset \""; + buildCommand += effectivePreset; + buildCommand += "\""; + + if (!presetsFile.empty()) { + buildCommand += " --presets-file \""; + buildCommand += presetsFile; + buildCommand += "\""; + } + + if (!cmakeBuildConfiguration.empty()) { + buildCommand += " --config \""; + buildCommand += cmakeBuildConfiguration; + buildCommand += "\""; + } + + if (!cmakeBuildTarget.empty()) { + buildCommand += " --target \""; + buildCommand += cmakeBuildTarget; + buildCommand += "\""; + } + + if (!args.ParallelLevel.empty()) { + buildCommand += " --parallel "; + buildCommand += args.ParallelLevel; + } + + if (!cmakeBuildAdditionalFlags.empty()) { + buildCommand += " -- "; + buildCommand += cmakeBuildAdditionalFlags; + } + + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "SetMakeCommand:" << buildCommand << "\n", args.Quiet); + this->CTest->SetCTestConfiguration("MakeCommand", buildCommand, + args.Quiet); } else { cmValue cmakeGeneratorName = mf.GetDefinition("CTEST_CMAKE_GENERATOR"); - // Build configuration is determined by: CONFIGURATION argument, - // or CTEST_BUILD_CONFIGURATION script variable, or - // CTEST_CONFIGURATION_TYPE script variable, or ctest -C command - // line argument... in that order. - // - cmValue ctestBuildConfiguration = - mf.GetDefinition("CTEST_BUILD_CONFIGURATION"); - std::string cmakeBuildConfiguration = cmNonempty(args.Configuration) - ? args.Configuration - : cmNonempty(ctestBuildConfiguration) ? *ctestBuildConfiguration - : this->CTest->GetConfigType(); - - std::string const& cmakeBuildAdditionalFlags = cmNonempty(args.Flags) - ? args.Flags - : mf.GetSafeDefinition("CTEST_BUILD_FLAGS"); - std::string const& cmakeBuildTarget = cmNonempty(args.Target) - ? args.Target - : mf.GetSafeDefinition("CTEST_BUILD_TARGET"); - if (cmNonempty(cmakeGeneratorName)) { if (cmakeBuildConfiguration.empty()) { cmakeBuildConfiguration = "Release";
diff --git a/Source/CTest/cmCTestBuildCommand.h b/Source/CTest/cmCTestBuildCommand.h index b83f5db..5da6427 100644 --- a/Source/CTest/cmCTestBuildCommand.h +++ b/Source/CTest/cmCTestBuildCommand.h
@@ -23,6 +23,8 @@ std::string Flags; std::string ProjectName; std::string ParallelLevel; + std::string Preset; + std::string PresetsFile; }; private:
diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 71c9e3f..c7e7c3d 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx
@@ -465,6 +465,9 @@ xml.StartElement("Build"); xml.Element("StartDateTime", this->StartBuild); xml.Element("StartBuildTime", this->StartBuildTime); + xml.Element("SourceDirectory", + this->CTest->GetCTestConfiguration("SourceDirectory")); + xml.Element("BinaryDirectory", this->CTest->GetBinaryDir()); xml.Element("BuildCommand", this->GetMakeCommand()); } @@ -511,7 +514,7 @@ launchDir.Load(this->CTestLaunchDir); unsigned long n = launchDir.GetNumberOfFiles(); for (unsigned long i = 0; i < n; ++i) { - char const* fname = launchDir.GetFile(i); + std::string const& fname = launchDir.GetFileName(i); if (this->IsLaunchedErrorFile(fname) && numErrorsAllowed) { numErrorsAllowed--; fragments.insert(cmStrCat(this->CTestLaunchDir, '/', fname)); @@ -616,7 +619,7 @@ if (!targets_dir.FileIsDirectory(i)) { continue; } - std::string target_name = targets_dir.GetFile(i); + std::string const& target_name = targets_dir.GetFileName(i); if (target_name == "." || target_name == "..") { continue; } @@ -696,14 +699,14 @@ this->CTest->EndXML(xml); } -bool cmCTestBuildHandler::IsLaunchedErrorFile(char const* fname) +bool cmCTestBuildHandler::IsLaunchedErrorFile(cm::string_view fname) { // error-{hash}.xml return (cmHasLiteralPrefix(fname, "error-") && cmHasLiteralSuffix(fname, ".xml")); } -bool cmCTestBuildHandler::IsLaunchedWarningFile(char const* fname) +bool cmCTestBuildHandler::IsLaunchedWarningFile(cm::string_view fname) { // warning-{hash}.xml return (cmHasLiteralPrefix(fname, "warning-") && @@ -949,7 +952,7 @@ launchDir.Load(this->CTestLaunchDir); unsigned long n = launchDir.GetNumberOfFiles(); for (unsigned long i = 0; i < n; ++i) { - char const* fname = launchDir.GetFile(i); + std::string const& fname = launchDir.GetFileName(i); if (cmHasLiteralSuffix(fname, ".xml")) { launcherXMLFound = true; break;
diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h index 2b8feec..470d3f9 100644 --- a/Source/CTest/cmCTestBuildHandler.h +++ b/Source/CTest/cmCTestBuildHandler.h
@@ -11,6 +11,8 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmsys/RegularExpression.hxx" #include "cmCTestGenericHandler.h" @@ -86,8 +88,8 @@ void GenerateXMLLogScraped(cmXMLWriter& xml); void GenerateInstrumentationXML(cmXMLWriter& xml); void GenerateXMLFooter(cmXMLWriter& xml, cmDuration elapsed_build_time); - bool IsLaunchedErrorFile(char const* fname); - bool IsLaunchedWarningFile(char const* fname); + bool IsLaunchedErrorFile(cm::string_view fname); + bool IsLaunchedWarningFile(cm::string_view fname); std::string StartBuild; std::string EndBuild;
diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx index 6bece77..c137d48 100644 --- a/Source/CTest/cmCTestConfigureCommand.cxx +++ b/Source/CTest/cmCTestConfigureCommand.cxx
@@ -9,9 +9,11 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmCMakePresetsGraph.h" #include "cmCTest.h" #include "cmDuration.h" #include "cmExecutionStatus.h" @@ -19,110 +21,244 @@ #include "cmGlobalGenerator.h" #include "cmInstrumentation.h" #include "cmInstrumentationQuery.h" +#include "cmJSONState.h" #include "cmList.h" #include "cmMakefile.h" +#include "cmOutputConverter.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmValue.h" #include "cmXMLWriter.h" #include "cmake.h" -bool cmCTestConfigureCommand::ExecuteConfigure(ConfigureArguments const& args, - cmExecutionStatus& status) const -{ - cmMakefile& mf = status.GetMakefile(); +using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset; - std::string const buildDirectory = !args.Build.empty() - ? args.Build - : mf.GetDefinition("CTEST_BINARY_DIRECTORY"); - if (buildDirectory.empty()) { +namespace { + +bool ConstructConfigureCommand(cmExecutionStatus& status, cmMakefile& mf, + std::string const sourceDirectory, + std::string const buildDirectory, + std::string const options, + std::string const presetName, + std::string const presetsFile, + std::string& configureCommand) +{ + configureCommand = cmStrCat('"', cmSystemTools::GetCMakeCommand(), '"'); + configureCommand += " \"-S"; + configureCommand += cmSystemTools::CollapseFullPath(sourceDirectory); + configureCommand += "\""; + + if (!buildDirectory.empty()) { + configureCommand += " \"-B"; + configureCommand += cmSystemTools::CollapseFullPath(buildDirectory); + configureCommand += "\""; + } + + cmValue cmakeGenerator = mf.GetDefinition("CTEST_CMAKE_GENERATOR"); + if (cmNonempty(cmakeGenerator)) { + configureCommand += " \"-G"; + configureCommand += cmakeGenerator; + configureCommand += "\""; + } + + bool presetProvidesBuildDir = false; + bool presetProvidesGenerator = false; + + if (!presetName.empty()) { + cmCMakePresetsGraph presetsGraph; + if (!presetsGraph.ReadProjectPresets(sourceDirectory, presetsFile)) { + status.SetError(cmStrCat("\n Could not read presets from \"", + sourceDirectory, "\":\n ", + presetsGraph.parseState.GetErrorMessage())); + return false; + } + + auto resolveResult = + presetsGraph.ResolvePreset(presetName, presetsGraph.ConfigurePresets); + auto resolveError = + cmCMakePresetsGraph::FormatPresetError<ConfigurePreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + sourceDirectory); + if (resolveError) { + status.SetError(*resolveError); + return false; + } + + auto const* expandedPreset = resolveResult.Preset; + + configureCommand += " \"--preset\""; + configureCommand += " \""; + configureCommand += presetName; + configureCommand += "\""; + + if (!presetsFile.empty()) { + configureCommand += " \"--presets-file\""; + configureCommand += " \""; + configureCommand += presetsFile; + configureCommand += "\""; + } + + if (!expandedPreset->BinaryDir.empty()) { + presetProvidesBuildDir = true; + } + + if (!expandedPreset->Generator.empty()) { + presetProvidesGenerator = true; + } + } + + if (buildDirectory.empty() && !presetProvidesBuildDir) { status.SetError("called with no build directory specified. " "Either use the BUILD argument or set the " "CTEST_BINARY_DIRECTORY variable."); return false; } - std::string configureCommand = mf.GetDefinition("CTEST_CONFIGURE_COMMAND"); - if (configureCommand.empty()) { - cmValue cmakeGenerator = mf.GetDefinition("CTEST_CMAKE_GENERATOR"); - if (!cmNonempty(cmakeGenerator)) { - status.SetError( - "called with no configure command specified. " - "If this is a \"built with CMake\" project, set " - "CTEST_CMAKE_GENERATOR. If not, set CTEST_CONFIGURE_COMMAND."); - return false; + if (!cmNonempty(cmakeGenerator) && !presetProvidesGenerator) { + status.SetError( + "called with no configure command specified. " + "If this is a \"built with CMake\" project, set " + "CTEST_CMAKE_GENERATOR. If not, set CTEST_CONFIGURE_COMMAND."); + return false; + } + + if (mf.IsOn("CTEST_USE_LAUNCHERS")) { + configureCommand += " \"-DCTEST_USE_LAUNCHERS:BOOL=TRUE\""; + } + + // Propagate CTEST_SITE / CTEST_BUILD_NAME into the SITE / BUILDNAME + // cache variables so DartConfiguration.tcl gets generated with + // correct values. We use an initial cache script (-C) instead of + // defining them on the command-line (-D) to avoid unused-cli warnings + // for projects that don't include the CTest module. + cmValue site = mf.GetDefinition("CTEST_SITE"); + cmValue buildName = mf.GetDefinition("CTEST_BUILD_NAME"); + if (cmNonempty(site) || cmNonempty(buildName)) { + std::string const initialCacheFile = + cmStrCat(cmSystemTools::CollapseFullPath(buildDirectory), + "/Testing/Temporary/CTestConfigureInitialCache.cmake"); + cmGeneratedFileStream initialCache(initialCacheFile); + if (cmNonempty(site)) { + initialCache << "set(SITE " << cmOutputConverter::EscapeForCMake(*site) + << " CACHE STRING \"\")\n"; } - - std::string const sourceDirectory = !args.Source.empty() - ? args.Source - : mf.GetDefinition("CTEST_SOURCE_DIRECTORY"); - if (sourceDirectory.empty() || - !cmSystemTools::FileExists(sourceDirectory + "/CMakeLists.txt")) { - status.SetError("called with invalid source directory. " - "CTEST_SOURCE_DIRECTORY must be set to a directory " - "that contains CMakeLists.txt."); - return false; + if (cmNonempty(buildName)) { + initialCache << "set(BUILDNAME " + << cmOutputConverter::EscapeForCMake(*buildName) + << " CACHE STRING \"\")\n"; } + initialCache.Close(); - bool const multiConfig = [&]() -> bool { - cmake* cm = mf.GetCMakeInstance(); - auto gg = cm->CreateGlobalGenerator(cmakeGenerator); - return gg && gg->IsMultiConfig(); - }(); - - bool const buildTypeInOptions = - args.Options.find("CMAKE_BUILD_TYPE=") != std::string::npos || - args.Options.find("CMAKE_BUILD_TYPE:STRING=") != std::string::npos; - - configureCommand = cmStrCat('"', cmSystemTools::GetCMakeCommand(), '"'); - - auto const options = cmList(args.Options); - for (std::string const& option : options) { - configureCommand += " \""; - configureCommand += option; - configureCommand += "\""; - } - - cmValue cmakeBuildType = mf.GetDefinition("CTEST_CONFIGURATION_TYPE"); - if (!multiConfig && !buildTypeInOptions && cmNonempty(cmakeBuildType)) { - configureCommand += " \"-DCMAKE_BUILD_TYPE:STRING="; - configureCommand += cmakeBuildType; - configureCommand += "\""; - } - - if (mf.IsOn("CTEST_USE_LAUNCHERS")) { - configureCommand += " \"-DCTEST_USE_LAUNCHERS:BOOL=TRUE\""; - } - - configureCommand += " \"-G"; - configureCommand += cmakeGenerator; + configureCommand += " \"-C"; + configureCommand += initialCacheFile; configureCommand += "\""; + } - cmValue cmakeGeneratorPlatform = - mf.GetDefinition("CTEST_CMAKE_GENERATOR_PLATFORM"); - if (cmNonempty(cmakeGeneratorPlatform)) { - configureCommand += " \"-A"; - configureCommand += *cmakeGeneratorPlatform; - configureCommand += "\""; - } - - cmValue cmakeGeneratorToolset = - mf.GetDefinition("CTEST_CMAKE_GENERATOR_TOOLSET"); - if (cmNonempty(cmakeGeneratorToolset)) { - configureCommand += " \"-T"; - configureCommand += *cmakeGeneratorToolset; - configureCommand += "\""; - } - - configureCommand += " \"-S"; - configureCommand += cmSystemTools::CollapseFullPath(sourceDirectory); + cmValue cmakeGeneratorPlatform = + mf.GetDefinition("CTEST_CMAKE_GENERATOR_PLATFORM"); + if (cmNonempty(cmakeGeneratorPlatform)) { + configureCommand += " \"-A"; + configureCommand += *cmakeGeneratorPlatform; configureCommand += "\""; + } - configureCommand += " \"-B"; - configureCommand += cmSystemTools::CollapseFullPath(buildDirectory); + cmValue cmakeGeneratorToolset = + mf.GetDefinition("CTEST_CMAKE_GENERATOR_TOOLSET"); + if (cmNonempty(cmakeGeneratorToolset)) { + configureCommand += " \"-T"; + configureCommand += *cmakeGeneratorToolset; configureCommand += "\""; } + // Append OPTIONS to the configure command. + bool const multiConfig = [&]() -> bool { + cmake* cm = mf.GetCMakeInstance(); + auto gg = cm->CreateGlobalGenerator(cmakeGenerator); + return gg && gg->IsMultiConfig(); + }(); + + bool const buildTypeInOptions = + options.find("CMAKE_BUILD_TYPE=") != std::string::npos || + options.find("CMAKE_BUILD_TYPE:STRING=") != std::string::npos; + + auto const optionsList = cmList(options); + for (std::string const& option : optionsList) { + configureCommand += " \""; + configureCommand += option; + configureCommand += "\""; + } + + cmValue cmakeBuildType = mf.GetDefinition("CTEST_CONFIGURATION_TYPE"); + if (!multiConfig && !buildTypeInOptions && cmNonempty(cmakeBuildType)) { + configureCommand += " \"-DCMAKE_BUILD_TYPE:STRING="; + configureCommand += cmakeBuildType; + configureCommand += "\""; + } + + return true; +} + +} // namespace + +bool cmCTestConfigureCommand::ExecuteConfigure(ConfigureArguments const& args, + cmExecutionStatus& status) const +{ + cmMakefile& mf = status.GetMakefile(); + + std::string buildDirectory = !args.Build.empty() + ? args.Build + : mf.GetDefinition("CTEST_BINARY_DIRECTORY"); + + std::string const sourceDirectory = !args.Source.empty() + ? args.Source + : mf.GetDefinition("CTEST_SOURCE_DIRECTORY"); + if (sourceDirectory.empty() || + !cmSystemTools::FileExists(sourceDirectory + "/CMakeLists.txt")) { + status.SetError("called with invalid source directory. " + "CTEST_SOURCE_DIRECTORY must be set to a directory " + "that contains CMakeLists.txt."); + return false; + } + + // Preset name is set according to the following priority order: + // 1) The PRESET option to ctest_configure() + // 2) CTEST_CONFIGURE_PRESET script variable + // 3) CTEST_PRESET script variable (error if no such configure preset exists) + std::string const presetName = !args.Preset.empty() ? args.Preset + : cmNonempty(mf.GetDefinition("CTEST_CONFIGURE_PRESET")) + ? *mf.GetDefinition("CTEST_CONFIGURE_PRESET") + : mf.GetSafeDefinition("CTEST_PRESET"); + + // Presets file is set according to the following priority order: + // 1) The PRESETS_FILE option to ctest_configure() + // 2) CTEST_PRESETS_FILE script variable + std::string const rawPresetsFile = !args.PresetsFile.empty() + ? args.PresetsFile + : mf.GetSafeDefinition("CTEST_PRESETS_FILE"); + + std::string const presetsFile = rawPresetsFile.empty() + ? "" + : cmSystemTools::CollapseFullPath(rawPresetsFile, sourceDirectory); + + // Skip checking CTEST_CONFIGURE_COMMAND when a preset is specified. + // We do this to avoid using a stale ConfigureCommand from a previous cmake + // run that would cause us to silently ignore the requested preset. + std::string configureCommand; + if (presetName.empty()) { + configureCommand = mf.GetDefinition("CTEST_CONFIGURE_COMMAND"); + } else if (cmNonempty(mf.GetDefinition("CTEST_CONFIGURE_COMMAND"))) { + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Ignoring CTEST_CONFIGURE_COMMAND because preset \"" + << presetName << "\" is in use.\n", + args.Quiet); + } + if (configureCommand.empty() && + !ConstructConfigureCommand(status, mf, sourceDirectory, buildDirectory, + args.Options, presetName, presetsFile, + configureCommand)) { + return false; + } + cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Configure project\n", args.Quiet); @@ -222,7 +358,9 @@ using Args = ConfigureArguments; static auto const parser = cmArgumentParser<Args>{ MakeHandlerParser<Args>() } // - .Bind("OPTIONS"_s, &ConfigureArguments::Options); + .Bind("OPTIONS"_s, &ConfigureArguments::Options) + .Bind("PRESET"_s, &ConfigureArguments::Preset) + .Bind("PRESETS_FILE"_s, &ConfigureArguments::PresetsFile); return this->Invoke(parser, args, status, [&](ConfigureArguments& a) { return this->ExecuteConfigure(a, status);
diff --git a/Source/CTest/cmCTestConfigureCommand.h b/Source/CTest/cmCTestConfigureCommand.h index 6caa56b..c652d12 100644 --- a/Source/CTest/cmCTestConfigureCommand.h +++ b/Source/CTest/cmCTestConfigureCommand.h
@@ -19,6 +19,8 @@ struct ConfigureArguments : HandlerArguments { std::string Options; + std::string Preset; + std::string PresetsFile; }; private:
diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index 65cbd53..9c80a85 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx
@@ -255,6 +255,11 @@ if (file_count < 0) { return error; } + file_count += this->HandleClangSourceCodeCoverage(&cont); + error = cont.Error; + if (file_count < 0) { + return error; + } file_count += this->HandleTracePyCoverage(&cont); error = cont.Error; if (file_count < 0) { @@ -393,6 +398,11 @@ this->CTest->GetShortPathToFile(fullFileName); cmCTestCoverageHandlerContainer::SingleFileCoverageVector const& fcov = file.second; + cmCTestCoverageHandlerContainer::SingleFileBranchCoverageVector fBranchcov; + if (cont.TotalBranchCoverage.find(file.first) != + cont.TotalBranchCoverage.end()) { + fBranchcov = cont.TotalBranchCoverage[file.first]; + } covLogXML.StartElement("File"); covLogXML.Attribute("Name", fileName); covLogXML.Attribute("FullPath", shortFileName); @@ -409,7 +419,8 @@ int tested = 0; int untested = 0; - + int branchestested = 0; + int branchesuntested = 0; cmCTestCoverageHandlerContainer::SingleFileCoverageVector::size_type cc; std::string line; cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, @@ -429,6 +440,14 @@ covLogXML.StartElement("Line"); covLogXML.Attribute("Number", cc); covLogXML.Attribute("Count", fcov[cc]); + if (!fBranchcov.empty()) { + covLogXML.Attribute("BranchesTested", fBranchcov[cc][0]); + covLogXML.Attribute("BranchesUntested", + fBranchcov[cc][1] - fBranchcov[cc][0]); + + branchestested += fBranchcov[cc][0]; + branchesuntested += fBranchcov[cc][1] - fBranchcov[cc][0]; + } covLogXML.Content(line); covLogXML.EndElement(); // Line if (fcov[cc] == 0) { @@ -462,6 +481,8 @@ covSumXML.Attribute("Covered", tested + untested > 0 ? "true" : "false"); covSumXML.Element("LOCTested", tested); covSumXML.Element("LOCUnTested", untested); + covSumXML.Element("BranchesTested", branchestested); + covSumXML.Element("BranchesUnTested", branchesuntested); covSumXML.Element("PercentCoverage", cper); covSumXML.Element("CoverageMetric", cmet); this->WriteXMLLabels(covSumXML, shortFileName); @@ -1245,6 +1266,175 @@ return file_count; } +int cmCTestCoverageHandler::HandleClangSourceCodeCoverage( + cmCTestCoverageHandlerContainer* cont) +{ + cmCTestOptionalLog( + this->CTest, HANDLER_VERBOSE_OUTPUT, + "Looking for Clang Source Code Coverage data: " << std::endl, this->Quiet); + // find all *.profraw files + cmsys::Glob gl; + gl.RecurseOn(); + gl.RecurseThroughSymlinksOff(); + std::string dir; + std::vector<std::string> profRawFiles; + dir = this->CTest->GetBinaryDir(); + std::string daGlob; + daGlob = cmStrCat(dir, "/*.profdata"); + cmCTestOptionalLog( + this->CTest, HANDLER_VERBOSE_OUTPUT, + " looking for .profdata files in: " << daGlob << std::endl, this->Quiet); + gl.FindFiles(daGlob); + // Keep a list of all LCOV files + cm::append(profRawFiles, gl.GetFiles()); + if (profRawFiles.empty()) { + cmCTestOptionalLog( + this->CTest, HANDLER_VERBOSE_OUTPUT, + " Cannot find any profdata coverage files." << std::endl, this->Quiet); + // No coverage files is a valid thing, so the exit code is 0 + return 0; + } + // Write filenames to input file + cmGeneratedFileStream manifestStr; + manifestStr.open(cmStrCat(dir, "/coverage.manifest")); + for (std::string const& f : profRawFiles) { + manifestStr << f << "\n"; + } + manifestStr.close(); + // execute merge command : + // (xcrun) llvm-profdata merge -sparse <>.profraw <.....> -o default.profdata + std::vector<std::string> covargs; +#ifdef __APPLE__ + covargs.push_back("xcrun"); +#endif + covargs.push_back( + this->CTest->GetCTestConfiguration("CTestTestCoverageMergeExecutable")); + covargs.push_back("merge"); + covargs.push_back("-sparse"); + covargs.push_back("--input-files=" + cmStrCat(dir, "/coverage.manifest")); + covargs.push_back("-o"); + covargs.push_back("default.profdata"); + covargs.push_back("--failure-mode=all"); + + std::string output; + std::string errors; + int retVal = 0; + this->CTest->RunCommand(covargs, &output, &errors, &retVal, dir.c_str(), + cmDuration::zero() /*this->TimeOut*/); + + if (!cmSystemTools::FileExists("default.profdata")) { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Something went wrong while merging .profraw data.\n"); + return 0; + } + // Loop through object files and export LCOV format for each one + std::vector<std::string> objectFiles; + daGlob = cmStrCat(dir, "/*.o"); + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " looking for .o files in: " << daGlob << std::endl, + this->Quiet); + gl.FindFiles(daGlob); + // Keep a list of all LCOV files + cm::append(objectFiles, gl.GetFiles()); + if (objectFiles.empty()) { + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " Cannot find any object files." << std::endl, + this->Quiet); + // No coverage files is a valid thing, so the exit code is 0 + return 0; + } + + // (xcrun) llvm-cov export ./hello -instr-profile=default.profdata + std::vector<std::string> covExportArgs; +#ifdef __APPLE__ + covExportArgs.push_back("xcrun"); +#endif + covExportArgs.push_back( + this->CTest->GetCTestConfiguration("CTestTestCoverageDataExecutable")); + covExportArgs.push_back("export"); + covExportArgs.push_back("-instr-profile=default.profdata"); + covExportArgs.push_back("-format=lcov"); + for (std::string const& f : objectFiles) { + + covExportArgs.push_back(f); + this->CTest->RunCommand(covExportArgs, &output, &errors, &retVal, + dir.c_str(), cmDuration::zero() /*this->TimeOut*/); + this->HandleClangSourceCodeCoverageFile(cont, output); + covExportArgs.pop_back(); + } + // Clean up created file + cmSystemTools::RemoveFile("default.profdata"); + cmSystemTools::RemoveFile("coverage.manifest"); + return static_cast<int>(cont->TotalCoverage.size()); +} + +void cmCTestCoverageHandler::HandleClangSourceCodeCoverageFile( + cmCTestCoverageHandlerContainer* cont, std::string output) +{ + int coveredLineIndex = 0; + int coveredLineHits = 0; + std::string line; + std::string lineType; + std::istringstream stream(output); + std::string coveredFile; + bool pauseCollecting = false; + while (cmSystemTools::GetLineFromStream(stream, line)) { + lineType = line.substr(0, line.find(':')); + // SF is the full path to the source file + if (lineType == "SF") { + coveredFile = + cmSystemTools::CollapseFullPath(line.substr(line.find(':') + 1)); + if (cont->TotalCoverage[coveredFile].empty()) { + pauseCollecting = false; + cmCTestCoverageHandlerContainer::SingleFileCoverageVector& vec = + cont->TotalCoverage[coveredFile]; + cmCTestCoverageHandlerContainer::SingleFileBranchCoverageVector& + branchVec = cont->TotalBranchCoverage[coveredFile]; + + cmsys::ifstream in(coveredFile); + if (!in) { + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Cannot find " << coveredFile << std::endl, + this->Quiet); + pauseCollecting = true; + } else { + while (cmSystemTools::GetLineFromStream(in, line)) { + vec.push_back(-1); + branchVec.push_back({ { 0, 0 } }); + } + } + } else { + // Dont parse repeat copies of data, skip all lines of coverage info + // until we reach the next file name + pauseCollecting = true; + } + + } + // DA represents the line and number of hits DA:<LineNo>,<NumHits> + else if (lineType == "DA" && !pauseCollecting) { + // Starts at 1, so subtract one from index to get real line data + coveredLineIndex = + std::atoi(line.substr(line.find(':') + 1, line.find(',')).c_str()) - 1; + coveredLineHits = std::atoi(line.substr(line.find(',') + 1).c_str()); + cont->TotalCoverage[coveredFile][coveredLineIndex] = coveredLineHits; + } + // BRDA represents the line and number of Branch hits + // BRDA:<LineNo>,<blockNo>,<BranchNo>,<NumHits> + else if (lineType == "BRDA" && !pauseCollecting) { + // First increment count of branches for this line + coveredLineIndex = + std::atoi(line.substr(line.find(':') + 1, line.find(',')).c_str()) - 1; + cont->TotalBranchCoverage[coveredFile][coveredLineIndex][1]++; + // If lines hit > 0, increment the first number to indicate the branch + // was hit + coveredLineHits = std::atoi(line.substr(line.rfind(',') + 1).c_str()); + if (coveredLineHits > 0) { + cont->TotalBranchCoverage[coveredFile][coveredLineIndex][0]++; + } + } + } +} + int cmCTestCoverageHandler::HandleLCovCoverage( cmCTestCoverageHandlerContainer* cont) {
diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h index b278161..c938734 100644 --- a/Source/CTest/cmCTestCoverageHandler.h +++ b/Source/CTest/cmCTestCoverageHandler.h
@@ -4,6 +4,7 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <array> #include <iosfwd> #include <map> #include <set> @@ -25,8 +26,12 @@ std::string SourceDir; std::string BinaryDir; using SingleFileCoverageVector = std::vector<int>; + using SingleFileBranchCoverageVector = std::vector<std::array<int, 2>>; using TotalCoverageMap = std::map<std::string, SingleFileCoverageVector>; + using TotalBranchCoverageMap = + std::map<std::string, SingleFileBranchCoverageVector>; TotalCoverageMap TotalCoverage; + TotalBranchCoverageMap TotalBranchCoverage; std::ostream* OFS; bool Quiet; }; @@ -75,6 +80,11 @@ //! Handle coverage using xdebug php coverage int HandlePHPCoverage(cmCTestCoverageHandlerContainer* cont); + //! Handle coverage for Clang with llvm function + int HandleClangSourceCodeCoverage(cmCTestCoverageHandlerContainer* cont); + + void HandleClangSourceCodeCoverageFile(cmCTestCoverageHandlerContainer* cont, + std::string output); //! Handle coverage for Python with coverage.py int HandleCoberturaCoverage(cmCTestCoverageHandlerContainer* cont);
diff --git a/Source/CTest/cmCTestDiscoverTests.cxx b/Source/CTest/cmCTestDiscoverTests.cxx new file mode 100644 index 0000000..cb1709d --- /dev/null +++ b/Source/CTest/cmCTestDiscoverTests.cxx
@@ -0,0 +1,177 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmCTestDiscoverTests.h" + +#include <algorithm> +#include <cstddef> +#include <cstdlib> +#include <iterator> +#include <string> +#include <utility> +#include <vector> + +#include <cmsys/RegularExpression.hxx> + +#include "cmArgumentParserTypes.h" +#include "cmCTestTestHandler.h" +#include "cmDuration.h" +#include "cmEnvironment.h" +#include "cmExecutionStatus.h" +#include "cmList.h" +#include "cmProcessOutput.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTestDiscovery.h" + +namespace { + +struct DiscoveryProperties +{ + DiscoveryProperties(std::vector<std::string> const& props); + + std::vector<std::string> Environment; + std::vector<std::string> EnvironmentModification; + cmDuration Timeout = cmDuration(5.0); + std::string WorkingDirectory; +}; + +DiscoveryProperties::DiscoveryProperties(std::vector<std::string> const& props) +{ + for (std::size_t i = 0; i < props.size(); i += 2) { + auto const& key = props[i]; + auto const& val = props[i + 1]; + if (key == "ENVIRONMENT") { + cmExpandList(val, this->Environment); + } else if (key == "ENVIRONMENT_MODIFICATION") { + cmExpandList(val, this->EnvironmentModification); + } else if (key == "TIMEOUT") { + this->Timeout = cmDuration(atof(val.c_str())); + } else if (key == "WORKING_DIRECTORY") { + this->WorkingDirectory = val; + } + } +} + +std::string AddAnchors(std::string str) +{ + if (str.empty() || str.front() != '^') { + str.insert(str.begin(), '^'); + } + if (str.size() < 2 || str.back() != '$') { + str.push_back('$'); + } + return str; +} + +class RegexReplacer +{ +public: + RegexReplacer(cmsys::RegularExpression& regex) + : RegEx(regex) + { + } + + std::string operator()(std::string str) + { + for (int i = 0; i <= this->RegEx.num_groups(); ++i) { + cmsys::SystemTools::ReplaceString(str, cmStrCat('\\', i), + this->RegEx.match(i)); + } + return str; + } + +private: + cmsys::RegularExpression& RegEx; +}; + +} // namespace + +bool cmCTestDiscoverTests(cmTestDiscoveryArgs const& args, + cmCTestTestHandler* handler, + std::vector<std::string>& testList, + cmExecutionStatus& status) +{ + if (args.DiscoveryProperties.size() % 2 != 0) { + status.SetError(" DISCOVERY_PROPERTIES must be key-value pairs."); + return false; + } + + if (args.TestProperties.size() % 2 != 0) { + status.SetError(" TEST_PROPERTIES must be key-value pairs."); + return false; + } + + cmsys::RegularExpression re; + if (!re.compile(AddAnchors(args.DiscoveryMatch))) { + std::string e = "DISCOVERY_MATCH failed to compile regex \"" + + args.DiscoveryMatch + "\"."; + status.SetError(e); + return false; + } + + auto const props = DiscoveryProperties{ args.DiscoveryProperties }; + + auto env = cmEnvironment{ cmSystemTools::GetEnvironmentVariables() }; + env.Update(props.Environment); + if (!props.EnvironmentModification.empty()) { + auto diff = cmEnvironmentModification{}; + if (!diff.Add(props.EnvironmentModification)) { + return false; + } + diff.ApplyTo(env); + } + + auto runCommand = std::vector<std::string>(args.Command); + runCommand.reserve(runCommand.size() + args.DiscoveryArgs.size()); + std::copy(args.DiscoveryArgs.begin(), args.DiscoveryArgs.end(), + std::back_inserter(runCommand)); + + std::string stdOut; + std::string stdErr; + bool const res = cmSystemTools::RunSingleCommand( + runCommand, &stdOut, &stdErr, nullptr, props.WorkingDirectory.c_str(), + cmSystemTools::OUTPUT_NONE, props.Timeout, cmProcessOutput::Auto, + env.GetVariables()); + if (!res) { + status.SetError(cmStrCat(" failed to run command: ", + cmSystemTools::PrintSingleCommand(runCommand), + "\n", stdErr)); + return false; + } + + auto replace = RegexReplacer{ re }; + + std::vector<std::string> lines; + cmSystemTools::Split(stdOut, lines); + for (auto const& line : lines) { + if (!re.find(line)) { + continue; + } + + auto testName = replace(args.TestName); + auto testArgs = std::vector<std::string>{ testName }; + testArgs.reserve(1 + args.Command.size() + args.TestArgs.size()); + std::copy(args.Command.begin(), args.Command.end(), + std::back_inserter(testArgs)); + std::transform(args.TestArgs.begin(), args.TestArgs.end(), + std::back_inserter(testArgs), replace); + + if (!handler->AddTest(testArgs)) { + return false; + } + + auto testProperties = std::vector<std::string>{ testName, "PROPERTIES" }; + testProperties.reserve(2 + args.TestProperties.size()); + for (std::size_t i = 0; i < args.TestProperties.size(); i += 2) { + testProperties.push_back(args.TestProperties[i]); + testProperties.push_back(replace(args.TestProperties[i + 1])); + } + if (!handler->SetTestsProperties(testProperties)) { + return false; + } + + testList.push_back(std::move(testName)); + } + + return true; +}
diff --git a/Source/CTest/cmCTestDiscoverTests.h b/Source/CTest/cmCTestDiscoverTests.h new file mode 100644 index 0000000..b393185 --- /dev/null +++ b/Source/CTest/cmCTestDiscoverTests.h
@@ -0,0 +1,17 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +struct cmTestDiscoveryArgs; +class cmCTestTestHandler; +class cmExecutionStatus; + +bool cmCTestDiscoverTests(cmTestDiscoveryArgs const& args, + cmCTestTestHandler* handler, + std::vector<std::string>& testList, + cmExecutionStatus& status);
diff --git a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx index 3fba37e..edcb1ef 100644 --- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx +++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
@@ -4,6 +4,7 @@ #include "cmsys/Directory.hxx" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -21,7 +22,7 @@ // Make sure that CMakeCache.txt is deleted last. for (unsigned long i = 0; i < directory.GetNumberOfFiles(); ++i) { - std::string path = directory.GetFile(i); + std::string const& path = directory.GetFileName(i); if (path == "." || path == ".." || path == "CMakeCache.txt") { continue; @@ -97,7 +98,7 @@ if (!EmptyBinaryDirectory(args[0], err)) { cmMakefile& mf = status.GetMakefile(); mf.GetMessenger()->DisplayMessage( - MessageType::FATAL_ERROR, + MessageType::FATAL_ERROR, cmDiagnostics::CMD_NONE, cmStrCat("Did not remove the binary directory:\n ", args[0], "\nbecause:\n ", err), mf.GetBacktrace());
diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index b8e6a03..a85386e 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx
@@ -8,9 +8,11 @@ #include <cm/string_view> +#include "cmCMakePresetsGraph.h" #include "cmCTest.h" #include "cmCTestGenericHandler.h" #include "cmExecutionStatus.h" +#include "cmJSONState.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -216,3 +218,55 @@ cmExecutionStatus&) const { } + +namespace { +enum class PresetType +{ + Build, + Test, +}; + +cmCTestHandlerCommand::PresetCheckResult PresetExistsInGraph( + cmCMakePresetsGraph& graph, std::string const& name, + std::string const& sourceDir, std::string const& presetsFile, + PresetType presetType, std::string& errorMessage) +{ + using Result = cmCTestHandlerCommand::PresetCheckResult; + if (!graph.ReadProjectPresets(sourceDir, presetsFile)) { + errorMessage = cmStrCat("Could not read presets from \"", sourceDir, + "\":\n ", graph.parseState.GetErrorMessage()); + return Result::ReadError; + } + using Status = cmCMakePresetsGraph::PresetResolveStatus; + if (presetType == PresetType::Build) { + auto result = graph.ResolvePreset(name, graph.BuildPresets); + return result.StatusCode == Status::Success ? Result::Found + : Result::NotFound; + } + auto result = graph.ResolvePreset(name, graph.TestPresets); + return result.StatusCode == Status::Success ? Result::Found + : Result::NotFound; +} +} + +cmCTestHandlerCommand::PresetCheckResult +cmCTestHandlerCommand::BuildPresetExists(std::string const& name, + std::string const& sourceDir, + std::string const& presetsFile, + std::string& errorMessage) +{ + cmCMakePresetsGraph graph; + return PresetExistsInGraph(graph, name, sourceDir, presetsFile, + PresetType::Build, errorMessage); +} + +cmCTestHandlerCommand::PresetCheckResult +cmCTestHandlerCommand::TestPresetExists(std::string const& name, + std::string const& sourceDir, + std::string const& presetsFile, + std::string& errorMessage) +{ + cmCMakePresetsGraph graph; + return PresetExistsInGraph(graph, name, sourceDir, presetsFile, + PresetType::Test, errorMessage); +}
diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index ef7bc5d..4b5111b 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h
@@ -24,6 +24,13 @@ public: using cmCTestCommand::cmCTestCommand; + enum class PresetCheckResult + { + Found, + NotFound, + ReadError, + }; + protected: struct BasicArguments : ArgumentParser::ParseResult { @@ -78,6 +85,15 @@ bool ExecuteHandlerCommand(HandlerArguments& args, cmExecutionStatus& status) const; + static PresetCheckResult BuildPresetExists(std::string const& name, + std::string const& sourceDir, + std::string const& presetsFile, + std::string& errorMessage); + static PresetCheckResult TestPresetExists(std::string const& name, + std::string const& sourceDir, + std::string const& presetsFile, + std::string& errorMessage); + private: bool InvokeImpl(BasicArguments& args, std::vector<std::string> const& unparsed,
diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx index 5d81676..39206a0 100644 --- a/Source/CTest/cmCTestLaunch.cxx +++ b/Source/CTest/cmCTestLaunch.cxx
@@ -21,6 +21,7 @@ #include "cmCTestLaunchReporter.h" #include "cmGlobalGenerator.h" #include "cmInstrumentation.h" +#include "cmInstrumentationQuery.h" #include "cmMakefile.h" #include "cmProcessOutput.h" #include "cmState.h" @@ -206,6 +207,9 @@ return; } + this->CapturedStdOut.clear(); + this->CapturedStdErr.clear(); + // Prepare to run the real command. cmUVProcessChainBuilder builder; builder.AddCommand(this->RealArgV); @@ -213,18 +217,19 @@ // We always share the input pipe. builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin); + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); cmsys::ofstream fout; cmsys::ofstream ferr; - if (this->Reporter.Passthru) { - // In passthru mode we just share the output pipes. - builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout) - .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr); - } else { + cmsys::ofstream* foutPtr = nullptr; + cmsys::ofstream* ferrPtr = nullptr; + + if (!this->Reporter.Passthru) { // 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); + foutPtr = &fout; + ferrPtr = &ferr; } #ifdef _WIN32 @@ -243,36 +248,43 @@ cmProcessOutput processOutput; std::unique_ptr<cmUVStreamReadHandle> outputHandle; std::unique_ptr<cmUVStreamReadHandle> errorHandle; - if (!this->Reporter.Passthru) { - auto beginRead = - [&processOutput](uv_stream_t* stream, std::ostream& out, - cmsys::ofstream& file, bool& haveData, bool& finished, - int id) -> std::unique_ptr<cmUVStreamReadHandle> { - finished = false; - return cmUVStreamRead( - stream, - [&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()); + auto beginRead = + [&processOutput]( + uv_stream_t* stream, std::ostream& out, cmsys::ofstream* file, + bool& haveData, bool& finished, int id, + std::string& capture) -> std::unique_ptr<cmUVStreamReadHandle> { + finished = false; + return cmUVStreamRead( + stream, + [&processOutput, &out, file, id, &haveData, + &capture](std::vector<char> data) { + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata, id); + if (file && file->is_open()) { + file->write(strdata.c_str(), strdata.size()); + } + out.write(strdata.c_str(), strdata.size()); + capture.append(strdata); + haveData = true; + }, + [&processOutput, &out, file, &finished, id, &capture]() { + std::string strdata; + processOutput.DecodeText(std::string(), strdata, id); + if (!strdata.empty()) { + if (file && file->is_open()) { + file->write(strdata.c_str(), strdata.size()); } - finished = true; - }); - }; - outputHandle = beginRead(chain.OutputStream(), std::cout, fout, - this->HaveOut, outFinished, 1); - errorHandle = beginRead(chain.ErrorStream(), std::cerr, ferr, - this->HaveErr, errFinished, 2); - } + out.write(strdata.c_str(), strdata.size()); + capture.append(strdata); + } + finished = true; + }); + }; + outputHandle = + beginRead(chain.OutputStream(), std::cout, foutPtr, this->HaveOut, + outFinished, 1, this->CapturedStdOut); + errorHandle = beginRead(chain.ErrorStream(), std::cerr, ferrPtr, + this->HaveErr, errFinished, 2, this->CapturedStdErr); // Wait for the real command to finish. while (!(chain.Finished() && outFinished && errFinished)) { @@ -291,6 +303,8 @@ int cmCTestLaunch::Run() { auto instrumentation = cmInstrumentation(this->Reporter.OptionBuildDir); + bool const captureOutput = + instrumentation.HasOption(cmInstrumentationQuery::Option::CaptureOutput); std::map<std::string, std::string> options; if (this->Reporter.OptionTargetName != "TARGET_NAME") { options["target"] = this->Reporter.OptionTargetName; @@ -305,9 +319,15 @@ arrayOptions["targetLabels"] = this->Reporter.OptionTargetLabels; instrumentation.InstrumentCommand( this->Reporter.OptionCommandType, this->RealArgV, - [this]() -> int { + [this, captureOutput]() -> cmInstrumentation::CommandResult { this->RunChild(); - return this->Reporter.ExitCode; + cmInstrumentation::CommandResult result; + result.ExitCode = this->Reporter.ExitCode; + if (captureOutput) { + result.StdOut = this->CapturedStdOut; + result.StdErr = this->CapturedStdErr; + } + return result; }, options, arrayOptions);
diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h index 0aeb211..cf2d1ed6 100644 --- a/Source/CTest/cmCTestLaunch.h +++ b/Source/CTest/cmCTestLaunch.h
@@ -59,6 +59,8 @@ // Whether or not any data have been written to stdout or stderr. bool HaveOut; bool HaveErr; + std::string CapturedStdOut; + std::string CapturedStdErr; // Load custom rules to match warnings and their exceptions. bool ScrapeRulesLoaded;
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 207dd33..ebbd452 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx
@@ -14,13 +14,18 @@ #include <cm/memory> #include <cm/optional> +#include <cm/string_view> +#include <cmext/string_view> +#include "cmsys/FStream.hxx" +#include "cmsys/Glob.hxx" #include "cmsys/RegularExpression.hxx" #include "cmCTest.h" #include "cmCTestMemCheckHandler.h" #include "cmCTestMultiProcessHandler.h" #include "cmDuration.h" +#include "cmEnvironment.h" #include "cmInstrumentation.h" #include "cmProcess.h" #include "cmStringAlgorithms.h" @@ -707,6 +712,12 @@ testCommand += "\""; this->Arguments.push_back(*j); } + // Append passthrough arguments from ctest command line (after --) + for (std::string const& arg : + this->TestHandler->TestOptions.TestPassthroughArguments) { + testCommand = cmStrCat(std::move(testCommand), " \"", arg, '"'); + this->Arguments.push_back(arg); + } this->TestResult.FullCommandLine = testCommand; // Print the test command in verbose mode @@ -853,62 +864,69 @@ this->TestHandler->GetQuiet()); } - cmSystemTools::SaveRestoreEnvironment sre; - std::ostringstream envMeasurement; + // Record the original environment before modifying it + auto const originalEnvironment = + cmEnvironment{ cmSystemTools::GetEnvironmentVariables() }; - // We split processing ENVIRONMENT and ENVIRONMENT_MODIFICATION into two - // phases to ensure that MYVAR=reset: in the latter phase resets to the - // former phase's settings, rather than to the original environment. - if (!this->TestProperties->Environment.empty()) { - cmSystemTools::EnvDiff diff; - diff.AppendEnv(this->TestProperties->Environment); - diff.ApplyToCurrentEnv(&envMeasurement); - } - + auto env = originalEnvironment; + env.Update(this->TestProperties->Environment); if (!this->TestProperties->EnvironmentModification.empty()) { - cmSystemTools::EnvDiff diff; - bool env_ok = true; - - for (auto const& envmod : this->TestProperties->EnvironmentModification) { - env_ok &= diff.ParseOperation(envmod); - } - - if (!env_ok) { + auto diff = cmEnvironmentModification{}; + if (!diff.Add(this->TestProperties->EnvironmentModification)) { return false; } - - diff.ApplyToCurrentEnv(&envMeasurement); + diff.ApplyTo(env); } + // Inject LLVM Profile name : + // https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program + if (this->CTest->GetCTestConfiguration("CTestTestCoverageTool") == + "LLVM-COV"_s || + this->TestHandler->TestOptions.CoverageTool == "LLVM-COV"_s) { + + this->UseLLVMCov = true; + // Isolate the test from any ambient LLVM_PROFILE_FILE + env.UnPutEnv("LLVM_PROFILE_FILE"); + // Value is <Test Dir>/<testName>_<processID>.profraw + + std::string profRawPath = this->GenerateLLVMPath("_%p.profraw"); + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + this->Index + << ": Using environment variable LLVM_PROFILE_FILE="_s + << profRawPath << " \n", + this->TestHandler->GetQuiet()); + env.PutEnv(cmStrCat("LLVM_PROFILE_FILE="_s, profRawPath)); + // ProcessID -> * to allow for glob to find all + // files generated by the test + std::string profRawGlobPath = this->GenerateLLVMPath("_*.profraw"); + + cmsys::Glob glob; + glob.FindFiles(profRawGlobPath); + for (std::string const& file : glob.GetFiles()) { + cmSystemTools::RemoveFile(file); + } + // Remove merged coverage data + + std::string profDataPath = this->GenerateLLVMPath(".profdata"); + cmSystemTools::RemoveFile(profDataPath); + }; + if (this->UseAllocatedResources) { - std::vector<std::string> envLog; - this->SetupResourcesEnvironment(&envLog); - for (auto const& var : envLog) { - envMeasurement << var << std::endl; - } + this->SetupResourcesEnvironment(env); } else { - cmSystemTools::UnsetEnv("CTEST_RESOURCE_GROUP_COUNT"); - // Signify that this variable is being actively unset - envMeasurement << "#CTEST_RESOURCE_GROUP_COUNT=" << std::endl; + env.UnPutEnv("CTEST_RESOURCE_GROUP_COUNT"); } - this->TestResult.Environment = envMeasurement.str(); - // Remove last newline - this->TestResult.Environment.erase(this->TestResult.Environment.length() - - 1); - + this->TestProcess->SetEnvironment(env.GetVariables()); + this->TestResult.Environment = env.RecordDifference(originalEnvironment); return this->TestProcess->StartProcess(*this->MultiTestHandler.Loop, &this->TestProperties->Affinity); } -void cmCTestRunTest::SetupResourcesEnvironment(std::vector<std::string>* log) +void cmCTestRunTest::SetupResourcesEnvironment(cmEnvironment& env) { - std::string processCount = - cmStrCat("CTEST_RESOURCE_GROUP_COUNT=", this->AllocatedResources.size()); - cmSystemTools::PutEnv(processCount); - if (log) { - log->emplace_back(std::move(processCount)); - } + env.PutEnv( + cmStrCat("CTEST_RESOURCE_GROUP_COUNT=", this->AllocatedResources.size())); std::size_t i = 0; for (auto const& process : this->AllocatedResources) { @@ -933,15 +951,9 @@ firstName = false; var += cmStrCat("id:", it2.Id, ",slots:", it2.Slots); } - cmSystemTools::PutEnv(var); - if (log) { - log->push_back(var); - } + env.PutEnv(var); } - cmSystemTools::PutEnv(resourceList); - if (log) { - log->push_back(resourceList); - } + env.PutEnv(resourceList); ++i; } } @@ -1023,14 +1035,94 @@ "Testing " << this->TestProperties->Name << " ... "); } +std::string cmCTestRunTest::GenerateLLVMPath(std::string fileString) +{ + std::string dir = this->TestProperties->CTestDirectory; + std::string profRawRoot = cmStrCat(dir, "/", this->TestProperties->Name); + return cmStrCat(profRawRoot, fileString); +} + +void cmCTestRunTest::CollectLLVMCoverage() +{ + // find all *.profraw files + cmsys::Glob gl; + std::vector<std::string> profRawFiles; + std::string profRawPath = this->GenerateLLVMPath("_*.profraw"); + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " looking for .profraw files in: " << profRawPath + << std::endl, + this->TestHandler->Quiet); + gl.FindFiles(profRawPath); + // Keep a list of all profraw files + profRawFiles = gl.GetFiles(); + if (profRawFiles.empty()) { + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " Cannot find any profraw coverage files." << std::endl, + this->TestHandler->Quiet); + // No coverage files is a valid thing, so the exit code is 0 + return; + } + + // Write filenames to input file + std::string profManifestPath = this->GenerateLLVMPath(".manifest"); + cmsys::ofstream manifestStream; + manifestStream.open(profManifestPath); + for (std::string const& f : profRawFiles) { + manifestStream << f << "\n"; + } + manifestStream.close(); + // execute merge command : + // (xcrun) llvm-profdata merge -sparse --input-files=<test_name>.manifest -o + // <test_name>.profdata + std::vector<std::string> covargs; + std::string mergeExecutable = + this->CTest->GetCTestConfiguration("CTestTestCoverageMergeExecutable"); + if (mergeExecutable.empty()) { + mergeExecutable = "llvm-profdata"; + } + std::string profDataPath = this->GenerateLLVMPath(".profdata"); +#ifdef __APPLE__ + covargs.push_back("xcrun"); +#endif + covargs.push_back(mergeExecutable); + covargs.push_back("merge"); + covargs.push_back("-sparse"); + covargs.push_back(cmStrCat("--input-files=", profManifestPath)); + covargs.push_back("-o"); + covargs.push_back(profDataPath); + covargs.push_back("--failure-mode=all"); + + std::string output; + std::string errors; + int retVal = 0; + this->CTest->RunCommand(covargs, &output, &errors, &retVal, + this->TestProperties->CTestDirectory.c_str(), + cmDuration::zero() /*this->TimeOut*/); + + if (!cmSystemTools::FileExists(profDataPath)) { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Something went wrong while merging .profraw data.\n"); + return; + } + for (std::string const& f : profRawFiles) { + cmSystemTools::RemoveFile(f); + } + cmSystemTools::RemoveFile(profManifestPath); +} + void cmCTestRunTest::FinalizeTest(bool started) { + // Collect known LLVM coverage files into binary form + if (this->UseLLVMCov) { + this->CollectLLVMCoverage(); + } + if (started && this->CTest->GetInstrumentation().HasQuery()) { std::string data_file = this->CTest->GetInstrumentation().InstrumentTest( this->TestProperties->Name, this->ActualCommand, this->Arguments, this->TestProcess->GetExitValue(), this->TestProcess->GetStartTime(), this->TestProcess->GetSystemStartTime(), - this->GetCTest()->GetConfigType()); + this->GetCTest()->GetConfigType(), this->ProcessOutput); this->TestResult.InstrumentationFile = data_file; } this->MultiTestHandler.FinishTestProcess(this->TestProcess->GetRunner(),
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h index 3905dab..3e82e58 100644 --- a/Source/CTest/cmCTestRunTest.h +++ b/Source/CTest/cmCTestRunTest.h
@@ -16,6 +16,8 @@ #include "cmCTestTestHandler.h" #include "cmProcess.h" +class cmEnvironment; + /** \class cmRunTest * \brief represents a single test to be run * @@ -112,8 +114,9 @@ void WriteLogOutputTop(size_t completed, size_t total); // Run post processing of the process output for MemCheck void MemCheckPostProcess(); - - void SetupResourcesEnvironment(std::vector<std::string>* log = nullptr); + std::string GenerateLLVMPath(std::string fileString); + void CollectLLVMCoverage(); + void SetupResourcesEnvironment(cmEnvironment& env); // Returns "completed/total Test #Index: " std::string GetTestPrefix(size_t completed, size_t total) const; @@ -139,6 +142,7 @@ int NumberOfRunsLeft = 1; // default to 1 run of the test int NumberOfRunsTotal = 1; // default to 1 run of the test bool RunAgain = false; // default to not having to run again + bool UseLLVMCov = false; size_t TotalNumberOfTests; };
diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx index 4b34cf6..24f5d87 100644 --- a/Source/CTest/cmCTestSubmitCommand.cxx +++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -204,6 +204,21 @@ { cmMakefile& mf = status.GetMakefile(); auto& args = static_cast<SubmitArguments&>(arguments); + + // Honor CTEST_SUBMIT_PARTS when PARTS is not given explicitly. + // Skip for FILES-only calls (where PARTS is intentionally absent so that + // only the named files are submitted) and for CDASH_UPLOAD calls (where + // the PARTS concept does not apply at all). + if (!args.Parts && !args.Files && !args.CDashUpload) { + cmValue submitPartsVar = mf.GetDefinition("CTEST_SUBMIT_PARTS"); + if (submitPartsVar && !submitPartsVar->empty()) { + cmList submitPartsList{ *submitPartsVar }; + if (!submitPartsList.empty()) { + args.Parts.emplace(submitPartsList.begin(), submitPartsList.end()); + } + } + } + if (args.Parts) { cm::erase_if(*(args.Parts), [this, &mf](std::string const& arg) -> bool { cmCTest::Part p = this->CTest->GetPartFromName(arg);
diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx index 2d923a4..3e8e319 100644 --- a/Source/CTest/cmCTestTestCommand.cxx +++ b/Source/CTest/cmCTestTestCommand.cxx
@@ -6,25 +6,101 @@ #include <cstdlib> #include <ratio> #include <sstream> +#include <string> #include <utility> #include <vector> #include <cm/memory> +#include <cm/optional> +#include "cmArgumentParserTypes.h" +#include "cmCMakePresetsGraph.h" #include "cmCTest.h" #include "cmCTestGenericHandler.h" #include "cmCTestTestHandler.h" #include "cmDuration.h" #include "cmExecutionStatus.h" +#include "cmJSONState.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" +#include "cmSystemTools.h" #include "cmValue.h" +using TestPreset = cmCMakePresetsGraph::TestPreset; + std::unique_ptr<cmCTestGenericHandler> cmCTestTestCommand::InitializeHandler( HandlerArguments& arguments, cmExecutionStatus& status) const { cmMakefile& mf = status.GetMakefile(); auto& args = static_cast<TestArguments&>(arguments); + + std::string const sourceDirectory = + mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); + + // Presets file is set according to the following priority order: + // 1) The PRESETS_FILE option to ctest_test() + // 2) CTEST_PRESETS_FILE script variable + std::string const rawPresetsFile = !args.PresetsFile.empty() + ? args.PresetsFile + : mf.GetSafeDefinition("CTEST_PRESETS_FILE"); + + std::string const presetsFile = rawPresetsFile.empty() + ? "" + : cmSystemTools::CollapseFullPath(rawPresetsFile, sourceDirectory); + + // Preset name is set according to the following priority order: + // 1) The PRESET option to ctest_test() + // 2) CTEST_TEST_PRESET script variable + // 3) CTEST_PRESET script variable (a warning is emitted if no test preset + // exists with this name) + std::string effectivePreset = !args.Preset.empty() ? args.Preset + : cmNonempty(mf.GetDefinition("CTEST_TEST_PRESET")) + ? *mf.GetDefinition("CTEST_TEST_PRESET") + : ""; + if (effectivePreset.empty()) { + cmValue v = mf.GetDefinition("CTEST_PRESET"); + if (cmNonempty(v)) { + std::string presetError; + auto presetCheck = + TestPresetExists(*v, sourceDirectory, presetsFile, presetError); + if (presetCheck == PresetCheckResult::ReadError) { + status.SetError(cmStrCat('\n', presetError)); + return nullptr; + } + if (presetCheck == PresetCheckResult::Found) { + effectivePreset = *v; + } else { + cmCTestLog(this->CTest, WARNING, + "No test preset named \"" + << *v << "\" found, ignoring CTEST_PRESET." << std::endl); + } + } + } + + std::unique_ptr<cmCMakePresetsGraph> presetsGraph; + TestPreset const* expandedPreset = nullptr; + if (!effectivePreset.empty()) { + presetsGraph = cm::make_unique<cmCMakePresetsGraph>(); + if (!presetsGraph->ReadProjectPresets(sourceDirectory, presetsFile)) { + status.SetError(cmStrCat("\n Could not read presets from \"", + sourceDirectory, "\":\n ", + presetsGraph->parseState.GetErrorMessage())); + return nullptr; + } + + auto resolveResult = + presetsGraph->ResolvePreset(effectivePreset, presetsGraph->TestPresets); + auto resolveError = cmCMakePresetsGraph::FormatPresetError<TestPreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + sourceDirectory); + if (resolveError) { + status.SetError(*resolveError); + return nullptr; + } + + expandedPreset = resolveResult.Preset; + } + cmValue ctestTimeout = mf.GetDefinition("CTEST_TEST_TIMEOUT"); cmDuration timeout; @@ -39,12 +115,56 @@ } this->CTest->SetTimeOut(timeout); - cmValue resourceSpecFile = mf.GetDefinition("CTEST_RESOURCE_SPEC_FILE"); - if (args.ResourceSpecFile.empty() && resourceSpecFile) { - args.ResourceSpecFile = *resourceSpecFile; + auto handler = this->InitializeActualHandler(args, status); + + // Load settings from the preset if one was specified. + if (expandedPreset) { + cmCTestApplyTestPresetToOptions(handler->TestOptions, *expandedPreset); + + if (!args.ParallelLevel && expandedPreset->Execution) { + if (auto const& jobs = expandedPreset->Execution->Jobs) { + ArgumentParser::Maybe<std::string> level; + if (jobs->has_value()) { + level = std::to_string(**jobs); + } + handler->ParallelLevel = level; + } + } + + if (args.Repeat.empty() && expandedPreset->Execution && + expandedPreset->Execution->Repeat) { + auto const& rep = *expandedPreset->Execution->Repeat; + using RepeatMode = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum; + std::string modeStr; + switch (rep.Mode) { + case RepeatMode::UntilFail: + modeStr = "UNTIL_FAIL"; + break; + case RepeatMode::UntilPass: + modeStr = "UNTIL_PASS"; + break; + case RepeatMode::AfterTimeout: + modeStr = "AFTER_TIMEOUT"; + break; + } + handler->Repeat = cmStrCat(modeStr, ':', rep.Count); + } + + if (args.TestLoad.empty() && expandedPreset->Execution && + expandedPreset->Execution->TestLoad) { + args.TestLoad = std::to_string(*expandedPreset->Execution->TestLoad); + } } - auto handler = this->InitializeActualHandler(args, status); + if (args.ResourceSpecFile.empty()) { + cmValue resourceSpecFile = mf.GetDefinition("CTEST_RESOURCE_SPEC_FILE"); + if (resourceSpecFile) { + args.ResourceSpecFile = *resourceSpecFile; + } + } + + // Apply explicitly specified ctest_test() options, + // overriding any conflicting preset values. if (!args.Start.empty() || !args.End.empty() || !args.Stride.empty()) { handler->TestOptions.TestsToRunInformation = cmStrCat(args.Start, ',', args.End, ',', args.Stride); @@ -56,10 +176,12 @@ handler->TestOptions.IncludeRegularExpression = args.Include; } if (!args.ExcludeLabel.empty()) { + handler->TestOptions.ExcludeLabelRegularExpression.clear(); handler->TestOptions.ExcludeLabelRegularExpression.push_back( args.ExcludeLabel); } if (!args.IncludeLabel.empty()) { + handler->TestOptions.LabelRegularExpression.clear(); handler->TestOptions.LabelRegularExpression.push_back(args.IncludeLabel); } @@ -96,6 +218,9 @@ if (!args.ResourceSpecFile.empty()) { handler->TestOptions.ResourceSpecFile = args.ResourceSpecFile; } + if (!args.CoverageTool.empty()) { + handler->TestOptions.CoverageTool = args.CoverageTool; + } if (!args.StopTime.empty()) { this->CTest->SetStopTime(args.StopTime); }
diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h index 125d5a5..d878265 100644 --- a/Source/CTest/cmCTestTestCommand.h +++ b/Source/CTest/cmCTestTestCommand.h
@@ -46,7 +46,10 @@ std::string TestLoad; std::string ResourceSpecFile; std::string OutputJUnit; + std::string CoverageTool; bool StopOnFailure = false; + std::string Preset; + std::string PresetsFile; }; template <typename Args> @@ -74,7 +77,10 @@ .Bind("TEST_LOAD"_s, &TestArguments::TestLoad) .Bind("RESOURCE_SPEC_FILE"_s, &TestArguments::ResourceSpecFile) .Bind("STOP_ON_FAILURE"_s, &TestArguments::StopOnFailure) - .Bind("OUTPUT_JUNIT"_s, &TestArguments::OutputJUnit); + .Bind("COVERAGE_TOOL"_s, &TestArguments::CoverageTool) + .Bind("OUTPUT_JUNIT"_s, &TestArguments::OutputJUnit) + .Bind("PRESET"_s, &TestArguments::Preset) + .Bind("PRESETS_FILE"_s, &TestArguments::PresetsFile); } private:
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 5c6679e..1661b69 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -15,6 +15,7 @@ #include <ratio> #include <set> #include <sstream> +#include <string> #include <utility> #ifndef _WIN32 @@ -33,7 +34,10 @@ #include "cm_utf8.h" +#include "cmArgumentParser.h" +#include "cmCMakePresetsGraph.h" #include "cmCTest.h" +#include "cmCTestDiscoverTests.h" #include "cmCTestMultiProcessHandler.h" #include "cmCTestResourceGroupsLexerHelper.h" #include "cmCTestTestMeasurementXMLParser.h" @@ -49,12 +53,76 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTestDiscovery.h" #include "cmTimestamp.h" #include "cmValue.h" #include "cmWorkingDirectory.h" #include "cmXMLWriter.h" #include "cmake.h" +void cmCTestApplyTestPresetToOptions( + cmCTestTestOptions& opts, cmCMakePresetsGraph::TestPreset const& preset) +{ + if (preset.Filter) { + if (preset.Filter->Include) { + auto const& inc = *preset.Filter->Include; + opts.IncludeRegularExpression = inc.Name; + if (!inc.Label.empty()) { + opts.LabelRegularExpression.push_back(inc.Label); + } + opts.UseUnion = inc.UseUnion.value_or(false); + if (inc.Index) { + auto const& idx = *inc.Index; + if (!idx.IndexFile.empty()) { + opts.TestsToRunInformation = idx.IndexFile; + } else { + opts.TestsToRunInformation = cmStrCat( + (idx.Start ? std::to_string(*idx.Start) : std::string{}), ',', + (idx.End ? std::to_string(*idx.End) : std::string{}), ',', + (idx.Stride ? std::to_string(*idx.Stride) : std::string{}), ',', + cmJoin(idx.SpecificTests, ",")); + } + } + } + if (preset.Filter->Exclude) { + auto const& exc = *preset.Filter->Exclude; + opts.ExcludeRegularExpression = exc.Name; + if (!exc.Label.empty()) { + opts.ExcludeLabelRegularExpression.push_back(exc.Label); + } + if (exc.Fixtures) { + opts.ExcludeFixtureRegularExpression = exc.Fixtures->Any; + opts.ExcludeFixtureSetupRegularExpression = exc.Fixtures->Setup; + opts.ExcludeFixtureCleanupRegularExpression = exc.Fixtures->Cleanup; + } + } + } + + if (preset.Execution) { + auto const& exec = *preset.Execution; + opts.StopOnFailure = exec.StopOnFailure.value_or(false); + opts.ResourceSpecFile = exec.ResourceSpecFile; + opts.ScheduleRandom = exec.ScheduleRandom.value_or(false); + opts.TestPassthroughArguments = exec.TestPassthroughArguments; + } + + if (preset.Output) { + auto const& output = *preset.Output; + if (!output.OutputJUnitFile.empty()) { + opts.JUnitXMLFileName = output.OutputJUnitFile; + } + if (output.MaxPassedTestOutputSize) { + opts.OutputSizePassed = *output.MaxPassedTestOutputSize; + } + if (output.MaxFailedTestOutputSize) { + opts.OutputSizeFailed = *output.MaxFailedTestOutputSize; + } + if (output.TestOutputTruncation) { + opts.OutputTruncation = *output.TestOutputTruncation; + } + } +} + namespace { class cmCTestCommand @@ -184,6 +252,52 @@ return this->TestHandler->AddTest(args); } +class cmCTestDiscoverTestsCommand : public cmCTestCommand +{ +public: + using cmCTestCommand::cmCTestCommand; + bool InitialPass(std::vector<std::string> const& args, + cmExecutionStatus& status) override; +}; + +bool cmCTestDiscoverTestsCommand::InitialPass( + std::vector<std::string> const& args, cmExecutionStatus& status) +{ + struct Arguments : cmTestDiscoveryArgs + { + std::string TestList; + }; + static auto const parser = + cmArgumentParser<Arguments>{ cmTestDiscoveryParser<Arguments>() } // + .Bind("TEST_LIST"_s, &Arguments::TestList); + + auto unparsed = std::vector<std::string>{}; + Arguments const arguments = parser.Parse(args, &unparsed); + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; + } + + if (!unparsed.empty()) { + status.SetError(" given unknown argument \"" + unparsed.front() + "\"."); + return false; + } + + std::vector<std::string> testList; + if (!cmCTestDiscoverTests(arguments, this->TestHandler, testList, status)) { + return false; + } + + if (!arguments.TestList.empty()) { + for (std::string& testName : testList) { + cmSystemTools::ReplaceString(testName, ";", "\\;"); + } + status.GetMakefile().AddDefinition(arguments.TestList, + cmList::to_string(testList)); + } + + return true; +} + class cmCTestSetTestsPropertiesCommand : public cmCTestCommand { public: @@ -359,7 +473,7 @@ << cmSystemTools::GetLogicalWorkingDirectory() << std::endl, this->Quiet); - if (!this->PreProcessHandler()) { + if (!this->CTest->GetShowOnly() && !this->PreProcessHandler()) { return -1; } @@ -428,7 +542,7 @@ return 1; } - if (!this->PostProcessHandler()) { + if (!this->CTest->GetShowOnly() && !this->PostProcessHandler()) { this->LogFile = nullptr; return -1; } @@ -547,13 +661,21 @@ } else { failedColorCode = this->CTest->GetColorCode(cmCTest::Color::RED); } - cmCTestLog(this->CTest, HANDLER_OUTPUT, - std::endl - << passColorCode << std::lround(percent) << "% tests passed" - << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR) - << ", " << failedColorCode << failed.size() << " tests failed" - << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR) - << " out of " << total << std::endl); + if (failed.empty()) { + cmCTestLog(this->CTest, HANDLER_OUTPUT, + std::endl + << passColorCode << std::lround(percent) << "% tests passed" + << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR) + << " out of " << total << std::endl); + } else { + cmCTestLog(this->CTest, HANDLER_OUTPUT, + std::endl + << passColorCode << std::lround(percent) << "% tests passed" + << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR) + << ", " << failedColorCode << failed.size() << " tests failed" + << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR) + << " out of " << total << std::endl); + } if ((!this->CTest->GetLabelsForSubprojects().empty() && this->CTest->GetSubprojectSummary())) { this->PrintLabelOrSubprojectSummary(true); @@ -1772,6 +1894,10 @@ // Add handler for ADD_TEST cm.GetState()->AddBuiltinCommand("add_test", cmCTestAddTestCommand(this)); + // Add handler for DISCOVER_TESTS + cm.GetState()->AddBuiltinCommand("discover_tests", + cmCTestDiscoverTestsCommand(this)); + // Add handler for SUBDIRS cm.GetState()->AddBuiltinCommand("subdirs", cmCTestSubdirCommand); @@ -1933,15 +2059,11 @@ int numFiles = static_cast<int>(cmsys::Directory::GetNumberOfFilesInDirectory(dirName)); - std::string pattern = "LastTestsFailed"; std::string logName; for (int i = 0; i < numFiles; ++i) { - std::string fileName = directory.GetFile(i); - // bcc crashes if we attempt a normal substring comparison, - // hence the following workaround - std::string fileNameSubstring = fileName.substr(0, pattern.length()); - if (fileNameSubstring != pattern) { + std::string const& fileName = directory.GetFileName(i); + if (!cmHasLiteralPrefix(fileName, "LastTestsFailed")) { continue; } if (logName.empty()) { @@ -2441,42 +2563,20 @@ this->ExcludeTestsRegularExpression.find(testname)) { return true; } - if (this->MemCheck) { - std::vector<std::string>::iterator it; - bool found = false; - for (it = this->CustomTestsIgnore.begin(); - it != this->CustomTestsIgnore.end(); ++it) { - if (*it == testname) { - found = true; - break; - } - } - if (found) { - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "Ignore memcheck: " << *it << std::endl, this->Quiet); - return true; - } - } else { - std::vector<std::string>::iterator it; - bool found = false; - for (it = this->CustomTestsIgnore.begin(); - it != this->CustomTestsIgnore.end(); ++it) { - if (*it == testname) { - found = true; - break; - } - } - if (found) { - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "Ignore test: " << *it << std::endl, this->Quiet); - return true; - } + + if (cm::contains(this->CustomTestsIgnore, testname)) { + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Ignore " << (this->MemCheck ? "memcheck" : "test") + << ": " << testname << std::endl, + this->Quiet); + return true; } cmCTestTestProperties test; test.Name = testname; test.Args = args; - test.Directory = cmSystemTools::GetLogicalWorkingDirectory(); + test.CTestDirectory = cmSystemTools::GetLogicalWorkingDirectory(); + test.Directory = test.CTestDirectory; cmCTestOptionalLog(this->CTest, DEBUG, "Set test directory: " << test.Directory << std::endl, this->Quiet); @@ -2487,7 +2587,7 @@ this->ExcludeTestsRegularExpression.find(testname)))) { test.IsInBasedOnREOptions = false; } - this->TestList.push_back(test); + this->TestList.push_back(std::move(test)); return true; }
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index 3a2e556..276ad16 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h
@@ -19,6 +19,7 @@ #include "cmsys/RegularExpression.hxx" +#include "cmCMakePresetsGraph.h" #include "cmCTest.h" #include "cmCTestGenericHandler.h" #include "cmCTestTypes.h" // IWYU pragma: keep @@ -55,8 +56,24 @@ std::string ExcludeTestListFile; std::string ResourceSpecFile; std::string JUnitXMLFileName; + + std::string CoverageTool; + + std::vector<std::string> TestPassthroughArguments; }; +/** Apply a resolved TestPreset's fields to \a opts. + * + * Both the \c ctest \c --preset CLI path and the \c ctest_test(PRESET) + * script-command path call this to keep the mapping in one place and avoid + * drift when new preset fields are added. + * + * Fields that do not live in cmCTestTestOptions (e.g. ParallelLevel, Repeat, + * Timeout, NoTestsAction) are handled separately at each call site. + */ +void cmCTestApplyTestPresetToOptions( + cmCTestTestOptions& opts, cmCMakePresetsGraph::TestPreset const& preset); + /** \class cmCTestTestHandler * \brief A class that handles ctest -S invocations * @@ -129,7 +146,10 @@ void AppendError(cm::string_view err); cm::optional<std::string> Error; std::string Name; + // working directory for test, overridden by WORKING_DIRECTORY property std::string Directory; + // Original directory of test creation + std::string CTestDirectory; std::vector<std::string> Args; std::vector<std::string> RequiredFiles; std::vector<std::string> Depends;
diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx index 925941b..4d0e8ab 100644 --- a/Source/CTest/cmCTestUpdateCommand.cxx +++ b/Source/CTest/cmCTestUpdateCommand.cxx
@@ -172,6 +172,13 @@ updateType = DetermineType(mf.GetSafeDefinition("CTEST_UPDATE_TYPE")); } + std::string const& versionOverride = !args.VersionOverride.empty() + ? args.VersionOverride + : mf.GetSafeDefinition("CTEST_UPDATE_VERSION_OVERRIDE"); + + bool const versionOnly = + args.VersionOnly || mf.IsOn("CTEST_UPDATE_VERSION_ONLY"); + // If no update command was specified, lookup one for this VCS tool. if (updateCommand.empty()) { char const* key = TypeToCommandKey(updateType); @@ -231,7 +238,16 @@ auto start_time_time = std::chrono::system_clock::now(); auto elapsed_time_start = std::chrono::steady_clock::now(); - bool updated = vc->Update(); + bool const updated = [&]() -> bool { + if (!versionOverride.empty()) { + return vc->UpdateVersionOverride(versionOverride); + } + if (versionOnly) { + return vc->UpdateVersionOnly(); + } + return vc->Update(); + }(); + std::string buildname = cmCTest::SafeBuildIdField(mf.GetSafeDefinition("CTEST_BUILD_NAME")); @@ -321,6 +337,8 @@ cmArgumentParser<UpdateArguments>{ MakeBasicParser<UpdateArguments>() } .Bind("SOURCE"_s, &UpdateArguments::Source) .Bind("RETURN_VALUE"_s, &UpdateArguments::ReturnValue) + .Bind("VERSION_ONLY"_s, &UpdateArguments::VersionOnly) + .Bind("VERSION_OVERRIDE"_s, &UpdateArguments::VersionOverride) .Bind("QUIET"_s, &UpdateArguments::Quiet); return this->Invoke(parser, args, status, [&](UpdateArguments& a) {
diff --git a/Source/CTest/cmCTestUpdateCommand.h b/Source/CTest/cmCTestUpdateCommand.h index 7bc2e01..71efdf8 100644 --- a/Source/CTest/cmCTestUpdateCommand.h +++ b/Source/CTest/cmCTestUpdateCommand.h
@@ -20,6 +20,8 @@ { std::string Source; std::string ReturnValue; + std::string VersionOverride; + bool VersionOnly = false; bool Quiet = false; };
diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx index ea38c26..b8e465d 100644 --- a/Source/CTest/cmCTestVC.cxx +++ b/Source/CTest/cmCTestVC.cxx
@@ -138,27 +138,25 @@ bool cmCTestVC::Update() { bool result = true; - - // Use the explicitly specified version. - std::string versionOverride = - this->Makefile->GetSafeDefinition("CTEST_UPDATE_VERSION_OVERRIDE"); - if (!versionOverride.empty()) { - this->SetNewRevision(versionOverride); - return true; - } - - // if update version only is on then do not actually update, - // just note the current version and finish - if (!this->Makefile->IsOn("CTEST_UPDATE_VERSION_ONLY")) { - result = this->NoteOldRevision() && result; - this->Log << "--- Begin Update ---\n"; - result = this->UpdateImpl() && result; - this->Log << "--- End Update ---\n"; - } + result = this->NoteOldRevision() && result; + this->Log << "--- Begin Update ---\n"; + result = this->UpdateImpl() && result; + this->Log << "--- End Update ---\n"; result = this->NoteNewRevision() && result; return result; } +bool cmCTestVC::UpdateVersionOverride(std::string const& version) +{ + this->SetNewRevision(version); + return true; +} + +bool cmCTestVC::UpdateVersionOnly() +{ + return this->NoteNewRevision(); +} + bool cmCTestVC::NoteOldRevision() { // We do nothing by default.
diff --git a/Source/CTest/cmCTestVC.h b/Source/CTest/cmCTestVC.h index 5ca5d75..592c427 100644 --- a/Source/CTest/cmCTestVC.h +++ b/Source/CTest/cmCTestVC.h
@@ -45,6 +45,12 @@ /** Update the working tree to the new revision. */ bool Update(); + /** Use the explicitly specified version. */ + bool UpdateVersionOverride(std::string const& version); + + /** Do not actually update, just note the current version and finish. */ + bool UpdateVersionOnly(); + /** Get the command line used by the Update method. */ std::string const& GetUpdateCommandLine() const {
diff --git a/Source/CTest/cmParseCacheCoverage.cxx b/Source/CTest/cmParseCacheCoverage.cxx index 07fa478..44dcc6b 100644 --- a/Source/CTest/cmParseCacheCoverage.cxx +++ b/Source/CTest/cmParseCacheCoverage.cxx
@@ -31,7 +31,7 @@ unsigned int i; numf = dir.GetNumberOfFiles(); for (i = 0; i < numf; i++) { - std::string file = dir.GetFile(i); + std::string const& file = dir.GetFileName(i); if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) { std::string path = cmStrCat(d, '/', file); if (cmSystemTools::GetFilenameLastExtension(path) == ".cmcov") {
diff --git a/Source/CTest/cmParseGTMCoverage.cxx b/Source/CTest/cmParseGTMCoverage.cxx index c11be30..7ca5fe8 100644 --- a/Source/CTest/cmParseGTMCoverage.cxx +++ b/Source/CTest/cmParseGTMCoverage.cxx
@@ -29,7 +29,7 @@ unsigned int i; numf = dir.GetNumberOfFiles(); for (i = 0; i < numf; i++) { - std::string file = dir.GetFile(i); + std::string const& file = dir.GetFileName(i); if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) { std::string path = cmStrCat(d, '/', file); if (cmSystemTools::GetFilenameLastExtension(path) == ".mcov") {
diff --git a/Source/CTest/cmParsePHPCoverage.cxx b/Source/CTest/cmParsePHPCoverage.cxx index 14c9fe8..0bc98d7 100644 --- a/Source/CTest/cmParsePHPCoverage.cxx +++ b/Source/CTest/cmParsePHPCoverage.cxx
@@ -210,7 +210,7 @@ unsigned int i; numf = dir.GetNumberOfFiles(); for (i = 0; i < numf; i++) { - std::string file = dir.GetFile(i); + std::string const& file = dir.GetFileName(i); if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) { std::string path = cmStrCat(d, '/', file); if (!this->ReadPHPData(path.c_str())) {
diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index 0610672..d0e3d0c 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx
@@ -35,19 +35,24 @@ cmProcess::~cmProcess() = default; -void cmProcess::SetCommand(std::string const& command) +void cmProcess::SetCommand(std::string command) { - this->Command = command; + this->Command = std::move(command); } -void cmProcess::SetCommandArguments(std::vector<std::string> const& args) +void cmProcess::SetCommandArguments(std::vector<std::string> args) { - this->Arguments = args; + this->Arguments = std::move(args); } -void cmProcess::SetWorkingDirectory(std::string const& dir) +void cmProcess::SetEnvironment(std::vector<std::string> env) { - this->WorkingDirectory = dir; + this->Environment = std::move(env); +} + +void cmProcess::SetWorkingDirectory(std::string dir) +{ + this->WorkingDirectory = std::move(dir); } bool cmProcess::StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity) @@ -128,6 +133,15 @@ #else static_cast<void>(affinity); #endif + if (!this->Environment.empty()) { + this->Env.clear(); + this->Env.reserve(this->Environment.size() + 1); + for (auto const& var : this->Environment) { + this->Env.push_back(var.c_str()); + } + this->Env.push_back(nullptr); + options.env = const_cast<char**>(this->Env.data()); + } status = uv_read_start(pipe_reader, &cmProcess::OnAllocateCB, &cmProcess::OnReadCB);
diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h index a4704b4..d475dd3 100644 --- a/Source/CTest/cmProcess.h +++ b/Source/CTest/cmProcess.h
@@ -34,9 +34,10 @@ public: explicit cmProcess(std::unique_ptr<cmCTestRunTest> runner); ~cmProcess(); - void SetCommand(std::string const& command); - void SetCommandArguments(std::vector<std::string> const& arg); - void SetWorkingDirectory(std::string const& dir); + void SetCommand(std::string command); + void SetCommandArguments(std::vector<std::string> arg); + void SetEnvironment(std::vector<std::string> env); + void SetWorkingDirectory(std::string dir); void SetStopTimeout(cmDuration t) { this->StopTimeout = t; } void SetTimeout(cmDuration t) { this->Timeout = t; } void ChangeTimeout(cmDuration t); @@ -161,6 +162,8 @@ std::string WorkingDirectory; std::vector<std::string> Arguments; std::vector<char const*> ProcessArgs; + std::vector<std::string> Environment; + std::vector<char const*> Env; int Id; int64_t ExitValue; Termination TerminationStyle = Termination::Normal;
diff --git a/Source/Checks/Curses/CMakeLists.txt b/Source/Checks/Curses/CMakeLists.txt index c7b8e5f..2854172 100644 --- a/Source/Checks/Curses/CMakeLists.txt +++ b/Source/Checks/Curses/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13...4.1 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...4.2 FATAL_ERROR) project(CheckCurses C) set(CURSES_NEED_NCURSES TRUE)
diff --git a/Source/LexerParser/cmDependsJavaParser.cxx b/Source/LexerParser/cmDependsJavaParser.cxx index e85bf98..578646f 100644 --- a/Source/LexerParser/cmDependsJavaParser.cxx +++ b/Source/LexerParser/cmDependsJavaParser.cxx
@@ -119,13 +119,16 @@ # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wfree-nonheap-object" #endif +#if defined(__GNUC__) && __GNUC__ >= 16 +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif #if defined(__clang__) && defined(__has_warning) # if __has_warning("-Wunused-but-set-variable") # pragma clang diagnostic ignored "-Wunused-but-set-variable" # endif #endif -#line 129 "cmDependsJavaParser.cxx" +#line 132 "cmDependsJavaParser.cxx" # ifndef YY_CAST # ifdef __cplusplus @@ -813,42 +816,42 @@ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { - 0, 184, 184, 193, 201, 209, 217, 225, 233, 242, - 250, 259, 267, 276, 281, 286, 291, 296, 301, 306, - 311, 317, 325, 334, 344, 353, 362, 370, 380, 386, - 393, 400, 406, 413, 422, 432, 442, 451, 459, 468, - 477, 483, 492, 498, 507, 513, 522, 534, 542, 551, - 563, 576, 584, 592, 601, 609, 618, 618, 618, 619, - 620, 620, 620, 620, 620, 620, 621, 624, 634, 643, - 652, 661, 671, 677, 686, 695, 704, 712, 721, 730, - 736, 745, 753, 761, 769, 778, 786, 795, 801, 809, - 818, 826, 835, 844, 853, 861, 870, 878, 886, 895, - 904, 914, 921, 931, 941, 948, 955, 958, 964, 974, - 984, 994, 1000, 1010, 1020, 1030, 1039, 1049, 1060, 1070, - 1077, 1087, 1096, 1106, 1115, 1125, 1131, 1141, 1150, 1160, - 1170, 1177, 1186, 1195, 1204, 1213, 1221, 1230, 1239, 1249, - 1259, 1268, 1278, 1288, 1295, 1304, 1314, 1323, 1333, 1342, - 1349, 1359, 1368, 1378, 1387, 1396, 1406, 1416, 1425, 1435, - 1444, 1453, 1462, 1471, 1480, 1490, 1499, 1508, 1517, 1526, - 1536, 1545, 1554, 1563, 1572, 1581, 1590, 1599, 1608, 1617, - 1626, 1635, 1645, 1655, 1666, 1676, 1686, 1695, 1704, 1713, - 1722, 1731, 1740, 1750, 1760, 1770, 1780, 1787, 1794, 1801, - 1811, 1818, 1828, 1838, 1847, 1857, 1866, 1876, 1883, 1890, - 1897, 1905, 1912, 1922, 1929, 1939, 1949, 1956, 1966, 1975, - 1985, 1995, 2004, 2014, 2023, 2033, 2044, 2051, 2058, 2069, - 2079, 2089, 2099, 2108, 2118, 2125, 2135, 2144, 2154, 2161, - 2171, 2180, 2190, 2199, 2205, 2214, 2223, 2232, 2241, 2251, - 2261, 2268, 2278, 2285, 2295, 2304, 2314, 2323, 2332, 2341, - 2351, 2358, 2368, 2377, 2387, 2397, 2403, 2410, 2420, 2430, - 2440, 2451, 2461, 2472, 2482, 2493, 2503, 2513, 2522, 2531, - 2540, 2549, 2559, 2569, 2579, 2588, 2597, 2606, 2615, 2625, - 2635, 2645, 2654, 2663, 2672, 2682, 2691, 2700, 2707, 2716, - 2725, 2734, 2744, 2753, 2762, 2772, 2781, 2790, 2799, 2809, - 2818, 2827, 2836, 2845, 2854, 2864, 2873, 2882, 2892, 2901, - 2911, 2920, 2930, 2939, 2949, 2958, 2968, 2977, 2987, 2996, - 3006, 3015, 3025, 3035, 3045, 3054, 3064, 3073, 3082, 3091, - 3100, 3109, 3118, 3127, 3136, 3145, 3154, 3163, 3173, 3183, - 3193, 3202 + 0, 187, 187, 196, 204, 212, 220, 228, 236, 245, + 253, 262, 270, 279, 284, 289, 294, 299, 304, 309, + 314, 320, 328, 337, 347, 356, 365, 373, 383, 389, + 396, 403, 409, 416, 425, 435, 445, 454, 462, 471, + 480, 486, 495, 501, 510, 516, 525, 537, 545, 554, + 566, 579, 587, 595, 604, 612, 621, 621, 621, 622, + 623, 623, 623, 623, 623, 623, 624, 627, 637, 646, + 655, 664, 674, 680, 689, 698, 707, 715, 724, 733, + 739, 748, 756, 764, 772, 781, 789, 798, 804, 812, + 821, 829, 838, 847, 856, 864, 873, 881, 889, 898, + 907, 917, 924, 934, 944, 951, 958, 961, 967, 977, + 987, 997, 1003, 1013, 1023, 1033, 1042, 1052, 1063, 1073, + 1080, 1090, 1099, 1109, 1118, 1128, 1134, 1144, 1153, 1163, + 1173, 1180, 1189, 1198, 1207, 1216, 1224, 1233, 1242, 1252, + 1262, 1271, 1281, 1291, 1298, 1307, 1317, 1326, 1336, 1345, + 1352, 1362, 1371, 1381, 1390, 1399, 1409, 1419, 1428, 1438, + 1447, 1456, 1465, 1474, 1483, 1493, 1502, 1511, 1520, 1529, + 1539, 1548, 1557, 1566, 1575, 1584, 1593, 1602, 1611, 1620, + 1629, 1638, 1648, 1658, 1669, 1679, 1689, 1698, 1707, 1716, + 1725, 1734, 1743, 1753, 1763, 1773, 1783, 1790, 1797, 1804, + 1814, 1821, 1831, 1841, 1850, 1860, 1869, 1879, 1886, 1893, + 1900, 1908, 1915, 1925, 1932, 1942, 1952, 1959, 1969, 1978, + 1988, 1998, 2007, 2017, 2026, 2036, 2047, 2054, 2061, 2072, + 2082, 2092, 2102, 2111, 2121, 2128, 2138, 2147, 2157, 2164, + 2174, 2183, 2193, 2202, 2208, 2217, 2226, 2235, 2244, 2254, + 2264, 2271, 2281, 2288, 2298, 2307, 2317, 2326, 2335, 2344, + 2354, 2361, 2371, 2380, 2390, 2400, 2406, 2413, 2423, 2433, + 2443, 2454, 2464, 2475, 2485, 2496, 2506, 2516, 2525, 2534, + 2543, 2552, 2562, 2572, 2582, 2591, 2600, 2609, 2618, 2628, + 2638, 2648, 2657, 2666, 2675, 2685, 2694, 2703, 2710, 2719, + 2728, 2737, 2747, 2756, 2765, 2775, 2784, 2793, 2802, 2812, + 2821, 2830, 2839, 2848, 2857, 2867, 2876, 2885, 2895, 2904, + 2914, 2923, 2933, 2942, 2952, 2961, 2971, 2980, 2990, 2999, + 3009, 3018, 3028, 3038, 3048, 3057, 3067, 3076, 3085, 3094, + 3103, 3112, 3121, 3130, 3139, 3148, 3157, 3166, 3176, 3186, + 3196, 3205 }; #endif @@ -2469,214 +2472,214 @@ switch (yyn) { case 2: /* Goal: CompilationUnit */ -#line 185 "cmDependsJavaParser.y" +#line 188 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2480 "cmDependsJavaParser.cxx" +#line 2483 "cmDependsJavaParser.cxx" break; case 3: /* Literal: IntegerLiteral */ -#line 194 "cmDependsJavaParser.y" +#line 197 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2491 "cmDependsJavaParser.cxx" +#line 2494 "cmDependsJavaParser.cxx" break; case 4: /* Literal: jp_FLOATINGPOINTLITERAL */ -#line 202 "cmDependsJavaParser.y" +#line 205 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2502 "cmDependsJavaParser.cxx" +#line 2505 "cmDependsJavaParser.cxx" break; case 5: /* Literal: jp_BOOLEANLITERAL */ -#line 210 "cmDependsJavaParser.y" +#line 213 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2513 "cmDependsJavaParser.cxx" +#line 2516 "cmDependsJavaParser.cxx" break; case 6: /* Literal: jp_CHARACTERLITERAL */ -#line 218 "cmDependsJavaParser.y" +#line 221 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2524 "cmDependsJavaParser.cxx" +#line 2527 "cmDependsJavaParser.cxx" break; case 7: /* Literal: jp_STRINGLITERAL */ -#line 226 "cmDependsJavaParser.y" +#line 229 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2535 "cmDependsJavaParser.cxx" +#line 2538 "cmDependsJavaParser.cxx" break; case 8: /* Literal: jp_NULLLITERAL */ -#line 234 "cmDependsJavaParser.y" +#line 237 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2546 "cmDependsJavaParser.cxx" +#line 2549 "cmDependsJavaParser.cxx" break; case 9: /* IntegerLiteral: jp_DECIMALINTEGERLITERAL */ -#line 243 "cmDependsJavaParser.y" +#line 246 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2557 "cmDependsJavaParser.cxx" +#line 2560 "cmDependsJavaParser.cxx" break; case 10: /* IntegerLiteral: jp_HEXINTEGERLITERAL */ -#line 251 "cmDependsJavaParser.y" +#line 254 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2568 "cmDependsJavaParser.cxx" +#line 2571 "cmDependsJavaParser.cxx" break; case 11: /* Type: PrimitiveType */ -#line 260 "cmDependsJavaParser.y" +#line 263 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2579 "cmDependsJavaParser.cxx" +#line 2582 "cmDependsJavaParser.cxx" break; case 12: /* Type: ReferenceType */ -#line 268 "cmDependsJavaParser.y" +#line 271 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2590 "cmDependsJavaParser.cxx" +#line 2593 "cmDependsJavaParser.cxx" break; case 13: /* PrimitiveType: jp_BYTE_TYPE */ -#line 277 "cmDependsJavaParser.y" +#line 280 "cmDependsJavaParser.y" { jpElementStart(0); } -#line 2598 "cmDependsJavaParser.cxx" +#line 2601 "cmDependsJavaParser.cxx" break; case 14: /* PrimitiveType: jp_SHORT_TYPE */ -#line 282 "cmDependsJavaParser.y" +#line 285 "cmDependsJavaParser.y" { jpElementStart(0); } -#line 2606 "cmDependsJavaParser.cxx" +#line 2609 "cmDependsJavaParser.cxx" break; case 15: /* PrimitiveType: jp_INT_TYPE */ -#line 287 "cmDependsJavaParser.y" +#line 290 "cmDependsJavaParser.y" { jpElementStart(0); } -#line 2614 "cmDependsJavaParser.cxx" +#line 2617 "cmDependsJavaParser.cxx" break; case 16: /* PrimitiveType: jp_LONG_TYPE */ -#line 292 "cmDependsJavaParser.y" +#line 295 "cmDependsJavaParser.y" { jpElementStart(0); } -#line 2622 "cmDependsJavaParser.cxx" +#line 2625 "cmDependsJavaParser.cxx" break; case 17: /* PrimitiveType: jp_CHAR_TYPE */ -#line 297 "cmDependsJavaParser.y" +#line 300 "cmDependsJavaParser.y" { jpElementStart(0); } -#line 2630 "cmDependsJavaParser.cxx" +#line 2633 "cmDependsJavaParser.cxx" break; case 18: /* PrimitiveType: jp_FLOAT_TYPE */ -#line 302 "cmDependsJavaParser.y" +#line 305 "cmDependsJavaParser.y" { jpElementStart(0); } -#line 2638 "cmDependsJavaParser.cxx" +#line 2641 "cmDependsJavaParser.cxx" break; case 19: /* PrimitiveType: jp_DOUBLE_TYPE */ -#line 307 "cmDependsJavaParser.y" +#line 310 "cmDependsJavaParser.y" { jpElementStart(0); } -#line 2646 "cmDependsJavaParser.cxx" +#line 2649 "cmDependsJavaParser.cxx" break; case 20: /* PrimitiveType: jp_BOOLEAN_TYPE */ -#line 312 "cmDependsJavaParser.y" +#line 315 "cmDependsJavaParser.y" { jpElementStart(0); } -#line 2654 "cmDependsJavaParser.cxx" +#line 2657 "cmDependsJavaParser.cxx" break; case 21: /* ReferenceType: ClassOrInterfaceType */ -#line 318 "cmDependsJavaParser.y" +#line 321 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2665 "cmDependsJavaParser.cxx" +#line 2668 "cmDependsJavaParser.cxx" break; case 22: /* ReferenceType: ArrayType */ -#line 326 "cmDependsJavaParser.y" +#line 329 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2676 "cmDependsJavaParser.cxx" +#line 2679 "cmDependsJavaParser.cxx" break; case 23: /* ClassOrInterfaceType: Name */ -#line 335 "cmDependsJavaParser.y" +#line 338 "cmDependsJavaParser.y" { jpElementStart(1); jpStoreClass((yyvsp[0].str)); @@ -2684,44 +2687,44 @@ (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2688 "cmDependsJavaParser.cxx" +#line 2691 "cmDependsJavaParser.cxx" break; case 24: /* ClassType: ClassOrInterfaceType */ -#line 345 "cmDependsJavaParser.y" +#line 348 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2699 "cmDependsJavaParser.cxx" +#line 2702 "cmDependsJavaParser.cxx" break; case 25: /* InterfaceType: ClassOrInterfaceType */ -#line 354 "cmDependsJavaParser.y" +#line 357 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2710 "cmDependsJavaParser.cxx" +#line 2713 "cmDependsJavaParser.cxx" break; case 26: /* ArrayType: PrimitiveType Dims */ -#line 363 "cmDependsJavaParser.y" +#line 366 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2721 "cmDependsJavaParser.cxx" +#line 2724 "cmDependsJavaParser.cxx" break; case 27: /* ArrayType: Name Dims */ -#line 371 "cmDependsJavaParser.y" +#line 374 "cmDependsJavaParser.y" { jpElementStart(2); jpStoreClass((yyvsp[-1].str)); @@ -2729,56 +2732,56 @@ (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2733 "cmDependsJavaParser.cxx" +#line 2736 "cmDependsJavaParser.cxx" break; case 28: /* Name: SimpleName */ -#line 381 "cmDependsJavaParser.y" +#line 384 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = (yyvsp[0].str); } -#line 2742 "cmDependsJavaParser.cxx" +#line 2745 "cmDependsJavaParser.cxx" break; case 29: /* Name: QualifiedName */ -#line 387 "cmDependsJavaParser.y" +#line 390 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = (yyvsp[0].str); } -#line 2751 "cmDependsJavaParser.cxx" +#line 2754 "cmDependsJavaParser.cxx" break; case 30: /* SimpleName: Identifier */ -#line 394 "cmDependsJavaParser.y" +#line 397 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = (yyvsp[0].str); } -#line 2760 "cmDependsJavaParser.cxx" +#line 2763 "cmDependsJavaParser.cxx" break; case 31: /* Identifier: jp_NAME */ -#line 401 "cmDependsJavaParser.y" +#line 404 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = (yyvsp[0].str); } -#line 2769 "cmDependsJavaParser.cxx" +#line 2772 "cmDependsJavaParser.cxx" break; case 32: /* Identifier: jp_DOLLAR jp_NAME */ -#line 407 "cmDependsJavaParser.y" +#line 410 "cmDependsJavaParser.y" { jpElementStart(2); (yyval.str) = (yyvsp[0].str); } -#line 2778 "cmDependsJavaParser.cxx" +#line 2781 "cmDependsJavaParser.cxx" break; case 33: /* QualifiedName: Name jp_DOT Identifier */ -#line 414 "cmDependsJavaParser.y" +#line 417 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->AddClassFound((yyvsp[-2].str)); @@ -2786,11 +2789,11 @@ yyGetParser->DeallocateParserType(&((yyvsp[-2].str))); (yyval.str) = const_cast<char*>(yyGetParser->GetCurrentCombine()); } -#line 2790 "cmDependsJavaParser.cxx" +#line 2793 "cmDependsJavaParser.cxx" break; case 34: /* QualifiedName: Name jp_DOT jp_CLASS */ -#line 423 "cmDependsJavaParser.y" +#line 426 "cmDependsJavaParser.y" { jpElementStart(3); jpStoreClass((yyvsp[-2].str)); @@ -2799,11 +2802,11 @@ (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2803 "cmDependsJavaParser.cxx" +#line 2806 "cmDependsJavaParser.cxx" break; case 35: /* QualifiedName: Name jp_DOT jp_THIS */ -#line 433 "cmDependsJavaParser.y" +#line 436 "cmDependsJavaParser.y" { jpElementStart(3); jpStoreClass((yyvsp[-2].str)); @@ -2812,118 +2815,118 @@ (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2816 "cmDependsJavaParser.cxx" +#line 2819 "cmDependsJavaParser.cxx" break; case 36: /* QualifiedName: SimpleType jp_DOT jp_CLASS */ -#line 443 "cmDependsJavaParser.y" +#line 446 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2827 "cmDependsJavaParser.cxx" +#line 2830 "cmDependsJavaParser.cxx" break; case 37: /* SimpleType: PrimitiveType */ -#line 452 "cmDependsJavaParser.y" +#line 455 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2838 "cmDependsJavaParser.cxx" +#line 2841 "cmDependsJavaParser.cxx" break; case 38: /* SimpleType: jp_VOID */ -#line 460 "cmDependsJavaParser.y" +#line 463 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2849 "cmDependsJavaParser.cxx" +#line 2852 "cmDependsJavaParser.cxx" break; case 39: /* CompilationUnit: PackageDeclarationopt ImportDeclarations TypeDeclarations */ -#line 469 "cmDependsJavaParser.y" +#line 472 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2860 "cmDependsJavaParser.cxx" +#line 2863 "cmDependsJavaParser.cxx" break; case 40: /* PackageDeclarationopt: %empty */ -#line 477 "cmDependsJavaParser.y" +#line 480 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2870 "cmDependsJavaParser.cxx" +#line 2873 "cmDependsJavaParser.cxx" break; case 41: /* PackageDeclarationopt: PackageDeclaration */ -#line 484 "cmDependsJavaParser.y" +#line 487 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2881 "cmDependsJavaParser.cxx" +#line 2884 "cmDependsJavaParser.cxx" break; case 42: /* ImportDeclarations: %empty */ -#line 492 "cmDependsJavaParser.y" +#line 495 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2891 "cmDependsJavaParser.cxx" +#line 2894 "cmDependsJavaParser.cxx" break; case 43: /* ImportDeclarations: ImportDeclarations ImportDeclaration */ -#line 499 "cmDependsJavaParser.y" +#line 502 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2902 "cmDependsJavaParser.cxx" +#line 2905 "cmDependsJavaParser.cxx" break; case 44: /* TypeDeclarations: %empty */ -#line 507 "cmDependsJavaParser.y" +#line 510 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2912 "cmDependsJavaParser.cxx" +#line 2915 "cmDependsJavaParser.cxx" break; case 45: /* TypeDeclarations: TypeDeclarations TypeDeclaration */ -#line 514 "cmDependsJavaParser.y" +#line 517 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2923 "cmDependsJavaParser.cxx" +#line 2926 "cmDependsJavaParser.cxx" break; case 46: /* PackageDeclaration: jp_PACKAGE Name jp_SEMICOL */ -#line 523 "cmDependsJavaParser.y" +#line 526 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->SetCurrentPackage((yyvsp[-1].str)); @@ -2933,33 +2936,33 @@ (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2937 "cmDependsJavaParser.cxx" +#line 2940 "cmDependsJavaParser.cxx" break; case 47: /* ImportDeclaration: SingleTypeImportDeclaration */ -#line 535 "cmDependsJavaParser.y" +#line 538 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2948 "cmDependsJavaParser.cxx" +#line 2951 "cmDependsJavaParser.cxx" break; case 48: /* ImportDeclaration: TypeImportOnDemandDeclaration */ -#line 543 "cmDependsJavaParser.y" +#line 546 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2959 "cmDependsJavaParser.cxx" +#line 2962 "cmDependsJavaParser.cxx" break; case 49: /* SingleTypeImportDeclaration: jp_IMPORT Name jp_SEMICOL */ -#line 552 "cmDependsJavaParser.y" +#line 555 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->AddPackagesImport((yyvsp[-1].str)); @@ -2969,11 +2972,11 @@ (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2973 "cmDependsJavaParser.cxx" +#line 2976 "cmDependsJavaParser.cxx" break; case 50: /* TypeImportOnDemandDeclaration: jp_IMPORT Name jp_DOT jp_TIMES jp_SEMICOL */ -#line 564 "cmDependsJavaParser.y" +#line 567 "cmDependsJavaParser.y" { jpElementStart(5); std::string str = (yyvsp[-3].str); @@ -2984,77 +2987,77 @@ (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2988 "cmDependsJavaParser.cxx" +#line 2991 "cmDependsJavaParser.cxx" break; case 51: /* TypeDeclaration: ClassDeclaration */ -#line 577 "cmDependsJavaParser.y" +#line 580 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 2999 "cmDependsJavaParser.cxx" +#line 3002 "cmDependsJavaParser.cxx" break; case 52: /* TypeDeclaration: InterfaceDeclaration */ -#line 585 "cmDependsJavaParser.y" +#line 588 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3010 "cmDependsJavaParser.cxx" +#line 3013 "cmDependsJavaParser.cxx" break; case 53: /* TypeDeclaration: jp_SEMICOL */ -#line 593 "cmDependsJavaParser.y" +#line 596 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3021 "cmDependsJavaParser.cxx" +#line 3024 "cmDependsJavaParser.cxx" break; case 54: /* Modifiers: Modifier */ -#line 602 "cmDependsJavaParser.y" +#line 605 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3032 "cmDependsJavaParser.cxx" +#line 3035 "cmDependsJavaParser.cxx" break; case 55: /* Modifiers: Modifiers Modifier */ -#line 610 "cmDependsJavaParser.y" +#line 613 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3043 "cmDependsJavaParser.cxx" +#line 3046 "cmDependsJavaParser.cxx" break; case 67: /* ClassHeader: Modifiersopt jp_CLASS Identifier */ -#line 625 "cmDependsJavaParser.y" +#line 628 "cmDependsJavaParser.y" { yyGetParser->StartClass((yyvsp[0].str)); jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); jpCheckEmpty(3); } -#line 3054 "cmDependsJavaParser.cxx" +#line 3057 "cmDependsJavaParser.cxx" break; case 68: /* ClassDeclaration: ClassHeader ClassBody */ -#line 635 "cmDependsJavaParser.y" +#line 638 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3062,11 +3065,11 @@ yyGetParser->SetCurrentCombine(""); yyGetParser->EndClass(); } -#line 3066 "cmDependsJavaParser.cxx" +#line 3069 "cmDependsJavaParser.cxx" break; case 69: /* ClassDeclaration: ClassHeader Interfaces ClassBody */ -#line 644 "cmDependsJavaParser.y" +#line 647 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(2); @@ -3074,11 +3077,11 @@ yyGetParser->SetCurrentCombine(""); yyGetParser->EndClass(); } -#line 3078 "cmDependsJavaParser.cxx" +#line 3081 "cmDependsJavaParser.cxx" break; case 70: /* ClassDeclaration: ClassHeader Super ClassBody */ -#line 653 "cmDependsJavaParser.y" +#line 656 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -3086,11 +3089,11 @@ yyGetParser->SetCurrentCombine(""); yyGetParser->EndClass(); } -#line 3090 "cmDependsJavaParser.cxx" +#line 3093 "cmDependsJavaParser.cxx" break; case 71: /* ClassDeclaration: ClassHeader Super Interfaces ClassBody */ -#line 662 "cmDependsJavaParser.y" +#line 665 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -3098,226 +3101,226 @@ yyGetParser->SetCurrentCombine(""); yyGetParser->EndClass(); } -#line 3102 "cmDependsJavaParser.cxx" +#line 3105 "cmDependsJavaParser.cxx" break; case 72: /* Modifiersopt: %empty */ -#line 671 "cmDependsJavaParser.y" +#line 674 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3112 "cmDependsJavaParser.cxx" +#line 3115 "cmDependsJavaParser.cxx" break; case 73: /* Modifiersopt: Modifiers */ -#line 678 "cmDependsJavaParser.y" +#line 681 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3123 "cmDependsJavaParser.cxx" +#line 3126 "cmDependsJavaParser.cxx" break; case 74: /* Super: jp_EXTENDS ClassType */ -#line 687 "cmDependsJavaParser.y" +#line 690 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3134 "cmDependsJavaParser.cxx" +#line 3137 "cmDependsJavaParser.cxx" break; case 75: /* Interfaces: jp_IMPLEMENTS InterfaceTypeList */ -#line 696 "cmDependsJavaParser.y" +#line 699 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3145 "cmDependsJavaParser.cxx" +#line 3148 "cmDependsJavaParser.cxx" break; case 76: /* InterfaceTypeList: InterfaceType */ -#line 705 "cmDependsJavaParser.y" +#line 708 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3156 "cmDependsJavaParser.cxx" +#line 3159 "cmDependsJavaParser.cxx" break; case 77: /* InterfaceTypeList: InterfaceTypeList jp_COMMA InterfaceType */ -#line 713 "cmDependsJavaParser.y" +#line 716 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3167 "cmDependsJavaParser.cxx" +#line 3170 "cmDependsJavaParser.cxx" break; case 78: /* ClassBody: jp_CURLYSTART ClassBodyDeclarations jp_CURLYEND */ -#line 722 "cmDependsJavaParser.y" +#line 725 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3178 "cmDependsJavaParser.cxx" +#line 3181 "cmDependsJavaParser.cxx" break; case 79: /* ClassBodyDeclarations: %empty */ -#line 730 "cmDependsJavaParser.y" +#line 733 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3188 "cmDependsJavaParser.cxx" +#line 3191 "cmDependsJavaParser.cxx" break; case 80: /* ClassBodyDeclarations: ClassBodyDeclarations ClassBodyDeclaration */ -#line 737 "cmDependsJavaParser.y" +#line 740 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3199 "cmDependsJavaParser.cxx" +#line 3202 "cmDependsJavaParser.cxx" break; case 81: /* ClassBodyDeclaration: ClassMemberDeclaration */ -#line 746 "cmDependsJavaParser.y" +#line 749 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3210 "cmDependsJavaParser.cxx" +#line 3213 "cmDependsJavaParser.cxx" break; case 82: /* ClassBodyDeclaration: StaticInitializer */ -#line 754 "cmDependsJavaParser.y" +#line 757 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3221 "cmDependsJavaParser.cxx" +#line 3224 "cmDependsJavaParser.cxx" break; case 83: /* ClassBodyDeclaration: ConstructorDeclaration */ -#line 762 "cmDependsJavaParser.y" +#line 765 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3232 "cmDependsJavaParser.cxx" +#line 3235 "cmDependsJavaParser.cxx" break; case 84: /* ClassBodyDeclaration: TypeDeclaration */ -#line 770 "cmDependsJavaParser.y" +#line 773 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3243 "cmDependsJavaParser.cxx" +#line 3246 "cmDependsJavaParser.cxx" break; case 85: /* ClassMemberDeclaration: FieldDeclaration */ -#line 779 "cmDependsJavaParser.y" +#line 782 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3254 "cmDependsJavaParser.cxx" +#line 3257 "cmDependsJavaParser.cxx" break; case 86: /* ClassMemberDeclaration: MethodDeclaration */ -#line 787 "cmDependsJavaParser.y" +#line 790 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3265 "cmDependsJavaParser.cxx" +#line 3268 "cmDependsJavaParser.cxx" break; case 87: /* FieldDeclaration: Modifiersopt Type VariableDeclarators jp_SEMICOL */ -#line 796 "cmDependsJavaParser.y" +#line 799 "cmDependsJavaParser.y" { jpElementStart(4); } -#line 3273 "cmDependsJavaParser.cxx" +#line 3276 "cmDependsJavaParser.cxx" break; case 88: /* VariableDeclarators: VariableDeclarator */ -#line 802 "cmDependsJavaParser.y" +#line 805 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3284 "cmDependsJavaParser.cxx" +#line 3287 "cmDependsJavaParser.cxx" break; case 89: /* VariableDeclarators: VariableDeclarators jp_COMMA VariableDeclarator */ -#line 810 "cmDependsJavaParser.y" +#line 813 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3295 "cmDependsJavaParser.cxx" +#line 3298 "cmDependsJavaParser.cxx" break; case 90: /* VariableDeclarator: VariableDeclaratorId */ -#line 819 "cmDependsJavaParser.y" +#line 822 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3306 "cmDependsJavaParser.cxx" +#line 3309 "cmDependsJavaParser.cxx" break; case 91: /* VariableDeclarator: VariableDeclaratorId jp_EQUALS VariableInitializer */ -#line 827 "cmDependsJavaParser.y" +#line 830 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3317 "cmDependsJavaParser.cxx" +#line 3320 "cmDependsJavaParser.cxx" break; case 92: /* VariableDeclaratorId: Identifier */ -#line 836 "cmDependsJavaParser.y" +#line 839 "cmDependsJavaParser.y" { jpElementStart(1); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); @@ -3325,77 +3328,77 @@ (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3329 "cmDependsJavaParser.cxx" +#line 3332 "cmDependsJavaParser.cxx" break; case 93: /* VariableDeclaratorId: VariableDeclaratorId jp_BRACKETSTART jp_BRACKETEND */ -#line 845 "cmDependsJavaParser.y" +#line 848 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3340 "cmDependsJavaParser.cxx" +#line 3343 "cmDependsJavaParser.cxx" break; case 94: /* VariableInitializer: Expression */ -#line 854 "cmDependsJavaParser.y" +#line 857 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3351 "cmDependsJavaParser.cxx" +#line 3354 "cmDependsJavaParser.cxx" break; case 95: /* VariableInitializer: ArrayInitializer */ -#line 862 "cmDependsJavaParser.y" +#line 865 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3362 "cmDependsJavaParser.cxx" +#line 3365 "cmDependsJavaParser.cxx" break; case 96: /* MethodDeclaration: MethodHeader jp_SEMICOL */ -#line 871 "cmDependsJavaParser.y" +#line 874 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3373 "cmDependsJavaParser.cxx" +#line 3376 "cmDependsJavaParser.cxx" break; case 97: /* MethodDeclaration: MethodHeader MethodBody */ -#line 879 "cmDependsJavaParser.y" +#line 882 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3384 "cmDependsJavaParser.cxx" +#line 3387 "cmDependsJavaParser.cxx" break; case 98: /* MethodDeclaration: MethodHeader MethodBody jp_SEMICOL */ -#line 887 "cmDependsJavaParser.y" +#line 890 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3395 "cmDependsJavaParser.cxx" +#line 3398 "cmDependsJavaParser.cxx" break; case 99: /* MethodHeader: Modifiersopt Type MethodDeclarator Throwsopt */ -#line 896 "cmDependsJavaParser.y" +#line 899 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -3403,11 +3406,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3407 "cmDependsJavaParser.cxx" +#line 3410 "cmDependsJavaParser.cxx" break; case 100: /* MethodHeader: Modifiersopt jp_VOID MethodDeclarator Throwsopt */ -#line 905 "cmDependsJavaParser.y" +#line 908 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -3415,22 +3418,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 3419 "cmDependsJavaParser.cxx" +#line 3422 "cmDependsJavaParser.cxx" break; case 101: /* Throwsopt: %empty */ -#line 914 "cmDependsJavaParser.y" +#line 917 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3430 "cmDependsJavaParser.cxx" +#line 3433 "cmDependsJavaParser.cxx" break; case 102: /* Throwsopt: Throws */ -#line 922 "cmDependsJavaParser.y" +#line 925 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3438,11 +3441,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3442 "cmDependsJavaParser.cxx" +#line 3445 "cmDependsJavaParser.cxx" break; case 103: /* MethodDeclarator: Identifier jp_PARESTART FormalParameterListopt jp_PAREEND */ -#line 932 "cmDependsJavaParser.y" +#line 935 "cmDependsJavaParser.y" { jpElementStart(4); yyGetParser->DeallocateParserType(&((yyvsp[-3].str))); @@ -3451,40 +3454,40 @@ yyGetParser->SetCurrentCombine(""); } -#line 3455 "cmDependsJavaParser.cxx" +#line 3458 "cmDependsJavaParser.cxx" break; case 104: /* MethodDeclarator: MethodDeclarator jp_BRACKETSTART jp_BRACKETEND */ -#line 942 "cmDependsJavaParser.y" +#line 945 "cmDependsJavaParser.y" { jpElementStart(3); } -#line 3464 "cmDependsJavaParser.cxx" +#line 3467 "cmDependsJavaParser.cxx" break; case 105: /* FormalParameterListopt: %empty */ -#line 948 "cmDependsJavaParser.y" +#line 951 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3475 "cmDependsJavaParser.cxx" +#line 3478 "cmDependsJavaParser.cxx" break; case 107: /* FormalParameterList: FormalParameter */ -#line 959 "cmDependsJavaParser.y" +#line 962 "cmDependsJavaParser.y" { jpElementStart(1); } -#line 3484 "cmDependsJavaParser.cxx" +#line 3487 "cmDependsJavaParser.cxx" break; case 108: /* FormalParameterList: FormalParameterList jp_COMMA FormalParameter */ -#line 965 "cmDependsJavaParser.y" +#line 968 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -3492,11 +3495,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3496 "cmDependsJavaParser.cxx" +#line 3499 "cmDependsJavaParser.cxx" break; case 109: /* FormalParameter: Modifiersopt Type VariableDeclaratorId */ -#line 975 "cmDependsJavaParser.y" +#line 978 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -3504,11 +3507,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3508 "cmDependsJavaParser.cxx" +#line 3511 "cmDependsJavaParser.cxx" break; case 110: /* Throws: jp_THROWS ClassTypeList */ -#line 985 "cmDependsJavaParser.y" +#line 988 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3516,20 +3519,20 @@ yyGetParser->SetCurrentCombine(""); } -#line 3520 "cmDependsJavaParser.cxx" +#line 3523 "cmDependsJavaParser.cxx" break; case 111: /* ClassTypeList: ClassType */ -#line 995 "cmDependsJavaParser.y" +#line 998 "cmDependsJavaParser.y" { jpElementStart(1); } -#line 3529 "cmDependsJavaParser.cxx" +#line 3532 "cmDependsJavaParser.cxx" break; case 112: /* ClassTypeList: ClassTypeList jp_COMMA ClassType */ -#line 1001 "cmDependsJavaParser.y" +#line 1004 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -3537,11 +3540,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3541 "cmDependsJavaParser.cxx" +#line 3544 "cmDependsJavaParser.cxx" break; case 113: /* MethodBody: Block */ -#line 1011 "cmDependsJavaParser.y" +#line 1014 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3549,11 +3552,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3553 "cmDependsJavaParser.cxx" +#line 3556 "cmDependsJavaParser.cxx" break; case 114: /* StaticInitializer: jp_STATIC Block */ -#line 1021 "cmDependsJavaParser.y" +#line 1024 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3561,11 +3564,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3565 "cmDependsJavaParser.cxx" +#line 3568 "cmDependsJavaParser.cxx" break; case 115: /* ConstructorDeclaration: Modifiersopt ConstructorDeclarator Throwsopt ConstructorBody */ -#line 1031 "cmDependsJavaParser.y" +#line 1034 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -3573,11 +3576,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3577 "cmDependsJavaParser.cxx" +#line 3580 "cmDependsJavaParser.cxx" break; case 116: /* ConstructorDeclaration: Modifiersopt ConstructorDeclarator Throwsopt ConstructorBody jp_SEMICOL */ -#line 1040 "cmDependsJavaParser.y" +#line 1043 "cmDependsJavaParser.y" { jpElementStart(5); jpCheckEmpty(5); @@ -3585,11 +3588,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3589 "cmDependsJavaParser.cxx" +#line 3592 "cmDependsJavaParser.cxx" break; case 117: /* ConstructorDeclarator: SimpleName jp_PARESTART FormalParameterListopt jp_PAREEND */ -#line 1050 "cmDependsJavaParser.y" +#line 1053 "cmDependsJavaParser.y" { jpElementStart(4); yyGetParser->DeallocateParserType(&((yyvsp[-3].str))); @@ -3598,11 +3601,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3602 "cmDependsJavaParser.cxx" +#line 3605 "cmDependsJavaParser.cxx" break; case 118: /* ConstructorBody: jp_CURLYSTART ExplicitConstructorInvocationopt BlockStatementsopt jp_CURLYEND */ -#line 1061 "cmDependsJavaParser.y" +#line 1064 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -3610,22 +3613,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 3614 "cmDependsJavaParser.cxx" +#line 3617 "cmDependsJavaParser.cxx" break; case 119: /* ExplicitConstructorInvocationopt: %empty */ -#line 1070 "cmDependsJavaParser.y" +#line 1073 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3625 "cmDependsJavaParser.cxx" +#line 3628 "cmDependsJavaParser.cxx" break; case 120: /* ExplicitConstructorInvocationopt: ExplicitConstructorInvocationopt ExplicitConstructorInvocation */ -#line 1078 "cmDependsJavaParser.y" +#line 1081 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3633,11 +3636,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3637 "cmDependsJavaParser.cxx" +#line 3640 "cmDependsJavaParser.cxx" break; case 121: /* ExplicitConstructorInvocation: jp_THIS jp_PARESTART ArgumentListopt jp_PAREEND jp_SEMICOL */ -#line 1088 "cmDependsJavaParser.y" +#line 1091 "cmDependsJavaParser.y" { jpElementStart(5); jpCheckEmpty(5); @@ -3645,11 +3648,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3649 "cmDependsJavaParser.cxx" +#line 3652 "cmDependsJavaParser.cxx" break; case 122: /* ExplicitConstructorInvocation: jp_SUPER jp_PARESTART ArgumentListopt jp_PAREEND jp_SEMICOL */ -#line 1097 "cmDependsJavaParser.y" +#line 1100 "cmDependsJavaParser.y" { jpElementStart(5); jpCheckEmpty(5); @@ -3657,22 +3660,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 3661 "cmDependsJavaParser.cxx" +#line 3664 "cmDependsJavaParser.cxx" break; case 123: /* InterfaceHeader: Modifiersopt jp_INTERFACE Identifier */ -#line 1107 "cmDependsJavaParser.y" +#line 1110 "cmDependsJavaParser.y" { yyGetParser->StartClass((yyvsp[0].str)); jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); jpCheckEmpty(3); } -#line 3672 "cmDependsJavaParser.cxx" +#line 3675 "cmDependsJavaParser.cxx" break; case 124: /* InterfaceDeclaration: InterfaceHeader ExtendsInterfacesopt InterfaceBody */ -#line 1116 "cmDependsJavaParser.y" +#line 1119 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3680,21 +3683,21 @@ yyGetParser->SetCurrentCombine(""); yyGetParser->EndClass(); } -#line 3684 "cmDependsJavaParser.cxx" +#line 3687 "cmDependsJavaParser.cxx" break; case 125: /* ExtendsInterfacesopt: %empty */ -#line 1125 "cmDependsJavaParser.y" +#line 1128 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3694 "cmDependsJavaParser.cxx" +#line 3697 "cmDependsJavaParser.cxx" break; case 126: /* ExtendsInterfacesopt: ExtendsInterfaces */ -#line 1132 "cmDependsJavaParser.y" +#line 1135 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3702,11 +3705,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3706 "cmDependsJavaParser.cxx" +#line 3709 "cmDependsJavaParser.cxx" break; case 127: /* ExtendsInterfaces: jp_EXTENDS InterfaceType */ -#line 1142 "cmDependsJavaParser.y" +#line 1145 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3714,11 +3717,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3718 "cmDependsJavaParser.cxx" +#line 3721 "cmDependsJavaParser.cxx" break; case 128: /* ExtendsInterfaces: ExtendsInterfaces jp_COMMA InterfaceType */ -#line 1151 "cmDependsJavaParser.y" +#line 1154 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -3726,11 +3729,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3730 "cmDependsJavaParser.cxx" +#line 3733 "cmDependsJavaParser.cxx" break; case 129: /* InterfaceBody: jp_CURLYSTART InterfaceMemberDeclarations jp_CURLYEND */ -#line 1161 "cmDependsJavaParser.y" +#line 1164 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -3738,33 +3741,33 @@ yyGetParser->SetCurrentCombine(""); } -#line 3742 "cmDependsJavaParser.cxx" +#line 3745 "cmDependsJavaParser.cxx" break; case 130: /* InterfaceMemberDeclarations: %empty */ -#line 1170 "cmDependsJavaParser.y" +#line 1173 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3753 "cmDependsJavaParser.cxx" +#line 3756 "cmDependsJavaParser.cxx" break; case 131: /* InterfaceMemberDeclarations: InterfaceMemberDeclarations InterfaceMemberDeclaration */ -#line 1178 "cmDependsJavaParser.y" +#line 1181 "cmDependsJavaParser.y" { jpElementStart(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3764 "cmDependsJavaParser.cxx" +#line 3767 "cmDependsJavaParser.cxx" break; case 132: /* InterfaceMemberDeclaration: ConstantDeclaration */ -#line 1187 "cmDependsJavaParser.y" +#line 1190 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3772,11 +3775,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3776 "cmDependsJavaParser.cxx" +#line 3779 "cmDependsJavaParser.cxx" break; case 133: /* InterfaceMemberDeclaration: AbstractMethodDeclaration */ -#line 1196 "cmDependsJavaParser.y" +#line 1199 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3784,11 +3787,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3788 "cmDependsJavaParser.cxx" +#line 3791 "cmDependsJavaParser.cxx" break; case 134: /* InterfaceMemberDeclaration: ClassDeclaration */ -#line 1205 "cmDependsJavaParser.y" +#line 1208 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3796,22 +3799,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 3800 "cmDependsJavaParser.cxx" +#line 3803 "cmDependsJavaParser.cxx" break; case 135: /* InterfaceMemberDeclaration: ClassDeclaration jp_SEMICOL */ -#line 1214 "cmDependsJavaParser.y" +#line 1217 "cmDependsJavaParser.y" { jpElementStart(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3811 "cmDependsJavaParser.cxx" +#line 3814 "cmDependsJavaParser.cxx" break; case 136: /* InterfaceMemberDeclaration: InterfaceDeclaration */ -#line 1222 "cmDependsJavaParser.y" +#line 1225 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3819,22 +3822,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 3823 "cmDependsJavaParser.cxx" +#line 3826 "cmDependsJavaParser.cxx" break; case 137: /* InterfaceMemberDeclaration: InterfaceDeclaration jp_SEMICOL */ -#line 1231 "cmDependsJavaParser.y" +#line 1234 "cmDependsJavaParser.y" { jpElementStart(2); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3834 "cmDependsJavaParser.cxx" +#line 3837 "cmDependsJavaParser.cxx" break; case 138: /* ConstantDeclaration: FieldDeclaration */ -#line 1240 "cmDependsJavaParser.y" +#line 1243 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3842,11 +3845,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3846 "cmDependsJavaParser.cxx" +#line 3849 "cmDependsJavaParser.cxx" break; case 139: /* AbstractMethodDeclaration: MethodHeader Semicols */ -#line 1250 "cmDependsJavaParser.y" +#line 1253 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3854,11 +3857,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3858 "cmDependsJavaParser.cxx" +#line 3861 "cmDependsJavaParser.cxx" break; case 140: /* Semicols: jp_SEMICOL */ -#line 1260 "cmDependsJavaParser.y" +#line 1263 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3866,11 +3869,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3870 "cmDependsJavaParser.cxx" +#line 3873 "cmDependsJavaParser.cxx" break; case 141: /* Semicols: Semicols jp_SEMICOL */ -#line 1269 "cmDependsJavaParser.y" +#line 1272 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3878,11 +3881,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3882 "cmDependsJavaParser.cxx" +#line 3885 "cmDependsJavaParser.cxx" break; case 142: /* ArrayInitializer: jp_CURLYSTART VariableInitializersOptional jp_CURLYEND */ -#line 1279 "cmDependsJavaParser.y" +#line 1282 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -3890,22 +3893,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 3894 "cmDependsJavaParser.cxx" +#line 3897 "cmDependsJavaParser.cxx" break; case 143: /* VariableInitializersOptional: %empty */ -#line 1288 "cmDependsJavaParser.y" +#line 1291 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3905 "cmDependsJavaParser.cxx" +#line 3908 "cmDependsJavaParser.cxx" break; case 144: /* VariableInitializersOptional: VariableInitializers */ -#line 1296 "cmDependsJavaParser.y" +#line 1299 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3913,11 +3916,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3917 "cmDependsJavaParser.cxx" +#line 3920 "cmDependsJavaParser.cxx" break; case 145: /* VariableInitializersOptional: VariableInitializers jp_COMMA */ -#line 1305 "cmDependsJavaParser.y" +#line 1308 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -3925,11 +3928,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3929 "cmDependsJavaParser.cxx" +#line 3932 "cmDependsJavaParser.cxx" break; case 146: /* VariableInitializers: VariableInitializer */ -#line 1315 "cmDependsJavaParser.y" +#line 1318 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3937,11 +3940,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3941 "cmDependsJavaParser.cxx" +#line 3944 "cmDependsJavaParser.cxx" break; case 147: /* VariableInitializers: VariableInitializers jp_COMMA VariableInitializer */ -#line 1324 "cmDependsJavaParser.y" +#line 1327 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -3949,33 +3952,33 @@ yyGetParser->SetCurrentCombine(""); } -#line 3953 "cmDependsJavaParser.cxx" +#line 3956 "cmDependsJavaParser.cxx" break; case 148: /* Block: jp_CURLYSTART BlockStatementsopt jp_CURLYEND */ -#line 1334 "cmDependsJavaParser.y" +#line 1337 "cmDependsJavaParser.y" { jpElementStart(4); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3964 "cmDependsJavaParser.cxx" +#line 3967 "cmDependsJavaParser.cxx" break; case 149: /* BlockStatementsopt: %empty */ -#line 1342 "cmDependsJavaParser.y" +#line 1345 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 3975 "cmDependsJavaParser.cxx" +#line 3978 "cmDependsJavaParser.cxx" break; case 150: /* BlockStatementsopt: BlockStatements */ -#line 1350 "cmDependsJavaParser.y" +#line 1353 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3983,11 +3986,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3987 "cmDependsJavaParser.cxx" +#line 3990 "cmDependsJavaParser.cxx" break; case 151: /* BlockStatements: BlockStatement */ -#line 1360 "cmDependsJavaParser.y" +#line 1363 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -3995,11 +3998,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 3999 "cmDependsJavaParser.cxx" +#line 4002 "cmDependsJavaParser.cxx" break; case 152: /* BlockStatements: BlockStatements BlockStatement */ -#line 1369 "cmDependsJavaParser.y" +#line 1372 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(2); @@ -4007,11 +4010,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4011 "cmDependsJavaParser.cxx" +#line 4014 "cmDependsJavaParser.cxx" break; case 153: /* BlockStatement: LocalVariableDeclarationStatement */ -#line 1379 "cmDependsJavaParser.y" +#line 1382 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4019,11 +4022,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4023 "cmDependsJavaParser.cxx" +#line 4026 "cmDependsJavaParser.cxx" break; case 154: /* BlockStatement: Statement */ -#line 1388 "cmDependsJavaParser.y" +#line 1391 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4031,11 +4034,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4035 "cmDependsJavaParser.cxx" +#line 4038 "cmDependsJavaParser.cxx" break; case 155: /* BlockStatement: ClassDeclaration */ -#line 1397 "cmDependsJavaParser.y" +#line 1400 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4043,11 +4046,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4047 "cmDependsJavaParser.cxx" +#line 4050 "cmDependsJavaParser.cxx" break; case 156: /* LocalVariableDeclarationStatement: LocalVariableDeclaration jp_SEMICOL */ -#line 1407 "cmDependsJavaParser.y" +#line 1410 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(2); @@ -4055,11 +4058,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4059 "cmDependsJavaParser.cxx" +#line 4062 "cmDependsJavaParser.cxx" break; case 157: /* LocalVariableDeclaration: Modifiers Type VariableDeclarators */ -#line 1417 "cmDependsJavaParser.y" +#line 1420 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(3); @@ -4067,11 +4070,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4071 "cmDependsJavaParser.cxx" +#line 4074 "cmDependsJavaParser.cxx" break; case 158: /* LocalVariableDeclaration: Type VariableDeclarators */ -#line 1426 "cmDependsJavaParser.y" +#line 1429 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(2); @@ -4079,11 +4082,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4083 "cmDependsJavaParser.cxx" +#line 4086 "cmDependsJavaParser.cxx" break; case 159: /* Statement: StatementWithoutTrailingSubstatement */ -#line 1436 "cmDependsJavaParser.y" +#line 1439 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4091,11 +4094,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4095 "cmDependsJavaParser.cxx" +#line 4098 "cmDependsJavaParser.cxx" break; case 160: /* Statement: LabeledStatement */ -#line 1445 "cmDependsJavaParser.y" +#line 1448 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4103,11 +4106,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4107 "cmDependsJavaParser.cxx" +#line 4110 "cmDependsJavaParser.cxx" break; case 161: /* Statement: IfThenStatement */ -#line 1454 "cmDependsJavaParser.y" +#line 1457 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4115,11 +4118,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4119 "cmDependsJavaParser.cxx" +#line 4122 "cmDependsJavaParser.cxx" break; case 162: /* Statement: IfThenElseStatement */ -#line 1463 "cmDependsJavaParser.y" +#line 1466 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4127,11 +4130,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4131 "cmDependsJavaParser.cxx" +#line 4134 "cmDependsJavaParser.cxx" break; case 163: /* Statement: WhileStatement */ -#line 1472 "cmDependsJavaParser.y" +#line 1475 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4139,11 +4142,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4143 "cmDependsJavaParser.cxx" +#line 4146 "cmDependsJavaParser.cxx" break; case 164: /* Statement: ForStatement */ -#line 1481 "cmDependsJavaParser.y" +#line 1484 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4151,11 +4154,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4155 "cmDependsJavaParser.cxx" +#line 4158 "cmDependsJavaParser.cxx" break; case 165: /* StatementNoShortIf: StatementWithoutTrailingSubstatement */ -#line 1491 "cmDependsJavaParser.y" +#line 1494 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4163,11 +4166,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4167 "cmDependsJavaParser.cxx" +#line 4170 "cmDependsJavaParser.cxx" break; case 166: /* StatementNoShortIf: LabeledStatementNoShortIf */ -#line 1500 "cmDependsJavaParser.y" +#line 1503 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4175,11 +4178,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4179 "cmDependsJavaParser.cxx" +#line 4182 "cmDependsJavaParser.cxx" break; case 167: /* StatementNoShortIf: IfThenElseStatementNoShortIf */ -#line 1509 "cmDependsJavaParser.y" +#line 1512 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4187,11 +4190,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4191 "cmDependsJavaParser.cxx" +#line 4194 "cmDependsJavaParser.cxx" break; case 168: /* StatementNoShortIf: WhileStatementNoShortIf */ -#line 1518 "cmDependsJavaParser.y" +#line 1521 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4199,11 +4202,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4203 "cmDependsJavaParser.cxx" +#line 4206 "cmDependsJavaParser.cxx" break; case 169: /* StatementNoShortIf: ForStatementNoShortIf */ -#line 1527 "cmDependsJavaParser.y" +#line 1530 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4211,11 +4214,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4215 "cmDependsJavaParser.cxx" +#line 4218 "cmDependsJavaParser.cxx" break; case 170: /* StatementWithoutTrailingSubstatement: Block */ -#line 1537 "cmDependsJavaParser.y" +#line 1540 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4223,11 +4226,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4227 "cmDependsJavaParser.cxx" +#line 4230 "cmDependsJavaParser.cxx" break; case 171: /* StatementWithoutTrailingSubstatement: EmptyStatement */ -#line 1546 "cmDependsJavaParser.y" +#line 1549 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4235,11 +4238,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4239 "cmDependsJavaParser.cxx" +#line 4242 "cmDependsJavaParser.cxx" break; case 172: /* StatementWithoutTrailingSubstatement: ExpressionStatement */ -#line 1555 "cmDependsJavaParser.y" +#line 1558 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4247,11 +4250,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4251 "cmDependsJavaParser.cxx" +#line 4254 "cmDependsJavaParser.cxx" break; case 173: /* StatementWithoutTrailingSubstatement: SwitchStatement */ -#line 1564 "cmDependsJavaParser.y" +#line 1567 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4259,11 +4262,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4263 "cmDependsJavaParser.cxx" +#line 4266 "cmDependsJavaParser.cxx" break; case 174: /* StatementWithoutTrailingSubstatement: DoStatement */ -#line 1573 "cmDependsJavaParser.y" +#line 1576 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4271,11 +4274,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4275 "cmDependsJavaParser.cxx" +#line 4278 "cmDependsJavaParser.cxx" break; case 175: /* StatementWithoutTrailingSubstatement: BreakStatement */ -#line 1582 "cmDependsJavaParser.y" +#line 1585 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4283,11 +4286,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4287 "cmDependsJavaParser.cxx" +#line 4290 "cmDependsJavaParser.cxx" break; case 176: /* StatementWithoutTrailingSubstatement: ContinueStatement */ -#line 1591 "cmDependsJavaParser.y" +#line 1594 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4295,11 +4298,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4299 "cmDependsJavaParser.cxx" +#line 4302 "cmDependsJavaParser.cxx" break; case 177: /* StatementWithoutTrailingSubstatement: ReturnStatement */ -#line 1600 "cmDependsJavaParser.y" +#line 1603 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4307,11 +4310,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4311 "cmDependsJavaParser.cxx" +#line 4314 "cmDependsJavaParser.cxx" break; case 178: /* StatementWithoutTrailingSubstatement: SynchronizedStatement */ -#line 1609 "cmDependsJavaParser.y" +#line 1612 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4319,11 +4322,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4323 "cmDependsJavaParser.cxx" +#line 4326 "cmDependsJavaParser.cxx" break; case 179: /* StatementWithoutTrailingSubstatement: ThrowStatement */ -#line 1618 "cmDependsJavaParser.y" +#line 1621 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4331,11 +4334,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4335 "cmDependsJavaParser.cxx" +#line 4338 "cmDependsJavaParser.cxx" break; case 180: /* StatementWithoutTrailingSubstatement: TryStatement */ -#line 1627 "cmDependsJavaParser.y" +#line 1630 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4343,11 +4346,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4347 "cmDependsJavaParser.cxx" +#line 4350 "cmDependsJavaParser.cxx" break; case 181: /* StatementWithoutTrailingSubstatement: AssertStatement */ -#line 1636 "cmDependsJavaParser.y" +#line 1639 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4355,11 +4358,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4359 "cmDependsJavaParser.cxx" +#line 4362 "cmDependsJavaParser.cxx" break; case 182: /* EmptyStatement: jp_SEMICOL */ -#line 1646 "cmDependsJavaParser.y" +#line 1649 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4367,11 +4370,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4371 "cmDependsJavaParser.cxx" +#line 4374 "cmDependsJavaParser.cxx" break; case 183: /* LabeledStatement: Identifier jp_COLON Statement */ -#line 1656 "cmDependsJavaParser.y" +#line 1659 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[-2].str))); @@ -4380,11 +4383,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4384 "cmDependsJavaParser.cxx" +#line 4387 "cmDependsJavaParser.cxx" break; case 184: /* LabeledStatementNoShortIf: Identifier jp_COLON StatementNoShortIf */ -#line 1667 "cmDependsJavaParser.y" +#line 1670 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -4392,11 +4395,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4396 "cmDependsJavaParser.cxx" +#line 4399 "cmDependsJavaParser.cxx" break; case 185: /* ExpressionStatement: StatementExpression jp_SEMICOL */ -#line 1677 "cmDependsJavaParser.y" +#line 1680 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -4404,11 +4407,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4408 "cmDependsJavaParser.cxx" +#line 4411 "cmDependsJavaParser.cxx" break; case 186: /* StatementExpression: Assignment */ -#line 1687 "cmDependsJavaParser.y" +#line 1690 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4416,11 +4419,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4420 "cmDependsJavaParser.cxx" +#line 4423 "cmDependsJavaParser.cxx" break; case 187: /* StatementExpression: PreIncrementExpression */ -#line 1696 "cmDependsJavaParser.y" +#line 1699 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4428,11 +4431,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4432 "cmDependsJavaParser.cxx" +#line 4435 "cmDependsJavaParser.cxx" break; case 188: /* StatementExpression: PreDecrementExpression */ -#line 1705 "cmDependsJavaParser.y" +#line 1708 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4440,11 +4443,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4444 "cmDependsJavaParser.cxx" +#line 4447 "cmDependsJavaParser.cxx" break; case 189: /* StatementExpression: PostIncrementExpression */ -#line 1714 "cmDependsJavaParser.y" +#line 1717 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4452,11 +4455,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4456 "cmDependsJavaParser.cxx" +#line 4459 "cmDependsJavaParser.cxx" break; case 190: /* StatementExpression: PostDecrementExpression */ -#line 1723 "cmDependsJavaParser.y" +#line 1726 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4464,11 +4467,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4468 "cmDependsJavaParser.cxx" +#line 4471 "cmDependsJavaParser.cxx" break; case 191: /* StatementExpression: MethodInvocation */ -#line 1732 "cmDependsJavaParser.y" +#line 1735 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4476,11 +4479,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4480 "cmDependsJavaParser.cxx" +#line 4483 "cmDependsJavaParser.cxx" break; case 192: /* StatementExpression: ClassInstanceCreationExpression */ -#line 1741 "cmDependsJavaParser.y" +#line 1744 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4488,11 +4491,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4492 "cmDependsJavaParser.cxx" +#line 4495 "cmDependsJavaParser.cxx" break; case 193: /* IfThenStatement: jp_IF jp_PARESTART Expression jp_PAREEND Statement */ -#line 1751 "cmDependsJavaParser.y" +#line 1754 "cmDependsJavaParser.y" { jpElementStart(5); jpCheckEmpty(5); @@ -4500,11 +4503,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4504 "cmDependsJavaParser.cxx" +#line 4507 "cmDependsJavaParser.cxx" break; case 194: /* IfThenElseStatement: jp_IF jp_PARESTART Expression jp_PAREEND StatementNoShortIf jp_ELSE Statement */ -#line 1761 "cmDependsJavaParser.y" +#line 1764 "cmDependsJavaParser.y" { jpElementStart(7); jpCheckEmpty(7); @@ -4512,11 +4515,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4516 "cmDependsJavaParser.cxx" +#line 4519 "cmDependsJavaParser.cxx" break; case 195: /* IfThenElseStatementNoShortIf: jp_IF jp_PARESTART Expression jp_PAREEND StatementNoShortIf jp_ELSE StatementNoShortIf */ -#line 1771 "cmDependsJavaParser.y" +#line 1774 "cmDependsJavaParser.y" { jpElementStart(7); jpCheckEmpty(7); @@ -4524,40 +4527,40 @@ yyGetParser->SetCurrentCombine(""); } -#line 4528 "cmDependsJavaParser.cxx" +#line 4531 "cmDependsJavaParser.cxx" break; case 196: /* SwitchStatement: jp_SWITCH jp_PARESTART Expression jp_PAREEND SwitchBlock */ -#line 1781 "cmDependsJavaParser.y" +#line 1784 "cmDependsJavaParser.y" { jpElementStart(5); } -#line 4537 "cmDependsJavaParser.cxx" +#line 4540 "cmDependsJavaParser.cxx" break; case 197: /* SwitchBlock: jp_CURLYSTART SwitchBlockStatementGroups SwitchLabelsopt jp_CURLYEND */ -#line 1788 "cmDependsJavaParser.y" +#line 1791 "cmDependsJavaParser.y" { jpElementStart(4); } -#line 4546 "cmDependsJavaParser.cxx" +#line 4549 "cmDependsJavaParser.cxx" break; case 198: /* SwitchLabelsopt: %empty */ -#line 1794 "cmDependsJavaParser.y" +#line 1797 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 4557 "cmDependsJavaParser.cxx" +#line 4560 "cmDependsJavaParser.cxx" break; case 199: /* SwitchLabelsopt: SwitchLabels */ -#line 1802 "cmDependsJavaParser.y" +#line 1805 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4565,22 +4568,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 4569 "cmDependsJavaParser.cxx" +#line 4572 "cmDependsJavaParser.cxx" break; case 200: /* SwitchBlockStatementGroups: %empty */ -#line 1811 "cmDependsJavaParser.y" +#line 1814 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 4580 "cmDependsJavaParser.cxx" +#line 4583 "cmDependsJavaParser.cxx" break; case 201: /* SwitchBlockStatementGroups: SwitchBlockStatementGroups SwitchBlockStatementGroup */ -#line 1819 "cmDependsJavaParser.y" +#line 1822 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -4588,11 +4591,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4592 "cmDependsJavaParser.cxx" +#line 4595 "cmDependsJavaParser.cxx" break; case 202: /* SwitchBlockStatementGroup: SwitchLabels BlockStatements */ -#line 1829 "cmDependsJavaParser.y" +#line 1832 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -4600,11 +4603,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4604 "cmDependsJavaParser.cxx" +#line 4607 "cmDependsJavaParser.cxx" break; case 203: /* SwitchLabels: SwitchLabel */ -#line 1839 "cmDependsJavaParser.y" +#line 1842 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4612,11 +4615,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4616 "cmDependsJavaParser.cxx" +#line 4619 "cmDependsJavaParser.cxx" break; case 204: /* SwitchLabels: SwitchLabels SwitchLabel */ -#line 1848 "cmDependsJavaParser.y" +#line 1851 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -4624,11 +4627,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4628 "cmDependsJavaParser.cxx" +#line 4631 "cmDependsJavaParser.cxx" break; case 205: /* SwitchLabel: jp_CASE ConstantExpression jp_COLON */ -#line 1858 "cmDependsJavaParser.y" +#line 1861 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -4636,11 +4639,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4640 "cmDependsJavaParser.cxx" +#line 4643 "cmDependsJavaParser.cxx" break; case 206: /* SwitchLabel: jp_DEFAULT jp_COLON */ -#line 1867 "cmDependsJavaParser.y" +#line 1870 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -4648,58 +4651,58 @@ yyGetParser->SetCurrentCombine(""); } -#line 4652 "cmDependsJavaParser.cxx" +#line 4655 "cmDependsJavaParser.cxx" break; case 207: /* WhileStatement: jp_WHILE jp_PARESTART Expression jp_PAREEND Statement */ -#line 1877 "cmDependsJavaParser.y" +#line 1880 "cmDependsJavaParser.y" { jpElementStart(5); } -#line 4661 "cmDependsJavaParser.cxx" +#line 4664 "cmDependsJavaParser.cxx" break; case 208: /* WhileStatementNoShortIf: jp_WHILE jp_PARESTART Expression jp_PAREEND StatementNoShortIf */ -#line 1884 "cmDependsJavaParser.y" +#line 1887 "cmDependsJavaParser.y" { jpElementStart(5); } -#line 4670 "cmDependsJavaParser.cxx" +#line 4673 "cmDependsJavaParser.cxx" break; case 209: /* DoStatement: jp_DO Statement jp_WHILE jp_PARESTART Expression jp_PAREEND jp_SEMICOL */ -#line 1891 "cmDependsJavaParser.y" +#line 1894 "cmDependsJavaParser.y" { jpElementStart(7); } -#line 4679 "cmDependsJavaParser.cxx" +#line 4682 "cmDependsJavaParser.cxx" break; case 210: /* ForStatement: jp_FOR jp_PARESTART ForInitopt jp_SEMICOL Expressionopt jp_SEMICOL ForUpdateopt jp_PAREEND Statement */ -#line 1899 "cmDependsJavaParser.y" +#line 1902 "cmDependsJavaParser.y" { jpElementStart(9); } -#line 4688 "cmDependsJavaParser.cxx" +#line 4691 "cmDependsJavaParser.cxx" break; case 211: /* ForUpdateopt: %empty */ -#line 1905 "cmDependsJavaParser.y" +#line 1908 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 4699 "cmDependsJavaParser.cxx" +#line 4702 "cmDependsJavaParser.cxx" break; case 212: /* ForUpdateopt: ForUpdate */ -#line 1913 "cmDependsJavaParser.y" +#line 1916 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4707,22 +4710,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 4711 "cmDependsJavaParser.cxx" +#line 4714 "cmDependsJavaParser.cxx" break; case 213: /* ForInitopt: %empty */ -#line 1922 "cmDependsJavaParser.y" +#line 1925 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 4722 "cmDependsJavaParser.cxx" +#line 4725 "cmDependsJavaParser.cxx" break; case 214: /* ForInitopt: ForInit */ -#line 1930 "cmDependsJavaParser.y" +#line 1933 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4730,33 +4733,33 @@ yyGetParser->SetCurrentCombine(""); } -#line 4734 "cmDependsJavaParser.cxx" +#line 4737 "cmDependsJavaParser.cxx" break; case 215: /* ForStatementNoShortIf: jp_FOR jp_PARESTART ForInitopt jp_SEMICOL Expressionopt jp_SEMICOL ForUpdateopt jp_PAREEND StatementNoShortIf */ -#line 1941 "cmDependsJavaParser.y" +#line 1944 "cmDependsJavaParser.y" { jpElementStart(9); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 4745 "cmDependsJavaParser.cxx" +#line 4748 "cmDependsJavaParser.cxx" break; case 216: /* Expressionopt: %empty */ -#line 1949 "cmDependsJavaParser.y" +#line 1952 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 4756 "cmDependsJavaParser.cxx" +#line 4759 "cmDependsJavaParser.cxx" break; case 217: /* Expressionopt: Expression */ -#line 1957 "cmDependsJavaParser.y" +#line 1960 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4764,11 +4767,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4768 "cmDependsJavaParser.cxx" +#line 4771 "cmDependsJavaParser.cxx" break; case 218: /* ForInit: StatementExpressionList */ -#line 1967 "cmDependsJavaParser.y" +#line 1970 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4776,11 +4779,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4780 "cmDependsJavaParser.cxx" +#line 4783 "cmDependsJavaParser.cxx" break; case 219: /* ForInit: LocalVariableDeclaration */ -#line 1976 "cmDependsJavaParser.y" +#line 1979 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4788,11 +4791,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4792 "cmDependsJavaParser.cxx" +#line 4795 "cmDependsJavaParser.cxx" break; case 220: /* ForUpdate: StatementExpressionList */ -#line 1986 "cmDependsJavaParser.y" +#line 1989 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4800,11 +4803,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4804 "cmDependsJavaParser.cxx" +#line 4807 "cmDependsJavaParser.cxx" break; case 221: /* StatementExpressionList: StatementExpression */ -#line 1996 "cmDependsJavaParser.y" +#line 1999 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4812,11 +4815,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4816 "cmDependsJavaParser.cxx" +#line 4819 "cmDependsJavaParser.cxx" break; case 222: /* StatementExpressionList: StatementExpressionList jp_COMMA StatementExpression */ -#line 2005 "cmDependsJavaParser.y" +#line 2008 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -4824,11 +4827,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4828 "cmDependsJavaParser.cxx" +#line 4831 "cmDependsJavaParser.cxx" break; case 223: /* AssertStatement: jp_ASSERT Expression jp_SEMICOL */ -#line 2015 "cmDependsJavaParser.y" +#line 2018 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -4836,11 +4839,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4840 "cmDependsJavaParser.cxx" +#line 4843 "cmDependsJavaParser.cxx" break; case 224: /* AssertStatement: jp_ASSERT Expression jp_COLON Expression jp_SEMICOL */ -#line 2024 "cmDependsJavaParser.y" +#line 2027 "cmDependsJavaParser.y" { jpElementStart(5); jpCheckEmpty(5); @@ -4848,11 +4851,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4852 "cmDependsJavaParser.cxx" +#line 4855 "cmDependsJavaParser.cxx" break; case 225: /* BreakStatement: jp_BREAK Identifieropt jp_SEMICOL */ -#line 2034 "cmDependsJavaParser.y" +#line 2037 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[-1].str))); @@ -4861,31 +4864,31 @@ yyGetParser->SetCurrentCombine(""); } -#line 4865 "cmDependsJavaParser.cxx" +#line 4868 "cmDependsJavaParser.cxx" break; case 226: /* Identifieropt: %empty */ -#line 2044 "cmDependsJavaParser.y" +#line 2047 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 4876 "cmDependsJavaParser.cxx" +#line 4879 "cmDependsJavaParser.cxx" break; case 227: /* Identifieropt: Identifier */ -#line 2052 "cmDependsJavaParser.y" +#line 2055 "cmDependsJavaParser.y" { jpElementStart(1); } -#line 4885 "cmDependsJavaParser.cxx" +#line 4888 "cmDependsJavaParser.cxx" break; case 228: /* ContinueStatement: jp_CONTINUE Identifieropt jp_SEMICOL */ -#line 2059 "cmDependsJavaParser.y" +#line 2062 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[-1].str))); @@ -4894,11 +4897,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4898 "cmDependsJavaParser.cxx" +#line 4901 "cmDependsJavaParser.cxx" break; case 229: /* ReturnStatement: jp_RETURN Expressionopt jp_SEMICOL */ -#line 2070 "cmDependsJavaParser.y" +#line 2073 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -4906,11 +4909,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4910 "cmDependsJavaParser.cxx" +#line 4913 "cmDependsJavaParser.cxx" break; case 230: /* ThrowStatement: jp_THROW Expression jp_SEMICOL */ -#line 2080 "cmDependsJavaParser.y" +#line 2083 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -4918,11 +4921,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4922 "cmDependsJavaParser.cxx" +#line 4925 "cmDependsJavaParser.cxx" break; case 231: /* SynchronizedStatement: jp_SYNCHRONIZED jp_PARESTART Expression jp_PAREEND Block */ -#line 2090 "cmDependsJavaParser.y" +#line 2093 "cmDependsJavaParser.y" { jpElementStart(5); jpCheckEmpty(5); @@ -4930,11 +4933,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4934 "cmDependsJavaParser.cxx" +#line 4937 "cmDependsJavaParser.cxx" break; case 232: /* TryStatement: jp_TRY Block Catches */ -#line 2100 "cmDependsJavaParser.y" +#line 2103 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -4942,11 +4945,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4946 "cmDependsJavaParser.cxx" +#line 4949 "cmDependsJavaParser.cxx" break; case 233: /* TryStatement: jp_TRY Block Catchesopt Finally */ -#line 2109 "cmDependsJavaParser.y" +#line 2112 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -4954,22 +4957,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 4958 "cmDependsJavaParser.cxx" +#line 4961 "cmDependsJavaParser.cxx" break; case 234: /* Catchesopt: %empty */ -#line 2118 "cmDependsJavaParser.y" +#line 2121 "cmDependsJavaParser.y" { jpElementStart(1); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 4969 "cmDependsJavaParser.cxx" +#line 4972 "cmDependsJavaParser.cxx" break; case 235: /* Catchesopt: Catches */ -#line 2126 "cmDependsJavaParser.y" +#line 2129 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4977,11 +4980,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4981 "cmDependsJavaParser.cxx" +#line 4984 "cmDependsJavaParser.cxx" break; case 236: /* Catches: CatchClause */ -#line 2136 "cmDependsJavaParser.y" +#line 2139 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -4989,11 +4992,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 4993 "cmDependsJavaParser.cxx" +#line 4996 "cmDependsJavaParser.cxx" break; case 237: /* Catches: Catches CatchClause */ -#line 2145 "cmDependsJavaParser.y" +#line 2148 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5001,20 +5004,20 @@ yyGetParser->SetCurrentCombine(""); } -#line 5005 "cmDependsJavaParser.cxx" +#line 5008 "cmDependsJavaParser.cxx" break; case 238: /* CatchClause: jp_CATCH jp_PARESTART FormalParameter jp_PAREEND Block */ -#line 2155 "cmDependsJavaParser.y" +#line 2158 "cmDependsJavaParser.y" { jpElementStart(5); } -#line 5014 "cmDependsJavaParser.cxx" +#line 5017 "cmDependsJavaParser.cxx" break; case 239: /* Finally: jp_FINALLY Block */ -#line 2162 "cmDependsJavaParser.y" +#line 2165 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5022,11 +5025,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5026 "cmDependsJavaParser.cxx" +#line 5029 "cmDependsJavaParser.cxx" break; case 240: /* Primary: PrimaryNoNewArray */ -#line 2172 "cmDependsJavaParser.y" +#line 2175 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5034,11 +5037,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5038 "cmDependsJavaParser.cxx" +#line 5041 "cmDependsJavaParser.cxx" break; case 241: /* Primary: ArrayCreationExpression */ -#line 2181 "cmDependsJavaParser.y" +#line 2184 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5046,11 +5049,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5050 "cmDependsJavaParser.cxx" +#line 5053 "cmDependsJavaParser.cxx" break; case 242: /* PrimaryNoNewArray: Literal */ -#line 2191 "cmDependsJavaParser.y" +#line 2194 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5058,20 +5061,20 @@ yyGetParser->SetCurrentCombine(""); } -#line 5062 "cmDependsJavaParser.cxx" +#line 5065 "cmDependsJavaParser.cxx" break; case 243: /* PrimaryNoNewArray: jp_THIS */ -#line 2200 "cmDependsJavaParser.y" +#line 2203 "cmDependsJavaParser.y" { jpElementStart(1); } -#line 5071 "cmDependsJavaParser.cxx" +#line 5074 "cmDependsJavaParser.cxx" break; case 244: /* PrimaryNoNewArray: jp_PARESTART Expression jp_PAREEND */ -#line 2206 "cmDependsJavaParser.y" +#line 2209 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5079,11 +5082,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5083 "cmDependsJavaParser.cxx" +#line 5086 "cmDependsJavaParser.cxx" break; case 245: /* PrimaryNoNewArray: ClassInstanceCreationExpression */ -#line 2215 "cmDependsJavaParser.y" +#line 2218 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5091,11 +5094,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5095 "cmDependsJavaParser.cxx" +#line 5098 "cmDependsJavaParser.cxx" break; case 246: /* PrimaryNoNewArray: FieldAccess */ -#line 2224 "cmDependsJavaParser.y" +#line 2227 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5103,11 +5106,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5107 "cmDependsJavaParser.cxx" +#line 5110 "cmDependsJavaParser.cxx" break; case 247: /* PrimaryNoNewArray: MethodInvocation */ -#line 2233 "cmDependsJavaParser.y" +#line 2236 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5115,11 +5118,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5119 "cmDependsJavaParser.cxx" +#line 5122 "cmDependsJavaParser.cxx" break; case 248: /* PrimaryNoNewArray: ArrayAccess */ -#line 2242 "cmDependsJavaParser.y" +#line 2245 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5127,11 +5130,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5131 "cmDependsJavaParser.cxx" +#line 5134 "cmDependsJavaParser.cxx" break; case 249: /* ClassInstanceCreationExpression: New ClassType jp_PARESTART ArgumentListopt jp_PAREEND ClassBodyOpt */ -#line 2252 "cmDependsJavaParser.y" +#line 2255 "cmDependsJavaParser.y" { jpElementStart(6); jpCheckEmpty(6); @@ -5139,22 +5142,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 5143 "cmDependsJavaParser.cxx" +#line 5146 "cmDependsJavaParser.cxx" break; case 250: /* ClassBodyOpt: %empty */ -#line 2261 "cmDependsJavaParser.y" +#line 2264 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 5154 "cmDependsJavaParser.cxx" +#line 5157 "cmDependsJavaParser.cxx" break; case 251: /* ClassBodyOpt: ClassBody */ -#line 2269 "cmDependsJavaParser.y" +#line 2272 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5162,22 +5165,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 5166 "cmDependsJavaParser.cxx" +#line 5169 "cmDependsJavaParser.cxx" break; case 252: /* ArgumentListopt: %empty */ -#line 2278 "cmDependsJavaParser.y" +#line 2281 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 5177 "cmDependsJavaParser.cxx" +#line 5180 "cmDependsJavaParser.cxx" break; case 253: /* ArgumentListopt: ArgumentList */ -#line 2286 "cmDependsJavaParser.y" +#line 2289 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5185,11 +5188,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5189 "cmDependsJavaParser.cxx" +#line 5192 "cmDependsJavaParser.cxx" break; case 254: /* ArgumentList: Expression */ -#line 2296 "cmDependsJavaParser.y" +#line 2299 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5197,11 +5200,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5201 "cmDependsJavaParser.cxx" +#line 5204 "cmDependsJavaParser.cxx" break; case 255: /* ArgumentList: ArgumentList jp_COMMA Expression */ -#line 2305 "cmDependsJavaParser.y" +#line 2308 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5209,11 +5212,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5213 "cmDependsJavaParser.cxx" +#line 5216 "cmDependsJavaParser.cxx" break; case 256: /* ArrayCreationExpression: New PrimitiveType DimExprs Dimsopt */ -#line 2315 "cmDependsJavaParser.y" +#line 2318 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -5221,11 +5224,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5225 "cmDependsJavaParser.cxx" +#line 5228 "cmDependsJavaParser.cxx" break; case 257: /* ArrayCreationExpression: New ClassOrInterfaceType DimExprs Dimsopt */ -#line 2324 "cmDependsJavaParser.y" +#line 2327 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -5233,11 +5236,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5237 "cmDependsJavaParser.cxx" +#line 5240 "cmDependsJavaParser.cxx" break; case 258: /* ArrayCreationExpression: New PrimitiveType Dims ArrayInitializer */ -#line 2333 "cmDependsJavaParser.y" +#line 2336 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -5245,11 +5248,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5249 "cmDependsJavaParser.cxx" +#line 5252 "cmDependsJavaParser.cxx" break; case 259: /* ArrayCreationExpression: New ClassOrInterfaceType Dims ArrayInitializer */ -#line 2342 "cmDependsJavaParser.y" +#line 2345 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -5257,22 +5260,22 @@ yyGetParser->SetCurrentCombine(""); } -#line 5261 "cmDependsJavaParser.cxx" +#line 5264 "cmDependsJavaParser.cxx" break; case 260: /* Dimsopt: %empty */ -#line 2351 "cmDependsJavaParser.y" +#line 2354 "cmDependsJavaParser.y" { jpElementStart(0); (yyval.str) = 0; yyGetParser->SetCurrentCombine(""); } -#line 5272 "cmDependsJavaParser.cxx" +#line 5275 "cmDependsJavaParser.cxx" break; case 261: /* Dimsopt: Dims */ -#line 2359 "cmDependsJavaParser.y" +#line 2362 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5280,11 +5283,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5284 "cmDependsJavaParser.cxx" +#line 5287 "cmDependsJavaParser.cxx" break; case 262: /* DimExprs: DimExpr */ -#line 2369 "cmDependsJavaParser.y" +#line 2372 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5292,11 +5295,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5296 "cmDependsJavaParser.cxx" +#line 5299 "cmDependsJavaParser.cxx" break; case 263: /* DimExprs: DimExprs DimExpr */ -#line 2378 "cmDependsJavaParser.y" +#line 2381 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5304,11 +5307,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5308 "cmDependsJavaParser.cxx" +#line 5311 "cmDependsJavaParser.cxx" break; case 264: /* DimExpr: jp_BRACKETSTART Expression jp_BRACKETEND */ -#line 2388 "cmDependsJavaParser.y" +#line 2391 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5316,29 +5319,29 @@ yyGetParser->SetCurrentCombine(""); } -#line 5320 "cmDependsJavaParser.cxx" +#line 5323 "cmDependsJavaParser.cxx" break; case 265: /* Dims: jp_BRACKETSTART jp_BRACKETEND */ -#line 2398 "cmDependsJavaParser.y" +#line 2401 "cmDependsJavaParser.y" { jpElementStart(2); } -#line 5329 "cmDependsJavaParser.cxx" +#line 5332 "cmDependsJavaParser.cxx" break; case 266: /* Dims: Dims jp_BRACKETSTART jp_BRACKETEND */ -#line 2404 "cmDependsJavaParser.y" +#line 2407 "cmDependsJavaParser.y" { jpElementStart(3); } -#line 5338 "cmDependsJavaParser.cxx" +#line 5341 "cmDependsJavaParser.cxx" break; case 267: /* FieldAccess: Primary jp_DOT Identifier */ -#line 2411 "cmDependsJavaParser.y" +#line 2414 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); @@ -5347,11 +5350,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5351 "cmDependsJavaParser.cxx" +#line 5354 "cmDependsJavaParser.cxx" break; case 268: /* FieldAccess: jp_SUPER jp_DOT Identifier */ -#line 2421 "cmDependsJavaParser.y" +#line 2424 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); @@ -5360,11 +5363,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5364 "cmDependsJavaParser.cxx" +#line 5367 "cmDependsJavaParser.cxx" break; case 269: /* FieldAccess: jp_THIS jp_DOT Identifier */ -#line 2431 "cmDependsJavaParser.y" +#line 2434 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); @@ -5373,11 +5376,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5377 "cmDependsJavaParser.cxx" +#line 5380 "cmDependsJavaParser.cxx" break; case 270: /* FieldAccess: Primary jp_DOT jp_THIS */ -#line 2441 "cmDependsJavaParser.y" +#line 2444 "cmDependsJavaParser.y" { jpElementStart(3); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); @@ -5386,11 +5389,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5390 "cmDependsJavaParser.cxx" +#line 5393 "cmDependsJavaParser.cxx" break; case 271: /* MethodInvocation: Name jp_PARESTART ArgumentListopt jp_PAREEND */ -#line 2452 "cmDependsJavaParser.y" +#line 2455 "cmDependsJavaParser.y" { jpElementStart(4); yyGetParser->DeallocateParserType(&((yyvsp[-3].str))); @@ -5399,11 +5402,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5403 "cmDependsJavaParser.cxx" +#line 5406 "cmDependsJavaParser.cxx" break; case 272: /* MethodInvocation: Primary jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND */ -#line 2462 "cmDependsJavaParser.y" +#line 2465 "cmDependsJavaParser.y" { jpElementStart(6); yyGetParser->DeallocateParserType(&((yyvsp[-5].str))); @@ -5413,11 +5416,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5417 "cmDependsJavaParser.cxx" +#line 5420 "cmDependsJavaParser.cxx" break; case 273: /* MethodInvocation: jp_SUPER jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND */ -#line 2473 "cmDependsJavaParser.y" +#line 2476 "cmDependsJavaParser.y" { jpElementStart(6); yyGetParser->DeallocateParserType(&((yyvsp[-3].str))); @@ -5426,11 +5429,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5430 "cmDependsJavaParser.cxx" +#line 5433 "cmDependsJavaParser.cxx" break; case 274: /* MethodInvocation: jp_THIS jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND */ -#line 2483 "cmDependsJavaParser.y" +#line 2486 "cmDependsJavaParser.y" { jpElementStart(6); yyGetParser->DeallocateParserType(&((yyvsp[-3].str))); @@ -5439,11 +5442,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5443 "cmDependsJavaParser.cxx" +#line 5446 "cmDependsJavaParser.cxx" break; case 275: /* ArrayAccess: Name jp_BRACKETSTART Expression jp_BRACKETEND */ -#line 2494 "cmDependsJavaParser.y" +#line 2497 "cmDependsJavaParser.y" { jpElementStart(4); yyGetParser->DeallocateParserType(&((yyvsp[-3].str))); @@ -5452,11 +5455,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5456 "cmDependsJavaParser.cxx" +#line 5459 "cmDependsJavaParser.cxx" break; case 276: /* ArrayAccess: PrimaryNoNewArray jp_BRACKETSTART Expression jp_BRACKETEND */ -#line 2504 "cmDependsJavaParser.y" +#line 2507 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -5464,11 +5467,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5468 "cmDependsJavaParser.cxx" +#line 5471 "cmDependsJavaParser.cxx" break; case 277: /* PostfixExpression: Primary */ -#line 2514 "cmDependsJavaParser.y" +#line 2517 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5476,11 +5479,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5480 "cmDependsJavaParser.cxx" +#line 5483 "cmDependsJavaParser.cxx" break; case 278: /* PostfixExpression: Name */ -#line 2523 "cmDependsJavaParser.y" +#line 2526 "cmDependsJavaParser.y" { jpElementStart(1); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); @@ -5488,11 +5491,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5492 "cmDependsJavaParser.cxx" +#line 5495 "cmDependsJavaParser.cxx" break; case 279: /* PostfixExpression: ArrayType jp_DOT jp_CLASS */ -#line 2532 "cmDependsJavaParser.y" +#line 2535 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5500,11 +5503,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5504 "cmDependsJavaParser.cxx" +#line 5507 "cmDependsJavaParser.cxx" break; case 280: /* PostfixExpression: PostIncrementExpression */ -#line 2541 "cmDependsJavaParser.y" +#line 2544 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5512,11 +5515,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5516 "cmDependsJavaParser.cxx" +#line 5519 "cmDependsJavaParser.cxx" break; case 281: /* PostfixExpression: PostDecrementExpression */ -#line 2550 "cmDependsJavaParser.y" +#line 2553 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5524,11 +5527,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5528 "cmDependsJavaParser.cxx" +#line 5531 "cmDependsJavaParser.cxx" break; case 282: /* PostIncrementExpression: PostfixExpression jp_PLUSPLUS */ -#line 2560 "cmDependsJavaParser.y" +#line 2563 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5536,11 +5539,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5540 "cmDependsJavaParser.cxx" +#line 5543 "cmDependsJavaParser.cxx" break; case 283: /* PostDecrementExpression: PostfixExpression jp_MINUSMINUS */ -#line 2570 "cmDependsJavaParser.y" +#line 2573 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5548,11 +5551,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5552 "cmDependsJavaParser.cxx" +#line 5555 "cmDependsJavaParser.cxx" break; case 284: /* UnaryExpression: PreIncrementExpression */ -#line 2580 "cmDependsJavaParser.y" +#line 2583 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5560,11 +5563,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5564 "cmDependsJavaParser.cxx" +#line 5567 "cmDependsJavaParser.cxx" break; case 285: /* UnaryExpression: PreDecrementExpression */ -#line 2589 "cmDependsJavaParser.y" +#line 2592 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5572,11 +5575,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5576 "cmDependsJavaParser.cxx" +#line 5579 "cmDependsJavaParser.cxx" break; case 286: /* UnaryExpression: jp_PLUS UnaryExpression */ -#line 2598 "cmDependsJavaParser.y" +#line 2601 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5584,11 +5587,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5588 "cmDependsJavaParser.cxx" +#line 5591 "cmDependsJavaParser.cxx" break; case 287: /* UnaryExpression: jp_MINUS UnaryExpression */ -#line 2607 "cmDependsJavaParser.y" +#line 2610 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5596,11 +5599,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5600 "cmDependsJavaParser.cxx" +#line 5603 "cmDependsJavaParser.cxx" break; case 288: /* UnaryExpression: UnaryExpressionNotPlusMinus */ -#line 2616 "cmDependsJavaParser.y" +#line 2619 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5608,11 +5611,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5612 "cmDependsJavaParser.cxx" +#line 5615 "cmDependsJavaParser.cxx" break; case 289: /* PreIncrementExpression: jp_PLUSPLUS UnaryExpression */ -#line 2626 "cmDependsJavaParser.y" +#line 2629 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5620,11 +5623,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5624 "cmDependsJavaParser.cxx" +#line 5627 "cmDependsJavaParser.cxx" break; case 290: /* PreDecrementExpression: jp_MINUSMINUS UnaryExpression */ -#line 2636 "cmDependsJavaParser.y" +#line 2639 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5632,11 +5635,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5636 "cmDependsJavaParser.cxx" +#line 5639 "cmDependsJavaParser.cxx" break; case 291: /* UnaryExpressionNotPlusMinus: PostfixExpression */ -#line 2646 "cmDependsJavaParser.y" +#line 2649 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5644,11 +5647,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5648 "cmDependsJavaParser.cxx" +#line 5651 "cmDependsJavaParser.cxx" break; case 292: /* UnaryExpressionNotPlusMinus: jp_TILDE UnaryExpression */ -#line 2655 "cmDependsJavaParser.y" +#line 2658 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5656,11 +5659,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5660 "cmDependsJavaParser.cxx" +#line 5663 "cmDependsJavaParser.cxx" break; case 293: /* UnaryExpressionNotPlusMinus: jp_EXCLAMATION UnaryExpression */ -#line 2664 "cmDependsJavaParser.y" +#line 2667 "cmDependsJavaParser.y" { jpElementStart(2); jpCheckEmpty(2); @@ -5668,11 +5671,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5672 "cmDependsJavaParser.cxx" +#line 5675 "cmDependsJavaParser.cxx" break; case 294: /* UnaryExpressionNotPlusMinus: CastExpression */ -#line 2673 "cmDependsJavaParser.y" +#line 2676 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5680,11 +5683,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5684 "cmDependsJavaParser.cxx" +#line 5687 "cmDependsJavaParser.cxx" break; case 295: /* CastExpression: jp_PARESTART PrimitiveType Dimsopt jp_PAREEND UnaryExpression */ -#line 2683 "cmDependsJavaParser.y" +#line 2686 "cmDependsJavaParser.y" { jpElementStart(5); jpCheckEmpty(5); @@ -5692,11 +5695,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5696 "cmDependsJavaParser.cxx" +#line 5699 "cmDependsJavaParser.cxx" break; case 296: /* CastExpression: jp_PARESTART Expression jp_PAREEND UnaryExpressionNotPlusMinus */ -#line 2692 "cmDependsJavaParser.y" +#line 2695 "cmDependsJavaParser.y" { jpElementStart(4); jpCheckEmpty(4); @@ -5704,20 +5707,20 @@ yyGetParser->SetCurrentCombine(""); } -#line 5708 "cmDependsJavaParser.cxx" +#line 5711 "cmDependsJavaParser.cxx" break; case 297: /* CastExpression: jp_PARESTART Name Dims jp_PAREEND UnaryExpressionNotPlusMinus */ -#line 2701 "cmDependsJavaParser.y" +#line 2704 "cmDependsJavaParser.y" { jpElementStart(5); } -#line 5717 "cmDependsJavaParser.cxx" +#line 5720 "cmDependsJavaParser.cxx" break; case 298: /* MultiplicativeExpression: UnaryExpression */ -#line 2708 "cmDependsJavaParser.y" +#line 2711 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5725,11 +5728,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5729 "cmDependsJavaParser.cxx" +#line 5732 "cmDependsJavaParser.cxx" break; case 299: /* MultiplicativeExpression: MultiplicativeExpression jp_TIMES UnaryExpression */ -#line 2717 "cmDependsJavaParser.y" +#line 2720 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5737,11 +5740,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5741 "cmDependsJavaParser.cxx" +#line 5744 "cmDependsJavaParser.cxx" break; case 300: /* MultiplicativeExpression: MultiplicativeExpression jp_DIVIDE UnaryExpression */ -#line 2726 "cmDependsJavaParser.y" +#line 2729 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5749,11 +5752,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5753 "cmDependsJavaParser.cxx" +#line 5756 "cmDependsJavaParser.cxx" break; case 301: /* MultiplicativeExpression: MultiplicativeExpression jp_PERCENT UnaryExpression */ -#line 2735 "cmDependsJavaParser.y" +#line 2738 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5761,11 +5764,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5765 "cmDependsJavaParser.cxx" +#line 5768 "cmDependsJavaParser.cxx" break; case 302: /* AdditiveExpression: MultiplicativeExpression */ -#line 2745 "cmDependsJavaParser.y" +#line 2748 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5773,11 +5776,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5777 "cmDependsJavaParser.cxx" +#line 5780 "cmDependsJavaParser.cxx" break; case 303: /* AdditiveExpression: AdditiveExpression jp_PLUS MultiplicativeExpression */ -#line 2754 "cmDependsJavaParser.y" +#line 2757 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5785,11 +5788,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5789 "cmDependsJavaParser.cxx" +#line 5792 "cmDependsJavaParser.cxx" break; case 304: /* AdditiveExpression: AdditiveExpression jp_MINUS MultiplicativeExpression */ -#line 2763 "cmDependsJavaParser.y" +#line 2766 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5797,11 +5800,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5801 "cmDependsJavaParser.cxx" +#line 5804 "cmDependsJavaParser.cxx" break; case 305: /* ShiftExpression: AdditiveExpression */ -#line 2773 "cmDependsJavaParser.y" +#line 2776 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5809,11 +5812,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5813 "cmDependsJavaParser.cxx" +#line 5816 "cmDependsJavaParser.cxx" break; case 306: /* ShiftExpression: ShiftExpression jp_LTLT AdditiveExpression */ -#line 2782 "cmDependsJavaParser.y" +#line 2785 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5821,11 +5824,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5825 "cmDependsJavaParser.cxx" +#line 5828 "cmDependsJavaParser.cxx" break; case 307: /* ShiftExpression: ShiftExpression jp_GTGT AdditiveExpression */ -#line 2791 "cmDependsJavaParser.y" +#line 2794 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5833,11 +5836,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5837 "cmDependsJavaParser.cxx" +#line 5840 "cmDependsJavaParser.cxx" break; case 308: /* ShiftExpression: ShiftExpression jp_GTGTGT AdditiveExpression */ -#line 2800 "cmDependsJavaParser.y" +#line 2803 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5845,11 +5848,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5849 "cmDependsJavaParser.cxx" +#line 5852 "cmDependsJavaParser.cxx" break; case 309: /* RelationalExpression: ShiftExpression */ -#line 2810 "cmDependsJavaParser.y" +#line 2813 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5857,11 +5860,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5861 "cmDependsJavaParser.cxx" +#line 5864 "cmDependsJavaParser.cxx" break; case 310: /* RelationalExpression: RelationalExpression jp_LESSTHAN ShiftExpression */ -#line 2819 "cmDependsJavaParser.y" +#line 2822 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5869,11 +5872,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5873 "cmDependsJavaParser.cxx" +#line 5876 "cmDependsJavaParser.cxx" break; case 311: /* RelationalExpression: RelationalExpression jp_GREATER ShiftExpression */ -#line 2828 "cmDependsJavaParser.y" +#line 2831 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5881,11 +5884,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5885 "cmDependsJavaParser.cxx" +#line 5888 "cmDependsJavaParser.cxx" break; case 312: /* RelationalExpression: RelationalExpression jp_LTEQUALS ShiftExpression */ -#line 2837 "cmDependsJavaParser.y" +#line 2840 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5893,11 +5896,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5897 "cmDependsJavaParser.cxx" +#line 5900 "cmDependsJavaParser.cxx" break; case 313: /* RelationalExpression: RelationalExpression jp_GTEQUALS ShiftExpression */ -#line 2846 "cmDependsJavaParser.y" +#line 2849 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5905,11 +5908,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5909 "cmDependsJavaParser.cxx" +#line 5912 "cmDependsJavaParser.cxx" break; case 314: /* RelationalExpression: RelationalExpression jp_INSTANCEOF ReferenceType */ -#line 2855 "cmDependsJavaParser.y" +#line 2858 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5917,11 +5920,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5921 "cmDependsJavaParser.cxx" +#line 5924 "cmDependsJavaParser.cxx" break; case 315: /* EqualityExpression: RelationalExpression */ -#line 2865 "cmDependsJavaParser.y" +#line 2868 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5929,11 +5932,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5933 "cmDependsJavaParser.cxx" +#line 5936 "cmDependsJavaParser.cxx" break; case 316: /* EqualityExpression: EqualityExpression jp_EQUALSEQUALS RelationalExpression */ -#line 2874 "cmDependsJavaParser.y" +#line 2877 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5941,11 +5944,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5945 "cmDependsJavaParser.cxx" +#line 5948 "cmDependsJavaParser.cxx" break; case 317: /* EqualityExpression: EqualityExpression jp_EXCLAMATIONEQUALS RelationalExpression */ -#line 2883 "cmDependsJavaParser.y" +#line 2886 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5953,11 +5956,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5957 "cmDependsJavaParser.cxx" +#line 5960 "cmDependsJavaParser.cxx" break; case 318: /* AndExpression: EqualityExpression */ -#line 2893 "cmDependsJavaParser.y" +#line 2896 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5965,11 +5968,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5969 "cmDependsJavaParser.cxx" +#line 5972 "cmDependsJavaParser.cxx" break; case 319: /* AndExpression: AndExpression jp_AND EqualityExpression */ -#line 2902 "cmDependsJavaParser.y" +#line 2905 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -5977,11 +5980,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5981 "cmDependsJavaParser.cxx" +#line 5984 "cmDependsJavaParser.cxx" break; case 320: /* ExclusiveOrExpression: AndExpression */ -#line 2912 "cmDependsJavaParser.y" +#line 2915 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -5989,11 +5992,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 5993 "cmDependsJavaParser.cxx" +#line 5996 "cmDependsJavaParser.cxx" break; case 321: /* ExclusiveOrExpression: ExclusiveOrExpression jp_CARROT AndExpression */ -#line 2921 "cmDependsJavaParser.y" +#line 2924 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -6001,11 +6004,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6005 "cmDependsJavaParser.cxx" +#line 6008 "cmDependsJavaParser.cxx" break; case 322: /* InclusiveOrExpression: ExclusiveOrExpression */ -#line 2931 "cmDependsJavaParser.y" +#line 2934 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6013,11 +6016,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6017 "cmDependsJavaParser.cxx" +#line 6020 "cmDependsJavaParser.cxx" break; case 323: /* InclusiveOrExpression: InclusiveOrExpression jp_PIPE ExclusiveOrExpression */ -#line 2940 "cmDependsJavaParser.y" +#line 2943 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -6025,11 +6028,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6029 "cmDependsJavaParser.cxx" +#line 6032 "cmDependsJavaParser.cxx" break; case 324: /* ConditionalAndExpression: InclusiveOrExpression */ -#line 2950 "cmDependsJavaParser.y" +#line 2953 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6037,11 +6040,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6041 "cmDependsJavaParser.cxx" +#line 6044 "cmDependsJavaParser.cxx" break; case 325: /* ConditionalAndExpression: ConditionalAndExpression jp_ANDAND InclusiveOrExpression */ -#line 2959 "cmDependsJavaParser.y" +#line 2962 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -6049,11 +6052,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6053 "cmDependsJavaParser.cxx" +#line 6056 "cmDependsJavaParser.cxx" break; case 326: /* ConditionalOrExpression: ConditionalAndExpression */ -#line 2969 "cmDependsJavaParser.y" +#line 2972 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6061,11 +6064,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6065 "cmDependsJavaParser.cxx" +#line 6068 "cmDependsJavaParser.cxx" break; case 327: /* ConditionalOrExpression: ConditionalOrExpression jp_PIPEPIPE ConditionalAndExpression */ -#line 2978 "cmDependsJavaParser.y" +#line 2981 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -6073,11 +6076,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6077 "cmDependsJavaParser.cxx" +#line 6080 "cmDependsJavaParser.cxx" break; case 328: /* ConditionalExpression: ConditionalOrExpression */ -#line 2988 "cmDependsJavaParser.y" +#line 2991 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6085,11 +6088,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6089 "cmDependsJavaParser.cxx" +#line 6092 "cmDependsJavaParser.cxx" break; case 329: /* ConditionalExpression: ConditionalOrExpression jp_QUESTION Expression jp_COLON ConditionalExpression */ -#line 2997 "cmDependsJavaParser.y" +#line 3000 "cmDependsJavaParser.y" { jpElementStart(5); jpCheckEmpty(5); @@ -6097,11 +6100,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6101 "cmDependsJavaParser.cxx" +#line 6104 "cmDependsJavaParser.cxx" break; case 330: /* AssignmentExpression: ConditionalExpression */ -#line 3007 "cmDependsJavaParser.y" +#line 3010 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6109,11 +6112,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6113 "cmDependsJavaParser.cxx" +#line 6116 "cmDependsJavaParser.cxx" break; case 331: /* AssignmentExpression: Assignment */ -#line 3016 "cmDependsJavaParser.y" +#line 3019 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6121,11 +6124,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6125 "cmDependsJavaParser.cxx" +#line 6128 "cmDependsJavaParser.cxx" break; case 332: /* Assignment: LeftHandSide AssignmentOperator AssignmentExpression */ -#line 3026 "cmDependsJavaParser.y" +#line 3029 "cmDependsJavaParser.y" { jpElementStart(3); jpCheckEmpty(3); @@ -6133,11 +6136,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6137 "cmDependsJavaParser.cxx" +#line 6140 "cmDependsJavaParser.cxx" break; case 333: /* LeftHandSide: Name */ -#line 3036 "cmDependsJavaParser.y" +#line 3039 "cmDependsJavaParser.y" { jpElementStart(1); yyGetParser->DeallocateParserType(&((yyvsp[0].str))); @@ -6146,11 +6149,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6150 "cmDependsJavaParser.cxx" +#line 6153 "cmDependsJavaParser.cxx" break; case 334: /* LeftHandSide: FieldAccess */ -#line 3046 "cmDependsJavaParser.y" +#line 3049 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6158,11 +6161,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6162 "cmDependsJavaParser.cxx" +#line 6165 "cmDependsJavaParser.cxx" break; case 335: /* LeftHandSide: ArrayAccess */ -#line 3055 "cmDependsJavaParser.y" +#line 3058 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6170,11 +6173,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6174 "cmDependsJavaParser.cxx" +#line 6177 "cmDependsJavaParser.cxx" break; case 336: /* AssignmentOperator: jp_EQUALS */ -#line 3065 "cmDependsJavaParser.y" +#line 3068 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6182,11 +6185,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6186 "cmDependsJavaParser.cxx" +#line 6189 "cmDependsJavaParser.cxx" break; case 337: /* AssignmentOperator: jp_TIMESEQUALS */ -#line 3074 "cmDependsJavaParser.y" +#line 3077 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6194,11 +6197,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6198 "cmDependsJavaParser.cxx" +#line 6201 "cmDependsJavaParser.cxx" break; case 338: /* AssignmentOperator: jp_DIVIDEEQUALS */ -#line 3083 "cmDependsJavaParser.y" +#line 3086 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6206,11 +6209,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6210 "cmDependsJavaParser.cxx" +#line 6213 "cmDependsJavaParser.cxx" break; case 339: /* AssignmentOperator: jp_PERCENTEQUALS */ -#line 3092 "cmDependsJavaParser.y" +#line 3095 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6218,11 +6221,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6222 "cmDependsJavaParser.cxx" +#line 6225 "cmDependsJavaParser.cxx" break; case 340: /* AssignmentOperator: jp_PLUSEQUALS */ -#line 3101 "cmDependsJavaParser.y" +#line 3104 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6230,11 +6233,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6234 "cmDependsJavaParser.cxx" +#line 6237 "cmDependsJavaParser.cxx" break; case 341: /* AssignmentOperator: jp_MINUSEQUALS */ -#line 3110 "cmDependsJavaParser.y" +#line 3113 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6242,11 +6245,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6246 "cmDependsJavaParser.cxx" +#line 6249 "cmDependsJavaParser.cxx" break; case 342: /* AssignmentOperator: jp_LESLESEQUALS */ -#line 3119 "cmDependsJavaParser.y" +#line 3122 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6254,11 +6257,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6258 "cmDependsJavaParser.cxx" +#line 6261 "cmDependsJavaParser.cxx" break; case 343: /* AssignmentOperator: jp_GTGTEQUALS */ -#line 3128 "cmDependsJavaParser.y" +#line 3131 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6266,11 +6269,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6270 "cmDependsJavaParser.cxx" +#line 6273 "cmDependsJavaParser.cxx" break; case 344: /* AssignmentOperator: jp_GTGTGTEQUALS */ -#line 3137 "cmDependsJavaParser.y" +#line 3140 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6278,11 +6281,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6282 "cmDependsJavaParser.cxx" +#line 6285 "cmDependsJavaParser.cxx" break; case 345: /* AssignmentOperator: jp_ANDEQUALS */ -#line 3146 "cmDependsJavaParser.y" +#line 3149 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6290,11 +6293,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6294 "cmDependsJavaParser.cxx" +#line 6297 "cmDependsJavaParser.cxx" break; case 346: /* AssignmentOperator: jp_CARROTEQUALS */ -#line 3155 "cmDependsJavaParser.y" +#line 3158 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6302,11 +6305,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6306 "cmDependsJavaParser.cxx" +#line 6309 "cmDependsJavaParser.cxx" break; case 347: /* AssignmentOperator: jp_PIPEEQUALS */ -#line 3164 "cmDependsJavaParser.y" +#line 3167 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6314,11 +6317,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6318 "cmDependsJavaParser.cxx" +#line 6321 "cmDependsJavaParser.cxx" break; case 348: /* Expression: AssignmentExpression */ -#line 3174 "cmDependsJavaParser.y" +#line 3177 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6326,11 +6329,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6330 "cmDependsJavaParser.cxx" +#line 6333 "cmDependsJavaParser.cxx" break; case 349: /* ConstantExpression: Expression */ -#line 3184 "cmDependsJavaParser.y" +#line 3187 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6338,11 +6341,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6342 "cmDependsJavaParser.cxx" +#line 6345 "cmDependsJavaParser.cxx" break; case 350: /* New: jp_NEW */ -#line 3194 "cmDependsJavaParser.y" +#line 3197 "cmDependsJavaParser.y" { jpElementStart(1); jpCheckEmpty(1); @@ -6350,11 +6353,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6354 "cmDependsJavaParser.cxx" +#line 6357 "cmDependsJavaParser.cxx" break; case 351: /* New: Name jp_DOT jp_NEW */ -#line 3203 "cmDependsJavaParser.y" +#line 3206 "cmDependsJavaParser.y" { jpElementStart(3); jpStoreClass((yyvsp[-2].str)); @@ -6363,11 +6366,11 @@ yyGetParser->SetCurrentCombine(""); } -#line 6367 "cmDependsJavaParser.cxx" +#line 6370 "cmDependsJavaParser.cxx" break; -#line 6371 "cmDependsJavaParser.cxx" +#line 6374 "cmDependsJavaParser.cxx" default: break; } @@ -6591,7 +6594,7 @@ return yyresult; } -#line 3212 "cmDependsJavaParser.y" +#line 3215 "cmDependsJavaParser.y" /* End of grammar */
diff --git a/Source/LexerParser/cmDependsJavaParser.y b/Source/LexerParser/cmDependsJavaParser.y index 01d0017..7679212 100644 --- a/Source/LexerParser/cmDependsJavaParser.y +++ b/Source/LexerParser/cmDependsJavaParser.y
@@ -44,6 +44,9 @@ # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wfree-nonheap-object" #endif +#if defined(__GNUC__) && __GNUC__ >= 16 +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif #if defined(__clang__) && defined(__has_warning) # if __has_warning("-Wunused-but-set-variable") # pragma clang diagnostic ignored "-Wunused-but-set-variable"
diff --git a/Source/LexerParser/cmExprParser.cxx b/Source/LexerParser/cmExprParser.cxx index b63fbf4..d4168a0 100644 --- a/Source/LexerParser/cmExprParser.cxx +++ b/Source/LexerParser/cmExprParser.cxx
@@ -90,7 +90,6 @@ #include <stdlib.h> #include <string.h> -#include <stdexcept> /*-------------------------------------------------------------------------*/ #define YYDEBUG 1 @@ -113,6 +112,9 @@ # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wfree-nonheap-object" #endif +#if defined(__GNUC__) && __GNUC__ >= 16 +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif #if defined(__clang__) && defined(__has_warning) # if __has_warning("-Wunused-but-set-variable") # pragma clang diagnostic ignored "-Wunused-but-set-variable" @@ -126,7 +128,7 @@ # pragma diag_suppress 550 /* variable set but never used */ #endif -#line 130 "cmExprParser.cxx" +#line 132 "cmExprParser.cxx" # ifndef YY_CAST # ifdef __cplusplus @@ -568,9 +570,9 @@ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { - 0, 87, 87, 92, 95, 100, 103, 108, 111, 116, - 119, 122, 127, 130, 133, 138, 141, 144, 150, 158, - 161, 164, 167, 172, 175 + 0, 89, 89, 94, 97, 102, 105, 110, 113, 118, + 121, 125, 131, 134, 138, 144, 147, 151, 155, 161, + 164, 167, 171, 176, 179 }; #endif @@ -1431,197 +1433,199 @@ switch (yyn) { case 2: /* start: exp */ -#line 87 "cmExprParser.y" +#line 89 "cmExprParser.y" { cmExpr_yyget_extra(yyscanner)->SetResult((yyvsp[0].Number)); } -#line 1439 "cmExprParser.cxx" +#line 1441 "cmExprParser.cxx" break; case 3: /* exp: bitwiseor */ -#line 92 "cmExprParser.y" +#line 94 "cmExprParser.y" { (yyval.Number) = (yyvsp[0].Number); } -#line 1447 "cmExprParser.cxx" +#line 1449 "cmExprParser.cxx" break; case 4: /* exp: exp exp_OR bitwiseor */ -#line 95 "cmExprParser.y" +#line 97 "cmExprParser.y" { (yyval.Number) = (yyvsp[-2].Number) | (yyvsp[0].Number); } -#line 1455 "cmExprParser.cxx" +#line 1457 "cmExprParser.cxx" break; case 5: /* bitwiseor: bitwisexor */ -#line 100 "cmExprParser.y" +#line 102 "cmExprParser.y" { (yyval.Number) = (yyvsp[0].Number); } -#line 1463 "cmExprParser.cxx" +#line 1465 "cmExprParser.cxx" break; case 6: /* bitwiseor: bitwiseor exp_XOR bitwisexor */ -#line 103 "cmExprParser.y" +#line 105 "cmExprParser.y" { (yyval.Number) = (yyvsp[-2].Number) ^ (yyvsp[0].Number); } -#line 1471 "cmExprParser.cxx" +#line 1473 "cmExprParser.cxx" break; case 7: /* bitwisexor: bitwiseand */ -#line 108 "cmExprParser.y" +#line 110 "cmExprParser.y" { (yyval.Number) = (yyvsp[0].Number); } -#line 1479 "cmExprParser.cxx" +#line 1481 "cmExprParser.cxx" break; case 8: /* bitwisexor: bitwisexor exp_AND bitwiseand */ -#line 111 "cmExprParser.y" +#line 113 "cmExprParser.y" { (yyval.Number) = (yyvsp[-2].Number) & (yyvsp[0].Number); } -#line 1487 "cmExprParser.cxx" +#line 1489 "cmExprParser.cxx" break; case 9: /* bitwiseand: shift */ -#line 116 "cmExprParser.y" +#line 118 "cmExprParser.y" { (yyval.Number) = (yyvsp[0].Number); } -#line 1495 "cmExprParser.cxx" +#line 1497 "cmExprParser.cxx" break; case 10: /* bitwiseand: bitwiseand exp_SHIFTLEFT shift */ -#line 119 "cmExprParser.y" +#line 121 "cmExprParser.y" { - (yyval.Number) = (yyvsp[-2].Number) << (yyvsp[0].Number); + (yyval.Number) = cmExpr_yyget_extra(yyscanner) + ->ShL((yyvsp[-2].Number), (yyvsp[0].Number)); } -#line 1503 "cmExprParser.cxx" +#line 1506 "cmExprParser.cxx" break; case 11: /* bitwiseand: bitwiseand exp_SHIFTRIGHT shift */ -#line 122 "cmExprParser.y" +#line 125 "cmExprParser.y" { - (yyval.Number) = (yyvsp[-2].Number) >> (yyvsp[0].Number); + (yyval.Number) = cmExpr_yyget_extra(yyscanner) + ->ShR((yyvsp[-2].Number), (yyvsp[0].Number)); } -#line 1511 "cmExprParser.cxx" +#line 1515 "cmExprParser.cxx" break; case 12: /* shift: term */ -#line 127 "cmExprParser.y" +#line 131 "cmExprParser.y" { (yyval.Number) = (yyvsp[0].Number); } -#line 1519 "cmExprParser.cxx" +#line 1523 "cmExprParser.cxx" break; case 13: /* shift: shift exp_PLUS term */ -#line 130 "cmExprParser.y" +#line 134 "cmExprParser.y" { - (yyval.Number) = (yyvsp[-2].Number) + (yyvsp[0].Number); + (yyval.Number) = cmExpr_yyget_extra(yyscanner) + ->Add((yyvsp[-2].Number), (yyvsp[0].Number)); } -#line 1527 "cmExprParser.cxx" +#line 1532 "cmExprParser.cxx" break; case 14: /* shift: shift exp_MINUS term */ -#line 133 "cmExprParser.y" +#line 138 "cmExprParser.y" { - (yyval.Number) = (yyvsp[-2].Number) - (yyvsp[0].Number); + (yyval.Number) = cmExpr_yyget_extra(yyscanner) + ->Sub((yyvsp[-2].Number), (yyvsp[0].Number)); } -#line 1535 "cmExprParser.cxx" +#line 1541 "cmExprParser.cxx" break; case 15: /* term: unary */ -#line 138 "cmExprParser.y" +#line 144 "cmExprParser.y" { (yyval.Number) = (yyvsp[0].Number); } -#line 1543 "cmExprParser.cxx" +#line 1549 "cmExprParser.cxx" break; case 16: /* term: term exp_TIMES unary */ -#line 141 "cmExprParser.y" +#line 147 "cmExprParser.y" { - (yyval.Number) = (yyvsp[-2].Number) * (yyvsp[0].Number); + (yyval.Number) = cmExpr_yyget_extra(yyscanner) + ->Mul((yyvsp[-2].Number), (yyvsp[0].Number)); } -#line 1551 "cmExprParser.cxx" +#line 1558 "cmExprParser.cxx" break; case 17: /* term: term exp_DIVIDE unary */ -#line 144 "cmExprParser.y" +#line 151 "cmExprParser.y" { - if (yyvsp[0].Number == 0) { - throw std::overflow_error("divide by zero"); - } - (yyval.Number) = (yyvsp[-2].Number) / (yyvsp[0].Number); + (yyval.Number) = cmExpr_yyget_extra(yyscanner) + ->Div((yyvsp[-2].Number), (yyvsp[0].Number)); } -#line 1562 "cmExprParser.cxx" +#line 1567 "cmExprParser.cxx" break; case 18: /* term: term exp_MOD unary */ -#line 150 "cmExprParser.y" +#line 155 "cmExprParser.y" { - if (yyvsp[0].Number == 0) { - throw std::overflow_error("modulo by zero"); - } - (yyval.Number) = (yyvsp[-2].Number) % (yyvsp[0].Number); + (yyval.Number) = cmExpr_yyget_extra(yyscanner) + ->Mod((yyvsp[-2].Number), (yyvsp[0].Number)); } -#line 1573 "cmExprParser.cxx" +#line 1576 "cmExprParser.cxx" break; case 19: /* unary: factor */ -#line 158 "cmExprParser.y" +#line 161 "cmExprParser.y" { (yyval.Number) = (yyvsp[0].Number); } -#line 1581 "cmExprParser.cxx" +#line 1584 "cmExprParser.cxx" break; case 20: /* unary: exp_PLUS unary */ -#line 161 "cmExprParser.y" +#line 164 "cmExprParser.y" { (yyval.Number) = + (yyvsp[0].Number); } -#line 1589 "cmExprParser.cxx" +#line 1592 "cmExprParser.cxx" break; case 21: /* unary: exp_MINUS unary */ -#line 164 "cmExprParser.y" +#line 167 "cmExprParser.y" { - (yyval.Number) = - (yyvsp[0].Number); + (yyval.Number) = cmExpr_yyget_extra(yyscanner) + ->Neg((yyvsp[0].Number)); } -#line 1597 "cmExprParser.cxx" +#line 1601 "cmExprParser.cxx" break; case 22: /* unary: exp_NOT unary */ -#line 167 "cmExprParser.y" +#line 171 "cmExprParser.y" { (yyval.Number) = ~ (yyvsp[0].Number); } -#line 1605 "cmExprParser.cxx" +#line 1609 "cmExprParser.cxx" break; case 23: /* factor: exp_NUMBER */ -#line 172 "cmExprParser.y" +#line 176 "cmExprParser.y" { (yyval.Number) = (yyvsp[0].Number); } -#line 1613 "cmExprParser.cxx" +#line 1617 "cmExprParser.cxx" break; case 24: /* factor: exp_OPENPARENT exp exp_CLOSEPARENT */ -#line 175 "cmExprParser.y" +#line 179 "cmExprParser.y" { (yyval.Number) = (yyvsp[-1].Number); } -#line 1621 "cmExprParser.cxx" +#line 1625 "cmExprParser.cxx" break; -#line 1625 "cmExprParser.cxx" +#line 1629 "cmExprParser.cxx" default: break; } @@ -1845,7 +1849,7 @@ return yyresult; } -#line 180 "cmExprParser.y" +#line 184 "cmExprParser.y" /* End of grammar */
diff --git a/Source/LexerParser/cmExprParser.y b/Source/LexerParser/cmExprParser.y index 0cb120c..b4f7e60 100644 --- a/Source/LexerParser/cmExprParser.y +++ b/Source/LexerParser/cmExprParser.y
@@ -15,7 +15,6 @@ #include <stdlib.h> #include <string.h> -#include <stdexcept> /*-------------------------------------------------------------------------*/ #define YYDEBUG 1 @@ -38,6 +37,9 @@ # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wfree-nonheap-object" #endif +#if defined(__GNUC__) && __GNUC__ >= 16 +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif #if defined(__clang__) && defined(__has_warning) # if __has_warning("-Wunused-but-set-variable") # pragma clang diagnostic ignored "-Wunused-but-set-variable" @@ -117,10 +119,12 @@ $<Number>$ = $<Number>1; } | bitwiseand exp_SHIFTLEFT shift { - $<Number>$ = $<Number>1 << $<Number>3; + $<Number>$ = cmExpr_yyget_extra(yyscanner) + ->ShL($<Number>1, $<Number>3); } | bitwiseand exp_SHIFTRIGHT shift { - $<Number>$ = $<Number>1 >> $<Number>3; + $<Number>$ = cmExpr_yyget_extra(yyscanner) + ->ShR($<Number>1, $<Number>3); } shift: @@ -128,10 +132,12 @@ $<Number>$ = $<Number>1; } | shift exp_PLUS term { - $<Number>$ = $<Number>1 + $<Number>3; + $<Number>$ = cmExpr_yyget_extra(yyscanner) + ->Add($<Number>1, $<Number>3); } | shift exp_MINUS term { - $<Number>$ = $<Number>1 - $<Number>3; + $<Number>$ = cmExpr_yyget_extra(yyscanner) + ->Sub($<Number>1, $<Number>3); } term: @@ -139,19 +145,16 @@ $<Number>$ = $<Number>1; } | term exp_TIMES unary { - $<Number>$ = $<Number>1 * $<Number>3; + $<Number>$ = cmExpr_yyget_extra(yyscanner) + ->Mul($<Number>1, $<Number>3); } | term exp_DIVIDE unary { - if (yyvsp[0].Number == 0) { - throw std::overflow_error("divide by zero"); - } - $<Number>$ = $<Number>1 / $<Number>3; + $<Number>$ = cmExpr_yyget_extra(yyscanner) + ->Div($<Number>1, $<Number>3); } | term exp_MOD unary { - if (yyvsp[0].Number == 0) { - throw std::overflow_error("modulo by zero"); - } - $<Number>$ = $<Number>1 % $<Number>3; + $<Number>$ = cmExpr_yyget_extra(yyscanner) + ->Mod($<Number>1, $<Number>3); } unary: @@ -162,7 +165,8 @@ $<Number>$ = + $<Number>2; } | exp_MINUS unary { - $<Number>$ = - $<Number>2; + $<Number>$ = cmExpr_yyget_extra(yyscanner) + ->Neg($<Number>2); } | exp_NOT unary { $<Number>$ = ~ $<Number>2;
diff --git a/Source/LexerParser/cmFortranParser.cxx b/Source/LexerParser/cmFortranParser.cxx index a43f4fc..5bc20b0 100644 --- a/Source/LexerParser/cmFortranParser.cxx +++ b/Source/LexerParser/cmFortranParser.cxx
@@ -132,13 +132,16 @@ # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wfree-nonheap-object" #endif +#if defined(__GNUC__) && __GNUC__ >= 16 +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif #if defined(__clang__) && defined(__has_warning) # if __has_warning("-Wunused-but-set-variable") # pragma clang diagnostic ignored "-Wunused-but-set-variable" # endif #endif -#line 142 "cmFortranParser.cxx" +#line 145 "cmFortranParser.cxx" # ifndef YY_CAST # ifdef __cplusplus @@ -610,13 +613,13 @@ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { - 0, 106, 106, 106, 109, 113, 118, 123, 129, 136, - 141, 146, 150, 155, 167, 172, 177, 182, 187, 192, - 197, 202, 207, 211, 215, 219, 223, 224, 229, 229, - 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, - 234, 235, 235, 236, 236, 237, 237, 238, 238, 241, - 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, - 252, 253, 254, 255, 256, 257 + 0, 109, 109, 109, 112, 116, 121, 126, 132, 139, + 144, 149, 153, 158, 170, 175, 180, 185, 190, 195, + 200, 205, 210, 214, 218, 222, 226, 227, 232, 232, + 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, + 237, 238, 238, 239, 239, 240, 240, 241, 241, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260 }; #endif @@ -1327,21 +1330,21 @@ switch (yykind) { case YYSYMBOL_STRING: /* STRING */ -#line 100 "cmFortranParser.y" +#line 103 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1333 "cmFortranParser.cxx" +#line 1336 "cmFortranParser.cxx" break; case YYSYMBOL_WORD: /* WORD */ -#line 100 "cmFortranParser.y" +#line 103 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1339 "cmFortranParser.cxx" +#line 1342 "cmFortranParser.cxx" break; case YYSYMBOL_CPP_INCLUDE_ANGLE: /* CPP_INCLUDE_ANGLE */ -#line 100 "cmFortranParser.y" +#line 103 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1345 "cmFortranParser.cxx" +#line 1348 "cmFortranParser.cxx" break; default: @@ -1618,47 +1621,47 @@ switch (yyn) { case 4: /* stmt: INTERFACE EOSTMT */ -#line 109 "cmFortranParser.y" +#line 112 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, true); } -#line 1627 "cmFortranParser.cxx" +#line 1630 "cmFortranParser.cxx" break; case 5: /* stmt: USE WORD other EOSTMT */ -#line 113 "cmFortranParser.y" +#line 116 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleUse(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1637 "cmFortranParser.cxx" +#line 1640 "cmFortranParser.cxx" break; case 6: /* stmt: MODULE WORD EOSTMT */ -#line 118 "cmFortranParser.y" +#line 121 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleModule(parser, (yyvsp[-1].string)); free((yyvsp[-1].string)); } -#line 1647 "cmFortranParser.cxx" +#line 1650 "cmFortranParser.cxx" break; case 7: /* stmt: SUBMODULE LPAREN WORD RPAREN WORD EOSTMT */ -#line 123 "cmFortranParser.y" +#line 126 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleSubmodule(parser, (yyvsp[-3].string), (yyvsp[-1].string)); free((yyvsp[-3].string)); free((yyvsp[-1].string)); } -#line 1658 "cmFortranParser.cxx" +#line 1661 "cmFortranParser.cxx" break; case 8: /* stmt: SUBMODULE LPAREN WORD COLON WORD RPAREN WORD EOSTMT */ -#line 129 "cmFortranParser.y" +#line 132 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-5].string), (yyvsp[-3].string), (yyvsp[-1].string)); @@ -1666,50 +1669,50 @@ free((yyvsp[-3].string)); free((yyvsp[-1].string)); } -#line 1670 "cmFortranParser.cxx" +#line 1673 "cmFortranParser.cxx" break; case 9: /* stmt: INTERFACE WORD EOSTMT */ -#line 136 "cmFortranParser.y" +#line 139 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, true); free((yyvsp[-1].string)); } -#line 1680 "cmFortranParser.cxx" +#line 1683 "cmFortranParser.cxx" break; case 10: /* stmt: END INTERFACE WORD EOSTMT */ -#line 141 "cmFortranParser.y" +#line 144 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, false); free((yyvsp[-1].string)); } -#line 1690 "cmFortranParser.cxx" +#line 1693 "cmFortranParser.cxx" break; case 11: /* stmt: END INTERFACE EOSTMT */ -#line 146 "cmFortranParser.y" +#line 149 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, false); } -#line 1699 "cmFortranParser.cxx" +#line 1702 "cmFortranParser.cxx" break; case 12: /* stmt: USE DCOLON WORD other EOSTMT */ -#line 150 "cmFortranParser.y" +#line 153 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleUse(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1709 "cmFortranParser.cxx" +#line 1712 "cmFortranParser.cxx" break; case 13: /* stmt: USE COMMA WORD DCOLON WORD other EOSTMT */ -#line 155 "cmFortranParser.y" +#line 158 "cmFortranParser.y" { if (cmsysString_strcasecmp((yyvsp[-4].string), "non_intrinsic") == 0) { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); @@ -1722,139 +1725,139 @@ free((yyvsp[-4].string)); free((yyvsp[-2].string)); } -#line 1726 "cmFortranParser.cxx" +#line 1729 "cmFortranParser.cxx" break; case 14: /* stmt: INCLUDE STRING EOSTMT */ -#line 167 "cmFortranParser.y" +#line 170 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, (yyvsp[-1].string)); free((yyvsp[-1].string)); } -#line 1736 "cmFortranParser.cxx" +#line 1739 "cmFortranParser.cxx" break; case 15: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT */ -#line 172 "cmFortranParser.y" +#line 175 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleLineDirective(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1746 "cmFortranParser.cxx" +#line 1749 "cmFortranParser.cxx" break; case 16: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT */ -#line 177 "cmFortranParser.y" +#line 180 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1756 "cmFortranParser.cxx" +#line 1759 "cmFortranParser.cxx" break; case 17: /* stmt: include STRING other EOSTMT */ -#line 182 "cmFortranParser.y" +#line 185 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1766 "cmFortranParser.cxx" +#line 1769 "cmFortranParser.cxx" break; case 18: /* stmt: define WORD other EOSTMT */ -#line 187 "cmFortranParser.y" +#line 190 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleDefine(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1776 "cmFortranParser.cxx" +#line 1779 "cmFortranParser.cxx" break; case 19: /* stmt: undef WORD other EOSTMT */ -#line 192 "cmFortranParser.y" +#line 195 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleUndef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1786 "cmFortranParser.cxx" +#line 1789 "cmFortranParser.cxx" break; case 20: /* stmt: ifdef WORD other EOSTMT */ -#line 197 "cmFortranParser.y" +#line 200 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1796 "cmFortranParser.cxx" +#line 1799 "cmFortranParser.cxx" break; case 21: /* stmt: ifndef WORD other EOSTMT */ -#line 202 "cmFortranParser.y" +#line 205 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1806 "cmFortranParser.cxx" +#line 1809 "cmFortranParser.cxx" break; case 22: /* stmt: if other EOSTMT */ -#line 207 "cmFortranParser.y" +#line 210 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIf(parser); } -#line 1815 "cmFortranParser.cxx" +#line 1818 "cmFortranParser.cxx" break; case 23: /* stmt: elif other EOSTMT */ -#line 211 "cmFortranParser.y" +#line 214 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleElif(parser); } -#line 1824 "cmFortranParser.cxx" +#line 1827 "cmFortranParser.cxx" break; case 24: /* stmt: else other EOSTMT */ -#line 215 "cmFortranParser.y" +#line 218 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleElse(parser); } -#line 1833 "cmFortranParser.cxx" +#line 1836 "cmFortranParser.cxx" break; case 25: /* stmt: endif other EOSTMT */ -#line 219 "cmFortranParser.y" +#line 222 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleEndif(parser); } -#line 1842 "cmFortranParser.cxx" +#line 1845 "cmFortranParser.cxx" break; case 49: /* misc_code: WORD */ -#line 241 "cmFortranParser.y" +#line 244 "cmFortranParser.y" { free ((yyvsp[0].string)); } -#line 1848 "cmFortranParser.cxx" +#line 1851 "cmFortranParser.cxx" break; case 56: /* misc_code: STRING */ -#line 248 "cmFortranParser.y" +#line 251 "cmFortranParser.y" { free ((yyvsp[0].string)); } -#line 1854 "cmFortranParser.cxx" +#line 1857 "cmFortranParser.cxx" break; -#line 1858 "cmFortranParser.cxx" +#line 1861 "cmFortranParser.cxx" default: break; } @@ -2078,6 +2081,6 @@ return yyresult; } -#line 260 "cmFortranParser.y" +#line 263 "cmFortranParser.y" /* End of grammar */
diff --git a/Source/LexerParser/cmFortranParser.y b/Source/LexerParser/cmFortranParser.y index 431d6ab..77eaf75 100644 --- a/Source/LexerParser/cmFortranParser.y +++ b/Source/LexerParser/cmFortranParser.y
@@ -57,6 +57,9 @@ # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wfree-nonheap-object" #endif +#if defined(__GNUC__) && __GNUC__ >= 16 +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif #if defined(__clang__) && defined(__has_warning) # if __has_warning("-Wunused-but-set-variable") # pragma clang diagnostic ignored "-Wunused-but-set-variable"
diff --git a/Source/LexerParser/cmFortranParserTokens.h b/Source/LexerParser/cmFortranParserTokens.h index cb2b999..51b6a45 100644 --- a/Source/LexerParser/cmFortranParserTokens.h +++ b/Source/LexerParser/cmFortranParserTokens.h
@@ -100,7 +100,7 @@ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 76 "cmFortranParser.y" +#line 79 "cmFortranParser.y" char* string;
diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake index 78aa95a..15bc35b 100644 --- a/Source/Modules/CMakeBuildUtilities.cmake +++ b/Source/Modules/CMakeBuildUtilities.cmake
@@ -181,6 +181,10 @@ endif() set(CMAKE_EXPAT_INCLUDES ${EXPAT_INCLUDE_DIRS}) set(CMAKE_EXPAT_LIBRARIES ${EXPAT_LIBRARIES}) + # Expat's CMake package provides a lower-case target name. + if(NOT TARGET EXPAT::EXPAT AND TARGET expat::expat) + add_library(EXPAT::EXPAT ALIAS expat::expat) + endif() else() set(CMAKE_EXPAT_INCLUDES) set(CMAKE_EXPAT_LIBRARIES cmexpat) @@ -290,7 +294,11 @@ set(ENABLE_WIN32_XMLLITE OFF) set(ENABLE_XATTR OFF) set(ENABLE_ACL OFF) - set(ENABLE_ICONV OFF) + if(WIN32) + set(ENABLE_ICONV OFF) + else() + set(ENABLE_ICONV ON) + endif() set(ENABLE_TEST OFF) set(ENABLE_COVERAGE OFF) set(ENABLE_INSTALL OFF)
diff --git a/Source/QtDialog/AddCacheEntry.cxx b/Source/QtDialog/AddCacheEntry.cxx index 3c3be35..e354d24 100644 --- a/Source/QtDialog/AddCacheEntry.cxx +++ b/Source/QtDialog/AddCacheEntry.cxx
@@ -2,10 +2,11 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "AddCacheEntry.h" -#include "QCMakeSizeType.h" #include <QCompleter> #include <QMetaProperty> +#include "QCMakeSizeType.h" + static int const NumTypes = 4; static int const DefaultTypeIndex = 0; static QByteArray const TypeStrings[NumTypes] = { "BOOL", "PATH", "FILEPATH",
diff --git a/Source/QtDialog/AddCacheEntry.h b/Source/QtDialog/AddCacheEntry.h index c289c6b..d9563b9 100644 --- a/Source/QtDialog/AddCacheEntry.h +++ b/Source/QtDialog/AddCacheEntry.h
@@ -2,11 +2,11 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #pragma once -#include "QCMake.h" #include <QCheckBox> #include <QStringList> #include <QWidget> +#include "QCMake.h" #include "ui_AddCacheEntry.h" class AddCacheEntry
diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index e74b871..dfc8265 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx
@@ -2,7 +2,6 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include <iostream> -#include "QCMake.h" // include to disable MS warnings #include <QApplication> #include <QDir> #include <QLocale> @@ -23,6 +22,8 @@ #include "cmSystemTools.h" // IWYU pragma: keep #include "cmake.h" +#include "QCMake.h" // include to disable MS warnings + namespace { cmDocumentationEntry const cmDocumentationName = { {},
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index 34e9c9f..eb3a534 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -30,16 +30,15 @@ # include <QWinTaskbarProgress> #endif -#include "QCMake.h" -#include "QCMakeCacheView.h" -#include "QCMakeSizeType.h" - #include "cmSystemTools.h" #include "cmVersion.h" #include "AddCacheEntry.h" #include "EnvironmentDialog.h" #include "FirstConfigure.h" +#include "QCMake.h" +#include "QCMakeCacheView.h" +#include "QCMakeSizeType.h" #include "RegexExplorer.h" #include "WarningMessagesDialog.h" @@ -191,9 +190,6 @@ a = OptionsMenu->addAction(tr("Warning Messages...")); QObject::connect(a, &QAction::triggered, this, &CMakeSetupDialog::doWarningMessagesDialog); - this->WarnUninitializedAction = - OptionsMenu->addAction(tr("&Warn Uninitialized (--warn-uninitialized)")); - this->WarnUninitializedAction->setCheckable(true); QAction* debugAction = OptionsMenu->addAction(tr("&Debug Output")); debugAction->setCheckable(true); @@ -342,13 +338,6 @@ QObject::connect(this->Environment, &QAbstractButton::clicked, this, &CMakeSetupDialog::editEnvironment); - QObject::connect(this->WarnUninitializedAction, &QAction::triggered, - this->CMakeThread->cmakeInstance(), - &QCMake::setWarnUninitializedMode); - QObject::connect(this->CMakeThread->cmakeInstance(), - &QCMake::warnUninitializedModeChanged, - this->WarnUninitializedAction, &QAction::setChecked); - if (!this->SourceDirectory->text().isEmpty() && !this->DeferredPreset.isNull()) { this->onSourceDirectoryChanged(this->SourceDirectory->text());
diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index e5cd753..8e42ef9 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h
@@ -4,13 +4,13 @@ #include <memory> -#include "QCMake.h" -#include "QCMakePreset.h" #include <QEventLoop> #include <QMainWindow> #include <QThread> #include <QVector> +#include "QCMake.h" +#include "QCMakePreset.h" #include "ui_CMakeSetupDialog.h" class QCMakePresetItemModel; @@ -119,7 +119,6 @@ QAction* ExitAction; QAction* ConfigureAction; QAction* GenerateAction; - QAction* WarnUninitializedAction; QAction* InstallForCommandLineAction; State CurrentState; QString DeferredPreset;
diff --git a/Source/QtDialog/EnvironmentDialog.cxx b/Source/QtDialog/EnvironmentDialog.cxx index 8ffb7f1..1a9db4e 100644 --- a/Source/QtDialog/EnvironmentDialog.cxx +++ b/Source/QtDialog/EnvironmentDialog.cxx
@@ -2,7 +2,6 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "EnvironmentDialog.h" -#include "QCMakeWidgets.h" #include <QDialogButtonBox> #include <QGridLayout> #include <QItemSelectionModel> @@ -11,6 +10,8 @@ #include <QMessageBox> #include <QStandardItem> +#include "QCMakeWidgets.h" + EnvironmentItemModel::EnvironmentItemModel( QProcessEnvironment const& environment, QObject* parent) : QStandardItemModel(parent)
diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx index 4a3133e..2886ed8 100644 --- a/Source/QtDialog/FirstConfigure.cxx +++ b/Source/QtDialog/FirstConfigure.cxx
@@ -1,7 +1,6 @@ #include "FirstConfigure.h" -#include "QCMakeSizeType.h" #include <QComboBox> #include <QRadioButton> #include <QSettings> @@ -10,6 +9,7 @@ #include "cmStringAlgorithms.h" #include "Compilers.h" +#include "QCMakeSizeType.h" StartCompilerSetup::StartCompilerSetup(QString defaultGeneratorPlatform, QString defaultGeneratorToolset,
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index cf031b1..e05f3ad 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx
@@ -6,12 +6,12 @@ #include <cm/memory> -#include "QCMakeSizeType.h" #include <QCoreApplication> #include <QDir> #include <QString> #include <QVector> +#include "cmDiagnostics.h" #include "cmExternalMakefileProjectGenerator.h" #include "cmGlobalGenerator.h" #include "cmMessageMetadata.h" @@ -19,6 +19,8 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "QCMakeSizeType.h" + #ifdef Q_OS_WIN # include "qt_windows.h" // For SetErrorMode #endif @@ -28,7 +30,6 @@ , StartEnvironment(QProcessEnvironment::systemEnvironment()) , Environment(QProcessEnvironment::systemEnvironment()) { - this->WarnUninitializedMode = false; qRegisterMetaType<QCMakeProperty>(); qRegisterMetaType<QCMakePropertyList>(); qRegisterMetaType<QProcessEnvironment>(); @@ -163,7 +164,7 @@ if (!name.isNull()) { std::string presetName(name.toStdString()); - auto const& expandedPreset = + auto const expandedPreset = this->CMakePresetsGraph.ConfigurePresets[presetName].Expanded; if (expandedPreset) { if (setBinary && !expandedPreset->BinaryDir.empty()) { @@ -171,27 +172,8 @@ QString::fromStdString(expandedPreset->BinaryDir); this->setBinaryDirectory(binaryDir); } - if (expandedPreset->WarnDev) { - this->CMakeInstance->SetSuppressDevWarnings( - !*expandedPreset->WarnDev); - } - if (expandedPreset->ErrorDev) { - this->CMakeInstance->SetDevWarningsAsErrors( - *expandedPreset->ErrorDev); - } - if (expandedPreset->WarnDeprecated) { - this->CMakeInstance->SetSuppressDeprecatedWarnings( - !*expandedPreset->WarnDeprecated); - } - if (expandedPreset->ErrorDeprecated) { - this->CMakeInstance->SetDeprecatedWarningsAsErrors( - *expandedPreset->ErrorDeprecated); - } - if (expandedPreset->WarnUninitialized) { - this->WarnUninitializedMode = *expandedPreset->WarnUninitialized; - emit this->warnUninitializedModeChanged( - *expandedPreset->WarnUninitialized); - } + this->CMakeInstance->SetDiagnosticsFromPreset(expandedPreset->Warnings, + expandedPreset->Errors); this->Environment = this->StartEnvironment; for (auto const& v : expandedPreset->Environment) { if (v.second) { @@ -258,7 +240,6 @@ this->CMakeInstance->SetGeneratorPlatform(this->Platform.toStdString()); this->CMakeInstance->SetGeneratorToolset(this->Toolset.toStdString()); this->CMakeInstance->LoadCache(); - this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode); this->CMakeInstance->PreLoadCMakeFiles(); InterruptFlag = 0; @@ -560,7 +541,8 @@ void QCMake::loadPresets() { auto result = this->CMakePresetsGraph.ReadProjectPresets( - this->SourceDirectory.toStdString(), true); + this->SourceDirectory.toStdString(), "", + cmCMakePresetsGraph::ReadOption::AllowNoFiles); if (!result) { emit this->presetLoadError( this->SourceDirectory, @@ -667,49 +649,17 @@ return this->CMakeInstance->GetDebugOutput(); } -bool QCMake::getSuppressDevWarnings() +void QCMake::setDiagnosticAction(cmDiagnostics::DiagnosticCategory category, + cmDiagnostics::DiagnosticAction action) { - return this->CMakeInstance->GetSuppressDevWarnings(); + this->CMakeInstance->GetCurrentSnapshot().SetDiagnostic(category, action, + false); } -void QCMake::setSuppressDevWarnings(bool value) +cmDiagnosticAction QCMake::getDiagnosticAction( + cmDiagnosticCategory category) const { - this->CMakeInstance->SetSuppressDevWarnings(value); -} - -bool QCMake::getSuppressDeprecatedWarnings() -{ - return this->CMakeInstance->GetSuppressDeprecatedWarnings(); -} - -void QCMake::setSuppressDeprecatedWarnings(bool value) -{ - this->CMakeInstance->SetSuppressDeprecatedWarnings(value); -} - -bool QCMake::getDevWarningsAsErrors() -{ - return this->CMakeInstance->GetDevWarningsAsErrors(); -} - -void QCMake::setDevWarningsAsErrors(bool value) -{ - this->CMakeInstance->SetDevWarningsAsErrors(value); -} - -bool QCMake::getDeprecatedWarningsAsErrors() -{ - return this->CMakeInstance->GetDeprecatedWarningsAsErrors(); -} - -void QCMake::setDeprecatedWarningsAsErrors(bool value) -{ - this->CMakeInstance->SetDeprecatedWarningsAsErrors(value); -} - -void QCMake::setWarnUninitializedMode(bool value) -{ - this->WarnUninitializedMode = value; + return this->CMakeInstance->GetCurrentSnapshot().GetDiagnostic(category); } void QCMake::checkOpenPossible()
diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index 50305f9..9be5259 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h
@@ -5,6 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include "cmCMakePresetsGraph.h" +#include "cmDiagnostics.h" #include "cmake.h" #ifdef _MSC_VER @@ -15,7 +16,6 @@ #include <memory> #include <vector> -#include "QCMakePreset.h" #include <QAtomicInt> #include <QList> #include <QMetaType> @@ -26,6 +26,8 @@ #include <QTimer> #include <QVariant> +#include "QCMakePreset.h" + /// struct to represent cmake properties in Qt /// Value is of type String or Bool struct QCMakeProperty @@ -104,27 +106,12 @@ void reloadCache(); /// set whether to do debug output void setDebugOutput(bool); - /// get whether to do suppress dev warnings - bool getSuppressDevWarnings(); - /// set whether to do suppress dev warnings - void setSuppressDevWarnings(bool value); - /// get whether to do suppress deprecated warnings - bool getSuppressDeprecatedWarnings(); - /// set whether to do suppress deprecated warnings - void setSuppressDeprecatedWarnings(bool value); - /// get whether to treat developer (author) warnings as errors - bool getDevWarningsAsErrors(); - /// set whether to treat developer (author) warnings as errors - void setDevWarningsAsErrors(bool value); - /// get whether to treat deprecated warnings as errors - bool getDeprecatedWarningsAsErrors(); - /// set whether to treat deprecated warnings as errors - void setDeprecatedWarningsAsErrors(bool value); - /// set whether to run cmake with warnings about uninitialized variables - void setWarnUninitializedMode(bool value); + /// set diagnostic action + void setDiagnosticAction(cmDiagnostics::DiagnosticCategory, + cmDiagnostics::DiagnosticAction); /// check if project IDE open is possible and emit openPossible signal void checkOpenPossible(); - /// Reload the preset files and tree + /// Reload the presets files and tree void loadPresets(); public: @@ -144,6 +131,8 @@ std::vector<cmake::GeneratorInfo> const& availableGenerators() const; /// get whether to do debug output bool getDebugOutput() const; + /// get diagnostic action + cmDiagnosticAction getDiagnosticAction(cmDiagnosticCategory) const; signals: /// signal when properties change (during read from disk or configure @@ -162,8 +151,6 @@ void presetChanged(QString const& name); /// signal when there's an error reading the presets files void presetLoadError(QString const& dir, QString const& error); - /// signal when uninitialized warning changes - void warnUninitializedModeChanged(bool value); /// signal for progress events void progressChanged(QString const& msg, float percent); /// signal when configure is done @@ -195,7 +182,6 @@ void stderrCallback(std::string const& msg); void setUpEnvironment() const; - bool WarnUninitializedMode; QString SourceDirectory; QString BinaryDirectory; QString MaybeRelativeBinaryDirectory;
diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx index fa2ad3d..e9eb792 100644 --- a/Source/QtDialog/QCMakeCacheView.cxx +++ b/Source/QtDialog/QCMakeCacheView.cxx
@@ -2,8 +2,6 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "QCMakeCacheView.h" -#include "QCMakeSizeType.h" -#include "QCMakeWidgets.h" #include <QApplication> #include <QEvent> #include <QHBoxLayout> @@ -13,6 +11,9 @@ #include <QSortFilterProxyModel> #include <QStyle> +#include "QCMakeSizeType.h" +#include "QCMakeWidgets.h" + // filter for searches class QCMakeSearchFilter : public QSortFilterProxyModel {
diff --git a/Source/QtDialog/QCMakeCacheView.h b/Source/QtDialog/QCMakeCacheView.h index 8d2c742..a334673 100644 --- a/Source/QtDialog/QCMakeCacheView.h +++ b/Source/QtDialog/QCMakeCacheView.h
@@ -2,13 +2,14 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #pragma once -#include "QCMake.h" -#include "QCMakeSizeType.h" #include <QItemDelegate> #include <QSet> #include <QStandardItemModel> #include <QTreeView> +#include "QCMake.h" +#include "QCMakeSizeType.h" + class QSortFilterProxyModel; class QCMakeCacheModel; class QCMakeAdvancedFilter;
diff --git a/Source/QtDialog/QCMakePresetComboBox.h b/Source/QtDialog/QCMakePresetComboBox.h index 47d8b8e..e7ac173 100644 --- a/Source/QtDialog/QCMakePresetComboBox.h +++ b/Source/QtDialog/QCMakePresetComboBox.h
@@ -2,12 +2,13 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #pragma once -#include "QCMakePreset.h" #include <QComboBox> #include <QObject> #include <QString> #include <QVector> +#include "QCMakePreset.h" + class QCMakePresetItemModel; class QCMakePresetComboBox : public QComboBox
diff --git a/Source/QtDialog/QCMakePresetItemModel.cxx b/Source/QtDialog/QCMakePresetItemModel.cxx index cb4e4fe..cae0a42 100644 --- a/Source/QtDialog/QCMakePresetItemModel.cxx +++ b/Source/QtDialog/QCMakePresetItemModel.cxx
@@ -2,9 +2,10 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "QCMakePresetItemModel.h" -#include "QCMakeSizeType.h" #include <QFont> +#include "QCMakeSizeType.h" + QCMakePresetItemModel::QCMakePresetItemModel(QObject* parent) : QAbstractItemModel(parent) {
diff --git a/Source/QtDialog/QCMakePresetItemModel.h b/Source/QtDialog/QCMakePresetItemModel.h index b565cbd..a3a0cf3 100644 --- a/Source/QtDialog/QCMakePresetItemModel.h +++ b/Source/QtDialog/QCMakePresetItemModel.h
@@ -4,7 +4,6 @@ #include <cm/optional> -#include "QCMakePreset.h" #include <QAbstractItemModel> #include <QModelIndex> #include <QString> @@ -12,6 +11,8 @@ #include <QVector> #include <QtGlobal> +#include "QCMakePreset.h" + class QObject; class QCMakePresetItemModel : public QAbstractItemModel
diff --git a/Source/QtDialog/WarningMessagesDialog.cxx b/Source/QtDialog/WarningMessagesDialog.cxx index 3728693..cf4c8e5 100644 --- a/Source/QtDialog/WarningMessagesDialog.cxx +++ b/Source/QtDialog/WarningMessagesDialog.cxx
@@ -2,6 +2,20 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "WarningMessagesDialog.h" +#include "cm/string_view" + +#include <QButtonGroup> +#include <QHeaderView> +#include <QRadioButton> +#include <QToolButton> +#include <QTreeWidget> +#include <QTreeWidgetItem> + +#include "cmDiagnostics.h" + +#include "QCMake.h" +#include "QCMakeSizeType.h" + WarningMessagesDialog::WarningMessagesDialog(QWidget* prnt, QCMake* instance) : QDialog(prnt) , cmakeInstance(instance) @@ -13,84 +27,83 @@ void WarningMessagesDialog::setInitialValues() { - this->suppressDeveloperWarnings->setChecked( - this->cmakeInstance->getSuppressDevWarnings()); - this->suppressDeprecatedWarnings->setChecked( - this->cmakeInstance->getSuppressDeprecatedWarnings()); + QHeaderView* const header = this->treeWidget->header(); + header->setSectionResizeMode(0, QHeaderView::Stretch); + header->setSectionResizeMode(1, QHeaderView::ResizeToContents); + header->setSectionResizeMode(2, QHeaderView::ResizeToContents); + header->setSectionResizeMode(3, QHeaderView::ResizeToContents); - this->developerWarningsAsErrors->setChecked( - this->cmakeInstance->getDevWarningsAsErrors()); - this->deprecatedWarningsAsErrors->setChecked( - this->cmakeInstance->getDeprecatedWarningsAsErrors()); + std::map<unsigned, QTreeWidgetItem*> items = { + { 0, this->treeWidget->invisibleRootItem() }, + }; + + for (unsigned i = 1; i < cmDiagnostics::CategoryCount; ++i) { + auto const category = static_cast<cmDiagnosticCategory>(i); + cm::string_view const cname = + cmDiagnostics::GetCategoryString(category).substr(4); + QTreeWidgetItem* const parent = + items[cmDiagnostics::CategoryInfo[category].Parent]; + + QTreeWidgetItem* const item = new QTreeWidgetItem(parent); + item->setText(0, + QString::fromUtf8(cname.data(), + static_cast<cm_qsizetype>(cname.size()))); + + auto makeButton = [](QString const& text) { + QToolButton* const button = new QToolButton; + button->setCheckable(true); + button->setText(text); + return button; + }; + QAbstractButton* const ignore = makeButton(tr("Ignore")); + QAbstractButton* const warn = makeButton(tr("Warn")); + QAbstractButton* const error = makeButton(tr("Error")); + + QButtonGroup* const buttonGroup = new QButtonGroup(treeWidget); + buttonGroup->addButton(ignore, cmDiagnosticAction::Ignore); + buttonGroup->addButton(warn, cmDiagnosticAction::Warn); + buttonGroup->addButton(error, cmDiagnosticAction::SendError); + buttonGroup->setExclusive(true); + this->buttons.emplace(i, buttonGroup); + + treeWidget->setItemWidget(item, 1, ignore); + treeWidget->setItemWidget(item, 2, warn); + treeWidget->setItemWidget(item, 3, error); + + cmDiagnosticAction const action = + this->cmakeInstance->getDiagnosticAction(category); + switch (action) { + case cmDiagnostics::SendError: + case cmDiagnostics::FatalError: + error->setChecked(true); + break; + case cmDiagnostics::Warn: + warn->setChecked(true); + break; + default: + ignore->setChecked(true); + break; + } + + items.emplace(i, item); + } + + for (auto const& ii : items) { + ii.second->setExpanded(true); + } } void WarningMessagesDialog::setupSignals() { -#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - static auto const checkStateChanged = &QCheckBox::checkStateChanged; -#else - static auto const checkStateChanged = &QCheckBox::stateChanged; -#endif QObject::connect(this->buttonBox, &QDialogButtonBox::accepted, this, &WarningMessagesDialog::doAccept); - QObject::connect(this->suppressDeveloperWarnings, checkStateChanged, this, - &WarningMessagesDialog::doSuppressDeveloperWarningsChanged); - QObject::connect( - this->suppressDeprecatedWarnings, checkStateChanged, this, - &WarningMessagesDialog::doSuppressDeprecatedWarningsChanged); - - QObject::connect(this->developerWarningsAsErrors, checkStateChanged, this, - &WarningMessagesDialog::doDeveloperWarningsAsErrorsChanged); - QObject::connect( - this->deprecatedWarningsAsErrors, checkStateChanged, this, - &WarningMessagesDialog::doDeprecatedWarningsAsErrorsChanged); } void WarningMessagesDialog::doAccept() { - this->cmakeInstance->setSuppressDevWarnings( - this->suppressDeveloperWarnings->isChecked()); - this->cmakeInstance->setSuppressDeprecatedWarnings( - this->suppressDeprecatedWarnings->isChecked()); - - this->cmakeInstance->setDevWarningsAsErrors( - this->developerWarningsAsErrors->isChecked()); - this->cmakeInstance->setDeprecatedWarningsAsErrors( - this->deprecatedWarningsAsErrors->isChecked()); -} - -void WarningMessagesDialog::doSuppressDeveloperWarningsChanged( - CheckState state) -{ - // no warnings implies no errors either - if (state) { - this->developerWarningsAsErrors->setChecked(false); - } -} - -void WarningMessagesDialog::doSuppressDeprecatedWarningsChanged( - CheckState state) -{ - // no warnings implies no errors either - if (state) { - this->deprecatedWarningsAsErrors->setChecked(false); - } -} - -void WarningMessagesDialog::doDeveloperWarningsAsErrorsChanged( - CheckState state) -{ - // warnings as errors implies warnings are not suppressed - if (state) { - this->suppressDeveloperWarnings->setChecked(false); - } -} - -void WarningMessagesDialog::doDeprecatedWarningsAsErrorsChanged( - CheckState state) -{ - // warnings as errors implies warnings are not suppressed - if (state) { - this->suppressDeprecatedWarnings->setChecked(false); + for (auto const& ii : this->buttons) { + this->cmakeInstance->setDiagnosticAction( + static_cast<cmDiagnosticCategory>(ii.first), + static_cast<cmDiagnosticAction>(ii.second->checkedId())); } }
diff --git a/Source/QtDialog/WarningMessagesDialog.h b/Source/QtDialog/WarningMessagesDialog.h index e5f0660..cf32841 100644 --- a/Source/QtDialog/WarningMessagesDialog.h +++ b/Source/QtDialog/WarningMessagesDialog.h
@@ -2,12 +2,17 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #pragma once -#include "QCMake.h" +#include <map> + #include <QDialog> #include <QWidget> #include "ui_WarningMessagesDialog.h" +class QButtonGroup; + +class QCMake; + /** * Dialog window for setting the warning message related options. */ @@ -17,12 +22,6 @@ { Q_OBJECT -#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - using CheckState = Qt::CheckState; -#else - using CheckState = int; -#endif - public: WarningMessagesDialog(QWidget* prnt, QCMake* instance); @@ -32,30 +31,9 @@ */ void doAccept(); - /** - * Handler for checked state changed event of the suppress developer warnings - * checkbox. - */ - void doSuppressDeveloperWarningsChanged(CheckState state); - /** - * Handler for checked state changed event of the suppress deprecated - * warnings checkbox. - */ - void doSuppressDeprecatedWarningsChanged(CheckState state); - - /** - * Handler for checked state changed event of the developer warnings as - * errors checkbox. - */ - void doDeveloperWarningsAsErrorsChanged(CheckState state); - /** - * Handler for checked state changed event of the deprecated warnings as - * errors checkbox. - */ - void doDeprecatedWarningsAsErrorsChanged(CheckState state); - private: QCMake* cmakeInstance; + std::map<unsigned, QButtonGroup*> buttons; /** * Set the initial values of the widgets on this dialog window, using the
diff --git a/Source/QtDialog/WarningMessagesDialog.ui b/Source/QtDialog/WarningMessagesDialog.ui index 3b35cbc..72c4939 100644 --- a/Source/QtDialog/WarningMessagesDialog.ui +++ b/Source/QtDialog/WarningMessagesDialog.ui
@@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>300</width> + <width>450</width> <height>300</height> </rect> </property> @@ -18,103 +18,54 @@ </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> - <widget class="QGroupBox" name="groupBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <widget class="QTreeWidget" name="treeWidget"> + <property name="showDropIndicator" stdset="0"> + <bool>false</bool> </property> - <property name="title"> - <string>Suppress Warnings</string> + <property name="alternatingRowColors"> + <bool>true</bool> </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QCheckBox" name="suppressDeveloperWarnings"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="toolTip"> - <string>Suppress developer (author) warnings.</string> - </property> - <property name="text"> - <string>Developer Warnings</string> - </property> - <property name="tristate"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="suppressDeprecatedWarnings"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="toolTip"> - <string>Suppress deprecated warnings.</string> - </property> - <property name="text"> - <string>Deprecated Warnings</string> - </property> - <property name="tristate"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="groupBox_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <property name="rootIsDecorated"> + <bool>false</bool> </property> - <property name="title"> - <string>Warnings as Errors</string> + <property name="uniformRowHeights"> + <bool>true</bool> </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <widget class="QCheckBox" name="developerWarningsAsErrors"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="toolTip"> - <string>Treat developer (author) warnings as errors.</string> - </property> - <property name="text"> - <string>Developer Warnings as Errors</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="deprecatedWarningsAsErrors"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="toolTip"> - <string>Treat deprecated warnings as errors.</string> - </property> - <property name="text"> - <string>Deprecated Warnings as Errors</string> - </property> - </widget> - </item> - </layout> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="expandsOnDoubleClick"> + <bool>false</bool> + </property> + <attribute name="headerVisible"> + <bool>false</bool> + </attribute> + <attribute name="headerStretchLastSection"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string notr="true">Category</string> + </property> + </column> + <column> + <property name="text"> + <string notr="true">Ignore</string> + </property> + </column> + <column> + <property name="text"> + <string notr="true">Warn</string> + </property> + </column> + <column> + <property name="text"> + <string notr="true">Error</string> + </property> + </column> </widget> </item> <item>
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 60ed118..46067d1 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx
@@ -208,10 +208,7 @@ return false; } if (cmp0175 == cmPolicies::WARN) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(msg, '\n', - cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + mf.IssuePolicyWarning(cmPolicies::CMP0175, msg); } } keywordExpectingValue = nullptr; @@ -381,10 +378,7 @@ return false; } if (cmp0175 == cmPolicies::WARN) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(msg, '\n', - cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + mf.IssuePolicyWarning(cmPolicies::CMP0175, msg); } } comment_buffer = copy; @@ -465,10 +459,7 @@ return false; } if (cmp0175 == cmPolicies::WARN) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(msg, ".\n", - cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + mf.IssuePolicyWarning(cmPolicies::CMP0175, cmStrCat(msg, '.')); } } mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends, @@ -503,10 +494,7 @@ return false; } if (cmp0175 == cmPolicies::WARN) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(msg, '\n', - cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + mf.IssuePolicyWarning(cmPolicies::CMP0175, msg); } } @@ -525,10 +513,7 @@ return false; } if (cmp0175 == cmPolicies::WARN) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(msg, ".\n", - cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + mf.IssuePolicyWarning(cmPolicies::CMP0175, cmStrCat(msg, '.')); } } auto const prePostCount = keywordsSeen.count(keyPRE_BUILD) + @@ -552,10 +537,9 @@ case cmCustomCommandType::POST_BUILD: msg += "POST_BUILD"; } - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(msg, " to preserve backward compatibility.\n", - cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + mf.IssuePolicyWarning( + cmPolicies::CMP0175, + cmStrCat(msg, " to preserve backward compatibility."_s)); } } mf.AddCustomCommandToTarget(target, cctype, std::move(cc)); @@ -576,10 +560,7 @@ return false; } if (cmp0175 == cmPolicies::WARN) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(msg, ".\n", - cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + mf.IssuePolicyWarning(cmPolicies::CMP0175, cmStrCat(msg, '.')); } } cc->SetOutputs(output);
diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx index 1c9d0c6..c9209b2 100644 --- a/Source/cmAddLibraryCommand.cxx +++ b/Source/cmAddLibraryCommand.cxx
@@ -2,6 +2,7 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmAddLibraryCommand.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" @@ -235,8 +236,8 @@ !mf.GetState()->GetGlobalPropertyAsBool("TARGET_SUPPORTS_SHARED_LIBS")) { switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0164)) { case cmPolicies::WARN: - mf.IssueMessage( - MessageType::AUTHOR_WARNING, + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat( "ADD_LIBRARY called with ", (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"),
diff --git a/Source/cmAddTestCommand.cxx b/Source/cmAddTestCommand.cxx index f2579e3..5663eaf 100644 --- a/Source/cmAddTestCommand.cxx +++ b/Source/cmAddTestCommand.cxx
@@ -7,6 +7,7 @@ #include <cm/memory> #include "cmExecutionStatus.h" +#include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmPolicies.h" #include "cmStringAlgorithms.h" @@ -90,6 +91,7 @@ std::vector<std::string> configurations; std::string working_directory; std::vector<std::string> command; + std::vector<std::string> buildDepends; bool command_expand_lists = false; cmPolicies::PolicyStatus cmp0178 = mf.GetPolicyStatus(cmPolicies::CMP0178); @@ -100,6 +102,7 @@ DoingCommand, DoingConfigs, DoingWorkingDirectory, + DoingBuildDepends, DoingCmp0178, DoingNone }; @@ -117,6 +120,8 @@ return false; } doing = DoingConfigs; + } else if (args[i] == "BUILD_DEPENDS") { + doing = DoingBuildDepends; } else if (args[i] == "WORKING_DIRECTORY") { if (!working_directory.empty()) { status.SetError(" may be given at most one WORKING_DIRECTORY."); @@ -139,6 +144,8 @@ command.push_back(args[i]); } else if (doing == DoingConfigs) { configurations.push_back(args[i]); + } else if (doing == DoingBuildDepends) { + buildDepends.push_back(args[i]); } else if (doing == DoingWorkingDirectory) { working_directory = args[i]; doing = DoingNone; @@ -185,6 +192,15 @@ test->SetProperty("WORKING_DIRECTORY", working_directory); } test->SetCommandExpandLists(command_expand_lists); + if (!buildDepends.empty()) { + if (!cmGeneratorExpression::IsValidTargetName(name)) { + status.SetError(cmStrCat("Cannot set build dependencies for a test with" + " NAME \"", + name, "\" which is not a valid target name.")); + return false; + } + test->SetBuildDependencies(buildDepends); + } mf.AddTestGenerator(cm::make_unique<cmTestGenerator>(test, configurations)); return true;
diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx index 0adcefd..374e476 100644 --- a/Source/cmArchiveWrite.cxx +++ b/Source/cmArchiveWrite.cxx
@@ -3,7 +3,6 @@ #include "cmArchiveWrite.h" #include <cstdlib> -#include <cstring> #include <ctime> #include <iostream> #include <limits> @@ -12,6 +11,7 @@ #include <thread> #include <cm/algorithm> +#include <cm/string_view> #include <cm3p/archive.h> #include <cm3p/archive_entry.h> @@ -43,6 +43,10 @@ { #ifdef _WIN32 // libarchive converts our UTF-8 encoding to the archive's encoding. + // `archive_entry_update_pathname_utf8` always populates the WCS form too. + // It also populates the MBS form if possible, but we ignore conversion + // failure because the archive formats support converting directly from + // the WCS form to the archive's encoding without using the MBS form. archive_entry_update_pathname_utf8(e, dest); #else // libarchive converts our locale's encoding to the archive's encoding. @@ -92,8 +96,9 @@ }; cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, - std::string const& format, int compressionLevel, - int numThreads) + std::string const& format, + std::string const& encoding, + int compressionLevel, int numThreads) : Stream(os) , Archive(archive_write_new()) , Disk(archive_read_disk_new()) @@ -222,6 +227,20 @@ } } + // 7zip always uses UTF16-LE for the headers and doesn't support + // header encoding specification. + // arbsd can use the default encoding of the system only. + if (!is7zip && format != "arbsd" && encoding != "OEM") { + char const* formatForOptions = format == "paxr" ? "pax" : format.c_str(); + if (archive_write_set_format_option(this->Archive, formatForOptions, + "hdrcharset", + encoding.c_str()) != ARCHIVE_OK) { + this->Error = cmStrCat("archive_write_set_format_option(hdrcharset): ", + cm_archive_error_string(this->Archive)); + return; + } + } + if (isFormatSupportsCompressionNatively || compressionLevel != 0) { std::string compressionLevelStr = std::to_string(compressionLevel); std::string archiveFilterName; @@ -399,15 +418,14 @@ if (!path.empty() && path.back() == '/') { path.erase(path.size() - 1); } - this->AddPath(path.c_str(), skip, prefix, recursive); + this->AddPath(path, skip, prefix, recursive); return this->Okay(); } -bool cmArchiveWrite::AddPath(char const* path, size_t skip, char const* prefix, - bool recursive) +bool cmArchiveWrite::AddPath(std::string const& path, size_t skip, + char const* prefix, bool recursive) { - if (strcmp(path, ".") != 0 || - (this->Format != "zip" && this->Format != "7zip")) { + if (path != "." || (this->Format != "zip" && this->Format != "7zip")) { if (!this->AddFile(path, skip, prefix)) { return false; } @@ -425,11 +443,11 @@ std::string::size_type end = next.size(); unsigned long n = d.GetNumberOfFiles(); for (unsigned long i = 0; i < n; ++i) { - char const* file = d.GetFile(i); - if (strcmp(file, ".") != 0 && strcmp(file, "..") != 0) { + std::string const& file = d.GetFileName(i); + if (file != "." && file != "..") { next.erase(end); next += file; - if (!this->AddPath(next.c_str(), skip, prefix)) { + if (!this->AddPath(next, skip, prefix)) { return false; } } @@ -438,15 +456,16 @@ return true; } -bool cmArchiveWrite::AddFile(char const* file, size_t skip, char const* prefix) +bool cmArchiveWrite::AddFile(std::string const& file, size_t skip, + char const* prefix) { this->Error = ""; // Skip the file if we have no name for it. This may happen on a // top-level directory, which does not need to be included anyway. - if (skip >= strlen(file)) { + if (skip >= file.length()) { return true; } - char const* out = file + skip; + cm::string_view out = cm::string_view(file).substr(skip); // Meta-data. std::string dest = cmStrCat(prefix ? prefix : "", out); @@ -538,9 +557,9 @@ return true; } -bool cmArchiveWrite::AddData(char const* file, size_t size) +bool cmArchiveWrite::AddData(std::string const& file, size_t size) { - cmsys::ifstream fin(file, std::ios::in | std::ios::binary); + cmsys::ifstream fin(file.c_str(), std::ios::in | std::ios::binary); if (!fin) { this->Error = cmStrCat("Error opening \"", file, "\": ", cmSystemTools::GetLastSystemError());
diff --git a/Source/cmArchiveWrite.h b/Source/cmArchiveWrite.h index dac0a57..dfefc72 100644 --- a/Source/cmArchiveWrite.h +++ b/Source/cmArchiveWrite.h
@@ -55,8 +55,9 @@ /** Construct with output stream to which to write archive. */ cmArchiveWrite(std::ostream& os, Compress c = CompressNone, - std::string const& format = "paxr", int compressionLevel = 0, - int numThreads = 1); + std::string const& format = "paxr", + std::string const& encoding = "UTF-8", + int compressionLevel = 0, int numThreads = 1); ~cmArchiveWrite(); @@ -144,10 +145,10 @@ private: bool Okay() const { return this->Error.empty(); } - bool AddPath(char const* path, size_t skip, char const* prefix, + bool AddPath(std::string const& path, size_t skip, char const* prefix, bool recursive = true); - bool AddFile(char const* file, size_t skip, char const* prefix); - bool AddData(char const* file, size_t size); + bool AddFile(std::string const& file, size_t skip, char const* prefix); + bool AddData(std::string const& file, size_t size); struct Callback; friend struct Callback;
diff --git a/Source/cmAuxSourceDirectoryCommand.cxx b/Source/cmAuxSourceDirectoryCommand.cxx index d571a1a..947dc7c 100644 --- a/Source/cmAuxSourceDirectoryCommand.cxx +++ b/Source/cmAuxSourceDirectoryCommand.cxx
@@ -46,7 +46,7 @@ if (dir.Load(tdir)) { size_t numfiles = dir.GetNumberOfFiles(); for (size_t i = 0; i < numfiles; ++i) { - std::string file = dir.GetFile(static_cast<unsigned long>(i)); + std::string const& file = dir.GetFileName(i); // Split the filename into base and extension std::string::size_type dotpos = file.rfind('.'); if (dotpos != std::string::npos) {
diff --git a/Source/cmBinUtilsLinker.cxx b/Source/cmBinUtilsLinker.cxx index 9766f8d..836c6ca 100644 --- a/Source/cmBinUtilsLinker.cxx +++ b/Source/cmBinUtilsLinker.cxx
@@ -7,7 +7,6 @@ #include "cmCMakePath.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmRuntimeDependencyArchive.h" #include "cmStringAlgorithms.h" @@ -34,14 +33,9 @@ cmPolicies::PolicyStatus policy = this->Archive->GetMakefile()->GetPolicyStatus(cmPolicies::CMP0207); if (policy == cmPolicies::WARN) { - this->Archive->GetMakefile()->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0207), - "\n" - "Path\n \"", - path, - "\"\n" - "would be converted to\n \"", + this->Archive->GetMakefile()->IssuePolicyWarning( + cmPolicies::CMP0207, {}, + cmStrCat("Path\n \"", path, "\"\nwould be converted to\n \"", normalizedPath, "\"\n")); } else if (policy == cmPolicies::NEW) { path = std::move(normalizedPath);
diff --git a/Source/cmBlockCommand.cxx b/Source/cmBlockCommand.cxx index de31848..575efe6 100644 --- a/Source/cmBlockCommand.cxx +++ b/Source/cmBlockCommand.cxx
@@ -25,7 +25,8 @@ enum class ScopeType : std::uint8_t { VARIABLES, - POLICIES + POLICIES, + DIAGNOSTICS, }; using ScopeSet = cm::enum_set<ScopeType>; @@ -41,6 +42,7 @@ private: std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope; std::unique_ptr<cmMakefile::VariablePushPop> VariableScope; + std::unique_ptr<cmMakefile::DiagnosticPushPop> DiagnosticScope; }; BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, ScopeSet const& scopes) @@ -51,6 +53,9 @@ if (scopes.contains(ScopeType::VARIABLES)) { this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf); } + if (scopes.contains(ScopeType::DIAGNOSTICS)) { + this->DiagnosticScope = cm::make_unique<cmMakefile::DiagnosticPushPop>(mf); + } } class cmBlockFunctionBlocker : public cmFunctionBlocker @@ -177,12 +182,17 @@ scopes.insert(ScopeType::POLICIES); continue; } + if (scope == "DIAGNOSTICS"_s) { + scopes.insert(ScopeType::DIAGNOSTICS); + continue; + } status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"')); cmSystemTools::SetFatalErrorOccurred(); return false; } } else { - scopes = { ScopeType::VARIABLES, ScopeType::POLICIES }; + scopes = { ScopeType::VARIABLES, ScopeType::POLICIES, + ScopeType::DIAGNOSTICS }; } if (!scopes.contains(ScopeType::VARIABLES) && !parsedArgs.Propagate.empty()) {
diff --git a/Source/cmBuildCommand.cxx b/Source/cmBuildCommand.cxx index d84418b..ed2a80d 100644 --- a/Source/cmBuildCommand.cxx +++ b/Source/cmBuildCommand.cxx
@@ -2,10 +2,10 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmBuildCommand.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -79,8 +79,9 @@ cmMakefile& mf = status.GetMakefile(); if (!project_name.empty()) { - mf.IssueMessage(MessageType::AUTHOR_WARNING, - "Ignoring PROJECT_NAME option because it has no effect."); + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + "Ignoring PROJECT_NAME option because it has no effect."); } std::string makecommand = mf.GetGlobalGenerator()->GenerateCMakeBuildCommand(
diff --git a/Source/cmBuildDatabase.cxx b/Source/cmBuildDatabase.cxx index b25ca27..f5ddafa 100644 --- a/Source/cmBuildDatabase.cxx +++ b/Source/cmBuildDatabase.cxx
@@ -17,8 +17,9 @@ #include "cmsys/FStream.hxx" #include "cmComputeLinkInformation.h" -#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGeneratedFileStream.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmListFileCache.h" @@ -449,9 +450,9 @@ bool isPrivate = true; if (sf->GetLanguage() == "CXX"_s) { auto const* fs = gt->GetFileSetForSource(config, sf); - if (fs && fs->GetType() == "CXX_MODULES"_s) { + if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) { isCXXModule = true; - isPrivate = !cmFileSetVisibilityIsForInterface(fs->GetVisibility()); + isPrivate = !fs->IsForInterface(); } }
diff --git a/Source/cmBuildSbomBuilder.cxx b/Source/cmBuildSbomBuilder.cxx new file mode 100644 index 0000000..fbec0e1 --- /dev/null +++ b/Source/cmBuildSbomBuilder.cxx
@@ -0,0 +1,41 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmBuildSbomBuilder.h" + +#include <utility> + +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSbomArguments.h" + +cmBuildSbomBuilder::cmBuildSbomBuilder(cmSbomArguments args, + std::vector<cmExportSet*> exportSets, + cmLocalGenerator* lg) + : cmSbomBuilder(std::move(args), std::move(exportSets), lg) +{ +} + +bool cmBuildSbomBuilder::Generate(std::ostream& os) +{ + if (!this->LocalGenerator) { + return false; + } + return this->GenerateForTargets(os, cmGeneratorExpression::BuildInterface); +} + +cmExportFileGenerator::ExportInfo cmBuildSbomBuilder::FindExportInfoFor( + cmGeneratorTarget const* target) const +{ + return target->GetLocalGenerator() + ->GetGlobalGenerator() + ->FindBuildExportInfo(target); +} + +cmSbomBuilder::SbomInfo cmBuildSbomBuilder::FindSbomInfoFor( + cmGeneratorTarget const* target) const +{ + return target->GetLocalGenerator()->GetGlobalGenerator()->FindBuildSbomInfo( + target); +}
diff --git a/Source/cmBuildSbomBuilder.h b/Source/cmBuildSbomBuilder.h new file mode 100644 index 0000000..6cca846 --- /dev/null +++ b/Source/cmBuildSbomBuilder.h
@@ -0,0 +1,32 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <vector> + +#include "cmExportFileGenerator.h" +#include "cmSbomBuilder.h" + +class cmExportSet; +class cmLocalGenerator; +class cmSbomArguments; + +/** Build-tree SBOM (`export(SBOM ...)`). Covers the targets in the + * associated export sets. */ +class cmBuildSbomBuilder final : public cmSbomBuilder +{ +public: + cmBuildSbomBuilder(cmSbomArguments args, + std::vector<cmExportSet*> exportSets, + cmLocalGenerator* lg = nullptr); + + bool Generate(std::ostream& os) override; + +protected: + cmExportFileGenerator::ExportInfo FindExportInfoFor( + cmGeneratorTarget const* target) const override; + SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const override; +};
diff --git a/Source/cmBuildSbomGenerator.cxx b/Source/cmBuildSbomGenerator.cxx new file mode 100644 index 0000000..d77e583 --- /dev/null +++ b/Source/cmBuildSbomGenerator.cxx
@@ -0,0 +1,16 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmBuildSbomGenerator.h" + +#include "cmGeneratedFileStream.h" + +void cmBuildSbomGenerator::Compute(cmLocalGenerator* lg) +{ + this->Builder->Compute(lg); +} + +bool cmBuildSbomGenerator::GenerateForBuild() +{ + cmGeneratedFileStream os(this->OutputFile); + return this->Builder->Generate(os); +}
diff --git a/Source/cmBuildSbomGenerator.h b/Source/cmBuildSbomGenerator.h new file mode 100644 index 0000000..d3623c1 --- /dev/null +++ b/Source/cmBuildSbomGenerator.h
@@ -0,0 +1,59 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> +#include <utility> +#include <vector> + +#include <cm/memory> + +#include "cmBuildSbomBuilder.h" +#include "cmSbomArguments.h" + +class cmExportSet; +class cmGeneratorTarget; +class cmLocalGenerator; + +/** \class cmBuildSbomGenerator + * \brief Thin wrapper around cmBuildSbomBuilder for the build-tree SBOM case. + * + * Stored on cmMakefile at configure time. At generate time, + * ComputeBuildFileGenerators() calls Compute(lg) to resolve export set + * targets and supply the local generator - mirroring + * cmExportBuildFileGenerator. + */ +class cmBuildSbomGenerator +{ +public: + cmBuildSbomGenerator(cmSbomArguments args, + std::vector<cmExportSet*> exportSets, + std::string outputFile) + : OutputFile(std::move(outputFile)) + , Builder(cm::make_unique<cmBuildSbomBuilder>(std::move(args), + std::move(exportSets))) + { + } + + void Compute(cmLocalGenerator* lg); + + std::string const& GetOutputFile() const { return this->OutputFile; } + + /** True if this SBOM directly describes `target`. Used by peer SBOMs to + * attribute cross-references when install(export) provenance is absent. */ + bool CoversTarget(cmGeneratorTarget const* target) const + { + return this->Builder->CoversTarget(target); + } + std::string const& GetPackageName() const + { + return this->Builder->GetPackageName(); + } + + /** Open the output file and write the SBOM document to it. */ + bool GenerateForBuild(); + +private: + std::string OutputFile; + std::unique_ptr<cmBuildSbomBuilder> Builder; +};
diff --git a/Source/cmCMakeDiagnosticCommand.cxx b/Source/cmCMakeDiagnosticCommand.cxx new file mode 100644 index 0000000..d05a489 --- /dev/null +++ b/Source/cmCMakeDiagnosticCommand.cxx
@@ -0,0 +1,129 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmCMakeDiagnosticCommand.h" + +#include <cm/optional> +#include <cmext/string_view> + +#include "cmDiagnostics.h" +#include "cmExecutionStatus.h" +#include "cmMakefile.h" +#include "cmStringAlgorithms.h" + +namespace { +using AlterFunction = bool (cmMakefile::*)(cmDiagnosticCategory, + cmDiagnosticAction, bool); + +bool HandleAlterMode(std::vector<std::string> const& args, + cmExecutionStatus& status, AlterFunction function, + bool recursive) +{ + if (args.size() < 3 || args.size() > 4) { + status.SetError(cmStrCat( + args[0], " must be given exactly 2 or 3 additional arguments."_s)); + return false; + } + + if (args.size() == 4) { + if (args[3] == "RECURSE") { + recursive = true; + } else if (args[3] == "NO_RECURSE") { + recursive = false; + } else { + status.SetError( + cmStrCat(args[0], " given unknown option \""_s, args[3], "\"."_s)); + return false; + } + } + + cm::optional<cmDiagnosticAction> const action = + cmDiagnostics::GetDiagnosticAction(args[2]); + if (!action) { + status.SetError(cmStrCat( + args[0], " given unrecognized diagnostic action \"", args[2], "\"."_s)); + return false; + } + + cm::optional<cmDiagnosticCategory> const category = + cmDiagnostics::GetDiagnosticCategory(args[1]); + if (!category) { + status.SetError(cmStrCat(args[0], + " given unrecognized diagnostic category \"", + args[1], "\"."_s)); + return false; + } + + if (!(status.GetMakefile().*function)(*category, *action, recursive)) { + status.SetError(cmStrCat(args[0], " failed to set diagnostic action."_s)); + return false; + } + return true; +} + +bool HandleGetMode(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() != 3) { + status.SetError("GET must be given exactly 2 additional arguments."); + return false; + } + + cm::optional<cmDiagnosticCategory> const category = + cmDiagnostics::GetDiagnosticCategory(args[1]); + if (!category) { + status.SetError(cmStrCat(args[0], + " given unrecognized diagnostic category \"", + args[1], "\"."_s)); + return false; + } + + cmDiagnosticAction const action = + status.GetMakefile().GetDiagnosticAction(*category); + + status.GetMakefile().AddDefinition(args[2], + cmDiagnostics::GetActionString(action)); + return true; +} +} + +// cmCMakeDiagnosticCommand +bool cmCMakeDiagnosticCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.empty()) { + status.SetError("requires at least one argument."); + return false; + } + + if (args[0] == "SET") { + return HandleAlterMode(args, status, &cmMakefile::SetDiagnostic, false); + } + if (args[0] == "PROMOTE") { + return HandleAlterMode(args, status, &cmMakefile::PromoteDiagnostic, true); + } + if (args[0] == "DEMOTE") { + return HandleAlterMode(args, status, &cmMakefile::DemoteDiagnostic, true); + } + if (args[0] == "GET") { + return HandleGetMode(args, status); + } + if (args[0] == "PUSH") { + if (args.size() > 1) { + status.SetError("PUSH may not be given additional arguments."); + return false; + } + status.GetMakefile().PushDiagnostic(); + return true; + } + if (args[0] == "POP") { + if (args.size() > 1) { + status.SetError("POP may not be given additional arguments."); + return false; + } + status.GetMakefile().PopDiagnostic(); + return true; + } + + status.SetError(cmStrCat("given unknown first argument \"", args[0], '"')); + return false; +}
diff --git a/Source/cmCMakeDiagnosticCommand.h b/Source/cmCMakeDiagnosticCommand.h new file mode 100644 index 0000000..c5d6e8a --- /dev/null +++ b/Source/cmCMakeDiagnosticCommand.h
@@ -0,0 +1,18 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +class cmExecutionStatus; + +/** + * \brief Set how CMake should handle diagnostics + * + * cmCMakeDiagnosticCommand sets how CMake should deal with diagnostics. + */ +bool cmCMakeDiagnosticCommand(std::vector<std::string> const& args, + cmExecutionStatus& status);
diff --git a/Source/cmCMakeMinimumRequired.cxx b/Source/cmCMakeMinimumRequired.cxx index 1a68f03..b9ab7a9 100644 --- a/Source/cmCMakeMinimumRequired.cxx +++ b/Source/cmCMakeMinimumRequired.cxx
@@ -5,6 +5,7 @@ #include <cstdio> #include <sstream> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -113,8 +114,8 @@ } if (required_major < 2 || (required_major == 2 && required_minor < 4)) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, "Compatibility with CMake < 2.4 is not supported by CMake >= 3.0."); status.GetMakefile().SetPolicyVersion("2.4", version_max); } else {
diff --git a/Source/cmCMakePolicyCommand.cxx b/Source/cmCMakePolicyCommand.cxx index 9b346d9..6fe421e 100644 --- a/Source/cmCMakePolicyCommand.cxx +++ b/Source/cmCMakePolicyCommand.cxx
@@ -2,9 +2,14 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmCMakePolicyCommand.h" +#include <cmext/string_view> + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmStringAlgorithms.h" namespace { @@ -16,6 +21,8 @@ cmExecutionStatus& status); bool HandleGetWarningMode(std::vector<std::string> const& args, cmExecutionStatus& status); +bool HandleIssueWarningMode(std::vector<std::string> const& args, + cmExecutionStatus& status); } // cmCMakePolicyCommand @@ -55,6 +62,9 @@ if (args[0] == "GET_WARNING") { return HandleGetWarningMode(args, status); } + if (args[0] == "ISSUE_WARNING") { + return HandleIssueWarningMode(args, status); + } status.SetError(cmStrCat("given unknown first argument \"", args[0], '"')); return false; @@ -191,4 +201,45 @@ return true; } + +bool HandleIssueWarningMode(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 2) { + status.SetError( + "ISSUE_WARNING must be given at least one additional argument."); + return false; + } + + // Get arguments. + std::string const& id = args[1]; + + struct Arguments + { + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreArgs; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostArgs; + }; + + static auto const parser = cmArgumentParser<Arguments>{} + .Bind("PRE"_s, &Arguments::PreArgs) + .Bind("POST"_s, &Arguments::PostArgs); + + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2), + /*unparsedArguments=*/nullptr); + + // Lookup the policy number. + cmPolicies::PolicyID pid; + if (!cmPolicies::GetPolicyID(id.c_str(), pid)) { + status.SetError( + cmStrCat("ISSUE_WARNING given policy \"", id, + "\" which is not known to this version of CMake.")); + return false; + } + + // Issue the policy warning. + status.GetMakefile().IssuePolicyWarning(pid, cmJoin(arguments.PreArgs, {}), + cmJoin(arguments.PostArgs, {})); + + return true; +} }
diff --git a/Source/cmCMakePresetsArgs.h b/Source/cmCMakePresetsArgs.h new file mode 100644 index 0000000..7228c9d --- /dev/null +++ b/Source/cmCMakePresetsArgs.h
@@ -0,0 +1,78 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> + +class cmCMakePresetsArgsBase +{ +public: + virtual ~cmCMakePresetsArgsBase() = default; + + virtual bool HasPresetsArg() const { return !this->PresetName.empty(); }; + virtual void Clear() { this->PresetName.clear(); } + + std::string PresetName; + std::string PresetsFile; + +protected: + cmCMakePresetsArgsBase() = default; +}; + +class cmCMakePresetsArgs : public cmCMakePresetsArgsBase +{ +public: + bool HasPresetsArg() const override + { + return this->cmCMakePresetsArgsBase::HasPresetsArg() || this->ListPresets; + } + + void Clear() override + { + this->cmCMakePresetsArgsBase::Clear(); + this->ListPresets = false; + } + + bool ListPresets = false; +}; + +class cmCMakePresetsConfigureArgs : public cmCMakePresetsArgsBase +{ +public: + enum class ListPresetsOption + { + None, + Configure, + Build, + Test, + Package, + Workflow, + All, + }; + + bool HasPresetsArg() const override + { + return this->cmCMakePresetsArgsBase::HasPresetsArg() || + this->ListPresets != ListPresetsOption::None; + } + + void Clear() override + { + this->cmCMakePresetsArgsBase::Clear(); + this->ListPresets = ListPresetsOption::None; + } + + ListPresetsOption ListPresets = ListPresetsOption::None; +}; + +class cmCMakePresetsWorkflowArgs : public cmCMakePresetsArgs +{ +public: + void Clear() override + { + this->cmCMakePresetsArgs::Clear(); + this->Fresh = false; + } + + bool Fresh = false; +};
diff --git a/Source/cmCMakePresetsErrors.cxx b/Source/cmCMakePresetsErrors.cxx index f2cbe84..be3d193 100644 --- a/Source/cmCMakePresetsErrors.cxx +++ b/Source/cmCMakePresetsErrors.cxx
@@ -80,9 +80,16 @@ state->AddErrorAtValue("Invalid preset", value); } -void INVALID_PRESET_NAMED(std::string const& presetName, cmJSONState* state) +void INVALID_PRESET_NAMED(std::string const& presetName, + std::string const& kind, cmJSONState* state, + std::string const& detail) { - state->AddError(cmStrCat("Invalid preset: \"", presetName, '"')); + std::string err_msg = + cmStrCat("Invalid ", kind, " preset: \"", presetName, '"'); + if (!detail.empty()) { + err_msg = cmStrCat(err_msg, ": ", detail); + } + state->AddError(err_msg); } void INVALID_VARIABLE(Json::Value const* value, cmJSONState* state) @@ -97,17 +104,18 @@ } void CYCLIC_PRESET_INHERITANCE(std::string const& presetName, - cmJSONState* state) + std::string const& kind, cmJSONState* state) { - state->AddError( - cmStrCat("Cyclic preset inheritance for preset \"", presetName, '"')); + state->AddError(cmStrCat("Cyclic preset inheritance for ", kind, + " preset \"", presetName, '"')); } void INHERITED_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName, + std::string const& kind, cmJSONState* state) { - state->AddError(cmStrCat("Inherited preset \"", presetName, + state->AddError(cmStrCat("Inherited ", kind, " preset \"", presetName, "\" is unreachable from preset's file")); } @@ -189,9 +197,15 @@ "processor-count-based jobs preset support"); } +void PASSTHROUGH_ARGS_UNSUPPORTED(cmJSONState* state) +{ + state->AddError("File version must be 12 or higher for " + "testPassthroughArguments preset support"); +} + void CYCLIC_INCLUDE(std::string const& file, cmJSONState* state) { - state->AddError(cmStrCat("Cyclic include among preset files: ", file)); + state->AddError(cmStrCat("Cyclic include among presets files: ", file)); } void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state) @@ -329,4 +343,18 @@ { state->AddError("File version must be 8 or higher for $schema support"); } + +void DIAGNOSTIC_UNSUPPORTED(cm::string_view name, cm::string_view context, + int version, cmJSONState* state) +{ + state->AddError(cmStrCat("File version must be ", std::to_string(version), + " or higher for ", context, '.', name, " support")); +} + +void DIAGNOSTIC_REMOVED(cm::string_view name, cm::string_view context, + int version, cmJSONState* state) +{ + state->AddError(cmStrCat("File version must be ", std::to_string(version), + " or lower for ", context, '.', name, " support")); +} }
diff --git a/Source/cmCMakePresetsErrors.h b/Source/cmCMakePresetsErrors.h index 372ceb8..e8ca3e5 100644 --- a/Source/cmCMakePresetsErrors.h +++ b/Source/cmCMakePresetsErrors.h
@@ -6,6 +6,8 @@ #include <string> +#include <cm/string_view> + #include <cm3p/json/value.h> #include "cmJSONHelpers.h" @@ -33,16 +35,19 @@ void INVALID_PRESET(Json::Value const* value, cmJSONState* state); -void INVALID_PRESET_NAMED(std::string const& presetName, cmJSONState* state); +void INVALID_PRESET_NAMED(std::string const& presetName, + std::string const& kind, cmJSONState* state, + std::string const& detail); void INVALID_VARIABLE(Json::Value const* value, cmJSONState* state); void DUPLICATE_PRESETS(std::string const& presetName, cmJSONState* state); void CYCLIC_PRESET_INHERITANCE(std::string const& presetName, - cmJSONState* state); + std::string const& kind, cmJSONState* state); void INHERITED_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName, + std::string const& kind, cmJSONState* state); void CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName, @@ -74,6 +79,8 @@ void JOBS_PROC_UNSUPPORTED(cmJSONState* state); +void PASSTHROUGH_ARGS_UNSUPPORTED(cmJSONState* state); + void CYCLIC_INCLUDE(std::string const& file, cmJSONState* state); void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state); @@ -121,4 +128,10 @@ std::string const& missingField, cmJSONState* state); void SCHEMA_UNSUPPORTED(cmJSONState* state); + +void DIAGNOSTIC_UNSUPPORTED(cm::string_view name, cm::string_view context, + int version, cmJSONState* state); + +void DIAGNOSTIC_REMOVED(cm::string_view name, cm::string_view context, + int version, cmJSONState* state); }
diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx index 870315f..98dc6c6 100644 --- a/Source/cmCMakePresetsGraph.cxx +++ b/Source/cmCMakePresetsGraph.cxx
@@ -4,8 +4,8 @@ #include <algorithm> #include <cassert> -#include <cstddef> #include <functional> +#include <iomanip> #include <iostream> #include <iterator> #include <utility> @@ -27,6 +27,8 @@ case ExpandMacroResult::Ignore: \ out.reset(); \ return true; \ + case ExpandMacroResult::Defer: \ + CM_FALLTHROUGH; \ case ExpandMacroResult::Ok: \ break; \ } \ @@ -54,8 +56,13 @@ template <typename T> using PresetMacroExpander = cmCMakePresetsGraphInternal::PresetMacroExpander<T>; +template <typename T> +using ImmediateMacroExpander = + cmCMakePresetsGraphInternal::ImmediateMacroExpander<T>; using cmCMakePresetsGraphInternal::ExpandMacros; +bool gSkipNewLine = true; + void InheritString(std::string& child, std::string const& parent) { if (child.empty()) { @@ -80,13 +87,21 @@ } } +template <typename K, typename V> +void InheritMap(std::map<K, V>& child, std::map<K, V> const& parent) +{ + for (auto const& item : parent) { + child.insert(item); + } +} + /** * Check preset inheritance for cycles (using a DAG check algorithm) while * also bubbling up fields through the inheritance hierarchy, then verify * that each preset has the required fields, either directly or through * inheritance. */ -template <class T> +template <typename T> bool VisitPreset( T& preset, std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets, @@ -94,8 +109,8 @@ { switch (cycleStatus[preset.Name]) { case CycleStatus::InProgress: - cmCMakePresetsErrors::CYCLIC_PRESET_INHERITANCE(preset.Name, - &graph.parseState); + cmCMakePresetsErrors::CYCLIC_PRESET_INHERITANCE( + preset.Name, preset.kind(), &graph.parseState); return false; case CycleStatus::Verified: return true; @@ -106,28 +121,32 @@ cycleStatus[preset.Name] = CycleStatus::InProgress; if (preset.Environment.count("") != 0) { - cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState); + cmCMakePresetsErrors::INVALID_PRESET_NAMED( + preset.Name, preset.kind(), &graph.parseState, + "Empty environment variable names are not allowed"); return false; } bool result = preset.VisitPresetBeforeInherit(); if (!result) { - cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState); + cmCMakePresetsErrors::INVALID_PRESET_NAMED( + preset.Name, preset.kind(), &graph.parseState, preset.ErrorDetail); return false; } for (auto const& i : preset.Inherits) { auto parent = presets.find(i); if (parent == presets.end()) { - cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, - &graph.parseState); + cmCMakePresetsErrors::INVALID_PRESET_NAMED( + preset.Name, preset.kind(), &graph.parseState, + cmStrCat("Could not find inherited preset \"", i, "\"")); return false; } auto& parentPreset = parent->second.Unexpanded; if (!preset.OriginFile->ReachableFiles.count(parentPreset.OriginFile)) { cmCMakePresetsErrors::INHERITED_PRESET_UNREACHABLE_FROM_FILE( - preset.Name, &graph.parseState); + preset.Name, preset.kind(), &graph.parseState); return false; } @@ -137,14 +156,12 @@ result = preset.VisitPresetInherit(parentPreset); if (!result) { - cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, - &graph.parseState); + cmCMakePresetsErrors::INVALID_PRESET_NAMED( + preset.Name, preset.kind(), &graph.parseState, preset.ErrorDetail); return false; } - for (auto const& v : parentPreset.Environment) { - preset.Environment.insert(v); - } + InheritMap(preset.Environment, parentPreset.Environment); if (!preset.ConditionEvaluator) { preset.ConditionEvaluator = parentPreset.ConditionEvaluator; @@ -158,7 +175,8 @@ result = preset.VisitPresetAfterInherit(graph.GetVersion(preset), &graph.parseState); if (!result) { - cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState); + cmCMakePresetsErrors::INVALID_PRESET_NAMED( + preset.Name, preset.kind(), &graph.parseState, preset.ErrorDetail); return false; } @@ -166,7 +184,7 @@ return true; } -template <class T> +template <typename T> bool ComputePresetInheritance( std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets, cmCMakePresetsGraph& graph) @@ -210,7 +228,7 @@ ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status, MacroExpanderVector const& macroExpanders, int version); -template <class T> +template <typename T> class EnvironmentMacroExpander : public MacroExpander { std::map<std::string, CycleStatus>& EnvCycles; @@ -259,156 +277,165 @@ } }; -bool ExpandMacros(cmCMakePresetsGraph const& graph, - ConfigurePreset const& preset, - cm::optional<ConfigurePreset>& out, - MacroExpanderVector const& macroExpanders) +bool CheckExpandMacros(ConfigurePreset const& preset, + cm::optional<ConfigurePreset>& out, + MacroExpanderVector const& macroExpanders, + cmCMakePresetsGraph const* graph = nullptr) { + int version = graph ? graph->GetVersion(preset) : preset.OriginFile->Version; + std::string binaryDir = preset.BinaryDir; - CHECK_EXPAND(out, binaryDir, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, binaryDir, macroExpanders, version); if (!binaryDir.empty()) { - if (!cmSystemTools::FileIsFullPath(binaryDir)) { - binaryDir = cmStrCat(graph.SourceDir, '/', binaryDir); + if (graph) { + if (!cmSystemTools::FileIsFullPath(binaryDir)) { + binaryDir = cmStrCat(graph->SourceDir, '/', binaryDir); + } + out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir); + cmSystemTools::ConvertToUnixSlashes(out->BinaryDir); + } else { + // Defer path normalization until the graph is merged, but store the + // intermediate result. + out->BinaryDir = binaryDir; } - out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir); - cmSystemTools::ConvertToUnixSlashes(out->BinaryDir); } if (!preset.InstallDir.empty()) { std::string installDir = preset.InstallDir; - CHECK_EXPAND(out, installDir, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, installDir, macroExpanders, version); - if (!cmSystemTools::FileIsFullPath(installDir)) { - installDir = cmStrCat(graph.SourceDir, '/', installDir); + if (graph) { + if (!cmSystemTools::FileIsFullPath(installDir)) { + installDir = cmStrCat(graph->SourceDir, '/', installDir); + } + out->InstallDir = cmSystemTools::CollapseFullPath(installDir); + cmSystemTools::ConvertToUnixSlashes(out->InstallDir); + } else { + // Defer path normalization until the graph is merged, but store the + // intermediate result. + out->InstallDir = installDir; } - out->InstallDir = cmSystemTools::CollapseFullPath(installDir); - cmSystemTools::ConvertToUnixSlashes(out->InstallDir); } if (!preset.ToolchainFile.empty()) { std::string toolchain = preset.ToolchainFile; - CHECK_EXPAND(out, toolchain, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, toolchain, macroExpanders, version); out->ToolchainFile = toolchain; } if (!preset.GraphVizFile.empty()) { std::string graphVizFile = preset.GraphVizFile; - CHECK_EXPAND(out, graphVizFile, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, graphVizFile, macroExpanders, version); out->GraphVizFile = graphVizFile; } for (auto& variable : out->CacheVariables) { if (variable.second) { - CHECK_EXPAND(out, variable.second->Value, macroExpanders, - graph.GetVersion(preset)); + CHECK_EXPAND(out, variable.second->Value, macroExpanders, version); } } return true; } -bool ExpandMacros(cmCMakePresetsGraph const& graph, BuildPreset const& preset, - cm::optional<BuildPreset>& out, - MacroExpanderVector const& macroExpanders) +bool CheckExpandMacros(BuildPreset const& preset, + cm::optional<BuildPreset>& out, + MacroExpanderVector const& macroExpanders, + cmCMakePresetsGraph const* graph = nullptr) { + int version = graph ? graph->GetVersion(preset) : preset.OriginFile->Version; + for (auto& target : out->Targets) { - CHECK_EXPAND(out, target, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, target, macroExpanders, version); } for (auto& nativeToolOption : out->NativeToolOptions) { - CHECK_EXPAND(out, nativeToolOption, macroExpanders, - graph.GetVersion(preset)); + CHECK_EXPAND(out, nativeToolOption, macroExpanders, version); } return true; } -bool ExpandMacros(cmCMakePresetsGraph const& graph, TestPreset const& preset, - cm::optional<TestPreset>& out, - MacroExpanderVector const& macroExpanders) +bool CheckExpandMacros(TestPreset const& preset, cm::optional<TestPreset>& out, + MacroExpanderVector const& macroExpanders, + cmCMakePresetsGraph const* graph = nullptr) { + int version = graph ? graph->GetVersion(preset) : preset.OriginFile->Version; + for (auto& overwrite : out->OverwriteConfigurationFile) { - CHECK_EXPAND(out, overwrite, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, overwrite, macroExpanders, version); } if (out->Output) { - CHECK_EXPAND(out, out->Output->OutputLogFile, macroExpanders, - graph.GetVersion(preset)); - CHECK_EXPAND(out, out->Output->OutputJUnitFile, macroExpanders, - graph.GetVersion(preset)); + CHECK_EXPAND(out, out->Output->OutputLogFile, macroExpanders, version); + CHECK_EXPAND(out, out->Output->OutputJUnitFile, macroExpanders, version); } if (out->Filter) { if (out->Filter->Include) { - CHECK_EXPAND(out, out->Filter->Include->Name, macroExpanders, - graph.GetVersion(preset)); - CHECK_EXPAND(out, out->Filter->Include->Label, macroExpanders, - graph.GetVersion(preset)); + CHECK_EXPAND(out, out->Filter->Include->Name, macroExpanders, version); + CHECK_EXPAND(out, out->Filter->Include->Label, macroExpanders, version); if (out->Filter->Include->Index) { CHECK_EXPAND(out, out->Filter->Include->Index->IndexFile, - macroExpanders, graph.GetVersion(preset)); + macroExpanders, version); } } if (out->Filter->Exclude) { - CHECK_EXPAND(out, out->Filter->Exclude->Name, macroExpanders, - graph.GetVersion(preset)); - CHECK_EXPAND(out, out->Filter->Exclude->Label, macroExpanders, - graph.GetVersion(preset)); + CHECK_EXPAND(out, out->Filter->Exclude->Name, macroExpanders, version); + CHECK_EXPAND(out, out->Filter->Exclude->Label, macroExpanders, version); if (out->Filter->Exclude->Fixtures) { CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Any, macroExpanders, - graph.GetVersion(preset)); + version); CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Setup, - macroExpanders, graph.GetVersion(preset)); + macroExpanders, version); CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Cleanup, - macroExpanders, graph.GetVersion(preset)); + macroExpanders, version); } } } if (out->Execution) { CHECK_EXPAND(out, out->Execution->ResourceSpecFile, macroExpanders, - graph.GetVersion(preset)); + version); } return true; } -bool ExpandMacros(cmCMakePresetsGraph const& graph, - PackagePreset const& preset, - cm::optional<PackagePreset>& out, - MacroExpanderVector const& macroExpanders) +bool CheckExpandMacros(PackagePreset const& preset, + cm::optional<PackagePreset>& out, + MacroExpanderVector const& macroExpanders, + cmCMakePresetsGraph const* graph = nullptr) { + int version = graph ? graph->GetVersion(preset) : preset.OriginFile->Version; + for (auto& variable : out->Variables) { - CHECK_EXPAND(out, variable.second, macroExpanders, - graph.GetVersion(preset)); + CHECK_EXPAND(out, variable.second, macroExpanders, version); } - CHECK_EXPAND(out, out->ConfigFile, macroExpanders, graph.GetVersion(preset)); - CHECK_EXPAND(out, out->PackageName, macroExpanders, - graph.GetVersion(preset)); - CHECK_EXPAND(out, out->PackageVersion, macroExpanders, - graph.GetVersion(preset)); - CHECK_EXPAND(out, out->PackageDirectory, macroExpanders, - graph.GetVersion(preset)); - CHECK_EXPAND(out, out->VendorName, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, out->ConfigFile, macroExpanders, version); + CHECK_EXPAND(out, out->PackageName, macroExpanders, version); + CHECK_EXPAND(out, out->PackageVersion, macroExpanders, version); + CHECK_EXPAND(out, out->PackageDirectory, macroExpanders, version); + CHECK_EXPAND(out, out->VendorName, macroExpanders, version); return true; } -bool ExpandMacros(cmCMakePresetsGraph const& /*graph*/, - WorkflowPreset const& /*preset*/, - cm::optional<WorkflowPreset>& /*out*/, - MacroExpanderVector const& /*macroExpanders*/) +bool CheckExpandMacros(WorkflowPreset const& /*preset*/, + cm::optional<WorkflowPreset>& /*out*/, + MacroExpanderVector const& /*macroExpanders*/, + cmCMakePresetsGraph const* /*graph*/ = nullptr) { return true; } -template <class T> -bool ExpandMacros(cmCMakePresetsGraph& graph, T const& preset, +template <typename T> +bool ExpandMacros(cmCMakePresetsGraph* graph, T const& preset, cm::optional<T>& out) { out.emplace(preset); @@ -420,23 +447,26 @@ MacroExpanderVector macroExpanders{}; - macroExpanders.push_back(cm::make_unique<BaseMacroExpander>(graph)); + macroExpanders.push_back(cm::make_unique<BaseMacroExpander>(*graph)); macroExpanders.push_back( - cm::make_unique<PresetMacroExpander<T>>(graph, preset)); + cm::make_unique<PresetMacroExpander<T>>(*graph, preset)); macroExpanders.push_back(cm::make_unique<EnvironmentMacroExpander<T>>( macroExpanders, out, envCycles)); for (auto& v : out->Environment) { if (v.second) { switch (VisitEnv(*v.second, envCycles[v.first], macroExpanders, - graph.GetVersion(preset))) { + graph->GetVersion(preset))) { case ExpandMacroResult::Error: - cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, - &graph.parseState); + cmCMakePresetsErrors::INVALID_PRESET_NAMED( + preset.Name, preset.kind(), &graph->parseState, + "Invalid macro expansion"); return false; case ExpandMacroResult::Ignore: out.reset(); return true; + case ExpandMacroResult::Defer: + CM_FALLTHROUGH; case ExpandMacroResult::Ok: break; } @@ -446,9 +476,9 @@ if (preset.ConditionEvaluator) { cm::optional<bool> result; if (!preset.ConditionEvaluator->Evaluate( - macroExpanders, graph.GetVersion(preset), result)) { - cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, - &graph.parseState); + macroExpanders, graph->GetVersion(preset), result)) { + cmCMakePresetsErrors::INVALID_PRESET_NAMED( + preset.Name, preset.kind(), &graph->parseState, "Invalid condition"); return false; } if (!result) { @@ -458,7 +488,7 @@ out->ConditionResult = *result; } - return ExpandMacros(graph, preset, out, macroExpanders); + return CheckExpandMacros(preset, out, macroExpanders, graph); } ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status, @@ -480,8 +510,95 @@ status = CycleStatus::Verified; return ExpandMacroResult::Ok; } + +void PrintPresets( + std::vector<cmCMakePresetsGraph::Preset const*> const& presets) +{ + if (presets.empty()) { + return; + } + + auto presetWithLongestName = + std::max_element(presets.begin(), presets.end(), + [](cmCMakePresetsGraph::Preset const* a, + cmCMakePresetsGraph::Preset const* b) { + return a->Name.length() < b->Name.length(); + }); + auto longestLength = (*presetWithLongestName)->Name.length(); + + for (auto const* preset : presets) { + std::string name = cmStrCat(" \"", preset->Name, '"'); + if (!preset->DisplayName.empty()) { + int const width = static_cast<int>(longestLength + name.length() - + preset->Name.length()); + std::cout << std::left << std::setw(width) << name << " - " + << preset->DisplayName << '\n'; + } else { + std::cout << name << '\n'; + } + } } +struct AlwaysTrue +{ + template <typename T> + constexpr bool operator()(T const&) const noexcept + { + return true; + } +}; + +template <typename PresetType, typename Filter = AlwaysTrue> +void PrintPresetList( + cmCMakePresetsGraph const* const graph, + std::map<std::string, cmCMakePresetsGraph::PresetPair<PresetType>> + cmCMakePresetsGraph::*data, + std::vector<std::string> cmCMakePresetsGraph::*index, Filter filter = {}) +{ + std::vector<cmCMakePresetsGraph::Preset const*> presets; + presets.reserve((graph->*index).size()); + for (auto const& p : graph->*index) { + auto const& preset = (graph->*data).at(p); + if (!preset.Unexpanded.Hidden && preset.Expanded && + preset.Expanded->ConditionResult && filter(preset.Unexpanded)) { + presets.push_back( + static_cast<cmCMakePresetsGraph::Preset const*>(&preset.Unexpanded)); + } + } + + if (!presets.empty()) { + std::cout << (gSkipNewLine ? "" : "\n") << "Available " + << PresetType::kind() << " presets:\n\n"; + gSkipNewLine = false; + PrintPresets(presets); + } +} +} + +template <typename T> +bool cmCMakePresetsGraphInternal::ExpandImmediateMacros(T& preset) +{ + MacroExpanderVector macroExpanders{}; + macroExpanders.push_back(cm::make_unique<ImmediateMacroExpander<T>>(preset)); + cm::optional<T> out = preset; + bool result = CheckExpandMacros(preset, out, macroExpanders); + if (out.has_value()) { + preset = out.value(); + }; + return result; +} + +template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros< + ConfigurePreset>(ConfigurePreset&); +template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros<BuildPreset>( + BuildPreset&); +template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros<TestPreset>( + TestPreset&); +template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros< + PackagePreset>(PackagePreset&); +template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros< + WorkflowPreset>(WorkflowPreset&); + ExpandMacroResult cmCMakePresetsGraphInternal::ExpandMacros( std::string& out, MacroExpanderVector const& macroExpanders, int version) { @@ -781,17 +898,17 @@ InheritString(preset.InstallDir, parent.InstallDir); InheritString(preset.ToolchainFile, parent.ToolchainFile); InheritString(preset.GraphVizFile, parent.GraphVizFile); - InheritOptionalValue(preset.WarnDev, parent.WarnDev); - InheritOptionalValue(preset.ErrorDev, parent.ErrorDev); - InheritOptionalValue(preset.WarnDeprecated, parent.WarnDeprecated); - InheritOptionalValue(preset.ErrorDeprecated, parent.ErrorDeprecated); - InheritOptionalValue(preset.WarnUninitialized, parent.WarnUninitialized); - InheritOptionalValue(preset.WarnUnusedCli, parent.WarnUnusedCli); + InheritMap(preset.Warnings, parent.Warnings); + InheritMap(preset.Errors, parent.Errors); InheritOptionalValue(preset.WarnSystemVars, parent.WarnSystemVars); - - for (auto const& v : parent.CacheVariables) { - preset.CacheVariables.insert(v); - } + InheritMap(preset.CacheVariables, parent.CacheVariables); + InheritOptionalValue(preset.DebugOutput, parent.DebugOutput); + InheritOptionalValue(preset.DebugTryCompile, parent.DebugTryCompile); + InheritOptionalValue(preset.DebugFind, parent.DebugFind); + InheritOptionalValue(preset.TraceMode, parent.TraceMode); + InheritOptionalValue(preset.TraceFormat, parent.TraceFormat); + InheritVector(preset.TraceSource, parent.TraceSource); + InheritString(preset.TraceRedirect, parent.TraceRedirect); return true; } @@ -800,6 +917,8 @@ { auto& preset = *this; if (preset.Environment.count("") != 0) { + this->ErrorDetail = + "Empty environment variable names are not allowed in configure presets"; return false; } @@ -824,13 +943,17 @@ } } - if (preset.WarnDev == false && preset.ErrorDev == true) { - return false; + for (auto const& w : preset.Warnings) { + auto const ei = preset.Errors.find(w.first); + if (ei != preset.Errors.end()) { + if (w.second == false && ei->second == true) { + return false; + } + } } - if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) { - return false; - } + if (preset.CacheVariables.count("") != 0) { + this->ErrorDetail = "Empty cache variable names are not allowed"; return false; } } @@ -863,8 +986,9 @@ bool cmCMakePresetsGraph::BuildPreset::VisitPresetAfterInherit( int /* version */, cmJSONState* /*stat*/) { - auto& preset = *this; - if (!preset.Hidden && preset.ConfigurePreset.empty()) { + if (!this->Hidden && this->ConfigurePreset.empty()) { + this->ErrorDetail = "Build presets must either be hidden or have an " + "associated configure preset"; return false; } return true; @@ -964,6 +1088,8 @@ InheritOptionalValue(execution.Timeout, parentExecution.Timeout); InheritOptionalValue(execution.NoTestsAction, parentExecution.NoTestsAction); + InheritVector(execution.TestPassthroughArguments, + parentExecution.TestPassthroughArguments); } else { preset.Execution = parent.Execution; } @@ -975,8 +1101,9 @@ bool cmCMakePresetsGraph::TestPreset::VisitPresetAfterInherit( int /* version */, cmJSONState* /*state*/) { - auto& preset = *this; - if (!preset.Hidden && preset.ConfigurePreset.empty()) { + if (!this->Hidden && this->ConfigurePreset.empty()) { + this->ErrorDetail = "Test presets must either be hidden or have an " + "associated configure preset"; return false; } return true; @@ -994,11 +1121,7 @@ parent.InheritConfigureEnvironment); InheritVector(preset.Generators, parent.Generators); InheritVector(preset.Configurations, parent.Configurations); - - for (auto const& v : parent.Variables) { - preset.Variables.insert(v); - } - + InheritMap(preset.Variables, parent.Variables); InheritOptionalValue(preset.DebugOutput, parent.DebugOutput); InheritOptionalValue(preset.VerboseOutput, parent.VerboseOutput); InheritString(preset.PackageName, parent.PackageName); @@ -1012,8 +1135,9 @@ bool cmCMakePresetsGraph::PackagePreset::VisitPresetAfterInherit( int /* version */, cmJSONState* /*state*/) { - auto& preset = *this; - if (!preset.Hidden && preset.ConfigurePreset.empty()) { + if (!this->Hidden && this->ConfigurePreset.empty()) { + this->ErrorDetail = "Package presets must either be hidden or have an " + "associated configure preset"; return false; } return true; @@ -1042,12 +1166,13 @@ } bool cmCMakePresetsGraph::ReadProjectPresets(std::string const& sourceDir, - bool allowNoFiles) + std::string const& presetsFile, + ReadOption readFilesOption) { - this->SourceDir = sourceDir; + this->SourceDir = cmSystemTools::CollapseFullPath(sourceDir); this->ClearPresets(); - if (!this->ReadProjectPresetsInternal(allowNoFiles)) { + if (!this->ReadProjectPresetsInternal(presetsFile, readFilesOption)) { this->ClearPresets(); return false; } @@ -1055,33 +1180,74 @@ return true; } -bool cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) +std::string cmCMakePresetsGraph::GetGeneratorForPreset( + std::string const& presetName) const +{ + auto configurePresetName = presetName; + + auto buildPresetIterator = this->BuildPresets.find(presetName); + if (buildPresetIterator != this->BuildPresets.end()) { + configurePresetName = + buildPresetIterator->second.Unexpanded.ConfigurePreset; + } else { + auto testPresetIterator = this->TestPresets.find(presetName); + if (testPresetIterator != this->TestPresets.end()) { + configurePresetName = + testPresetIterator->second.Unexpanded.ConfigurePreset; + } + } + + auto configurePresetIterator = + this->ConfigurePresets.find(configurePresetName); + if (configurePresetIterator != this->ConfigurePresets.end()) { + return configurePresetIterator->second.Unexpanded.Generator; + } + + // This should only happen if the preset is hidden + // or (for build or test presets) if ConfigurePreset is invalid. + return {}; +} + +bool cmCMakePresetsGraph::ReadProjectPresetsInternal( + std::string const& presetsFile, ReadOption readFilesOption) { bool haveOneFile = false; File* file; - std::string filename = GetUserFilename(this->SourceDir); + std::string filename; std::vector<File*> inProgressFiles; - if (cmSystemTools::FileExists(filename)) { - if (!this->ReadJSONFile(filename, RootType::User, ReadReason::Root, - inProgressFiles, file, this->errors)) { - return false; - } - haveOneFile = true; - } else { - filename = GetFilename(this->SourceDir); + if (!presetsFile.empty()) { + filename = presetsFile; if (cmSystemTools::FileExists(filename)) { - if (!this->ReadJSONFile(filename, RootType::Project, ReadReason::Root, + if (!this->ReadJSONFile(filename, RootType::Any, ReadReason::Root, inProgressFiles, file, this->errors)) { return false; } haveOneFile = true; } + } else { + filename = GetUserFilename(this->SourceDir); + if (cmSystemTools::FileExists(filename)) { + if (!this->ReadJSONFile(filename, RootType::User, ReadReason::Root, + inProgressFiles, file, this->errors)) { + return false; + } + haveOneFile = true; + } else { + filename = GetFilename(this->SourceDir); + if (cmSystemTools::FileExists(filename)) { + if (!this->ReadJSONFile(filename, RootType::Project, ReadReason::Root, + inProgressFiles, file, this->errors)) { + return false; + } + haveOneFile = true; + } + } } assert(inProgressFiles.empty()); if (!haveOneFile) { - if (allowNoFiles) { + if (readFilesOption == ReadOption::AllowNoFiles) { return true; } cmCMakePresetsErrors::FILE_NOT_FOUND(filename, &this->parseState); @@ -1098,7 +1264,7 @@ } for (auto& it : this->ConfigurePresets) { - if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + if (!ExpandMacros(this, it.second.Unexpanded, it.second.Expanded)) { cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first, &this->parseState); return false; @@ -1128,7 +1294,7 @@ } } - if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + if (!ExpandMacros(this, it.second.Unexpanded, it.second.Expanded)) { cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first, &this->parseState); return false; @@ -1158,7 +1324,7 @@ } } - if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + if (!ExpandMacros(this, it.second.Unexpanded, it.second.Expanded)) { cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first, &this->parseState); return false; @@ -1188,7 +1354,7 @@ } } - if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + if (!ExpandMacros(this, it.second.Unexpanded, it.second.Expanded)) { cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first, &this->parseState); return false; @@ -1243,7 +1409,7 @@ return false; } - if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + if (!ExpandMacros(this, it.second.Unexpanded, it.second.Expanded)) { cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first, &this->parseState); return false; @@ -1270,166 +1436,58 @@ this->Files.clear(); } -void cmCMakePresetsGraph::printPrecedingNewline(PrintPrecedingNewline* newline) +void cmCMakePresetsGraph::PrintConfigurePresetList() const { - if (newline) { - if (*newline == PrintPrecedingNewline::True) { - std::cout << std::endl; - } - *newline = PrintPrecedingNewline::True; - } -} - -void cmCMakePresetsGraph::PrintPresets( - std::vector<cmCMakePresetsGraph::Preset const*> const& presets) -{ - if (presets.empty()) { - return; - } - - auto longestPresetName = - std::max_element(presets.begin(), presets.end(), - [](cmCMakePresetsGraph::Preset const* a, - cmCMakePresetsGraph::Preset const* b) { - return a->Name.length() < b->Name.length(); - }); - auto longestLength = (*longestPresetName)->Name.length(); - - for (auto const* preset : presets) { - std::cout << " \"" << preset->Name << '"'; - auto const& description = preset->DisplayName; - if (!description.empty()) { - for (std::size_t i = 0; i < longestLength - preset->Name.length(); ++i) { - std::cout << ' '; - } - std::cout << " - " << description; - } - std::cout << '\n'; - } + PrintPresetList<ConfigurePreset>(this, + &cmCMakePresetsGraph::ConfigurePresets, + &cmCMakePresetsGraph::ConfigurePresetOrder); } void cmCMakePresetsGraph::PrintConfigurePresetList( - PrintPrecedingNewline* newline) const + std::function<bool(ConfigurePreset const&)> const& filter) const { - PrintConfigurePresetList([](ConfigurePreset const&) { return true; }, - newline); + PrintPresetList<ConfigurePreset>( + this, &cmCMakePresetsGraph::ConfigurePresets, + &cmCMakePresetsGraph::ConfigurePresetOrder, filter); } -void cmCMakePresetsGraph::PrintConfigurePresetList( - std::function<bool(ConfigurePreset const&)> const& filter, - PrintPrecedingNewline* newline) const +void cmCMakePresetsGraph::PrintBuildPresetList() const { - std::vector<cmCMakePresetsGraph::Preset const*> presets; - for (auto const& p : this->ConfigurePresetOrder) { - auto const& preset = this->ConfigurePresets.at(p); - if (!preset.Unexpanded.Hidden && preset.Expanded && - preset.Expanded->ConditionResult && filter(preset.Unexpanded)) { - presets.push_back( - static_cast<cmCMakePresetsGraph::Preset const*>(&preset.Unexpanded)); - } - } - - if (!presets.empty()) { - printPrecedingNewline(newline); - std::cout << "Available configure presets:\n\n"; - cmCMakePresetsGraph::PrintPresets(presets); - } + PrintPresetList<BuildPreset>(this, &cmCMakePresetsGraph::BuildPresets, + &cmCMakePresetsGraph::BuildPresetOrder); } -void cmCMakePresetsGraph::PrintBuildPresetList( - PrintPrecedingNewline* newline) const +void cmCMakePresetsGraph::PrintTestPresetList() const { - std::vector<cmCMakePresetsGraph::Preset const*> presets; - for (auto const& p : this->BuildPresetOrder) { - auto const& preset = this->BuildPresets.at(p); - if (!preset.Unexpanded.Hidden && preset.Expanded && - preset.Expanded->ConditionResult) { - presets.push_back( - static_cast<cmCMakePresetsGraph::Preset const*>(&preset.Unexpanded)); - } - } - - if (!presets.empty()) { - printPrecedingNewline(newline); - std::cout << "Available build presets:\n\n"; - cmCMakePresetsGraph::PrintPresets(presets); - } + PrintPresetList<TestPreset>(this, &cmCMakePresetsGraph::TestPresets, + &cmCMakePresetsGraph::TestPresetOrder); } -void cmCMakePresetsGraph::PrintTestPresetList( - PrintPrecedingNewline* newline) const +void cmCMakePresetsGraph::PrintPackagePresetList() const { - std::vector<cmCMakePresetsGraph::Preset const*> presets; - for (auto const& p : this->TestPresetOrder) { - auto const& preset = this->TestPresets.at(p); - if (!preset.Unexpanded.Hidden && preset.Expanded && - preset.Expanded->ConditionResult) { - presets.push_back( - static_cast<cmCMakePresetsGraph::Preset const*>(&preset.Unexpanded)); - } - } - - if (!presets.empty()) { - printPrecedingNewline(newline); - std::cout << "Available test presets:\n\n"; - cmCMakePresetsGraph::PrintPresets(presets); - } + PrintPresetList<PackagePreset>(this, &cmCMakePresetsGraph::PackagePresets, + &cmCMakePresetsGraph::PackagePresetOrder); } void cmCMakePresetsGraph::PrintPackagePresetList( - PrintPrecedingNewline* newline) const + std::function<bool(PackagePreset const&)> const& filter) const { - this->PrintPackagePresetList([](PackagePreset const&) { return true; }, - newline); + PrintPresetList<PackagePreset>(this, &cmCMakePresetsGraph::PackagePresets, + &cmCMakePresetsGraph::PackagePresetOrder, + filter); } -void cmCMakePresetsGraph::PrintPackagePresetList( - std::function<bool(PackagePreset const&)> const& filter, - PrintPrecedingNewline* newline) const +void cmCMakePresetsGraph::PrintWorkflowPresetList() const { - std::vector<cmCMakePresetsGraph::Preset const*> presets; - for (auto const& p : this->PackagePresetOrder) { - auto const& preset = this->PackagePresets.at(p); - if (!preset.Unexpanded.Hidden && preset.Expanded && - preset.Expanded->ConditionResult && filter(preset.Unexpanded)) { - presets.push_back( - static_cast<cmCMakePresetsGraph::Preset const*>(&preset.Unexpanded)); - } - } - - if (!presets.empty()) { - printPrecedingNewline(newline); - std::cout << "Available package presets:\n\n"; - cmCMakePresetsGraph::PrintPresets(presets); - } -} - -void cmCMakePresetsGraph::PrintWorkflowPresetList( - PrintPrecedingNewline* newline) const -{ - std::vector<cmCMakePresetsGraph::Preset const*> presets; - for (auto const& p : this->WorkflowPresetOrder) { - auto const& preset = this->WorkflowPresets.at(p); - if (!preset.Unexpanded.Hidden && preset.Expanded && - preset.Expanded->ConditionResult) { - presets.push_back( - static_cast<cmCMakePresetsGraph::Preset const*>(&preset.Unexpanded)); - } - } - - if (!presets.empty()) { - printPrecedingNewline(newline); - std::cout << "Available workflow presets:\n\n"; - cmCMakePresetsGraph::PrintPresets(presets); - } + PrintPresetList<WorkflowPreset>(this, &cmCMakePresetsGraph::WorkflowPresets, + &cmCMakePresetsGraph::WorkflowPresetOrder); } void cmCMakePresetsGraph::PrintAllPresets() const { - PrintPrecedingNewline newline = PrintPrecedingNewline::False; - this->PrintConfigurePresetList(&newline); - this->PrintBuildPresetList(&newline); - this->PrintTestPresetList(&newline); - this->PrintPackagePresetList(&newline); - this->PrintWorkflowPresetList(&newline); + this->PrintConfigurePresetList(); + this->PrintBuildPresetList(); + this->PrintTestPresetList(); + this->PrintPackagePresetList(); + this->PrintWorkflowPresetList(); }
diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h index 4ce0ace..f04dfdd 100644 --- a/Source/cmCMakePresetsGraph.h +++ b/Source/cmCMakePresetsGraph.h
@@ -9,11 +9,11 @@ #include <memory> #include <string> #include <unordered_set> -#include <utility> #include <vector> #include <cm/optional> +#include "cmDiagnostics.h" #include "cmJSONState.h" #include "cmStateTypes.h" // IWYU pragma: keep @@ -40,22 +40,19 @@ Expand, }; - class CacheVariable + struct CacheVariable { - public: std::string Type; std::string Value; }; class Condition; - class File + struct File { - public: + std::unordered_set<File*> ReachableFiles; std::string Filename; int Version; - - std::unordered_set<File*> ReachableFiles; }; class Preset @@ -87,6 +84,8 @@ std::map<std::string, cm::optional<std::string>> Environment; + std::string ErrorDetail; + virtual bool VisitPresetInherit(Preset const& parent) = 0; virtual bool VisitPresetBeforeInherit() { return true; } @@ -126,13 +125,11 @@ std::map<std::string, cm::optional<CacheVariable>> CacheVariables; - cm::optional<bool> WarnDev; - cm::optional<bool> ErrorDev; - cm::optional<bool> WarnDeprecated; - cm::optional<bool> ErrorDeprecated; - cm::optional<bool> WarnUninitialized; - cm::optional<bool> WarnUnusedCli; + std::map<cmDiagnosticCategory, bool> Warnings; + std::map<cmDiagnosticCategory, bool> Errors; cm::optional<bool> WarnSystemVars; + cm::optional<bool> WarnDev; // Deprecated synonym for Warnings.CMD_AUTHOR + cm::optional<bool> ErrorDev; // Deprecated synonym for Errors.CMD_AUTHOR cm::optional<bool> DebugOutput; cm::optional<bool> DebugTryCompile; @@ -146,6 +143,8 @@ bool VisitPresetInherit(Preset const& parent) override; bool VisitPresetBeforeInherit() override; bool VisitPresetAfterInherit(int version, cmJSONState* state) override; + + static char const* kind() { return "configure"; } }; class BuildPreset : public Preset @@ -178,6 +177,8 @@ bool VisitPresetInherit(Preset const& parent) override; bool VisitPresetAfterInherit(int /* version */, cmJSONState* /*state*/) override; + + static char const* kind() { return "build"; } }; class TestPreset : public Preset @@ -299,6 +300,7 @@ cm::optional<bool> ScheduleRandom; cm::optional<int> Timeout; cm::optional<NoTestsActionEnum> NoTestsAction; + std::vector<std::string> TestPassthroughArguments; }; std::string ConfigurePreset; @@ -312,6 +314,8 @@ bool VisitPresetInherit(Preset const& parent) override; bool VisitPresetAfterInherit(int /* version */, cmJSONState* /*state*/) override; + + static char const* kind() { return "test"; } }; class PackagePreset : public Preset @@ -349,6 +353,8 @@ bool VisitPresetInherit(Preset const& parent) override; bool VisitPresetAfterInherit(int /* version */, cmJSONState* /*state*/) override; + + static char const* kind() { return "package"; } }; class WorkflowPreset : public Preset @@ -368,9 +374,8 @@ WorkflowPreset& operator=(WorkflowPreset&& /*other*/) = delete; #endif - class WorkflowStep + struct WorkflowStep { - public: enum class Type { Configure, @@ -387,16 +392,49 @@ bool VisitPresetInherit(Preset const& parent) override; bool VisitPresetAfterInherit(int /* version */, cmJSONState* /* state */) override; + + static char const* kind() { return "workflow"; } }; - template <class T> - class PresetPair + template <typename T> + struct PresetPair { - public: T Unexpanded; cm::optional<T> Expanded; }; + enum class PresetResolveStatus + { + Success, + NotFound, + Hidden, + InvalidMacroExpansion, + Disabled, + }; + + // Result type for preset resolution + template <class T> + struct PresetResolveResult + { + using Status = PresetResolveStatus; + + Status StatusCode = Status::Success; + std::string ErrorPresetName; + T const* Preset = nullptr; + }; + + template <class T> + PresetResolveResult<T> ResolvePreset( + std::string const& presetName, + std::map<std::string, PresetPair<T>> const& presets) const; + + // Returns an error message for a preset resolve status, + // or cm::nullopt on Success. + template <class T> + static cm::optional<std::string> FormatPresetError( + PresetResolveStatus status, std::string const& errorPresetName, + std::string const& directory); + std::map<std::string, PresetPair<ConfigurePreset>> ConfigurePresets; std::map<std::string, PresetPair<BuildPreset>> BuildPresets; std::map<std::string, PresetPair<TestPreset>> TestPresets; @@ -417,64 +455,33 @@ return preset.OriginFile->Version; } - static std::string GetFilename(std::string const& sourceDir); - static std::string GetUserFilename(std::string const& sourceDir); - bool ReadProjectPresets(std::string const& sourceDir, - bool allowNoFiles = false); - - std::string GetGeneratorForPreset(std::string const& presetName) const + enum class ReadOption { - auto configurePresetName = presetName; - - auto buildPresetIterator = this->BuildPresets.find(presetName); - if (buildPresetIterator != this->BuildPresets.end()) { - configurePresetName = - buildPresetIterator->second.Unexpanded.ConfigurePreset; - } else { - auto testPresetIterator = this->TestPresets.find(presetName); - if (testPresetIterator != this->TestPresets.end()) { - configurePresetName = - testPresetIterator->second.Unexpanded.ConfigurePreset; - } - } - - auto configurePresetIterator = - this->ConfigurePresets.find(configurePresetName); - if (configurePresetIterator != this->ConfigurePresets.end()) { - return configurePresetIterator->second.Unexpanded.Generator; - } - - // This should only happen if the preset is hidden - // or (for build or test presets) if ConfigurePreset is invalid. - return ""; - } - - enum class PrintPrecedingNewline - { - False, - True, + RequireFiles, + AllowNoFiles, }; - static void printPrecedingNewline(PrintPrecedingNewline* p); - static void PrintPresets( - std::vector<cmCMakePresetsGraph::Preset const*> const& presets); + bool ReadProjectPresets( + std::string const& sourceDir, std::string const& presetsFile, + ReadOption readFilesOption = ReadOption::RequireFiles); + + std::string GetGeneratorForPreset(std::string const& presetName) const; + + void PrintConfigurePresetList() const; void PrintConfigurePresetList( - PrintPrecedingNewline* newline = nullptr) const; - void PrintConfigurePresetList( - std::function<bool(ConfigurePreset const&)> const& filter, - PrintPrecedingNewline* newline = nullptr) const; - void PrintBuildPresetList(PrintPrecedingNewline* newline = nullptr) const; - void PrintTestPresetList(PrintPrecedingNewline* newline = nullptr) const; - void PrintPackagePresetList(PrintPrecedingNewline* newline = nullptr) const; + std::function<bool(ConfigurePreset const&)> const& filter) const; + void PrintBuildPresetList() const; + void PrintTestPresetList() const; + void PrintPackagePresetList() const; void PrintPackagePresetList( - std::function<bool(PackagePreset const&)> const& filter, - PrintPrecedingNewline* newline = nullptr) const; - void PrintWorkflowPresetList(PrintPrecedingNewline* newline = nullptr) const; + std::function<bool(PackagePreset const&)> const& filter) const; + void PrintWorkflowPresetList() const; void PrintAllPresets() const; private: enum class RootType { + Any, Project, User, }; @@ -485,9 +492,67 @@ Included, }; - bool ReadProjectPresetsInternal(bool allowNoFiles); + bool ReadProjectPresetsInternal(std::string const& presetsFile, + ReadOption readFilesOption); bool ReadJSONFile(std::string const& filename, RootType rootType, ReadReason readReason, std::vector<File*>& inProgressFiles, File*& file, std::string& errMsg); void ClearPresets(); + + static std::string GetFilename(std::string const& sourceDir); + static std::string GetUserFilename(std::string const& sourceDir); }; + +extern template cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::ConfigurePreset> +cmCMakePresetsGraph::ResolvePreset( + std::string const&, + std::map<std::string, + cmCMakePresetsGraph::PresetPair< + cmCMakePresetsGraph::ConfigurePreset>> const&) const; + +extern template cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::BuildPreset> +cmCMakePresetsGraph::ResolvePreset( + std::string const&, + std::map< + std::string, + cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::BuildPreset>> const&) + const; + +extern template cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::TestPreset> +cmCMakePresetsGraph::ResolvePreset( + std::string const&, + std::map< + std::string, + cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::TestPreset>> const&) + const; + +extern template cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::PackagePreset> +cmCMakePresetsGraph::ResolvePreset( + std::string const&, + std::map<std::string, + cmCMakePresetsGraph::PresetPair< + cmCMakePresetsGraph::PackagePreset>> const&) const; + +extern template cm::optional<std::string> +cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::ConfigurePreset>( + cmCMakePresetsGraph::PresetResolveStatus, std::string const&, + std::string const&); + +extern template cm::optional<std::string> +cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::BuildPreset>( + cmCMakePresetsGraph::PresetResolveStatus, std::string const&, + std::string const&); + +extern template cm::optional<std::string> +cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::TestPreset>( + cmCMakePresetsGraph::PresetResolveStatus, std::string const&, + std::string const&); + +extern template cm::optional<std::string> +cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::PackagePreset>( + cmCMakePresetsGraph::PresetResolveStatus, std::string const&, + std::string const&);
diff --git a/Source/cmCMakePresetsGraphInternal.h b/Source/cmCMakePresetsGraphInternal.h index 02c99ee..772a3d4 100644 --- a/Source/cmCMakePresetsGraphInternal.h +++ b/Source/cmCMakePresetsGraphInternal.h
@@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <cm/optional> + #include <cm3p/json/value.h> #include "cmCMakePresetsGraph.h" @@ -25,6 +27,7 @@ { Ok, Ignore, + Defer, Error, }; @@ -83,7 +86,7 @@ int version) const override; }; -template <class T> +template <typename T> class PresetMacroExpander : public MacroExpander { cmCMakePresetsGraph const& Graph; @@ -125,6 +128,49 @@ } }; +template <class T> +class ImmediateMacroExpander : public MacroExpander +{ + T const& Preset; + +public: + ImmediateMacroExpander(T const& preset) + : Preset(preset) + { + } + ExpandMacroResult operator()(std::string const& macroNamespace, + std::string const& macroName, + std::string& macroOut, + int version) const override + { + if (macroNamespace.empty()) { + if (macroName == "fileDir") { + if (version < 12) { + return ExpandMacroResult::Defer; + } + macroOut += + cmSystemTools::GetParentDirectory(Preset.OriginFile->Filename); + return ExpandMacroResult::Ok; + } + } + return ExpandMacroResult::Defer; + } +}; + +template <typename T> +bool ExpandImmediateMacros(T& preset); + +extern template bool ExpandImmediateMacros< + cmCMakePresetsGraph::ConfigurePreset>(cmCMakePresetsGraph::ConfigurePreset&); +extern template bool ExpandImmediateMacros<cmCMakePresetsGraph::BuildPreset>( + cmCMakePresetsGraph::BuildPreset&); +extern template bool ExpandImmediateMacros<cmCMakePresetsGraph::TestPreset>( + cmCMakePresetsGraph::TestPreset&); +extern template bool ExpandImmediateMacros<cmCMakePresetsGraph::PackagePreset>( + cmCMakePresetsGraph::PackagePreset&); +extern template bool ExpandImmediateMacros< + cmCMakePresetsGraph::WorkflowPreset>(cmCMakePresetsGraph::WorkflowPreset&); + class NullCondition : public cmCMakePresetsGraph::Condition { bool Evaluate(MacroExpanderVector const& /*expanders*/, int /*version*/, @@ -213,6 +259,22 @@ bool PresetOptionalBoolHelper(cm::optional<bool>& out, Json::Value const* value, cmJSONState* state); +template <typename K> +bool PresetMapToBoolHelper(std::map<K, bool>& out, Json::Value const* value, + K key, cmJSONState* state) +{ + cm::optional<bool> temp; + if (!PresetOptionalBoolHelper(temp, value, state)) { + return false; + } + + if (temp) { + out[key] = *temp; + } + + return true; +} + bool PresetIntHelper(int& out, Json::Value const* value, cmJSONState* state); bool PresetOptionalIntHelper(cm::optional<int>& out, Json::Value const* value, @@ -259,4 +321,7 @@ Json::Value const* value, cmJSONState* state); cmJSONHelper<std::nullptr_t> SchemaHelper(); + +bool CheckDiagnostics(cmJSONState* state, int version, + cmCMakePresetsGraph::ConfigurePreset& preset); }
diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx index d141b10..27c3b99 100644 --- a/Source/cmCMakePresetsGraphReadJSON.cxx +++ b/Source/cmCMakePresetsGraphReadJSON.cxx
@@ -38,9 +38,10 @@ using MacroExpanderVector = cmCMakePresetsGraphInternal::MacroExpanderVector; using cmCMakePresetsGraphInternal::BaseMacroExpander; using cmCMakePresetsGraphInternal::ExpandMacros; +using cmCMakePresetsGraphInternal::ExpandImmediateMacros; constexpr int MIN_VERSION = 1; -constexpr int MAX_VERSION = 11; +constexpr int MAX_VERSION = 12; struct CMakeVersion { @@ -591,11 +592,9 @@ return false; } - PresetPair<ConfigurePreset> presetPair; - presetPair.Unexpanded = preset; - presetPair.Expanded = cm::nullopt; - if (!this->ConfigurePresets.emplace(preset.Name, presetPair).second) { - cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState); + if (!ExpandImmediateMacros<ConfigurePreset>(preset)) { + cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name, + &this->parseState); return false; } @@ -632,6 +631,20 @@ return false; } + // Support for diagnostics. + if (!cmCMakePresetsGraphInternal::CheckDiagnostics(&this->parseState, v, + preset)) { + return false; + } + + PresetPair<ConfigurePreset> presetPair; + presetPair.Unexpanded = preset; + presetPair.Expanded = cm::nullopt; + if (!this->ConfigurePresets.emplace(preset.Name, presetPair).second) { + cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState); + return false; + } + this->ConfigurePresetOrder.push_back(preset.Name); } @@ -642,6 +655,12 @@ return false; } + if (!ExpandImmediateMacros<BuildPreset>(preset)) { + cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name, + &this->parseState); + return false; + } + PresetPair<BuildPreset> presetPair; presetPair.Unexpanded = preset; presetPair.Expanded = cm::nullopt; @@ -666,6 +685,12 @@ return false; } + if (!ExpandImmediateMacros<TestPreset>(preset)) { + cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name, + &this->parseState); + return false; + } + PresetPair<TestPreset> presetPair; presetPair.Unexpanded = preset; presetPair.Expanded = cm::nullopt; @@ -700,6 +725,13 @@ return false; } + // Support for testPassthroughArguments added in version 12. + if (v < 12 && preset.Execution && + !preset.Execution->TestPassthroughArguments.empty()) { + cmCMakePresetsErrors::PASSTHROUGH_ARGS_UNSUPPORTED(&this->parseState); + return false; + } + this->TestPresetOrder.push_back(preset.Name); } @@ -710,6 +742,12 @@ return false; } + if (!ExpandImmediateMacros<PackagePreset>(preset)) { + cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name, + &this->parseState); + return false; + } + PresetPair<PackagePreset> presetPair; presetPair.Unexpanded = preset; presetPair.Expanded = cm::nullopt; @@ -731,6 +769,12 @@ return false; } + if (!ExpandImmediateMacros<WorkflowPreset>(preset)) { + cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name, + &this->parseState); + return false; + } + PresetPair<WorkflowPreset> presetPair; presetPair.Unexpanded = preset; presetPair.Expanded = cm::nullopt;
diff --git a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx index 5b62d09..9298fda 100644 --- a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx +++ b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
@@ -1,20 +1,27 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file LICENSE.rst or https://cmake.org/licensing for details. */ +#include <cassert> #include <cstddef> #include <functional> #include <map> #include <memory> #include <string> +#include <utility> #include <vector> #include <cm/optional> +#include <cm/string_view> +#include <cmext/algorithm> #include <cmext/string_view> #include <cm3p/json/value.h> +#include "cmsys/String.h" + #include "cmCMakePresetsErrors.h" #include "cmCMakePresetsGraph.h" #include "cmCMakePresetsGraphInternal.h" +#include "cmDiagnostics.h" #include "cmJSONHelpers.h" #include "cmStateTypes.h" @@ -204,27 +211,68 @@ JSONHelperBuilder::Map<cm::optional<CacheVariable>>( cmCMakePresetsErrors::INVALID_PRESET, VariableHelper); +template <cmDiagnosticCategory C> +cm::string_view GetJSONName() +{ + static std::string storage = [] { + cm::string_view const in = cmDiagnostics::GetCategoryString(C).substr(4); + std::string out; + bool sep = false; + for (char const c : in) { + if (sep) { + out += c; + sep = false; + } else if (c == '_') { + sep = true; + } else { + out += static_cast<char>(cmsysString_tolower(c)); + } + } + return out; + }(); + return storage; +} + +cm::string_view GetJSONName(cmDiagnosticCategory category) +{ + static cm::string_view const names[] = { + "none"_s, // CMD_NONE +#define DIAGNOSTIC_JSON_NAME(C) GetJSONName<cmDiagnostics::C>(), + CM_FOR_EACH_DIAGNOSTIC_CATEGORY(DIAGNOSTIC_JSON_NAME) +#undef DIAGNOSTIC_JSON_NAME + }; + assert(category > 0 && category < cmDiagnostics::CategoryCount); + return names[category]; +} + +auto const PresetDiagnosticMapHelper = + cmCMakePresetsGraphInternal::PresetMapToBoolHelper<cmDiagnosticCategory>; + +#define BIND_DIAGNOSTIC(C) \ + .Bind(GetJSONName<cmDiagnostics::C>(), &DIAGNOSTIC_MEMBER, \ + PresetDiagnosticMapHelper, cmDiagnostics::C, false) + +#define DIAGNOSTIC_MEMBER ConfigurePreset::Warnings auto const PresetWarningsHelper = JSONHelperBuilder::Object<ConfigurePreset>( JsonErrors::INVALID_NAMED_OBJECT_KEY, false) .Bind("dev"_s, &ConfigurePreset::WarnDev, cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) - .Bind("deprecated"_s, &ConfigurePreset::WarnDeprecated, - cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) - .Bind("uninitialized"_s, &ConfigurePreset::WarnUninitialized, - cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) - .Bind("unusedCli"_s, &ConfigurePreset::WarnUnusedCli, - cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) + CM_FOR_EACH_DIAGNOSTIC_CATEGORY(BIND_DIAGNOSTIC) .Bind("systemVars"_s, &ConfigurePreset::WarnSystemVars, cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false); +#undef DIAGNOSTIC_MEMBER +#define DIAGNOSTIC_MEMBER ConfigurePreset::Errors auto const PresetErrorsHelper = JSONHelperBuilder::Object<ConfigurePreset>( JsonErrors::INVALID_NAMED_OBJECT_KEY, false) .Bind("dev"_s, &ConfigurePreset::ErrorDev, cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) - .Bind("deprecated"_s, &ConfigurePreset::ErrorDeprecated, - cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false); + CM_FOR_EACH_DIAGNOSTIC_CATEGORY(BIND_DIAGNOSTIC); +#undef DIAGNOSTIC_MEMBER + +#undef BIND_DIAGNOSTIC auto const PresetDebugHelper = JSONHelperBuilder::Object<ConfigurePreset>( @@ -301,4 +349,71 @@ return helper(out, value, state); } + +bool CheckDiagnostics(cmJSONState* state, int version, + std::map<cmDiagnosticCategory, bool> values, + cm::string_view group) +{ + // NOLINTNEXTLINE(readability-use-anyofallof) + for (auto const& i : values) { + assert(i.first > 0 && i.first < cmDiagnostics::CategoryCount); + int const minVersion = cmDiagnostics::CategoryInfo[i.first].PresetVersion; + if (version < minVersion) { + cm::string_view dn = GetJSONName(i.first); + cmCMakePresetsErrors::DIAGNOSTIC_UNSUPPORTED(dn, group, minVersion, + state); + return false; + } + } + + return true; +} + +bool CheckDiagnostics(cmJSONState* state, int version, + cmCMakePresetsGraph::ConfigurePreset& preset) +{ + // Check for diagnostics added in later schemes. + if (!CheckDiagnostics(state, version, preset.Warnings, "warnings"_s) || + !CheckDiagnostics(state, version, preset.Errors, "errors"_s)) { + return false; + } + + if (version < 12) { + // Handle 'dev'. + if (preset.WarnDev) { + preset.Warnings.emplace(cmDiagnostics::CMD_AUTHOR, *preset.WarnDev); + } + if (preset.ErrorDev) { + preset.Errors.emplace(cmDiagnostics::CMD_AUTHOR, *preset.ErrorDev); + } + + // Check for diagnostics only present as warnings before v12. + constexpr cmDiagnosticCategory unsupportedErrors[] = { + cmDiagnostics::CMD_UNINITIALIZED, + cmDiagnostics::CMD_UNUSED_CLI, + }; + + for (cmDiagnosticCategory c : unsupportedErrors) { + if (cm::contains(preset.Errors, c)) { + cm::string_view dn = GetJSONName(c); + cmCMakePresetsErrors::DIAGNOSTIC_UNSUPPORTED(dn, "errors"_s, 12, + state); + return false; + } + } + } else { + // Check for diagnostics removed in v12. + if (preset.WarnDev) { + cmCMakePresetsErrors::DIAGNOSTIC_REMOVED("dev"_s, "warnings"_s, 11, + state); + return false; + } + if (preset.ErrorDev) { + cmCMakePresetsErrors::DIAGNOSTIC_REMOVED("dev"_s, "errors"_s, 11, state); + return false; + } + } + + return true; +} }
diff --git a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx index d716445..050c701 100644 --- a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx +++ b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
@@ -353,7 +353,10 @@ .Bind("timeout"_s, &TestPreset::ExecutionOptions::Timeout, cmCMakePresetsGraphInternal::PresetOptionalIntHelper, false) .Bind("noTestsAction"_s, &TestPreset::ExecutionOptions::NoTestsAction, - TestPresetOptionalExecutionNoTestsActionHelper, false)); + TestPresetOptionalExecutionNoTestsActionHelper, false) + .Bind("testPassthroughArguments"_s, + &TestPreset::ExecutionOptions::TestPassthroughArguments, + cmCMakePresetsGraphInternal::PresetVectorStringHelper, false)); auto const TestPresetFilterHelper = JSONHelperBuilder::Optional<TestPreset::FilterOptions>(
diff --git a/Source/cmCMakePresetsGraphResolve.cxx b/Source/cmCMakePresetsGraphResolve.cxx new file mode 100644 index 0000000..c024a25 --- /dev/null +++ b/Source/cmCMakePresetsGraphResolve.cxx
@@ -0,0 +1,116 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include <cassert> +#include <map> +#include <string> + +#include <cm/optional> + +#include "cmCMakePresetsGraph.h" +#include "cmStringAlgorithms.h" + +template <class T> +cmCMakePresetsGraph::PresetResolveResult<T> cmCMakePresetsGraph::ResolvePreset( + std::string const& presetName, + std::map<std::string, PresetPair<T>> const& presets) const +{ + using Result = PresetResolveResult<T>; + Result result; + + auto presetPair = presets.find(presetName); + if (presetPair == presets.end()) { + result.StatusCode = Result::Status::NotFound; + result.ErrorPresetName = presetName; + return result; + } + if (presetPair->second.Unexpanded.Hidden) { + result.StatusCode = Result::Status::Hidden; + result.ErrorPresetName = presetName; + return result; + } + if (!presetPair->second.Expanded) { + result.StatusCode = Result::Status::InvalidMacroExpansion; + result.ErrorPresetName = presetName; + return result; + } + if (!presetPair->second.Expanded->ConditionResult) { + result.StatusCode = Result::Status::Disabled; + result.ErrorPresetName = presetName; + return result; + } + + result.Preset = &*presetPair->second.Expanded; + return result; +} + +// Explicit template instantiations +template cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::ConfigurePreset> +cmCMakePresetsGraph::ResolvePreset( + std::string const&, + std::map<std::string, + PresetPair<cmCMakePresetsGraph::ConfigurePreset>> const&) const; + +template cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::BuildPreset> +cmCMakePresetsGraph::ResolvePreset( + std::string const&, + std::map<std::string, PresetPair<cmCMakePresetsGraph::BuildPreset>> const&) + const; + +template cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::TestPreset> +cmCMakePresetsGraph::ResolvePreset( + std::string const&, + std::map<std::string, PresetPair<cmCMakePresetsGraph::TestPreset>> const&) + const; + +template cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::PackagePreset> +cmCMakePresetsGraph::ResolvePreset( + std::string const&, + std::map<std::string, PresetPair<cmCMakePresetsGraph::PackagePreset>> const&) + const; + +template <class T> +cm::optional<std::string> cmCMakePresetsGraph::FormatPresetError( + PresetResolveStatus status, std::string const& errorPresetName, + std::string const& directory) +{ + using Status = PresetResolveStatus; + switch (status) { + case Status::NotFound: + return cmStrCat("No such ", T::kind(), " preset in ", directory, ": \"", + errorPresetName, '"'); + case Status::Hidden: + return cmStrCat("Cannot use hidden ", T::kind(), " preset in ", + directory, ": \"", errorPresetName, '"'); + case Status::InvalidMacroExpansion: + return cmStrCat("Could not evaluate ", T::kind(), " preset \"", + errorPresetName, "\": Invalid macro expansion"); + case Status::Disabled: + return cmStrCat("Cannot use disabled ", T::kind(), " preset in ", + directory, ": \"", errorPresetName, '"'); + case Status::Success: + return cm::nullopt; + } + assert(false && "Unreachable."); + return cm::nullopt; +} + +// Explicit template instantiations for FormatPresetError +template cm::optional<std::string> +cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::ConfigurePreset>( + PresetResolveStatus, std::string const&, std::string const&); + +template cm::optional<std::string> +cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::BuildPreset>( + PresetResolveStatus, std::string const&, std::string const&); + +template cm::optional<std::string> +cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::TestPreset>( + PresetResolveStatus, std::string const&, std::string const&); + +template cm::optional<std::string> +cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::PackagePreset>( + PresetResolveStatus, std::string const&, std::string const&);
diff --git a/Source/cmCPackPropertiesGenerator.cxx b/Source/cmCPackPropertiesGenerator.cxx index db33bba..6193ede 100644 --- a/Source/cmCPackPropertiesGenerator.cxx +++ b/Source/cmCPackPropertiesGenerator.cxx
@@ -1,11 +1,11 @@ #include "cmCPackPropertiesGenerator.h" #include <map> +#include <memory> #include <ostream> #include "cmGeneratorExpression.h" #include "cmInstalledFile.h" -#include "cmOutputConverter.h" cmCPackPropertiesGenerator::cmCPackPropertiesGenerator( cmLocalGenerator* lg, cmInstalledFile const& installedFile, @@ -31,13 +31,11 @@ cmInstalledFile::Property const& property = i.second; os << indent << "set_property(INSTALL " - << cmOutputConverter::EscapeForCMake(expandedFileName) << " PROPERTY " - << cmOutputConverter::EscapeForCMake(name); + << cmScriptGenerator::Quote(expandedFileName) << " PROPERTY " + << cmScriptGenerator::Quote(name); - for (cmInstalledFile::ExpressionVectorType::value_type const& j : - property.ValueExpressions) { - std::string value = j->Evaluate(this->LG, config); - os << " " << cmOutputConverter::EscapeForCMake(value); + for (auto const& j : property.ValueExpressions) { + os << ' ' << cmScriptGenerator::Quote(j->Evaluate(this->LG, config)); } os << ")\n";
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index ed1516f..bd063f5 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx
@@ -12,6 +12,7 @@ #include <functional> #include <initializer_list> #include <iostream> +#include <iterator> #include <map> #include <ratio> #include <sstream> @@ -41,6 +42,7 @@ #include "cm_parse_date.h" +#include "cmCMakePresetsArgs.h" #include "cmCMakePresetsGraph.h" #include "cmCTestBuildAndTest.h" #include "cmCTestScriptHandler.h" @@ -77,6 +79,10 @@ # include <be/kernel/OS.h> /* disable_debugger() API. */ #endif +namespace { +cm::string_view const kVT100_EraseLine = "\x1B[K"_s; +} + struct tm; struct cmCTest::Private @@ -119,7 +125,7 @@ bool Failover = false; cmJSONState parseState; - bool FlushTestProgressLine = false; + bool TestProgressNewlinePending = false; // these are helper classes cmCTestBuildAndTest BuildAndTest; @@ -161,9 +167,13 @@ int CompatibilityMode; - // information for the --build-and-test options + // Build and source directories used by various operating modes. + // BinaryDir is the active build tree. + // TestDir and SourceDir hold the raw values from --test-dir/--build-dir and + // --source-dir respectively, before path normalization is applied. std::string BinaryDir; std::string TestDir; + std::string SourceDir; std::string NotesFiles; @@ -533,6 +543,7 @@ // No need to exit if we are not producing XML if (this->Impl->ProduceXML) { cmCTestLog(this, WARNING, "Cannot find file: " << fileName << std::endl); + this->ApplyDefinitionsToCTestConfig(); return false; } } else { @@ -596,6 +607,8 @@ this->Impl->CompressXMLFiles = cmIsOn(this->GetCTestConfiguration("CompressSubmission")); } + // Command-line definitions override values loaded from the config file. + this->ApplyDefinitionsToCTestConfig(); return true; } @@ -725,8 +738,75 @@ script.CreateCMake(); cmMakefile& mf = *script.GetMakefile(); this->ReadCustomConfigurationFileTree(this->Impl->BinaryDir, &mf); - this->SetTimeLimit(mf.GetDefinition("CTEST_TIME_LIMIT")); this->SetCMakeVariables(mf); + + // Inject variables passed via -D so that ctest_* commands can read them + // using mf.GetDefinition(). This matches what cmCTestScriptHandler does + // before running a -S script. SetCMakeVariables() (above) only defines + // variables that correspond to a CTest Configuration key. Other variables + // such as CTEST_PRESET are read directly from the makefile and would + // otherwise be invisible here. + for (auto const& def : this->GetDefinitions()) { + mf.AddDefinition(def.first, def.second); + } + + if (!this->Impl->SourceDir.empty() && this->Impl->TestDir.empty() && + this->Impl->CTestConfigurationOverwrites.find("BuildDirectory") == + this->Impl->CTestConfigurationOverwrites.end()) { + std::string const configurePresetName = + cmNonempty(mf.GetDefinition("CTEST_CONFIGURE_PRESET")) + ? *mf.GetDefinition("CTEST_CONFIGURE_PRESET") + : mf.GetSafeDefinition("CTEST_PRESET"); + if (!configurePresetName.empty()) { + // Check if we should use the binary directory from the specified + // configure preset. + std::string const sourceDir = + this->GetCTestConfiguration("SourceDirectory"); + std::string const rawPresetsFile = + mf.GetSafeDefinition("CTEST_PRESETS_FILE"); + std::string const presetsFile = rawPresetsFile.empty() + ? std::string{} + : cmSystemTools::CollapseFullPath(rawPresetsFile, sourceDir); + cmCMakePresetsGraph presetsGraph; + if (!sourceDir.empty()) { + if (!presetsGraph.ReadProjectPresets(sourceDir, presetsFile)) { + cmCTestLog(this, ERROR_MESSAGE, + "Could not read presets from \"" + << sourceDir << "\":\n " + << presetsGraph.parseState.GetErrorMessage() + << std::endl); + return 12; + } + cmCMakePresetsGraph::PresetResolveResult< + cmCMakePresetsGraph::ConfigurePreset> + resolveResult = presetsGraph.ResolvePreset( + configurePresetName, presetsGraph.ConfigurePresets); + cm::optional<std::string> resolveError = + cmCMakePresetsGraph::FormatPresetError< + cmCMakePresetsGraph::ConfigurePreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + sourceDir); + if (resolveError) { + cmCTestLog(this, ERROR_MESSAGE, *resolveError << std::endl); + return 12; + } + if (resolveResult.Preset && !resolveResult.Preset->BinaryDir.empty()) { + std::string const binaryDir = resolveResult.Preset->BinaryDir; + this->SetCTestConfiguration("BuildDirectory", binaryDir); + this->Impl->BinaryDir = binaryDir; + cmSystemTools::SetLogicalWorkingDirectory(binaryDir); + mf.AddDefinition("CTEST_BINARY_DIRECTORY", binaryDir); + // Re-parse DartConfiguration.tcl since we changed BinaryDir. + this->UpdateCTestConfiguration(); + this->SetCMakeVariables(mf); + } + } + } + } + + // CTEST_TIME_LIMIT may come from CTestCustom.cmake (already in the makefile) + // or from the config map (just populated by SetCMakeVariables above). + this->SetTimeLimit(mf.GetDefinition("CTEST_TIME_LIMIT")); std::vector<cmListFileArgument> args{ cmListFileArgument("RETURN_VALUE"_s, cmListFileArgument::Unquoted, 0), cmListFileArgument("return_value"_s, cmListFileArgument::Unquoted, 0), @@ -821,7 +901,7 @@ d.Load(notes_dir); unsigned long kk; for (kk = 0; kk < d.GetNumberOfFiles(); kk++) { - char const* file = d.GetFile(kk); + std::string const& file = d.GetFileName(kk); std::string fullname = cmStrCat(notes_dir, '/', file); if (cmSystemTools::FileExists(fullname, true)) { if (!this->Impl->NotesFiles.empty()) { @@ -844,16 +924,26 @@ std::string count = this->GetCTestConfiguration("CTestSubmitRetryCount"); std::string delay = this->GetCTestConfiguration("CTestSubmitRetryDelay"); - auto const func = cmListFileFunction( - "ctest_submit", 0, 0, - { - cmListFileArgument("RETRY_COUNT"_s, cmListFileArgument::Unquoted, 0), - cmListFileArgument(count, cmListFileArgument::Quoted, 0), - cmListFileArgument("RETRY_DELAY"_s, cmListFileArgument::Unquoted, 0), - cmListFileArgument(delay, cmListFileArgument::Quoted, 0), - cmListFileArgument("RETURN_VALUE"_s, cmListFileArgument::Unquoted, 0), - cmListFileArgument("return_value"_s, cmListFileArgument::Unquoted, 0), - }); + std::vector<cmListFileArgument> submitArgs = { + cmListFileArgument("RETRY_COUNT"_s, cmListFileArgument::Unquoted, 0), + cmListFileArgument(count, cmListFileArgument::Quoted, 0), + cmListFileArgument("RETRY_DELAY"_s, cmListFileArgument::Unquoted, 0), + cmListFileArgument(delay, cmListFileArgument::Quoted, 0), + cmListFileArgument("RETURN_VALUE"_s, cmListFileArgument::Unquoted, 0), + cmListFileArgument("return_value"_s, cmListFileArgument::Unquoted, 0), + }; + cmValue submitParts = mf.GetDefinition("CTEST_SUBMIT_PARTS"); + if (submitParts && !submitParts->empty()) { + cmList submitPartsList{ *submitParts }; + if (!submitPartsList.empty()) { + submitArgs.emplace_back("PARTS"_s, cmListFileArgument::Unquoted, 0); + for (auto const& part : submitPartsList) { + submitArgs.emplace_back(part, cmListFileArgument::Quoted, 0); + } + } + } + auto const func = + cmListFileFunction("ctest_submit", 0, 0, std::move(submitArgs)); auto status = cmExecutionStatus(mf); if (!mf.ExecuteCommand(func, status) || std::stoi(mf.GetDefinition("return_value")) < 0) { @@ -1251,8 +1341,8 @@ std::vector<std::string> files; files.push_back(file); - if (!cmSystemTools::CreateTar(tarFile, files, {}, - cmSystemTools::TarCompressGZip, false)) { + if (!cmSystemTools::CreateTar( + tarFile, files, {}, cmSystemTools::TarCompressGZip, "UTF-8", false)) { cmCTestLog(this, ERROR_MESSAGE, "Error creating tar while " "encoding file: " @@ -1458,7 +1548,9 @@ " ctest -D Nightly\n" " ctest -D Nightly(Start|Update|Configure|Build)\n" " ctest -D Nightly(Test|Coverage|MemCheck|Submit)\n" - " ctest -D NightlyMemoryCheck\n"); + " ctest -D NightlyMemoryCheck\n" + " ctest -D CTEST_<VAR>=<value>\n" + " ctest -D <var>:<type>=<value>\n"); } bool cmCTest::CheckArgument(std::string const& arg, cm::string_view varg1, @@ -1497,16 +1589,26 @@ return true; } + // Also accept CTEST_VAR=VALUE (without :TYPE=). + auto const eq = arg.find('='); + if (eq != std::string::npos && eq > 0) { + name = arg.substr(0, eq); + if (cmHasLiteralPrefix(name, "CTEST_")) { + this->Impl->Definitions[name] = arg.substr(eq + 1); + return true; + } + } + return false; } -bool cmCTest::SetArgsFromPreset(std::string const& presetName, - bool listPresets) +bool cmCTest::SetArgsFromPreset(cmCMakePresetsArgs const& args) { auto const workingDirectory = cmSystemTools::GetLogicalWorkingDirectory(); cmCMakePresetsGraph settingsFile; - auto result = settingsFile.ReadProjectPresets(workingDirectory); + auto result = + settingsFile.ReadProjectPresets(workingDirectory, args.PresetsFile); if (result != true) { cmSystemTools::Error(cmStrCat("Could not read presets from ", workingDirectory, ":\n", @@ -1514,40 +1616,23 @@ return false; } - if (listPresets) { + if (args.ListPresets) { settingsFile.PrintTestPresetList(); return true; } - auto presetPair = settingsFile.TestPresets.find(presetName); - if (presetPair == settingsFile.TestPresets.end()) { - cmSystemTools::Error(cmStrCat("No such test preset in ", workingDirectory, - ": \"", presetName, '"')); + auto resolveResult = + settingsFile.ResolvePreset(args.PresetName, settingsFile.TestPresets); + auto resolveError = + cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::TestPreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + workingDirectory); + if (resolveError) { + cmSystemTools::Error(*resolveError); settingsFile.PrintTestPresetList(); return false; } - - if (presetPair->second.Unexpanded.Hidden) { - cmSystemTools::Error(cmStrCat("Cannot use hidden test preset in ", - workingDirectory, ": \"", presetName, '"')); - settingsFile.PrintTestPresetList(); - return false; - } - - auto const& expandedPreset = presetPair->second.Expanded; - if (!expandedPreset) { - cmSystemTools::Error(cmStrCat("Could not evaluate test preset \"", - presetName, "\": Invalid macro expansion")); - settingsFile.PrintTestPresetList(); - return false; - } - - if (!expandedPreset->ConditionResult) { - cmSystemTools::Error(cmStrCat("Cannot use disabled test preset in ", - workingDirectory, ": \"", presetName, '"')); - settingsFile.PrintTestPresetList(); - return false; - } + auto const* expandedPreset = resolveResult.Preset; auto configurePresetPair = settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset); @@ -1633,76 +1718,12 @@ this->Impl->SubprojectSummary = expandedPreset->Output->SubprojectSummary.value_or(true); - if (expandedPreset->Output->MaxPassedTestOutputSize) { - this->Impl->TestOptions.OutputSizePassed = - *expandedPreset->Output->MaxPassedTestOutputSize; - } - - if (expandedPreset->Output->MaxFailedTestOutputSize) { - this->Impl->TestOptions.OutputSizeFailed = - *expandedPreset->Output->MaxFailedTestOutputSize; - } - - if (expandedPreset->Output->TestOutputTruncation) { - this->Impl->TestOptions.OutputTruncation = - *expandedPreset->Output->TestOutputTruncation; - } - if (expandedPreset->Output->MaxTestNameWidth) { this->Impl->MaxTestNameWidth = *expandedPreset->Output->MaxTestNameWidth; } } - if (expandedPreset->Filter) { - if (expandedPreset->Filter->Include) { - this->Impl->TestOptions.IncludeRegularExpression = - expandedPreset->Filter->Include->Name; - if (!expandedPreset->Filter->Include->Label.empty()) { - this->Impl->TestOptions.LabelRegularExpression.push_back( - expandedPreset->Filter->Include->Label); - } - - if (expandedPreset->Filter->Include->Index) { - if (expandedPreset->Filter->Include->Index->IndexFile.empty()) { - auto const& start = expandedPreset->Filter->Include->Index->Start; - auto const& end = expandedPreset->Filter->Include->Index->End; - auto const& stride = expandedPreset->Filter->Include->Index->Stride; - std::string indexOptions = cmStrCat( - (start ? std::to_string(*start) : std::string{}), ',', - (end ? std::to_string(*end) : std::string{}), ',', - (stride ? std::to_string(*stride) : std::string{}), ',', - cmJoin(expandedPreset->Filter->Include->Index->SpecificTests, - ",")); - - this->Impl->TestOptions.TestsToRunInformation = indexOptions; - } else { - this->Impl->TestOptions.TestsToRunInformation = - expandedPreset->Filter->Include->Index->IndexFile; - } - } - - this->Impl->TestOptions.UseUnion = - expandedPreset->Filter->Include->UseUnion.value_or(false); - } - - if (expandedPreset->Filter->Exclude) { - this->Impl->TestOptions.ExcludeRegularExpression = - expandedPreset->Filter->Exclude->Name; - if (!expandedPreset->Filter->Exclude->Label.empty()) { - this->Impl->TestOptions.ExcludeLabelRegularExpression.push_back( - expandedPreset->Filter->Exclude->Label); - } - - if (expandedPreset->Filter->Exclude->Fixtures) { - this->Impl->TestOptions.ExcludeFixtureRegularExpression = - expandedPreset->Filter->Exclude->Fixtures->Any; - this->Impl->TestOptions.ExcludeFixtureSetupRegularExpression = - expandedPreset->Filter->Exclude->Fixtures->Setup; - this->Impl->TestOptions.ExcludeFixtureCleanupRegularExpression = - expandedPreset->Filter->Exclude->Fixtures->Cleanup; - } - } - } + cmCTestApplyTestPresetToOptions(this->Impl->TestOptions, *expandedPreset); if (expandedPreset->Execution) { this->Impl->StopOnFailure = @@ -1720,9 +1741,6 @@ this->Impl->ParallelLevelSetInCli = true; } - this->Impl->TestOptions.ResourceSpecFile = - expandedPreset->Execution->ResourceSpecFile; - if (expandedPreset->Execution->TestLoad) { auto testLoad = *expandedPreset->Execution->TestLoad; this->SetTestLoad(testLoad); @@ -1812,47 +1830,11 @@ bool processSteps = false; bool SRArgumentSpecified = false; std::vector<std::pair<std::string, bool>> runScripts; + cmCMakePresetsArgs presetsArgs; // copy the command line cm::append(this->Impl->InitialCommandLineArguments, args); - // check if a test preset was specified - - bool listPresets = - find(args.begin(), args.end(), "--list-presets") != args.end(); - auto it = - std::find_if(args.begin(), args.end(), [](std::string const& arg) -> bool { - return arg == "--preset" || cmHasLiteralPrefix(arg, "--preset="); - }); - if (listPresets || it != args.end()) { - std::string errormsg; - bool success; - - if (listPresets) { - // If listing presets we don't need a presetName - success = this->SetArgsFromPreset("", listPresets); - } else { - if (cmHasLiteralPrefix(*it, "--preset=")) { - auto const& presetName = it->substr(9); - success = this->SetArgsFromPreset(presetName, listPresets); - } else if (++it != args.end()) { - auto const& presetName = *it; - success = this->SetArgsFromPreset(presetName, listPresets); - } else { - cmSystemTools::Error("'--preset' requires an argument"); - success = false; - } - } - - if (listPresets) { - return success ? 0 : 1; - } - - if (!success) { - return 1; - } - } - auto const dashD = [this, &processSteps](std::string const& targ) -> bool { // AddTestsForDashboard parses the dashboard type and converts it // into the separate stages @@ -2034,6 +2016,30 @@ using CommandArgument = cmCommandLineArgument<bool(std::string const& value)>; + auto const presetArguments = std::vector<CommandArgument>{ + CommandArgument{ "--list-presets", CommandArgument::Values::Zero, + [&presetsArgs](std::string const&) -> bool { + presetsArgs.ListPresets = true; + return true; + } }, + CommandArgument{ "--preset", "'--preset' requires an argument", + CommandArgument::Values::One, + [&presetsArgs](std::string const& presetArg) -> bool { + presetsArgs.PresetName = presetArg; + return true; + } }, + CommandArgument{ "--presets-file", "'--presets-file' requires an argument", + CommandArgument::Values::One, + [&presetsArgs](std::string const& presetFileArg) -> bool { + presetsArgs.PresetsFile = + cmSystemTools::ToNormalizedPathOnDisk(presetFileArg); + return true; + } } + }; + auto const isPresetArgument = [&](std::string const& arg) -> bool { + return cmHasLiteralPrefix(arg, "--preset") || arg == "--list-presets"; + }; + auto const arguments = std::vector<CommandArgument>{ CommandArgument{ "--dashboard", CommandArgument::Values::One, dashD }, CommandArgument{ "-D", @@ -2416,6 +2422,18 @@ } }, CommandArgument{ "-A", CommandArgument::Values::One, dashA }, CommandArgument{ "--add-notes", CommandArgument::Values::One, dashA }, + CommandArgument{ "--source-dir", "'--source-dir' requires an argument", + CommandArgument::Values::One, + [this](std::string const& dir) -> bool { + this->Impl->SourceDir = dir; + return true; + } }, + CommandArgument{ "--build-dir", "'--build-dir' requires an argument", + CommandArgument::Values::One, + [this](std::string const& dir) -> bool { + this->Impl->TestDir = dir; + return true; + } }, CommandArgument{ "--test-dir", "'--test-dir' requires an argument", CommandArgument::Values::One, [this](std::string const& dir) -> bool { @@ -2505,9 +2523,45 @@ } }, }; - // process the command line arguments + // Process command line arguments for presets first, since other arguments + // can override those settings. for (size_t i = 1; i < args.size(); ++i) { std::string const& arg = args[i]; + for (auto const& m : presetArguments) { + if (m.matches(arg)) { + if (!m.parse(arg, i, args)) { + return 1; + } + break; + } + } + } + + if (presetsArgs.HasPresetsArg()) { + bool success = this->SetArgsFromPreset(presetsArgs); + if (presetsArgs.ListPresets) { + return static_cast<int>(!success); + } + if (!success) { + return 1; + } + } + + // Process the remaining command line arguments. + bool hadPassthroughDelimiter = false; + for (size_t i = 1; i < args.size(); ++i) { + std::string const& arg = args[i]; + + // The "--" argument terminates processing and passes + // all remaining arguments through to the tests. + if (arg == "--"_s) { + hadPassthroughDelimiter = true; + std::copy( + args.begin() + i + 1, args.end(), + std::back_inserter(this->Impl->TestOptions.TestPassthroughArguments)); + break; + } + bool matched = false; for (auto const& m : arguments) { if (m.matches(arg)) { @@ -2535,8 +2589,7 @@ this->Impl->BuildAndTest.TestCommandArgs.emplace_back(args[i]); } } - if (!matched && cmHasPrefix(arg, '-') && - !cmHasLiteralPrefix(arg, "--preset")) { + if (!matched && cmHasPrefix(arg, '-') && !isPresetArgument(arg)) { cmSystemTools::Error(cmStrCat("Unknown argument: ", arg)); cmSystemTools::Error("Run 'ctest --help' for all supported options."); return 1; @@ -2580,8 +2633,29 @@ } } - // TestProgressOutput only supported if console supports it and not logging - // to a file + // Passthrough arguments (after --) are only supported in direct test + // execution mode, not --build-and-test, -S, or -D/-T/-M modes. + if (hadPassthroughDelimiter) { + if (cmakeAndTest) { + cmSystemTools::Error( + "The -- option cannot be used with --build-and-test. " + "Use --build-options or --test-command to forward arguments."); + return 1; + } + if (!runScripts.empty()) { + cmSystemTools::Error( + "The -- option cannot be used with -S or -SP script mode."); + return 1; + } + if (processSteps) { + cmSystemTools::Error( + "The -- option cannot be used with -D, -T, or -M dashboard mode."); + return 1; + } + } + + // TestProgressOutput only supported if console supports it and not + // logging to a file this->Impl->TestProgressOutput = this->Impl->TestProgressOutput && !this->Impl->OutputLogFile && this->ProgressOutputSupportedByConsole(); #ifdef _WIN32 @@ -2609,6 +2683,19 @@ if (!this->Impl->TestDir.empty()) { workDir = cmSystemTools::ToNormalizedPathOnDisk(this->Impl->TestDir); } + + // When --source-dir is given, record it as the source directory override + // and ensure the binary directory exists so an empty dir is accepted. + if (!this->Impl->SourceDir.empty()) { + this->Impl->CTestConfigurationOverwrites["SourceDirectory"] = + cmSystemTools::ToNormalizedPathOnDisk(this->Impl->SourceDir); + if (!cmSystemTools::MakeDirectory(workDir)) { + cmCTestLog(this, ERROR_MESSAGE, + "Failed to create directory: " << workDir << std::endl); + return 1; + } + } + cmWorkingDirectory changeDir(workDir); if (changeDir.Failed()) { cmCTestLog(this, ERROR_MESSAGE, changeDir.GetError() << std::endl); @@ -2676,12 +2763,16 @@ cmInstrumentation instrumentation(this->GetBinaryDir()); auto processHandler = [&handler]() -> int { - return handler.ProcessHandler() < 0 ? cmCTest::TEST_ERRORS : 0; + return handler.ProcessHandler() != 0 ? cmCTest::TEST_ERRORS : 0; }; std::map<std::string, std::string> data; data["showOnly"] = this->GetShowOnly() ? "1" : "0"; - int ret = - instrumentation.InstrumentCommand("ctest", args, processHandler, data); + int ret = instrumentation.InstrumentCommand( + "ctest", args, + [processHandler]() -> cmInstrumentation::CommandResult { + return { processHandler(), cm::nullopt, cm::nullopt }; + }, + data); instrumentation.CollectTimingData(cmInstrumentationQuery::Hook::PostCTest); if (ret == cmCTest::TEST_ERRORS) { cmCTestLog(this, ERROR_MESSAGE, "Errors while running CTest\n"); @@ -3180,82 +3271,140 @@ return true; } +namespace { +// Mapping of CTEST_* variable names to CTest configuration keys. +struct CTestVarConfigEntry +{ + char const* Var; // CTEST_* variable name + char const* Config; // CTest configuration key +}; + +// clang-format off +CTestVarConfigEntry const kCTestVarConfigMap[] = { + // General + { "CTEST_SITE", "Site" }, + { "CTEST_BUILD_NAME", "BuildName" }, + { "CTEST_NIGHTLY_START_TIME", "NightlyStartTime" }, + { "CTEST_SOURCE_DIRECTORY", "SourceDirectory" }, + { "CTEST_BINARY_DIRECTORY", "BuildDirectory" }, + // Start step + { "CTEST_CHECKOUT_COMMAND", "CheckoutCommand" }, + // Update step + { "CTEST_UPDATE_COMMAND", "UpdateCommand" }, + { "CTEST_UPDATE_OPTIONS", "UpdateOptions" }, + { "CTEST_UPDATE_TYPE", "UpdateType" }, + { "CTEST_CVS_COMMAND", "CVSCommand" }, + { "CTEST_CVS_UPDATE_OPTIONS", "CVSUpdateOptions" }, + { "CTEST_SVN_COMMAND", "SVNCommand" }, + { "CTEST_SVN_UPDATE_OPTIONS", "SVNUpdateOptions" }, + { "CTEST_SVN_OPTIONS", "SVNOptions" }, + { "CTEST_BZR_COMMAND", "BZRCommand" }, + { "CTEST_BZR_UPDATE_OPTIONS", "BZRUpdateOptions" }, + { "CTEST_GIT_COMMAND", "GITCommand" }, + { "CTEST_GIT_UPDATE_OPTIONS", "GITUpdateOptions" }, + { "CTEST_GIT_INIT_SUBMODULES", "GITInitSubmodules" }, + { "CTEST_GIT_UPDATE_CUSTOM", "GITUpdateCustom" }, + { "CTEST_UPDATE_VERSION_ONLY", "UpdateVersionOnly" }, + { "CTEST_UPDATE_VERSION_OVERRIDE", "UpdateVersionOverride" }, + { "CTEST_HG_COMMAND", "HGCommand" }, + { "CTEST_HG_UPDATE_OPTIONS", "HGUpdateOptions" }, + { "CTEST_P4_COMMAND", "P4Command" }, + { "CTEST_P4_UPDATE_CUSTOM", "P4UpdateCustom" }, + { "CTEST_P4_UPDATE_OPTIONS", "P4UpdateOptions" }, + { "CTEST_P4_CLIENT", "P4Client" }, + { "CTEST_P4_OPTIONS", "P4Options" }, + // Configure step + { "CTEST_CONFIGURE_COMMAND", "ConfigureCommand" }, + { "CTEST_LABELS_FOR_SUBPROJECTS", "LabelsForSubprojects" }, + { "CTEST_CMAKE_GENERATOR", "CMakeGenerator" }, + { "CTEST_CMAKE_GENERATOR_PLATFORM", "CMakeGeneratorPlatform" }, + { "CTEST_CMAKE_GENERATOR_TOOLSET", "CMakeGeneratorToolset" }, + // Build step + { "CTEST_BUILD_COMMAND", "MakeCommand" }, + { "CTEST_USE_LAUNCHERS", "UseLaunchers" }, + { "CTEST_BUILD_FLAGS", "BuildFlags" }, + { "CTEST_BUILD_TARGET", "BuildTarget" }, + // Test step + { "CTEST_TEST_TIMEOUT", "TimeOut" }, + { "CTEST_TEST_COVERAGE_TOOL", "CTestTestCoverageTool" }, + { "CTEST_RESOURCE_SPEC_FILE", "ResourceSpecFile" }, + { "CTEST_TEST_LOAD", "TestLoad" }, + // Coverage step + { "CTEST_COVERAGE_COMMAND", "CoverageCommand" }, + { "CTEST_COVERAGE_EXTRA_FLAGS", "CoverageExtraFlags" }, + { "CTEST_EXTRA_COVERAGE_GLOB", "ExtraCoverageGlob" }, + // MemCheck step + { "CTEST_MEMORYCHECK_TYPE", "MemoryCheckType" }, + { "CTEST_MEMORYCHECK_SANITIZER_OPTIONS", "MemoryCheckSanitizerOptions" }, + { "CTEST_MEMORYCHECK_COMMAND", "MemoryCheckCommand" }, + { "CTEST_MEMORYCHECK_COMMAND_OPTIONS", "MemoryCheckCommandOptions" }, + { "CTEST_MEMORYCHECK_SUPPRESSIONS_FILE", "MemoryCheckSuppressionFile" }, + // Submit step + { "CTEST_NOTES_FILES", "NotesFiles" }, + { "CTEST_EXTRA_SUBMIT_FILES", "ExtraSubmitFiles" }, + { "CTEST_SUBMIT_PARTS", "SubmitParts" }, + { "CTEST_SUBMIT_URL", "SubmitURL" }, + { "CTEST_DROP_METHOD", "DropMethod" }, + { "CTEST_DROP_SITE_USER", "DropSiteUser" }, + { "CTEST_DROP_SITE_PASSWORD", "DropSitePassword" }, + { "CTEST_DROP_SITE", "DropSite" }, + { "CTEST_DROP_LOCATION", "DropLocation" }, + { "CTEST_TLS_VERIFY", "TLSVerify" }, + { "CTEST_TLS_VERSION", "TLSVersion" }, + { "CTEST_CURL_OPTIONS", "CurlOptions" }, + { "CTEST_SUBMIT_INACTIVITY_TIMEOUT", "SubmitInactivityTimeout" }, + { "CTEST_TIME_LIMIT", "TimeLimit" }, +}; +// clang-format on + +// Entries accepted by ApplyDefinitionsToCTestConfig() (i.e. settable via +// "ctest -D") but not emitted by SetCMakeVariables(), because their forward +// direction is handled outside of that function. +// +// CTEST_CVS_CHECKOUT - Deprecated alias for CTEST_CHECKOUT_COMMAND. +// The canonical name is already in kCTestVarConfigMap. +// CTEST_CHANGE_ID - Handler commands push this *into* the config map +// themselves rather than reading it out; +// SetCMakeVariables intentionally has no entry for it. +CTestVarConfigEntry const kCTestVarConfigMapReverseOnly[] = { + { "CTEST_CVS_CHECKOUT", "CheckoutCommand" }, + { "CTEST_CHANGE_ID", "ChangeId" }, +}; +} // namespace + void cmCTest::SetCMakeVariables(cmMakefile& mf) { - auto set = [&](char const* cmake_var, char const* ctest_opt) { - std::string val = this->GetCTestConfiguration(ctest_opt); + for (auto const& entry : kCTestVarConfigMap) { + std::string val = this->GetCTestConfiguration(entry.Config); if (!val.empty()) { cmCTestOptionalLog( this, HANDLER_VERBOSE_OUTPUT, - "SetCMakeVariable:" << cmake_var << ":" << val << std::endl, false); - mf.AddDefinition(cmake_var, val); + "SetCMakeVariable:" << entry.Var << ":" << val << std::endl, false); + mf.AddDefinition(entry.Var, val); } - }; + } +} - set("CTEST_SITE", "Site"); - set("CTEST_BUILD_NAME", "BuildName"); - set("CTEST_NIGHTLY_START_TIME", "NightlyStartTime"); - set("CTEST_SOURCE_DIRECTORY", "SourceDirectory"); - set("CTEST_BINARY_DIRECTORY", "BuildDirectory"); +void cmCTest::ApplyDefinitionsToCTestConfig() +{ + // Build a reverse-lookup map from both tables the first time this is called. + static std::map<std::string, std::string> const reverseMap = []() { + std::map<std::string, std::string> m; + for (auto const& e : kCTestVarConfigMap) { + m.emplace(e.Var, e.Config); + } + for (auto const& e : kCTestVarConfigMapReverseOnly) { + m.emplace(e.Var, e.Config); + } + return m; + }(); - // CTest Update Step - set("CTEST_UPDATE_COMMAND", "UpdateCommand"); - set("CTEST_UPDATE_OPTIONS", "UpdateOptions"); - set("CTEST_UPDATE_TYPE", "UpdateType"); - set("CTEST_CVS_COMMAND", "CVSCommand"); - set("CTEST_CVS_UPDATE_OPTIONS", "CVSUpdateOptions"); - set("CTEST_SVN_COMMAND", "SVNCommand"); - set("CTEST_SVN_UPDATE_OPTIONS", "SVNUpdateOptions"); - set("CTEST_SVN_OPTIONS", "SVNOptions"); - set("CTEST_BZR_COMMAND", "BZRCommand"); - set("CTEST_BZR_UPDATE_OPTIONS", "BZRUpdateOptions"); - set("CTEST_GIT_COMMAND", "GITCommand"); - set("CTEST_GIT_UPDATE_OPTIONS", "GITUpdateOptions"); - set("CTEST_GIT_INIT_SUBMODULES", "GITInitSubmodules"); - set("CTEST_GIT_UPDATE_CUSTOM", "GITUpdateCustom"); - set("CTEST_UPDATE_VERSION_ONLY", "UpdateVersionOnly"); - set("CTEST_UPDATE_VERSION_OVERRIDE", "UpdateVersionOverride"); - set("CTEST_HG_COMMAND", "HGCommand"); - set("CTEST_HG_UPDATE_OPTIONS", "HGUpdateOptions"); - set("CTEST_P4_COMMAND", "P4Command"); - set("CTEST_P4_UPDATE_CUSTOM", "P4UpdateCustom"); - set("CTEST_P4_UPDATE_OPTIONS", "P4UpdateOptions"); - set("CTEST_P4_CLIENT", "P4Client"); - set("CTEST_P4_OPTIONS", "P4Options"); - - // CTest Configure Step - set("CTEST_CONFIGURE_COMMAND", "ConfigureCommand"); - set("CTEST_LABELS_FOR_SUBPROJECTS", "LabelsForSubprojects"); - - // CTest Build Step - set("CTEST_BUILD_COMMAND", "MakeCommand"); - set("CTEST_USE_LAUNCHERS", "UseLaunchers"); - - // CTest Test Step - set("CTEST_TEST_TIMEOUT", "TimeOut"); - - // CTest Coverage Step - set("CTEST_COVERAGE_COMMAND", "CoverageCommand"); - set("CTEST_COVERAGE_EXTRA_FLAGS", "CoverageExtraFlags"); - - // CTest MemCheck Step - set("CTEST_MEMORYCHECK_TYPE", "MemoryCheckType"); - set("CTEST_MEMORYCHECK_SANITIZER_OPTIONS", "MemoryCheckSanitizerOptions"); - set("CTEST_MEMORYCHECK_COMMAND", "MemoryCheckCommand"); - set("CTEST_MEMORYCHECK_COMMAND_OPTIONS", "MemoryCheckCommandOptions"); - set("CTEST_MEMORYCHECK_SUPPRESSIONS_FILE", "MemoryCheckSuppressionFile"); - - // CTest Submit Step - set("CTEST_SUBMIT_URL", "SubmitURL"); - set("CTEST_DROP_METHOD", "DropMethod"); - set("CTEST_DROP_SITE_USER", "DropSiteUser"); - set("CTEST_DROP_SITE_PASSWORD", "DropSitePassword"); - set("CTEST_DROP_SITE", "DropSite"); - set("CTEST_DROP_LOCATION", "DropLocation"); - set("CTEST_TLS_VERIFY", "TLSVerify"); - set("CTEST_TLS_VERSION", "TLSVersion"); - set("CTEST_CURL_OPTIONS", "CurlOptions"); - set("CTEST_SUBMIT_INACTIVITY_TIMEOUT", "SubmitInactivityTimeout"); + for (auto const& def : this->Impl->Definitions) { + auto const it = reverseMap.find(def.first); + if (it != reverseMap.end()) { + this->SetCTestConfiguration(it->second.c_str(), def.second); + } + } } bool cmCTest::RunCommand(std::vector<std::string> const& args, @@ -3436,22 +3585,19 @@ if (!this->Impl->Quiet) { if (logType == HANDLER_TEST_PROGRESS_OUTPUT) { if (this->Impl->TestProgressOutput) { - if (this->Impl->FlushTestProgressLine) { - printf("\r"); - this->Impl->FlushTestProgressLine = false; - std::cout.flush(); + if (this->Impl->TestProgressNewlinePending) { + this->Impl->TestProgressNewlinePending = false; + std::cout << '\r'; } if (msg.find('\n') != std::string::npos) { - this->Impl->FlushTestProgressLine = true; + this->Impl->TestProgressNewlinePending = true; msg.erase(std::remove(msg.begin(), msg.end(), '\n'), msg.end()); } - std::cout << msg; -#ifndef _WIN32 - printf("\x1B[K"); // move caret to end -#endif - std::cout.flush(); + // ProgressOutputSupportedByConsole() already verified VT100 support. + // Erase the rest of the line before printing the message. + std::cout << kVT100_EraseLine << msg << std::flush; return; } logType = HANDLER_OUTPUT; @@ -3654,7 +3800,7 @@ for (unsigned int i = 0; i < d.GetNumberOfFiles(); i++) { std::string fpath = d.GetFilePath(i); - std::string fname = d.GetFile(i); + std::string const& fname = d.GetFileName(i); if (fname.rfind('.', 0) == 0) { continue; }
diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 46c88b7..4227e0e 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h
@@ -20,6 +20,7 @@ #include "cmProcessOutput.h" class cmake; +class cmCMakePresetsArgs; class cmGeneratedFileStream; class cmInstrumentation; class cmMakefile; @@ -451,8 +452,11 @@ /** add a variable definition from a command line -D value */ bool AddVariableDefinition(std::string const& arg); + /** apply CTEST_* variable definitions to the CTest configuration map */ + void ApplyDefinitionsToCTestConfig(); + /** set command line arguments read from a test preset */ - bool SetArgsFromPreset(std::string const& presetName, bool listPresets); + bool SetArgsFromPreset(cmCMakePresetsArgs const& args); /** returns true iff the console supports progress output */ static bool ProgressOutputSupportedByConsole();
diff --git a/Source/cmCacheDocumentationTable.cxx b/Source/cmCacheDocumentationTable.cxx new file mode 100644 index 0000000..186ffee --- /dev/null +++ b/Source/cmCacheDocumentationTable.cxx
@@ -0,0 +1,739 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +// Built-in cache variable documentation table. +// +// This file is hand-maintained. Each entry's Summary is a one-line tooltip +// installed as the ``HELPSTRING`` of cache variables created via +// ``cmake -D <var>=<value>`` when the user did not supply one. +// +// Membership policy: this table is deliberately limited to variables that a +// user would plausibly set on the ``cmake`` command line when configuring a +// project. +// +// Maintenance discipline: +// * Adding a new user-facing variable: hand-write one entry below in +// ASCII sort order and make sure the corresponding +// Help/variable/<NAME>.rst exists. +// * Significantly rewording a Help/variable/<NAME>.rst that has an entry +// here: review and update the corresponding Summary in the same change +// so the tooltip and the manual stay in step. CMake reviewers should +// look for this co-evolution. +// * Structural drift (typo in NAME, RST renamed or removed) is caught by +// Tests/RunCMake/CacheVarHelpCoverage, which asserts that every entry +// in this table has a matching Help/variable/<NAME>.rst. +// +// The Entry layout below (two lines per row, ``{ "NAME",\n "summary" },``) +// is intentional: it keeps the table easy to diff, review, and hand-edit +// against the corresponding Help/variable/*.rst manuals. clang-format is +// suppressed across the array so contributors can update one Summary +// without the formatter reflowing every neighbor. + +#include "cmCacheDocumentationTable.h" + +#include <algorithm> +#include <cstddef> +#include <iterator> +#include <string> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmCachePatternTable.h" + +namespace { + +using cmCacheDocumentationTable::Entry; + +// The entries are sorted by Name in ASCII order so that std::lower_bound +// can locate a variable in O(log N) time without any runtime indexing +// structure. The testCacheDocumentationTable regression test in +// Tests/CMakeLib verifies this invariant. +/* clang-format off */ +Entry const kEntries[] = { + { "BUILD_SHARED_LIBS"_s, + "Global default for the library type used by add_library() when no STATIC/SHARED keyword is given: SHARED if true, STATIC otherwise. Not defined by CMake itself; commonly exposed as an option() by projects."_s }, + { "BUILD_TESTING"_s, + "Control whether the CTest module invokes enable_testing()"_s }, + { "CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY"_s, + "Enable DEPENDS_EXPLICIT_ONLY option by default for add_custom_command()"_s }, + { "CMAKE_ADSP_ROOT"_s, + "When cross-compiling for ADSP SHARC/Blackfin, this variable holds the absolute path to the root install directory of the CCES or VDSP++ toolchain."_s }, + { "CMAKE_AIX_EXPORT_ALL_SYMBOLS"_s, + "Default value for AIX_EXPORT_ALL_SYMBOLS target property"_s }, + { "CMAKE_AIX_SHARED_LIBRARY_ARCHIVE"_s, + "Enable or disable creation of shared library archives"_s }, + { "CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS"_s, + "Default value for ANDROID_ANT_ADDITIONAL_OPTIONS target property"_s }, + { "CMAKE_ANDROID_API"_s, + "When cross-compiling for Android with NVIDIA Nsight Tegra Visual Studio Edition, sets the default for the ANDROID_API target property. In other Android builds, initializes CMAKE_SYSTEM_VERSION if not already set."_s }, + { "CMAKE_ANDROID_API_MIN"_s, + "Default minimum Android API level for targets"_s }, + { "CMAKE_ANDROID_ARCH"_s, + "When cross-compiling for Android with NVIDIA Nsight Tegra Visual Studio Edition, sets the default for the ANDROID_ARCH target property. Otherwise, the architecture name (arm, arm64, x86, x86_64) corresponding to CMAKE_ANDROID_ARCH_ABI."_s }, + { "CMAKE_ANDROID_ARCH_ABI"_s, + "When cross-compiling for Android, this variable specifies the target architecture and ABI (arm64-v8a, armeabi-v7a, x86, x86_64, etc.)."_s }, + { "CMAKE_ANDROID_ARM_MODE"_s, + "When cross-compiling for Android with armeabi architecture, set to ON to target 32-bit ARM (-marm) instead of the default 16-bit Thumb."_s }, + { "CMAKE_ANDROID_ARM_NEON"_s, + "When Cross Compiling for Android and CMAKE_ANDROID_ARCH_ABI is set to armeabi-v7a set CMAKE_ANDROID_ARM_NEON to ON to target ARM NEON devices."_s }, + { "CMAKE_ANDROID_ASSETS_DIRECTORIES"_s, + "Default value for ANDROID_ASSETS_DIRECTORIES target property"_s }, + { "CMAKE_ANDROID_EXCEPTIONS"_s, + "Specify whether C++ exceptions are enabled in Android NDK builds"_s }, + { "CMAKE_ANDROID_GUI"_s, + "Default value for ANDROID_GUI target property of executables"_s }, + { "CMAKE_ANDROID_JAR_DEPENDENCIES"_s, + "Default value for ANDROID_JAR_DEPENDENCIES target property"_s }, + { "CMAKE_ANDROID_JAR_DIRECTORIES"_s, + "Default value for ANDROID_JAR_DIRECTORIES target property"_s }, + { "CMAKE_ANDROID_JAVA_SOURCE_DIR"_s, + "Default value for ANDROID_JAVA_SOURCE_DIR target property"_s }, + { "CMAKE_ANDROID_NATIVE_LIB_DEPENDENCIES"_s, + "Default value for ANDROID_NATIVE_LIB_DEPENDENCIES target property"_s }, + { "CMAKE_ANDROID_NATIVE_LIB_DIRECTORIES"_s, + "Default value for ANDROID_NATIVE_LIB_DIRECTORIES target property"_s }, + { "CMAKE_ANDROID_NDK"_s, + "When Cross Compiling for Android with the NDK, this variable holds the absolute path to the root directory of the NDK. The directory must contain a platforms subdirectory holding the android-<api> directories."_s }, + { "CMAKE_ANDROID_NDK_DEPRECATED_HEADERS"_s, + "When cross-compiling for Android with the NDK, specify whether to use deprecated per-api-level headers instead of the unified headers."_s }, + { "CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION"_s, + "When cross-compiling for Android with the NDK, specify the toolchain version (clang on r19+, or GCC/Clang version on r18-); default selects latest available GCC."_s }, + { "CMAKE_ANDROID_PROCESS_MAX"_s, + "Default value for ANDROID_PROCESS_MAX target property"_s }, + { "CMAKE_ANDROID_PROGUARD"_s, + "Default value for ANDROID_PROGUARD target property"_s }, + { "CMAKE_ANDROID_PROGUARD_CONFIG_PATH"_s, + "Default value for ProGuard config file location on Android targets"_s }, + { "CMAKE_ANDROID_RTTI"_s, + "Enable RTTI for Android NDK cross compilation"_s }, + { "CMAKE_ANDROID_SECURE_PROPS_PATH"_s, + "Default value for Android secure properties file location"_s }, + { "CMAKE_ANDROID_SKIP_ANT_STEP"_s, + "Default value for skipping Ant build step for Android targets"_s }, + { "CMAKE_ANDROID_STANDALONE_TOOLCHAIN"_s, + "When Cross Compiling for Android with a Standalone Toolchain, this variable holds the absolute path to the root directory of the toolchain. The specified directory must contain a sysroot subdirectory."_s }, + { "CMAKE_ANDROID_STL_TYPE"_s, + "For Android with Tegra, sets default ANDROID_STL_TYPE target property; for NDK, selects STL variant (none, system, gabi++, gnustl, c++, stlport); default is gnustl_static or c++_static."_s }, + { "CMAKE_APPBUNDLE_PATH"_s, + "Search path for macOS application bundles used by find_program() and find_package()"_s }, + { "CMAKE_APPLE_SILICON_PROCESSOR"_s, + "On Apple Silicon macOS hosts, set to arm64 or x86_64 to specify CMAKE_HOST_SYSTEM_PROCESSOR; user cache entry only, never modified by project code."_s }, + { "CMAKE_ARCHIVE_OUTPUT_DIRECTORY"_s, + "Default output directory for archive target files"_s }, + { "CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG"_s, + "Enable better dependency graph for multi-config autogen targets"_s }, + { "CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX"_s, + "Limit for autogen command line length triggering response file use on Windows"_s }, + { "CMAKE_AUTOGEN_INTERMEDIATE_DIR_STRATEGY"_s, + "Strategy for autogen intermediate directories: FULL (pattern-based) or SHORT (hash-based)"_s }, + { "CMAKE_AUTOGEN_ORIGIN_DEPENDS"_s, + "Forward origin target dependencies to autogen targets"_s }, + { "CMAKE_AUTOGEN_PARALLEL"_s, + "Number of parallel moc/uic processes for autogen targets"_s }, + { "CMAKE_AUTOGEN_USE_SYSTEM_INCLUDE"_s, + "Add autogen include directory as system include to targets"_s }, + { "CMAKE_AUTOGEN_VERBOSE"_s, + "Sets verbosity of AUTOMOC/AUTOUIC/AUTORCC generators; positive integer or true boolean enables extra output."_s }, + { "CMAKE_AUTOMOC"_s, + "Whether to handle Qt moc automatically for targets"_s }, + { "CMAKE_AUTOMOC_COMPILER_PREDEFINES"_s, + "Whether to generate compiler predefines for moc"_s }, + { "CMAKE_AUTOMOC_DEPEND_FILTERS"_s, + "Filter definitions for extracting additional dependencies for moc"_s }, + { "CMAKE_AUTOMOC_EXECUTABLE"_s, + "Path to moc executable for AUTOMOC"_s }, + { "CMAKE_AUTOMOC_INCLUDE_DIRECTORIES"_s, + "Include directories for AUTOMOC to pass to moc."_s }, + { "CMAKE_AUTOMOC_MACRO_NAMES"_s, + "Macro names used by AUTOMOC to determine if a file needs moc processing."_s }, + { "CMAKE_AUTOMOC_MOC_OPTIONS"_s, + "Additional options for moc when using CMAKE_AUTOMOC."_s }, + { "CMAKE_AUTOMOC_PATH_PREFIX"_s, + "Generate -p path prefix option for moc on AUTOMOC targets."_s }, + { "CMAKE_AUTORCC"_s, + "Whether to handle rcc automatically for Qt targets."_s }, + { "CMAKE_AUTORCC_EXECUTABLE"_s, + "Custom rcc executable path for AUTORCC."_s }, + { "CMAKE_AUTORCC_OPTIONS"_s, + "Additional options for rcc when using CMAKE_AUTORCC."_s }, + { "CMAKE_AUTOUIC"_s, + "Whether to handle uic automatically for Qt targets."_s }, + { "CMAKE_AUTOUIC_EXECUTABLE"_s, + "Custom uic executable path for AUTOUIC."_s }, + { "CMAKE_AUTOUIC_OPTIONS"_s, + "Additional options for uic when using CMAKE_AUTOUIC."_s }, + { "CMAKE_AUTOUIC_SEARCH_PATHS"_s, + "Search paths for AUTOUIC to find included .ui files."_s }, + { "CMAKE_BUILD_RPATH"_s, + "Runtime path (RPATH) entries to add to binaries in the build tree."_s }, + { "CMAKE_BUILD_RPATH_USE_ORIGIN"_s, + "Whether to use relative paths for the build RPATH."_s }, + { "CMAKE_BUILD_TYPE"_s, + "Build type for single-configuration generators (Makefile Generators, Ninja, etc.). Typical values: Debug, Release, RelWithDebInfo, MinSizeRel; custom build types may also be defined."_s }, + { "CMAKE_BUILD_WITH_INSTALL_NAME_DIR"_s, + "Whether to use INSTALL_NAME_DIR on targets in the build tree."_s }, + { "CMAKE_BUILD_WITH_INSTALL_RPATH"_s, + "Build with install RPATH instead of requiring relinking."_s }, + { "CMAKE_CLANG_VFS_OVERLAY"_s, + "Absolute path to clang virtual file system YAML file for Windows clang-cl cross-compilation"_s }, + { "CMAKE_CODEBLOCKS_COMPILER_ID"_s, + "Compiler ID in generated CodeBlocks project files; auto-detected if empty"_s }, + { "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"_s, + "Exclude files outside project root from CodeBlocks project files"_s }, + { "CMAKE_CODELITE_USE_TARGETS"_s, + "Generate CodeLite project files based on targets rather than projects"_s }, + { "CMAKE_COLOR_DIAGNOSTICS"_s, + "Enable color diagnostics in compiler and build system output"_s }, + { "CMAKE_COLOR_MAKEFILE"_s, + "Enable color output when using Makefile generators"_s }, + { "CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY"_s, + "Directory for compiler-generated .pdb debug symbol files"_s }, + { "CMAKE_COMPILE_WARNING_AS_ERROR"_s, + "Specify whether to treat warnings on compile as errors. This variable is used to initialize the COMPILE_WARNING_AS_ERROR property on all the targets."_s }, + { "CMAKE_CONFIGURATION_TYPES"_s, + "Semicolon-separated list of available build types for multi-config generators (Visual Studio, Xcode, Ninja Multi-Config). Typical entries: Debug, Release, RelWithDebInfo, MinSizeRel; custom entries may also be defined."_s }, + { "CMAKE_CROSSCOMPILING"_s, + "Set to true by CMake when cross compiling (if CMAKE_SYSTEM_NAME is manually set). Not recommended for Apple device builds due to SDK-based target selection."_s }, + { "CMAKE_CROSS_CONFIGS"_s, + "Semicolon-separated list of configurations for Ninja Multi-Config cross-config mode"_s }, + { "CMAKE_CTEST_ARGUMENTS"_s, + "Command-line arguments to pass to ctest when running tests"_s }, + { "CMAKE_CUDA_ARCHITECTURES"_s, + "Default GPU architectures for CUDA compilation"_s }, + { "CMAKE_CUDA_EXTENSIONS"_s, + "Default value for CUDA_EXTENSIONS target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS"_s, + "Default value for CUDA_RESOLVE_DEVICE_SYMBOLS target property"_s }, + { "CMAKE_CUDA_RUNTIME_LIBRARY"_s, + "Select CUDA runtime library for compiling and linking CUDA code"_s }, + { "CMAKE_CUDA_SEPARABLE_COMPILATION"_s, + "Default value for CUDA_SEPARABLE_COMPILATION target property"_s }, + { "CMAKE_CUDA_STANDARD"_s, + "Default value for CUDA_STANDARD target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_CUDA_STANDARD_REQUIRED"_s, + "Default value for CUDA_STANDARD_REQUIRED target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_CXX_EXTENSIONS"_s, + "Default value for CXX_EXTENSIONS target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_CXX_MODULE_STD"_s, + "Whether to add utility targets as dependencies for C++23 targets"_s }, + { "CMAKE_CXX_SCAN_FOR_MODULES"_s, + "Whether to scan C++ source files for module dependencies"_s }, + { "CMAKE_CXX_STANDARD"_s, + "Default value used to initialize the CXX_STANDARD target property when a target is created. See the cmake-compile-features(7) manual for the list of supported standards and compilers."_s }, + { "CMAKE_CXX_STANDARD_REQUIRED"_s, + "Default value for CXX_STANDARD_REQUIRED target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_C_EXTENSIONS"_s, + "Default value for C_EXTENSIONS target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_C_STANDARD"_s, + "Default value used to initialize the C_STANDARD target property when a target is created. See the cmake-compile-features(7) manual for the list of supported standards and compilers."_s }, + { "CMAKE_C_STANDARD_REQUIRED"_s, + "Default value for C_STANDARD_REQUIRED target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_DEBUGGER_WORKING_DIRECTORY"_s, + "Working directory for debugger when launching targets"_s }, + { "CMAKE_DEBUG_TARGET_PROPERTIES"_s, + "Enables debug output for target properties by listing which properties to trace when evaluating targets."_s }, + { "CMAKE_DEFAULT_BUILD_TYPE"_s, + "Default configuration to use in build.ninja for Ninja Multi-Config"_s }, + { "CMAKE_DEFAULT_CONFIGS"_s, + "Default configurations to build for targets without :Config suffix"_s }, + { "CMAKE_DEPENDS_IN_PROJECT_ONLY"_s, + "Only consider dependencies within project source and binary directories"_s }, + { "CMAKE_DEPENDS_USE_COMPILER"_s, + "Set FALSE to use CMake for dependency discovery instead of compiler"_s }, + { "CMAKE_DISABLE_PRECOMPILE_HEADERS"_s, + "Default value for disabling precompiled headers on all targets"_s }, + { "CMAKE_DLL_NAME_WITH_SOVERSION"_s, + "Whether to include soversion in DLL names on Windows platform"_s }, + { "CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES"_s, + "Generate linked resources in Eclipse project generator output"_s }, + { "CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT"_s, + "Generate Eclipse project in source directory for version control integration"_s }, + { "CMAKE_ECLIPSE_RESOURCE_ENCODING"_s, + "Set the resource encoding in Eclipse CDT4 project generator."_s }, + { "CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION"_s, + "Error when cmake_install.cmake encounters absolute INSTALL DESTINATION."_s }, + { "CMAKE_EXECUTABLE_ENABLE_EXPORTS"_s, + "Specify whether executables export symbols for loadable modules"_s }, + { "CMAKE_EXECUTE_PROCESS_COMMAND_ECHO"_s, + "Print commands in execute_process() calls to STDERR, STDOUT, or NONE."_s }, + { "CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL"_s, + "Default for execute_process() COMMAND_ERROR_IS_FATAL option."_s }, + { "CMAKE_EXE_LINKER_FLAGS"_s, + "Linker flags to be used to create executables"_s }, + { "CMAKE_EXPORT_BUILD_DATABASE"_s, + "Experimental; gated by CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE. Enables build_database.json with C++ module compile commands."_s }, + { "CMAKE_EXPORT_COMPILE_COMMANDS"_s, + "Enable output of a compile_commands.json file listing the exact compiler invocation for each translation unit. Implemented by the Makefile and Ninja generators; ignored elsewhere."_s }, + { "CMAKE_EXPORT_NO_PACKAGE_REGISTRY"_s, + "Disable the export(PACKAGE) command from writing to the user package registry when CMP0090 is not set to NEW."_s }, + { "CMAKE_EXPORT_PACKAGE_REGISTRY"_s, + "Enable the export(PACKAGE) command to write to the user package registry when CMP0090 is set to NEW."_s }, + { "CMAKE_EXPORT_SARIF"_s, + "Enable CMake diagnostics output in SARIF format, written to .cmake/sarif/cmake.sarif by default or to path specified by cmake --sarif-output."_s }, + { "CMAKE_FIND_APPBUNDLE"_s, + "Control how find_*() commands select macOS Application Bundles."_s }, + { "CMAKE_FIND_DEBUG_MODE"_s, + "Print debug information for find_program, find_library, find_file, find_path, and find_package to stderr. Output is human-readable, not machine-parseable."_s }, + { "CMAKE_FIND_FRAMEWORK"_s, + "Control how find_*() commands select macOS Frameworks."_s }, + { "CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX"_s, + "Suffix to search in lib<suffix> directories before lib in find_library()."_s }, + { "CMAKE_FIND_NO_INSTALL_PREFIX"_s, + "Exclude CMAKE_INSTALL_PREFIX from CMAKE_SYSTEM_PREFIX_PATH."_s }, + { "CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY"_s, + "DEPRECATED: Use CMAKE_FIND_USE_PACKAGE_REGISTRY instead. When unset, find_package uses User Package Registry unless disabled. Ignored if CMAKE_FIND_USE_PACKAGE_REGISTRY is set."_s }, + { "CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY"_s, + "DEPRECATED: Use CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY instead. When unset, find_package uses System Package Registry unless disabled. Ignored if CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY is set."_s }, + { "CMAKE_FIND_PACKAGE_PREFER_CONFIG"_s, + "Prefer find_package() Config mode over Module mode."_s }, + { "CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS"_s, + "Resolve symbolic links in <PackageName>_DIR during find_package() lookups."_s }, + { "CMAKE_FIND_PACKAGE_TARGETS_GLOBAL"_s, + "Promote IMPORTED targets found by find_package() to GLOBAL scope."_s }, + { "CMAKE_FIND_PACKAGE_WARN_NO_MODULE"_s, + "Warn when find_package() implicitly assumes Config mode."_s }, + { "CMAKE_FIND_ROOT_PATH"_s, + "Semicolon-separated list of alternative filesystem roots searched by find_package(), find_library(), and the other find_*() commands. Most useful when cross-compiling."_s }, + { "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE"_s, + "Control whether CMAKE_FIND_ROOT_PATH is used by find_file/find_path"_s }, + { "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY"_s, + "Control whether CMAKE_FIND_ROOT_PATH is used by find_library"_s }, + { "CMAKE_FIND_ROOT_PATH_MODE_PACKAGE"_s, + "Control whether CMAKE_FIND_ROOT_PATH is used by find_package"_s }, + { "CMAKE_FIND_ROOT_PATH_MODE_PROGRAM"_s, + "Control whether CMAKE_FIND_ROOT_PATH is used by find_program"_s }, + { "CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH"_s, + "Controls whether find commands search cmake-specific environment variables"_s }, + { "CMAKE_FIND_USE_CMAKE_PATH"_s, + "Controls whether find commands search cmake-specific cache variables"_s }, + { "CMAKE_FIND_USE_CMAKE_SYSTEM_PATH"_s, + "Controls whether find commands search platform-specific cmake variables"_s }, + { "CMAKE_FIND_USE_INSTALL_PREFIX"_s, + "Controls whether find commands search CMAKE_INSTALL_PREFIX locations"_s }, + { "CMAKE_FIND_USE_PACKAGE_REGISTRY"_s, + "Controls whether find_package searches the User Package Registry; takes precedence over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY variable."_s }, + { "CMAKE_FIND_USE_PACKAGE_ROOT_PATH"_s, + "Controls whether find commands search <PackageName>_ROOT variables"_s }, + { "CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH"_s, + "Controls whether find commands search system environment variables"_s }, + { "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY"_s, + "Controls whether find_package searches the System Package Registry"_s }, + { "CMAKE_FOLDER"_s, + "Set the folder name to organize targets in an IDE"_s }, + { "CMAKE_FRAMEWORK"_s, + "Default value for FRAMEWORK target property"_s }, + { "CMAKE_FRAMEWORK_PATH"_s, + "Semicolon-separated list of directories for macOS framework search path"_s }, + { "CMAKE_Fortran_FORMAT"_s, + "Set to FIXED or FREE to indicate the Fortran source layout"_s }, + { "CMAKE_Fortran_MODULE_DIRECTORY"_s, + "Fortran module output directory"_s }, + { "CMAKE_Fortran_PREPROCESS"_s, + "Default value for Fortran_PREPROCESS target property"_s }, + { "CMAKE_GENERATOR_PLATFORM"_s, + "Generator-specific target-platform name (set via cmake -A, cmake-gui, or the CMAKE_GENERATOR_PLATFORM environment variable). Used by Visual Studio and Green Hills MULTI; ignored by other generators. Not to be modified by project code."_s }, + { "CMAKE_GENERATOR_TOOLSET"_s, + "Native-build-system toolset specification (set via cmake -T, cmake-gui, or the CMAKE_GENERATOR_TOOLSET environment variable). Used by Visual Studio, Xcode, and Green Hills MULTI; ignored by other generators. Not to be modified by project code."_s }, + { "CMAKE_GHS_NO_SOURCE_GROUP_FILE"_s, + "ON/OFF boolean to control if project file for target is single or multiple"_s }, + { "CMAKE_GLOBAL_AUTOGEN_TARGET"_s, + "Switch to enable generation of a global autogen target"_s }, + { "CMAKE_GLOBAL_AUTOGEN_TARGET_NAME"_s, + "Change the name of the global autogen target"_s }, + { "CMAKE_GLOBAL_AUTORCC_TARGET"_s, + "Switch to enable generation of a global autorcc target"_s }, + { "CMAKE_GLOBAL_AUTORCC_TARGET_NAME"_s, + "Change the name of the global autorcc target"_s }, + { "CMAKE_GNUtoMS"_s, + "Convert GNU import libraries (.dll.a) to MS format (.lib)"_s }, + { "CMAKE_HIP_ARCHITECTURES"_s, + "List of GPU architectures for HIP device code generation; interpreted per CMAKE_HIP_PLATFORM"_s }, + { "CMAKE_HIP_EXTENSIONS"_s, + "Default value for HIP_EXTENSIONS target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_HIP_PLATFORM"_s, + "GPU platform for HIP targets (amd, nvidia, or spirv)"_s }, + { "CMAKE_HIP_STANDARD"_s, + "Default value for HIP_STANDARD target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_HIP_STANDARD_REQUIRED"_s, + "Default value for HIP_STANDARD_REQUIRED target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_IGNORE_PATH"_s, + "Semicolon-separated list of directories to ignore in find commands"_s }, + { "CMAKE_IGNORE_PREFIX_PATH"_s, + "Semicolon-separated list of search prefixes to ignore in find commands"_s }, + { "CMAKE_INCLUDE_CURRENT_DIR"_s, + "Automatically add CMAKE_CURRENT_SOURCE_DIR and CMAKE_CURRENT_BINARY_DIR to include path. Does not propagate to subdirectories. Defaults to OFF."_s }, + { "CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE"_s, + "Automatically add CMAKE_CURRENT_SOURCE_DIR and CMAKE_CURRENT_BINARY_DIR to INTERFACE_INCLUDE_DIRECTORIES for libraries and executables. Defaults to OFF."_s }, + { "CMAKE_INCLUDE_DIRECTORIES_BEFORE"_s, + "When ON, include_directories prepends instead of appending directories"_s }, + { "CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"_s, + "Force project include directories to appear first in compiler command lines"_s }, + { "CMAKE_INCLUDE_PATH"_s, + "Semicolon-separated search path for find_file and find_path commands"_s }, + { "CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"_s, + "Default component name for install commands when COMPONENT argument is omitted"_s }, + { "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS"_s, + "Default permissions for directories created implicitly during installation"_s }, + { "CMAKE_INSTALL_EXPORTS_AS_PACKAGE_INFO"_s, + "Experimental feature (requires CMAKE_EXPERIMENTAL_FIXME gate): directives to install package information when exported target info is installed via install(EXPORT)."_s }, + { "CMAKE_INSTALL_MESSAGE"_s, + "Verbosity of installation messages: ALWAYS (default, shows Installing and Up-to-date), LAZY (only Installing), or NEVER (silent)."_s }, + { "CMAKE_INSTALL_NAME_DIR"_s, + "Directory name for installed targets on Apple platforms"_s }, + { "CMAKE_INSTALL_OBJECT_NAME_STRATEGY"_s, + "Strategy to use for naming installed object files"_s }, + { "CMAKE_INSTALL_OBJECT_ONLY_USE_DESTINATION"_s, + "Controls whether install DESTINATION for object libraries uses subdirectories"_s }, + { "CMAKE_INSTALL_PREFIX"_s, + "Install directory prepended to all install() destinations. Defaults to c:/Program Files/${PROJECT_NAME} on Windows and /usr/local on UNIX; may be overridden at install time via cmake --install --prefix."_s }, + { "CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, + "Boolean controlling whether toolchain-defined rpaths are removed during install"_s }, + { "CMAKE_INSTALL_RPATH"_s, + "Semicolon-separated list specifying rpath to use in installed targets"_s }, + { "CMAKE_INSTALL_RPATH_USE_LINK_PATH"_s, + "If TRUE, append linker search and installed library paths to installed rpath"_s }, + { "CMAKE_INTERMEDIATE_DIR_STRATEGY"_s, + "CMAKE_INTERMEDIATE_DIR_STRATEGY is a string cache variable specifying the strategy to use for target intermediate directories and their contents. The supported values are:"_s }, + { "CMAKE_INTERPROCEDURAL_OPTIMIZATION"_s, + "Default value for INTERPROCEDURAL_OPTIMIZATION of targets"_s }, + { "CMAKE_IOS_INSTALL_COMBINED"_s, + "This is deprecated. Default value for IOS_INSTALL_COMBINED of targets. This variable is used to initialize the IOS_INSTALL_COMBINED property on all the targets."_s }, + { "CMAKE_ISPC_HEADER_DIRECTORY"_s, + "Default output directory for ISPC-generated headers"_s }, + { "CMAKE_ISPC_HEADER_SUFFIX"_s, + "Output suffix for ISPC-generated headers"_s }, + { "CMAKE_ISPC_INSTRUCTION_SETS"_s, + "Default instruction sets for ISPC compilation"_s }, + { "CMAKE_KATE_FILES_MODE"_s, + "Kate project generator file mode (AUTO, SVN, GIT, HG, FOSSIL, or LIST)"_s }, + { "CMAKE_KATE_MAKE_ARGUMENTS"_s, + "Arguments passed to make tool when Kate invokes build"_s }, + { "CMAKE_LIBRARY_OUTPUT_DIRECTORY"_s, + "Where to put all LIBRARY target files when built"_s }, + { "CMAKE_LIBRARY_PATH"_s, + "Semicolon-separated search path for find_library command"_s }, + { "CMAKE_LINKER_TYPE"_s, + "Specify which linker to use for link steps; initialized on targets and used by try_compile()."_s }, + { "CMAKE_LINK_DEPENDS_NO_SHARED"_s, + "Whether to skip link dependencies on shared library files"_s }, + { "CMAKE_LINK_DEPENDS_USE_LINKER"_s, + "For Makefile/Ninja generators, whether linker generates link dependencies"_s }, + { "CMAKE_LINK_DIRECTORIES_BEFORE"_s, + "Prepend directories by default in link_directories instead of appending"_s }, + { "CMAKE_LINK_INTERFACE_LIBRARIES"_s, + "Default value for LINK_INTERFACE_LIBRARIES of targets"_s }, + { "CMAKE_LINK_LIBRARIES_ONLY_TARGETS"_s, + "Enable check that all target-like items in target_link_libraries() are existing targets."_s }, + { "CMAKE_LINK_LIBRARIES_STRATEGY"_s, + "Specify a strategy for ordering targets' link dependencies on linker command lines."_s }, + { "CMAKE_LINK_SEARCH_END_STATIC"_s, + "End link line with static system libraries; use -Bstatic with compatible linkers."_s }, + { "CMAKE_LINK_SEARCH_START_STATIC"_s, + "Assume linker looks for static libraries by default; use -Bstatic with compatible linkers."_s }, + { "CMAKE_LINK_WARNING_AS_ERROR"_s, + "Specify whether to treat warnings on link as errors. This variable is used to initialize the LINK_WARNING_AS_ERROR property on all the targets."_s }, + { "CMAKE_LINK_WHAT_YOU_USE"_s, + "Default value for LINK_WHAT_YOU_USE target property"_s }, + { "CMAKE_MACOSX_BUNDLE"_s, + "Default value for MACOSX_BUNDLE target property"_s }, + { "CMAKE_MACOSX_RPATH"_s, + "Whether to use rpaths on macOS and iOS"_s }, + { "CMAKE_MAKE_PROGRAM"_s, + "Tool that can launch the native build system. The value may be the full path to an executable or just the tool name if it is expected to be in the PATH. The tool selected depends on the CMAKE_GENERATOR used to configure the project:"_s }, + { "CMAKE_MAXIMUM_RECURSION_DEPTH"_s, + "Maximum recursion depth for CMake scripts"_s }, + { "CMAKE_MESSAGE_CONTEXT"_s, + "List of context names prepended to message output when logging context enabled"_s }, + { "CMAKE_MESSAGE_CONTEXT_SHOW"_s, + "Setting this variable to true enables showing a context with each line logged by message command (see CMAKE_MESSAGE_CONTEXT for how context is specified), as an alternative to cmake --log-context option."_s }, + { "CMAKE_MESSAGE_INDENT"_s, + "Indentation strings prepended to message command output for NOTICE and below"_s }, + { "CMAKE_MESSAGE_LOG_LEVEL"_s, + "When set, specifies the logging level used by message command with values same as cmake(1) --log-level option; command line option takes precedence if both are set."_s }, + { "CMAKE_MFC_FLAG"_s, + "Use the MFC library for an executable or dll; enables Microsoft Foundation Classes (MFC); set to 1 for static library, 2 for shared; used in Visual Studio project files."_s }, + { "CMAKE_MODULE_LINKER_FLAGS"_s, + "Linker flags to be used to create modules"_s }, + { "CMAKE_MODULE_PATH"_s, + "Semicolon-separated list of directories searched for CMake modules by include() and find_package() before the modules shipped with CMake. Empty by default."_s }, + { "CMAKE_MSVCIDE_RUN_PATH"_s, + "Extra PATH locations for executing add_custom_command or add_custom_target with Visual Studio Generators; allows running commands and dlls the IDE doesn't know about; initialized from CMAKE_MSVCIDE_RUN_PATH environment variable if set."_s }, + { "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s, + "Select the MSVC debug information format targeting the MSVC ABI"_s }, + { "CMAKE_MSVC_RUNTIME_CHECKS"_s, + "Select the list of enabled runtime checks when targeting MSVC ABI"_s }, + { "CMAKE_MSVC_RUNTIME_LIBRARY"_s, + "Select the MSVC runtime library for use by compilers targeting MSVC ABI"_s }, + { "CMAKE_NINJA_OUTPUT_PATH_PREFIX"_s, + "Tell Ninja Generators to add a prefix to every output path"_s }, + { "CMAKE_NO_BUILTIN_CHRPATH"_s, + "Do not use builtin binary editor to fix runtime library search paths"_s }, + { "CMAKE_NO_SYSTEM_FROM_IMPORTED"_s, + "Default value for NO_SYSTEM_FROM_IMPORTED target property"_s }, + { "CMAKE_OBJCXX_EXTENSIONS"_s, + "Default value for OBJCXX_EXTENSIONS target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_OBJCXX_STANDARD"_s, + "Default value for OBJCXX_STANDARD target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_OBJCXX_STANDARD_REQUIRED"_s, + "Default value for OBJCXX_STANDARD_REQUIRED target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_OBJC_EXTENSIONS"_s, + "Default value for OBJC_EXTENSIONS target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_OBJC_STANDARD"_s, + "Default value for OBJC_STANDARD target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_OBJC_STANDARD_REQUIRED"_s, + "Default value for OBJC_STANDARD_REQUIRED target property if set when a target is created. See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers."_s }, + { "CMAKE_OBJECT_PATH_MAX"_s, + "Maximum object file full-path length allowed by native build tools; CMake hashes long paths to keep them under this limit."_s }, + { "CMAKE_OPTIMIZE_DEPENDENCIES"_s, + "Initializes the OPTIMIZE_DEPENDENCIES target property"_s }, + { "CMAKE_OSX_ARCHITECTURES"_s, + "Target architectures for macOS and iOS builds (e.g. x86_64;arm64). Initializes the OSX_ARCHITECTURES target property; if empty, the compiler's default architecture is used."_s }, + { "CMAKE_OSX_DEPLOYMENT_TARGET"_s, + "Minimum macOS or iOS version on which the built binaries are to be deployed. Initialized from the MACOSX_DEPLOYMENT_TARGET environment variable when not set explicitly."_s }, + { "CMAKE_OSX_SYSROOT"_s, + "Location or short name (e.g. macosx) of the macOS platform SDK passed to the compiler via -isysroot. Initialized from the SDKROOT environment variable; empty by default."_s }, + { "CMAKE_PCH_INSTANTIATE_TEMPLATES"_s, + "Initializes the PCH_INSTANTIATE_TEMPLATES target property"_s }, + { "CMAKE_PCH_WARN_INVALID"_s, + "Initializes the PCH_WARN_INVALID target property"_s }, + { "CMAKE_PDB_OUTPUT_DIRECTORY"_s, + "Output directory for MS debug symbol .pdb files generated by linker"_s }, + { "CMAKE_PKG_CONFIG_DISABLE_UNINSTALLED"_s, + "Disable uninstalled package file priority in pkg-config searches"_s }, + { "CMAKE_PKG_CONFIG_PC_LIB_DIRS"_s, + "Primary search paths for pkg-config package files"_s }, + { "CMAKE_PKG_CONFIG_PC_PATH"_s, + "Supplementary search paths for pkg-config package files"_s }, + { "CMAKE_PKG_CONFIG_SYSROOT_DIR"_s, + "Path to prepend to -I include and -L library directories extracted by cmake_pkg_config command."_s }, + { "CMAKE_PKG_CONFIG_TOP_BUILD_DIR"_s, + "Path to derive pc_top_builddir package variable by cmake_pkg_config command."_s }, + { "CMAKE_PLATFORM_NO_VERSIONED_SONAME"_s, + "Disable version information on shared library targets on applicable platforms"_s }, + { "CMAKE_POLICY_VERSION_MINIMUM"_s, + "Set minimum Policy Version for a project; for configuring third-party projects without modifying their code."_s }, + { "CMAKE_POSITION_INDEPENDENT_CODE"_s, + "Default value used to initialize the POSITION_INDEPENDENT_CODE target property on targets that are not SHARED or MODULE libraries. SHARED and MODULE libraries always have PIC enabled."_s }, + { "CMAKE_PREFIX_PATH"_s, + "Semicolon-separated list of installation prefixes searched by find_package(), find_program(), find_library(), find_file(), and find_path(). Empty by default; intended to be set by the project or the user."_s }, + { "CMAKE_PROGRAM_PATH"_s, + "Semicolon-separated list of directories to search for programs with find_program command."_s }, + { "CMAKE_PROJECT_INCLUDE"_s, + "CMake language file(s) to include as last step of project command calls for code injection without source modification."_s }, + { "CMAKE_PROJECT_INCLUDE_BEFORE"_s, + "CMake language file(s) to include as first step of project command calls for code injection without source modification."_s }, + { "CMAKE_PROJECT_TOP_LEVEL_INCLUDES"_s, + "Semicolon-separated list of CMake language files to include as part of the very first project() call."_s }, + { "CMAKE_RUNTIME_OUTPUT_DIRECTORY"_s, + "Where to put all RUNTIME target files when built"_s }, + { "CMAKE_Rust_EDITION"_s, + "Default Rust edition for Rust targets when created"_s }, + { "CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS"_s, + "Specify whether shared library generates an import file"_s }, + { "CMAKE_SHARED_LINKER_FLAGS"_s, + "Linker flags to be used to create shared libraries"_s }, + { "CMAKE_SKIP_BUILD_RPATH"_s, + "Do not include RPATHs in the build tree"_s }, + { "CMAKE_SKIP_INSTALL_ALL_DEPENDENCY"_s, + "If true, install target does not depend on all target; installation proceeds independently of build status."_s }, + { "CMAKE_SKIP_INSTALL_RPATH"_s, + "Do not include RPATHs in the install tree"_s }, + { "CMAKE_SKIP_LINTING"_s, + "Default value for the SKIP_LINTING target property"_s }, + { "CMAKE_SKIP_TEST_ALL_DEPENDENCY"_s, + "If true, test target does not depend on all target; test execution proceeds independently of build status."_s }, + { "CMAKE_STAGING_PREFIX"_s, + "Install path to use when cross-compiling; also used as a search prefix by find_*() commands."_s }, + { "CMAKE_STATIC_LINKER_FLAGS"_s, + "Flags to be used to create static libraries"_s }, + { "CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS"_s, + "Semicolon-separated list of environment variables (var=value format) for Sublime Text 2 project generation."_s }, + { "CMAKE_SUBLIME_TEXT_2_EXCLUDE_BUILD_TREE"_s, + "If true, exclude build tree from Sublime Text 2 .sublime-project file if build tree is inside source tree."_s }, + { "CMAKE_SUPPRESS_REGENERATION"_s, + "If true, CMake does not add special regeneration target or perform build system checks for source changes."_s }, + { "CMAKE_SYSROOT"_s, + "Path passed to the compiler via --sysroot (when supported) and used as a prefix for find_*() searches. May only be set in a toolchain file (CMAKE_TOOLCHAIN_FILE)."_s }, + { "CMAKE_SYSTEM_NAME"_s, + "Name of the operating system being targeted (e.g. Linux, Darwin, Windows). Defaults to CMAKE_HOST_SYSTEM_NAME; set explicitly when first configuring a build tree to enable cross-compiling."_s }, + { "CMAKE_SYSTEM_PROCESSOR"_s, + "Target system processor; same as CMAKE_HOST_SYSTEM_PROCESSOR for non-cross-compiling, set explicitly in toolchain files."_s }, + { "CMAKE_SYSTEM_VERSION"_s, + "Version of the target operating system. Defaults to CMAKE_HOST_SYSTEM_VERSION for host builds; must be set explicitly alongside CMAKE_SYSTEM_NAME when cross-compiling."_s }, + { "CMAKE_Swift_LANGUAGE_VERSION"_s, + "Swift language version (defaults based on Xcode version if not set)"_s }, + { "CMAKE_TLS_CAINFO"_s, + "Path to TLS CA certificate file for file(DOWNLOAD), file(UPLOAD), ExternalProject, and FetchContent"_s }, + { "CMAKE_TLS_VERIFY"_s, + "Enable TLS verification for file(DOWNLOAD), file(UPLOAD), ExternalProject, and FetchContent"_s }, + { "CMAKE_TLS_VERSION"_s, + "Minimum TLS version for file(DOWNLOAD), file(UPLOAD), ExternalProject, and FetchContent (see manual)"_s }, + { "CMAKE_TOOLCHAIN_FILE"_s, + "Path to a CMake toolchain file, read early in the CMake run, that specifies the compilers, toolchain utilities, and target-platform information used when cross-compiling."_s }, + { "CMAKE_TRY_COMPILE_CONFIGURATION"_s, + "Build configuration used for try_compile and try_run projects"_s }, + { "CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES"_s, + "Set to a true value to tell the try_compile command not to propagate any platform variables into the test project. The try_compile command normally passes some CMake variables that configure the platform and toolchain behavior into test projects. See policy CMP0137."_s }, + { "CMAKE_TRY_COMPILE_PLATFORM_VARIABLES"_s, + "List of variables that the try_compile command source file signature must propagate into the test project in order to target the same platform as the host project. This variable should not be set by project code. It is meant to be set by CMake's platform information modules or by a toolchain file."_s }, + { "CMAKE_TRY_COMPILE_TARGET_TYPE"_s, + "Type of target generated for try_compile calls using the source file signature. Valid values are EXECUTABLE (use add_executable, default) or STATIC_LIBRARY (use add_library with STATIC option to avoid linking)."_s }, + { "CMAKE_UNITY_BUILD"_s, + "Enable batch compilation of multiple sources within targets"_s }, + { "CMAKE_UNITY_BUILD_BATCH_SIZE"_s, + "Specifies default upper limit on number of source files combined in one unity source file"_s }, + { "CMAKE_UNITY_BUILD_RELOCATABLE"_s, + "Enable relative paths in generated unity build source files"_s }, + { "CMAKE_UNITY_BUILD_UNIQUE_ID"_s, + "Specify unique identifier name generated per file in unity build"_s }, + { "CMAKE_USER_MAKE_RULES_OVERRIDE"_s, + "Specify a CMake file that overrides platform information. CMake loads the specified file while enabling support for each language from either the project or enable_language commands. It is loaded after CMake's builtin compiler and platform information modules have been loaded but before the information is used."_s }, + { "CMAKE_VERBOSE_MAKEFILE"_s, + "Enable verbose output from Makefile builds, showing each command line as it is launched. Initialized to FALSE by the project() command; users may override per build tree."_s }, + { "CMAKE_VERIFY_INTERFACE_HEADER_SETS"_s, + "Enable interface header set verification for targets"_s }, + { "CMAKE_VERIFY_PRIVATE_HEADER_SETS"_s, + "Enable private header set verification for targets"_s }, + { "CMAKE_VISIBILITY_INLINES_HIDDEN"_s, + "Default value for VISIBILITY_INLINES_HIDDEN target property"_s }, + { "CMAKE_VS_DEBUGGER_COMMAND"_s, + "Initialize debugger command path for Visual Studio targets"_s }, + { "CMAKE_VS_DEBUGGER_COMMAND_ARGUMENTS"_s, + "Initialize debugger command arguments for Visual Studio targets"_s }, + { "CMAKE_VS_DEBUGGER_ENVIRONMENT"_s, + "Initialize debugger environment variables for Visual Studio targets"_s }, + { "CMAKE_VS_DEBUGGER_WORKING_DIRECTORY"_s, + "Initialize debugger working directory for Visual Studio targets"_s }, + { "CMAKE_VS_GLOBALS"_s, + "Set custom global Visual Studio properties on all targets"_s }, + { "CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD"_s, + "Include INSTALL target in default Visual Studio build"_s }, + { "CMAKE_VS_INCLUDE_PACKAGE_TO_DEFAULT_BUILD"_s, + "Include PACKAGE target in default Visual Studio build"_s }, + { "CMAKE_VS_JUST_MY_CODE_DEBUGGING"_s, + "Enable Just My Code debugging with Visual Studio"_s }, + { "CMAKE_VS_NO_COMPILE_BATCHING"_s, + "Disable compile batching for Visual Studio generator"_s }, + { "CMAKE_VS_NUGET_PACKAGE_RESTORE"_s, + "Controls whether msbuild automatically restores NuGet packages before a build (Visual Studio Generators)"_s }, + { "CMAKE_VS_SDK_EXCLUDE_DIRECTORIES"_s, + "Override Visual Studio default Exclude Directories"_s }, + { "CMAKE_VS_SDK_EXECUTABLE_DIRECTORIES"_s, + "Override Visual Studio default Executable Directories"_s }, + { "CMAKE_VS_SDK_INCLUDE_DIRECTORIES"_s, + "Override Visual Studio default Include Directories"_s }, + { "CMAKE_VS_SDK_LIBRARY_DIRECTORIES"_s, + "Override Visual Studio default Library Directories"_s }, + { "CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES"_s, + "Override Visual Studio default Library WinRT Directories"_s }, + { "CMAKE_VS_SDK_REFERENCE_DIRECTORIES"_s, + "Override Visual Studio default Reference Directories"_s }, + { "CMAKE_VS_SDK_SOURCE_DIRECTORIES"_s, + "Override Visual Studio default Source Directories"_s }, + { "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION"_s, + "Visual Studio Windows Target Platform Version. CMake selects the Windows SDK version when targeting Windows 10 and above on VS 2015+."_s }, + { "CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION"_s, + "Ask cmake_install.cmake script to warn when encountering files with absolute INSTALL DESTINATION"_s }, + { "CMAKE_WATCOM_RUNTIME_LIBRARY"_s, + "Initialize WATCOM_RUNTIME_LIBRARY target property for all targets"_s }, + { "CMAKE_WIN32_EXECUTABLE"_s, + "Default value for WIN32_EXECUTABLE target property"_s }, + { "CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS"_s, + "Default value for WINDOWS_EXPORT_ALL_SYMBOLS target property"_s }, + { "CMAKE_XCODE_GENERATE_SCHEME"_s, + "Enable generation of scheme files by the Xcode generator for analyze, archive, and test actions"_s }, + { "CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY"_s, + "If enabled, Xcode generator produces a single project file instead of one per project() invocation"_s }, + { "CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER"_s, + "Enable Address Sanitizer in the Diagnostics section of the generated Xcode scheme"_s }, + { "CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN"_s, + "Enable detection of stack use-after-return in the Diagnostics section of the generated Xcode scheme"_s }, + { "CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING"_s, + "Enable debugging when using document Versions Browser in the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"_s, + "Disable the Main Thread Checker in the Diagnostics section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS"_s, + "Enable Dynamic Library Loads in the Diagnostics section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"_s, + "Enable Dynamic Linker API usage in the Diagnostics section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION"_s, + "Populate Metal: API Validation in the Options section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE"_s, + "Set GPU Frame Capture to Metal or Disabled in the Options section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION"_s, + "Populate Metal: Shader Validation in the Options section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_ENVIRONMENT"_s, + "Specify environment variables as MYVAR=value list to add to the Arguments section of the Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_GUARD_MALLOC"_s, + "Enable Guard Malloc in the Diagnostics section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION"_s, + "Set the build configuration to run the target in the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_LAUNCH_MODE"_s, + "Populate Launch in the Info section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_LLDB_INIT_FILE"_s, + "Populate LLDB Init File in the Info section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"_s, + "Enable Main Thread Checker pause-on-issues in the Diagnostics section of the Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES"_s, + "Enable Malloc Guard Edges in the Diagnostics section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE"_s, + "Enable Malloc Scribble in the Diagnostics section of the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_MALLOC_STACK"_s, + "Enable Malloc Stack diagnostics in the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_TEST_CONFIGURATION"_s, + "Build configuration for testing targets in the Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_THREAD_SANITIZER"_s, + "Enable Thread Sanitizer diagnostics in the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_THREAD_SANITIZER_STOP"_s, + "Enable Thread Sanitizer pause-on-issues in the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"_s, + "Enable Undefined Behavior Sanitizer in the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP"_s, + "Enable Undefined Behavior Sanitizer pause-on-issues in the generated Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_WORKING_DIRECTORY"_s, + "Working directory for Run and Profile actions in the Xcode scheme."_s }, + { "CMAKE_XCODE_SCHEME_ZOMBIE_OBJECTS"_s, + "Enable Zombie Objects diagnostics in the generated Xcode scheme."_s }, + { "CMAKE_XCODE_XCCONFIG"_s, + "If set, the Xcode generator will register the specified file as a global XCConfig file. For target-level XCConfig files see the XCODE_XCCONFIG target property."_s }, +}; +/* clang-format on */ + +} // namespace + +cmCacheDocumentationTable::LookupResult cmCacheDocumentationTable::Get( + cm::string_view varName) +{ + auto const* const first = std::begin(kEntries); + auto const* const last = std::end(kEntries); + auto const* const it = std::lower_bound( + first, last, varName, [](Entry const& entry, cm::string_view target) { + return entry.Name < target; + }); + if (it != last && it->Name == varName) { + return { it->Summary }; + } + // Exact-match miss: fall back to the pattern table (CMAKE_<LANG>_FLAGS, + // CMAKE_POLICY_DEFAULT_CMP<NNNN>, etc.). Exact-match always wins by + // construction; only on a miss do we consult the (small, linearly + // scanned) pattern table. See Source/cmCachePatternTable.{h,cxx} and + // the testPatternMatch* cases in Tests/CMakeLib for the contract. + return cmCachePatternTable::Match(varName); +} + +cmCacheDocumentationTable::Entry const* +cmCacheDocumentationTable::EntriesBegin() +{ + return std::begin(kEntries); +} + +cmCacheDocumentationTable::Entry const* cmCacheDocumentationTable::EntriesEnd() +{ + return std::end(kEntries); +} + +std::size_t cmCacheDocumentationTable::EntriesSize() +{ + return static_cast<std::size_t>(std::end(kEntries) - std::begin(kEntries)); +}
diff --git a/Source/cmCacheDocumentationTable.h b/Source/cmCacheDocumentationTable.h new file mode 100644 index 0000000..a0ba5bd --- /dev/null +++ b/Source/cmCacheDocumentationTable.h
@@ -0,0 +1,67 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <cstddef> + +#include <cm/string_view> + +/** \namespace cmCacheDocumentationTable + * \brief Compile-time lookup for built-in CMake cache variable documentation. + * + * The table is a hand-maintained list of short, single-line plain-text + * summaries keyed by the canonical CMake variable name (for example + * ``CMAKE_BUILD_TYPE``). See the header comment in + * ``cmCacheDocumentationTable.cxx`` for the scope of variables included + * and the maintenance discipline. + * + * The sole consumer is ``cmake::ProcessCacheArg``, which reads + * ``Summary`` to populate the ``HELPSTRING`` of cache entries created + * via ``-D <var>=<value>`` when the user did not supply one. + */ +namespace cmCacheDocumentationTable { + +/** \brief A single row in the built-in documentation table. + * + * The ``Name`` and ``Summary`` views reference storage with static + * lifetime. + */ +struct Entry +{ + cm::string_view Name; + cm::string_view Summary; +}; + +/** \brief Result of a single ``Get`` lookup. + * + * An unknown key yields an empty ``Summary``. + */ +struct LookupResult +{ + cm::string_view Summary; +}; + +/** \brief Look up the built-in documentation for \a varName. + * + * Returns a ``LookupResult`` whose ``Summary`` references storage with + * static lifetime when \a varName corresponds to a documented CMake + * variable. Otherwise returns an empty ``Summary``. The lookup runs + * in ``O(log N)`` and performs no allocation. + */ +LookupResult Get(cm::string_view varName); + +/** \brief Iterate the underlying table. + * + * These are exposed primarily for the ``testCacheDocumentationTable`` + * regression test in ``Tests/CMakeLib``, which verifies that the entries + * are sorted (a precondition of ``Get``'s ``std::lower_bound`` lookup), + * unique, and otherwise well-formed. Callers should not rely on a + * particular entry count or ordering beyond the sort invariant. + */ +Entry const* EntriesBegin(); +Entry const* EntriesEnd(); +std::size_t EntriesSize(); + +}
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index 23d6751..276e187 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx
@@ -410,9 +410,6 @@ std::string const& helpString) { std::string::size_type end = helpString.size(); - if (end == 0) { - return; - } std::string oneLine; std::string::size_type pos = 0; for (std::string::size_type i = 1; i <= end; i++) { @@ -437,7 +434,7 @@ std::string::size_type end = message.size(); std::string oneLine; std::string::size_type pos = 0; - for (std::string::size_type i = 0; i <= end; i++) { + for (std::string::size_type i = 1; i <= end; i++) { if ((i == end) || (message[i] == '\n') || ((i - pos >= 60) && (message[i] == ' ') && wrapSpaces)) { fout << "# ";
diff --git a/Source/cmCachePatternTable.cxx b/Source/cmCachePatternTable.cxx new file mode 100644 index 0000000..201ec32 --- /dev/null +++ b/Source/cmCachePatternTable.cxx
@@ -0,0 +1,536 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +// Built-in cache variable documentation pattern table. +// +// This file is hand-maintained. Each entry's Pattern is a literal template +// string containing one or more angle-bracketed placeholders (``<LANG>``, +// ``<CONFIG>``, ``<PackageName>``, ``<PROJECT-NAME>``, ``<an-attribute>``, +// ``<NNNN>``, ``<n>``). The Summary is a one-line tooltip installed as +// the ``HELPSTRING`` of cache variables created via +// ``cmake -D <var>=<value>`` whose name matches the template under the +// lexical classes defined below and for which no exact-match entry exists +// in ``cmCacheDocumentationTable``. +// +// The pattern table is consulted only on the miss path of the exact-match +// ``cmCacheDocumentationTable::Get`` lookup; exact entries always win. +// +// Maintenance discipline: +// * Adding a new pattern: hand-write one entry below in the canonical +// ordering ``(numPlaceholders ASC, totalLiteralLen DESC, Pattern ASC)`` +// and make sure that, with ``<`` and ``>`` stripped, the pattern +// names an existing ``Help/variable/<NAME>.rst`` file. The +// ``testPatternTableOrdered`` regression test in +// ``Tests/CMakeLib`` verifies the canonical ordering; the +// ``CacheVarHelpCoverage`` lint verifies the structural-parity +// invariant. +// * Per-language or per-configuration restrictions documented in the +// corresponding ``.rst`` file must be reproduced in the Summary +// prose (the matcher is intentionally permissive; restrictions live +// in the tooltip). +// * Summaries must be single-line printable ASCII, no embedded +// double quotes, no embedded newlines, and ``<= 200`` characters +// (so that ``cmake-gui`` / ``ccmake`` render them legibly). The +// ``testPatternTableFieldsValid`` regression test enforces all of +// these. +// +// Matcher semantics: lazy + lookahead-anchored, single pass, no +// backtracking. For each placeholder, the matcher consumes the minimum +// class-valid prefix of the input that lets the next literal anchor +// match at the cursor. The fixed-length classes ``<NNNN>`` and +// ``<n>`` consume exactly four or one ASCII digits respectively. +// Full-input consumption is required at the end. First fully-matching +// entry in ``kPatterns[]`` wins. + +#include "cmCachePatternTable.h" + +#include <cstddef> +#include <iterator> +#include <string> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmCacheDocumentationTable.h" + +namespace { + +using cmCachePatternTable::Entry; + +// The Entry layout below (two lines per row, ``{ "PATTERN",\n "summary" +// },``) is intentional: it keeps the table easy to diff, review, and hand-edit +// against the corresponding Help/variable/*.rst manuals. clang-format is +// suppressed across the array so contributors can update one Summary +// without the formatter reflowing every neighbor. +// +// Section headers ``// === N-placeholder, ... (K-literal-char bucket) ===`` +// are organizational signposts that mirror the canonical ordering invariant +// enforced by ``testPatternTableOrdered``: +// +// (numPlaceholders ASC, totalLiteralLen DESC, Pattern ASC) +// +// where ``totalLiteralLen`` is the byte count of the pattern after every +// ``<...>`` placeholder has been stripped. Examples: +// +// ``CMAKE_<LANG>_COMPILER_LAUNCHER`` -> ``CMAKE__COMPILER_LAUNCHER`` (24) +// ``CMAKE_<LANG>_CLANG_TIDY`` -> ``CMAKE__CLANG_TIDY`` (17) +// ``CMAKE_<LANG>_FLAGS`` -> ``CMAKE__FLAGS`` (12) +// +// Ordering literals DESC within a placeholder-count band is what makes +// ``Match()``'s first-match-wins linear scan correct: a more-anchored +// pattern (e.g. ``CMAKE_<LANG>_FLAGS_<CONFIG>_INIT``, literal=18) must +// shadow a less-anchored one (``CMAKE_<LANG>_FLAGS_<CONFIG>``, literal=13) +// so a lookup of ``CMAKE_CXX_FLAGS_DEBUG_INIT`` resolves to the former +// rather than lazily binding ``CONFIG`` to ``DEBUG_INIT`` against the +// latter. Contributors adding a new entry: place it in the bucket whose +// literal-count matches yours, and update or add a new ``=== ... ===`` +// header if your literal-count is novel. The unit test catches mistakes. +/* clang-format off */ +Entry const kPatterns[] = { + + // === 1-placeholder, (37-literal-char bucket) === + { "CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>"_s, + "Default framework filename postfix under configuration <CONFIG>."_s }, + + // === 1-placeholder, (35-literal-char bucket) === + { "CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_<CONFIG>"_s, + "Initializes COMPILE_PDB_OUTPUT_DIRECTORY_<CONFIG> target property."_s }, + { "CMAKE_INTERPROCEDURAL_OPTIMIZATION_<CONFIG>"_s, + "Initializes INTERPROCEDURAL_OPTIMIZATION_<CONFIG> target property."_s }, + { "CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED"_s, + "Set to TRUE if <FEATURE> is supported regardless of linker language."_s }, + + // === 1-placeholder, (34-literal-char bucket) === + { "CMAKE_<LANG>_CLANG_TIDY_EXPORT_FIXES_DIR"_s, + "Default value for the <LANG>_CLANG_TIDY_EXPORT_FIXES_DIR target property; directory where clang-tidy writes suggested fix .yaml files. Applies only when <LANG> is C, CXX, OBJC, or OBJCXX."_s }, + + // === 1-placeholder, (33-literal-char bucket) === + { "CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED"_s, + "Specifies if <FEATURE> is supported regardless of link language."_s }, + + // === 1-placeholder, (31-literal-char bucket) === + { "CMAKE_ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>"_s, + "Initializes ARCHIVE_OUTPUT_DIRECTORY_<CONFIG> target property."_s }, + { "CMAKE_LIBRARY_OUTPUT_DIRECTORY_<CONFIG>"_s, + "Initializes LIBRARY_OUTPUT_DIRECTORY_<CONFIG> target property."_s }, + { "CMAKE_RUNTIME_OUTPUT_DIRECTORY_<CONFIG>"_s, + "Initializes RUNTIME_OUTPUT_DIRECTORY_<CONFIG> target property."_s }, + { "CMAKE_USER_MAKE_RULES_OVERRIDE_<LANG>"_s, + "Specify a CMake file that overrides platform information for <LANG>"_s }, + + // === 1-placeholder, (30-literal-char bucket) === + { "CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES"_s, + "Defines behavior and attributes of the specified link library <FEATURE>."_s }, + + // === 1-placeholder, (29-literal-char bucket) === + { "CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE"_s, + "Path to a CMake language file included as the first step of any project() command call matching <PROJECT-NAME>. Intended for injecting custom code into project builds without modifying the source."_s }, + + // === 1-placeholder, (27-literal-char bucket) === + { "CMAKE_<LANG>_INCLUDE_WHAT_YOU_USE"_s, + "Default value for the <LANG>_INCLUDE_WHAT_YOU_USE target property; names an include-what-you-use command (and arguments) run alongside compilation. Applies only when <LANG> is C or CXX."_s }, + { "CMAKE_DISABLE_FIND_PACKAGE_<PackageName>"_s, + "Set to TRUE to disable calls to find_package(<PackageName>); the call returns as if the package were not found, even when REQUIRED was specified."_s }, + { "CMAKE_PDB_OUTPUT_DIRECTORY_<CONFIG>"_s, + "Initializes PDB_OUTPUT_DIRECTORY_<CONFIG> target property."_s }, + { "CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>"_s, + "Set to TRUE to make every call to find_package(<PackageName>) behave as if REQUIRED were specified, causing CMake to fail when the package is not found."_s }, + + // === 1-placeholder, (26-literal-char bucket) === + { "CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>"_s, + "Initializes MAP_IMPORTED_CONFIG_<CONFIG> target property."_s }, + { "CMAKE_MODULE_LINKER_FLAGS_<CONFIG>"_s, + "Flags used by the linker when creating a module library for the <CONFIG> configuration. Appended after the configuration-independent CMAKE_MODULE_LINKER_FLAGS."_s }, + { "CMAKE_SHARED_LINKER_FLAGS_<CONFIG>"_s, + "Flags used by the linker when creating a shared library for the <CONFIG> configuration. Appended after the configuration-independent CMAKE_SHARED_LINKER_FLAGS."_s }, + { "CMAKE_STATIC_LINKER_FLAGS_<CONFIG>"_s, + "Flags used by the static-library archiver when creating a static library for the <CONFIG> configuration. Appended after the configuration-independent CMAKE_STATIC_LINKER_FLAGS."_s }, + + // === 1-placeholder, (25-literal-char bucket) === + { "CMAKE_LINK_LIBRARY_USING_<FEATURE>"_s, + "Defines how to link a library for <FEATURE>."_s }, + + // === 1-placeholder, (24-literal-char bucket) === + { "CMAKE_<LANG>_COMPILER_LAUNCHER"_s, + "Default value for the <LANG>_COMPILER_LAUNCHER target property; launcher tool prepended to every <LANG> compile command. Applies only when <LANG> is C, CXX, Fortran, HIP, ISPC, OBJC, OBJCXX, or CUDA."_s }, + { "CMAKE_<LANG>_STANDARD_REQUIRED"_s, + "Default value for the <LANG>_STANDARD_REQUIRED target property when a target is created. If TRUE, the requested language standard must be supported by the compiler."_s }, + { "CMAKE_<LANG>_VISIBILITY_PRESET"_s, + "Default value for the <LANG>_VISIBILITY_PRESET target property when a target is created. Controls the default symbol visibility (default, hidden, protected, internal) for <LANG> code."_s }, + { "CMAKE_POLICY_DEFAULT_CMP<NNNN>"_s, + "Default for CMake Policy CMP<NNNN> when otherwise left unset. Set to OLD or NEW to externally control the policy for projects that have not set it via cmake_minimum_required or cmake_policy."_s }, + { "CMAKE_POLICY_WARNING_CMP<NNNN>"_s, + "Explicitly enable or disable the warning when CMake Policy CMP<NNNN> has not been set explicitly. Meaningful only for the policies that do not warn by default."_s }, + + // === 1-placeholder, (23-literal-char bucket) === + { "CMAKE_EXE_LINKER_FLAGS_<CONFIG>"_s, + "Flags used by the linker when creating an executable for the <CONFIG> configuration. Appended after the configuration-independent CMAKE_EXE_LINKER_FLAGS."_s }, + { "CMAKE_LINK_GROUP_USING_<FEATURE>"_s, + "Defines how to link a group of libraries for <FEATURE>."_s }, + + // === 1-placeholder, (22-literal-char bucket) === + { "CMAKE_<LANG>_LINKER_LAUNCHER"_s, + "Default value for the <LANG>_LINKER_LAUNCHER target property; names a launcher tool prepended to every <LANG> link command. Applies only when <LANG> is C, CXX, OBJC, OBJCXX, or CUDA."_s }, + { "CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE"_s, + "Path to a CMake language file included as the last step of any project() command call matching <PROJECT-NAME>. Intended for injecting custom code without modifying the project source."_s }, + { "CMAKE_XCODE_ATTRIBUTE_<an-attribute>"_s, + "Tells the Xcode generator to set <an-attribute> to the given value in the generated Xcode project. Ignored on other generators. Low-level escape hatch for Xcode-specific settings."_s }, + + // === 1-placeholder, (20-literal-char bucket) === + { "CMAKE_<LANG>_HOST_COMPILER"_s, + "Host compiler executable for CUDA or HIP code compilation"_s }, + + // === 1-placeholder, (17-literal-char bucket) === + { "CMAKE_<LANG>_CLANG_TIDY"_s, + "Default value for the <LANG>_CLANG_TIDY target property; names a clang-tidy command (with arguments) run alongside compilation. Applies only when <LANG> is C, CXX, OBJC, or OBJCXX."_s }, + { "CMAKE_<LANG>_EXTENSIONS"_s, + "Default value for the <LANG>_EXTENSIONS target property when a target is created. Controls whether compiler-specific extensions to the language standard are enabled."_s }, + { "CMAKE_<LANG>_FLAGS_INIT"_s, + "Toolchain-file hook initializing CMAKE_<LANG>_FLAGS the first time the build tree is configured for <LANG>. Set in toolchain or platform files; user projects should set CMAKE_<LANG>_FLAGS instead."_s }, + { "CMAKE_<LANG>_LINK_FLAGS"_s, + "Language-wide flags for <LANG> used when linking for all configurations. Passed to every compiler invocation that drives linking. Per-config CMAKE_<LANG>_LINK_FLAGS_<CONFIG> are appended on top."_s }, + { "CMAKE_<LANG>_PVS_STUDIO"_s, + "Default value for the <LANG>_PVS_STUDIO target property; names a pvs-studio-analyzer command (with arguments) run alongside compilation. Applies only when <LANG> is C or CXX."_s }, + + // === 1-placeholder, (15-literal-char bucket) === + { "CMAKE_<LANG>_COMPILER"_s, + "Full path to the compiler used for language <LANG>. Set by CMake during compiler detection on the first configure; users may override on the command line to select a specific toolchain."_s }, + { "CMAKE_<LANG>_CPPCHECK"_s, + "Default value for the <LANG>_CPPCHECK target property; names a cppcheck command (with arguments) run alongside compilation. Applies only when <LANG> is C or CXX."_s }, + { "CMAKE_<LANG>_STANDARD"_s, + "Default value for the <LANG>_STANDARD target property when a target is created. Applies to <LANG> = C, CXX, CUDA, HIP, OBJC, OBJCXX. See cmake-compile-features(7)."_s }, + + // === 1-placeholder, (14-literal-char bucket) === + { "CMAKE_<CONFIG>_POSTFIX"_s, + "Default filename postfix for libraries under configuration <CONFIG>."_s }, + { "CMAKE_<LANG>_CPPLINT"_s, + "Default value for the <LANG>_CPPLINT target property; names a cpplint command (with arguments) run alongside compilation. Applies only when <LANG> is C or CXX."_s }, + + // === 1-placeholder, (13-literal-char bucket) === + { "CMAKE_<LANG>_ICSTAT"_s, + "Default value for the <LANG>_ICSTAT target property; names an icstat static-analysis command (with arguments) run alongside compilation. Applies only when <LANG> is C or CXX."_s }, + + // === 1-placeholder, (12-literal-char bucket) === + { "CMAKE_<LANG>_FLAGS"_s, + "Language-wide compiler flags for <LANG>. Initialized from the language-specific environment variable (CFLAGS, CXXFLAGS, FFLAGS, etc.) the first time the language is enabled."_s }, + { "CMAKE_MATCH_<n>"_s, + "Capture group <n> matched by the most recent regular expression (groups 0 through 9). Group 0 is the entire match; groups 1 through 9 are the parenthesized subexpressions."_s }, + + // === 1-placeholder, (11-literal-char bucket) === + { "<PROJECT-NAME>_BINARY_DIR"_s, + "Top-level binary directory for the named project. Created by the project() command with the given <PROJECT-NAME>; useful when add_subdirectory is used to connect several projects."_s }, + { "<PROJECT-NAME>_SOURCE_DIR"_s, + "Top-level source directory for the named project. Created by the project() command with the given <PROJECT-NAME>; useful when add_subdirectory is used to connect several projects."_s }, + + // === 1-placeholder, (8-literal-char bucket) === + { "<PROJECT-NAME>_VERSION"_s, + "Value given to the VERSION option of the most recent project() command with project name <PROJECT-NAME>, if any. Component values live in <PROJECT-NAME>_VERSION_{MAJOR,MINOR,PATCH,TWEAK}."_s }, + + // === 1-placeholder, (5-literal-char bucket) === + { "<PackageName>_ROOT"_s, + "Initial search prefix(es) for find_package(<PackageName>); covers both the mixed-case form (CMP0074, e.g. Foo_ROOT) and the upper-case form (CMP0144, e.g. FOO_ROOT)."_s }, + + // === 2-placeholder, (36-literal-char bucket) === + { "CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED"_s, + "Set to TRUE if <FEATURE> is supported for linker language <LANG>."_s }, + + // === 2-placeholder, (34-literal-char bucket) === + { "CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED"_s, + "Specifies if <FEATURE> is supported for link language <LANG>."_s }, + + // === 2-placeholder, (31-literal-char bucket) === + { "CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_ATTRIBUTES"_s, + "Defines semantics of link library <FEATURE> for language <LANG>."_s }, + + // === 2-placeholder, (26-literal-char bucket) === + { "CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>"_s, + "Defines how to link a library for <FEATURE>."_s }, + + // === 2-placeholder, (24-literal-char bucket) === + { "CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>"_s, + "Defines how to link a group of libraries for <FEATURE>."_s }, + + // === 2-placeholder, (20-literal-char bucket) === + { "CMAKE_<LANG>_USING_LINKER_<TYPE>"_s, + "Defines how to specify the <TYPE> linker for the link step."_s }, + + // === 2-placeholder, (18-literal-char bucket) === + { "CMAKE_<LANG>_FLAGS_<CONFIG>_INIT"_s, + "Toolchain-file hook initializing CMAKE_<LANG>_FLAGS_<CONFIG> the first time the build tree is configured for language <LANG>. Intended for toolchain or platform files."_s }, + { "CMAKE_<LANG>_LINK_FLAGS_<CONFIG>"_s, + "Language-wide flags for <LANG> used when linking for the <CONFIG> configuration. Passed to every compiler invocation that drives linking. Appended after CMAKE_<LANG>_LINK_FLAGS."_s }, + + // === 2-placeholder, (13-literal-char bucket) === + { "CMAKE_<LANG>_FLAGS_<CONFIG>"_s, + "Language-wide flags for <LANG> used when building for the <CONFIG> configuration. Appended after CMAKE_<LANG>_FLAGS on both compile and link invocations."_s }, +}; +/* clang-format on */ + +// --------------------------------------------------------------------------- +// Character classes used by the matcher. +// --------------------------------------------------------------------------- + +constexpr bool IsAsciiAlpha(char c) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +constexpr bool IsAsciiDigit(char c) +{ + return c >= '0' && c <= '9'; +} + +// Identifier: ``[A-Za-z][A-Za-z0-9_]*`` -- start vs. continuation. +constexpr bool IsIdentStart(char c) +{ + return IsAsciiAlpha(c); +} +constexpr bool IsIdentCont(char c) +{ + return IsAsciiAlpha(c) || IsAsciiDigit(c) || c == '_'; +} + +// Identifier-with-hyphen: ``[A-Za-z][A-Za-z0-9_-]*`` -- start vs. +// continuation. +constexpr bool IsIdentHyphenStart(char c) +{ + return IsAsciiAlpha(c); +} +constexpr bool IsIdentHyphenCont(char c) +{ + return IsAsciiAlpha(c) || IsAsciiDigit(c) || c == '_' || c == '-'; +} + +// --------------------------------------------------------------------------- +// Placeholder dispatch. +// --------------------------------------------------------------------------- + +enum class PlaceholderClass +{ + Ident, // <LANG>, <CONFIG>, <PackageName> + IdentHyphen, // <PROJECT-NAME>, <an-attribute> + FourDigit, // <NNNN> + OneDigit, // <n> + Unknown, // malformed pattern entry +}; + +PlaceholderClass ClassOf(cm::string_view placeholder) +{ + // ``placeholder`` includes the surrounding angle brackets, e.g. "<LANG>". + // ``<FEATURE>`` and ``<TYPE>`` are documented as identifiers by their + // RSTs (e.g. WEAK / WHOLE_ARCHIVE for FEATURE in + // Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.rst and LLD / MOLD + // for TYPE in Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst), so they + // share the ``Ident`` lexical class with <LANG>/<CONFIG>/<PackageName>. + // Without this clause the eleven pattern entries that use these two + // placeholders would never match any input (regression guarded by + // testEveryPatternReachable in Tests/CMakeLib). + if (placeholder == "<LANG>" || placeholder == "<CONFIG>" || + placeholder == "<PackageName>" || placeholder == "<FEATURE>" || + placeholder == "<TYPE>") { + return PlaceholderClass::Ident; + } + if (placeholder == "<PROJECT-NAME>" || placeholder == "<an-attribute>") { + return PlaceholderClass::IdentHyphen; + } + if (placeholder == "<NNNN>") { + return PlaceholderClass::FourDigit; + } + if (placeholder == "<n>") { + return PlaceholderClass::OneDigit; + } + return PlaceholderClass::Unknown; +} + +bool ClassAllowsStart(PlaceholderClass cls, char c) +{ + switch (cls) { + case PlaceholderClass::Ident: + return IsIdentStart(c); + case PlaceholderClass::IdentHyphen: + return IsIdentHyphenStart(c); + case PlaceholderClass::FourDigit: + case PlaceholderClass::OneDigit: + return IsAsciiDigit(c); + case PlaceholderClass::Unknown: + break; + } + return false; +} + +bool ClassAllowsCont(PlaceholderClass cls, char c) +{ + switch (cls) { + case PlaceholderClass::Ident: + return IsIdentCont(c); + case PlaceholderClass::IdentHyphen: + return IsIdentHyphenCont(c); + case PlaceholderClass::FourDigit: + case PlaceholderClass::OneDigit: + return IsAsciiDigit(c); + case PlaceholderClass::Unknown: + break; + } + return false; +} + +// --------------------------------------------------------------------------- +// Helpers. +// --------------------------------------------------------------------------- + +bool LiteralMatches(cm::string_view input, std::size_t pos, + cm::string_view literal) +{ + return input.size() - pos >= literal.size() && + input.compare(pos, literal.size(), literal) == 0; +} + +// Try to match ``pattern`` against ``input`` end-to-end under the +// lazy + lookahead-anchored semantics documented at the top of this file. +// Returns true on full-consumption match; false otherwise. +bool TryMatch(cm::string_view pattern, cm::string_view input) +{ + std::size_t pi = 0; + std::size_t ii = 0; + + while (pi < pattern.size()) { + if (pattern[pi] != '<') { + // Literal segment: walk until next '<' or end of pattern. + std::size_t literal_end = pi; + while (literal_end < pattern.size() && pattern[literal_end] != '<') { + ++literal_end; + } + cm::string_view const lit = pattern.substr(pi, literal_end - pi); + if (!LiteralMatches(input, ii, lit)) { + return false; + } + pi = literal_end; + ii += lit.size(); + continue; + } + + // Placeholder segment: find the closing '>'. + std::size_t const close = pattern.find('>', pi); + if (close == cm::string_view::npos) { + return false; // malformed pattern; matched by no input + } + cm::string_view const ph = pattern.substr(pi, close - pi + 1); + PlaceholderClass const cls = ClassOf(ph); + if (cls == PlaceholderClass::Unknown) { + return false; // unknown placeholder class; rejected by unit test too + } + + // Locate the next anchor literal that follows this placeholder. An + // empty anchor means the placeholder is the last segment in the + // pattern (must consume to end-of-input) or that it is followed + // immediately by another placeholder (adjacent placeholders are + // forbidden -- testPatternTableFieldsValid rejects them at table + // build time, so we treat the case defensively here). + std::size_t const after_ph = close + 1; + std::size_t anchor_end = after_ph; + while (anchor_end < pattern.size() && pattern[anchor_end] != '<') { + ++anchor_end; + } + cm::string_view const anchor = + pattern.substr(after_ph, anchor_end - after_ph); + bool const consume_to_eoi = + anchor.empty() && (anchor_end == pattern.size()); + if (anchor.empty() && !consume_to_eoi) { + return false; // adjacent placeholders are not supported + } + + // Fixed-length classes consume exactly that many digits, then anchor. + if (cls == PlaceholderClass::FourDigit || + cls == PlaceholderClass::OneDigit) { + std::size_t const fixed_len = + (cls == PlaceholderClass::FourDigit) ? 4u : 1u; + if (input.size() - ii < fixed_len) { + return false; + } + for (std::size_t k = 0; k < fixed_len; ++k) { + if (!IsAsciiDigit(input[ii + k])) { + return false; + } + } + std::size_t const cur = ii + fixed_len; + if (consume_to_eoi) { + if (cur != input.size()) { + return false; + } + ii = cur; + } else { + if (!LiteralMatches(input, cur, anchor)) { + return false; + } + ii = cur + anchor.size(); + } + pi = anchor_end; + continue; + } + + // Variable-length classes: placeholder is non-empty; consume lazily, + // stopping at the first cursor position where the anchor matches (or + // at end-of-input when there is no trailing literal anchor). + if (ii >= input.size()) { + return false; + } + if (!ClassAllowsStart(cls, input[ii])) { + return false; + } + std::size_t cur = ii + 1; // consumed first char + if (consume_to_eoi) { + while (cur < input.size()) { + if (!ClassAllowsCont(cls, input[cur])) { + return false; + } + ++cur; + } + ii = cur; + } else { + while (true) { + if (LiteralMatches(input, cur, anchor)) { + break; + } + if (cur >= input.size()) { + return false; + } + if (!ClassAllowsCont(cls, input[cur])) { + return false; + } + ++cur; + } + ii = cur + anchor.size(); + } + pi = anchor_end; + } + + return ii == input.size(); +} + +} // namespace + +cmCacheDocumentationTable::LookupResult cmCachePatternTable::Match( + cm::string_view varName) +{ + for (auto const& entry : kPatterns) { + if (TryMatch(entry.Pattern, varName)) { + return { entry.Summary }; + } + } + return {}; +} + +cmCachePatternTable::Entry const* cmCachePatternTable::EntriesBegin() +{ + return std::begin(kPatterns); +} + +cmCachePatternTable::Entry const* cmCachePatternTable::EntriesEnd() +{ + return std::end(kPatterns); +} + +std::size_t cmCachePatternTable::EntriesSize() +{ + return static_cast<std::size_t>(std::end(kPatterns) - std::begin(kPatterns)); +}
diff --git a/Source/cmCachePatternTable.h b/Source/cmCachePatternTable.h new file mode 100644 index 0000000..f35fc92 --- /dev/null +++ b/Source/cmCachePatternTable.h
@@ -0,0 +1,84 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <cstddef> + +#include <cm/string_view> + +#include "cmCacheDocumentationTable.h" + +/** \namespace cmCachePatternTable + * \brief Compile-time pattern-matching fallback for built-in CMake cache + * variable documentation. + * + * The pattern table is a hand-maintained list of placeholder-shaped + * tooltips, e.g. ``CMAKE_<LANG>_FLAGS`` or + * ``CMAKE_POLICY_DEFAULT_CMP<NNNN>``, that complement the exact-match + * entries in ``cmCacheDocumentationTable``. It is consulted only on + * the miss path: ``cmCacheDocumentationTable::Get`` first attempts an + * exact-match lookup, then falls back to ``cmCachePatternTable::Match`` + * for names that follow a known template shape. + * + * Placeholder lexical classes recognized by the matcher (see + * ``cmCachePatternTable.cxx`` for details): + * + * * ``<LANG>``, ``<CONFIG>``, ``<PackageName>``: identifier + * (``[A-Za-z][A-Za-z0-9_]*``). + * * ``<PROJECT-NAME>``, ``<an-attribute>``: identifier with + * hyphens (``[A-Za-z][A-Za-z0-9_-]*``). + * * ``<NNNN>``: exactly 4 ASCII digits (``[0-9]{4}``). + * * ``<n>``: exactly 1 ASCII digit (``[0-9]``). + * + * Matching is leftmost-anchor, lazy on each placeholder, with full + * input consumption required at the end. The matcher is intentionally + * permissive: per-language or per-configuration restrictions documented + * in the corresponding ``Help/variable/<NAME>.rst`` file are reproduced + * in the entry's Summary prose, not enforced by the matcher. First + * fully-matching entry in ``kPatterns[]`` wins; entries are kept in + * ``(numPlaceholders ASC, totalLiteralLen DESC, Pattern ASC)`` order so + * that more-specific patterns are tried before less specific ones. + */ +namespace cmCachePatternTable { + +/** \brief A single row in the pattern documentation table. + * + * The ``Pattern`` and ``Summary`` views reference storage with static + * lifetime. ``Pattern`` is a literal template string containing one or + * more angle-bracketed placeholders such as ``<LANG>`` or ``<NNNN>``. + */ +struct Entry +{ + cm::string_view Pattern; + cm::string_view Summary; +}; + +/** \brief Look up the built-in documentation for \a varName by pattern. + * + * Walks ``kPatterns[]`` in declaration order and returns the first + * entry whose ``Pattern`` template fully matches \a varName under the + * lexical classes above. On miss returns an empty ``Summary``. + * Performs no allocation; runs in ``O(N * |varName|)`` for the small + * (~30) table. + * + * The result type is reused from ``cmCacheDocumentationTable`` so that + * the single call site in ``cmCacheDocumentationTable::Get`` can return + * either an exact-match or a pattern-match result without translation. + */ +cmCacheDocumentationTable::LookupResult Match(cm::string_view varName); + +/** \brief Iterate the underlying pattern table. + * + * Exposed for the ``testCacheDocumentationTable`` unit test in + * ``Tests/CMakeLib``, which verifies that the entries are well-formed + * and obey the canonical ordering rule. Callers should not rely on a + * particular entry count or ordering beyond what the unit test + * asserts. + */ +Entry const* EntriesBegin(); +Entry const* EntriesEnd(); +std::size_t EntriesSize(); + +}
diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 498c1b5..9e0d2e4 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx
@@ -17,6 +17,7 @@ #include "cmBlockCommand.h" #include "cmBreakCommand.h" #include "cmBuildCommand.h" +#include "cmCMakeDiagnosticCommand.h" #include "cmCMakeLanguageCommand.h" #include "cmCMakeMinimumRequired.h" #include "cmCMakePathCommand.h" @@ -25,6 +26,7 @@ #include "cmContinueCommand.h" #include "cmCreateTestSourceList.h" #include "cmDefinePropertyCommand.h" +#include "cmDiscoverTestsCommand.h" #include "cmEnableLanguageCommand.h" #include "cmEnableTestingCommand.h" #include "cmExecProgramCommand.h" @@ -124,6 +126,7 @@ state->AddFlowControlCommand("while", cmWhileCommand); state->AddFlowControlCommand("block", cmBlockCommand); + state->AddBuiltinCommand("cmake_diagnostic", cmCMakeDiagnosticCommand); state->AddBuiltinCommand("cmake_language", cmCMakeLanguageCommand); state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired); state->AddBuiltinCommand("cmake_path", cmCMakePathCommand); @@ -237,6 +240,7 @@ state->AddBuiltinCommand("build_command", cmBuildCommand); state->AddBuiltinCommand("create_test_sourcelist", cmCreateTestSourceList); state->AddBuiltinCommand("define_property", cmDefinePropertyCommand); + state->AddBuiltinCommand("discover_tests", cmDiscoverTestsCommand); state->AddBuiltinCommand("enable_language", cmEnableLanguageCommand); state->AddBuiltinCommand("enable_testing", cmEnableTestingCommand); state->AddBuiltinCommand("get_source_file_property",
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index baa1198..f219c6e 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx
@@ -6,6 +6,7 @@ #include <sstream> #include <utility> +#include <cm/filesystem> #include <cm/string_view> #include <cmext/string_view> @@ -180,35 +181,103 @@ if (cmComputeLinkInformation* cli = this->GeneratorTarget->GetLinkInformation(config)) { - auto addLinkedTarget = - [this, &lang, &config, &dirs, &direct_emitted, &forward_emitted, - gg](cmGeneratorTarget const* linkee, Forwarding forward) { - if (linkee && - !linkee->IsImported() - // Skip targets that build after this one in a static lib cycle. - && gg->TargetOrderIndexLess(linkee, this->GeneratorTarget) - // We can ignore the INTERFACE_LIBRARY items because - // Target->GetLinkInformation already processed their - // link interface and they don't have any output themselves. - && (linkee->GetType() != cmStateEnums::INTERFACE_LIBRARY - // Synthesized targets may have relevant rules. - || linkee->IsSynthetic()) && - ((lang == "CXX"_s && linkee->HaveCxx20ModuleSources()) || - (lang == "Fortran"_s && linkee->HaveFortranSources(config)))) { - cmLocalGenerator* lg = linkee->GetLocalGenerator(); - std::string di = linkee->GetSupportDirectory(); - if (lg->GetGlobalGenerator()->IsMultiConfig()) { - di = cmStrCat(di, '/', config); - } - if (forward == Forwarding::Yes && - forward_emitted.insert(linkee).second) { - dirs.Forward.push_back(di); - } - if (direct_emitted.insert(linkee).second) { - dirs.Direct.emplace_back(di); + + auto findSyntheticTarget = + [this, &config, + cli](cmGeneratorTarget const* linkee) -> cmGeneratorTarget const* { + if (!linkee) { + return nullptr; + } + + // Check the map of direct synthetic dependencies for a substitute + auto const& synthDeps = this->GeneratorTarget->GetSyntheticDeps(config); + auto it = synthDeps.find(linkee); + if (it != synthDeps.end() && !it->second.empty()) { + return *it->second.begin(); + } + + // Check linked targets to find synthetic targets for transitive deps + std::vector<cmGeneratorTarget const*> pending; + std::set<cmGeneratorTarget const*> visited; + for (auto const& dep : synthDeps) { + for (auto const* synth : dep.second) { + if (synth && visited.insert(synth).second) { + pending.push_back(synth); } } - }; + } + + // Also seed the search from direct linked targets that have + // CXX20 module sources, in case they hold synthetic deps for + // transitive dependencies that we need to inherit. Skip targets + // that already have synthetic substitutes in our own synthDeps + // (their transitive deps will be explored via the substitute). + for (auto const& item : cli->GetItems()) { + if (item.Target && item.Target->HaveCxx20ModuleSources() && + synthDeps.find(item.Target) == synthDeps.end() && + visited.insert(item.Target).second) { + pending.push_back(item.Target); + } + } + + while (!pending.empty()) { + auto const* current = pending.back(); + pending.pop_back(); + auto const& transitiveSynthDeps = current->GetSyntheticDeps(config); + auto itLinkeeSynth = transitiveSynthDeps.find(linkee); + if (itLinkeeSynth != transitiveSynthDeps.end() && + !itLinkeeSynth->second.empty()) { + return *itLinkeeSynth->second.begin(); + } + for (auto const& entry : transitiveSynthDeps) { + for (auto const* synth : entry.second) { + if (synth && visited.insert(synth).second) { + pending.push_back(synth); + } + } + } + } + + return nullptr; + }; + + auto addLinkedTarget = [this, &lang, &config, &dirs, &direct_emitted, + &forward_emitted, &findSyntheticTarget, + gg](cmGeneratorTarget const* linkee, + Forwarding forward) { + // Check if the linkee has a synthetic target to use for importing + cmGeneratorTarget const* mappedLinkee = linkee; + if (auto const* synth = findSyntheticTarget(linkee)) { + mappedLinkee = synth; + } + + if (mappedLinkee && + !mappedLinkee->IsImported() + // Skip targets that build after this one in a static lib cycle. + && gg->TargetOrderIndexLess(mappedLinkee, this->GeneratorTarget) + // We can ignore the INTERFACE_LIBRARY items because + // Target->GetLinkInformation already processed their + // link interface and they don't have any output themselves. + && (mappedLinkee->GetType() != cmStateEnums::INTERFACE_LIBRARY + // Synthesized targets may have relevant rules. + || mappedLinkee->IsSynthetic()) && + ((lang == "CXX"_s && mappedLinkee->HaveCxx20ModuleSources()) || + (lang == "Fortran"_s && + mappedLinkee->HaveFortranSources(config)))) { + cmLocalGenerator* lg = mappedLinkee->GetLocalGenerator(); + std::string di = mappedLinkee->GetSupportDirectory(); + if (lg->GetGlobalGenerator()->IsMultiConfig()) { + di = cmStrCat(di, '/', config); + } + if (forward == Forwarding::Yes && + forward_emitted.insert(mappedLinkee).second) { + dirs.Forward.push_back(di); + } + if (direct_emitted.insert(mappedLinkee).second) { + dirs.Direct.emplace_back(di); + } + } + }; for (auto const& item : cli->GetItems()) { if (item.Target) { addLinkedTarget(item.Target, Forwarding::No); @@ -598,3 +667,34 @@ }; return std::all_of(languagesNeeded.cbegin(), languagesNeeded.cend(), unary); } + +void cmCommonTargetGenerator::ComputeRustFlagsForObjects( + std::string& linkCrates, std::string& nativeObjects, + std::vector<std::string> const& objects) +{ + std::stringstream rlibsArgs; + std::stringstream objectsArgs; + auto const processObject = [&](std::string const& obj) { + cm::filesystem::path const objPath(obj); + if (objPath.extension() == ".rlib") { + // Drop the "lib..." prefix and the ".rs" suffix. The prefix is required + // by Rust on the crate rlib file, but is hidden from the user when using + // the crate from Rust source code, so we drop it to be consistent with + // common usage in Rust. + std::string objStem = objPath.stem().string(); + objStem = objStem.substr(3, objStem.length() - 6); + rlibsArgs << " --extern=" << objStem << "=" + << this->LocalCommonGenerator->ConvertToOutputFormat( + obj, cmOutputConverter::SHELL); + } else { + objectsArgs << " -Clink-arg=" + << this->LocalCommonGenerator->ConvertToOutputFormat( + obj, cmOutputConverter::SHELL); + } + }; + for (auto const& obj : objects) { + processObject(obj); + } + linkCrates += rlibsArgs.str(); + nativeObjects += objectsArgs.str(); +}
diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h index b97b947..d8fd694 100644 --- a/Source/cmCommonTargetGenerator.h +++ b/Source/cmCommonTargetGenerator.h
@@ -90,6 +90,10 @@ bool HaveRequiredLanguages(std::vector<cmSourceFile const*> const& sources, std::set<std::string>& languagesNeeded) const; + void ComputeRustFlagsForObjects(std::string& linkCrates, + std::string& nativeObjects, + std::vector<std::string> const& objects); + private: using ByLanguageMap = std::map<std::string, std::string>; struct ByConfig
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index d2da7ec..26abfbd 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx
@@ -18,6 +18,7 @@ #include "cmsys/RegularExpression.hxx" #include "cmComputeComponentGraph.h" +#include "cmDiagnostics.h" #include "cmGenExContext.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionDAGChecker.h" @@ -362,11 +363,10 @@ if (!makefile->GetCMakeInstance()->GetIsInTryCompile() && makefile->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0156")) { - makefile->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0156), - "\nSince the policy is not set, legacy libraries " - "de-duplication strategy will be applied."), + makefile->IssuePolicyWarning( + cmPolicies::CMP0156, {}, + "Since the policy is not set, legacy libraries " + "de-duplication strategy will be applied."_s, target->GetBacktrace()); } CM_FALLTHROUGH; @@ -379,12 +379,10 @@ !makefile->GetCMakeInstance()->GetIsInTryCompile() && makefile->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0179")) { - makefile->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0179), - "\nSince the policy is not set, static libraries " - "de-duplication will keep the last occurrence of the " - "static libraries."), + makefile->IssuePolicyWarning( + cmPolicies::CMP0179, {}, + "Since the policy is not set, static libraries de-duplication " + "will keep the last occurrence of the static libraries."_s, target->GetBacktrace()); } @@ -1037,8 +1035,8 @@ if (depender.Target && depender.Target->IsImported() && !IsFeatureSupported(this->Makefile, this->LinkLanguage, item.Feature)) { - this->CMakeInstance->IssueMessage( - MessageType::AUTHOR_ERROR, + this->CMakeInstance->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(), "' uses the generator-expression '$<LINK_LIBRARY>' with " "the feature '", @@ -1088,8 +1086,8 @@ if (depender.Target && depender.Target->IsImported() && !IsGroupFeatureSupported(this->Makefile, this->LinkLanguage, groupFeature)) { - this->CMakeInstance->IssueMessage( - MessageType::AUTHOR_ERROR, + this->CMakeInstance->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(), "' uses the generator-expression '$<LINK_GROUP>' with " "the feature '", @@ -1117,8 +1115,8 @@ cmStateEnums::TargetType::INTERFACE_LIBRARY)) { supportedItem = false; auto const& groupFeature = this->EntryList[group->first].Feature; - this->CMakeInstance->IssueMessage( - MessageType::AUTHOR_WARNING, + this->CMakeInstance->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat( "The feature '", groupFeature, "', specified as part of a generator-expression " @@ -1137,8 +1135,8 @@ if (featureAttributes.LibraryTypes.find(entry.Target->GetType()) == featureAttributes.LibraryTypes.end()) { supportedItem = false; - this->CMakeInstance->IssueMessage( - MessageType::AUTHOR_WARNING, + this->CMakeInstance->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("The feature '", itemFeature, "', specified as part of a generator-expression " "'$<LINK_LIBRARY:",
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 3967a0b..62e3d80 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx
@@ -2312,9 +2312,13 @@ } } - // Add runtime paths required by the languages to always be - // present. This is done even when skipping rpath support. - { + // Add runtime paths required by the toolchains' runtime libraries. + // This is done even when skipping RPATH entries modeled by CMake. + // However, INSTALL_REMOVE_ENVIRONMENT_RPATH tells us to exclude + // toolchain-defined RPATH entries from installation. + bool const removeEnvironmentRPath = for_install && + this->Target->GetPropertyAsBool("INSTALL_REMOVE_ENVIRONMENT_RPATH"); + if (!removeEnvironmentRPath) { cmGeneratorTarget::LinkClosure const* lc = this->Target->GetLinkClosure(this->Config); for (std::string const& li : lc->Languages) { @@ -2330,8 +2334,8 @@ } } - // Add runtime paths required by the platform to always be - // present. This is done even when skipping rpath support. + // Add runtime paths required by the platform to always be present. + // This is done even when skipping RPATH entries modeled by CMake. cmCLI_ExpandListUnique(this->RuntimeAlways, runtimeDirs, emitted); }
diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx index 4b165b1..1e079ab 100644 --- a/Source/cmComputeTargetDepends.cxx +++ b/Source/cmComputeTargetDepends.cxx
@@ -219,6 +219,7 @@ emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); + // Build link dependencies before the current target. if (cmLinkImplementation const* impl = depender->GetLinkImplementation( it, cmGeneratorTarget::UseTo::Link)) { for (cmLinkItem const& lib : impl->Libraries) { @@ -236,6 +237,18 @@ } } + // Build compile dependencies before the current target. + if (cmLinkImplementation const* impl = depender->GetLinkImplementation( + it, cmGeneratorTarget::UseTo::Compile)) { + for (cmLinkItem const& lib : impl->Libraries) { + // Don't emit the same library twice for this target. + if (emitted.insert(lib).second) { + this->AddTargetDepend(depender_index, lib, true, false, emitted); + this->AddInterfaceDepends(depender_index, lib, it, emitted); + } + } + } + // Add dependencies on object libraries not otherwise handled above. std::vector<cmSourceFile const*> objectFiles; depender->GetExternalObjects(objectFiles, it);
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx index 1c0900c..9cfd0fb 100644 --- a/Source/cmConditionEvaluator.cxx +++ b/Source/cmConditionEvaluator.cxx
@@ -11,12 +11,14 @@ #include <sstream> #include <utility> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include "cmsys/RegularExpression.hxx" #include "cmCMakePath.h" +#include "cmDiagnostics.h" #include "cmExpandedCommandArgument.h" #include "cmList.h" #include "cmMakefile.h" @@ -30,6 +32,7 @@ auto const keyAND = "AND"_s; auto const keyCOMMAND = "COMMAND"_s; auto const keyDEFINED = "DEFINED"_s; +auto const keyDIAGNOSTIC = "DIAGNOSTIC"_s; auto const keyEQUAL = "EQUAL"_s; auto const keyEXISTS = "EXISTS"_s; auto const keyIS_READABLE = "IS_READABLE"_s; @@ -409,6 +412,9 @@ // now recursively invoke IsTrue to handle the values inside the // parenthetical expression auto const value = this->IsTrue(subExpr, errorString, status); + if (!errorString.empty()) { + return false; + } *arg = cmExpandedCommandArgument(bool2string(value), true); argOpen = std::next(arg); // remove the now evaluated parenthetical expression @@ -476,6 +482,13 @@ this->Makefile.GetState()->GetCommand(args.next->GetValue())), args); } + // does a diagnostic exist + else if (this->IsKeyword(keyDIAGNOSTIC, *args.current)) { + newArgs.ReduceOneArg( + cmDiagnostics::GetDiagnosticCategory(args.next->GetValue()) + .has_value(), + args); + } // does a policy exist else if (this->IsKeyword(keyPOLICY, *args.current)) { cmPolicies::PolicyID pid; @@ -659,14 +672,11 @@ } else if (this->Policy139Status == cmPolicies::WARN) { - std::ostringstream e; - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0139) - << "\n" - "PATH_EQUAL will be interpreted as an operator " - "when the policy is set to NEW. " - "Since the policy is not set the OLD behavior will be used."; - - this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + this->Makefile.IssuePolicyWarning( + cmPolicies::CMP0139, {}, + "PATH_EQUAL will be interpreted as an operator " + "when the policy is set to NEW. " + "Since the policy is not set the OLD behavior will be used."_s); } } }
diff --git a/Source/cmConfigureFileCommand.cxx b/Source/cmConfigureFileCommand.cxx index 42ef230..8848a4c 100644 --- a/Source/cmConfigureFileCommand.cxx +++ b/Source/cmConfigureFileCommand.cxx
@@ -10,10 +10,10 @@ #include <sys/types.h> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmFSPermissions.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmNewLineStyle.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -156,7 +156,7 @@ if (!unknown_args.empty()) { std::string msg = cmStrCat( "configure_file called with unknown argument(s):\n", unknown_args); - status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, msg); + status.GetMakefile().IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, msg); } if (useSourcePermissions && noSourcePermissions) {
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 6befcbf..b4d35c5 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx
@@ -4,7 +4,6 @@ #include <array> #include <cstdio> -#include <cstring> #include <functional> #include <set> #include <sstream> @@ -19,15 +18,16 @@ #include "cmArgumentParser.h" #include "cmConfigureLog.h" +#include "cmDiagnostics.h" #include "cmExperimental.h" #include "cmExportTryCompileFileGenerator.h" #include "cmGlobalGenerator.h" #include "cmList.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmRange.h" +#include "cmScriptGenerator.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -85,7 +85,6 @@ "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES"; std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES = "CMAKE_TRY_COMPILE_PLATFORM_VARIABLES"; -std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED"; std::string const kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT = "CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT"; std::string const kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT = @@ -249,7 +248,7 @@ for (auto const& i : unparsedArguments) { m = cmStrCat(m, "\n \"", i, '"'); } - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m); + this->Makefile->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, m); } return arguments; } @@ -738,7 +737,7 @@ std::string langFlags = cmStrCat("CMAKE_", li, "_FLAGS"); cmValue flags = this->Makefile->GetDefinition(langFlags); fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(), - cmOutputConverter::EscapeForCMake(*flags).c_str()); + cmScriptGenerator::Quote(*flags).str().c_str()); fprintf(fout, "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}" " ${COMPILE_DEFINITIONS}\")\n", @@ -751,15 +750,11 @@ case cmPolicies::WARN: if (this->Makefile->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0066")) { - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0066) << "\n" + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0066, {}, "For compatibility with older versions of CMake, try_compile " "is not honoring caller config-specific compiler flags " - "(e.g. CMAKE_C_FLAGS_DEBUG) in the test project." - ; - /* clang-format on */ - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + "(e.g. CMAKE_C_FLAGS_DEBUG) in the test project."_s); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -775,7 +770,7 @@ cmStrCat("CMAKE_", li, "_FLAGS_", cfg); cmValue flagsCfg = this->Makefile->GetDefinition(langFlagsCfg); fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(), - cmOutputConverter::EscapeForCMake(*flagsCfg).c_str()); + cmScriptGenerator::Quote(*flagsCfg).str().c_str()); if (flagsCfg) { cmakeVariables.emplace(langFlagsCfg, *flagsCfg); } @@ -786,7 +781,7 @@ cmValue exeLinkFlags = this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS"); fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n", - cmOutputConverter::EscapeForCMake(*exeLinkFlags).c_str()); + cmScriptGenerator::Quote(*exeLinkFlags).str().c_str()); if (exeLinkFlags) { cmakeVariables.emplace("CMAKE_EXE_LINKER_FLAGS", *exeLinkFlags); } @@ -812,14 +807,14 @@ std::string langLinkFlags = cmStrCat("CMAKE_", li, "_LINK_FLAGS"); cmValue flags = this->Makefile->GetDefinition(langLinkFlags); fprintf(fout, "set(CMAKE_%s_LINK_FLAGS %s)\n", li.c_str(), - cmOutputConverter::EscapeForCMake(*flags).c_str()); + cmScriptGenerator::Quote(*flags).str().c_str()); std::string langLinkFlagsConfig = cmStrCat("CMAKE_", li, "_LINK_FLAGS_", tcConfig); cmValue flagsConfig = this->Makefile->GetDefinition(langLinkFlagsConfig); fprintf(fout, "set(CMAKE_%s_LINK_FLAGS_%s %s)\n", li.c_str(), tcConfig.c_str(), - cmOutputConverter::EscapeForCMake(*flagsConfig).c_str()); + cmScriptGenerator::Quote(*flagsConfig).str().c_str()); if (flags) { cmakeVariables.emplace(langLinkFlags, *flags); @@ -921,6 +916,13 @@ ? "NEW" : "OLD"); + // Honor CMAKE_EXE_LINKER_FLAGS in Swift if the outer project does. + fprintf(fout, "cmake_policy(SET CMP0214 %s)\n", + this->Makefile->GetPolicyStatus(cmPolicies::CMP0214) == + cmPolicies::NEW + ? "NEW" + : "OLD"); + // Workaround for -Wl,-headerpad_max_install_names issue until we can // avoid adding that flag in the platform and compiler language files fprintf(fout, @@ -1040,16 +1042,12 @@ if (!warnCMP0067Variables.empty()) { std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0067) << "\n" - "For compatibility with older versions of CMake, try_compile " - "is not honoring language standard variables in the test project:\n" - ; - /* clang-format on */ + w << "For compatibility with older versions of CMake, try_compile is " + "not honoring language standard variables in the test project:\n"_s; for (std::string const& vi : warnCMP0067Variables) { w << " " << vi << "\n"; } - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + this->Makefile->IssuePolicyWarning(cmPolicies::CMP0067, {}, w.str()); } for (auto const& p : arguments.LangProps) { @@ -1058,15 +1056,15 @@ } fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n", targetName.c_str(), - cmOutputConverter::EscapeForCMake(p.first).c_str(), - cmOutputConverter::EscapeForCMake(p.second).c_str()); + cmScriptGenerator::Quote(p.first).str().c_str(), + cmScriptGenerator::Quote(p.second).str().c_str()); } if (!arguments.LinkOptions.empty()) { std::vector<std::string> options; options.reserve(arguments.LinkOptions.size()); for (auto const& option : arguments.LinkOptions) { - options.emplace_back(cmOutputConverter::EscapeForCMake(option)); + options.emplace_back(cmScriptGenerator::Quote(option)); } if (targetType == cmStateEnums::STATIC_LIBRARY) { @@ -1137,7 +1135,6 @@ vars.insert(kCMAKE_SYSROOT); vars.insert(kCMAKE_SYSROOT_COMPILE); vars.insert(kCMAKE_SYSROOT_LINK); - vars.insert(kCMAKE_WARN_DEPRECATED); vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s); vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s); vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s); @@ -1368,8 +1365,8 @@ dir.Load(binDir); std::set<std::string> deletedFiles; for (unsigned long i = 0; i < dir.GetNumberOfFiles(); ++i) { - char const* fileName = dir.GetFile(i); - if (strcmp(fileName, ".") != 0 && strcmp(fileName, "..") != 0 && + std::string const& fileName = dir.GetFileName(i); + if (fileName != "." && fileName != ".." && // Do not delete NFS temporary files. !cmHasPrefix(fileName, ".nfs")) { if (deletedFiles.insert(fileName).second) {
diff --git a/Source/cmCxxModuleMetadata.cxx b/Source/cmCxxModuleMetadata.cxx index bf3a875..322b5a1 100644 --- a/Source/cmCxxModuleMetadata.cxx +++ b/Source/cmCxxModuleMetadata.cxx
@@ -16,6 +16,7 @@ #include "cmsys/FStream.hxx" #include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGeneratedFileStream.h" #include "cmJSONState.h" #include "cmListFileCache.h" @@ -463,13 +464,19 @@ props.MetadataDir = cmSystemTools::GetFilenamePath(meta.MetadataFilePath); +#ifndef _WIN32 + props.BaseDirs.insert("/"); +#endif + for (auto const& module : meta.Modules) { std::string sourcePath = props.NormalizePath(module.SourcePath); props.Sources.insert(sourcePath); - // Module metadata files can reference files in different roots, - // just use the immediate parent directory as a base directory - props.BaseDirs.insert(cmSystemTools::GetFilenamePath(sourcePath)); +#ifdef _WIN32 + std::string root; + cmSystemTools::SplitPathRootComponent(sourcePath, &root); + props.BaseDirs.insert(root); +#endif if (module.LocalArguments) { for (auto const& incDir : module.LocalArguments->IncludeDirectories) { @@ -504,8 +511,10 @@ void PopulateFileSet(cmTarget& target, MetaDataProperties const& props) { - auto fileSet = target.GetOrCreateFileSet("CXX_MODULES", "CXX_MODULES", - cmFileSetVisibility::Public); + auto fileSet = + target.GetOrCreateFileSet(std::string{ cm::FileSetMetadata::CXX_MODULES }, + std::string{ cm::FileSetMetadata::CXX_MODULES }, + cm::FileSetMetadata::Visibility::Public); for (auto const& source : props.Sources) { fileSet.first->AddFileEntry(source);
diff --git a/Source/cmCxxModuleUsageEffects.cxx b/Source/cmCxxModuleUsageEffects.cxx index aa1a13f..1130c37 100644 --- a/Source/cmCxxModuleUsageEffects.cxx +++ b/Source/cmCxxModuleUsageEffects.cxx
@@ -2,20 +2,335 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmCxxModuleUsageEffects.h" -cmCxxModuleUsageEffects::cmCxxModuleUsageEffects( - cmGeneratorTarget const* /*gt*/) - : Hash("0000000000000000000000000000000000000000") +#include <algorithm> +#include <set> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmCryptoHash.h" +#include "cmGeneratorTarget.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmStringAlgorithms.h" +#include "cmTarget.h" +#include "cmValue.h" + +namespace { + +bool IsMSVCWarning(cm::string_view flag) { - // TODO: collect information from the generator target as to what might - // affect module consumption. + if (flag.size() < 2) { + return false; + } + + if (flag.front() != '/' && flag.front() != '-') { + return false; + } + + flag.remove_prefix(1); + + if (flag.front() == 'w' || flag.front() == 'W') { + return true; + } + + if (flag.substr(0, 9) == "external:") { + return true; + } + + return false; } -void cmCxxModuleUsageEffects::ApplyToTarget(cmTarget* /*tgt*/) +bool IsGNUWarning(cm::string_view flag) { - // TODO: apply the information collected in the constructor + if (flag.size() < 2) { + return false; + } + + if (flag.front() != '-') { + return false; + } + + flag.remove_prefix(1); + + if (flag.front() == 'w' || flag.front() == 'W') { + return true; + } + + if (flag.front() == '-') { + flag.remove_prefix(1); + } + + static std::set<cm::string_view> const long_names{ + "pedantic", "pedantic-errors", "all-warnings", "extra-warnings", + "no-warnings" + }; + + return long_names.find(flag) != long_names.end(); +} + +void AppendUsage(std::string& usageHashInput, std::string const& entry) +{ + usageHashInput += entry; + usageHashInput.push_back('\0'); +} + +template <typename Range> +void AppendUsageEntries(std::string& usageHashInput, Range const& entries) +{ + std::vector<std::string> entryValues; + entryValues.reserve(entries.size()); + for (auto const& entry : entries) { + entryValues.push_back(entry.Value); + } + std::sort(entryValues.begin(), entryValues.end()); + + for (auto const& entryValue : entryValues) { + usageHashInput += entryValue; + usageHashInput.push_back('\n'); + } + + usageHashInput.push_back('\0'); +} + +enum class CompileOptionUsage +{ + Hash, + Ignore, + SavePreprocessor, +}; + +struct CompileOptionClassification +{ + CompileOptionClassification( + CompileOptionUsage usage = CompileOptionUsage::Hash, + bool saveFollowingArgument = false) + : Usage(usage) + , SaveFollowingArgument(saveFollowingArgument) + { + } + + CompileOptionUsage Usage = CompileOptionUsage::Hash; + bool SaveFollowingArgument = false; +}; + +CompileOptionClassification SaveIfStartsWith(cm::string_view flag, + cm::string_view prefix) +{ + if (flag.rfind(prefix, 0) == 0) { + return { CompileOptionUsage::SavePreprocessor, + flag.size() == prefix.size() }; + } + + return {}; +} + +CompileOptionClassification ClassifyGNUCompileOption(cm::string_view flag) +{ + if (IsGNUWarning(flag)) { + return { CompileOptionUsage::Ignore, false }; + } + + if (flag.size() < 2 || flag.front() != '-') { + return {}; + } + + flag.remove_prefix(1); + switch (flag.front()) { + case 'D': + case 'I': + case 'U': + return { CompileOptionUsage::SavePreprocessor, flag.size() == 1 }; + case 'i': { + CompileOptionClassification classification = + SaveIfStartsWith(flag, "isystem"_s); + if (classification.Usage != CompileOptionUsage::Hash) { + return classification; + } + } + { + CompileOptionClassification classification = + SaveIfStartsWith(flag, "iquote"_s); + if (classification.Usage != CompileOptionUsage::Hash) { + return classification; + } + } + { + CompileOptionClassification classification = + SaveIfStartsWith(flag, "idirafter"_s); + if (classification.Usage != CompileOptionUsage::Hash) { + return classification; + } + } + { + CompileOptionClassification classification = + SaveIfStartsWith(flag, "include"_s); + if (classification.Usage != CompileOptionUsage::Hash) { + return classification; + } + } + { + CompileOptionClassification classification = + SaveIfStartsWith(flag, "imacros"_s); + if (classification.Usage != CompileOptionUsage::Hash) { + return classification; + } + } + break; + case '-': + flag.remove_prefix(1); + return SaveIfStartsWith(flag, "embed-dir"_s); + default: + break; + } + + return {}; +} + +CompileOptionClassification ClassifyMSVCCompileOption(cm::string_view flag) +{ + if (IsMSVCWarning(flag)) { + return { CompileOptionUsage::Ignore, false }; + } + + if (flag.size() < 2 || (flag.front() != '/' && flag.front() != '-')) { + return {}; + } + + flag.remove_prefix(1); + switch (flag.front()) { + case 'D': + case 'I': + case 'U': + return { CompileOptionUsage::SavePreprocessor, flag.size() == 1 }; + default: + break; + } + + return {}; +} + +CompileOptionClassification ClassifyUnknownCompileOption(cm::string_view flag) +{ + static_cast<void>(flag); + return {}; +} + +using CompileOptionClassifier = + CompileOptionClassification (*)(cm::string_view flag); + +CompileOptionClassifier GetCompileOptionClassifier(cmGeneratorTarget const* gt) +{ + static CompileOptionClassifier classifier = nullptr; + if (classifier) { + return classifier; + } + + cmValue frontendVariant = + gt->Makefile->GetDefinition("CMAKE_CXX_COMPILER_FRONTEND_VARIANT"); + + if (frontendVariant == "GNU") { + classifier = ClassifyGNUCompileOption; + } else if (frontendVariant == "MSVC") { + classifier = ClassifyMSVCCompileOption; + } else { + classifier = ClassifyUnknownCompileOption; + } + + return classifier; +} + +struct SplitCompileOptionsResult +{ + std::vector<BT<std::string>> HashEntries; + std::vector<BT<std::string>> PreprocessorEntries; +}; + +template <typename Range> +SplitCompileOptionsResult SplitCompileOptions(Range const& entries, + CompileOptionClassifier classify) +{ + SplitCompileOptionsResult result; + + for (auto it = entries.begin(); it != entries.end(); ++it) { + BT<std::string> const& option = *it; + auto const classification = classify(option.Value); + + switch (classification.Usage) { + case CompileOptionUsage::Ignore: + continue; + case CompileOptionUsage::SavePreprocessor: + result.PreprocessorEntries.push_back(option); + if (classification.SaveFollowingArgument) { + auto next = it; + ++next; + if (next != entries.end()) { + result.PreprocessorEntries.push_back(*next); + it = next; + } + } + continue; + case CompileOptionUsage::Hash: + break; + } + + result.HashEntries.push_back(option); + } + + return result; +} +} + +cmCxxModuleUsageEffects::cmCxxModuleUsageEffects(cmGeneratorTarget const* gt, + std::string const& config) +{ + auto const* tgt = gt->Target; + auto const classify = GetCompileOptionClassifier(gt); + + std::string usageHashInput; + if (tgt->IsImported()) { + AppendUsageEntries(usageHashInput, + tgt->GetImportedCxxModulesCompileFeaturesEntries()); + auto compileOptions = SplitCompileOptions( + tgt->GetImportedCxxModulesCompileOptionsEntries(), classify); + this->PreprocessorCompileOptions = + std::move(compileOptions.PreprocessorEntries); + AppendUsageEntries(usageHashInput, compileOptions.HashEntries); + } else { + auto compileOptions = + SplitCompileOptions(gt->GetCompileOptions(config, "CXX"), classify); + this->PreprocessorCompileOptions = + std::move(compileOptions.PreprocessorEntries); + AppendUsageEntries(usageHashInput, compileOptions.HashEntries); + + cmValue langStd = gt->GetLanguageStandard("CXX", config); + if (!langStd) { + langStd = gt->Makefile->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT"); + } + AppendUsage(usageHashInput, + cmStrCat("CXX_STANDARD:", langStd ? *langStd : "20")); + AppendUsage( + usageHashInput, + cmStrCat("CXX_EXTENSIONS:", *gt->GetLanguageExtensions("CXX"))); + AppendUsage( + usageHashInput, + cmStrCat("CXX_STANDARD_REQUIRED:", + gt->GetLanguageStandardRequired("CXX") ? "TRUE" : "FALSE")); + } + + cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512); + this->Hash = hasher.HashString(usageHashInput); } std::string const& cmCxxModuleUsageEffects::GetHash() const { return this->Hash; } + +std::vector<BT<std::string>> const& +cmCxxModuleUsageEffects::GetPreprocessorCompileOptions() const +{ + return this->PreprocessorCompileOptions; +}
diff --git a/Source/cmCxxModuleUsageEffects.h b/Source/cmCxxModuleUsageEffects.h index 56a85b9..8b8c50f 100644 --- a/Source/cmCxxModuleUsageEffects.h +++ b/Source/cmCxxModuleUsageEffects.h
@@ -10,17 +10,16 @@ #include "cmListFileCache.h" class cmGeneratorTarget; -class cmTarget; class cmCxxModuleUsageEffects { public: - cmCxxModuleUsageEffects(cmGeneratorTarget const* gt); - - void ApplyToTarget(cmTarget* tgt); + cmCxxModuleUsageEffects(cmGeneratorTarget const* gt, + std::string const& config); std::string const& GetHash() const; + std::vector<BT<std::string>> const& GetPreprocessorCompileOptions() const; private: std::string Hash; - std::vector<BT<std::string>> CompileFeatures; + std::vector<BT<std::string>> PreprocessorCompileOptions; };
diff --git a/Source/cmDebuggerAdapter.cxx b/Source/cmDebuggerAdapter.cxx index 7cf9efe..7dfb334 100644 --- a/Source/cmDebuggerAdapter.cxx +++ b/Source/cmDebuggerAdapter.cxx
@@ -312,6 +312,14 @@ while (SessionActive.load()) { if (auto payload = Session->getPayload()) { payload(); + } else { + // Connection closed or unrecoverable error. + BreakpointManager->ClearAll(); + ExceptionManager->ClearAll(); + ClearStepRequests(); + ContinueSem->Notify(); + DisconnectEvent->Fire(); + SessionActive.store(false); } } });
diff --git a/Source/cmDebuggerBreakpointManager.cxx b/Source/cmDebuggerBreakpointManager.cxx index b33985b..555426d 100644 --- a/Source/cmDebuggerBreakpointManager.cxx +++ b/Source/cmDebuggerBreakpointManager.cxx
@@ -17,6 +17,16 @@ namespace cmDebugger { +// Resolve a source path to its canonical form so that breakpoint map +// keys match regardless of the case used by the DAP client or by +// CollapseFullPath. On case-insensitive filesystems (macOS, Windows) +// ToNormalizedPathOnDisk loads the on-disk capitalization while +// preserving symbolic links in logical paths. +static std::string NormalizePath(std::string const& sourcePath) +{ + return cmSystemTools::ToNormalizedPathOnDisk(sourcePath); +} + cmDebuggerBreakpointManager::cmDebuggerBreakpointManager( dap::Session* dapSession) : DapSession(dapSession) @@ -73,8 +83,7 @@ dap::SetBreakpointsResponse response; - auto sourcePath = - cmSystemTools::GetActualCaseForPath(request.source.path.value()); + auto sourcePath = NormalizePath(request.source.path.value()); dap::array<dap::SourceBreakpoint> const defaultValue{}; auto const& breakpoints = request.breakpoints.value(defaultValue); @@ -126,42 +135,45 @@ std::string const& sourcePath, std::vector<cmListFileFunction> const& functions) { + auto normalizedPath = NormalizePath(sourcePath); + std::unique_lock<std::mutex> lock(Mutex); - if (ListFileFunctionLines.find(sourcePath) != ListFileFunctionLines.end()) { + if (ListFileFunctionLines.find(normalizedPath) != + ListFileFunctionLines.end()) { // this is not expected. return; } for (cmListFileFunction const& func : functions) { - ListFileFunctionLines[sourcePath].emplace_back( + ListFileFunctionLines[normalizedPath].emplace_back( cmDebuggerFunctionLocation{ func.Line(), func.LineEnd() }); } - if (ListFilePendingValidations.find(sourcePath) == + if (ListFilePendingValidations.find(normalizedPath) == ListFilePendingValidations.end()) { return; } - ListFilePendingValidations.erase(sourcePath); + ListFilePendingValidations.erase(normalizedPath); - for (size_t i = 0; i < Breakpoints[sourcePath].size(); i++) { + for (size_t i = 0; i < Breakpoints[normalizedPath].size(); i++) { dap::BreakpointEvent breakpointEvent; - breakpointEvent.breakpoint.id = Breakpoints[sourcePath][i].GetId(); - breakpointEvent.breakpoint.line = Breakpoints[sourcePath][i].GetLine(); + breakpointEvent.breakpoint.id = Breakpoints[normalizedPath][i].GetId(); + breakpointEvent.breakpoint.line = Breakpoints[normalizedPath][i].GetLine(); auto source = dap::Source(); - source.path = sourcePath; + source.path = normalizedPath; breakpointEvent.breakpoint.source = source; int64_t correctedLine = CalibrateBreakpointLine( - sourcePath, Breakpoints[sourcePath][i].GetLine()); - if (correctedLine != Breakpoints[sourcePath][i].GetLine()) { - Breakpoints[sourcePath][i].ChangeLine(correctedLine); + normalizedPath, Breakpoints[normalizedPath][i].GetLine()); + if (correctedLine != Breakpoints[normalizedPath][i].GetLine()) { + Breakpoints[normalizedPath][i].ChangeLine(correctedLine); } breakpointEvent.reason = "changed"; breakpointEvent.breakpoint.verified = (correctedLine > 0); if (breakpointEvent.breakpoint.verified) { breakpointEvent.breakpoint.line = correctedLine; } else { - Breakpoints[sourcePath][i].Invalid(); + Breakpoints[normalizedPath][i].Invalid(); } DapSession->send(breakpointEvent); @@ -171,8 +183,10 @@ std::vector<int64_t> cmDebuggerBreakpointManager::GetBreakpoints( std::string const& sourcePath, int64_t line) { + auto normalizedPath = NormalizePath(sourcePath); + std::unique_lock<std::mutex> lock(Mutex); - auto const& all = Breakpoints[sourcePath]; + auto const& all = Breakpoints[normalizedPath]; std::vector<int64_t> breakpoints; if (all.empty()) { return breakpoints;
diff --git a/Source/cmDebuggerExceptionManager.cxx b/Source/cmDebuggerExceptionManager.cxx index 22a577a..b5a1fc9 100644 --- a/Source/cmDebuggerExceptionManager.cxx +++ b/Source/cmDebuggerExceptionManager.cxx
@@ -29,10 +29,6 @@ return HandleExceptionInfoRequest(); }); - ExceptionMap[MessageType::AUTHOR_WARNING] = - cmDebuggerExceptionFilter{ "AUTHOR_WARNING", "Warning (dev)" }; - ExceptionMap[MessageType::AUTHOR_ERROR] = - cmDebuggerExceptionFilter{ "AUTHOR_ERROR", "Error (dev)" }; ExceptionMap[MessageType::FATAL_ERROR] = cmDebuggerExceptionFilter{ "FATAL_ERROR", "Fatal error" }; ExceptionMap[MessageType::INTERNAL_ERROR] = @@ -43,14 +39,8 @@ cmDebuggerExceptionFilter{ "WARNING", "Warning" }; ExceptionMap[MessageType::LOG] = cmDebuggerExceptionFilter{ "LOG", "Debug log" }; - ExceptionMap[MessageType::DEPRECATION_ERROR] = - cmDebuggerExceptionFilter{ "DEPRECATION_ERROR", "Deprecation error" }; - ExceptionMap[MessageType::DEPRECATION_WARNING] = - cmDebuggerExceptionFilter{ "DEPRECATION_WARNING", "Deprecation warning" }; - RaiseExceptions["AUTHOR_ERROR"] = true; RaiseExceptions["FATAL_ERROR"] = true; RaiseExceptions["INTERNAL_ERROR"] = true; - RaiseExceptions["DEPRECATION_ERROR"] = true; } dap::SetExceptionBreakpointsResponse
diff --git a/Source/cmDebuggerPosixPipeConnection.cxx b/Source/cmDebuggerPosixPipeConnection.cxx index ac5fd04..e4572fa 100644 --- a/Source/cmDebuggerPosixPipeConnection.cxx +++ b/Source/cmDebuggerPosixPipeConnection.cxx
@@ -10,6 +10,7 @@ #include <unistd.h> #include <sys/socket.h> +#include <sys/types.h> namespace cmDebugger { @@ -110,10 +111,13 @@ { size_t result = 0; if (rw_pipe >= 0) { - result = ::read(rw_pipe, buffer, n); - if (result == 0) { + ssize_t count = ::read(rw_pipe, buffer, n); + if (count <= 0) { + // EOF or error (including EBADF from a concurrent close). close(); + return 0; } + result = static_cast<size_t>(count); } return result; @@ -174,17 +178,26 @@ } } +void cmDebuggerPipeClient_POSIX::ShutdownForTesting() +{ + if (isOpen()) { + ::shutdown(rw_pipe, SHUT_RDWR); + } +} + size_t cmDebuggerPipeClient_POSIX::read(void* buffer, size_t n) { - int count = 0; + ssize_t count = 0; if (isOpen()) { - count = static_cast<int>(::read(rw_pipe, buffer, n)); - if (count == 0) { + count = ::read(rw_pipe, buffer, n); + if (count <= 0) { + // EOF or error (including EBADF from a concurrent close). close(); + return 0; } } - return count; + return static_cast<size_t>(count); } bool cmDebuggerPipeClient_POSIX::write(void const* buffer, size_t n)
diff --git a/Source/cmDebuggerPosixPipeConnection.h b/Source/cmDebuggerPosixPipeConnection.h index 98e3f6f..d1e09d6 100644 --- a/Source/cmDebuggerPosixPipeConnection.h +++ b/Source/cmDebuggerPosixPipeConnection.h
@@ -69,6 +69,11 @@ size_t read(void* buffer, size_t n) override; bool write(void const* buffer, size_t n) override; + // Unit-test helper: signal EOF on both halves of the socket without + // freeing the fd, so a sibling thread blocked in read() wakes up + // cleanly. Production code does not use cmDebuggerPipeClient. + void ShutdownForTesting(); + private: std::string const PipeName; int rw_pipe = -1;
diff --git a/Source/cmDebuggerVariablesHelper.cxx b/Source/cmDebuggerVariablesHelper.cxx index 07d9802..9f8338f 100644 --- a/Source/cmDebuggerVariablesHelper.cxx +++ b/Source/cmDebuggerVariablesHelper.cxx
@@ -16,6 +16,7 @@ #include "cmDebuggerStackFrame.h" #include "cmDebuggerVariables.h" #include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGlobalGenerator.h" #include "cmList.h" #include "cmListFileCache.h" @@ -195,18 +196,19 @@ return {}; } - static auto visibilityString = [](cmFileSetVisibility visibility) { - switch (visibility) { - case cmFileSetVisibility::Private: - return "Private"; - case cmFileSetVisibility::Public: - return "Public"; - case cmFileSetVisibility::Interface: - return "Interface"; - default: - return "Unknown"; - } - }; + static auto visibilityString = + [](cm::FileSetMetadata::Visibility visibility) { + switch (visibility) { + case cm::FileSetMetadata::Visibility::Private: + return "Private"; + case cm::FileSetMetadata::Visibility::Public: + return "Public"; + case cm::FileSetMetadata::Visibility::Interface: + return "Interface"; + default: + return "Unknown"; + } + }; auto variables = std::make_shared<cmDebuggerVariables>( variablesManager, name, supportsVariableType, [=]() { @@ -305,15 +307,15 @@ targetVariables->AddSubVariables( CreateIfAny(variablesManager, "CompileOptions", supportsVariableType, target->GetCompileOptionsEntries())); - targetVariables->AddSubVariables( - CreateIfAny(variablesManager, "CxxModuleSets", supportsVariableType, - target->GetCxxModuleSetsEntries())); + targetVariables->AddSubVariables(CreateIfAny( + variablesManager, "CxxModuleSets", supportsVariableType, + target->GetFileSetsEntries(cm::FileSetMetadata::CXX_MODULES))); targetVariables->AddSubVariables( CreateIfAny(variablesManager, "HeaderSets", supportsVariableType, - target->GetHeaderSetsEntries())); + target->GetFileSetsEntries(cm::FileSetMetadata::HEADERS))); targetVariables->AddSubVariables(CreateIfAny( variablesManager, "InterfaceHeaderSets", supportsVariableType, - target->GetInterfaceHeaderSetsEntries())); + target->GetInterfaceFileSetsEntries(cm::FileSetMetadata::HEADERS))); targetVariables->AddSubVariables( CreateIfAny(variablesManager, "LinkDirectories", supportsVariableType, target->GetLinkDirectoriesEntries()));
diff --git a/Source/cmDebuggerWindowsPipeConnection.h b/Source/cmDebuggerWindowsPipeConnection.h index 5611255..dad23fb 100644 --- a/Source/cmDebuggerWindowsPipeConnection.h +++ b/Source/cmDebuggerWindowsPipeConnection.h
@@ -89,6 +89,11 @@ size_t read(void* buffer, size_t n) override; bool write(void const* buffer, size_t n) override; + // Unit-test helper: on Windows CloseHandle already cancels any + // pending overlapped I/O, so this just delegates to close(). + // Production code does not use cmDebuggerPipeClient. + void ShutdownForTesting() { this->close(); } + private: std::string GetErrorMessage(DWORD errorCode);
diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx index 48725dc..b7595bc 100644 --- a/Source/cmDependsC.cxx +++ b/Source/cmDependsC.cxx
@@ -176,9 +176,7 @@ fileIt->second.Used = true; dependencies.insert(fullName); for (UnscannedEntry const& inc : fileIt->second.UnscannedEntries) { - if (this->Encountered.find(inc.FileName) == - this->Encountered.end()) { - this->Encountered.insert(inc.FileName); + if (this->Encountered.insert(inc.FileName).second) { this->Unscanned.push(inc); } } @@ -382,9 +380,7 @@ // preprocessor-like implementation of this scanner is created. if (this->IncludeRegexScan.find(entry.FileName)) { newCacheEntry.UnscannedEntries.push_back(entry); - if (this->Encountered.find(entry.FileName) == - this->Encountered.end()) { - this->Encountered.insert(entry.FileName); + if (this->Encountered.insert(entry.FileName).second) { this->Unscanned.push(entry); } }
diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx index 2b85d2b..dd5779f 100644 --- a/Source/cmDependsFortran.cxx +++ b/Source/cmDependsFortran.cxx
@@ -87,7 +87,7 @@ for (std::string def : definitions) { std::string::size_type assignment = def.find('='); if (assignment != std::string::npos) { - def = def.substr(0, assignment); + def.resize(assignment); } this->PPDefinitions.insert(def); }
diff --git a/Source/cmDiagnosticContext.cxx b/Source/cmDiagnosticContext.cxx new file mode 100644 index 0000000..665201f --- /dev/null +++ b/Source/cmDiagnosticContext.cxx
@@ -0,0 +1,14 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmDiagnosticContext.h" + +#include <array> + +#include "cmStateSnapshot.h" + +void cmDiagnosticContext::RecordDiagnostic(cmDiagnosticCategory category, + cmStateSnapshot const& state) +{ + this->DiagnosticState[category] = state.GetDiagnostic(category); + this->HasState = true; +}
diff --git a/Source/cmDiagnosticContext.h b/Source/cmDiagnosticContext.h new file mode 100644 index 0000000..9f72244 --- /dev/null +++ b/Source/cmDiagnosticContext.h
@@ -0,0 +1,39 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <utility> + +#include "cmDiagnostics.h" +#include "cmListFileCache.h" + +class cmStateSnapshot; + +/** \class cmDiagnosticContext + * \brief Records context for issuing diagnostics + */ +class cmDiagnosticContext +{ +public: + explicit cmDiagnosticContext(cmListFileBacktrace backtrace) + : Backtrace{ std::move(backtrace) } + { + } + cmDiagnosticContext() = default; + cmDiagnosticContext(cmDiagnosticContext&&) = default; + cmDiagnosticContext(cmDiagnosticContext const&) = default; + + void RecordDiagnostic(cmDiagnosticCategory category, + cmStateSnapshot const& state); + + cmListFileBacktrace const& GetBacktrace() const { return this->Backtrace; } + +protected: + friend class cmMessenger; + + cmListFileBacktrace Backtrace; + cmDiagnostics::DiagnosticMap DiagnosticState; + bool HasState = false; +};
diff --git a/Source/cmDiagnostics.cxx b/Source/cmDiagnostics.cxx new file mode 100644 index 0000000..50d3c02 --- /dev/null +++ b/Source/cmDiagnostics.cxx
@@ -0,0 +1,120 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmDiagnostics.h" + +#include <cassert> +#include <map> +#include <string> +#include <utility> + +#include <cmext/string_view> + +#include "cmStringAlgorithms.h" + +namespace { + +#if __cplusplus >= 201703L +constexpr unsigned validateDiagnosticsSubtree(unsigned parent, unsigned index) +{ + // Ensure that all diagnostics, starting from the specified index, have the + // specified parent as an ancestor. Return the first index that violates + // this condition. + while (index < cmDiagnostics::CategoryCount && + cmDiagnostics::CategoryInfo[index].Parent == parent) { + unsigned const child = index; + // For each diagnostic, 'consume' its children (if any). + index = validateDiagnosticsSubtree(child, ++index); + } + return index; +} + +static_assert(validateDiagnosticsSubtree(cmDiagnostics::CMD_NONE, 1) == + cmDiagnostics::CategoryCount, + "Diagnostics are not properly ordered" + " (hint: LHS is the index of the first misordered diagnostic)"); +#endif + +cm::optional<cmDiagnosticCategory> stringToCategory(cm::string_view input) +{ + using Map = std::map<cm::string_view, cmDiagnosticCategory>; + static Map const mapping = { +#define CATEGORY_MAP(C) { #C ""_s, cmDiagnostics::C }, + CM_FOR_EACH_DIAGNOSTIC_CATEGORY(CATEGORY_MAP) +#undef CATEGORY_MAP + }; + + assert(!input.empty()); + if (input.size() >= 4 && cmHasLiteralPrefix(input, "CMD_")) { + auto const i = mapping.find(input); + if (i != mapping.end()) { + return i->second; + } + } + + return cm::nullopt; +} +} + +#if __cplusplus < 201703L +// Prior to C++17, the compiler is unhappy if this member doesn't have explicit +// storage... and clang-tidy is unhappy if it does. +// NOLINTNEXTLINE(*-redundant-declaration) +constexpr cmDiagnostics::DiagnosticCategoryInformation + cmDiagnostics::CategoryInfo[cmDiagnostics::CategoryCount]; +#endif + +cm::string_view cmDiagnostics::GetActionString(DiagnosticAction action) +{ + switch (action) { + case Ignore: + return "IGNORE"_s; + case Warn: + return "WARN"_s; + case SendError: + return "SEND_ERROR"_s; + case FatalError: + return "FATAL_ERROR"_s; + default: + return {}; + } +} + +cm::string_view cmDiagnostics::GetCategoryString(DiagnosticCategory category) +{ + static cm::string_view const names[CategoryCount] = { + {}, // CMD_NONE +#define CATEGORY_NAME(C) #C ""_s, + CM_FOR_EACH_DIAGNOSTIC_CATEGORY(CATEGORY_NAME) +#undef CATEGORY_MAP + }; + + if (category < CategoryCount) { + return names[category]; + } + return {}; +} + +cm::optional<cmDiagnosticAction> cmDiagnostics::GetDiagnosticAction( + cm::string_view name) +{ + if (name == "IGNORE"_s) { + return DiagnosticAction::Ignore; + } + if (name == "WARN"_s) { + return DiagnosticAction::Warn; + } + if (name == "SEND_ERROR"_s) { + return DiagnosticAction::SendError; + } + if (name == "FATAL_ERROR"_s) { + return DiagnosticAction::FatalError; + } + + return cm::nullopt; +} + +cm::optional<cmDiagnosticCategory> cmDiagnostics::GetDiagnosticCategory( + cm::string_view name) +{ + return stringToCategory(name); +}
diff --git a/Source/cmDiagnostics.h b/Source/cmDiagnostics.h new file mode 100644 index 0000000..5c1461c --- /dev/null +++ b/Source/cmDiagnostics.h
@@ -0,0 +1,114 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <array> +#include <cstddef> +#include <cstdint> + +#include <cm/optional> +#include <cm/string_view> + +// https://github.com/include-what-you-use/include-what-you-use/issues/1934 +// IWYU pragma: no_forward_declare cmDiagnostics::DiagnosticAction +// IWYU pragma: no_forward_declare cmDiagnostics::DiagnosticCategory + +// The list of diagnostic categories along with their associated data. +// Each entry is of the form: +// `SELECT(ACTION, <default>, <parent>, <name>, <preset-version>)` +// Entries MUST appear in the order that a depth-first enumeration would +// produce. +// +// When changing this table, Utilities/Scripts/regenerate-presets.py must be +// run to update the JSON schema for CMake presets. If the _TABLE macro name +// changes, said script will also need to be updated with the new name. + +#define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT) \ + SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12) \ + SELECT(ACTION, Warn, CMD_AUTHOR, CMD_DEPRECATED, 1) \ + SELECT(ACTION, Warn, CMD_AUTHOR, CMD_EXPERIMENTAL, 12) \ + SELECT(ACTION, Ignore, CMD_AUTHOR, CMD_INSTALL_ABSOLUTE_DESTINATION, 12) \ + SELECT(ACTION, Warn, CMD_AUTHOR, CMD_POLICY, 12) \ + SELECT(ACTION, Ignore, CMD_NONE, CMD_UNINITIALIZED, 1) \ + SELECT(ACTION, Warn, CMD_NONE, CMD_UNUSED_CLI, 1) + +#define CM_SELECT_CATEGORY(F, D, P, C, V) F(C) +#define CM_FOR_EACH_DIAGNOSTIC_CATEGORY(ACTION) \ + CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, CM_SELECT_CATEGORY) + +/** \class cmDiagnostics + * \brief Handles CMake diagnostic (warning) behavior + * + * See the cmake-diagnostics(7) manual for an overview of this class's purpose. + */ +class cmDiagnostics +{ +public: + /// Action to take when a diagnostic is triggered + enum DiagnosticAction : std::uint8_t + { + Undefined = 0, + Ignore, + Warn, + SendError, + FatalError, + }; + + /// Diagnostic category identifiers + enum DiagnosticCategory : unsigned + { + CMD_NONE, +#define DIAGNOSTIC_ENUM(CATEGORY) CATEGORY, + CM_FOR_EACH_DIAGNOSTIC_CATEGORY(DIAGNOSTIC_ENUM) +#undef DIAGNOSTIC_ENUM + + /** \brief Always the last entry. + * + * Used to determine the number of diagnostic categories. Also useful to + * avoid adding a comma the last diagnostic category when adding a new one. + */ + CMD_COUNT + }; + constexpr static size_t CategoryCount = static_cast<size_t>(CMD_COUNT); + + struct DiagnosticCategoryInformation + { + DiagnosticCategory Parent; + DiagnosticAction DefaultAction; + int PresetVersion; + }; + + constexpr static DiagnosticCategoryInformation + CategoryInfo[CategoryCount] = { + { CMD_NONE, Undefined, 0 }, // CMD_NONE +#define DIAGNOSTIC_CATEGORY_INFO(F, D, P, C, V) { P, D, V }, + CM_FOR_EACH_DIAGNOSTIC_TABLE(UNUSED, DIAGNOSTIC_CATEGORY_INFO) +#undef DIAGNOSTIC_CATEGORY_INFO + }; + + //! convert an action identifier into a string + static cm::string_view GetActionString(DiagnosticAction); + + //! convert a category identifier into a string + static cm::string_view GetCategoryString(DiagnosticCategory); + + //! Convert a string action into an identifier + static cm::optional<DiagnosticAction> GetDiagnosticAction( + cm::string_view name); + + //! Convert a string category into an identifier + static cm::optional<DiagnosticCategory> GetDiagnosticCategory( + cm::string_view name); + + /** Represent a set of diagnostic category actions. */ + struct DiagnosticMap : public std::array<DiagnosticAction, CategoryCount> + { + DiagnosticMap() + : array{} {}; + }; +}; + +using cmDiagnosticCategory = cmDiagnostics::DiagnosticCategory; +using cmDiagnosticAction = cmDiagnostics::DiagnosticAction;
diff --git a/Source/cmDiscoverTestsCommand.cxx b/Source/cmDiscoverTestsCommand.cxx new file mode 100644 index 0000000..dc2e75d --- /dev/null +++ b/Source/cmDiscoverTestsCommand.cxx
@@ -0,0 +1,127 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmDiscoverTestsCommand.h" + +#include <cstddef> +#include <ostream> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cmext/string_view> + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmExecutionStatus.h" +#include "cmGeneratorExpression.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmScriptGenerator.h" +#include "cmTestDiscovery.h" +#include "cmTestGenerator.h" + +namespace { + +struct Arguments : cmTestDiscoveryArgs +{ + bool CommandExpandLists = false; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations; +}; + +class DiscoveryGenerator : public cmTestGenerator +{ +public: + DiscoveryGenerator(Arguments arguments, cmListFileBacktrace backtrace) + : cmTestGenerator(nullptr, arguments.Configurations) + , Args{ std::move(arguments) } + , Backtrace{ std::move(backtrace) } + { + } + +private: + void GenerateProperties(std::ostream& os, Indent indent, + std::vector<std::string> const& props, + std::string const& config, cmGeneratorExpression& ge) + { + for (std::size_t i = 0; i < props.size(); i += 2) { + os << '\n' + << indent << Quote(props[i]) << ' ' + << Quote(ge.Parse(props[i + 1])->Evaluate(this->LG, config)); + } + } + + void GenerateScriptForConfig(std::ostream& os, std::string const& config, + Indent indent) override + { + // Set up generator expression evaluation context. + cmGeneratorExpression ge(*this->LG->GetMakefile()->GetCMakeInstance(), + this->Backtrace); + + auto const in = indent.Next(); + os << indent << "discover_tests(COMMAND "; + this->GenerateCommand(os, this->Args.Command, config, + this->Args.CommandExpandLists, ge); + os << '\n' << in << "DISCOVERY_ARGS"; + for (auto const& arg : this->Args.DiscoveryArgs) { + os << ' ' << Quote(arg); + } + os << '\n' << in << "DISCOVERY_MATCH " << Quote(this->Args.DiscoveryMatch); + if (!this->Args.DiscoveryProperties.empty()) { + os << '\n' << in << "DISCOVERY_PROPERTIES"; + this->GenerateProperties(os, in.Next(), this->Args.DiscoveryProperties, + config, ge); + } + os << '\n' << in << "TEST_NAME " << Quote(this->Args.TestName); + os << '\n' << in << "TEST_ARGS"; + for (auto const& arg : this->Args.TestArgs) { + os << ' ' << Quote(arg); + } + os << '\n' << in << "TEST_PROPERTIES"; + this->GenerateProperties(os, in.Next(), this->Args.TestProperties, config, + ge); + os << '\n' << in.Next(); + this->GenerateBacktrace(os, this->Backtrace); + os << '\n' << in << ")\n"; + } + + Arguments Args; + cmListFileBacktrace Backtrace; +}; + +} // namespace + +bool cmDiscoverTestsCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + static auto const parser = + cmArgumentParser<Arguments>{ cmTestDiscoveryParser<Arguments>() } + .Bind("COMMAND_EXPAND_LISTS"_s, &Arguments::CommandExpandLists) + .Bind("CONFIGURATIONS"_s, &Arguments::Configurations); + + auto unparsed = std::vector<std::string>{}; + Arguments arguments = parser.Parse(args, &unparsed); + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; + } + + if (!unparsed.empty()) { + status.SetError(" given unknown argument \"" + unparsed.front() + "\"."); + return false; + } + + if (arguments.DiscoveryProperties.size() % 2 != 0) { + status.SetError(" DISCOVERY_PROPERTIES must be key-value pairs."); + return false; + } + + if (arguments.TestProperties.size() % 2 != 0) { + status.SetError(" TEST_PROPERTIES must be key-value pairs."); + return false; + } + + cmMakefile& mf = status.GetMakefile(); + mf.AddTestGenerator(cm::make_unique<DiscoveryGenerator>(std::move(arguments), + mf.GetBacktrace())); + return true; +}
diff --git a/Source/cmDiscoverTestsCommand.h b/Source/cmDiscoverTestsCommand.h new file mode 100644 index 0000000..a41345a --- /dev/null +++ b/Source/cmDiscoverTestsCommand.h
@@ -0,0 +1,13 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +class cmExecutionStatus; + +bool cmDiscoverTestsCommand(std::vector<std::string> const& args, + cmExecutionStatus& status);
diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx index 66e3730..4ad66a7 100644 --- a/Source/cmDocumentation.cxx +++ b/Source/cmDocumentation.cxx
@@ -26,7 +26,7 @@ #include "cmVersion.h" namespace { -cmDocumentationEntry const cmDocumentationStandardOptions[21] = { +cmDocumentationEntry const cmDocumentationStandardOptions[] = { { "-h,-H,--help,-help,-usage,/?", "Print usage information and exit." }, { "--version[=json-v1],-version[=json-v1],/V[=json-v1],/version[=json-v1] " "[<file>]", @@ -39,6 +39,12 @@ { "--help-command-list [<file>]", "List commands with help available and exit." }, { "--help-commands [<file>]", "Print cmake-commands manual and exit." }, + { "--help-diagnostic <diag> [<file>]", + "Print help for one diagnostic and exit." }, + { "--help-diagnostic-list [<file>]", + "List diagnostics with help available and exit." }, + { "--help-diagnostics [<file>]", + "Print cmake-diagnostics manual and exit." }, { "--help-module <mod> [<file>]", "Print help for one module and exit." }, { "--help-module-list [<file>]", "List modules with help available and exit." }, @@ -164,6 +170,8 @@ return this->PrintHelpOneManual(os); case cmDocumentation::OneCommand: return this->PrintHelpOneCommand(os); + case cmDocumentation::OneDiagnostic: + return this->PrintHelpOneDiagnostic(os); case cmDocumentation::OneModule: return this->PrintHelpOneModule(os); case cmDocumentation::OnePolicy: @@ -176,6 +184,8 @@ return this->PrintHelpListManuals(os); case cmDocumentation::ListCommands: return this->PrintHelpListCommands(os); + case cmDocumentation::ListDiagnostics: + return this->PrintHelpListDiagnostics(os); case cmDocumentation::ListModules: return this->PrintHelpListModules(os); case cmDocumentation::ListProperties: @@ -377,6 +387,11 @@ cmSystemTools::Message( "Warning: --help-compatcommands no longer supported"); return true; + } else if (strcmp(argv[i], "--help-diagnostics") == 0) { + help.HelpType = cmDocumentation::OneManual; + help.Argument = "cmake-diagnostics.7"; + i += int(get_opt_argument(i + 1, help.Filename)); + this->WarnFormFromFilename(help, result); } else if (strcmp(argv[i], "--help-full") == 0) { help.HelpType = cmDocumentation::Full; i += int(get_opt_argument(i + 1, help.Filename)); @@ -393,6 +408,12 @@ i += int(get_opt_argument(i + 1, help.Filename)); help.Argument = cmSystemTools::LowerCase(help.Argument); this->WarnFormFromFilename(help, result); + } else if (strcmp(argv[i], "--help-diagnostic") == 0) { + help.HelpType = cmDocumentation::OneDiagnostic; + i += int(get_opt_argument(i + 1, help.Argument)); + i += int(get_opt_argument(i + 1, help.Filename)); + help.Argument = cmSystemTools::UpperCase(help.Argument); + this->WarnFormFromFilename(help, result); } else if (strcmp(argv[i], "--help-module") == 0) { help.HelpType = cmDocumentation::OneModule; i += int(get_opt_argument(i + 1, help.Argument)); @@ -421,6 +442,9 @@ } else if (strcmp(argv[i], "--help-command-list") == 0) { help.HelpType = cmDocumentation::ListCommands; i += int(get_opt_argument(i + 1, help.Filename)); + } else if (strcmp(argv[i], "--help-diagnostic-list") == 0) { + help.HelpType = cmDocumentation::ListDiagnostics; + i += int(get_opt_argument(i + 1, help.Filename)); } else if (strcmp(argv[i], "--help-module-list") == 0) { help.HelpType = cmDocumentation::ListModules; i += int(get_opt_argument(i + 1, help.Filename)); @@ -622,6 +646,25 @@ return true; } +bool cmDocumentation::PrintHelpOneDiagnostic(std::ostream& os) +{ + std::string dname = cmSystemTools::UpperCase(this->CurrentArgument); + if (this->PrintFiles(os, cmStrCat("diagnostic/", dname))) { + return true; + } + // Argument was not a command. Complain. + os << "Argument \"" << this->CurrentArgument + << "\" to --help-command is not a CMake diagnostic. " + "Use --help-diagnostic-list to see all diagnostics.\n"; + return false; +} + +bool cmDocumentation::PrintHelpListDiagnostics(std::ostream& os) +{ + this->PrintNames(os, "diagnostic/*"); + return true; +} + bool cmDocumentation::PrintHelpOneModule(std::ostream& os) { std::string mname = this->CurrentArgument; @@ -704,6 +747,11 @@ if (this->PrintFiles(os, cmStrCat("variable/", vname))) { return true; } + std::string const generalized = GeneralizeKeyword(vname); + if (generalized != vname && + this->PrintFiles(os, cmStrCat("variable/", generalized))) { + return true; + } // Argument was not a variable. Complain. os << "Argument \"" << this->CurrentArgument << "\" to --help-variable is not a defined variable. "
diff --git a/Source/cmDocumentation.h b/Source/cmDocumentation.h index dd0fadb..e23cee9 100644 --- a/Source/cmDocumentation.h +++ b/Source/cmDocumentation.h
@@ -30,6 +30,7 @@ Full, ListManuals, ListCommands, + ListDiagnostics, ListModules, ListProperties, ListVariables, @@ -38,6 +39,7 @@ OneArbitrary, OneManual, OneCommand, + OneDiagnostic, OneModule, OneProperty, OneVariable, @@ -124,12 +126,14 @@ bool PrintHelpOneArbitrary(std::ostream& os); bool PrintHelpOneManual(std::ostream& os); bool PrintHelpOneCommand(std::ostream& os); + bool PrintHelpOneDiagnostic(std::ostream& os); bool PrintHelpOneModule(std::ostream& os); bool PrintHelpOnePolicy(std::ostream& os); bool PrintHelpOneProperty(std::ostream& os); bool PrintHelpOneVariable(std::ostream& os); bool PrintHelpListManuals(std::ostream& os); bool PrintHelpListCommands(std::ostream& os); + bool PrintHelpListDiagnostics(std::ostream& os); bool PrintHelpListModules(std::ostream& os); bool PrintHelpListProperties(std::ostream& os); bool PrintHelpListVariables(std::ostream& os);
diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx index 2a17925..24e43d1 100644 --- a/Source/cmDyndepCollation.cxx +++ b/Source/cmDyndepCollation.cxx
@@ -20,10 +20,11 @@ #include "cmCxxModuleMetadata.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSet.h" -#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGenExContext.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" // IWYU pragma: keep +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmInstallCxxModuleBmiGenerator.h" @@ -56,7 +57,6 @@ cm::GenEx::Context const context(gt->LocalGenerator, config); TdiSourceInfo info; cmTarget const* tgt = gt->Target; - auto all_file_sets = tgt->GetAllFileSetNames(); Json::Value& tdi_sources = info.Sources = Json::objectValue; Json::Value& tdi_cxx_module_info = info.CxxModules = Json::objectValue; @@ -93,32 +93,20 @@ } } - for (auto const& file_set_name : all_file_sets) { - auto const* file_set = tgt->GetFileSet(file_set_name); - if (!file_set) { - gt->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), - "\" is tracked to have file set \"", - file_set_name, - "\", but it was not found.")); - continue; - } + for (auto const* file_set : gt->GetAllFileSets()) { auto fs_type = file_set->GetType(); // We only care about C++ module sources here. - if (fs_type != "CXX_MODULES"_s) { + if (fs_type != cm::FileSetMetadata::CXX_MODULES) { + continue; + } + // Synthetic (BMI-only) targets do not build private C++ modules. + if (tgt->IsSynthetic() && + file_set->GetVisibility() == + cm::FileSetMetadata::Visibility::Private) { continue; } - auto fileEntries = file_set->CompileFileEntries(); - auto directoryEntries = file_set->CompileDirectoryEntries(); - - auto directories = - file_set->EvaluateDirectoryEntries(directoryEntries, context, gt); - std::map<std::string, std::vector<std::string>> files_per_dirs; - for (auto const& entry : fileEntries) { - file_set->EvaluateFileEntry(directories, files_per_dirs, entry, context, - gt); - } + auto files_per_dirs = file_set->GetFiles(context, gt); Json::Value fs_dest = Json::nullValue; for (auto const& ig : gt->Makefile->GetInstallGenerators()) { @@ -131,32 +119,20 @@ } } - // Detect duplicate sources. - std::set<std::string> visited_sources; - - for (auto const& files_per_dir : files_per_dirs) { + for (auto const& files_per_dir : files_per_dirs.first) { for (auto const& file : files_per_dir.second) { auto const full_file = cmSystemTools::CollapseFullPath(file); auto lookup = sf_map.find(full_file); if (lookup == sf_map.end()) { - if (visited_sources.count(full_file)) { - // Duplicate source; raise an author warning. - gt->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat( - "Target \"", tgt->GetName(), "\" has source file\n ", file, - "\nin a \"FILE_SET TYPE CXX_MODULES\" multiple times.")); - continue; - } - gt->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), "\" has source file\n ", - file, - "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not " - "scheduled for compilation.")); + gt->Makefile->IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" has source file\n ", file, + "\nin a \"FILE_SET TYPE ", + cm::FileSetMetadata::CXX_MODULES, + "\" but it is not " + "scheduled for compilation.")); continue; } - visited_sources.insert(full_file); auto const* sf = lookup->second.first; CompileType const ct = lookup->second.second; @@ -206,8 +182,8 @@ tdi_module_info["relative-directory"] = files_per_dir.first; tdi_module_info["name"] = file_set->GetName(); tdi_module_info["type"] = file_set->GetType(); - tdi_module_info["visibility"] = - std::string(cmFileSetVisibilityToName(file_set->GetVisibility())); + tdi_module_info["visibility"] = std::string( + cm::FileSetMetadata::VisibilityToName(file_set->GetVisibility())); tdi_module_info["destination"] = fs_dest; } } @@ -398,7 +374,8 @@ std::string RelativeDirectory; std::string SourcePath; std::string Type; - cmFileSetVisibility Visibility = cmFileSetVisibility::Private; + cm::FileSetMetadata::Visibility Visibility = + cm::FileSetMetadata::Visibility::Private; cm::optional<std::string> Destination; std::vector<std::string> IncludeDirectories; std::vector<std::string> Definitions; @@ -521,7 +498,7 @@ } fsi.SourcePath = tdi_cxx_module_info["source"].asString(); fsi.Type = tdi_cxx_module_info["type"].asString(); - fsi.Visibility = cmFileSetVisibilityFromName( + fsi.Visibility = cm::FileSetMetadata::VisibilityFromName( tdi_cxx_module_info["visibility"].asString(), nullptr); auto const& tdi_fs_dest = tdi_cxx_module_info["destination"]; if (tdi_fs_dest.isString()) { @@ -671,8 +648,8 @@ cmSystemTools::Error( cmStrCat("Output ", object.PrimaryOutput, " provides the `", provides.LogicalName, - "` module but it is not found in a `FILE_SET` of type " - "`CXX_MODULES`")); + "` module but it is not found in a `FILE_SET` of type `", + cm::FileSetMetadata::CXX_MODULES, '`')); result = false; } @@ -712,12 +689,13 @@ } // Verify the fileset type for the object. - if (file_set.Type == "CXX_MODULES"_s) { + if (file_set.Type == cm::FileSetMetadata::CXX_MODULES) { if (!has_provides) { - cmSystemTools::Error( - cmStrCat("Output ", object.PrimaryOutput, - " is of type `CXX_MODULES` but does not provide a module " - "interface unit or partition")); + cmSystemTools::Error(cmStrCat("Output ", object.PrimaryOutput, + " is of type `", + cm::FileSetMetadata::CXX_MODULES, + "` but does not provide a module " + "interface unit or partition")); result = false; continue; } @@ -726,10 +704,11 @@ } else { if (has_provides) { auto const& provides = object.Provides[0]; - cmSystemTools::Error(cmStrCat( - "Source ", file_set.SourcePath, " provides the `", - provides.LogicalName, "` C++ module but is of type `", file_set.Type, - "` module but must be of type `CXX_MODULES`")); + cmSystemTools::Error( + cmStrCat("Source ", file_set.SourcePath, " provides the `", + provides.LogicalName, "` C++ module but is of type `", + file_set.Type, "` module but must be of type `", + cm::FileSetMetadata::CXX_MODULES, '`')); result = false; } @@ -737,7 +716,7 @@ continue; } - if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) { + if (!cm::FileSetMetadata::VisibilityIsForInterface(file_set.Visibility)) { // Nothing needs to be conveyed about non-`PUBLIC` modules. for (auto const& p : object.Provides) { private_modules.insert(p.LogicalName); @@ -921,7 +900,7 @@ return false; } auto const& file_set = fileset_info_itr->second; - return !cmFileSetVisibilityIsForInterface(file_set.Visibility); + return !cm::FileSetMetadata::VisibilityIsForInterface(file_set.Visibility); } bool cmDyndepCollation::IsBmiOnly(cmCxxModuleExportInfo const& exportInfo,
diff --git a/Source/cmELF.cxx b/Source/cmELF.cxx index 228e539..b9c333d 100644 --- a/Source/cmELF.cxx +++ b/Source/cmELF.cxx
@@ -22,6 +22,11 @@ #include "cmelf/elf64.h" #include "cmelf/elf_common.h" +// Maximum reasonable values to prevent DoS/OOM from malformed files. +static constexpr std::size_t kMaxSections = 65536; +static constexpr std::size_t kMaxSectionSize = 1024 * 1024 * 256; +static constexpr std::size_t kMaxDynamicEntries = 1024 * 1024; + // Low-level byte swapping implementation. template <size_t s> struct cmELFByteSwapSize @@ -216,8 +221,15 @@ // Return the number of sections as specified by the ELF header. std::size_t GetNumberOfSections() const override { - return static_cast<unsigned int>(this->ELFHeader.e_shnum + - this->SectionHeaders[0].sh_size); + std::size_t count = this->ELFHeader.e_shnum; + if (!this->SectionHeaders.empty()) { + count += static_cast<std::size_t>(this->SectionHeaders[0].sh_size); + } + // Prevent OOM from malformed files. + if (count > kMaxSections) { + return kMaxSections; + } + return count; } // Get the file position of a dynamic section entry. @@ -452,6 +464,11 @@ // Load the section headers. std::size_t const minSections = 1; std::size_t numSections = this->ELFHeader.e_shnum; + // Prevent OOM from malformed files. + if (numSections > kMaxSections) { + this->SetErrorMessage("ELF file has too many sections."); + return; + } this->SectionHeaders.resize(std::max(numSections, minSections)); if (!this->LoadSectionHeader(0)) { return; @@ -484,12 +501,23 @@ return false; } + // Prevent OOM from malformed files. + if (sec.sh_size > kMaxSectionSize) { + this->SetErrorMessage("DYNAMIC section is too large."); + return false; + } + // Allocate the dynamic section entries. - int n = static_cast<int>(sec.sh_size / sec.sh_entsize); + std::size_t n = static_cast<int>(sec.sh_size / sec.sh_entsize); + // Prevent OOM from malformed files. + if (n > kMaxDynamicEntries) { + this->SetErrorMessage("DYNAMIC section has too many entries."); + return false; + } this->DynamicSectionEntries.resize(n); // Read each entry. - for (int j = 0; j < n; ++j) { + for (std::size_t j = 0; j < n; ++j) { // Seek to the beginning of the section entry. this->Stream->seekg(sec.sh_offset + sec.sh_entsize * j); ELF_Dyn& dyn = this->DynamicSectionEntries[j];
diff --git a/Source/cmEnableLanguageCommand.cxx b/Source/cmEnableLanguageCommand.cxx index 96a845b..425be31 100644 --- a/Source/cmEnableLanguageCommand.cxx +++ b/Source/cmEnableLanguageCommand.cxx
@@ -2,6 +2,7 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmEnableLanguageCommand.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -20,8 +21,8 @@ if (!mf.IsNormalDefinitionSet("PROJECT_NAME")) { switch (mf.GetPolicyStatus(cmPolicies::CMP0165)) { case cmPolicies::WARN: - mf.IssueMessage( - MessageType::AUTHOR_WARNING, + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, "project() should be called prior to this enable_language() call."); break; case cmPolicies::OLD:
diff --git a/Source/cmEnvironment.cxx b/Source/cmEnvironment.cxx new file mode 100644 index 0000000..af5a47c --- /dev/null +++ b/Source/cmEnvironment.cxx
@@ -0,0 +1,229 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmEnvironment.h" + +#include <set> +#include <sstream> +#include <utility> + +#include <cm/string_view> +#include <cm/vector> +#include <cmext/algorithm> + +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +#if defined(_WIN32) +# include "cmsys/String.h" +#endif + +bool cmEnvironment::EnvNameLess::operator()(std::string const& lhs, + std::string const& rhs) const +{ +#if defined(_WIN32) + // Environment variable names are case-insensitive on Windows + return cmsysString_strcasecmp(lhs.c_str(), rhs.c_str()) < 0; +#else + return lhs < rhs; +#endif +} + +cmEnvironment::cmEnvironment(std::vector<std::string> const& env) +{ + for (std::string const& var : env) { + this->PutEnv(var); + } +} + +void cmEnvironment::PutEnv(std::string const& env) +{ + auto const pos = env.find('='); + if (pos != std::string::npos) { + this->Map[env.substr(0, pos)] = env.substr(pos + 1); + } else { + this->Map[env] = cm::nullopt; + } +} + +void cmEnvironment::UnPutEnv(std::string const& env) +{ + this->Map[env] = cm::nullopt; +} + +void cmEnvironment::Update(cmEnvironment&& other) +{ + for (auto& kv : other.Map) { + this->Map[kv.first] = std::move(kv.second); + } + other.Map.clear(); +} + +std::vector<std::string> cmEnvironment::GetVariables() const +{ + auto result = std::vector<std::string>{}; + result.reserve(this->Map.size()); + for (auto const& elem : this->Map) { + if (elem.second) { + result.push_back(elem.first + '=' + *elem.second); + } + } + return result; +} + +std::string cmEnvironment::RecordDifference( + cmEnvironment const& original) const +{ + cm::string_view nl; + std::ostringstream os; + for (auto const& elem : this->Map) { + if (!elem.second) { + // Signify that this variable is being actively unset + os << nl << '#' << elem.first << '='; + nl = "\n"; + continue; + } + auto const it = original.Map.find(elem.first); + if (it != original.Map.end() && *elem.second == it->second) { + // Skip variables that are unchanged + continue; + } + os << nl << elem.first << '=' << *elem.second; + nl = "\n"; + } + return os.str(); +} + +namespace { + +auto const ValidOperators = std::set<cm::string_view>{ + "set", + "unset", + "string_append", + "string_prepend", + "path_list_append", + "path_list_prepend", + "cmake_list_append", + "cmake_list_prepend", +}; + +struct ListAppend +{ +public: + ListAppend(std::string val, char sep) + : value(std::move(val)) + , separator(sep) + { + } + + void operator()(std::string& output) const + { + if (!output.empty()) { + output += separator; + } + output += value; + } + +private: + std::string value; + char separator; +}; + +struct ListPrepend +{ +public: + ListPrepend(std::string val, char sep) + : value(std::move(val)) + , separator(sep) + { + } + + void operator()(std::string& output) const + { + if (!output.empty()) { + output.insert(output.begin(), separator); + } + output.insert(0, value); + } + +private: + std::string value; + char separator; +}; + +} // namespace + +bool cmEnvironmentModification::Add(std::vector<std::string> const& envmod) +{ + bool ok = true; + for (auto const& entry : envmod) { + ok &= this->Add(entry); + } + return ok; +} + +bool cmEnvironmentModification::Add(std::string const& envmod) +{ + // Split on `=` + auto const eq_loc = envmod.find_first_of('='); + if (eq_loc == std::string::npos) { + cmSystemTools::Error(cmStrCat( + "Error: Missing `=` after the variable name in: ", envmod, '\n')); + return false; + } + + // Split operation on `:` + auto const op_value_start = eq_loc + 1; + auto const colon_loc = envmod.find_first_of(':', op_value_start); + if (colon_loc == std::string::npos) { + cmSystemTools::Error( + cmStrCat("Error: Missing `:` after the operation in: ", envmod, '\n')); + return false; + } + + auto entry = Entry{}; + entry.Name = envmod.substr(0, eq_loc); + entry.Op = envmod.substr(op_value_start, colon_loc - op_value_start); + entry.Value = envmod.substr(colon_loc + 1); + + if (entry.Op == "reset") { + cm::erase_if(this->Entries, + [&entry](Entry const& e) { return e.Name == entry.Name; }); + return true; + } + + if (!cm::contains(ValidOperators, entry.Op)) { + cmSystemTools::Error(cmStrCat( + "Error: Unrecognized environment manipulation argument: ", entry.Op, + '\n')); + return false; + } + + this->Entries.push_back(std::move(entry)); + return true; +} + +void cmEnvironmentModification::ApplyTo(cmEnvironment& env) +{ + char const path_sep = cmSystemTools::GetSystemPathlistSeparator(); + + for (auto const& e : this->Entries) { + if (e.Op == "set") { + env.PutEnv(e.Name + "=" + e.Value); + } else if (e.Op == "unset") { + env.UnPutEnv(e.Name); + } else if (e.Op == "string_append") { + env.Modify(e.Name, [&e](std::string& output) { output += e.Value; }); + } else if (e.Op == "string_prepend") { + env.Modify(e.Name, + [&e](std::string& output) { output.insert(0, e.Value); }); + } else if (e.Op == "path_list_append") { + env.Modify(e.Name, ListAppend(e.Value, path_sep)); + } else if (e.Op == "path_list_prepend") { + env.Modify(e.Name, ListPrepend(e.Value, path_sep)); + } else if (e.Op == "cmake_list_append") { + env.Modify(e.Name, ListAppend(e.Value, ';')); + } else if (e.Op == "cmake_list_prepend") { + env.Modify(e.Name, ListPrepend(e.Value, ';')); + } + } +}
diff --git a/Source/cmEnvironment.h b/Source/cmEnvironment.h new file mode 100644 index 0000000..806015f --- /dev/null +++ b/Source/cmEnvironment.h
@@ -0,0 +1,76 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <map> +#include <string> +#include <vector> + +#include <cm/optional> + +/** + * Helper class to represent an environment. + */ +class cmEnvironment +{ +public: + /** Initialize from `NAME=VALUE` strings. */ + cmEnvironment(std::vector<std::string> const& env = {}); + + /** + * Add a single variable (or remove if no = sign) to the current + * environment. + */ + void PutEnv(std::string const& env); + + /** Remove a single variable from the current environment. */ + void UnPutEnv(std::string const& env); + + /** + * Move the environment entries from `other` into this one, overwriting + * existing variables when a key is already present. */ + void Update(cmEnvironment&& other); + + /** Modify the value of a variable in-place using a transform function. */ + template <typename F> + void Modify(std::string const& name, F&& transform) + { + cm::optional<std::string>& val = this->Map[name]; + if (!val.has_value()) { + val = std::string{}; + } + transform(*val); + } + + std::vector<std::string> GetVariables() const; + + std::string RecordDifference(cmEnvironment const& original) const; + +protected: + struct EnvNameLess + { + bool operator()(std::string const& lhs, std::string const& rhs) const; + }; + std::map<std::string, cm::optional<std::string>, EnvNameLess> Map; +}; + +class cmEnvironmentModification +{ +public: + bool Add(std::string const& envmod); + bool Add(std::vector<std::string> const& envmod); + + void ApplyTo(cmEnvironment& env); + +private: + struct Entry + { + std::string Name; + std::string Value; + std::string Op; + }; + + std::vector<Entry> Entries; +};
diff --git a/Source/cmEvaluatedTargetProperty.cxx b/Source/cmEvaluatedTargetProperty.cxx index f786985..28b3faf 100644 --- a/Source/cmEvaluatedTargetProperty.cxx +++ b/Source/cmEvaluatedTargetProperty.cxx
@@ -7,12 +7,15 @@ #include "cmGenExContext.h" #include "cmGenExEvaluation.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorTarget.h" #include "cmLinkItem.h" #include "cmList.h" +#include "cmTargetPropertyEntry.h" struct cmGeneratorExpressionDAGChecker; +namespace cm { EvaluatedTargetPropertyEntry::EvaluatedTargetPropertyEntry( cmLinkItem const& item, cmListFileBacktrace bt) : LinkItem(item) @@ -22,8 +25,7 @@ EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry( cmGeneratorTarget const* thisTarget, cm::GenEx::Context const& context, - cmGeneratorExpressionDAGChecker* dagChecker, - cmGeneratorTarget::TargetPropertyEntry& entry) + cmGeneratorExpressionDAGChecker* dagChecker, cm::TargetPropertyEntry& entry) { EvaluatedTargetPropertyEntry ee(entry.LinkItem, entry.GetBacktrace()); cmExpandList(entry.Evaluate(context, thisTarget, dagChecker), ee.Values); @@ -36,8 +38,7 @@ EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries( cmGeneratorTarget const* thisTarget, cm::GenEx::Context const& context, cmGeneratorExpressionDAGChecker* dagChecker, - std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const& - in) + std::vector<std::unique_ptr<cm::TargetPropertyEntry>> const& in) { EvaluatedTargetPropertyEntries out; out.Entries.reserve(in.size()); @@ -73,6 +74,31 @@ } } } + +void addInterfaceFileSetsEntry(cmGeneratorTarget const* headTarget, + cm::string_view type, std::string const& prop, + cm::GenEx::Context const& context, + cmGeneratorExpressionDAGChecker* dagChecker, + EvaluatedTargetPropertyEntries& entries, + std::vector<cmLinkItem> const& libraries) +{ + for (cmLinkItem const& lib : libraries) { + if (lib.Target) { + EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace); + // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our + // caller's property and hand-evaluate it as if it were compiled. + // Create a context as cmCompiledGeneratorExpression::Evaluate does. + cm::GenEx::Evaluation eval(context, false, headTarget, headTarget, true, + lib.Backtrace); + cmExpandList( + lib.Target->GetGeneratorFileSets()->EvaluateInterfaceProperty( + type, prop, &eval, dagChecker), + ee.Values); + ee.ContextDependent = eval.HadContextSensitiveCondition; + entries.Entries.emplace_back(std::move(ee)); + } + } +} } void AddInterfaceEntries(cmGeneratorTarget const* headTarget, @@ -108,3 +134,19 @@ } } } + +void AddInterfaceFileSetsEntries(cmGeneratorTarget const* headTarget, + cm::string_view type, std::string const& prop, + cm::GenEx::Context const& context, + cmGeneratorExpressionDAGChecker* dagChecker, + EvaluatedTargetPropertyEntries& entries) +{ + if (cmLinkImplementationLibraries const* impl = + headTarget->GetLinkImplementationLibraries( + context.Config, cmGeneratorTarget::UseTo::Compile)) { + entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition; + addInterfaceFileSetsEntry(headTarget, type, prop, context, dagChecker, + entries, impl->Libraries); + } +} +}
diff --git a/Source/cmEvaluatedTargetProperty.h b/Source/cmEvaluatedTargetProperty.h index 1ffbec4..218793a 100644 --- a/Source/cmEvaluatedTargetProperty.h +++ b/Source/cmEvaluatedTargetProperty.h
@@ -6,10 +6,14 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmGeneratorTarget.h" #include "cmListFileCache.h" namespace cm { +class TargetPropertyEntry; + namespace GenEx { struct Context; } @@ -18,6 +22,7 @@ class cmLinkItem; struct cmGeneratorExpressionDAGChecker; +namespace cm { // Represent a target property entry after evaluating generator expressions // and splitting up lists. struct EvaluatedTargetPropertyEntry @@ -40,8 +45,7 @@ EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry( cmGeneratorTarget const* thisTarget, cm::GenEx::Context const& context, - cmGeneratorExpressionDAGChecker* dagChecker, - cmGeneratorTarget::TargetPropertyEntry& entry); + cmGeneratorExpressionDAGChecker* dagChecker, cm::TargetPropertyEntry& entry); struct EvaluatedTargetPropertyEntries { @@ -52,8 +56,7 @@ EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries( cmGeneratorTarget const* thisTarget, cm::GenEx::Context const& context, cmGeneratorExpressionDAGChecker* dagChecker, - std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const& - in); + std::vector<std::unique_ptr<cm::TargetPropertyEntry>> const& in); // IncludeRuntimeInterface is used to break the cycle in computing // the necessary transitive dependencies of targets that can occur @@ -82,3 +85,10 @@ EvaluatedTargetPropertyEntries& entries, IncludeRuntimeInterface searchRuntime, cmGeneratorTarget::UseTo usage = cmGeneratorTarget::UseTo::Compile); + +void AddInterfaceFileSetsEntries(cmGeneratorTarget const* headTarget, + cm::string_view type, std::string const& prop, + cm::GenEx::Context const& context, + cmGeneratorExpressionDAGChecker* dagChecker, + EvaluatedTargetPropertyEntries& entries); +}
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 63dcb60..9a2c7c0 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx
@@ -27,6 +27,9 @@ #include "cmsys/String.h" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmDiagnostics.h" +#include "cmEnvironment.h" #include "cmExecutionStatus.h" #include "cmList.h" #include "cmMakefile.h" @@ -94,6 +97,8 @@ bool EchoOutputVariable = false; bool EchoErrorVariable = false; cm::optional<std::string> Encoding; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Environment; + ArgumentParser::MaybeEmpty<std::vector<std::string>> EnvModification; std::string CommandErrorIsFatal; }; @@ -117,6 +122,8 @@ .Bind("ERROR_STRIP_TRAILING_WHITESPACE"_s, &Arguments::ErrorStripTrailingWhitespace) .Bind("ENCODING"_s, &Arguments::Encoding) + .Bind("ENVIRONMENT"_s, &Arguments::Environment) + .Bind("ENVIRONMENT_MODIFICATION"_s, &Arguments::EnvModification) .Bind("ECHO_OUTPUT_VARIABLE"_s, &Arguments::EchoOutputVariable) .Bind("ECHO_ERROR_VARIABLE"_s, &Arguments::EchoErrorVariable) .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal); @@ -213,6 +220,23 @@ builder.SetWorkingDirectory(arguments.WorkingDirectory); } + if (!arguments.Environment.empty() || !arguments.EnvModification.empty()) { +#ifndef CMAKE_BOOTSTRAP + auto diff = cmEnvironmentModification{}; + if (!diff.Add(arguments.EnvModification)) { + return false; + } + auto env = cmEnvironment{ cmSystemTools::GetEnvironmentVariables() }; + env.Update(arguments.Environment); + diff.ApplyTo(env); + builder.SetEnvironment(env.GetVariables()); +#else + status.SetError( + "does not support environment modification in bootstrap builds"); + return false; +#endif + } + // Check the output variables. std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose); if (!inputFilename.empty()) { @@ -347,8 +371,8 @@ cmProcessOutput::FindEncoding(*arguments.Encoding)) { encoding = *maybeEncoding; } else { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("ENCODING option given unknown value \"", *arguments.Encoding, "\". Ignoring.")); }
diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx index acf8e5c..cdfa405 100644 --- a/Source/cmExperimental.cxx +++ b/Source/cmExperimental.cxx
@@ -7,9 +7,9 @@ #include <cstddef> #include <string> +#include "cmDiagnostics.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmValue.h" @@ -31,7 +31,7 @@ cmExperimental::TryCompileCondition::Always }, // CxxImportStd { "CxxImportStd", - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b", + "f35a9ac6-8463-4d38-8eec-5d6008153e7d", "CMAKE_EXPERIMENTAL_CXX_IMPORT_STD", "CMake's support for `import std;` in C++23 and newer is experimental. It " "is meant only for experimentation and feedback to CMake developers.", @@ -48,14 +48,14 @@ cmExperimental::TryCompileCondition::Always }, // ExportBuildDatabase { "ExportBuildDatabase", - "73194a1d-c0b5-41b9-9190-a4512925e192", + "70ef007e-b743-492d-9407-e35eeac03a40", "CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE", "CMake's support for exporting build databases is experimental. It is " "meant only for experimentation and feedback to CMake developers.", {}, cmExperimental::TryCompileCondition::Never }, { "GenerateSbom", - "ca494ed3-b261-4205-a01f-603c95e4cae0", + "2d856d6d-53e8-488b-a17f-d486d2cac317", "CMAKE_EXPERIMENTAL_GENERATE_SBOM", "CMake's support for generating software bill of materials (Sbom) " "information in SPDX format is experimental. It is meant only for " @@ -64,7 +64,7 @@ cmExperimental::TryCompileCondition::Never }, // Rust support { "Rust", - "3cc9b32c-47d3-4056-8953-d74e69fc0d6c", + "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1", "CMAKE_EXPERIMENTAL_RUST", "CMake's support for the Rust programming language is experimental. " "It is meant only for experimentation and feedback to CMake developers.", @@ -111,10 +111,10 @@ if (mf.GetGlobalGenerator()->ShouldWarnExperimental(data.Name, *value)) { if (enabled) { - mf.IssueMessage(MessageType::AUTHOR_WARNING, data.Description); + mf.IssueDiagnostic(cmDiagnostics::CMD_EXPERIMENTAL, data.Description); } else { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, + mf.IssueDiagnostic( + cmDiagnostics::CMD_EXPERIMENTAL, cmStrCat( data.Variable, " is set to incorrect value\n ", value, "\n"
diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx index 8650d49..8fe57bb 100644 --- a/Source/cmExportBuildAndroidMKGenerator.cxx +++ b/Source/cmExportBuildAndroidMKGenerator.cxx
@@ -4,8 +4,10 @@ #include <functional> #include <sstream> +#include <utility> #include <vector> +#include "cmDiagnosticContext.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmStateTypes.h" @@ -13,7 +15,11 @@ #include "cmSystemTools.h" #include "cmTarget.h" -cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator() = default; +cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator( + cmDiagnosticContext context) + : cmExportBuildFileGenerator(std::move(context)) +{ +} bool cmExportBuildAndroidMKGenerator::GenerateMainFile(std::ostream& os) {
diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h index a754258..e937322 100644 --- a/Source/cmExportBuildAndroidMKGenerator.h +++ b/Source/cmExportBuildAndroidMKGenerator.h
@@ -11,6 +11,8 @@ #include "cmExportBuildFileGenerator.h" #include "cmStateTypes.h" +class cmDiagnosticContext; + /** \class cmExportBuildAndroidMKGenerator * \brief Generate a file exporting targets from a build tree. * @@ -25,7 +27,7 @@ , public cmExportAndroidMKGenerator { public: - cmExportBuildAndroidMKGenerator(); + cmExportBuildAndroidMKGenerator(cmDiagnosticContext context); /** Set whether to append generated code to the output file. */ void SetAppendMode(bool append) { this->AppendMode = append; }
diff --git a/Source/cmExportBuildCMakeConfigGenerator.cxx b/Source/cmExportBuildCMakeConfigGenerator.cxx index b163eae..9b131c6 100644 --- a/Source/cmExportBuildCMakeConfigGenerator.cxx +++ b/Source/cmExportBuildCMakeConfigGenerator.cxx
@@ -2,36 +2,33 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmExportBuildCMakeConfigGenerator.h" -#include <algorithm> #include <cstddef> #include <functional> #include <map> -#include <memory> +#include <ostream> #include <set> -#include <sstream> #include <utility> #include <vector> -#include <cm/string_view> -#include <cmext/string_view> - #include "cmCryptoHash.h" +#include "cmDiagnosticContext.h" #include "cmExportSet.h" -#include "cmFileSet.h" #include "cmGenExContext.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" -cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator() +cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator( + cmDiagnosticContext context) + : cmExportBuildFileGenerator(std::move(context)) { this->LG = nullptr; this->ExportSet = nullptr; @@ -73,12 +70,17 @@ return false; } + ImportFileSetPropertyMap fsProperties; + if (!this->PopulateFileSetInterfaceProperties(gte, fsProperties)) { + return false; + } + this->PopulateInterfaceLinkLibrariesProperty( gte, cmGeneratorExpression::BuildInterface, properties); this->GenerateInterfaceProperties(gte, os, properties); - this->GenerateTargetFileSets(gte, os); + this->GenerateTargetFileSets(gte, os, fsProperties); } std::string cxx_modules_name; @@ -160,47 +162,21 @@ } } -namespace { -bool EntryIsContextSensitive( - std::unique_ptr<cmCompiledGeneratorExpression> const& cge) -{ - return cge->GetHadContextSensitiveCondition(); -} -} - std::string cmExportBuildCMakeConfigGenerator::GetFileSetDirectories( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/) + cmGeneratorTarget* gte, cmGeneratorFileSet const* fileSet, + cmTargetExport const* /*te*/) { std::vector<std::string> resultVector; auto configs = gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - auto directoryEntries = fileSet->CompileDirectoryEntries(); for (auto const& config : configs) { cm::GenEx::Context context(gte->LocalGenerator, config); - auto directories = - fileSet->EvaluateDirectoryEntries(directoryEntries, context, gte); + auto directories = fileSet->GetDirectories(context, gte); + bool const contextSensitive = directories.second; - bool const contextSensitive = - std::any_of(directoryEntries.begin(), directoryEntries.end(), - EntryIsContextSensitive); - - auto const& type = fileSet->GetType(); - // C++ modules do not support interface file sets which are dependent upon - // the configuration. - if (contextSensitive && type == "CXX_MODULES"_s) { - auto* mf = this->LG->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive base directory entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return std::string{}; - } - - for (auto const& directory : directories) { + for (auto const& directory : directories.first) { auto dest = cmOutputConverter::EscapeForCMake( directory, cmOutputConverter::WrapQuotes::NoWrap); @@ -217,47 +193,21 @@ } std::string cmExportBuildCMakeConfigGenerator::GetFileSetFiles( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/) + cmGeneratorTarget* gte, cmGeneratorFileSet const* fileSet, + cmTargetExport const* /*te*/) { std::vector<std::string> resultVector; auto configs = gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - auto fileEntries = fileSet->CompileFileEntries(); - auto directoryEntries = fileSet->CompileDirectoryEntries(); - for (auto const& config : configs) { cm::GenEx::Context context(gte->LocalGenerator, config); - auto directories = - fileSet->EvaluateDirectoryEntries(directoryEntries, context, gte); - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - fileSet->EvaluateFileEntry(directories, files, entry, context, gte); - } + auto files = fileSet->GetFiles(context, gte); + bool const contextSensitive = files.second; - bool const contextSensitive = - std::any_of(directoryEntries.begin(), directoryEntries.end(), - EntryIsContextSensitive) || - std::any_of(fileEntries.begin(), fileEntries.end(), - EntryIsContextSensitive); - - auto const& type = fileSet->GetType(); - // C++ modules do not support interface file sets which are dependent upon - // the configuration. - if (contextSensitive && type == "CXX_MODULES"_s) { - auto* mf = this->LG->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive file entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return std::string{}; - } - - for (auto const& it : files) { + for (auto const& it : files.first) { for (auto const& filename : it.second) { auto escapedFile = cmOutputConverter::EscapeForCMake( filename, cmOutputConverter::WrapQuotes::NoWrap); @@ -317,9 +267,8 @@ cmGeneratedFileStream os(fileName, true); if (!os) { std::string se = cmSystemTools::GetLastSystemError(); - std::ostringstream e; - e << "cannot write to file \"" << fileName << "\": " << se; - cmSystemTools::Error(e.str()); + cmSystemTools::Error( + cmStrCat("cannot write to file \"", fileName, "\": ", se)); return false; } os.SetCopyIfDifferent(true);
diff --git a/Source/cmExportBuildCMakeConfigGenerator.h b/Source/cmExportBuildCMakeConfigGenerator.h index c625032..a011493 100644 --- a/Source/cmExportBuildCMakeConfigGenerator.h +++ b/Source/cmExportBuildCMakeConfigGenerator.h
@@ -10,6 +10,8 @@ #include "cmExportBuildFileGenerator.h" #include "cmExportCMakeConfigGenerator.h" +class cmDiagnosticContext; + /** \class cmExportBuildCMakeConfigGenerator * \brief Generate a file exporting targets from a build tree. * @@ -25,7 +27,7 @@ , public cmExportBuildFileGenerator { public: - cmExportBuildCMakeConfigGenerator(); + cmExportBuildCMakeConfigGenerator(cmDiagnosticContext context); /** Set whether to append generated code to the output file. */ void SetAppendMode(bool append) { this->AppendMode = append; } @@ -36,9 +38,11 @@ void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, std::string const& suffix) override; - std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, + std::string GetFileSetDirectories(cmGeneratorTarget* gte, + cmGeneratorFileSet const* fileSet, cmTargetExport const* te) override; - std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, + std::string GetFileSetFiles(cmGeneratorTarget* gte, + cmGeneratorFileSet const* fileSet, cmTargetExport const* te) override; void GenerateCxxModuleConfigInformation(std::string const&,
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index ee12b1d..6a02afa 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx
@@ -3,7 +3,6 @@ #include "cmExportBuildFileGenerator.h" #include <algorithm> -#include <map> #include <memory> #include <set> #include <sstream> @@ -11,6 +10,8 @@ #include "cmExportSet.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmList.h" @@ -21,16 +22,25 @@ #include "cmTarget.h" #include "cmTargetExport.h" #include "cmValue.h" -#include "cmake.h" class cmSourceFile; -cmExportBuildFileGenerator::cmExportBuildFileGenerator() +cmExportBuildFileGenerator::cmExportBuildFileGenerator( + cmDiagnosticContext context) + : Context(std::move(context)) { this->LG = nullptr; this->ExportSet = nullptr; } +cmDiagnosticContext cmExportBuildFileGenerator::CaptureContext( + cmMakefile const& mf) +{ + cmDiagnosticContext context{ mf.GetBacktrace() }; + context.RecordDiagnostic(cmDiagnostics::CMD_AUTHOR, mf.GetStateSnapshot()); + return context; +} + void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg) { this->LG = lg; @@ -176,33 +186,29 @@ cmExportFileGenerator::ExportInfo cmExportBuildFileGenerator::FindExportInfo( cmGeneratorTarget const* target) const { - std::vector<std::string> exportFiles; - std::set<std::string> exportSets; - std::set<std::string> namespaces; + return target->GetLocalGenerator() + ->GetGlobalGenerator() + ->FindBuildExportInfo(target); +} +cm::optional<cmExportBuildFileGenerator::ExportRecord> +cmExportBuildFileGenerator::FindRecordForTarget( + cmGeneratorTarget const* target) const +{ auto const& name = target->GetName(); - auto& allExportSets = - target->GetLocalGenerator()->GetGlobalGenerator()->GetBuildExportSets(); - - for (auto const& exp : allExportSets) { - cmExportBuildFileGenerator const* const bfg = exp.second; - cmExportSet const* const exportSet = bfg->GetExportSet(); - std::vector<TargetExport> targets; - bfg->GetTargets(targets); - if (std::any_of( - targets.begin(), targets.end(), - [&name](TargetExport const& te) { return te.Name == name; })) { - if (exportSet) { - exportSets.insert(exportSet->GetName()); - } else { - exportSets.insert(exp.first); - } - exportFiles.push_back(exp.first); - namespaces.insert(bfg->GetNamespace()); - } + std::vector<TargetExport> targets; + this->GetTargets(targets); + bool const contains = + std::any_of(targets.begin(), targets.end(), + [&name](TargetExport const& te) { return te.Name == name; }); + cm::optional<ExportRecord> result; + if (contains) { + ExportRecord rec; + rec.Name = this->ExportSet ? this->ExportSet->GetName() : std::string{}; + rec.Namespace = this->GetNamespace(); + result = rec; } - - return { exportFiles, exportSets, namespaces }; + return result; } void cmExportBuildFileGenerator::ComplainAboutMissingTarget( @@ -244,8 +250,13 @@ void cmExportBuildFileGenerator::IssueMessage(MessageType type, std::string const& message) const { - this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - type, message, this->LG->GetMakefile()->GetBacktrace()); + this->LG->GetMakefile()->IssueMessage(type, message); +} + +void cmExportBuildFileGenerator::IssueDiagnostic( + cmDiagnosticCategory category, std::string const& message) const +{ + this->LG->GetMakefile()->IssueDiagnostic(category, message, this->Context); } std::string cmExportBuildFileGenerator::InstallNameDir( @@ -280,3 +291,24 @@ return this->PopulateInterfaceProperties( target, {}, cmGeneratorExpression::BuildInterface, properties); } + +bool cmExportBuildFileGenerator::PopulateFileSetInterfaceProperties( + cmGeneratorTarget const* target, ImportFileSetPropertyMap& properties) +{ + cmGeneratorFileSets const* const gfs = target->GetGeneratorFileSets(); + bool result = true; + + for (auto const& type : gfs->GetInterfaceFileSetTypes()) { + for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) { + ImportPropertyMap& fsProperties = properties[fileSet->GetName()]; + this->PopulateFileSetInterfaceProperty( + "INTERFACE_INCLUDE_DIRECTORIES", target, fileSet, + cmGeneratorExpression::BuildInterface, fsProperties); + result = result && + this->PopulateFileSetInterfaceProperties( + target, fileSet, cmGeneratorExpression::InstallInterface, + fsProperties); + } + } + return result; +}
diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 575d8e2..93c1758 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h
@@ -9,14 +9,18 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cmext/algorithm> +#include "cmDiagnosticContext.h" +#include "cmDiagnostics.h" #include "cmExportFileGenerator.h" #include "cmStateTypes.h" class cmExportSet; class cmGeneratorTarget; class cmLocalGenerator; +class cmMakefile; /** \class cmExportBuildCMakeConfigGenerator * \brief Generate a file exporting targets from a build tree. @@ -41,7 +45,7 @@ std::string XcFrameworkLocation; }; - cmExportBuildFileGenerator(); + cmExportBuildFileGenerator(cmDiagnosticContext context); /** Set the list of targets to export. */ void SetTargets(std::vector<TargetExport> const& targets) @@ -55,6 +59,18 @@ } void SetExportSet(cmExportSet*); + struct ExportRecord + { + std::string Name; // export set name; empty for anonymous exports + std::string Namespace; // export namespace + }; + + /** If this export contains `target`, return a record identifying it + * (export set name + namespace). Used by cmGlobalGenerator to assemble + * a project-wide view of where targets are exported. */ + cm::optional<ExportRecord> FindRecordForTarget( + cmGeneratorTarget const* target) const; + /** Set the name of the C++ module directory. */ void SetCxxModuleDirectory(std::string cxx_module_dir) { @@ -67,6 +83,9 @@ void Compute(cmLocalGenerator* lg); + /** Capture context for generator. */ + static cmDiagnosticContext CaptureContext(cmMakefile const& mf); + protected: cmStateEnums::TargetType GetExportTargetType( cmGeneratorTarget const* target) const; @@ -88,6 +107,8 @@ void IssueMessage(MessageType type, std::string const& message) const override; + void IssueDiagnostic(cmDiagnosticCategory category, + std::string const& message) const override; /** Fill in properties indicating built file locations. */ void SetImportLocationProperty(std::string const& config, @@ -111,6 +132,10 @@ bool PopulateInterfaceProperties(cmGeneratorTarget const* target, ImportPropertyMap& properties); + using cmExportFileGenerator::PopulateFileSetInterfaceProperties; + bool PopulateFileSetInterfaceProperties( + cmGeneratorTarget const* target, ImportFileSetPropertyMap& properties); + struct TargetExportPrivate { TargetExportPrivate(cmGeneratorTarget* target, @@ -124,6 +149,7 @@ std::string XcFrameworkLocation; }; + cmDiagnosticContext Context; std::vector<TargetExport> Targets; cmExportSet* ExportSet; std::vector<TargetExportPrivate> Exports;
diff --git a/Source/cmExportBuildPackageInfoGenerator.cxx b/Source/cmExportBuildPackageInfoGenerator.cxx index dda6dc8..7c29c6d 100644 --- a/Source/cmExportBuildPackageInfoGenerator.cxx +++ b/Source/cmExportBuildPackageInfoGenerator.cxx
@@ -13,6 +13,7 @@ #include <cm3p/json/value.h> #include "cmAlgorithms.h" +#include "cmDiagnosticContext.h" #include "cmGeneratorExpression.h" #include "cmList.h" #include "cmPackageInfoArguments.h" @@ -20,8 +21,9 @@ #include "cmStringAlgorithms.h" cmExportBuildPackageInfoGenerator::cmExportBuildPackageInfoGenerator( - cmPackageInfoArguments arguments) - : cmExportPackageInfoGenerator(std::move(arguments)) + cmPackageInfoArguments arguments, cmDiagnosticContext context) + : cmExportBuildFileGenerator(std::move(context)) + , cmExportPackageInfoGenerator(std::move(arguments)) { this->SetNamespace(cmStrCat(this->GetPackageName(), "::"_s)); }
diff --git a/Source/cmExportBuildPackageInfoGenerator.h b/Source/cmExportBuildPackageInfoGenerator.h index 6be0c43..987a5b4 100644 --- a/Source/cmExportBuildPackageInfoGenerator.h +++ b/Source/cmExportBuildPackageInfoGenerator.h
@@ -14,6 +14,7 @@ class Value; } +class cmDiagnosticContext; class cmGeneratorTarget; class cmPackageInfoArguments; @@ -31,7 +32,8 @@ , public cmExportPackageInfoGenerator { public: - cmExportBuildPackageInfoGenerator(cmPackageInfoArguments arguments); + cmExportBuildPackageInfoGenerator(cmPackageInfoArguments arguments, + cmDiagnosticContext context); protected: // Implement virtual methods from the superclass.
diff --git a/Source/cmExportBuildSbomGenerator.cxx b/Source/cmExportBuildSbomGenerator.cxx deleted file mode 100644 index afb1491..0000000 --- a/Source/cmExportBuildSbomGenerator.cxx +++ /dev/null
@@ -1,89 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#include "cmExportBuildSbomGenerator.h" - -#include <functional> -#include <utility> -#include <vector> - -#include <cmext/string_view> - -#include "cmGeneratorExpression.h" -#include "cmSbomArguments.h" -#include "cmSbomObject.h" -#include "cmSpdx.h" -#include "cmStringAlgorithms.h" - -class cmGeneratorTarget; - -cmExportBuildSbomGenerator::cmExportBuildSbomGenerator(cmSbomArguments args) - : cmExportSbomGenerator(args) -{ - this->SetNamespace(cmStrCat(this->GetPackageName(), "::"_s)); -} - -bool cmExportBuildSbomGenerator::GenerateMainFile(std::ostream& os) -{ - if (!this->CollectExports([&](cmGeneratorTarget const*) {})) { - return false; - } - - cmSbomDocument doc; - doc.Graph.reserve(256); - - cmSpdxCreationInfo const* ci = - insert_back(doc.Graph, this->GenerateCreationInfo()); - cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom(ci)); - std::vector<TargetProperties> targets; - - for (auto const& exp : this->Exports) { - cmGeneratorTarget const* target = exp.Target; - - ImportPropertyMap properties; - this->PopulateInterfaceProperties(target, properties); - this->PopulateInterfaceLinkLibrariesProperty( - target, cmGeneratorExpression::BuildInterface, properties); - this->PopulateLinkLibrariesProperty( - target, cmGeneratorExpression::BuildInterface, properties); - - targets.push_back( - TargetProperties{ insert_back(project->RootElements, - this->GenerateImportTarget(ci, target)), - target, std::move(properties) }); - } - - for (auto const& target : targets) { - this->GenerateProperties(doc, project, ci, target, targets); - } - - this->WriteSbom(doc, os); - return true; -} - -void cmExportBuildSbomGenerator::HandleMissingTarget( - std::string& /* link_libs */, cmGeneratorTarget const* /* depender */, - cmGeneratorTarget* /* dependee */) -{ -} - -std::string cmExportBuildSbomGenerator::GetCxxModulesDirectory() const -{ - return {}; -} - -cm::string_view cmExportBuildSbomGenerator::GetImportPrefixWithSlash() const -{ - return ""; -} - -std::string cmExportBuildSbomGenerator::GetCxxModuleFile( - std::string const& /*name*/) const -{ - return {}; -} - -void cmExportBuildSbomGenerator::GenerateCxxModuleConfigInformation( - std::string const& /*name*/, std::ostream& /*os*/) const -{ - // TODO -}
diff --git a/Source/cmExportBuildSbomGenerator.h b/Source/cmExportBuildSbomGenerator.h deleted file mode 100644 index c84c373..0000000 --- a/Source/cmExportBuildSbomGenerator.h +++ /dev/null
@@ -1,41 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <iosfwd> -#include <string> - -#include <cm/string_view> - -#include "cmExportBuildFileGenerator.h" -#include "cmExportSbomGenerator.h" - -class cmSbomArguments; - -class cmExportBuildSbomGenerator - : public cmExportBuildFileGenerator - , public cmExportSbomGenerator -{ -public: - cmExportBuildSbomGenerator(cmSbomArguments args); - -protected: - void HandleMissingTarget(std::string& link_libs, - cmGeneratorTarget const* depender, - cmGeneratorTarget* dependee) override; - bool GenerateMainFile(std::ostream& os) override; - void GenerateImportTargetsConfig(std::ostream&, std::string const&, - std::string const&) override - { - } - std::string GetCxxModulesDirectory() const override; - - cm::string_view GetImportPrefixWithSlash() const override; - - std::string GetCxxModuleFile(std::string const& /*name*/) const override; - - void GenerateCxxModuleConfigInformation(std::string const& /*name*/, - std::ostream& /*os*/) const override; -};
diff --git a/Source/cmExportCMakeConfigGenerator.cxx b/Source/cmExportCMakeConfigGenerator.cxx index 83307d7..353d38f 100644 --- a/Source/cmExportCMakeConfigGenerator.cxx +++ b/Source/cmExportCMakeConfigGenerator.cxx
@@ -5,6 +5,7 @@ #include <algorithm> #include <cassert> #include <sstream> +#include <unordered_map> #include <utility> #include <vector> @@ -13,14 +14,16 @@ #include <cmext/string_view> #include "cmExportSet.h" -#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmFindPackageStack.h" #include "cmGeneratedFileStream.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorTarget.h" #include "cmLocalGenerator.h" -#include "cmMakefile.h" #include "cmMessageType.h" #include "cmOutputConverter.h" +#include "cmScriptGenerator.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -173,7 +176,7 @@ // Isolate the file policy level. // Support CMake versions as far back as the // RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW - // policy settings for up to CMake 4.1 (this upper limit may be reviewed + // policy settings for up to CMake 4.2 (this upper limit may be reviewed // and increased from time to time). This reduces the opportunity for CMake // warnings when an older export file is later used with newer CMake // versions. @@ -182,7 +185,7 @@ "cmake_policy(VERSION " << this->RequiredCMakeVersionMajor << '.' << this->RequiredCMakeVersionMinor << '.' - << this->RequiredCMakeVersionPatch << "...4.1)\n"; + << this->RequiredCMakeVersionPatch << "...4.2)\n"; /* clang-format on */ } @@ -471,7 +474,7 @@ if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) { os << "__find_dependency_no_return(" << it.first; for (auto const& arg : it.second.ExtraArguments) { - os << ' ' << cmOutputConverter::EscapeForCMake(arg); + os << ' ' << cmScriptGenerator::Quote(arg); } os << " ${_cmake_unwind_arg})\n"; os << "if(NOT " << it.first << "_FOUND)\n" @@ -596,54 +599,94 @@ os << ")\n\n"; } -void cmExportCMakeConfigGenerator::GenerateTargetFileSets( - cmGeneratorTarget* gte, std::ostream& os, cmTargetExport const* te) +namespace { +struct FileSetInformation { - auto interfaceFileSets = gte->Target->GetAllInterfaceFileSets(); - if (!interfaceFileSets.empty()) { + cm::string_view Type; + cm::string_view CMakeVersion; +}; + +cm::optional<FileSetInformation> GetFileSetInformation(cm::string_view type) +{ + static std::unordered_map<cm::string_view, FileSetInformation> + fileSetsInformation{ + { cm::FileSetMetadata::HEADERS, + { cm::FileSetMetadata::HEADERS, "3.23.0"_s } }, + { cm::FileSetMetadata::SOURCES, + { cm::FileSetMetadata::SOURCES, "4.4.0"_s } }, + { cm::FileSetMetadata::CXX_MODULES, + { cm::FileSetMetadata::CXX_MODULES, "3.28.0"_s } }, + }; + + auto fsInfo = fileSetsInformation.find(type); + if (fsInfo != fileSetsInformation.end()) { + return fsInfo->second; + } + return {}; +} +} + +void cmExportCMakeConfigGenerator::GenerateTargetFileSets( + cmGeneratorTarget* gte, std::ostream& os, + ImportFileSetPropertyMap const& properties, cmTargetExport const* te) +{ + cmGeneratorFileSets const* gfs = gte->GetGeneratorFileSets(); + auto const& types = gfs->GetInterfaceFileSetTypes(); + + if (!types.empty()) { std::string targetName = cmStrCat(this->Namespace, gte->GetExportName()); - os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.23.0\")\n" - " target_sources(" - << targetName << '\n'; + for (auto const& type : types) { + auto fsInfo = GetFileSetInformation(type); + if (fsInfo) { + os << "if(NOT CMAKE_VERSION VERSION_LESS \"" << fsInfo->CMakeVersion + << "\")\n "; + } + os << "target_sources(" << targetName << '\n'; - for (auto const& name : interfaceFileSets) { - auto* fileSet = gte->Target->GetFileSet(name); - if (!fileSet) { - gte->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("File set \"", name, - "\" is listed in interface file sets of ", gte->GetName(), - " but has not been created")); - return; + for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) { + os << " INTERFACE" + << "\n FILE_SET " + << cmScriptGenerator::Quote(fileSet->GetName()) << "\n TYPE " + << cmScriptGenerator::Quote(type) << "\n BASE_DIRS " + << this->GetFileSetDirectories(gte, fileSet, te) << "\n FILES " + << this->GetFileSetFiles(gte, fileSet, te) << '\n'; + } + os << " )\n"; + for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) { + auto fsProperties = properties.find(fileSet->GetName()); + if (fsProperties != properties.end()) { + for (auto const& property : fsProperties->second) { + os << "\n set_property(FILE_SET " + << cmScriptGenerator::Quote(fileSet->GetName()) << " TARGET " + << targetName << "\n PROPERTY " << property.first << ' ' + << cmExportFileGeneratorEscape(property.second) << "\n )"; + } + } } - os << " INTERFACE" - << "\n FILE_SET " << cmOutputConverter::EscapeForCMake(name) - << "\n TYPE " - << cmOutputConverter::EscapeForCMake(fileSet->GetType()) - << "\n BASE_DIRS " - << this->GetFileSetDirectories(gte, fileSet, te) << "\n FILES " - << this->GetFileSetFiles(gte, fileSet, te) << '\n'; - } - - os << " )\nelse()\n set_property(TARGET " << targetName - << "\n APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES"; - for (auto const& name : interfaceFileSets) { - auto* fileSet = gte->Target->GetFileSet(name); - if (!fileSet) { - gte->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("File set \"", name, - "\" is listed in interface file sets of ", gte->GetName(), - " but has not been created")); - return; - } - - if (fileSet->GetType() == "HEADERS"_s) { - os << "\n " << this->GetFileSetDirectories(gte, fileSet, te); + if (fsInfo) { + if (type == cm::FileSetMetadata::HEADERS) { + os << "\nelse()\n set_property(TARGET " << targetName + << "\n APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES"; + for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) { + os << "\n " << this->GetFileSetDirectories(gte, fileSet, te); + } + os << "\n )"; + } else if (type == cm::FileSetMetadata::SOURCES) { + os << "\nelse()\n message(FATAL_ERROR \"The target '" << targetName + << "' cannot be imported because it relies on 'FILE_SET' of type " + "'SOURCES' which is not supported by this CMake version.\")"; + } else if (type == cm::FileSetMetadata::CXX_MODULES) { + os << "\nelse()\n message(AUTHOR_WARNING \"The target '" + << targetName + << "' cannot be imported properly because it relies on C++ " + "modules which are not supported by this CMake version.\")"; + } + os << "\nendif()\n\n"; + } else { + os << ")\n\n"; } } - os << "\n )\nendif()\n\n"; } }
diff --git a/Source/cmExportCMakeConfigGenerator.h b/Source/cmExportCMakeConfigGenerator.h index f95676f..b2401e5 100644 --- a/Source/cmExportCMakeConfigGenerator.h +++ b/Source/cmExportCMakeConfigGenerator.h
@@ -15,7 +15,7 @@ #include "cmGeneratorExpression.h" #include "cmStateTypes.h" -class cmFileSet; +class cmGeneratorFileSet; class cmGeneratorTarget; class cmTargetExport; @@ -44,6 +44,7 @@ protected: using ImportPropertyMap = std::map<std::string, std::string>; + using ImportFileSetPropertyMap = std::map<std::string, ImportPropertyMap>; // Methods to implement export file code generation. bool GenerateImportFile(std::ostream& os) override; @@ -84,6 +85,7 @@ cmGeneratorTarget const* target, ImportPropertyMap& properties); void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, + ImportFileSetPropertyMap const& properties, cmTargetExport const* te = nullptr); std::string GetCxxModuleFile(std::string const& name) const override; @@ -91,10 +93,10 @@ void GenerateCxxModuleInformation(std::string const& name, std::ostream& os); virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, - cmFileSet* fileSet, + cmGeneratorFileSet const* fileSet, cmTargetExport const* te) = 0; virtual std::string GetFileSetFiles(cmGeneratorTarget* gte, - cmFileSet* fileSet, + cmGeneratorFileSet const* fileSet, cmTargetExport const* te) = 0; void SetRequiredCMakeVersion(unsigned int major, unsigned int minor,
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 53eafe6..2f6434a 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx
@@ -15,14 +15,16 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +#include "cmBuildSbomGenerator.h" #include "cmCryptoHash.h" +#include "cmDiagnosticContext.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmExperimental.h" #include "cmExportBuildAndroidMKGenerator.h" #include "cmExportBuildCMakeConfigGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmExportBuildPackageInfoGenerator.h" -#include "cmExportBuildSbomGenerator.h" #include "cmExportSet.h" #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" @@ -87,6 +89,29 @@ makefile.AddExportBuildFileGenerator(std::move(exportGenerator)); } +static bool ValidateExportableTarget(std::string const& name, cmMakefile& mf, + cmGlobalGenerator* gg, + cmExecutionStatus& status) +{ + if (mf.IsAlias(name)) { + status.SetError(cmStrCat("given ALIAS target \"", name, + "\" which may not be exported.")); + return false; + } + cmTarget const* target = gg->FindTarget(name); + if (!target) { + status.SetError(cmStrCat("given target \"", name, + "\" which is not built by this project.")); + return false; + } + if (target->GetType() == cmStateEnums::UTILITY) { + status.SetError(cmStrCat("given custom target \"", name, + "\" which may not be exported.")); + return false; + } + return true; +} + static bool HandleTargetsMode(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -162,25 +187,7 @@ cmGlobalGenerator* gg = mf.GetGlobalGenerator(); for (std::string const& currentTarget : *arguments.Targets) { - if (mf.IsAlias(currentTarget)) { - std::ostringstream e; - e << "given ALIAS target \"" << currentTarget - << "\" which may not be exported."; - status.SetError(e.str()); - return false; - } - - if (cmTarget* target = gg->FindTarget(currentTarget)) { - if (target->GetType() == cmStateEnums::UTILITY) { - status.SetError("given custom target \"" + currentTarget + - "\" which may not be exported."); - return false; - } - } else { - std::ostringstream e; - e << "given target \"" << currentTarget - << "\" which is not built by this project."; - status.SetError(e.str()); + if (!ValidateExportableTarget(currentTarget, mf, gg, status)) { return false; } targets.emplace_back(currentTarget, std::string{}); @@ -198,11 +205,9 @@ if (gg->GetExportedTargetsFile(fname)) { switch (mf.GetPolicyStatus(cmPolicies::CMP0103)) { case cmPolicies::WARN: - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0103), - "\n" - "export() command already specified for the file\n ", + mf.IssuePolicyWarning( + cmPolicies::CMP0103, {}, + cmStrCat("export() command already specified for the file\n ", arguments.Filename, "\nDid you miss 'APPEND' keyword?")); CM_FALLTHROUGH; case cmPolicies::OLD: @@ -215,14 +220,17 @@ } } + cmDiagnosticContext context = cmExportBuildFileGenerator::CaptureContext(mf); std::unique_ptr<cmExportBuildFileGenerator> ebfg = nullptr; if (android) { - auto ebag = cm::make_unique<cmExportBuildAndroidMKGenerator>(); + auto ebag = + cm::make_unique<cmExportBuildAndroidMKGenerator>(std::move(context)); ebag->SetNamespace(arguments.Namespace); ebag->SetAppendMode(arguments.Append); ebfg = std::move(ebag); } else { - auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>(); + auto ebcg = + cm::make_unique<cmExportBuildCMakeConfigGenerator>(std::move(context)); ebcg->SetNamespace(arguments.Namespace); ebcg->SetAppendMode(arguments.Append); ebcg->SetExportOld(arguments.ExportOld); @@ -283,11 +291,9 @@ return false; } if (p == cmPolicies::WARN) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, cmStrCat("export "_s, status.GetError())); - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0208)); + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("export "_s, status.GetError())); + status.GetMakefile().IssuePolicyWarning(cmPolicies::CMP0208); } } @@ -326,7 +332,8 @@ } // Set up export file generation. - auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>(); + auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>( + cmExportBuildFileGenerator::CaptureContext(mf)); ebcg->SetNamespace(arguments.Namespace); ebcg->SetExportPackageDependencies(arguments.ExportPackageDependencies); @@ -393,7 +400,8 @@ } // Create the export build generator - auto ebpg = cm::make_unique<GeneratorType>(arguments); + auto ebpg = cm::make_unique<GeneratorType>( + arguments, cmExportBuildFileGenerator::CaptureContext(mf)); AddExportGenerator(mf, gg, std::move(ebpg), fname, *exportSet, arguments.CxxModulesDirectory); return true; @@ -416,10 +424,66 @@ return false; } - using arg_t = cmSbomArguments; - using gen_t = cmExportBuildSbomGenerator; - status.GetMakefile().SetExplicitlyGeneratesSbom(true); - return HandleSpecialExportMode<arg_t, gen_t>(args, status); + struct SbomExportArguments + : public cmSbomArguments + , public ArgumentParser::ParseResult + { + ArgumentParser::NonEmpty<std::vector<std::string>> ExportSetNames; + + using cmSbomArguments::Check; + using ArgumentParser::ParseResult::Check; + }; + + auto parser = cmArgumentParser<SbomExportArguments>{}; + cmSbomArguments::Bind(parser); + parser.Bind("EXPORTS"_s, &SbomExportArguments::ExportSetNames); + + std::vector<std::string> unknownArgs; + SbomExportArguments arguments = parser.Parse(args, &unknownArgs); + + if (!arguments.Check(args[0], &unknownArgs, status)) { + return false; + } + + if (arguments.ExportSetNames.empty()) { + status.SetError(cmStrCat(args[0], " missing EXPORTS.")); + return false; + } + + if (!arguments.Check(status) || !arguments.SetMetadataFromProject(status)) { + return false; + } + + cmMakefile& mf = status.GetMakefile(); + cmGlobalGenerator* gg = mf.GetGlobalGenerator(); + + std::string const dir = + arguments.GetDefaultDestination(mf.GetCurrentBinaryDirectory()); + std::string const fpath = cmStrCat(dir, '/', arguments.GetPackageFileName()); + + if (gg->IsBuildSbomFile(fpath)) { + status.SetError(cmStrCat("SBOM command already specified for the file "_s, + cmSystemTools::GetFilenameNameView(fpath), '.')); + return false; + } + + std::vector<cmExportSet*> sets; + sets.reserve(arguments.ExportSetNames.size()); + for (std::string const& name : arguments.ExportSetNames) { + cm::optional<cmExportSet*> const exportSet = + GetExportSet(name, gg, status); + if (!exportSet) { + return false; + } + sets.push_back(*exportSet); + } + + auto builder = cm::make_unique<cmBuildSbomGenerator>(arguments, sets, fpath); + + cmBuildSbomGenerator* rawPtr = builder.get(); + mf.AddBuildSbomGenerator(std::move(builder)); + gg->AddBuildSbomGenerator(rawPtr); + return true; } static bool HandleSetupMode(std::vector<std::string> const& args,
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index fdf0dbd..49704a4 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx
@@ -4,10 +4,12 @@ #include <array> #include <cstddef> +#include <functional> #include <sstream> #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> @@ -16,6 +18,7 @@ #include "cmComputeLinkInformation.h" #include "cmFindPackageStack.h" #include "cmGeneratedFileStream.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmLinkItem.h" #include "cmList.h" @@ -62,8 +65,8 @@ openmodeApp); } else { // Generate atomically and with copy-if-different. - std::unique_ptr<cmGeneratedFileStream> ap( - new cmGeneratedFileStream(this->MainImportFile, true)); + auto ap = + cm::make_unique<cmGeneratedFileStream>(this->MainImportFile, true); ap->SetCopyIfDifferent(true); foutPtr = std::move(ap); } @@ -139,6 +142,20 @@ return true; } +bool cmExportFileGenerator::PopulateFileSetInterfaceProperties( + cmGeneratorTarget const* target, cmGeneratorFileSet const* fileSet, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + this->PopulateFileSetInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", + target, fileSet, preprocessRule, + properties); + this->PopulateFileSetInterfaceProperty("INTERFACE_COMPILE_OPTIONS", target, + fileSet, preprocessRule, properties); + + return true; +} + void cmExportFileGenerator::PopulateInterfaceProperty( std::string const& propName, cmGeneratorTarget const* target, ImportPropertyMap& properties) const @@ -181,6 +198,29 @@ properties); } +void cmExportFileGenerator::PopulateFileSetInterfaceProperty( + std::string const& propName, cmGeneratorTarget const* target, + cmGeneratorFileSet const* fileSet, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + cmValue input = fileSet->GetProperty(propName); + if (input) { + if (input->empty()) { + // Set to empty + properties[propName].clear(); + return; + } + + std::string prepro = + cmGeneratorExpression::Preprocess(*input, preprocessRule); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, target); + properties[propName] = prepro; + } + } +} + bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext preprocessRule, @@ -411,9 +451,9 @@ } } -void cmExportFileGenerator::ResolveTargetsInGeneratorExpression( - std::string& input, cmGeneratorTarget const* target, - cmLocalGenerator const* lg) +cm::optional<std::string> cmResolveTargetsInGeneratorExpression( + std::string& input, + std::function<bool(std::string& name)> const& addTargetNamespace) { std::string::size_type pos = 0; std::string::size_type lastPos = pos; @@ -436,13 +476,13 @@ std::string targetName = input.substr(nameStartPos, commaPos - nameStartPos); - if (this->AddTargetNamespace(targetName, target, lg)) { + if (addTargetNamespace(targetName)) { input.replace(nameStartPos, commaPos - nameStartPos, targetName); } lastPos = nameStartPos + targetName.size() + 1; } - std::string errorString; + cm::optional<std::string> errorString; pos = 0; lastPos = pos; while ((pos = input.find("$<TARGET_NAME:", lastPos)) != std::string::npos) { @@ -458,7 +498,7 @@ "literal."; break; } - if (!this->AddTargetNamespace(targetName, target, lg)) { + if (!addTargetNamespace(targetName)) { errorString = "$<TARGET_NAME:...> requires its parameter to be a " "reachable target."; break; @@ -469,7 +509,7 @@ pos = 0; lastPos = pos; - while (errorString.empty() && + while (!errorString && (pos = input.find("$<LINK_ONLY:", lastPos)) != std::string::npos) { std::string::size_type nameStartPos = pos + cmStrLen("$<LINK_ONLY:"); std::string::size_type endPos = input.find('>', nameStartPos); @@ -479,13 +519,13 @@ } std::string libName = input.substr(nameStartPos, endPos - nameStartPos); if (cmGeneratorExpression::IsValidTargetName(libName) && - this->AddTargetNamespace(libName, target, lg)) { + addTargetNamespace(libName)) { input.replace(nameStartPos, endPos - nameStartPos, libName); } lastPos = nameStartPos + libName.size() + 1; } - while (errorString.empty() && + while (!errorString && (pos = input.find("$<COMPILE_ONLY:", lastPos)) != std::string::npos) { std::string::size_type nameStartPos = pos + cmStrLen("$<COMPILE_ONLY:"); std::string::size_type endPos = input.find('>', nameStartPos); @@ -495,17 +535,26 @@ } std::string libName = input.substr(nameStartPos, endPos - nameStartPos); if (cmGeneratorExpression::IsValidTargetName(libName) && - this->AddTargetNamespace(libName, target, lg)) { + addTargetNamespace(libName)) { input.replace(nameStartPos, endPos - nameStartPos, libName); } lastPos = nameStartPos + libName.size() + 1; } - this->ReplaceInstallPrefix(input); + return errorString; +} - if (!errorString.empty()) { - target->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, - errorString); +void cmExportFileGenerator::ResolveTargetsInGeneratorExpression( + std::string& input, cmGeneratorTarget const* target, + cmLocalGenerator const* lg) +{ + auto err = cmResolveTargetsInGeneratorExpression( + input, [this, target, lg](std::string& name) { + return this->AddTargetNamespace(name, target, lg); + }); + this->ReplaceInstallPrefix(input); + if (err) { + target->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, *err); } } @@ -674,9 +723,9 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( cmGeneratorTarget const* gte, ImportPropertyMap& properties, cmGeneratorExpression::PreprocessContext ctx, - std::string const& includesDestinationDirs, std::string& errorMessage) + std::string const& includesDestinationDirs, std::string&) { - if (!gte->HaveCxx20ModuleSources(&errorMessage)) { + if (!gte->HaveCxx20ModuleSources()) { return true; }
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index f53cba7..b3562c5 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h
@@ -4,19 +4,23 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <functional> #include <iosfwd> #include <map> #include <set> #include <string> #include <vector> +#include <cm/optional> #include <cm/string_view> +#include "cmDiagnostics.h" #include "cmGeneratorExpression.h" #include "cmMessageType.h" class cmExportSet; class cmGeneratorTarget; +class cmGeneratorFileSet; class cmLocalGenerator; /** \class cmExportFileGenerator @@ -30,6 +34,13 @@ cmExportFileGenerator(); virtual ~cmExportFileGenerator() = default; + struct ExportInfo + { + std::vector<std::string> Files; + std::set<std::string> Sets; + std::set<std::string> Namespaces; + }; + /** Set the full path to the export file to generate. */ void SetExportFile(char const* mainFile); std::string const& GetMainExportFileName() const; @@ -47,6 +58,7 @@ protected: using ImportPropertyMap = std::map<std::string, std::string>; + using ImportFileSetPropertyMap = std::map<std::string, ImportPropertyMap>; // Collect properties with detailed information about targets beyond // their location on disk. @@ -111,6 +123,10 @@ cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); + void PopulateFileSetInterfaceProperty( + std::string const& propName, cmGeneratorTarget const* target, + cmGeneratorFileSet const* fileSet, + cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); bool PopulateInterfaceLinkLibrariesProperty( cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); @@ -121,21 +137,21 @@ cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties); + bool PopulateFileSetInterfaceProperties( + cmGeneratorTarget const* target, cmGeneratorFileSet const* fileSet, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + virtual void IssueMessage(MessageType type, std::string const& message) const = 0; + virtual void IssueDiagnostic(cmDiagnosticCategory category, + std::string const& message) const = 0; void ReportError(std::string const& errorMessage) const { this->IssueMessage(MessageType::FATAL_ERROR, errorMessage); } - struct ExportInfo - { - std::vector<std::string> Files; - std::set<std::string> Sets; - std::set<std::string> Namespaces; - }; - /** Find the set of export files and the unique namespace (if any) for a * target. */ virtual ExportInfo FindExportInfo(cmGeneratorTarget const* target) const = 0; @@ -224,3 +240,16 @@ std::string const&, cmGeneratorTarget const*, std::string const&, std::vector<cmLinkItem> const&, ImportPropertyMap& properties, ImportLinkPropertyTargetNames); + +/** Walk a generator expression and rewrite the target-name slot of each + `$<TARGET_PROPERTY:>`, `$<TARGET_NAME:>`, `$<LINK_ONLY:>`, and + `$<COMPILE_ONLY:>` construct via the provided callback. The callback + receives the bare target name; if it mutates the name and returns true, + the helper splices the new name back into the genex. Returns the + parse-level error message, if an error was encountered. Callers handle + install-prefix substitution and error reporting themselves. This is used + by the cmExportFileGenerator hierarchy, as well as the cmSbomBuilder + hierarchy. */ +cm::optional<std::string> cmResolveTargetsInGeneratorExpression( + std::string& input, + std::function<bool(std::string& name)> const& addTargetNamespace);
diff --git a/Source/cmExportInstallCMakeConfigGenerator.cxx b/Source/cmExportInstallCMakeConfigGenerator.cxx index d803121..970b4bc 100644 --- a/Source/cmExportInstallCMakeConfigGenerator.cxx +++ b/Source/cmExportInstallCMakeConfigGenerator.cxx
@@ -2,7 +2,6 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmExportInstallCMakeConfigGenerator.h" -#include <algorithm> #include <functional> #include <map> #include <memory> @@ -11,21 +10,17 @@ #include <utility> #include <vector> -#include <cm/string_view> -#include <cmext/string_view> - #include "cmExportFileGenerator.h" #include "cmExportSet.h" -#include "cmFileSet.h" #include "cmGenExContext.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -84,6 +79,11 @@ return false; } + ImportFileSetPropertyMap fsProperties; + if (!this->PopulateFileSetInterfaceProperties(te, fsProperties)) { + return false; + } + if (this->PopulateInterfaceLinkLibrariesProperty( gt, cmGeneratorExpression::InstallInterface, properties) && !this->ExportOld) { @@ -100,7 +100,7 @@ this->GenerateInterfaceProperties(gt, os, properties); - this->GenerateTargetFileSets(gt, os, te); + this->GenerateTargetFileSets(gt, os, fsProperties, te); } this->LoadConfigFiles(os); @@ -271,49 +271,27 @@ } } -namespace { -bool EntryIsContextSensitive( - std::unique_ptr<cmCompiledGeneratorExpression> const& cge) -{ - return cge->GetHadContextSensitiveCondition(); -} -} - std::string cmExportInstallCMakeConfigGenerator::GetFileSetDirectories( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te) + cmGeneratorTarget* gte, cmGeneratorFileSet const* fileSet, + cmTargetExport const* te) { std::vector<std::string> resultVector; auto configs = gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); - auto cge = - ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination()); - for (auto const& config : configs) { - auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte); - auto dest = cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + cmInstallFileSetGenerator::DestinationContext result = + te->FileSetGenerators.at(fileSet->GetName()) + ->GetDestination(gte, config); + + std::string dest = cmOutputConverter::EscapeForCMake( + result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap); + if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) { dest = cmStrCat("${_IMPORT_PREFIX}/", dest); } - auto const& type = fileSet->GetType(); - // C++ modules do not support interface file sets which are dependent upon - // the configuration. - if (cge->GetHadContextSensitiveCondition() && type == "CXX_MODULES"_s) { - auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive base file entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return std::string{}; - } - - if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) { + if (result.HadContextSensitiveCondition && configs.size() != 1) { resultVector.push_back( cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); } else { @@ -326,29 +304,22 @@ } std::string cmExportInstallCMakeConfigGenerator::GetFileSetFiles( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te) + cmGeneratorTarget* gte, cmGeneratorFileSet const* fileSet, + cmTargetExport const* te) { std::vector<std::string> resultVector; auto configs = gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - auto fileEntries = fileSet->CompileFileEntries(); - auto directoryEntries = fileSet->CompileDirectoryEntries(); - cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance()); auto destCge = destGe.Parse( te->FileSetGenerators.at(fileSet->GetName())->GetDestination()); for (auto const& config : configs) { cm::GenEx::Context context(gte->LocalGenerator, config); - auto directories = - fileSet->EvaluateDirectoryEntries(directoryEntries, context, gte); + auto files = fileSet->GetFiles(context, gte); - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - fileSet->EvaluateFileEntry(directories, files, entry, context, gte); - } auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte); auto dest = cmStrCat(cmOutputConverter::EscapeForCMake( @@ -358,27 +329,9 @@ dest = cmStrCat("${_IMPORT_PREFIX}/", dest); } - bool const contextSensitive = destCge->GetHadContextSensitiveCondition() || - std::any_of(directoryEntries.begin(), directoryEntries.end(), - EntryIsContextSensitive) || - std::any_of(fileEntries.begin(), fileEntries.end(), - EntryIsContextSensitive); - - auto const& type = fileSet->GetType(); - // C++ modules do not support interface file sets which are dependent upon - // the configuration. - if (contextSensitive && type == "CXX_MODULES"_s) { - auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive base file entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return std::string{}; - } - - for (auto const& it : files) { + bool const contextSensitive = + destCge->GetHadContextSensitiveCondition() || files.second; + for (auto const& it : files.first) { auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/'); for (auto const& filename : it.second) { auto relFile =
diff --git a/Source/cmExportInstallCMakeConfigGenerator.h b/Source/cmExportInstallCMakeConfigGenerator.h index 480384d..e4ebd29 100644 --- a/Source/cmExportInstallCMakeConfigGenerator.h +++ b/Source/cmExportInstallCMakeConfigGenerator.h
@@ -59,9 +59,11 @@ virtual void CleanupTemporaryVariables(std::ostream&); - std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, + std::string GetFileSetDirectories(cmGeneratorTarget* gte, + cmGeneratorFileSet const* fileSet, cmTargetExport const* te) override; - std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, + std::string GetFileSetFiles(cmGeneratorTarget* gte, + cmGeneratorFileSet const* fileSet, cmTargetExport const* te) override; std::string GetCxxModulesDirectory() const override;
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index db75480..89ebfb0 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx
@@ -12,6 +12,8 @@ #include "cmExportSet.h" #include "cmGeneratedFileStream.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmInstallTargetGenerator.h" @@ -24,7 +26,6 @@ #include "cmTarget.h" #include "cmTargetExport.h" #include "cmValue.h" -#include "cmake.h" cmExportInstallFileGenerator::cmExportInstallFileGenerator( cmInstallExportGenerator* iegen) @@ -264,34 +265,9 @@ cmExportFileGenerator::ExportInfo cmExportInstallFileGenerator::FindExportInfo( cmGeneratorTarget const* target) const { - std::vector<std::string> exportFiles; - std::set<std::string> exportSets; - std::set<std::string> namespaces; - - auto const& name = target->GetName(); - auto& allExportSets = - target->GetLocalGenerator()->GetGlobalGenerator()->GetExportSets(); - - for (auto const& exp : allExportSets) { - auto const& exportSet = exp.second; - auto const& targets = exportSet.GetTargetExports(); - - if (std::any_of(targets.begin(), targets.end(), - [&name](std::unique_ptr<cmTargetExport> const& te) { - return te->TargetName == name; - })) { - std::vector<cmInstallExportGenerator const*> const* installs = - exportSet.GetInstallations(); - if (!installs->empty()) { - exportSets.insert(exp.first); - for (cmInstallExportGenerator const* install : *installs) { - exportFiles.push_back(install->GetDestinationFile()); - namespaces.insert(install->GetNamespace()); - } - } - } - } - return { exportFiles, exportSets, namespaces }; + return target->GetLocalGenerator() + ->GetGlobalGenerator() + ->FindInstallExportInfo(target); } void cmExportInstallFileGenerator::ComplainAboutMissingTarget( @@ -300,9 +276,9 @@ { std::ostringstream e; e << "install(" << this->IEGen->InstallSubcommand() << " \"" - << this->GetExportName() << "\" ...) " - << "includes target \"" << depender->GetName() - << "\" which requires target \"" << dependee->GetName() << "\" "; + << this->GetExportName() << "\" ...) " << "includes target \"" + << depender->GetName() << "\" which requires target \"" + << dependee->GetName() << "\" "; if (exportInfo.Sets.empty()) { e << "that is not in any export set."; } else { @@ -329,18 +305,24 @@ { std::ostringstream e; e << "install(" << this->IEGen->InstallSubcommand() << " \"" - << this->GetExportName() << "\" ...) " - << "includes target \"" << targetName - << "\" more than once in the export set."; + << this->GetExportName() << "\" ...) " << "includes target \"" + << targetName << "\" more than once in the export set."; this->ReportError(e.str()); } void cmExportInstallFileGenerator::IssueMessage( MessageType type, std::string const& message) const { - this->IEGen->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - type, message, - this->IEGen->GetLocalGenerator()->GetMakefile()->GetBacktrace()); + cmLocalGenerator const* const lg = this->IEGen->GetLocalGenerator(); + lg->GetMakefile()->IssueMessage(type, message); +} + +void cmExportInstallFileGenerator::IssueDiagnostic( + cmDiagnosticCategory category, std::string const& message) const +{ + cmLocalGenerator const* const lg = this->IEGen->GetLocalGenerator(); + lg->GetMakefile()->IssueDiagnostic(category, message, + this->IEGen->GetDiagnosticContext()); } std::string cmExportInstallFileGenerator::InstallNameDir( @@ -405,6 +387,27 @@ properties); } +bool cmExportInstallFileGenerator::PopulateFileSetInterfaceProperties( + cmTargetExport const* targetExport, ImportFileSetPropertyMap& properties) +{ + cmGeneratorTarget const* const gt = targetExport->Target; + cmGeneratorFileSets const* const gfs = gt->GetGeneratorFileSets(); + + bool result = true; + + for (auto const& type : gfs->GetInterfaceFileSetTypes()) { + for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) { + ImportPropertyMap& fsProperties = properties[fileSet->GetName()]; + this->PopulateFileSetIncludeDirectoriesInterface( + gt, fileSet, cmGeneratorExpression::InstallInterface, fsProperties); + result = result && + this->PopulateFileSetInterfaceProperties( + gt, fileSet, cmGeneratorExpression::InstallInterface, fsProperties); + } + } + return result; +} + namespace { bool isSubDirectory(std::string const& a, std::string const& b) { @@ -615,6 +618,37 @@ } } +void cmExportInstallFileGenerator::PopulateFileSetIncludeDirectoriesInterface( + cmGeneratorTarget const* target, cmGeneratorFileSet const* fileSet, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + char const* const propName = "INTERFACE_INCLUDE_DIRECTORIES"; + cmValue includes = fileSet->GetProperty(propName); + + if (!includes) { + return; + } + if (includes && includes->empty()) { + // Set to empty + properties[propName].clear(); + return; + } + + std::string prepro = cmGeneratorExpression::Preprocess( + *includes, preprocessRule, this->GetImportPrefixWithSlash()); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, target); + + if (!this->CheckInterfaceDirs(prepro, target, propName)) { + return; + } + properties[propName] = prepro; + } +} + void cmExportInstallFileGenerator::PopulateLinkDependsInterface( cmGeneratorTarget const* gt, cmGeneratorExpression::PreprocessContext preprocessRule,
diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index 5a72903..baee785 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h
@@ -12,12 +12,14 @@ #include <cm/string_view> +#include "cmDiagnostics.h" #include "cmExportFileGenerator.h" #include "cmGeneratorExpression.h" #include "cmInstallExportGenerator.h" #include "cmStateTypes.h" class cmGeneratorTarget; +class cmGeneratorFileSet; class cmInstallTargetGenerator; class cmTargetExport; @@ -96,6 +98,8 @@ void IssueMessage(MessageType type, std::string const& message) const override; + void IssueDiagnostic(cmDiagnosticCategory category, + std::string const& message) const override; /** Generate a per-configuration file for the targets. */ virtual bool GenerateImportFileConfig(std::string const& config); @@ -135,6 +139,10 @@ ImportPropertyMap& properties, std::set<std::string>& importedLocations); + using cmExportFileGenerator::PopulateFileSetInterfaceProperties; + bool PopulateFileSetInterfaceProperties( + cmTargetExport const* targetExport, ImportFileSetPropertyMap& properties); + virtual bool CheckInterfaceDirs(std::string const& prepro, cmGeneratorTarget const* target, std::string const& prop) const; @@ -176,4 +184,9 @@ cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties); + + void PopulateFileSetIncludeDirectoriesInterface( + cmGeneratorTarget const* target, cmGeneratorFileSet const* fileSet, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); };
diff --git a/Source/cmExportInstallPackageInfoGenerator.cxx b/Source/cmExportInstallPackageInfoGenerator.cxx index f14942d..a0e4392 100644 --- a/Source/cmExportInstallPackageInfoGenerator.cxx +++ b/Source/cmExportInstallPackageInfoGenerator.cxx
@@ -6,7 +6,6 @@ #include <map> #include <memory> #include <set> -#include <sstream> #include <utility> #include <vector> @@ -19,8 +18,9 @@ #include "cmAlgorithms.h" #include "cmExportSet.h" -#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" @@ -117,14 +117,9 @@ } } - this->GeneratePackageRequires(root); - - // Write the primary packing information file. - this->WritePackageInfo(root, os); - + // Generate per-configuration files before the main file so that + // processing link-dependent targets populates package requirements. bool result = true; - - // Generate an import file for each configuration. if (this->RequiresConfigFiles) { for (std::string const& c : this->Configurations) { if (!this->GenerateImportFileConfig(c)) { @@ -133,6 +128,11 @@ } } + this->GeneratePackageRequires(root); + + // Write the primary packing information file. + this->WritePackageInfo(root, os); + return result; } @@ -220,43 +220,25 @@ cm::optional<std::string> cmExportInstallPackageInfoGenerator::GetFileSetDirectory( - cmGeneratorTarget* gte, cmTargetExport const* te, cmFileSet* fileSet, - cm::optional<std::string> const& config) + cmGeneratorTarget* gte, cmTargetExport const* te, + cmGeneratorFileSet const* fileSet, cm::optional<std::string> const& config) { - cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); - auto cge = - ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination()); + cmInstallFileSetGenerator::DestinationContext result = + te->FileSetGenerators.at(fileSet->GetName()) + ->GetDestination(gte, config.value_or("")); - std::string const unescapedDest = - cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte); - bool const isConfigDependent = cge->GetHadContextSensitiveCondition(); - - if (config && !isConfigDependent) { + if (config && !result.HadContextSensitiveCondition) { return {}; } - if (!config && isConfigDependent) { + if (!config && result.HadContextSensitiveCondition) { this->RequiresConfigFiles = true; return {}; } - std::string const& type = fileSet->GetType(); - if (config && (type == "CXX_MODULES"_s)) { - // C++ modules do not support interface file sets which are dependent - // upon the configuration. - cmMakefile* mf = gte->LocalGenerator->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive base file entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return {}; - } - cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); + result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) { dest = cmStrCat("@prefix@/"_s, *dest); } @@ -270,27 +252,27 @@ bool hasModules = false; std::set<std::string> seenIncludeDirectories; for (auto const& name : gte->Target->GetAllInterfaceFileSets()) { - cmFileSet* fileSet = gte->Target->GetFileSet(name); + cmGeneratorFileSet const* fileSet = gte->GetFileSet(name); if (!fileSet) { - gte->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("File set \"", name, - "\" is listed in interface file sets of ", gte->GetName(), - " but has not been created")); + this->IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("File set \"", name, + "\" is listed in interface file sets of ", + gte->GetName(), + " but has not been created")); return false; } cm::optional<std::string> const& fileSetDirectory = this->GetFileSetDirectory(gte, te, fileSet, config); - if (fileSet->GetType() == "HEADERS"_s) { + if (fileSet->GetType() == cm::FileSetMetadata::HEADERS) { if (fileSetDirectory && !cm::contains(seenIncludeDirectories, *fileSetDirectory)) { component["includes"].append(*fileSetDirectory); seenIncludeDirectories.insert(*fileSetDirectory); } - } else if (fileSet->GetType() == "CXX_MODULES"_s) { + } else if (fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) { hasModules = true; this->RequiresConfigFiles = true; }
diff --git a/Source/cmExportInstallPackageInfoGenerator.h b/Source/cmExportInstallPackageInfoGenerator.h index 1501234..da7f4e3 100644 --- a/Source/cmExportInstallPackageInfoGenerator.h +++ b/Source/cmExportInstallPackageInfoGenerator.h
@@ -12,7 +12,7 @@ #include "cmExportInstallFileGenerator.h" #include "cmExportPackageInfoGenerator.h" -class cmFileSet; +class cmGeneratorFileSet; class cmGeneratorTarget; class cmInstallExportGenerator; class cmPackageInfoArguments; @@ -71,7 +71,8 @@ std::string GetCxxModulesDirectory() const override; cm::optional<std::string> GetFileSetDirectory( - cmGeneratorTarget* gte, cmTargetExport const* te, cmFileSet* fileSet, + cmGeneratorTarget* gte, cmTargetExport const* te, + cmGeneratorFileSet const* fileSet, cm::optional<std::string> const& config = {}); bool GenerateFileSetProperties(Json::Value& component,
diff --git a/Source/cmExportInstallSbomGenerator.cxx b/Source/cmExportInstallSbomGenerator.cxx deleted file mode 100644 index c5c9acb..0000000 --- a/Source/cmExportInstallSbomGenerator.cxx +++ /dev/null
@@ -1,250 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#include "cmExportInstallSbomGenerator.h" - -#include <functional> -#include <map> -#include <memory> -#include <set> -#include <sstream> -#include <utility> -#include <vector> - -#include <cmext/string_view> - -#include "cmExportSet.h" -#include "cmFileSet.h" -#include "cmGeneratorExpression.h" -#include "cmGeneratorTarget.h" -#include "cmInstallExportGenerator.h" -#include "cmInstallFileSetGenerator.h" -#include "cmLocalGenerator.h" -#include "cmMakefile.h" -#include "cmMessageType.h" -#include "cmOutputConverter.h" -#include "cmSbomArguments.h" -#include "cmSbomObject.h" -#include "cmSpdx.h" -#include "cmStateTypes.h" -#include "cmStringAlgorithms.h" -#include "cmSystemTools.h" -#include "cmTarget.h" -#include "cmTargetExport.h" - -cmExportInstallSbomGenerator::cmExportInstallSbomGenerator( - cmInstallExportGenerator* iegen, cmSbomArguments args) - : cmExportSbomGenerator(std::move(args)) - , cmExportInstallFileGenerator(iegen) -{ - this->SetNamespace(cmStrCat(this->GetPackageName(), "::"_s)); -} - -std::string cmExportInstallSbomGenerator::GetConfigImportFileGlob() const -{ - std::string glob = cmStrCat(this->FileBase, "@*", this->FileExt); - return glob; -} - -std::string const& cmExportInstallSbomGenerator::GetExportName() const -{ - return this->GetPackageName(); -} - -cm::string_view cmExportInstallSbomGenerator::GetImportPrefixWithSlash() const -{ - return "@prefix@/"_s; -} - -bool cmExportInstallSbomGenerator::GenerateMainFile(std::ostream& os) -{ - std::vector<cmTargetExport const*> allTargets; - { - auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); }; - - if (!this->CollectExports(visitor)) { - return false; - } - } - cmSbomDocument doc; - doc.Graph.reserve(256); - - cmSpdxCreationInfo const* ci = - insert_back(doc.Graph, this->GenerateCreationInfo()); - cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom(ci)); - std::vector<TargetProperties> targets; - targets.reserve(allTargets.size()); - - for (cmTargetExport const* te : allTargets) { - cmGeneratorTarget const* gt = te->Target; - ImportPropertyMap properties; - if (!this->PopulateInterfaceProperties(te, properties)) { - return false; - } - this->PopulateLinkLibrariesProperty( - gt, cmGeneratorExpression::InstallInterface, properties); - this->PopulateInterfaceLinkLibrariesProperty( - gt, cmGeneratorExpression::InstallInterface, properties); - - targets.push_back(TargetProperties{ - insert_back(project->RootElements, - this->GenerateImportTarget(ci, te->Target)), - te->Target, std::move(properties) }); - } - - for (auto const& target : targets) { - this->GenerateProperties(doc, project, ci, target, targets); - } - - this->WriteSbom(doc, os); - return true; -} - -void cmExportInstallSbomGenerator::GenerateImportTargetsConfig( - std::ostream& os, std::string const& config, std::string const& suffix) -{ - cmSbomDocument doc; - doc.Graph.reserve(256); - - cmSpdxCreationInfo const* ci = - insert_back(doc.Graph, this->GenerateCreationInfo()); - cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom(ci)); - - std::vector<TargetProperties> targets; - std::string cfg = (config.empty() ? "noconfig" : config); - - for (auto const& te : this->GetExportSet()->GetTargetExports()) { - ImportPropertyMap properties; - std::set<std::string> importedLocations; - - if (this->GetExportTargetType(te.get()) != - cmStateEnums::INTERFACE_LIBRARY) { - this->PopulateImportProperties(config, suffix, te.get(), properties, - importedLocations); - } - this->PopulateInterfaceProperties(te.get(), properties); - this->PopulateInterfaceLinkLibrariesProperty( - te->Target, cmGeneratorExpression::InstallInterface, properties); - this->PopulateLinkLibrariesProperty( - te->Target, cmGeneratorExpression::InstallInterface, properties); - - targets.push_back(TargetProperties{ - insert_back(project->RootElements, - this->GenerateImportTarget(ci, te->Target)), - te->Target, std::move(properties) }); - } - - for (auto const& target : targets) { - this->GenerateProperties(doc, project, ci, target, targets); - } - - this->WriteSbom(doc, os); -} - -std::string cmExportInstallSbomGenerator::GenerateImportPrefix() const -{ - std::string expDest = this->IEGen->GetDestination(); - if (cmSystemTools::FileIsFullPath(expDest)) { - std::string const& installPrefix = - this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition( - "CMAKE_INSTALL_PREFIX"); - if (cmHasPrefix(expDest, installPrefix)) { - auto n = installPrefix.length(); - while (n < expDest.length() && expDest[n] == '/') { - ++n; - } - expDest = expDest.substr(n); - } else { - this->ReportError( - cmStrCat("install(SBOM \"", this->GetExportName(), - "\" ...) specifies DESTINATION \"", expDest, - "\" which is not a subdirectory of the install prefix.")); - return {}; - } - } - - if (expDest.empty()) { - return this->GetInstallPrefix(); - } - return cmStrCat(this->GetImportPrefixWithSlash(), expDest); -} - -void cmExportInstallSbomGenerator::HandleMissingTarget( - std::string& /* link_libs */, cmGeneratorTarget const* /* depender */, - cmGeneratorTarget* /* dependee */) -{ -} - -bool cmExportInstallSbomGenerator::CheckInterfaceDirs( - std::string const& /* prepro */, cmGeneratorTarget const* /* target */, - std::string const& /* prop */) const -{ - return true; -} - -std::string cmExportInstallSbomGenerator::InstallNameDir( - cmGeneratorTarget const* target, std::string const& config) -{ - std::string install_name_dir; - - cmMakefile* mf = target->Target->GetMakefile(); - if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { - install_name_dir = - target->GetInstallNameDirForInstallTree(config, "@prefix@"); - } - - return install_name_dir; -} - -std::string cmExportInstallSbomGenerator::GetCxxModulesDirectory() const -{ - return {}; -} - -void cmExportInstallSbomGenerator::GenerateCxxModuleConfigInformation( - std::string const&, std::ostream&) const -{ -} - -std::string cmExportInstallSbomGenerator::GetCxxModuleFile( - std::string const& /* name */) const -{ - return {}; -} - -cm::optional<std::string> cmExportInstallSbomGenerator::GetFileSetDirectory( - cmGeneratorTarget* gte, cmTargetExport const* te, cmFileSet* fileSet, - cm::optional<std::string> const& config) -{ - cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); - auto cge = - ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination()); - - std::string const unescapedDest = - cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte); - bool const isConfigDependent = cge->GetHadContextSensitiveCondition(); - - if (config && !isConfigDependent) { - return {}; - } - - std::string const& type = fileSet->GetType(); - if (config && (type == "CXX_MODULES"_s)) { - cmMakefile* mf = gte->LocalGenerator->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive base file entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return {}; - } - - cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); - - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { - dest = cmStrCat("@prefix@/"_s, *dest); - } - - return dest; -}
diff --git a/Source/cmExportInstallSbomGenerator.h b/Source/cmExportInstallSbomGenerator.h deleted file mode 100644 index c7fb9b6..0000000 --- a/Source/cmExportInstallSbomGenerator.h +++ /dev/null
@@ -1,68 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <iosfwd> -#include <string> - -#include <cm/optional> -#include <cm/string_view> - -#include "cmExportInstallFileGenerator.h" -#include "cmExportSbomGenerator.h" - -class cmFileSet; -class cmGeneratorTarget; -class cmInstallExportGenerator; -class cmSbomArguments; -class cmTargetExport; - -class cmExportInstallSbomGenerator - : public cmExportSbomGenerator - , public cmExportInstallFileGenerator -{ -public: - /** Construct with the export installer that will install the - files. */ - cmExportInstallSbomGenerator(cmInstallExportGenerator* iegen, - cmSbomArguments arguments); - - /** Compute the globbing expression used to load per-config import - files from the main file. */ - std::string GetConfigImportFileGlob() const override; - -protected: - std::string const& GetExportName() const override; - - cm::string_view GetImportPrefixWithSlash() const override; - std::string GetCxxModuleFile(std::string const& name) const override; - void GenerateCxxModuleConfigInformation(std::string const&, - std::ostream& os) const override; - - // Implement virtual methods from the superclass. - bool GenerateMainFile(std::ostream& os) override; - void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, - std::string const& suffix) override; - - void HandleMissingTarget(std::string& /* link_libs */, - cmGeneratorTarget const* /* depender */, - cmGeneratorTarget* /* dependee */) override; - - bool CheckInterfaceDirs(std::string const& /* prepro */, - cmGeneratorTarget const* /* target */, - std::string const& /* prop */) const override; - - char GetConfigFileNameSeparator() const override { return '@'; } - - std::string GenerateImportPrefix() const; - std::string InstallNameDir(cmGeneratorTarget const* target, - std::string const& config) override; - - std::string GetCxxModulesDirectory() const override; - - cm::optional<std::string> GetFileSetDirectory( - cmGeneratorTarget* gte, cmTargetExport const* te, cmFileSet* fileSet, - cm::optional<std::string> const& config = {}); -};
diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx index 62a01e5..3b657c5 100644 --- a/Source/cmExportPackageInfoGenerator.cxx +++ b/Source/cmExportPackageInfoGenerator.cxx
@@ -18,6 +18,7 @@ #include "cmsys/RegularExpression.hxx" #include "cmArgumentParserTypes.h" +#include "cmDiagnostics.h" #include "cmExportSet.h" #include "cmFindPackageStack.h" #include "cmGeneratorExpression.h" @@ -118,10 +119,11 @@ // so, but will probably need to introduce a policy whether to treat // invalid versions as an error. } else if (schema != "custom"_s) { - this->IssueMessage(MessageType::AUTHOR_WARNING, - cmStrCat("Package \""_s, this->GetPackageName(), - "\" uses unrecognized version schema \""_s, - this->PackageVersionSchema, "\"."_s)); + this->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("Package \""_s, this->GetPackageName(), + "\" uses unrecognized version schema \""_s, + this->PackageVersionSchema, "\"."_s)); } if (validator) { @@ -574,8 +576,8 @@ BuildArray(component, "dyld_requires", components); if (!libraries.empty()) { // In theory this can never happen? - this->IssueMessage( - MessageType::AUTHOR_WARNING, + this->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Package \""_s, this->GetPackageName(), "\" has IMPORTED_LINK_DEPENDENT_LIBRARIES \""_s, cmJoin(libraries, ";"_s), this->PackageVersionSchema,
diff --git a/Source/cmExportSbomGenerator.cxx b/Source/cmExportSbomGenerator.cxx deleted file mode 100644 index c99141f..0000000 --- a/Source/cmExportSbomGenerator.cxx +++ /dev/null
@@ -1,388 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#include "cmExportSbomGenerator.h" - -#include <array> -#include <map> -#include <memory> -#include <set> -#include <string> -#include <utility> -#include <vector> - -#include <cm/optional> -#include <cmext/algorithm> - -#include "cmArgumentParserTypes.h" -#include "cmFindPackageStack.h" -#include "cmGeneratorExpression.h" -#include "cmGeneratorTarget.h" -#include "cmList.h" -#include "cmMakefile.h" -#include "cmMessageType.h" -#include "cmSbomArguments.h" -#include "cmSbomObject.h" -#include "cmSpdx.h" -#include "cmSpdxSerializer.h" -#include "cmStateTypes.h" -#include "cmStringAlgorithms.h" -#include "cmSystemTools.h" -#include "cmTarget.h" -#include "cmValue.h" - -cmSpdxPackage::PurposeId GetPurpose(cmStateEnums::TargetType type) -{ - switch (type) { - case cmStateEnums::TargetType::EXECUTABLE: - return cmSpdxPackage::PurposeId::APPLICATION; - case cmStateEnums::TargetType::STATIC_LIBRARY: - case cmStateEnums::TargetType::SHARED_LIBRARY: - case cmStateEnums::TargetType::MODULE_LIBRARY: - case cmStateEnums::TargetType::OBJECT_LIBRARY: - case cmStateEnums::TargetType::INTERFACE_LIBRARY: - return cmSpdxPackage::PurposeId::LIBRARY; - case cmStateEnums::TargetType::UTILITY: - return cmSpdxPackage::PurposeId::SOURCE; - case cmStateEnums::TargetType::GLOBAL_TARGET: - case cmStateEnums::TargetType::UNKNOWN_LIBRARY: - default: - return cmSpdxPackage::PurposeId::ARCHIVE; - } -} - -cmExportSbomGenerator::cmExportSbomGenerator(cmSbomArguments args) - : PackageName(std::move(args.PackageName)) - , PackageVersion(std::move(args.Version)) - , PackageDescription(std::move(args.Description)) - , PackageWebsite(std::move(args.Website)) - , PackageLicense(std::move(args.License)) - , PackageFormat(args.GetFormat()) -{ -} - -bool cmExportSbomGenerator::GenerateImportFile(std::ostream& os) -{ - return this->GenerateMainFile(os); -} - -void cmExportSbomGenerator::WriteSbom(cmSbomDocument& doc, - std::ostream& os) const -{ - switch (this->PackageFormat) { - case cmSbomArguments::SbomFormat::SPDX_3_0_JSON: - cmSpdxSerializer{}.WriteSbom(os, cmSbomObject(doc)); - break; - case cmSbomArguments::SbomFormat::NONE: - break; - } -} - -bool cmExportSbomGenerator::AddPackageInformation( - cmSpdxPackage& artifact, std::string const& name, - cmPackageInformation const& package) const -{ - if (name.empty()) { - return false; - } - - cmSpdxOrganization org; - org.SpdxId = cmStrCat("urn:", name, "#Organization"); - org.Name = name; - org.CreationInfo = artifact.CreationInfo; - artifact.OriginatedBy.emplace_back(std::move(org)); - - if (package.Description) { - artifact.Description = *package.Description; - } - - if (package.Version) { - artifact.PackageVersion = *package.Version; - } - - if (package.PackageUrl) { - artifact.PackageUrl = *package.PackageUrl; - } - - if (package.License) { - artifact.CopyrightText = *package.License; - } - - artifact.BuiltTime = cmSystemTools::GetCurrentDateTime("%FT%TZ"); - cmSpdxExternalRef externalRef; - externalRef.Locator = cmStrCat("cmake:find_package(", name, ")"); - externalRef.ExternalRefType = "buildSystem"; - return true; -} - -cmSpdxCreationInfo cmExportSbomGenerator::GenerateCreationInfo() const -{ - cmSpdxCreationInfo ci; - ci.SpdxId = "_:Build#CreationInfo"; - ci.Created = cmSystemTools::GetCurrentDateTime("%FT%TZ"); - ci.CreatedBy = { "https://gitlab.kitware.com/cmake/cmake" }; - ci.Comment = "This SBOM was generated from the CMakeLists.txt File"; - ci.SpecVersion = "3.0.1"; - return ci; -} - -cmSpdxDocument cmExportSbomGenerator::GenerateSbom( - cmSpdxCreationInfo const* ci) const -{ - cmSpdxDocument proj; - proj.Name = PackageName; - proj.SpdxId = cmStrCat("urn:", PackageName, "#SPDXDocument"); - proj.ProfileConformance = { "core", "software" }; - proj.CreationInfo = ci; - - if (!this->PackageDescription.empty()) { - proj.Description = this->PackageDescription; - } - - if (!this->PackageLicense.empty()) { - proj.DataLicense = this->PackageLicense; - } - - return proj; -} - -cmSpdxPackage cmExportSbomGenerator::GenerateImportTarget( - cmSpdxCreationInfo const* ci, cmGeneratorTarget const* target) const -{ - cmSpdxPackage package; - package.SpdxId = cmStrCat("urn:", target->GetName(), "#Package"); - package.Name = target->GetName(); - package.PrimaryPurpose = GetPurpose(target->GetType()); - package.CreationInfo = ci; - - if (!this->PackageVersion.empty()) { - package.PackageVersion = this->PackageVersion; - } - - if (!this->PackageWebsite.empty()) { - package.Homepage = this->PackageWebsite; - } - - if (!this->PackageUrl.empty()) { - package.DownloadLocation = this->PackageUrl; - } - - return package; -} - -void cmExportSbomGenerator::GenerateLinkProperties( - cmSbomDocument& doc, cmSpdxDocument* project, cmSpdxCreationInfo const* ci, - std::string const& libraries, TargetProperties const& current, - std::vector<TargetProperties> const& allTargets) const -{ - auto itProp = current.Properties.find(libraries); - if (itProp == current.Properties.end()) { - return; - } - - std::map<std::string, std::vector<std::string>> allowList = { { "LINK_ONLY", - {} } }; - std::string interfaceLinkLibraries; - if (!cmGeneratorExpression::ForbidGeneratorExpressions( - current.Target, itProp->first, itProp->second, interfaceLinkLibraries, - allowList)) { - return; - } - - auto makeRel = [&](char const* id, char const* desc) { - cmSpdxRelationship r; - r.SpdxId = cmStrCat("urn:", id, "#Relationship"); - r.RelationshipType = cmSpdxRelationship::RelationshipTypeId::DEPENDS_ON; - r.Description = desc; - r.From = current.Package; - r.CreationInfo = ci; - return r; - }; - - auto linkLibraries = makeRel("Static", "Linked Libraries"); - auto linkRequires = makeRel("Dynamic", "Required Runtime Libraries"); - auto buildRequires = makeRel("Shared", "Required Build-Time Libraries"); - - auto addArtifact = - [&](std::string const& name) -> std::pair<bool, cmSpdxPackage const*> { - auto it = this->LinkTargets.find(name); - if (it != this->LinkTargets.end()) { - LinkInfo const& linkInfo = it->second; - if (linkInfo.Package.empty()) { - for (auto const& t : allTargets) { - if (t.Target->GetName() == linkInfo.Component) { - return { true, t.Package }; - } - } - } - std::string pkgName = - cmStrCat(linkInfo.Package, ":", linkInfo.Component); - cmSpdxPackage pkg; - pkg.Name = pkgName; - pkg.SpdxId = cmStrCat("urn:", pkgName, "#Package"); - pkg.CreationInfo = ci; - if (!linkInfo.Package.empty()) { - auto const& pkgIt = this->Requirements.find(linkInfo.Package); - if (pkgIt != this->Requirements.end() && - pkgIt->second.Components.count(linkInfo.Component) > 0) { - this->AddPackageInformation(pkg, pkgIt->first, pkgIt->second); - } - } - return { true, insert_back(project->Elements, std::move(pkg)) }; - } - - cmSpdxPackage pkg; - pkg.SpdxId = cmStrCat("urn:", name, "#Package"); - pkg.Name = name; - pkg.CreationInfo = ci; - return { false, insert_back(project->Elements, std::move(pkg)) }; - }; - - auto handleDependencies = [&](std::vector<std::string> const& names, - cmSpdxRelationship& internalDeps, - cmSpdxRelationship& externalDeps) { - for (auto const& n : names) { - auto res = addArtifact(n); - if (!res.second) { - continue; - } - - if (res.first) { - internalDeps.To.push_back(res.second); - } else { - externalDeps.To.push_back(res.second); - } - } - }; - - handleDependencies(allowList["LINK_ONLY"], linkLibraries, linkRequires); - handleDependencies(cmList{ interfaceLinkLibraries }, linkLibraries, - buildRequires); - - if (!linkLibraries.To.empty()) { - insert_back(doc.Graph, std::move(linkLibraries)); - } - if (!linkRequires.To.empty()) { - insert_back(doc.Graph, std::move(linkRequires)); - } - if (!buildRequires.To.empty()) { - insert_back(doc.Graph, std::move(buildRequires)); - } -} - -bool cmExportSbomGenerator::GenerateProperties( - cmSbomDocument& doc, cmSpdxDocument* proj, cmSpdxCreationInfo const* ci, - TargetProperties const& current, - std::vector<TargetProperties> const& allTargets) const -{ - this->GenerateLinkProperties(doc, proj, ci, "LINK_LIBRARIES", current, - allTargets); - this->GenerateLinkProperties(doc, proj, ci, "INTERFACE_LINK_LIBRARIES", - current, allTargets); - return true; -} - -bool cmExportSbomGenerator::PopulateLinkLibrariesProperty( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - static std::array<std::string, 3> const linkIfaceProps = { - { "LINK_LIBRARIES", "LINK_LIBRARIES_DIRECT", - "LINK_LIBRARIES_DIRECT_EXCLUDE" } - }; - bool hadLINK_LIBRARIES = false; - for (std::string const& linkIfaceProp : linkIfaceProps) { - if (cmValue input = target->GetProperty(linkIfaceProp)) { - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, target, - ReplaceFreeTargets); - properties[linkIfaceProp] = prepro; - hadLINK_LIBRARIES = true; - } - } - } - return hadLINK_LIBRARIES; -} - -bool cmExportSbomGenerator::NoteLinkedTarget( - cmGeneratorTarget const* target, std::string const& linkedName, - cmGeneratorTarget const* linkedTarget) -{ - if (cm::contains(this->ExportedTargets, linkedTarget)) { - this->LinkTargets.emplace(linkedName, - LinkInfo{ "", linkedTarget->GetExportName() }); - return true; - } - - if (linkedTarget->IsImported()) { - using Package = cm::optional<std::pair<std::string, cmPackageInformation>>; - auto pkgInfo = [](cmTarget* t) -> Package { - cmFindPackageStack pkgStack = t->GetFindPackageStack(); - if (!pkgStack.Empty()) { - return std::make_pair(pkgStack.Top().Name, - *pkgStack.Top().PackageInfo); - } - std::string const pkgName = - t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME"); - if (pkgName.empty()) { - return cm::nullopt; - } - cmPackageInformation package; - return std::make_pair(pkgName, package); - }(linkedTarget->Target); - - if (!pkgInfo) { - target->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat("Target \"", target->GetName(), - "\" references imported target \"", linkedName, - "\" which does not come from any known package.")); - return false; - } - - std::string const& pkgName = pkgInfo->first; - auto const& prefix = cmStrCat(pkgName, "::"); - std::string component; - if (!cmHasPrefix(linkedName, prefix)) { - component = linkedName; - } else { - component = linkedName.substr(prefix.length()); - } - this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component }); - cmPackageInformation& req = - this->Requirements.insert(std::move(*pkgInfo)).first->second; - req.Components.emplace(std::move(component)); - return true; - } - - // Target belongs to another export from this build. - auto const& exportInfo = this->FindExportInfo(linkedTarget); - if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) { - auto const& linkNamespace = *exportInfo.Namespaces.begin(); - if (!cmHasSuffix(linkNamespace, "::")) { - target->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat("Target \"", target->GetName(), "\" references target \"", - linkedName, - "\", which does not use the standard namespace separator. " - "This is not allowed.")); - } - - std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 }; - std::string component = linkedTarget->GetExportName(); - if (pkgName == this->GetPackageName()) { - this->LinkTargets.emplace(linkedName, LinkInfo{ "", component }); - } else { - this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component }); - this->Requirements[pkgName].Components.emplace(std::move(component)); - } - return true; - } - - // Target belongs to multiple namespaces or multiple export sets. - // cmExportFileGenerator::HandleMissingTarget should have complained about - // this already. - return false; -}
diff --git a/Source/cmExportSbomGenerator.h b/Source/cmExportSbomGenerator.h deleted file mode 100644 index 6fb67a1..0000000 --- a/Source/cmExportSbomGenerator.h +++ /dev/null
@@ -1,86 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <iosfwd> -#include <map> -#include <string> -#include <vector> - -#include "cmExportFileGenerator.h" -#include "cmFindPackageStack.h" -#include "cmGeneratorExpression.h" -#include "cmSbomArguments.h" - -class cmGeneratorTarget; -struct cmSbomDocument; -struct cmSpdxDocument; -struct cmSpdxPackage; -struct cmSpdxCreationInfo; - -class cmExportSbomGenerator : virtual public cmExportFileGenerator -{ -public: - cmExportSbomGenerator(cmSbomArguments args); - using cmExportFileGenerator::GenerateImportFile; - -protected: - using ImportPropertyMap = std::map<std::string, std::string>; - - struct TargetProperties - { - cmSpdxPackage const* Package; - cmGeneratorTarget const* Target; - ImportPropertyMap Properties; - }; - - void WriteSbom(cmSbomDocument& doc, std::ostream& os) const; - - cmSpdxCreationInfo GenerateCreationInfo() const; - cmSpdxDocument GenerateSbom(cmSpdxCreationInfo const* ci) const; - cmSpdxPackage GenerateImportTarget(cmSpdxCreationInfo const* ci, - cmGeneratorTarget const* target) const; - - std::string const& GetPackageName() const { return this->PackageName; } - - bool GenerateImportFile(std::ostream& os) override; - bool AddPackageInformation(cmSpdxPackage& artifact, std::string const& name, - cmPackageInformation const& package) const; - - bool GenerateProperties( - cmSbomDocument& doc, cmSpdxDocument* project, cmSpdxCreationInfo const* ci, - TargetProperties const& current, - std::vector<TargetProperties> const& allTargets) const; - - void GenerateLinkProperties( - cmSbomDocument& doc, cmSpdxDocument* project, cmSpdxCreationInfo const* ci, - std::string const& libraries, TargetProperties const& current, - std::vector<TargetProperties> const& allTargets) const; - - bool NoteLinkedTarget(cmGeneratorTarget const* target, - std::string const& linkedName, - cmGeneratorTarget const* linkedTarget) override; - - bool PopulateLinkLibrariesProperty(cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext, - ImportPropertyMap& properties); - -private: - struct LinkInfo - { - std::string Package; - std::string Component; - }; - - std::string const PackageName; - std::string const PackageVersion; - std::string const PackageDescription; - std::string const PackageWebsite; - std::string const PackageLicense; - std::string const PackageUrl; - cmSbomArguments::SbomFormat const PackageFormat; - std::map<std::string, LinkInfo> LinkTargets; - std::map<std::string, cmPackageInformation> Requirements; -};
diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index 886c639..9db95df 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx
@@ -6,12 +6,13 @@ #include <utility> #include <cm/memory> +#include <cm/string_view> #include <cmext/string_view> -#include "cmFileSet.h" #include "cmGenExContext.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmList.h" @@ -38,14 +39,10 @@ { switch (type) { case MessageType::FATAL_ERROR: - case MessageType::AUTHOR_ERROR: case MessageType::INTERNAL_ERROR: - case MessageType::DEPRECATION_ERROR: cmSystemTools::Error(message); break; case MessageType::WARNING: - case MessageType::AUTHOR_WARNING: - case MessageType::DEPRECATION_WARNING: cmSystemTools::Message(cmStrCat("CMake Warning: "_s, message), "Warning"); break; @@ -54,6 +51,15 @@ } } +void cmExportTryCompileFileGenerator::IssueDiagnostic( + cmDiagnosticCategory category, std::string const& message) const +{ + cm::string_view const cname = + cmDiagnostics::GetCategoryString(category).substr(4); + cmSystemTools::Message( + cmStrCat("CMake Diagnostic ("_s, cname, "): "_s, message), "Diagnostic"); +} + bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os) { std::set<cmGeneratorTarget const*> emitted; @@ -176,14 +182,16 @@ } std::string cmExportTryCompileFileGenerator::GetFileSetDirectories( - cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport const* /*te*/) + cmGeneratorTarget* /*gte*/, cmGeneratorFileSet const* fileSet, + cmTargetExport const* /*te*/) { return cmOutputConverter::EscapeForCMake( cmList::to_string(fileSet->GetDirectoryEntries())); } std::string cmExportTryCompileFileGenerator::GetFileSetFiles( - cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport const* /*te*/) + cmGeneratorTarget* /*gte*/, cmGeneratorFileSet const* fileSet, + cmTargetExport const* /*te*/) { return cmOutputConverter::EscapeForCMake( cmList::to_string(fileSet->GetFileEntries()));
diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h index 00231ba..432a53c 100644 --- a/Source/cmExportTryCompileFileGenerator.h +++ b/Source/cmExportTryCompileFileGenerator.h
@@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "cmDiagnostics.h" #include "cmExportCMakeConfigGenerator.h" class cmGeneratorTarget; @@ -32,6 +33,8 @@ std::string const& /*targetName*/) const override {}; void IssueMessage(MessageType type, std::string const& message) const override; + void IssueDiagnostic(cmDiagnosticCategory category, + std::string const& message) const override; bool GenerateMainFile(std::ostream& os) override; @@ -57,10 +60,11 @@ std::string const& config) override; std::string GetFileSetDirectories(cmGeneratorTarget* target, - cmFileSet* fileSet, + cmGeneratorFileSet const* fileSet, cmTargetExport const* te) override; - std::string GetFileSetFiles(cmGeneratorTarget* target, cmFileSet* fileSet, + std::string GetFileSetFiles(cmGeneratorTarget* target, + cmGeneratorFileSet const* fileSet, cmTargetExport const* te) override; std::string GetCxxModulesDirectory() const override { return {}; }
diff --git a/Source/cmExprParserHelper.cxx b/Source/cmExprParserHelper.cxx index e2a2553..71c55d5 100644 --- a/Source/cmExprParserHelper.cxx +++ b/Source/cmExprParserHelper.cxx
@@ -100,6 +100,11 @@ this->ErrorString = ostr.str(); } +void cmExprParserHelper::Warning(std::string str) +{ + this->WarningString = cmStrCat(this->WarningString, std::move(str), '\n'); +} + void cmExprParserHelper::UnexpectedChar(char c) { unsigned long pos = static_cast<unsigned long>(this->InputBufferPos); @@ -109,7 +114,7 @@ this->WarningString += ostr.str(); } -void cmExprParserHelper::SetResult(KWIML_INT_int64_t value) +void cmExprParserHelper::SetResult(std::int64_t value) { this->Result = value; } @@ -118,3 +123,165 @@ { this->ErrorString = std::move(errorString); } + +std::int64_t cmExprParserHelper::ShL(std::int64_t l, std::int64_t r) +{ + if (l < 0) { + this->Warning( + cmStrCat("left shift of negative value in:\n ", l, " << ", r)); + } + if (r < 0) { + this->Warning( + cmStrCat("shift exponent is negative in:\n ", l, " << ", r)); + r &= 0x3F; + } + if (r >= 64) { + this->Warning( + cmStrCat("shift exponent is too large in:\n ", l, " << ", r)); + r &= 0x3F; + } + return static_cast<std::int64_t>(static_cast<std::uint64_t>(l) << r); +} + +std::int64_t cmExprParserHelper::ShR(std::int64_t l, std::int64_t r) +{ + if (r < 0) { + this->Warning( + cmStrCat("shift exponent is negative in:\n ", l, " >> ", r)); + r &= 0x3F; + } + if (r >= 64) { + this->Warning( + cmStrCat("shift exponent is too large in:\n ", l, " >> ", r)); + r &= 0x3F; + } + return l >> r; +} + +std::int64_t cmExprParserHelper::Add(std::int64_t l, std::int64_t r) +{ + std::int64_t sum; + if (this->AddOverflow(l, r, &sum)) { + this->Warning(cmStrCat("signed integer overflow in:\n ", l, " + ", r)); + } + return sum; +} + +std::int64_t cmExprParserHelper::Sub(std::int64_t l, std::int64_t r) +{ + std::int64_t diff; + if (this->SubOverflow(l, r, &diff)) { + this->Warning(cmStrCat("signed integer overflow in:\n ", l, " - ", r)); + } + return diff; +} + +std::int64_t cmExprParserHelper::Mul(std::int64_t l, std::int64_t r) +{ + std::int64_t prod; + if (this->MulOverflow(l, r, &prod)) { + this->Warning(cmStrCat("signed integer overflow in:\n ", l, " * ", r)); + } + return prod; +} + +std::int64_t cmExprParserHelper::Div(std::int64_t l, std::int64_t r) +{ + if (r == 0) { + throw std::overflow_error("divide by zero"); + } + if (l == INT64_MIN && r == -1) { + throw std::overflow_error("signed integer overflow in division"); + } + return l / r; +} + +std::int64_t cmExprParserHelper::Mod(std::int64_t l, std::int64_t r) +{ + if (r == 0) { + throw std::overflow_error("modulo by zero"); + } + if (l == INT64_MIN && r == -1) { + throw std::overflow_error("signed integer overflow in modulo"); + } + return l % r; +} + +std::int64_t cmExprParserHelper::Neg(std::int64_t x) +{ + if (static_cast<std::uint64_t>(x) == 0x8000000000000000) { + this->Warning(cmStrCat("signed integer cannot negate:\n ", x)); + return x; + } + return -x; +} + +// The __has_builtin preprocessor check was added in Clang 2.6 and GCC 10. +// The __builtin_X_overflow intrinsics were added in Clang 3.4 and GCC 5. +#ifndef __has_builtin +# if defined(__GNUC__) && __GNUC__ >= 5 +# define __has_builtin(x) 1 +# else +# define __has_builtin(x) 0 +# endif +#endif + +bool cmExprParserHelper::AddOverflow(long l, long r, long* p) +{ +#if __has_builtin(__builtin_saddl_overflow) + return __builtin_saddl_overflow(l, r, p); +#else + *p = l + r; + return false; +#endif +} + +bool cmExprParserHelper::AddOverflow(long long l, long long r, long long* p) +{ +#if __has_builtin(__builtin_saddll_overflow) + return __builtin_saddll_overflow(l, r, p); +#else + *p = l + r; + return false; +#endif +} + +bool cmExprParserHelper::SubOverflow(long l, long r, long* p) +{ +#if __has_builtin(__builtin_ssubl_overflow) + return __builtin_ssubl_overflow(l, r, p); +#else + *p = l - r; + return false; +#endif +} + +bool cmExprParserHelper::SubOverflow(long long l, long long r, long long* p) +{ +#if __has_builtin(__builtin_ssubll_overflow) + return __builtin_ssubll_overflow(l, r, p); +#else + *p = l - r; + return false; +#endif +} + +bool cmExprParserHelper::MulOverflow(long l, long r, long* p) +{ +#if __has_builtin(__builtin_smull_overflow) + return __builtin_smull_overflow(l, r, p); +#else + *p = l * r; + return false; +#endif +} + +bool cmExprParserHelper::MulOverflow(long long l, long long r, long long* p) +{ +#if __has_builtin(__builtin_smulll_overflow) + return __builtin_smulll_overflow(l, r, p); +#else + *p = l * r; + return false; +#endif +}
diff --git a/Source/cmExprParserHelper.h b/Source/cmExprParserHelper.h index 54ef7fd..94c0b68 100644 --- a/Source/cmExprParserHelper.h +++ b/Source/cmExprParserHelper.h
@@ -4,17 +4,16 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <cstdint> #include <string> #include <vector> -#include <cm3p/kwiml/int.h> - class cmExprParserHelper { public: struct ParserType { - KWIML_INT_int64_t Number; + std::int64_t Number; }; cmExprParserHelper(); @@ -24,10 +23,11 @@ int LexInput(char* buf, int maxlen); void Error(char const* str); + void Warning(std::string str); - void SetResult(KWIML_INT_int64_t value); + void SetResult(std::int64_t value); - KWIML_INT_int64_t GetResult() const { return this->Result; } + std::int64_t GetResult() const { return this->Result; } char const* GetError() { return this->ErrorString.c_str(); } @@ -35,6 +35,22 @@ std::string const& GetWarning() const { return this->WarningString; } + std::int64_t ShL(std::int64_t l, std::int64_t r); + std::int64_t ShR(std::int64_t l, std::int64_t r); + std::int64_t Add(std::int64_t l, std::int64_t r); + std::int64_t Sub(std::int64_t l, std::int64_t r); + std::int64_t Mul(std::int64_t l, std::int64_t r); + std::int64_t Div(std::int64_t l, std::int64_t r); + std::int64_t Mod(std::int64_t l, std::int64_t r); + std::int64_t Neg(std::int64_t x); + + static bool AddOverflow(long l, long r, long* p); + static bool AddOverflow(long long l, long long r, long long* p); + static bool SubOverflow(long l, long r, long* p); + static bool SubOverflow(long long l, long long r, long long* p); + static bool MulOverflow(long l, long r, long* p); + static bool MulOverflow(long long l, long long r, long long* p); + private: std::string::size_type InputBufferPos; std::string InputBuffer; @@ -46,7 +62,7 @@ void SetError(std::string errorString); - KWIML_INT_int64_t Result; + std::int64_t Result; char const* FileName; long FileLine; std::string ErrorString;
diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index 7427422..c70f3ff 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx
@@ -591,8 +591,7 @@ dir = frameworkRx.match(1); } - if (emittedDirs.find(dir) == emittedDirs.end()) { - emittedDirs.insert(dir); + if (emittedDirs.insert(dir).second) { xml.StartElement("pathentry"); xml.Attribute("include", cmExtraEclipseCDT4Generator::GetEclipsePath(dir)); @@ -783,8 +782,7 @@ } // insert the definition if not already added. - if (emitted.find(def) == emitted.end()) { - emitted.insert(def); + if (emitted.insert(def).second) { xml.StartElement("pathentry"); xml.Attribute("kind", "mac"); xml.Attribute("name", def); @@ -815,8 +813,7 @@ } // insert the definition if not already added. - if (emitted.find(def) == emitted.end()) { - emitted.insert(def); + if (emitted.insert(def).second) { xml.StartElement("pathentry"); xml.Attribute("kind", "mac"); xml.Attribute("name", def); @@ -847,8 +844,7 @@ } // insert the definition if not already added. - if (emitted.find(def) == emitted.end()) { - emitted.insert(def); + if (emitted.insert(def).second) { xml.StartElement("pathentry"); xml.Attribute("kind", "mac"); xml.Attribute("name", def);
diff --git a/Source/cmFLTKWrapUICommand.cxx b/Source/cmFLTKWrapUICommand.cxx index 269cf23..b0ef2d0 100644 --- a/Source/cmFLTKWrapUICommand.cxx +++ b/Source/cmFLTKWrapUICommand.cxx
@@ -10,17 +10,16 @@ #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmRange.h" #include "cmSourceFile.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmake.h" -class cmListFileBacktrace; class cmTarget; static void FinalAction(cmMakefile& makefile, std::string const& name, @@ -36,8 +35,7 @@ ". The problem was found while processing the source directory: ", makefile.GetCurrentSourceDirectory(), ". This FLTK_WRAP_UI call will be ignored."); - makefile.GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_ERROR, msg, - lfbt); + makefile.IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, msg, lfbt); } }
diff --git a/Source/cmFastbuildNormalTargetGenerator.cxx b/Source/cmFastbuildNormalTargetGenerator.cxx index ee9e9fb..68a3b0e 100644 --- a/Source/cmFastbuildNormalTargetGenerator.cxx +++ b/Source/cmFastbuildNormalTargetGenerator.cxx
@@ -25,8 +25,11 @@ #include "cmCommonTargetGenerator.h" #include "cmCryptoHash.h" #include "cmFastbuildTargetGenerator.h" +#include "cmFileSetMetadata.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorTarget.h" #include "cmGlobalCommonGenerator.h" #include "cmGlobalFastbuildGenerator.h" @@ -74,6 +77,32 @@ char const kPATH_SLASH = '/'; #endif +bool IsExcludedFromUnity(cmGeneratorTarget const* target, + cmGeneratorFileSet const* fileSet, + cmSourceFile const& srcFile) +{ + if (fileSet && + (!cm::FileSetMetadata::GetAttributes(fileSet->GetType()) + .contains(cm::FileSetMetadata::FileSetAttributes::UnityBuild) || + fileSet->GetProperty("SKIP_UNITY_BUILD_INCLUSION").IsOn() || + fileSet->GetProperty(fileSet->BelongsTo(target) + ? "COMPILE_OPTIONS" + : "INTERFACE_COMPILE_OPTIONS") || + fileSet->GetProperty(fileSet->BelongsTo(target) + ? "COMPILE_DEFINITIONS" + : "INTERFACE_COMPILE_DEFINITIONS") || + fileSet->GetProperty(fileSet->BelongsTo(target) + ? "INCLUDE_DIRECTORIES" + : "INTERFACE_INCLUDE_DIRECTORIES"))) { + return true; + } + return srcFile.GetPropertyAsBool(SKIP_UNITY_BUILD_INCLUSION) || + srcFile.GetProperty(COMPILE_OPTIONS) || + srcFile.GetProperty(COMPILE_DEFINITIONS) || + srcFile.GetProperty(COMPILE_FLAGS) || + srcFile.GetProperty(INCLUDE_DIRECTORIES); +} + } // anonymous namespace cmFastbuildNormalTargetGenerator::cmFastbuildNormalTargetGenerator( @@ -117,7 +146,20 @@ cmGeneratorExpressionInterpreter genexInterpreter( this->GetLocalGenerator(), Config, this->GeneratorTarget, language); + auto const* fileSet = + this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( + this->Config, &srcFile); + std::vector<std::string> sourceIncludesVec; + if (fileSet) { + auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetIncludeDirectories(this->Config, language) + : fileSet->GetInterfaceIncludeDirectories(this->Config, language); + if (!fsIncludes.empty()) { + this->LocalGenerator->AppendIncludeDirectories( + sourceIncludesVec, cm::remove_BT(fsIncludes), srcFile); + } + } if (cmValue cincludes = srcFile.GetProperty(INCLUDE_DIRECTORIES)) { this->LocalGenerator->AppendIncludeDirectories( sourceIncludesVec, @@ -140,6 +182,17 @@ this->LocalGenerator->AppendCompileOptions( compileFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); } + // Add flags from file set properties. + if (fileSet) { + auto options = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetCompileOptions(this->Config, language) + : fileSet->GetInterfaceCompileOptions(this->Config, language); + if (!options.empty()) { + this->LocalGenerator->AppendCompileOptions(compileFlags, + cm::remove_BT(options)); + } + } + // Source includes take precedence over target includes. this->LocalGenerator->AppendFlags(compileFlags, sourceIncludesStr); this->LocalGenerator->AppendFlags(compileFlags, @@ -411,6 +464,17 @@ genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS)); } + if (auto const* fileSet = + this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( + this->Config, &srcFile)) { + auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetCompileDefinitions(this->Config, language) + : fileSet->GetInterfaceCompileDefinitions(this->Config, language); + if (!fsDefines.empty()) { + this->LocalGenerator->AppendDefines(defines, fsDefines); + } + } + std::string definesString = this->GetDefines(language, Config); LogMessage(cmStrCat("TARGET DEFINES = ", definesString)); this->GetLocalGenerator()->JoinDefines(defines, definesString, language); @@ -426,6 +490,13 @@ if (srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS")) { return; } + + cmGeneratorFileSet const* const fileSet = + this->GeneratorTarget->GetFileSetForSource(Config, &srcFile); + if (fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + return; + } + // We have already computed PCH for this node. if (!node.PCHOptions.empty() || !node.PCHInputFile.empty() || !node.PCHOutputFile.empty()) { @@ -1104,10 +1175,16 @@ std::string cmFastbuildNormalTargetGenerator::ComputeCodeCheckOptions( cmSourceFile const& srcFile) { + cmGeneratorFileSet const* fileSet = + this->GeneratorTarget->GetFileSetForSource(Config, &srcFile); + cmValue const fsSkipCodeCheckVal = + fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr; cmValue const srcSkipCodeCheckVal = srcFile.GetProperty("SKIP_LINTING"); - bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet() - ? srcSkipCodeCheckVal.IsOn() - : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING"); + bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet() + ? fsSkipCodeCheckVal.IsOn() + : (srcSkipCodeCheckVal.IsSet() + ? srcSkipCodeCheckVal.IsOn() + : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING")); if (skipCodeCheck) { return {}; @@ -1376,8 +1453,8 @@ useUnity = false; } - // List of sources isolated from the unity build if enabled. - std::set<std::string> isolatedFromUnity; + // List of sources excluded from the unity build if enabled. + std::set<std::string> excludedFromUnity; // Mapping from unity group (if any) to sources belonging to that group. std::map<std::string, std::vector<std::string>> sourcesWithGroups; @@ -1386,12 +1463,14 @@ cmSourceFile const& srcFile = *source; std::string const pathToFile = srcFile.GetFullPath(); + cmGeneratorFileSet const* const fileSet = + GeneratorTarget->GetFileSetForSource(Config, source); bool fileUsesUnity = useUnity; if (useUnity) { - // Check if the source should be added to "UnityInputIsolatedFiles". - if (srcFile.GetPropertyAsBool(SKIP_UNITY_BUILD_INCLUSION)) { + // Check if the source should be added to "UnityInputExcludedFiles". + if (IsExcludedFromUnity(GeneratorTarget, fileSet, srcFile)) { fileUsesUnity = false; - isolatedFromUnity.emplace(pathToFile); + excludedFromUnity.emplace(pathToFile); } std::string const perFileUnityGroup = srcFile.GetSafeProperty(UNITY_GROUP); @@ -1446,7 +1525,8 @@ std::string const objectListHash = hash.HashString(cmStrCat( compileOptions, staticCheckOptions, objOutDirWithPossibleSubdir, // If file does not need PCH - it must be in another ObjectList. - srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS"), + (fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS"), srcFile.GetLanguage())); LogMessage("ObjectList Hash: " + objectListHash); @@ -1530,27 +1610,27 @@ } if (useUnity) { target.UnityNodes = - GenerateUnity(objects, isolatedFromUnity, sourcesWithGroups); + GenerateUnity(objects, excludedFromUnity, sourcesWithGroups); } } FastbuildUnityNode cmFastbuildNormalTargetGenerator::GetOneUnity( - std::set<std::string> const& isolatedFiles, std::vector<std::string>& files, + std::set<std::string> const& excludedFiles, std::vector<std::string>& files, int unitySize) const { FastbuildUnityNode result; for (auto iter = files.begin(); iter != files.end();) { std::string pathToFile = std::move(*iter); iter = files.erase(iter); - // This source must be isolated - if (isolatedFiles.find(pathToFile) != isolatedFiles.end()) { + // This source must be excluded from the generated unity file. + if (excludedFiles.find(pathToFile) != excludedFiles.end()) { result.UnityInputFiles.emplace_back(pathToFile); - result.UnityInputIsolatedFiles.emplace_back(std::move(pathToFile)); + result.UnityInputExcludedFiles.emplace_back(std::move(pathToFile)); } else { result.UnityInputFiles.emplace_back(std::move(pathToFile)); } if (int(result.UnityInputFiles.size() - - result.UnityInputIsolatedFiles.size()) == unitySize) { + result.UnityInputExcludedFiles.size()) == unitySize) { break; } } @@ -1580,7 +1660,7 @@ std::vector<FastbuildUnityNode> cmFastbuildNormalTargetGenerator::GenerateUnity( std::vector<FastbuildObjectListNode>& objects, - std::set<std::string> const& isolatedSources, + std::set<std::string> const& excludedSources, std::map<std::string, std::vector<std::string>> const& sourcesWithGroups) { int const unitySize = GetUnityBatchSize(); @@ -1613,14 +1693,14 @@ // General unity batching of the remaining (non-grouped) sources. while (!obj.CompilerInputFiles.empty()) { FastbuildUnityNode node = - GetOneUnity(isolatedSources, obj.CompilerInputFiles, unitySize); + GetOneUnity(excludedSources, obj.CompilerInputFiles, unitySize); node.Name = cmStrCat(this->GetName(), "_Unity_", ++unityNumber); node.UnityOutputPath = obj.CompilerOutputPath; node.UnityOutputPattern = cmStrCat(node.Name, ext); - // Unity group of size 1 doesn't make sense - just isolate the source. + // Unity group of size 1 doesn't make sense - just exclude the source. if (groupedNode.UnityInputFiles.size() == 1) { - node.UnityInputIsolatedFiles.emplace_back( + node.UnityInputExcludedFiles.emplace_back( groupedNode.UnityInputFiles[0]); node.UnityInputFiles.emplace_back( std::move(groupedNode.UnityInputFiles[0]));
diff --git a/Source/cmFastbuildNormalTargetGenerator.h b/Source/cmFastbuildNormalTargetGenerator.h index 8a01f77..bc630b4 100644 --- a/Source/cmFastbuildNormalTargetGenerator.h +++ b/Source/cmFastbuildNormalTargetGenerator.h
@@ -76,14 +76,14 @@ std::string& args) const; void GenerateCudaDeviceLink(FastbuildTarget& target) const; void GenerateObjects(FastbuildTarget& target); - FastbuildUnityNode GetOneUnity(std::set<std::string> const& isolatedFiles, + FastbuildUnityNode GetOneUnity(std::set<std::string> const& excludedFiles, std::vector<std::string>& files, int unitySize) const; int GetUnityBatchSize() const; std::vector<FastbuildUnityNode> GenerateUnity( std::vector<FastbuildObjectListNode>& objects, - std::set<std::string> const& isolatedSources, + std::set<std::string> const& excludedSources, std::map<std::string, std::vector<std::string>> const& sourcesWithGroups); FastbuildUnityNode GenerateGroupedUnityNode( std::vector<std::string>& inputFiles,
diff --git a/Source/cmFastbuildTargetGenerator.cxx b/Source/cmFastbuildTargetGenerator.cxx index 06f6ab9..2c008bb 100644 --- a/Source/cmFastbuildTargetGenerator.cxx +++ b/Source/cmFastbuildTargetGenerator.cxx
@@ -356,7 +356,8 @@ for (auto dep : ccg.GetDepends()) { LogMessage("Dep: " + dep); auto orig = dep; - if (this->LocalCommonGenerator->GetRealDependency(dep, Config, dep)) { + if (this->LocalCommonGenerator->GetRealDependency( + dep, Config, dep, ccg.GetCC().GetCMP0212Status())) { LogMessage("Real dep: " + dep); if (!dep.empty()) { LogMessage("Custom command real dep: " + dep);
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index fa9c1c5..ff31855 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx
@@ -148,10 +148,11 @@ std::vector<std::string> files; cmsys::Directory d; d.Load(dir); + files.reserve(d.GetNumberOfFiles()); for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) { - std::string f = d.GetFile(i); + std::string const& f = d.GetFileName(i); if (f != "." && f != "..") { - files.push_back(std::move(f)); + files.push_back(f); } } std::sort(files.begin(), files.end()); @@ -822,7 +823,7 @@ // Update the following files as well when updating this constant: // Help/manual/cmake-file-api.7.rst // Tests/RunCMake/FileAPI/codemodel-v2-check.py (check_objects()) -static unsigned int const CodeModelV2Minor = 10; +static unsigned int const CodeModelV2Minor = 11; void cmFileAPI::BuildClientRequestCodeModel( ClientRequest& r, std::vector<RequestVersion> const& versions)
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index 0d916b0..2d81094 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx
@@ -17,16 +17,16 @@ #include <cm/string_view> #include <cmext/algorithm> -#include <cmext/string_view> #include <cm3p/json/value.h> #include "cmCryptoHash.h" #include "cmExportSet.h" #include "cmFileAPI.h" -#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGenExContext.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmInstallCxxModuleBmiGenerator.h" @@ -48,7 +48,6 @@ #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmRange.h" #include "cmSourceFile.h" #include "cmSourceGroup.h" @@ -463,9 +462,13 @@ std::unordered_map<CompileData, Json::ArrayIndex> CompileGroupMap; std::vector<CompileGroup> CompileGroups; - using FileSetDatabase = std::map<std::string, Json::ArrayIndex>; + using FileSetDatabase = std::map<std::string, std::vector<Json::ArrayIndex>>; - std::vector<cmFileSetVisibility> FileSetVisibilities; + using FileSetBacktraceDatabase = + std::unordered_map<std::string, std::vector<cmListFileBacktrace>>; + + std::vector<cm::FileSetMetadata::Visibility> FileSetVisibilities; + FileSetBacktraceDatabase FileSetBacktraces; template <typename T> JBT<T> ToJBT(BT<T> const& bt) @@ -502,7 +505,7 @@ Json::Value DumpLanguageStandard(JBTs<std::string> const& standard); Json::Value DumpDefine(JBT<std::string> const& def); std::pair<Json::Value, FileSetDatabase> DumpFileSets(); - Json::Value DumpFileSet(cmFileSet const* fs, + Json::Value DumpFileSet(cmGeneratorFileSet const* fs, std::vector<std::string> const& directories); Json::Value DumpSources(FileSetDatabase const& fsdb); Json::Value DumpSource(cmGeneratorTarget::SourceAndKind const& sk, @@ -1148,18 +1151,11 @@ auto* target = installFileSet->GetTarget(); cm::GenEx::Context context(target->LocalGenerator, this->Config); - - auto dirCges = fileSet->CompileDirectoryEntries(); - auto dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, target); - - auto entryCges = fileSet->CompileFileEntries(); - std::map<std::string, std::vector<std::string>> entries; - for (auto const& entryCge : entryCges) { - fileSet->EvaluateFileEntry(dirs, entries, entryCge, context, target); - } + auto dirs = fileSet->GetDirectories(context, target); + auto entries = fileSet->GetFiles(context, target); Json::Value files = Json::arrayValue; - for (auto const& it : entries) { + for (auto const& it : entries.first) { auto dir = it.first; if (!dir.empty()) { dir += '/'; @@ -1174,7 +1170,7 @@ installer["fileSetName"] = fileSet->GetName(); installer["fileSetType"] = fileSet->GetType(); installer["fileSetDirectories"] = Json::arrayValue; - for (auto const& dir : dirs) { + for (auto const& dir : dirs.first) { installer["fileSetDirectories"].append( RelativeIfUnder(this->TopSource, dir)); } @@ -1503,6 +1499,9 @@ cmGeneratorExpressionInterpreter genexInterpreter(lg, this->Config, this->GT, fd.Language); + cmGeneratorFileSet const* fileSet = + GT->GetFileSetForSource(this->Config, sf); + std::string const COMPILE_FLAGS("COMPILE_FLAGS"); if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) { std::string flags = genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS); @@ -1519,6 +1518,19 @@ BT<std::string> opt(tmp, tmpOpt.Backtrace); fd.Flags.emplace_back(this->ToJBT(opt)); } + // File set compile options, if any + if (fileSet) { + for (BT<std::string> const& tmpOpt : fileSet->BelongsTo(this->GT) + ? fileSet->GetCompileOptions(this->Config, fd.Language) + : fileSet->GetInterfaceCompileOptions(this->Config, fd.Language)) { + // We need to use the AppendCompileOptions method so we handle situations + // where backtrace entries have list and properly escape flags. + std::string tmp; + lg->AppendCompileOptions(tmp, tmpOpt.Value); + BT<std::string> opt(tmp, tmpOpt.Backtrace); + fd.Flags.emplace_back(this->ToJBT(opt)); + } + } // Add precompile headers compile options. std::vector<std::string> pchArchs = @@ -1533,7 +1545,9 @@ } } - if (!pchSources.empty() && !sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!pchSources.empty() && + !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + sf->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { std::string pchOptions; auto pchIt = pchSources.find(sf->ResolveFullPath()); if (pchIt != pchSources.end()) { @@ -1556,9 +1570,31 @@ fd.Flags.emplace_back(this->ToJBT(opt)); } + std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + // Add include directories from file set properties. + if (fileSet) { + for (BT<std::string> const& tmpInclude : fileSet->BelongsTo(this->GT) + ? fileSet->GetIncludeDirectories(this->Config, fd.Language) + : fileSet->GetInterfaceIncludeDirectories(this->Config, + fd.Language)) { + // We need to use the AppendIncludeDirectories method so we handle + // situations where backtrace entries have lists. + std::vector<std::string> tmp; + lg->AppendIncludeDirectories(tmp, tmpInclude.Value, *sf); + for (std::string& i : tmp) { + bool const isSystemInclude = + this->GT->IsSystemIncludeDirectory(i, this->Config, fd.Language); + BT<std::string> include(i, tmpInclude.Backtrace); + if (this->GT->IsApple() && cmSystemTools::IsPathToFramework(i)) { + fd.Frameworks.emplace_back(this->ToJBT(include), isSystemInclude); + } else { + fd.Includes.emplace_back(this->ToJBT(include), isSystemInclude); + } + } + } + } // Add include directories from source file properties. { - std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); for (BT<std::string> tmpInclude : sf->GetIncludeDirectories()) { tmpInclude.Value = genexInterpreter.Evaluate(tmpInclude.Value, INCLUDE_DIRECTORIES); @@ -1605,7 +1641,25 @@ genexInterpreter.Evaluate(*config_defs, COMPILE_DEFINITIONS)); } - fd.Defines.reserve(fileDefines.size() + configFileDefines.size()); + std::set<BT<std::string>> fileSetDefines; + if (fileSet) { + for (BT<std::string> const& tmpDef : fileSet->BelongsTo(this->GT) + ? fileSet->GetCompileDefinitions(this->Config, fd.Language) + : fileSet->GetInterfaceCompileDefinitions(this->Config, + fd.Language)) { + // We need to use the AppendDefines method so we handle situations where + // backtrace entries have lists. + std::set<std::string> tmp; + lg->AppendDefines(tmp, tmpDef.Value); + for (std::string const& i : tmp) { + BT<std::string> def(i, tmpDef.Backtrace); + fileSetDefines.insert(def); + } + } + } + + fd.Defines.reserve(fileDefines.size() + configFileDefines.size() + + fileSetDefines.size()); for (BT<std::string> const& def : fileDefines) { fd.Defines.emplace_back(this->ToJBT(def)); @@ -1615,6 +1669,10 @@ fd.Defines.emplace_back(d, JBTIndex()); } + for (BT<std::string> const& def : fileSetDefines) { + fd.Defines.emplace_back(this->ToJBT(def)); + } + return fd; } @@ -1724,43 +1782,25 @@ // interface sources, which needs to map files to file set visibility // with only an index available. Those indexes match this vector. this->FileSetVisibilities.clear(); + this->FileSetBacktraces.clear(); // Build the fileset database. - auto const* tgt = this->GT->Target; - auto const& fs_names = tgt->GetAllFileSetNames(); + auto const& fileSets = this->GT->GetAllFileSets(); - if (!fs_names.empty()) { + if (!fileSets.empty()) { fsJson = Json::arrayValue; size_t fsIndex = 0; - for (auto const& fs_name : fs_names) { - auto const* fs = tgt->GetFileSet(fs_name); - if (!fs) { - this->GT->Makefile->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), - "\" is tracked to have file set \"", fs_name, - "\", but it was not found.")); - continue; - } - + for (auto const* fs : fileSets) { cm::GenEx::Context context(this->GT->LocalGenerator, this->Config); - auto fileEntries = fs->CompileFileEntries(); - auto directoryEntries = fs->CompileDirectoryEntries(); + auto directories = fs->GetDirectories(context, this->GT); - auto directories = - fs->EvaluateDirectoryEntries(directoryEntries, context, this->GT); - - fsJson.append(this->DumpFileSet(fs, directories)); + fsJson.append(this->DumpFileSet(fs, directories.first)); this->FileSetVisibilities.push_back(fs->GetVisibility()); - std::map<std::string, std::vector<std::string>> files_per_dirs; - for (auto const& entry : fileEntries) { - fs->EvaluateFileEntry(directories, files_per_dirs, entry, context, - this->GT); - } + auto files_per_dirs = fs->GetFiles(context, this->GT); - for (auto const& files_per_dir : files_per_dirs) { + for (auto const& files_per_dir : files_per_dirs.first) { auto const& dir = files_per_dir.first; for (auto const& file : files_per_dir.second) { std::string sf_path; @@ -1769,7 +1809,35 @@ } else { sf_path = cmStrCat(dir, '/', file); } - fsdb[sf_path] = static_cast<Json::ArrayIndex>(fsIndex); + fsdb[sf_path].emplace_back(static_cast<Json::ArrayIndex>(fsIndex)); + } + } + + // Collect backtraces from each original file set FILES entry so that + // source backtraces preserve line metadata and can include repeated + // additions from multiple file sets. + auto const& fileEntries = fs->GetFileEntries(); + for (BT<std::string> const& fileEntry : fileEntries) { + cmGeneratorExpression ge( + *this->GT->GetLocalGenerator()->GetCMakeInstance(), + fileEntry.Backtrace); + for (std::string const& ex : cmList{ fileEntry.Value }) { + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(ex); + std::map<std::string, std::vector<std::string>> filesForEntry; + fs->EvaluateFileEntry(directories.first, filesForEntry, cge, context, + this->GT); + for (auto const& filesPerDir : filesForEntry) { + std::string const& dir = filesPerDir.first; + for (std::string const& file : filesPerDir.second) { + std::string sf_path; + if (dir.empty() || cmSystemTools::FileIsFullPath(file)) { + sf_path = file; + } else { + sf_path = cmStrCat(dir, '/', file); + } + this->FileSetBacktraces[sf_path].push_back(fileEntry.Backtrace); + } + } } } @@ -1780,7 +1848,7 @@ return std::make_pair(fsJson, fsdb); } -Json::Value Target::DumpFileSet(cmFileSet const* fs, +Json::Value Target::DumpFileSet(cmGeneratorFileSet const* fs, std::vector<std::string> const& directories) { Json::Value fileSet = Json::objectValue; @@ -1788,7 +1856,7 @@ fileSet["name"] = fs->GetName(); fileSet["type"] = fs->GetType(); fileSet["visibility"] = - std::string(cmFileSetVisibilityToName(fs->GetVisibility())); + std::string(cm::FileSetMetadata::VisibilityToName(fs->GetVisibility())); Json::Value baseDirs = Json::arrayValue; for (auto const& directory : directories) { @@ -1822,11 +1890,40 @@ if (sk.Source.Value->GetIsGenerated()) { source["isGenerated"] = true; } - this->AddBacktrace(source, sk.Source.Backtrace); + + JBTIndex sourceBacktrace = this->Backtraces.Add(sk.Source.Backtrace); + JBTIndex primaryBacktrace = sourceBacktrace; + Json::Value backtraces = Json::arrayValue; + auto const fileSetBacktraces = this->FileSetBacktraces.find(path); + if (fileSetBacktraces != this->FileSetBacktraces.end() && + !fileSetBacktraces->second.empty()) { + for (cmListFileBacktrace const& fsbt : fileSetBacktraces->second) { + if (JBTIndex bt = this->Backtraces.Add(fsbt)) { + if (!primaryBacktrace) { + primaryBacktrace = bt; + } + backtraces.append(bt.Index); + } + } + } else { + if (sourceBacktrace) { + backtraces.append(sourceBacktrace.Index); + } + } + + this->AddBacktrace(source, primaryBacktrace); + + if (!backtraces.empty()) { + source["backtraces"] = std::move(backtraces); + } auto fsit = fsdb.find(path); if (fsit != fsdb.end()) { - source["fileSetIndex"] = fsit->second; + source["fileSetIndex"] = fsit->second.back(); + source["fileSetIndexes"] = Json::arrayValue; + for (Json::ArrayIndex const& fsIndex : fsit->second) { + source["fileSetIndexes"].append(fsIndex); + } } if (cmSourceGroup const* sg = @@ -1854,6 +1951,7 @@ case cmGeneratorTarget::SourceKindResx: case cmGeneratorTarget::SourceKindXaml: case cmGeneratorTarget::SourceKindUnityBatched: + case cmGeneratorTarget::SourceKindRustMainCrateRoot: break; } @@ -1886,10 +1984,11 @@ } for (auto const& fsIter : fsdb) { - Json::ArrayIndex const index = fsIter.second; + Json::ArrayIndex const index = fsIter.second.back(); // FileSetVisibilities was populated by DumpFileSets() and will always // have the same size as the file sets array that index is indexing into - if (this->FileSetVisibilities[index] != cmFileSetVisibility::Private) { + if (this->FileSetVisibilities[index] != + cm::FileSetMetadata::Visibility::Private) { dumpFile(fsIter.first); } } @@ -1911,7 +2010,11 @@ auto fsit = fsdb.find(path); if (fsit != fsdb.end()) { - source["fileSetIndex"] = fsit->second; + source["fileSetIndex"] = fsit->second.back(); + source["fileSetIndexes"] = Json::arrayValue; + for (Json::ArrayIndex const& fsIndex : fsit->second) { + source["fileSetIndexes"].append(fsIndex); + } } if (cmSourceGroup const* sg =
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index fa87850..366314c 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx
@@ -33,6 +33,7 @@ #include "cmArgumentParserTypes.h" #include "cmCMakePath.h" #include "cmCryptoHash.h" +#include "cmDiagnostics.h" #include "cmELF.h" #include "cmExecutionStatus.h" #include "cmFSPermissions.h" @@ -415,12 +416,10 @@ case cmPolicies::WARN: if (status.GetMakefile().PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0159")) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0159), - "\n" - "For compatibility, CMake is leaving CMAKE_MATCH_<n> " - "unchanged.")); + status.GetMakefile().IssuePolicyWarning( + cmPolicies::CMP0159, {}, + "For compatibility, CMake is leaving CMAKE_MATCH_<n> " + "unchanged."_s); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -732,8 +731,8 @@ } else if (*i == "CONFIGURE_DEPENDS") { // Generated build system depends on glob results if (!configureDepends && warnConfigureLate) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, "CONFIGURE_DEPENDS flag was given after a glob expression was " "already evaluated."); } @@ -769,8 +768,8 @@ bool shouldExit = false; for (cmsys::Glob::Message const& globMessage : globMessages) { if (globMessage.type == cmsys::Glob::cyclicRecursion) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Cyclic recursion detected while globbing for '", *i, "':\n", globMessage.content)); } else if (globMessage.type == cmsys::Glob::error) { @@ -1375,25 +1374,22 @@ if (warnAbout152) { computeNewPath(input, realPath); if (oldPolicyPath != realPath) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0152), - "\n" - "From input path:\n ", - input, "\nthe policy OLD behavior produces path:\n ", - oldPolicyPath, - "\nbut the policy NEW behavior produces path:\n ", - realPath, - "\nSince the policy is not set, CMake is using the OLD " - "behavior for compatibility.")); + status.GetMakefile().IssuePolicyWarning( + cmPolicies::CMP0152, {}, + cmStrCat( + "From input path:\n ", input, + "\nthe policy OLD behavior produces path:\n ", oldPolicyPath, + "\nbut the policy NEW behavior produces path:\n ", realPath, + "\nSince the policy is not set, CMake is using the OLD " + "behavior for compatibility.")); } } realPath = oldPolicyPath; } if (!cmSystemTools::FileExists(realPath)) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Given path:\n ", input, "\ndoes not refer to an existing path on disk.")); } @@ -1609,8 +1605,8 @@ std::string fileName = arg; if (fileName.empty()) { std::string r = recurse ? "REMOVE_RECURSE" : "REMOVE"; - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Ignoring empty file name in ", std::move(r), '.')); continue; } @@ -2060,8 +2056,8 @@ file = *i; } else { // Do not return error for compatibility reason. - std::string err = cmStrCat("Unexpected argument: ", *i); - status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, err); + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Unexpected argument: ", *i)); } ++i; } @@ -2508,8 +2504,8 @@ curl_headers.push_back(*i); } else { // Do not return error for compatibility reason. - std::string err = cmStrCat("Unexpected argument: ", *i); - status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, err); + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Unexpected argument: ", *i)); } ++i; @@ -3177,7 +3173,7 @@ bool HandleReadSymlinkCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - if (args.size() != 3) { + if (args.size() < 3) { status.SetError( cmStrCat(args[0], " requires a file name and output variable")); return false; @@ -3186,14 +3182,39 @@ std::string const& filename = args[1]; std::string const& outputVariable = args[2]; + struct Arguments + { + std::string Result; + }; + + static auto const parser = + cmArgumentParser<Arguments>{}.Bind("RESULT"_s, &Arguments::Result); + + std::vector<std::string> unconsumedArgs; + Arguments const arguments = + parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs); + if (!unconsumedArgs.empty()) { + status.SetError( + cmStrCat("READ_SYMLINK unknown argument:\n ", unconsumedArgs.front())); + return false; + } + std::string result; if (!cmSystemTools::ReadSymlink(filename, result)) { - status.SetError(cmStrCat( - "READ_SYMLINK requested of path that is not a symlink:\n ", filename)); + std::string const error = cmStrCat( + "READ_SYMLINK requested of path that is not a symlink:\n ", filename); + if (!arguments.Result.empty()) { + status.GetMakefile().AddDefinition(arguments.Result, error); + return true; + } + status.SetError(error); return false; } status.GetMakefile().AddDefinition(outputVariable, result); + if (!arguments.Result.empty()) { + status.GetMakefile().AddDefinition(arguments.Result, "0"); + } return true; } @@ -3309,12 +3330,11 @@ if (cmp0205 == cmPolicies::NEW) { needToTry = false; } else if (cmp0205 == cmPolicies::WARN && arguments.CopyOnError) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssuePolicyWarning( + cmPolicies::CMP0205, cmStrCat("Path\n ", fileName, "\nis a directory. Hard link creation is not supported " - "for directories.\n", - cmPolicies::GetPolicyWarning(cmPolicies::CMP0205))); + "for directories.")); } } @@ -3335,12 +3355,11 @@ if (cmp0205 == cmPolicies::WARN && arguments.CopyOnError && sourceIsDirectory) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssuePolicyWarning( + cmPolicies::CMP0205, cmStrCat("Path\n ", fileName, "\nis a directory. It will be copied " - "recursively when CMP0205 is set to NEW.\n", - cmPolicies::GetPolicyWarning(cmPolicies::CMP0205))); + "recursively when CMP0205 is set to NEW.")); } // Check if copy-on-error is enabled in the arguments. @@ -3390,8 +3409,8 @@ } if (status.GetMakefile().GetState()->GetRole() == cmState::Role::Project) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, "You have used file(GET_RUNTIME_DEPENDENCIES)" " in project mode. This is probably not what " "you intended to do. Instead, please consider" @@ -3691,6 +3710,7 @@ std::string Format; std::string Compression; std::string CompressionLevel; + std::string Encoding; // "MTIME" should require one value, but it has long been accidentally // accepted without one and treated as if an empty value were given. // Fixing this would require a policy. @@ -3708,6 +3728,7 @@ .Bind("FORMAT"_s, &Arguments::Format) .Bind("COMPRESSION"_s, &Arguments::Compression) .Bind("COMPRESSION_LEVEL"_s, &Arguments::CompressionLevel) + .Bind("ENCODING"_s, &Arguments::Encoding) .Bind("MTIME"_s, &Arguments::MTime) .Bind("THREADS"_s, &Arguments::Threads) .Bind("WORKING_DIRECTORY"_s, &Arguments::WorkingDirectory) @@ -3835,10 +3856,19 @@ return false; } + if (parsedArgs.Encoding.empty()) { + if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0213) == + cmPolicies::NEW) { + parsedArgs.Encoding = "UTF-8"; + } else { + parsedArgs.Encoding = "OEM"; + } + } + if (!cmSystemTools::CreateTar( parsedArgs.Output, parsedArgs.Paths, parsedArgs.WorkingDirectory, - compress, parsedArgs.Verbose, parsedArgs.MTime, parsedArgs.Format, - compressionLevel, threads)) { + compress, parsedArgs.Encoding, parsedArgs.Verbose, parsedArgs.MTime, + parsedArgs.Format, compressionLevel, threads)) { status.SetError(cmStrCat("failed to compress: ", parsedArgs.Output)); cmSystemTools::SetFatalErrorOccurred(); return false; @@ -3853,6 +3883,7 @@ struct Arguments : public ArgumentParser::ParseResult { std::string Input; + std::string Encoding; bool Verbose = false; bool ListOnly = false; std::string Destination; @@ -3862,6 +3893,7 @@ static auto const parser = cmArgumentParser<Arguments>{} .Bind("INPUT"_s, &Arguments::Input) + .Bind("ENCODING"_s, &Arguments::Encoding) .Bind("VERBOSE"_s, &Arguments::Verbose) .Bind("LIST_ONLY"_s, &Arguments::ListOnly) .Bind("DESTINATION"_s, &Arguments::Destination) @@ -3885,9 +3917,18 @@ std::string inFile = parsedArgs.Input; + if (parsedArgs.Encoding.empty()) { + if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0213) == + cmPolicies::NEW) { + parsedArgs.Encoding = "UTF-8"; + } else { + parsedArgs.Encoding = "OEM"; + } + } + if (parsedArgs.ListOnly) { if (!cmSystemTools::ListTar(inFile, parsedArgs.Patterns, - parsedArgs.Verbose)) { + parsedArgs.Encoding, parsedArgs.Verbose)) { status.SetError(cmStrCat("failed to list: ", inFile)); cmSystemTools::SetFatalErrorOccurred(); return false; @@ -3924,7 +3965,7 @@ inFile, parsedArgs.Patterns, parsedArgs.Touch ? cmSystemTools::cmTarExtractTimestamps::No : cmSystemTools::cmTarExtractTimestamps::Yes, - parsedArgs.Verbose)) { + parsedArgs.Encoding, parsedArgs.Verbose)) { status.SetError(cmStrCat("failed to extract:\n ", inFile)); cmSystemTools::SetFatalErrorOccurred(); return false;
diff --git a/Source/cmFileCopier.cxx b/Source/cmFileCopier.cxx index c848ed7..28e4b60 100644 --- a/Source/cmFileCopier.cxx +++ b/Source/cmFileCopier.cxx
@@ -23,7 +23,6 @@ # include <cerrno> #endif -#include <cstring> #include <sstream> using namespace cmFSPermissions; @@ -715,10 +714,10 @@ } unsigned long numFiles = dir.GetNumberOfFiles(); for (unsigned long fileNum = 0; fileNum < numFiles; ++fileNum) { - if (!(strcmp(dir.GetFile(fileNum), ".") == 0 || - strcmp(dir.GetFile(fileNum), "..") == 0)) { - std::string fromPath = cmStrCat(source, '/', dir.GetFile(fileNum)); - std::string toPath = cmStrCat(destination, '/', dir.GetFile(fileNum)); + std::string const& file = dir.GetFileName(fileNum); + if (file != "." && file != "..") { + std::string fromPath = cmStrCat(source, '/', file); + std::string toPath = cmStrCat(destination, '/', file); if (!this->Install(fromPath, toPath)) { return false; }
diff --git a/Source/cmFileSet.cxx b/Source/cmFileSet.cxx index ccb7681..1119817 100644 --- a/Source/cmFileSet.cxx +++ b/Source/cmFileSet.cxx
@@ -2,7 +2,6 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmFileSet.h" -#include <sstream> #include <string> #include <unordered_map> #include <utility> @@ -12,85 +11,21 @@ #include <cmext/algorithm> #include <cmext/string_view> -#include "cmsys/RegularExpression.hxx" - -#include "cmGenExContext.h" -#include "cmGeneratorExpression.h" #include "cmList.h" #include "cmListFileCache.h" -#include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmRange.h" #include "cmStringAlgorithms.h" -#include "cmSystemTools.h" -#include "cmake.h" +#include "cmTarget.h" -cm::static_string_view cmFileSetVisibilityToName(cmFileSetVisibility vis) -{ - switch (vis) { - case cmFileSetVisibility::Interface: - return "INTERFACE"_s; - case cmFileSetVisibility::Public: - return "PUBLIC"_s; - case cmFileSetVisibility::Private: - return "PRIVATE"_s; - } - return ""_s; -} +namespace Metadata = cm::FileSetMetadata; -cmFileSetVisibility cmFileSetVisibilityFromName(cm::string_view name, - cmMakefile* mf) -{ - if (name == "INTERFACE"_s) { - return cmFileSetVisibility::Interface; - } - if (name == "PUBLIC"_s) { - return cmFileSetVisibility::Public; - } - if (name == "PRIVATE"_s) { - return cmFileSetVisibility::Private; - } - auto msg = cmStrCat("File set visibility \"", name, "\" is not valid."); - if (mf) { - mf->IssueMessage(MessageType::FATAL_ERROR, msg); - } else { - cmSystemTools::Error(msg); - } - return cmFileSetVisibility::Private; -} - -bool cmFileSetVisibilityIsForSelf(cmFileSetVisibility vis) -{ - switch (vis) { - case cmFileSetVisibility::Interface: - return false; - case cmFileSetVisibility::Public: - case cmFileSetVisibility::Private: - return true; - } - return false; -} - -bool cmFileSetVisibilityIsForInterface(cmFileSetVisibility vis) -{ - switch (vis) { - case cmFileSetVisibility::Interface: - case cmFileSetVisibility::Public: - return true; - case cmFileSetVisibility::Private: - return false; - } - return false; -} - -bool cmFileSetTypeCanBeIncluded(std::string const& type) -{ - return type == "HEADERS"_s; -} - -cmFileSet::cmFileSet(cmMakefile* makefile, std::string name, std::string type, - cmFileSetVisibility visibility) +cmFileSet::cmFileSet(cmMakefile* makefile, cmTarget* target, std::string name, + std::string type, Metadata::Visibility visibility) : Makefile(makefile) + , Target(target) , Name(std::move(name)) , Type(std::move(type)) , Visibility(visibility) @@ -123,171 +58,213 @@ this->FileEntries.push_back(std::move(files)); } -std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> -cmFileSet::CompileFileEntries() const +cmBTStringRange cmFileSet::GetIncludeDirectories() const { - std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> result; - - for (auto const& entry : this->FileEntries) { - for (auto const& ex : cmList{ entry.Value }) { - cmGeneratorExpression ge(*this->GetMakefile()->GetCMakeInstance(), - entry.Backtrace); - auto cge = ge.Parse(ex); - result.push_back(std::move(cge)); - } - } - - return result; + return cmMakeRange(this->IncludeDirectories); +} +cmBTStringRange cmFileSet::GetInterfaceIncludeDirectories() const +{ + return cmMakeRange(this->InterfaceIncludeDirectories); } -std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> -cmFileSet::CompileDirectoryEntries() const +cmBTStringRange cmFileSet::GetCompileOptions() const { - std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> result; - - for (auto const& entry : this->DirectoryEntries) { - for (auto const& ex : cmList{ entry.Value }) { - cmGeneratorExpression ge(*this->GetMakefile()->GetCMakeInstance(), - entry.Backtrace); - auto cge = ge.Parse(ex); - result.push_back(std::move(cge)); - } - } - - return result; + return cmMakeRange(this->CompileOptions); +} +cmBTStringRange cmFileSet::GetInterfaceCompileOptions() const +{ + return cmMakeRange(this->InterfaceCompileOptions); } -std::vector<std::string> cmFileSet::EvaluateDirectoryEntries( - std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& cges, - cm::GenEx::Context const& context, cmGeneratorTarget const* target, - cmGeneratorExpressionDAGChecker* dagChecker) const +cmBTStringRange cmFileSet::GetCompileDefinitions() const { - struct DirCacheEntry + return cmMakeRange(this->CompileDefinitions); +} +cmBTStringRange cmFileSet::GetInterfaceCompileDefinitions() const +{ + return cmMakeRange(this->InterfaceCompileDefinitions); +} + +namespace { +enum class ReadOnlyCondition +{ + All, + Imported, + NonImported, +}; + +struct ReadOnlyProperty +{ + ReadOnlyProperty(ReadOnlyCondition cond) + : Condition{ cond } { - std::string collapsedDir; - cm::optional<cmSystemTools::FileId> fileId; + } + // ReadOnlyProperty(ReadOnlyCondition cond, cmPolicies::PolicyID id) + // : Condition{ cond } + // , Policy{ id } + // { + // } + + ReadOnlyCondition Condition; + cm::optional<cmPolicies::PolicyID> Policy; + + std::string message(std::string const& prop, cmTarget* target, + cmFileSet* fileSet) const + { + std::string msg; + if (this->Condition == ReadOnlyCondition::All) { + msg = cmStrCat(" property is read-only for the file set \"", + fileSet->GetName(), " of the target \""); + } else if (this->Condition == ReadOnlyCondition::Imported) { + msg = " property can't be set on a file set attached to the imported " + "target \""; + } else if (this->Condition == ReadOnlyCondition::NonImported) { + msg = + " property can't be set on a file set attached to the non-imported " + "target \""; + } + return cmStrCat(prop, msg, target->GetName(), "\"\n"); + } + + bool isReadOnly(std::string const& prop, cmMakefile* context, + cmTarget* target, cmFileSet* fileSet) const + { + auto importedTarget = target->IsImported(); + bool matchingCondition = true; + if ((!importedTarget && this->Condition == ReadOnlyCondition::Imported) || + (importedTarget && + this->Condition == ReadOnlyCondition::NonImported)) { + matchingCondition = false; + } + if (!matchingCondition) { + // Not read-only in this scenario + return false; + } + + bool readOnly = true; + if (!this->Policy) { + // No policy associated, so is always read-only + context->IssueMessage(MessageType::FATAL_ERROR, + this->message(prop, target, fileSet)); + } + return readOnly; + } +}; + +bool IsSettableProperty(cmMakefile* context, cmTarget* target, + cmFileSet* fileSet, std::string const& prop) +{ + using ROC = ReadOnlyCondition; + static std::unordered_map<std::string, ReadOnlyProperty> const readOnlyProps{ + { "TYPE", { ROC::All } }, { "SCOPE", { ROC::All } } }; - std::unordered_map<std::string, DirCacheEntry> dirCache; - std::vector<std::string> result; - for (auto const& cge : cges) { - auto entry = cge->Evaluate(context, dagChecker, target); - cmList dirs{ entry }; - for (std::string dir : dirs) { - if (!cmSystemTools::FileIsFullPath(dir)) { - dir = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', dir); - } + auto it = readOnlyProps.find(prop); - auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry()); - auto& dirCacheEntry = dirCacheResult.first->second; - auto const isNewCacheEntry = dirCacheResult.second; - - if (isNewCacheEntry) { - cmSystemTools::FileId fileId; - auto isFileIdValid = cmSystemTools::GetFileId(dir, fileId); - dirCacheEntry.collapsedDir = cmSystemTools::CollapseFullPath(dir); - dirCacheEntry.fileId = - isFileIdValid ? cm::optional<decltype(fileId)>(fileId) : cm::nullopt; - } - - for (auto const& priorDir : result) { - auto priorDirCacheEntry = dirCache.at(priorDir); - bool sameFile = dirCacheEntry.fileId.has_value() && - priorDirCacheEntry.fileId.has_value() && - (*dirCacheEntry.fileId == *priorDirCacheEntry.fileId); - if (!sameFile && - (cmSystemTools::IsSubDirectory(dirCacheEntry.collapsedDir, - priorDirCacheEntry.collapsedDir) || - cmSystemTools::IsSubDirectory(priorDirCacheEntry.collapsedDir, - dirCacheEntry.collapsedDir))) { - context.LG->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat( - "Base directories in file set cannot be subdirectories of each " - "other:\n ", - priorDir, "\n ", dir), - cge->GetBacktrace()); - return {}; - } - } - result.push_back(dir); - } + if (it != readOnlyProps.end()) { + return !(it->second.isReadOnly(prop, context, target, fileSet)); } - return result; + return true; } -void cmFileSet::EvaluateFileEntry( - std::vector<std::string> const& dirs, - std::map<std::string, std::vector<std::string>>& filesPerDir, - std::unique_ptr<cmCompiledGeneratorExpression> const& cge, - cm::GenEx::Context const& context, cmGeneratorTarget const* target, - cmGeneratorExpressionDAGChecker* dagChecker) const -{ - auto files = cge->Evaluate(context, dagChecker, target); - for (std::string file : cmList{ files }) { - if (!cmSystemTools::FileIsFullPath(file)) { - file = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', file); - } - auto collapsedFile = cmSystemTools::CollapseFullPath(file); - bool found = false; - std::string relDir; - for (auto const& dir : dirs) { - auto collapsedDir = cmSystemTools::CollapseFullPath(dir); - if (cmSystemTools::IsSubDirectory(collapsedFile, collapsedDir)) { - found = true; - relDir = cmSystemTools::GetParentDirectory( - cmSystemTools::RelativePath(collapsedDir, collapsedFile)); - break; - } - } - if (!found) { - std::ostringstream e; - e << "File:\n " << file - << "\nmust be in one of the file set's base directories:"; - for (auto const& dir : dirs) { - e << "\n " << dir; - } - context.LG->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), cge->GetBacktrace()); - return; - } - - filesPerDir[relDir].push_back(file); - } +cm::string_view const BASE_DIRS = "BASE_DIRS"_s; +cm::string_view const SOURCES = "SOURCES"_s; +cm::string_view const INTERFACE_SOURCES = "INTERFACE_SOURCES"_s; +cm::string_view const INCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES"_s; +cm::string_view const INTERFACE_INCLUDE_DIRECTORIES = + "INTERFACE_INCLUDE_DIRECTORIES"_s; +cm::string_view const COMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"_s; +cm::string_view const INTERFACE_COMPILE_DEFINITIONS = + "INTERFACE_COMPILE_DEFINITIONS"_s; +cm::string_view const COMPILE_OPTIONS = "COMPILE_OPTIONS"_s; +cm::string_view const INTERFACE_COMPILE_OPTIONS = + "INTERFACE_COMPILE_OPTIONS"_s; } -bool cmFileSet::IsValidName(std::string const& name) -{ - static cmsys::RegularExpression const regex("^[a-z0-9][a-zA-Z0-9_]*$"); - - cmsys::RegularExpressionMatch match; - return regex.find(name.c_str(), match); -} - -std::string const cmFileSet::propCOMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"; -std::string const cmFileSet::propCOMPILE_OPTIONS = "COMPILE_OPTIONS"; -std::string const cmFileSet::propINCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES"; - void cmFileSet::SetProperty(std::string const& prop, cmValue value) { - if (prop == propINCLUDE_DIRECTORIES) { + if (!IsSettableProperty(this->Makefile, this->Target, this, prop)) { + return; + } + + if (prop == BASE_DIRS) { + this->ClearDirectoryEntries(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->AddDirectoryEntry(BT<std::string>{ value, lfbt }); + } + } else if (prop == SOURCES) { + if (!this->IsForSelf()) { + return; + } + this->ClearFileEntries(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->AddFileEntry(BT<std::string>{ value, lfbt }); + } + } else if (prop == INTERFACE_SOURCES) { + if (!this->IsForInterface()) { + return; + } + this->ClearFileEntries(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->AddFileEntry(BT<std::string>{ value, lfbt }); + } + } else if (prop == INCLUDE_DIRECTORIES) { + if (!this->IsForSelf()) { + return; + } this->IncludeDirectories.clear(); if (value) { cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); this->IncludeDirectories.emplace_back(value, lfbt); } - } else if (prop == propCOMPILE_OPTIONS) { + } else if (prop == INTERFACE_INCLUDE_DIRECTORIES) { + if (!this->IsForInterface()) { + return; + } + this->InterfaceIncludeDirectories.clear(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->InterfaceIncludeDirectories.emplace_back(value, lfbt); + } + } else if (prop == COMPILE_OPTIONS) { + if (!this->IsForSelf()) { + return; + } this->CompileOptions.clear(); if (value) { cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); this->CompileOptions.emplace_back(value, lfbt); } - } else if (prop == propCOMPILE_DEFINITIONS) { + } else if (prop == INTERFACE_COMPILE_OPTIONS) { + if (!this->IsForInterface()) { + return; + } + this->InterfaceCompileOptions.clear(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->InterfaceCompileOptions.emplace_back(value, lfbt); + } + } else if (prop == COMPILE_DEFINITIONS) { + if (!this->IsForSelf()) { + return; + } this->CompileDefinitions.clear(); if (value) { cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); this->CompileDefinitions.emplace_back(value, lfbt); } + } else if (prop == INTERFACE_COMPILE_DEFINITIONS) { + if (!this->IsForInterface()) { + return; + } + this->InterfaceCompileDefinitions.clear(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->InterfaceCompileDefinitions.emplace_back(value, lfbt); + } } else { this->Properties.SetProperty(prop, value); } @@ -296,21 +273,79 @@ void cmFileSet::AppendProperty(std::string const& prop, std::string const& value, bool asString) { - if (prop == propINCLUDE_DIRECTORIES) { + if (!IsSettableProperty(this->Makefile, this->Target, this, prop)) { + return; + } + + if (prop == BASE_DIRS) { + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->AddDirectoryEntry(BT<std::string>{ value, lfbt }); + } + } else if (prop == SOURCES) { + if (!this->IsForSelf()) { + return; + } + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->AddFileEntry(BT<std::string>{ value, lfbt }); + } + } else if (prop == INTERFACE_SOURCES) { + if (!this->IsForInterface()) { + return; + } + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->AddFileEntry(BT<std::string>{ value, lfbt }); + } + } else if (prop == INCLUDE_DIRECTORIES) { + if (!this->IsForSelf()) { + return; + } if (!value.empty()) { cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); this->IncludeDirectories.emplace_back(value, lfbt); } - } else if (prop == propCOMPILE_OPTIONS) { + } else if (prop == INTERFACE_INCLUDE_DIRECTORIES) { + if (!this->IsForInterface()) { + return; + } + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->InterfaceIncludeDirectories.emplace_back(value, lfbt); + } + } else if (prop == COMPILE_OPTIONS) { + if (!this->IsForSelf()) { + return; + } if (!value.empty()) { cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); this->CompileOptions.emplace_back(value, lfbt); } - } else if (prop == propCOMPILE_DEFINITIONS) { + } else if (prop == INTERFACE_COMPILE_OPTIONS) { + if (!this->IsForInterface()) { + return; + } + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->InterfaceCompileOptions.emplace_back(value, lfbt); + } + } else if (prop == COMPILE_DEFINITIONS) { + if (!this->IsForSelf()) { + return; + } if (!value.empty()) { cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); this->CompileDefinitions.emplace_back(value, lfbt); } + } else if (prop == INTERFACE_COMPILE_DEFINITIONS) { + if (!this->IsForInterface()) { + return; + } + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); + this->InterfaceCompileDefinitions.emplace_back(value, lfbt); + } } else { this->Properties.AppendProperty(prop, value, asString); } @@ -319,7 +354,32 @@ cmValue cmFileSet::GetProperty(std::string const& prop) const { // Check for the properties with backtraces. - if (prop == propINCLUDE_DIRECTORIES) { + if (prop == BASE_DIRS) { + + static std::string output; + output = cmList::to_string(this->GetDirectoryEntries()); + return cmValue(output); + } + if (prop == SOURCES) { + if (!this->IsForSelf() || this->GetFileEntries().empty()) { + return nullptr; + } + + static std::string output; + output = cmList::to_string(this->GetFileEntries()); + return cmValue(output); + } + if (prop == INTERFACE_SOURCES) { + if (!this->IsForInterface() || this->GetFileEntries().empty()) { + return nullptr; + } + + static std::string output; + output = cmList::to_string(this->GetFileEntries()); + return cmValue(output); + } + + if (prop == INCLUDE_DIRECTORIES) { if (this->IncludeDirectories.empty()) { return nullptr; } @@ -329,7 +389,17 @@ return cmValue(output); } - if (prop == propCOMPILE_OPTIONS) { + if (prop == INTERFACE_INCLUDE_DIRECTORIES) { + if (this->InterfaceIncludeDirectories.empty()) { + return nullptr; + } + + static std::string output; + output = cmList::to_string(this->InterfaceIncludeDirectories); + return cmValue(output); + } + + if (prop == COMPILE_OPTIONS) { if (this->CompileOptions.empty()) { return nullptr; } @@ -339,7 +409,17 @@ return cmValue(output); } - if (prop == propCOMPILE_DEFINITIONS) { + if (prop == INTERFACE_COMPILE_OPTIONS) { + if (this->InterfaceCompileOptions.empty()) { + return nullptr; + } + + static std::string output; + output = cmList::to_string(this->InterfaceCompileOptions); + return cmValue(output); + } + + if (prop == COMPILE_DEFINITIONS) { if (this->CompileDefinitions.empty()) { return nullptr; } @@ -349,5 +429,24 @@ return cmValue(output); } + if (prop == INTERFACE_COMPILE_DEFINITIONS) { + if (this->InterfaceCompileDefinitions.empty()) { + return nullptr; + } + + static std::string output; + output = cmList::to_string(this->InterfaceCompileDefinitions); + return cmValue(output); + } + + if (prop == "TYPE"_s) { + return cmValue{ this->GetType() }; + } + if (prop == "SCOPE"_s) { + static std::string scope = + std::string{ Metadata::VisibilityToName(this->GetVisibility()) }; + return cmValue{ scope }; + } + return this->Properties.GetPropertyValue(prop); }
diff --git a/Source/cmFileSet.h b/Source/cmFileSet.h index 598e832..eb5e9e3 100644 --- a/Source/cmFileSet.h +++ b/Source/cmFileSet.h
@@ -3,55 +3,49 @@ #pragma once #include <cstddef> -#include <map> -#include <memory> #include <string> #include <vector> #include <cm/string_view> -#include <cmext/string_view> +#include "cmAlgorithms.h" +#include "cmFileSetMetadata.h" #include "cmListFileCache.h" #include "cmPropertyMap.h" #include "cmValue.h" -namespace cm { -namespace GenEx { -struct Context; -} -} - -class cmCompiledGeneratorExpression; -struct cmGeneratorExpressionDAGChecker; -class cmGeneratorTarget; class cmMakefile; - -enum class cmFileSetVisibility -{ - Private, - Public, - Interface, -}; -cm::static_string_view cmFileSetVisibilityToName(cmFileSetVisibility vis); -cmFileSetVisibility cmFileSetVisibilityFromName(cm::string_view name, - cmMakefile* mf); -bool cmFileSetVisibilityIsForSelf(cmFileSetVisibility vis); -bool cmFileSetVisibilityIsForInterface(cmFileSetVisibility vis); - -bool cmFileSetTypeCanBeIncluded(std::string const& type); +class cmTarget; class cmFileSet { public: - cmFileSet(cmMakefile* makefile, std::string name, std::string type, - cmFileSetVisibility visibility); + cmFileSet(cmMakefile* makefile, cmTarget* target, std::string name, + std::string type, cm::FileSetMetadata::Visibility visibility); std::string const& GetName() const { return this->Name; } std::string const& GetType() const { return this->Type; } - cmFileSetVisibility GetVisibility() const { return this->Visibility; } + cm::FileSetMetadata::Visibility GetVisibility() const + { + return this->Visibility; + } cmMakefile* GetMakefile() const { return this->Makefile; } + bool IsForSelf() const + { + return cm::FileSetMetadata::VisibilityIsForSelf(this->GetVisibility()); + } + bool IsForInterface() const + { + return cm::FileSetMetadata::VisibilityIsForInterface( + this->GetVisibility()); + } + bool CanBeIncluded() const + { + return this->Type != cm::FileSetMetadata::CXX_MODULES; + } + void CopyEntries(cmFileSet const* fs); void ClearDirectoryEntries(); @@ -68,25 +62,15 @@ return this->FileEntries; } - std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> - CompileFileEntries() const; + // Special properties + cmBTStringRange GetIncludeDirectories() const; + cmBTStringRange GetInterfaceIncludeDirectories() const; - std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> - CompileDirectoryEntries() const; + cmBTStringRange GetCompileOptions() const; + cmBTStringRange GetInterfaceCompileOptions() const; - std::vector<std::string> EvaluateDirectoryEntries( - std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& cges, - cm::GenEx::Context const& context, cmGeneratorTarget const* target, - cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; - - void EvaluateFileEntry( - std::vector<std::string> const& dirs, - std::map<std::string, std::vector<std::string>>& filesPerDir, - std::unique_ptr<cmCompiledGeneratorExpression> const& cge, - cm::GenEx::Context const& context, cmGeneratorTarget const* target, - cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; - - static bool IsValidName(std::string const& name); + cmBTStringRange GetCompileDefinitions() const; + cmBTStringRange GetInterfaceCompileDefinitions() const; //! Set/Get a property of this file set void SetProperty(std::string const& prop, cmValue value); @@ -108,17 +92,17 @@ private: cmMakefile* Makefile; + cmTarget* Target; std::string Name; std::string Type; - cmFileSetVisibility Visibility; + cm::FileSetMetadata::Visibility Visibility; std::vector<BT<std::string>> DirectoryEntries; std::vector<BT<std::string>> FileEntries; cmPropertyMap Properties; - std::vector<BT<std::string>> CompileOptions; - std::vector<BT<std::string>> CompileDefinitions; std::vector<BT<std::string>> IncludeDirectories; - - static std::string const propCOMPILE_DEFINITIONS; - static std::string const propCOMPILE_OPTIONS; - static std::string const propINCLUDE_DIRECTORIES; + std::vector<BT<std::string>> InterfaceIncludeDirectories; + std::vector<BT<std::string>> CompileOptions; + std::vector<BT<std::string>> InterfaceCompileOptions; + std::vector<BT<std::string>> CompileDefinitions; + std::vector<BT<std::string>> InterfaceCompileDefinitions; };
diff --git a/Source/cmFileSetMetadata.cxx b/Source/cmFileSetMetadata.cxx new file mode 100644 index 0000000..3874e58 --- /dev/null +++ b/Source/cmFileSetMetadata.cxx
@@ -0,0 +1,171 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmFileSetMetadata.h" + +#include <map> +#include <string> +#include <utility> + +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmsys/RegularExpression.hxx" + +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +namespace cm { +namespace FileSetMetadata { +cm::string_view VisibilityToName(Visibility vis) +{ + switch (vis) { + case Visibility::Interface: + return "INTERFACE"_s; + case Visibility::Public: + return "PUBLIC"_s; + case Visibility::Private: + return "PRIVATE"_s; + } + return ""_s; +} + +Visibility VisibilityFromName(cm::string_view name, cmMakefile* mf) +{ + if (name == "INTERFACE"_s) { + return Visibility::Interface; + } + if (name == "PUBLIC"_s) { + return Visibility::Public; + } + if (name == "PRIVATE"_s) { + return Visibility::Private; + } + auto msg = cmStrCat("File set visibility \"", name, "\" is not valid."); + if (mf) { + mf->IssueMessage(MessageType::FATAL_ERROR, msg); + } else { + cmSystemTools::Error(msg); + } + return Visibility::Private; +} + +bool VisibilityIsForSelf(Visibility vis) +{ + switch (vis) { + case Visibility::Interface: + return false; + case Visibility::Public: + case Visibility::Private: + return true; + } + return false; +} + +bool VisibilityIsForInterface(Visibility vis) +{ + switch (vis) { + case Visibility::Interface: + case Visibility::Public: + return true; + case Visibility::Private: + return false; + } + return false; +} + +cm::string_view const HEADERS = "HEADERS"_s; +cm::string_view const SOURCES = "SOURCES"_s; +cm::string_view const CXX_MODULES = "CXX_MODULES"_s; + +namespace { +std::map<cm::string_view, FileSetDescriptor> const FileSetDescriptors{ + { cm::FileSetMetadata::HEADERS, + { cm::FileSetMetadata::HEADERS, + cm::FileSetMetadata::FileSetLookup::Target, + { DependencyMode ::Includables }, + DependencyMode ::Includables, + { cm::FileSetMetadata::FileSetAttributes::FilesInMultipleFileSets } } }, + { cm::FileSetMetadata::SOURCES, + { cm::FileSetMetadata::SOURCES, + cm::FileSetMetadata::FileSetLookup::Dependencies, + { DependencyMode ::IndependentFiles, DependencyMode ::Includables }, + DependencyMode ::Includables, + { cm::FileSetMetadata::FileSetAttributes::FrameworkCompatible, + cm::FileSetMetadata::FileSetAttributes::UnityBuild } } }, + { cm::FileSetMetadata::CXX_MODULES, + { cm::FileSetMetadata::CXX_MODULES, + cm::FileSetMetadata::FileSetLookup::Target, + { DependencyMode ::IndependentFiles }, + DependencyMode ::IndependentFiles, + {} } }, +}; + +std::vector<cm::string_view> KnownTypes{ HEADERS, SOURCES, CXX_MODULES }; + +cmsys::RegularExpression const ValidNameRegex("^[a-z0-9][a-zA-Z0-9_]*$"); +} + +cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type) +{ + auto it = FileSetDescriptors.find(type); + if (it != FileSetDescriptors.end()) { + return it->second; + } + return cm::nullopt; +} + +DependencyMode GetDependencyMode(cm::string_view type) +{ + auto descriptor = GetFileSetDescriptor(type); + if (descriptor) { + return descriptor->DefaultDependency; + } + return DependencyMode::Includables; +} +DependencyMode GetDependencyMode(cm::string_view type, + DependencyMode requestedMode) +{ + auto descriptor = GetFileSetDescriptor(type); + if (descriptor) { + // Select the requested mode or the next-weakest mode that is supported by + // the file set type + auto mode = descriptor->SupportedDependencies.lower_bound(requestedMode); + return mode == descriptor->SupportedDependencies.end() + ? descriptor->DefaultDependency + : *mode; + } + return DependencyMode::Includables; +} + +AttributeSet GetAttributes(cm::string_view type) +{ + auto descriptor = GetFileSetDescriptor(type); + if (descriptor) { + return descriptor->Attributes; + } + return {}; +} +bool IsFrameworkSupported(cm::string_view type) +{ + return GetAttributes(type).contains(FileSetAttributes::FrameworkCompatible); +} + +std::vector<cm::string_view> const& GetKnownTypes() +{ + return KnownTypes; +} + +bool IsKnownType(cm::string_view type) +{ + return cm::contains(GetKnownTypes(), type); +} + +bool IsValidName(cm::string_view name) +{ + cmsys::RegularExpressionMatch match; + return ValidNameRegex.find(name.data(), match); +} +} +}
diff --git a/Source/cmFileSetMetadata.h b/Source/cmFileSetMetadata.h new file mode 100644 index 0000000..7d789a8 --- /dev/null +++ b/Source/cmFileSetMetadata.h
@@ -0,0 +1,110 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include <cstdint> +#include <set> +#include <utility> +#include <vector> + +#include <cm/optional> +#include <cm/string_view> +#include <cmext/enum_set> + +class cmMakefile; + +namespace cm { +namespace FileSetMetadata { +enum class Visibility +{ + Private, + Public, + Interface +}; + +cm::string_view VisibilityToName(Visibility vis); +Visibility VisibilityFromName(cm::string_view name, cmMakefile* mf); + +bool VisibilityIsForSelf(Visibility vis); +bool VisibilityIsForInterface(Visibility vis); + +// Pre-defined FileSet types +extern cm::string_view const HEADERS; +extern cm::string_view const SOURCES; +extern cm::string_view const CXX_MODULES; + +enum class FileSetLookup +{ + // Search for file sets attached to the target + Target, + // Search also file sets inherited from link libraries + Dependencies +}; + +// Define the various modes regarding graph dependency for +// the generated files (Ninja specific) +// items must be kept in this order: "Lower" modes are "stronger" in that they +// have more restrictions (and therefore allow for more build graph +// optimization). +// std::set rely on it. +enum class DependencyMode +{ + IndependentFiles, // files in the file set are independent from each other + Includables // files can be used by another source during compilation +}; +using DependencySet = std::set<DependencyMode>; + +enum class FileSetAttributes : std::uint16_t +{ + FrameworkCompatible, // Can be part of an Apple framework + FilesInMultipleFileSets, // Files of this file set type can be part of other + // file sets + UnityBuild // Can be part of a unity build +}; +using AttributeSet = cm::enum_set<FileSetAttributes, 3>; + +struct FileSetDescriptor +{ + FileSetDescriptor(cm::string_view type, FileSetLookup lookup, + DependencySet dependencies, + DependencyMode defaultDependency, AttributeSet attributes) + : Type(type) + , Lookup(lookup) + , SupportedDependencies(std::move(dependencies)) + , DefaultDependency(defaultDependency) + , Attributes(attributes) + { + } + + FileSetDescriptor(FileSetLookup lookup) + : Type() + , Lookup(lookup) + , SupportedDependencies({ DependencyMode::Includables }) + , DefaultDependency(DependencyMode::Includables) + { + } + + cm::string_view const Type; + FileSetLookup const Lookup; + DependencySet const SupportedDependencies; + DependencyMode const DefaultDependency; + AttributeSet const Attributes; +}; + +cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type); +DependencyMode GetDependencyMode(cm::string_view type); +DependencyMode GetDependencyMode(cm::string_view type, + DependencyMode requestedMode); + +AttributeSet GetAttributes(cm::string_view type); +bool IsFrameworkSupported(cm::string_view type); + +std::vector<cm::string_view> const& GetKnownTypes(); +bool IsKnownType(cm::string_view type); + +// check validity of a user's file set name +bool IsValidName(cm::string_view type); +} +} + +CM_ENUM_SET_TRAITS(cm::FileSetMetadata::AttributeSet)
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 3e98e3f..e9e8dfd 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx
@@ -184,18 +184,16 @@ } // ensure a macro is not specified as validator auto const& validatorName = args[j]; - cmList macros{ this->Makefile->GetProperty("MACROS") }; - if (std::find_if(macros.begin(), macros.end(), - [&validatorName](std::string const& item) { - return cmSystemTools::Strucmp(validatorName.c_str(), - item.c_str()) == 0; - }) != macros.end()) { + if (this->Makefile->GetState() + ->GetCommandType(validatorName) + .value_or(cmStateEnums::CommandType::Macro) != + cmStateEnums::CommandType::Function) { this->SetError(cmStrCat( "command specified for VALIDATOR is not a function: ", args[j], '.')); return false; } - this->ValidatorName = args[j]; + this->ValidatorName = validatorName; } else if (this->CheckCommonArgument(args[j])) { doing = DoingNone; } else {
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index f0d784a..2f2c5c6 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx
@@ -26,6 +26,7 @@ #include "cmAlgorithms.h" #include "cmConfigureLog.h" #include "cmDependencyProvider.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmFindPackageStack.h" #include "cmList.h" @@ -113,12 +114,10 @@ }; #endif -bool isDirentryToIgnore(char const* const fname) +bool isDirentryToIgnore(std::string const& fname) { - assert(fname); - assert(fname[0] != 0); - return fname[0] == '.' && - (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0)); + assert(!fname.empty()); + return fname == "." || fname == ".."; } class cmAppendPathSegmentGenerator @@ -188,12 +187,12 @@ } while (this->CurrentIdx < this->DirectoryLister.GetNumberOfFiles()) { - char const* const fname = - this->DirectoryLister.GetFile(this->CurrentIdx++); + std::string const& fname = + this->DirectoryLister.GetFileName(this->CurrentIdx++); if (isDirentryToIgnore(fname)) { continue; } - if (cmsysString_strcasecmp(fname, this->DirName.data()) == 0) { + if (cmsysString_strcasecmp(fname.c_str(), this->DirName.data()) == 0) { auto candidate = cmStrCat(parent, fname, '/'); if (cmSystemTools::FileIsDirectory(candidate)) { return candidate; @@ -243,7 +242,7 @@ // TODO If so, just start with index 2 and drop the // `isDirentryToIgnore(i)` condition to check. for (auto i = 0ul; i < directoryLister.GetNumberOfFiles(); ++i) { - char const* const fname = directoryLister.GetFile(i); + std::string const& fname = directoryLister.GetFileName(i); // Skip entries to ignore or that aren't directories. if (isDirentryToIgnore(fname)) { continue; @@ -261,8 +260,8 @@ // Skip entries that don't match. auto const equal = ((this->ExactMatch - ? cmsysString_strcasecmp(fname, name.c_str()) - : cmsysString_strncasecmp(fname, name.c_str(), + ? cmsysString_strcasecmp(fname.c_str(), name.c_str()) + : cmsysString_strncasecmp(fname.c_str(), name.c_str(), name.length())) == 0); if (equal) { if (directoryLister.FileIsDirectory(i)) { @@ -983,8 +982,8 @@ // Ignore EXACT with no version. if (this->VersionComplete.empty() && this->VersionExact) { this->VersionExact = false; - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, + this->Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, "Ignoring EXACT since no version is requested."); } @@ -1335,7 +1334,7 @@ aw << "\n" "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this " "warning.)"; - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, aw.str()); + this->Makefile->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, aw.str()); } if (this->FindPackageUsingConfigMode()) { @@ -1567,9 +1566,7 @@ this->Makefile->GetPolicyStatus(it->second); switch (status) { case cmPolicies::WARN: { - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(it->second), '\n')); + this->Makefile->IssuePolicyWarning(it->second); CM_FALLTHROUGH; } case cmPolicies::OLD: @@ -1585,7 +1582,7 @@ found = true; std::string const var = cmStrCat(this->Name, "_FIND_MODULE"); this->Makefile->AddDefinition(var, "1"); - bool result = this->ReadListFile(mfile, DoPolicyScope); + bool result = this->ReadListFile(mfile, cm::PolicyScope::Local); this->Makefile->RemoveDefinition(var); std::string const foundVar = cmStrCat(this->Name, "_FOUND"); @@ -1703,7 +1700,7 @@ // The package has been found. found = true; result = this->ReadPackage(); - } else if (this->ReadListFile(this->FileFound, DoPolicyScope)) { + } else if (this->ReadListFile(this->FileFound, cm::PolicyScope::Local)) { // The package has been found. found = true; @@ -1876,7 +1873,7 @@ } if (!aw.str().empty()) { - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, aw.str()); + this->Makefile->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, aw.str()); } } // output result if in config mode but not in quiet mode @@ -2075,9 +2072,11 @@ } bool cmFindPackageCommand::ReadListFile(std::string const& f, - PolicyScopeRule const psr) + cm::PolicyScope ps) { - bool const noPolicyScope = !this->PolicyScope || psr == NoPolicyScope; + if (!this->PolicyScope) { + ps = cm::PolicyScope::None; + } using ITScope = cmMakefile::ImportedTargetScope; ITScope scope = this->GlobalScope ? ITScope::Global : ITScope::Local; @@ -2088,7 +2087,8 @@ // This allows child snapshots to inherit the CAN_UNWIND state from us, we'll // reset it immediately after the dependent file is done this->Makefile->GetStateSnapshot().SetUnwindType(cmStateEnums::CAN_UNWIND); - bool result = this->Makefile->ReadDependentFile(f, noPolicyScope); + bool const result = + this->Makefile->ReadDependentFile(f, ps, cm::DiagnosticScope::Local); this->Makefile->GetStateSnapshot().SetUnwindType(oldUnwind); this->Makefile->GetStateSnapshot().SetUnwindState( @@ -2726,7 +2726,7 @@ std::string fname; for (unsigned long i = 0; i < files.GetNumberOfFiles(); ++i) { - fname = cmStrCat(dir, '/', files.GetFile(i)); + fname = cmStrCat(dir, '/', files.GetFileName(i)); if (!cmSystemTools::FileIsDirectory(fname)) { // Hold this file hostage until it behaves. @@ -3209,7 +3209,7 @@ // Load the version check file. // Pass NoPolicyScope because we do our own policy push/pop. bool suitable = false; - if (this->ReadListFile(version_file, NoPolicyScope)) { + if (this->ReadListFile(version_file, cm::PolicyScope::None)) { // Check the output variables. bool okay = this->Makefile->IsOn("PACKAGE_VERSION_EXACT"); bool const unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE");
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 740cdca..7c71666 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h
@@ -32,6 +32,10 @@ /* clang-format on */ #endif +namespace cm { +enum class PolicyScope : bool; +} + class cmExecutionStatus; class cmPackageState; class cmSearchPath; @@ -139,12 +143,7 @@ bool FindFrameworkConfig(); bool FindAppBundleConfig(); bool FindEnvironmentConfig(); - enum PolicyScopeRule - { - NoPolicyScope, - DoPolicyScope - }; - bool ReadListFile(std::string const& f, PolicyScopeRule psr); + bool ReadListFile(std::string const& f, cm::PolicyScope ps); bool ReadPackage(); struct Appendix
diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index a3e17eb..d86560c 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx
@@ -6,10 +6,10 @@ #include <string> #include <cm/memory> +#include <cmext/string_view> #include "cmFindCommon.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -143,27 +143,17 @@ return isExeNew; } if (isExeNew) { - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0109), - "\n" - "The file\n" - " ", - file, - "\n" - "is executable but not readable. " - "CMake is ignoring it for compatibility.")); + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0109, {}, + cmStrCat("The file\n "_s, file, + "\nis executable but not readable. " + "CMake is ignoring it for compatibility."_s)); } else { - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0109), - "\n" - "The file\n" - " ", - file, - "\n" - "is readable but not executable. " - "CMake is using it for compatibility.")); + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0109, {}, + cmStrCat("The file\n "_s, file, + "\nis readable but not executable. " + "CMake is using it for compatibility."_s)); } return isExeOld; } @@ -322,7 +312,7 @@ kCFAllocatorDefault, bundlePath.c_str(), kCFStringEncodingUTF8); // Make a CFURLRef from the CFString representation of the - // bundle’s path. + // bundle's path. CFURLRef bundleURL = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, bundlePathCFS, kCFURLPOSIXPathStyle, true);
diff --git a/Source/cmFunctionBlocker.cxx b/Source/cmFunctionBlocker.cxx index a41c773..8bc0ef7 100644 --- a/Source/cmFunctionBlocker.cxx +++ b/Source/cmFunctionBlocker.cxx
@@ -8,9 +8,9 @@ #include <string> // IWYU pragma: keep #include <utility> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmake.h" bool cmFunctionBlocker::IsFunctionBlocked(cmListFileFunction const& lff, @@ -38,7 +38,7 @@ << " " << closingContext << "\n" << "with mis-matching arguments."; // noqa: spellcheck disable-line /* clang-format on */ - mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + mf.IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, e.str()); } else if (!this->EndCommandSupportsArguments() && !lff.Arguments().empty()) { std::ostringstream e; @@ -47,7 +47,7 @@ " " << closingContext << "\n" "has unexpected arguments."; /* clang-format on */ - mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + mf.IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, e.str()); } bool replayResult = this->Replay(std::move(this->Functions), status);
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index a629438..3e62649 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx
@@ -10,6 +10,7 @@ #include <cmext/algorithm> #include <cmext/string_view> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmFunctionBlocker.h" #include "cmList.h" @@ -18,11 +19,13 @@ #include "cmPolicies.h" #include "cmRange.h" #include "cmState.h" +#include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" namespace { std::string const ARGC = "ARGC"; +std::string const kFUNCTION_ARGNC = "_FUNCTION_ARGNC"; std::string const ARGN = "ARGN"; std::string const ARGV = "ARGV"; std::string const CMAKE_CURRENT_FUNCTION = "CMAKE_CURRENT_FUNCTION"; @@ -47,6 +50,7 @@ std::vector<std::string> Args; std::vector<cmListFileFunction> Functions; cmPolicies::PolicyMap Policies; + cmDiagnostics::DiagnosticMap Diagnostics; std::string FilePath; long Line; }; @@ -72,7 +76,7 @@ } cmMakefile::FunctionPushPop functionScope(&makefile, this->FilePath, - this->Policies); + this->Policies, this->Diagnostics); // set the value of argc makefile.AddDefinition(ARGC, std::to_string(expandedArgs.size())); @@ -90,15 +94,19 @@ makefile.AddDefinition(this->Args[j], expandedArgs[j - 1]); } - // define ARGV and ARGN + // define ARGV, ARGN, and _FUNCTION_ARGNC auto const argvDef = cmList::to_string(expandedArgs); auto const expIt = expandedArgs.begin() + (this->Args.size() - 1); auto const argnDef = cmList::to_string(cmMakeRange(expIt, expandedArgs.end())); + auto const functionArgncDef = + std::to_string(expandedArgs.size() - (this->Args.size() - 1)); makefile.AddDefinition(ARGV, argvDef); makefile.MarkVariableAsUsed(ARGV); makefile.AddDefinition(ARGN, argnDef); makefile.MarkVariableAsUsed(ARGN); + makefile.AddDefinition(kFUNCTION_ARGNC, functionArgncDef); + makefile.MarkVariableAsUsed(kFUNCTION_ARGNC); makefile.AddDefinition(CMAKE_CURRENT_FUNCTION, this->Args.front()); makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION); @@ -171,8 +179,9 @@ f.FilePath = this->GetStartingContext().FilePath; f.Line = this->GetStartingContext().Line; mf.RecordPolicies(f.Policies); + mf.RecordDiagnostics(f.Diagnostics); return mf.GetState()->AddScriptedCommand( - this->Args.front(), + this->Args.front(), cmStateEnums::CommandType::Function, BT<cmState::Command>(std::move(f), mf.GetBacktrace().Push(this->GetStartingContext())), mf);
diff --git a/Source/cmGeneratorExpressionEvaluationFile.cxx b/Source/cmGeneratorExpressionEvaluationFile.cxx index 38a320b..733d986 100644 --- a/Source/cmGeneratorExpressionEvaluationFile.cxx +++ b/Source/cmGeneratorExpressionEvaluationFile.cxx
@@ -6,6 +6,8 @@ #include <sstream> #include <utility> +#include <cmext/string_view> + #include "cmsys/FStream.hxx" #include "cmGenExContext.h" @@ -16,6 +18,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmSourceFile.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile( @@ -230,29 +233,16 @@ { std::string resultPath; switch (this->PolicyStatusCMP0070) { - case cmPolicies::WARN: { - std::string arg; - switch (role) { - case PathForInput: - arg = "INPUT"; - break; - case PathForOutput: - arg = "OUTPUT"; - break; - } - std::ostringstream w; - /* clang-format off */ - w << - cmPolicies::GetPolicyWarning(cmPolicies::CMP0070) << "\n" - "file(GENERATE) given relative " << arg << " path:\n" - " " << relativePath << "\n" - "This is not defined behavior unless CMP0070 is set to NEW. " - "For compatibility with older versions of CMake, the previous " - "undefined behavior will be used." - ; - /* clang-format on */ - lg->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); - } + case cmPolicies::WARN: + lg->IssuePolicyWarning( + cmPolicies::CMP0070, {}, + cmStrCat( + "file(GENERATE) given relative "_s, + (role == PathForInput ? "INPUT"_s : "OUTPUT"_s), " path:\n "_s, + relativePath, + "\nThis is not defined behavior unless CMP0070 is set to NEW. " + "For compatibility with older versions of CMake, the previous " + "undefined behavior will be used."_s)); CM_FALLTHROUGH; case cmPolicies::OLD: // OLD behavior is to use the relative path unchanged,
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index b938329..e164e8c 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx
@@ -29,12 +29,12 @@ #include "cmCMakePath.h" #include "cmCMakeString.hxx" #include "cmComputeLinkInformation.h" -#include "cmFileSet.h" #include "cmGenExContext.h" #include "cmGenExEvaluation.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionDAGChecker.h" #include "cmGeneratorExpressionEvaluator.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLinkItem.h" @@ -414,7 +414,8 @@ cmList values; cmList checkValues; bool check = false; - switch (eval->Context.LG->GetPolicyStatus(cmPolicies::CMP0085)) { + cmLocalGenerator const* const lg = eval->Context.LG; + switch (lg->GetPolicyStatus(cmPolicies::CMP0085)) { case cmPolicies::WARN: if (parameters.front().empty()) { check = true; @@ -424,12 +425,11 @@ case cmPolicies::OLD: values.assign(parameters[1]); if (check && values != checkValues) { - std::ostringstream e; - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085) - << "\nSearch Item:\n \"" << parameters.front() - << "\"\nList:\n \"" << parameters[1] << "\"\n"; - eval->Context.LG->GetCMakeInstance()->IssueMessage( - MessageType ::AUTHOR_WARNING, e.str(), eval->Backtrace); + lg->IssuePolicyWarning( + cmPolicies::CMP0085, {}, + cmStrCat("Search Item:\n \""_s, parameters.front(), + "\"\nList:\n \""_s, parameters[1], "\"\n"_s), + eval->Backtrace); return "0"; } if (values.empty()) { @@ -2893,14 +2893,13 @@ case cmPolicies::WARN: if (lg->GetMakefile()->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0199")) { - std::string const err = - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0199), - "\nEvaluation of $<CONFIG> for imported target \"", - eval->CurrentTarget->GetName(), "\", used by \"", + lg->IssuePolicyWarning( + cmPolicies::CMP0199, {}, + cmStrCat("Evaluation of $<CONFIG> for imported target \""_s, + eval->CurrentTarget->GetName(), "\", used by \""_s, eval->HeadTarget->GetName(), - "\", may match multiple configurations.\n"); - lg->GetCMakeInstance()->IssueMessage( - MessageType ::AUTHOR_WARNING, err, eval->Backtrace); + "\", may match multiple configurations."_s), + eval->Backtrace); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -3582,11 +3581,12 @@ namespace { bool GetFileSet(std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, - GeneratorExpressionContent const* content, cmFileSet*& fileSet) + GeneratorExpressionContent const* content, + cmGeneratorTarget const*& target, + cmGeneratorFileSet const*& fileSet) { auto const& fileSetName = parameters[0]; auto targetName = parameters[1]; - auto* makefile = eval->Context.LG->GetMakefile(); fileSet = nullptr; auto const TARGET = "TARGET:"_s; @@ -3598,7 +3598,11 @@ cmStrCat("No value provided for the ", TARGET, " option.")); return false; } - auto* target = makefile->FindTargetToUse(targetName); + + cmLocalGenerator const* lg = eval->CurrentTarget + ? eval->CurrentTarget->GetLocalGenerator() + : eval->Context.LG; + target = lg->FindGeneratorTargetToUse(targetName); if (!target) { reportError(eval, content->GetOriginalExpression(), cmStrCat("Non-existent target: ", targetName)); @@ -3634,8 +3638,9 @@ return std::string{}; } - cmFileSet* fileSet = nullptr; - if (!GetFileSet(parameters, eval, content, fileSet)) { + cmGeneratorTarget const* target = nullptr; + cmGeneratorFileSet const* fileSet = nullptr; + if (!GetFileSet(parameters, eval, content, target, fileSet)) { return std::string{}; } @@ -3653,7 +3658,7 @@ std::string Evaluate( std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* content, - cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override + cmGeneratorExpressionDAGChecker* dagCheckerParent) const override { static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); @@ -3686,8 +3691,9 @@ return std::string{}; } - cmFileSet* fileSet = nullptr; - if (!GetFileSet(parameters, eval, content, fileSet)) { + cmGeneratorTarget const* target = nullptr; + cmGeneratorFileSet const* fileSet = nullptr; + if (!GetFileSet(parameters, eval, content, target, fileSet)) { return std::string{}; } if (!fileSet) { @@ -3697,7 +3703,38 @@ return std::string{}; } - return fileSet->GetProperty(propertyName); + auto result = fileSet->GetProperty(propertyName); + + if (propertyName == "BASE_DIRS"_s || propertyName == "SOURCES"_s || + propertyName == "INTERFACE_SOURCES"_s || + propertyName == "INCLUDE_DIRECTORIES"_s || + propertyName == "INTERFACE_INCLUDE_DIRECTORIES"_s || + propertyName == "COMPILE_OPTIONS"_s || + propertyName == "INTERFACE_COMPILE_OPTIONS"_s || + propertyName == "COMPILE_DEFINITIONS"_s || + propertyName == "INTERFACE_COMPILE_DEFINITIONS"_s) { + cmGeneratorExpressionDAGChecker dagChecker{ + target, propertyName, content, + dagCheckerParent, eval->Context, eval->Backtrace, + }; + switch (dagChecker.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dagChecker.ReportError(eval, content->GetOriginalExpression()); + return std::string{}; + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: + // No error. We just skip cyclic references. + return std::string{}; + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + return cmGeneratorExpression::StripEmptyListElements( + this->EvaluateDependentExpression(result, eval, target, &dagChecker, + target)); + } + + return result; } } fileSetPropertyNode; @@ -3810,7 +3847,7 @@ std::string Evaluate( std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* content, - cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override + cmGeneratorExpressionDAGChecker* dagCheckerParent) const override { static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); @@ -3860,7 +3897,47 @@ return std::string{}; } - return sourceFile->GetPropertyForUser(propertyName); + if (propertyName == "OBJECT_NAME"_s) { + return eval->Context.LG->GetCustomObjectFileName(*sourceFile); + } + + std::string result = sourceFile->GetPropertyForUser(propertyName); + + if (propertyName == "INCLUDE_DIRECTORIES"_s || + propertyName == "COMPILE_DEFINITIONS"_s || + propertyName == "COMPILE_OPTIONS"_s || + propertyName == "COMPILE_FLAGS"_s || + propertyName == "OBJECT_OUTPUTS"_s || + propertyName == "VS_DEPLOYMENT_CONTENT"_s || + propertyName == "VS_SETTINGS"_s) { + if (eval->HeadTarget) { + cmGeneratorExpressionDAGChecker dagChecker{ + eval->HeadTarget, propertyName, content, + dagCheckerParent, eval->Context, eval->Backtrace, + }; + switch (dagChecker.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: { + dagChecker.ReportError(eval, content->GetOriginalExpression()); + return std::string{}; + } + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + return cmGeneratorExpression::StripEmptyListElements( + this->EvaluateDependentExpression(result, eval, eval->HeadTarget, + &dagChecker, eval->CurrentTarget)); + } + + return cmGeneratorExpression::StripEmptyListElements( + this->EvaluateDependentExpression(result, eval, eval->HeadTarget, + dagCheckerParent, + eval->CurrentTarget)); + } + + return result; } } sourcePropertyNode; @@ -4303,6 +4380,55 @@ return std::string(); } + std::vector<std::string> sourceFilePaths; + for (auto const& arg : cmMakeRange(parameters).advance(1)) { + if (cmHasLiteralPrefix(arg, "SOURCE_FILES:")) { + cm::string_view listView{ arg.c_str() + cmStrLen("SOURCE_FILES:") }; + std::size_t semicolon; + std::size_t start = 0; + do { + semicolon = listView.find(';', start); + sourceFilePaths.push_back( + std::string{ listView.substr(start, semicolon - start) }); + start = semicolon + 1; + } while (semicolon != cm::string_view::npos); + } else { + reportError(eval, content->GetOriginalExpression(), + cmStrCat("Unrecognized argument:\n ", arg)); + return std::string(); + } + } + + if (gt->IsImported() && !sourceFilePaths.empty()) { + reportError( + eval, content->GetOriginalExpression(), + cmStrCat("Cannot use SOURCE_FILES argument on imported target \"", + tgtName, '"')); + return std::string(); + } + std::set<cmSourceFile const*> sourceFiles; + for (auto const& sf : gt->GetSourceFiles(eval->Context.Config)) { + sourceFiles.insert(sf.Value); + } + std::set<cmSourceFile const*> filteredSourceFiles; + for (auto const& path : sourceFilePaths) { + if (!cmSystemTools::FileIsFullPath(path)) { + reportError( + eval, content->GetOriginalExpression(), + cmStrCat("Source file:\n ", path, "\nis not an absolute path")); + return std::string(); + } + + auto const* sf = gt->Makefile->GetSource(path); + if (!sf || !sourceFiles.count(sf)) { + reportError(eval, content->GetOriginalExpression(), + cmStrCat("Source file:\n ", path, + "\ndoes not exist for target \"", tgtName, '"')); + return std::string(); + } + filteredSourceFiles.insert(sf); + } + cmList objects; if (gt->IsImported()) { @@ -4315,7 +4441,11 @@ } eval->HadContextSensitiveCondition = true; } else { - gt->GetTargetObjectNames(eval->Context.Config, objects); + auto const filter = + [&filteredSourceFiles](cmSourceFile const& sf) -> bool { + return filteredSourceFiles.empty() || filteredSourceFiles.count(&sf); + }; + gt->GetTargetObjectNames(eval->Context.Config, filter, objects); std::string obj_dir; if (eval->EvaluateForBuildsystem && !gg->SupportsCrossConfigs()) { @@ -4341,6 +4471,8 @@ return objects.to_string(); } + + int NumExpectedParameters() const override { return OneOrMoreParameters; } } targetObjectsNode; struct TargetRuntimeDllsBaseNode : public cmGeneratorExpressionNode @@ -4575,9 +4707,7 @@ cmLocalGenerator* lg = eval->HeadTarget->GetLocalGenerator(); switch (statusForTarget(eval->HeadTarget, policy)) { case cmPolicies::WARN: - lg->IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(policyForString(policy))); + lg->IssuePolicyWarning(policyForString(policy)); CM_FALLTHROUGH; case cmPolicies::OLD: return "0"; @@ -4656,17 +4786,16 @@ cm::GenEx::Evaluation* eval) { eval->AllTargets.insert(target); - cmLocalGenerator const* lg = eval->Context.LG; + cmLocalGenerator const* const lg = eval->Context.LG; switch (target->GetPolicyStatusCMP0112()) { case cmPolicies::WARN: if (lg->GetMakefile()->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0112")) { - std::string err = - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112), - "\nDependency being added to target:\n \"", - target->GetName(), "\"\n"); - lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING, - err, eval->Backtrace); + lg->IssuePolicyWarning( + cmPolicies::CMP0112, {}, + cmStrCat("Dependency being added to target:\n \""_s, + target->GetName(), "\"\n"_s), + eval->Backtrace); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -5320,12 +5449,14 @@ return std::string(); } + cmLocalGenerator const* const lg = eval->Context.LG; + std::string language = target->GetLinkerLanguage(eval->Context.Config); std::string pdbSupportVar = cmStrCat("CMAKE_", language, "_LINKER_SUPPORTS_PDB"); - if (!eval->Context.LG->GetMakefile()->IsOn(pdbSupportVar)) { + if (!lg->GetMakefile()->IsOn(pdbSupportVar)) { ::reportError( eval, content->GetOriginalExpression(), "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker."); @@ -5353,13 +5484,10 @@ if (target->GetPolicyStatusCMP0202() == cmPolicies::WARN && postfix != Postfix::Unspecified) { - eval->Context.LG->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0202), - "\n" - "\"POSTFIX\" option is recognized only when the policy is " - "set to NEW. Since the policy is not set, the OLD behavior " - "will be used."), + lg->IssuePolicyWarning( + cmPolicies::CMP0202, {}, + "\"POSTFIX\" option is recognized only when the policy is set to NEW." + " Since the policy is not set, the OLD behavior will be used."_s, eval->Backtrace); }
diff --git a/Source/cmGeneratorFileSet.cxx b/Source/cmGeneratorFileSet.cxx new file mode 100644 index 0000000..c2505f5 --- /dev/null +++ b/Source/cmGeneratorFileSet.cxx
@@ -0,0 +1,598 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmGeneratorFileSet.h" + +#include <algorithm> +#include <map> +#include <sstream> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmAlgorithms.h" +#include "cmEvaluatedTargetProperty.h" +#include "cmFileSet.h" +#include "cmGenExContext.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorTarget.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmake.h" + +class cmLinkItem; + +namespace { +class FileSetPropertyEntry : public cm::TargetPropertyEntry +{ +public: + FileSetPropertyEntry( + std::vector<std::string> dirs, bool contextSensitiveDirs, + std::unique_ptr<cmCompiledGeneratorExpression> const& cge, + cmGeneratorFileSet const* fileSet, cmLinkItem const& item = NoLinkItem) + : cm::TargetPropertyEntry(item) + , BaseDirs(std::move(dirs)) + , ContextSensitiveDirs(contextSensitiveDirs) + , Cge(cge) + , FileSet(fileSet) + { + } + + static std::unique_ptr<cm::TargetPropertyEntry> CreateFileSetEntry( + std::vector<std::string> dirs, bool contextSensitiveDirs, + std::unique_ptr<cmCompiledGeneratorExpression> const& cge, + cmGeneratorFileSet const* fileSet, cmLinkItem const& item = NoLinkItem) + { + return cm::make_unique<FileSetPropertyEntry>( + std::move(dirs), contextSensitiveDirs, cge, fileSet, item); + } + + std::string const& Evaluate( + cm::GenEx::Context const& context, cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker) const override + { + std::map<std::string, std::vector<std::string>> filesPerDir; + this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir, this->Cge, + context, headTarget, dagChecker); + + std::vector<std::string> files; + for (auto const& it : filesPerDir) { + files.insert(files.end(), it.second.begin(), it.second.end()); + } + + this->Value = cmList::to_string(files); + return Value; + } + + cmListFileBacktrace GetBacktrace() const override + { + return this->Cge->GetBacktrace(); + } + + std::string const& GetInput() const override + { + return this->Cge->GetInput(); + } + + bool GetHadContextSensitiveCondition() const override + { + return this->ContextSensitiveDirs || + this->Cge->GetHadContextSensitiveCondition(); + } + +private: + std::vector<std::string> const BaseDirs; + bool const ContextSensitiveDirs; + std::unique_ptr<cmCompiledGeneratorExpression> const& Cge; + cmGeneratorFileSet const* FileSet; + mutable std::string Value; +}; + +void CreatePropertyGeneratorExpressions( + cmake& cmakeInstance, cmBTStringRange entries, + std::vector<std::unique_ptr<cm::TargetPropertyEntry>>& items) +{ + for (auto const& entry : entries) { + items.emplace_back( + cm::TargetPropertyEntry::Create(cmakeInstance, entry, false)); + } +} + +enum class OptionsParse +{ + None, + Shell +}; + +std::vector<BT<std::string>> ProcessOptions( + cm::EvaluatedTargetPropertyEntries const& entries, + OptionsParse parse = OptionsParse::None) +{ + std::vector<BT<std::string>> options; + std::unordered_set<std::string> uniqueOptions; + + for (cm::EvaluatedTargetPropertyEntry const& entry : entries.Entries) { + for (std::string const& opt : entry.Values) { + if (uniqueOptions.insert(opt).second) { + if (parse == OptionsParse::Shell && + cmHasLiteralPrefix(opt, "SHELL:")) { + std::vector<std::string> tmp; + cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp); + for (std::string& o : tmp) { + options.emplace_back(std::move(o), entry.Backtrace); + } + } else { + options.emplace_back(opt, entry.Backtrace); + } + } + } + } + return options; +} +std::vector<BT<std::string>> ProcessIncludes( + cmGeneratorTarget const* target, std::string const& fileSetName, + cm::string_view property, cm::EvaluatedTargetPropertyEntries& entries) +{ + std::vector<BT<std::string>> includes; + std::unordered_set<std::string> uniqueIncludes; + + for (cm::EvaluatedTargetPropertyEntry& entry : entries.Entries) { + for (std::string& include : entry.Values) { + if (!cmValue::IsOff(include)) { + cmSystemTools::ConvertToUnixSlashes(include); + } + + if (!cmSystemTools::FileIsFullPath(include)) { + target->GetLocalGenerator()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("File set \"", fileSetName, "\" from the target \"", + target->GetName(), "\" contains relative path in its ", + property, ":\n \"", include, "\"")); + return includes; + } + + if (uniqueIncludes.insert(include).second) { + includes.emplace_back(include, entry.Backtrace); + } + } + } + return includes; +} +} + +// +// Class cmGeneratorFileSet +// +cmGeneratorFileSet::cmGeneratorFileSet(cmGeneratorTarget const* target, + cmFileSet const* fileSet) + : Target(target) + , FileSet(fileSet) +{ + auto& cmake = *target->GetLocalGenerator()->GetCMakeInstance(); + + CreatePropertyGeneratorExpressions(cmake, fileSet->GetIncludeDirectories(), + this->IncludeDirectories); + CreatePropertyGeneratorExpressions(cmake, + fileSet->GetInterfaceIncludeDirectories(), + this->InterfaceIncludeDirectories); + + CreatePropertyGeneratorExpressions(cmake, fileSet->GetCompileOptions(), + this->CompileOptions); + CreatePropertyGeneratorExpressions(cmake, + fileSet->GetInterfaceCompileOptions(), + this->InterfaceCompileOptions); + + CreatePropertyGeneratorExpressions(cmake, fileSet->GetCompileDefinitions(), + this->CompileDefinitions); + CreatePropertyGeneratorExpressions(cmake, + fileSet->GetInterfaceCompileDefinitions(), + this->InterfaceCompileDefinitions); +} + +std::string const& cmGeneratorFileSet::GetName() const +{ + return this->FileSet->GetName(); +} +std::string const& cmGeneratorFileSet::GetType() const +{ + return this->FileSet->GetType(); +} +cm::FileSetMetadata::Visibility cmGeneratorFileSet::GetVisibility() const +{ + return this->FileSet->GetVisibility(); +} + +bool cmGeneratorFileSet::IsForSelf() const +{ + return this->FileSet->IsForSelf(); +} +bool cmGeneratorFileSet::IsForInterface() const +{ + return this->FileSet->IsForInterface(); +} +bool cmGeneratorFileSet::CanBeIncluded() const +{ + return this->FileSet->CanBeIncluded(); +} + +cmValue cmGeneratorFileSet::GetProperty(std::string const& prop) const +{ + return this->FileSet->GetProperty(prop); +} + +std::vector<BT<std::string>> cmGeneratorFileSet::GetIncludeDirectories( + std::string const& config, std::string const& lang) const +{ + ConfigAndLanguage cacheKey(config, lang); + { + auto it = this->IncludeDirectoriesCache.find(cacheKey); + if (it != this->IncludeDirectoriesCache.end()) { + return it->second; + } + } + + cm::GenEx::Context context(this->Target->GetLocalGenerator(), config, lang); + cmGeneratorExpressionDAGChecker dagChecker{ + this->Target, "INCLUDE_DIRECTORIES", nullptr, nullptr, context, + }; + + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker, + this->IncludeDirectories); + auto includes = ProcessIncludes(this->Target, this->GetName(), + "INCLUDE_DIRECTORIES"_s, entries); + this->IncludeDirectoriesCache.emplace(cacheKey, includes); + + return includes; +} +std::vector<BT<std::string>> +cmGeneratorFileSet::GetInterfaceIncludeDirectories( + std::string const& config, std::string const& lang) const +{ + ConfigAndLanguage cacheKey(config, lang); + { + auto it = this->InterfaceIncludeDirectoriesCache.find(cacheKey); + if (it != this->InterfaceIncludeDirectoriesCache.end()) { + return it->second; + } + } + + cm::GenEx::Context context(this->Target->GetLocalGenerator(), config, lang); + cmGeneratorExpressionDAGChecker dagChecker{ + this->Target, "INCLUDE_DIRECTORIES", nullptr, nullptr, context, + }; + + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker, + this->InterfaceIncludeDirectories); + auto includes = ProcessIncludes(this->Target, this->GetName(), + "INTERFACE_INCLUDE_DIRECTORIES"_s, entries); + this->InterfaceIncludeDirectoriesCache.emplace(cacheKey, includes); + + return includes; +} + +std::vector<BT<std::string>> cmGeneratorFileSet::GetCompileOptions( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey(config, language); + { + auto it = this->CompileOptionsCache.find(cacheKey); + if (it != this->CompileOptionsCache.end()) { + return it->second; + } + } + + cm::GenEx::Context context(this->Target->GetLocalGenerator(), config, + language); + cmGeneratorExpressionDAGChecker dagChecker{ + this->Target, "COMPILE_OPTIONS", nullptr, nullptr, context, + }; + + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker, + this->CompileOptions); + auto options = ProcessOptions(entries, OptionsParse::Shell); + this->CompileOptionsCache.emplace(cacheKey, options); + + return options; +} +std::vector<BT<std::string>> cmGeneratorFileSet::GetInterfaceCompileOptions( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey(config, language); + { + auto it = this->InterfaceCompileOptionsCache.find(cacheKey); + if (it != this->InterfaceCompileOptionsCache.end()) { + return it->second; + } + } + + cm::GenEx::Context context(this->Target->GetLocalGenerator(), config, + language); + cmGeneratorExpressionDAGChecker dagChecker{ + this->Target, "COMPILE_OPTIONS", nullptr, nullptr, context, + }; + + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker, + this->InterfaceCompileOptions); + auto options = ProcessOptions(entries, OptionsParse::Shell); + this->InterfaceCompileOptionsCache.emplace(cacheKey, options); + + return options; +} + +std::vector<BT<std::string>> cmGeneratorFileSet::GetCompileDefinitions( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey(config, language); + { + auto it = this->CompileDefinitionsCache.find(cacheKey); + if (it != this->CompileDefinitionsCache.end()) { + return it->second; + } + } + + cm::GenEx::Context context(this->Target->GetLocalGenerator(), config, + language); + cmGeneratorExpressionDAGChecker dagChecker{ + this->Target, "COMPILE_DEFINITIONS", nullptr, nullptr, context, + }; + + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker, + this->CompileDefinitions); + auto defines = ProcessOptions(entries); + this->CompileDefinitionsCache.emplace(cacheKey, defines); + + return defines; +} +std::vector<BT<std::string>> +cmGeneratorFileSet::GetInterfaceCompileDefinitions( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey(config, language); + { + auto it = this->InterfaceCompileDefinitionsCache.find(cacheKey); + if (it != this->InterfaceCompileDefinitionsCache.end()) { + return it->second; + } + } + + cm::GenEx::Context context(this->Target->GetLocalGenerator(), config, + language); + cmGeneratorExpressionDAGChecker dagChecker{ + this->Target, "COMPILE_DEFINITIONS", nullptr, nullptr, context, + }; + + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker, + this->InterfaceCompileDefinitions); + auto defines = ProcessOptions(entries); + this->InterfaceCompileDefinitionsCache.emplace(cacheKey, defines); + + return defines; +} + +std::vector<BT<std::string>> const& cmGeneratorFileSet::GetDirectoryEntries() + const +{ + return this->FileSet->GetDirectoryEntries(); +} + +std::vector<BT<std::string>> const& cmGeneratorFileSet::GetFileEntries() const +{ + return this->FileSet->GetFileEntries(); +} + +std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& +cmGeneratorFileSet::CompileFileEntries() const +{ + std::unordered_set<std::string> uniqueSrcs; + + if (this->CompiledFileEntries.empty() && + !this->FileSet->GetFileEntries().empty()) { + for (auto const& entry : this->FileSet->GetFileEntries()) { + for (auto const& ex : cmList{ entry.Value }) { + if (uniqueSrcs.insert(ex).second) { + cmGeneratorExpression ge( + *this->FileSet->GetMakefile()->GetCMakeInstance(), + entry.Backtrace); + auto cge = ge.Parse(ex); + this->CompiledFileEntries.push_back(std::move(cge)); + } + } + } + } + + return this->CompiledFileEntries; +} + +std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& +cmGeneratorFileSet::CompileDirectoryEntries() const +{ + if (this->CompiledDirectoryEntries.empty() && + !this->FileSet->GetDirectoryEntries().empty()) { + for (auto const& entry : this->FileSet->GetDirectoryEntries()) { + for (auto const& ex : cmList{ entry.Value }) { + cmGeneratorExpression ge( + *this->FileSet->GetMakefile()->GetCMakeInstance(), entry.Backtrace); + auto cge = ge.Parse(ex); + this->CompiledDirectoryEntries.push_back(std::move(cge)); + } + } + } + + return this->CompiledDirectoryEntries; +} + +std::vector<std::string> cmGeneratorFileSet::EvaluateDirectoryEntries( + std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& cges, + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + struct DirCacheEntry + { + std::string collapsedDir; + cm::optional<cmSystemTools::FileId> fileId; + }; + + std::unordered_map<std::string, DirCacheEntry> dirCache; + std::vector<std::string> result; + for (auto const& cge : cges) { + auto entry = cge->Evaluate(context, dagChecker, target); + cmList dirs{ entry }; + for (std::string dir : dirs) { + if (!cmSystemTools::FileIsFullPath(dir)) { + dir = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', dir); + } + + auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry()); + auto& dirCacheEntry = dirCacheResult.first->second; + auto const isNewCacheEntry = dirCacheResult.second; + + if (isNewCacheEntry) { + cmSystemTools::FileId fileId; + auto isFileIdValid = cmSystemTools::GetFileId(dir, fileId); + dirCacheEntry.collapsedDir = cmSystemTools::CollapseFullPath(dir); + dirCacheEntry.fileId = + isFileIdValid ? cm::optional<decltype(fileId)>(fileId) : cm::nullopt; + } + + for (auto const& priorDir : result) { + auto priorDirCacheEntry = dirCache.at(priorDir); + bool sameFile = dirCacheEntry.fileId.has_value() && + priorDirCacheEntry.fileId.has_value() && + (*dirCacheEntry.fileId == *priorDirCacheEntry.fileId); + if (!sameFile && + (cmSystemTools::IsSubDirectory(dirCacheEntry.collapsedDir, + priorDirCacheEntry.collapsedDir) || + cmSystemTools::IsSubDirectory(priorDirCacheEntry.collapsedDir, + dirCacheEntry.collapsedDir))) { + context.LG->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Base directories in file set cannot be subdirectories of each " + "other:\n ", + priorDir, "\n ", dir), + cge->GetBacktrace()); + return {}; + } + } + result.push_back(dir); + } + } + return result; +} + +void cmGeneratorFileSet::EvaluateFileEntry( + std::vector<std::string> const& dirs, + std::map<std::string, std::vector<std::string>>& filesPerDir, + std::unique_ptr<cmCompiledGeneratorExpression> const& cge, + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + auto files = cge->Evaluate(context, dagChecker, target); + for (std::string file : cmList{ files }) { + if (!cmSystemTools::FileIsFullPath(file)) { + file = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', file); + } + auto collapsedFile = cmSystemTools::CollapseFullPath(file); + bool found = false; + std::string relDir; + for (auto const& dir : dirs) { + auto collapsedDir = cmSystemTools::CollapseFullPath(dir); + if (cmSystemTools::IsSubDirectory(collapsedFile, collapsedDir)) { + found = true; + relDir = cmSystemTools::GetParentDirectory( + cmSystemTools::RelativePath(collapsedDir, collapsedFile)); + break; + } + } + if (!found) { + std::ostringstream e; + e << "File:\n " << file + << "\nmust be in one of the file set's base directories:"; + for (auto const& dir : dirs) { + e << "\n " << dir; + } + context.LG->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, e.str(), cge->GetBacktrace()); + return; + } + + filesPerDir[relDir].push_back(file); + } +} + +namespace { +bool EntryIsContextSensitive( + std::unique_ptr<cmCompiledGeneratorExpression> const& cge) +{ + return cge->GetHadContextSensitiveCondition(); +} +} + +std::vector<std::unique_ptr<cm::TargetPropertyEntry>> +cmGeneratorFileSet::GetSources( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + std::vector<std::unique_ptr<TargetPropertyEntry>> entries; + + auto directories = this->GetDirectories(context, target, dagChecker); + bool contextSensitive = directories.second; + + for (auto const& entry : this->CompileFileEntries()) { + auto propEntry = FileSetPropertyEntry::CreateFileSetEntry( + directories.first, contextSensitive, entry, this); + entries.push_back(std::move(propEntry)); + } + + return entries; +} + +std::pair<std::vector<std::string>, bool> cmGeneratorFileSet::GetDirectories( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + auto const& directoryEntries = this->CompileDirectoryEntries(); + auto directories = this->EvaluateDirectoryEntries(directoryEntries, context, + target, dagChecker); + bool contextSensitive = std::any_of( + directoryEntries.begin(), directoryEntries.end(), EntryIsContextSensitive); + + return std::make_pair(std::move(directories), contextSensitive); +} + +std::pair<std::map<std::string, std::vector<std::string>>, bool> +cmGeneratorFileSet::GetFiles(cm::GenEx::Context const& context, + cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + auto directories = this->GetDirectories(context, target, dagChecker); + + auto const& fileEntries = this->CompileFileEntries(); + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + this->EvaluateFileEntry(directories.first, files, entry, context, target, + dagChecker); + } + bool contextSensitive = directories.second || + std::any_of(fileEntries.begin(), fileEntries.end(), + EntryIsContextSensitive); + + return std::make_pair(std::move(files), contextSensitive); +}
diff --git a/Source/cmGeneratorFileSet.h b/Source/cmGeneratorFileSet.h new file mode 100644 index 0000000..cfad7e0 --- /dev/null +++ b/Source/cmGeneratorFileSet.h
@@ -0,0 +1,139 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "cmFileSetMetadata.h" +#include "cmGeneratorExpression.h" +#include "cmListFileCache.h" +#include "cmTargetPropertyEntry.h" +#include "cmValue.h" + +namespace cm { +namespace GenEx { +struct Context; +} +} + +struct cmGeneratorExpressionDAGChecker; + +class cmFileSet; +class cmGeneratorTarget; + +class cmGeneratorFileSet +{ +public: + using TargetPropertyEntry = cm::TargetPropertyEntry; + + cmGeneratorFileSet(cmGeneratorTarget const*, cmFileSet const*); + ~cmGeneratorFileSet() = default; + + cmGeneratorFileSet(cmGeneratorFileSet&&) = default; + cmGeneratorFileSet(cmGeneratorFileSet const&) = delete; + cmGeneratorFileSet& operator=(cmGeneratorFileSet const&) = delete; + + std::string const& GetName() const; + std::string const& GetType() const; + cm::FileSetMetadata::Visibility GetVisibility() const; + + bool IsForSelf() const; + bool IsForInterface() const; + bool CanBeIncluded() const; + + cmGeneratorTarget const* GetTarget() const { return this->Target; } + bool BelongsTo(cmGeneratorTarget const* target) const + { + return this->Target == target; + } + + cmFileSet const* GetFileSet() const { return this->FileSet; } + + cmValue GetProperty(std::string const& prop) const; + + std::vector<BT<std::string>> GetIncludeDirectories( + std::string const& config, std::string const& lang) const; + std::vector<BT<std::string>> GetInterfaceIncludeDirectories( + std::string const& config, std::string const& lang) const; + + std::vector<BT<std::string>> GetCompileOptions( + std::string const& config, std::string const& language) const; + std::vector<BT<std::string>> GetInterfaceCompileOptions( + std::string const& config, std::string const& language) const; + + std::vector<BT<std::string>> GetCompileDefinitions( + std::string const& config, std::string const& language) const; + std::vector<BT<std::string>> GetInterfaceCompileDefinitions( + std::string const& config, std::string const& language) const; + + std::vector<BT<std::string>> const& GetDirectoryEntries() const; + std::vector<BT<std::string>> const& GetFileEntries() const; + + std::vector<std::unique_ptr<TargetPropertyEntry>> GetSources( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + + // returned value: + // first: list of directories + // second: is context sensitive + std::pair<std::vector<std::string>, bool> GetDirectories( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + // returned value: + // first: list of files per directory + // second: is context sensitive + std::pair<std::map<std::string, std::vector<std::string>>, bool> GetFiles( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + + std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& + CompileFileEntries() const; + + std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& + CompileDirectoryEntries() const; + + std::vector<std::string> EvaluateDirectoryEntries( + std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& cges, + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + + void EvaluateFileEntry( + std::vector<std::string> const& dirs, + std::map<std::string, std::vector<std::string>>& filesPerDir, + std::unique_ptr<cmCompiledGeneratorExpression> const& cge, + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + +private: + cmGeneratorTarget const* Target; + cmFileSet const* FileSet; + mutable std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> + CompiledDirectoryEntries; + mutable std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> + CompiledFileEntries; + + using TargetPropertyEntries = + std::vector<std::unique_ptr<TargetPropertyEntry>>; + TargetPropertyEntries IncludeDirectories; + TargetPropertyEntries InterfaceIncludeDirectories; + TargetPropertyEntries CompileOptions; + TargetPropertyEntries InterfaceCompileOptions; + TargetPropertyEntries CompileDefinitions; + TargetPropertyEntries InterfaceCompileDefinitions; + + using ConfigAndLanguage = std::pair<std::string, std::string>; + using ConfigAndLanguageToBTStrings = + std::map<ConfigAndLanguage, std::vector<BT<std::string>>>; + mutable ConfigAndLanguageToBTStrings IncludeDirectoriesCache; + mutable ConfigAndLanguageToBTStrings InterfaceIncludeDirectoriesCache; + mutable ConfigAndLanguageToBTStrings CompileOptionsCache; + mutable ConfigAndLanguageToBTStrings InterfaceCompileOptionsCache; + mutable ConfigAndLanguageToBTStrings CompileDefinitionsCache; + mutable ConfigAndLanguageToBTStrings InterfaceCompileDefinitionsCache; +};
diff --git a/Source/cmGeneratorFileSets.cxx b/Source/cmGeneratorFileSets.cxx new file mode 100644 index 0000000..b480880 --- /dev/null +++ b/Source/cmGeneratorFileSets.cxx
@@ -0,0 +1,449 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmGeneratorFileSets.h" + +#include <algorithm> +#include <iterator> +#include <map> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cm/optional> +#include <cmext/algorithm> + +#include "cmFileSet.h" +#include "cmFileSetMetadata.h" +#include "cmGenExContext.h" +#include "cmGenExEvaluation.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorExpressionNode.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorTarget.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmSourceFile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" + +cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target, + cmLocalGenerator* lg) + : Target(target) + , LocalGenerator(lg) +{ + bool isFramework = target->IsFrameworkOnApple(); + auto issueMessage = [&target](cmFileSet const* fileSet) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The file set ")", fileSet->GetName(), R"(", of type ")", + fileSet->GetType(), + R"(", is incompatible with the "FRAMEWORK" target ")", + target->GetName(), R"(".)")); + }; + + for (auto const& name : target->Target->GetAllPrivateFileSets()) { + cmFileSet const* fileSet = target->Target->GetFileSet(name); + if (isFramework && + !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { + issueMessage(fileSet); + continue; + } + auto entry = this->FileSets.emplace( + name, cm::make_unique<cmGeneratorFileSet>(target, fileSet)); + auto const* genFileSet = entry.first->second.get(); + this->AllFileSets.push_back(genFileSet); + this->SelfFileSets[genFileSet->GetType()].push_back(genFileSet); + } + for (auto const& name : target->Target->GetAllInterfaceFileSets()) { + auto it = this->FileSets.find(name); + cmGeneratorFileSet const* genFileSet = nullptr; + if (it == this->FileSets.end()) { + cmFileSet const* fileSet = target->Target->GetFileSet(name); + if (isFramework && + !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { + issueMessage(fileSet); + continue; + } + auto entry = this->FileSets.emplace( + name, cm::make_unique<cmGeneratorFileSet>(target, fileSet)); + genFileSet = entry.first->second.get(); + this->AllFileSets.push_back(genFileSet); + } else { + genFileSet = it->second.get(); + } + this->InterfaceFileSets[genFileSet->GetType()].push_back(genFileSet); + } +} +cmGeneratorFileSets::~cmGeneratorFileSets() = default; + +std::vector<cm::string_view> cmGeneratorFileSets::GetFileSetTypes() const +{ + return cm::keys(this->SelfFileSets); +} + +std::vector<cm::string_view> cmGeneratorFileSets::GetInterfaceFileSetTypes() + const +{ + return cm::keys(this->InterfaceFileSets); +} + +std::vector<cmGeneratorFileSet const*> const& +cmGeneratorFileSets::GetAllFileSets() const +{ + return this->AllFileSets; +} + +namespace { +std::vector<cmGeneratorFileSet const*> NoFileSets; +} + +std::vector<cmGeneratorFileSet const*> const& cmGeneratorFileSets::GetFileSets( + cm::string_view type) const +{ + auto it = this->SelfFileSets.find(type); + if (it != this->SelfFileSets.end()) { + return it->second; + } + return NoFileSets; +} +std::vector<cmGeneratorFileSet const*> const& +cmGeneratorFileSets::GetInterfaceFileSets(cm::string_view type) const +{ + auto it = this->InterfaceFileSets.find(type); + if (it != this->InterfaceFileSets.end()) { + return it->second; + } + return NoFileSets; +} + +cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSet( + std::string const& name) const +{ + auto const it = this->FileSets.find(name); + if (it != this->FileSets.end()) { + return it->second.get(); + } + return nullptr; +} + +std::unordered_set<cmGeneratorFileSet const*> const& +cmGeneratorFileSets::GetAllFileSetsForSource(std::string const& config, + std::string const& path) const +{ + this->BuildInfoCache(config); + + auto const& info = this->Configs[config]; + + auto const it = info.FileSetCache.find(path); + if (it != info.FileSetCache.end()) { + return it->second; + } + + // search in all the dependents + auto const it2 = info.InterfaceFileSetCache.find(path); + if (it2 != info.InterfaceFileSetCache.end()) { + return it2->second; + } + + static std::unordered_set<cmGeneratorFileSet const*> emptySet; + return emptySet; +} +std::unordered_set<cmGeneratorFileSet const*> const& +cmGeneratorFileSets::GetAllFileSetsForSource(std::string const& config, + cmSourceFile const* sf) const +{ + return this->GetAllFileSetsForSource(config, sf->GetFullPath()); +} + +cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource( + std::string const& config, std::string const& file) const +{ + auto const& fileSets = this->GetAllFileSetsForSource(config, file); + if (fileSets.empty()) { + return nullptr; + } + return *fileSets.begin(); +} +cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource( + std::string const& config, cmSourceFile const* sf) const +{ + return this->GetFileSetForSource(config, sf->GetFullPath()); +} + +std::vector<std::unique_ptr<cm::TargetPropertyEntry>> +cmGeneratorFileSets::GetSources( + std::function<bool(cmGeneratorFileSet const*)> include, + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + std::vector<std::unique_ptr<TargetPropertyEntry>> entries; + + for (auto const& entry : this->FileSets) { + auto const* fileSet = entry.second.get(); + if (include(fileSet)) { + auto sources = fileSet->GetSources(context, target, dagChecker); + std::move(sources.begin(), sources.end(), std::back_inserter(entries)); + } + } + + return entries; +} + +std::vector<std::unique_ptr<cm::TargetPropertyEntry>> +cmGeneratorFileSets::GetSources( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + return this->GetSources( + [](cmGeneratorFileSet const* fileSet) -> bool { + return fileSet->IsForSelf(); + }, + context, target, dagChecker); +} +std::vector<std::unique_ptr<cm::TargetPropertyEntry>> +cmGeneratorFileSets::GetSources( + std::string type, cm::GenEx::Context const& context, + cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + return this->GetSources( + [&type](cmGeneratorFileSet const* fileSet) -> bool { + return fileSet->IsForSelf() && fileSet->GetType() == type; + }, + context, target, dagChecker); +} + +std::vector<std::unique_ptr<cm::TargetPropertyEntry>> +cmGeneratorFileSets::GetInterfaceSources( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + return this->GetSources( + [](cmGeneratorFileSet const* fileSet) -> bool { + return fileSet->IsForInterface(); + }, + context, target, dagChecker); +} +std::vector<std::unique_ptr<cm::TargetPropertyEntry>> +cmGeneratorFileSets::GetInterfaceSources( + std::string type, cm::GenEx::Context const& context, + cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const +{ + return this->GetSources( + [&type](cmGeneratorFileSet const* fileSet) -> bool { + return fileSet->IsForInterface() && fileSet->GetType() == type; + }, + context, target, dagChecker); +} + +bool cmGeneratorFileSets::MaybeHaveInterfaceProperty( + cm::string_view type, std::string const& prop, + cm::GenEx::Evaluation* eval) const +{ + std::string const key = + cmStrCat(type, "::", prop, '@', eval->Context.Config); + auto i = this->MaybeInterfacePropertyExists.find(key); + if (i == this->MaybeInterfacePropertyExists.end()) { + // Insert an entry now in case there is a cycle. + i = this->MaybeInterfacePropertyExists.emplace(key, false).first; + bool& maybeInterfaceProp = i->second; + + for (auto const* fileSet : this->GetInterfaceFileSets(type)) { + // If this file set itself has a non-empty property value, we are done. + maybeInterfaceProp = !fileSet->GetProperty(prop).IsEmpty(); + if (maybeInterfaceProp) { + break; + } + } + + // Otherwise, recurse to interface dependencies. + if (!maybeInterfaceProp) { + cmGeneratorTarget const* headTarget = + eval->HeadTarget ? eval->HeadTarget : this->Target; + if (cmLinkInterfaceLibraries const* iface = + this->Target->GetLinkInterfaceLibraries( + eval->Context.Config, headTarget, + cmGeneratorTarget::UseTo::Compile)) { + if (iface->HadHeadSensitiveCondition) { + // With a different head target we may get to a library with + // this interface property. + maybeInterfaceProp = true; + } else { + // The transitive interface libraries do not depend on the + // head target, so we can follow them. + for (cmLinkItem const& lib : iface->Libraries) { + if (lib.Target && + lib.Target->GetGeneratorFileSets()->MaybeHaveInterfaceProperty( + type, prop, eval)) { + maybeInterfaceProp = true; + break; + } + } + } + } + } + } + return i->second; +} + +std::string cmGeneratorFileSets::EvaluateInterfaceProperty( + cm::string_view type, std::string const& prop, cm::GenEx::Evaluation* eval, + cmGeneratorExpressionDAGChecker* dagCheckerParent) const +{ + // If the property does not appear transitively at all, we are done. + if (!this->MaybeHaveInterfaceProperty(type, prop, eval)) { + return std::string{}; + } + + cmList result; + cmGeneratorExpressionDAGChecker dagChecker{ + this->Target, prop, nullptr, + dagCheckerParent, eval->Context, eval->Backtrace, + }; + switch (dagChecker.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dagChecker.ReportError( + eval, + cmStrCat("$<FILE_SET_PROPERTY:*,TARGET:", this->Target->GetName(), ',', + prop, '>')); + return std::string{}; + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: + // No error. We just skip cyclic references. + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + // No error. We have already seen this transitive property. + return std::string{}; + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + cmGeneratorTarget const* headTarget = + eval->HeadTarget ? eval->HeadTarget : this->Target; + + for (auto const* fileSet : this->GetInterfaceFileSets(type)) { + if (cmValue p = fileSet->GetProperty(prop)) { + result.append(cmGeneratorExpressionNode::EvaluateDependentExpression( + *p, eval, headTarget, &dagChecker, this->Target)); + } + } + + if (cmLinkInterfaceLibraries const* iface = + this->Target->GetLinkInterfaceLibraries( + eval->Context.Config, headTarget, + cmGeneratorTarget::UseTo::Compile)) { + eval->HadContextSensitiveCondition = eval->HadContextSensitiveCondition || + iface->HadContextSensitiveCondition; + for (cmLinkItem const& lib : iface->Libraries) { + // Broken code can have a target in its own link interface. + // Don't follow such link interface entries so as not to create a + // self-referencing loop. + if (lib.Target && lib.Target != this->Target) { + // Pretend $<FILE_SET__PROPERTY:fileSet,TARGET:lib.Target,prop> + // appeared in the above property and hand-evaluate it as if it were + // compiled. + // Create a context as cmCompiledGeneratorExpression::Evaluate does. + cm::GenEx::Evaluation libEval( + eval->Context, eval->Quiet, headTarget, this->Target, + eval->EvaluateForBuildsystem, eval->Backtrace); + std::string libResult = cmGeneratorExpression::StripEmptyListElements( + lib.Target->GetGeneratorFileSets()->EvaluateInterfaceProperty( + type, prop, &libEval, &dagChecker)); + if (!libResult.empty()) { + result.append(libResult); + } + eval->HadContextSensitiveCondition = + eval->HadContextSensitiveCondition || + libEval.HadContextSensitiveCondition; + eval->HadHeadSensitiveCondition = + eval->HadHeadSensitiveCondition || libEval.HadHeadSensitiveCondition; + } + } + } + + return result.to_string(); +} + +namespace { +void GetInterfaceFiles( + cmGeneratorTarget const* target, cm::GenEx::Context const& context, + std::unordered_set<cmGeneratorTarget const*>& targets, + std::map<std::string, std::unordered_set<cmGeneratorFileSet const*>>& cache) +{ + namespace Metadata = cm::FileSetMetadata; + + for (auto const& type : Metadata::GetKnownTypes()) { + auto fileSetDescriptor = Metadata::GetFileSetDescriptor(type); + if (fileSetDescriptor && + fileSetDescriptor->Lookup == Metadata::FileSetLookup::Dependencies) { + for (auto const* fileSet : target->GetInterfaceFileSets(type)) { + auto files = fileSet->GetFiles(context, target); + + for (auto const& it : files.first) { + for (auto const& filename : it.second) { + auto collapsedFile = cmSystemTools::CollapseFullPath(filename); + cache[collapsedFile].insert(fileSet); + } + } + } + } + } + + if (cmLinkInterfaceLibraries const* iface = + target->GetLinkInterfaceLibraries(context.Config, target, + cmGeneratorTarget::UseTo::Compile)) { + for (cmLinkItem const& lib : iface->Libraries) { + if (lib.Target && lib.Target != target && + targets.insert(lib.Target).second) { + GetInterfaceFiles(lib.Target, context, targets, cache); + } + } + } +} +} + +void cmGeneratorFileSets::BuildInfoCache(std::string const& config) const +{ + auto& info = this->Configs[config]; + + if (info.BuiltCache) { + return; + } + + cm::GenEx::Context context(this->LocalGenerator, config); + + for (auto const& item : this->FileSets) { + auto const* fileSet = item.second.get(); + + auto files = fileSet->GetFiles(context, this->Target); + + for (auto const& it : files.first) { + for (auto const& filename : it.second) { + auto collapsedFile = cmSystemTools::CollapseFullPath(filename); + info.FileSetCache[collapsedFile].insert(fileSet); + } + } + } + + // retrieve all files inherited from dependent targets + std::unordered_set<cmGeneratorTarget const*> targets; + + if (cmLinkImplementationLibraries const* impl = + this->Target->GetLinkImplementationLibraries( + config, cmGeneratorTarget::UseTo::Compile)) { + for (cmLinkItem const& lib : impl->Libraries) { + if (lib.Target) { + GetInterfaceFiles(lib.Target, context, targets, + info.InterfaceFileSetCache); + } + } + } + + info.BuiltCache = true; +}
diff --git a/Source/cmGeneratorFileSets.h b/Source/cmGeneratorFileSets.h new file mode 100644 index 0000000..3d47a0d --- /dev/null +++ b/Source/cmGeneratorFileSets.h
@@ -0,0 +1,123 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <functional> +#include <map> +#include <memory> +#include <string> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include <cm/string_view> + +#include "cmTargetPropertyEntry.h" + +namespace cm { +namespace GenEx { +struct Context; +struct Evaluation; +} +} + +struct cmGeneratorExpressionDAGChecker; + +class cmSourceFile; +class cmGeneratorTarget; +class cmGeneratorFileSet; +class cmLocalGenerator; + +class cmGeneratorFileSets +{ +public: + using TargetPropertyEntry = cm::TargetPropertyEntry; + + cmGeneratorFileSets(cmGeneratorTarget* target, cmLocalGenerator* lg); + ~cmGeneratorFileSets(); + + cmGeneratorFileSets(cmGeneratorFileSets const&) = delete; + cmGeneratorFileSets& operator=(cmGeneratorFileSets const&) = delete; + + bool Empty() const { return this->FileSets.empty(); } + + std::vector<cm::string_view> GetFileSetTypes() const; + std::vector<cm::string_view> GetInterfaceFileSetTypes() const; + + std::vector<cmGeneratorFileSet const*> const& GetAllFileSets() const; + std::vector<cmGeneratorFileSet const*> const& GetFileSets( + cm::string_view type) const; + std::vector<cmGeneratorFileSet const*> const& GetInterfaceFileSets( + cm::string_view type) const; + + cmGeneratorFileSet const* GetFileSet(std::string const& name) const; + + std::unordered_set<cmGeneratorFileSet const*> const& GetAllFileSetsForSource( + std::string const& config, std::string const& file) const; + std::unordered_set<cmGeneratorFileSet const*> const& GetAllFileSetsForSource( + std::string const& config, cmSourceFile const* sf) const; + + // returns the first FileSet of the set + cmGeneratorFileSet const* GetFileSetForSource(std::string const& config, + std::string const& file) const; + cmGeneratorFileSet const* GetFileSetForSource(std::string const& config, + cmSourceFile const* sf) const; + + std::vector<std::unique_ptr<TargetPropertyEntry>> GetSources( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + std::vector<std::unique_ptr<TargetPropertyEntry>> GetSources( + std::string type, cm::GenEx::Context const& context, + cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + + std::vector<std::unique_ptr<TargetPropertyEntry>> GetInterfaceSources( + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + std::vector<std::unique_ptr<TargetPropertyEntry>> GetInterfaceSources( + std::string type, cm::GenEx::Context const& context, + cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; + + std::string EvaluateInterfaceProperty( + cm::string_view type, std::string const& prop, cm::GenEx::Evaluation* eval, + cmGeneratorExpressionDAGChecker* dagCheckerParent) const; + +private: + std::vector<std::unique_ptr<cm::TargetPropertyEntry>> GetSources( + std::function<bool(cmGeneratorFileSet const*)> include, + cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cmGeneratorExpressionDAGChecker* dagChecker) const; + + // file sets indexed by name + std::map<std::string, std::unique_ptr<cmGeneratorFileSet>> FileSets; + std::vector<cmGeneratorFileSet const*> AllFileSets; + // list of private file sets indexed by type + std::unordered_map<cm::string_view, std::vector<cmGeneratorFileSet const*>> + SelfFileSets; + // list of interface file sets indexed by type + std::unordered_map<cm::string_view, std::vector<cmGeneratorFileSet const*>> + InterfaceFileSets; + + mutable std::unordered_map<std::string, bool> MaybeInterfacePropertyExists; + bool MaybeHaveInterfaceProperty(cm::string_view type, + std::string const& prop, + cm::GenEx::Evaluation* eval) const; + + struct InfoByConfig + { + bool BuiltCache = false; + std::map<std::string, std::unordered_set<cmGeneratorFileSet const*>> + FileSetCache; + std::map<std::string, std::unordered_set<cmGeneratorFileSet const*>> + InterfaceFileSetCache; + }; + mutable std::map<std::string, InfoByConfig> Configs; + + void BuildInfoCache(std::string const& config) const; + + cmGeneratorTarget* Target; + cmLocalGenerator* LocalGenerator; +};
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index d44d4bd..458e961 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx
@@ -8,8 +8,8 @@ #include <cstddef> #include <cstdio> #include <cstring> -#include <initializer_list> #include <sstream> +#include <string> #include <unordered_set> #include <utility> @@ -28,11 +28,14 @@ #include "cmCxxModuleUsageEffects.h" #include "cmExperimental.h" #include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmFileTimes.h" #include "cmGenExContext.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorOptions.h" #include "cmGlobalGenerator.h" #include "cmList.h" @@ -43,14 +46,12 @@ #include "cmPropertyMap.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" -#include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" #include "cmStandardLevel.h" #include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" -#include "cmSyntheticTargetCache.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetLinkLibraryType.h" @@ -79,22 +80,22 @@ static void CreatePropertyGeneratorExpressions( cmake& cmakeInstance, cmBTStringRange entries, - std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>>& items, + std::vector<std::unique_ptr<cm::TargetPropertyEntry>>& items, bool evaluateForBuildsystem = false) { for (auto const& entry : entries) { - items.emplace_back(cmGeneratorTarget::TargetPropertyEntry::Create( + items.emplace_back(cm::TargetPropertyEntry::Create( cmakeInstance, entry, evaluateForBuildsystem)); } } cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) : Target(t) + , Makefile(t->GetMakefile()) + , LocalGenerator(lg) + , GlobalGenerator(lg->GetGlobalGenerator()) + , FileSets(cm::make_unique<cmGeneratorFileSets>(this, lg)) { - this->Makefile = this->Target->GetMakefile(); - this->LocalGenerator = lg; - this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); - this->GlobalGenerator->ComputeTargetObjectDirectory(this); CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), @@ -138,7 +139,7 @@ } auto configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); std::string build_db_languages[] = { "CXX" }; for (auto const& language : build_db_languages) { for (auto const& config : configs) { @@ -821,12 +822,12 @@ bool const in_try_compile = this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile(); if (cmp0069 == cmPolicies::WARN && !in_try_compile) { - std::ostringstream w; - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0069) << "\n"; - w << "INTERPROCEDURAL_OPTIMIZATION property will be ignored for target " - << "'" << this->GetName() << "'."; - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace()); + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0069, {}, + cmStrCat("INTERPROCEDURAL_OPTIMIZATION property " + "will be ignored for target '"_s, + this->GetName(), "'."_s), + this->GetBacktrace()); this->PolicyReportedCMP0069 = true; } @@ -1005,6 +1006,24 @@ IMPLEMENT_VISIT(SourceKindManifest); } +void cmGeneratorTarget::GetRustMainCrateRoot( + std::vector<cmSourceFile const*>& data, std::string const& config) const +{ + IMPLEMENT_VISIT(SourceKindRustMainCrateRoot); +} + +cmSourceFile const* cmGeneratorTarget::GetRustMainCrateRoot( + std::string const& config) const +{ + std::vector<cmSourceFile const*> files; + GetRustMainCrateRoot(files, config); + if (files.empty()) { + return nullptr; + } + assert(files.size() == 1); + return files[0]; +} + std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const { if (!this->UtilityItemsDone) { @@ -1122,8 +1141,8 @@ // An INTERFACE library is in the build system if it has SOURCES // or C++ module filesets. if (!this->SourceEntries.empty() || - !this->Target->GetHeaderSetsEntries().empty() || - !this->Target->GetCxxModuleSetsEntries().empty()) { + !this->GetFileSets(cm::FileSetMetadata::HEADERS).empty() || + !this->GetFileSets(cm::FileSetMetadata::CXX_MODULES).empty()) { return true; } break; @@ -2158,6 +2177,8 @@ auto* const lg = this->GetLocalGenerator(); auto const* const mf = this->Makefile; + cmGeneratorFileSet const* const fileSet = + this->GetFileSetForSource(config, sf); // Compute the compiler launcher flags. if (CanUseCompilerLauncher(lang)) { @@ -2214,6 +2235,45 @@ } } + // File set specific flags + if (fileSet) { + // include flags + { + auto fsIncludes = fileSet->BelongsTo(this) + ? fileSet->GetIncludeDirectories(config, lang) + : fileSet->GetInterfaceIncludeDirectories(config, lang); + if (!fsIncludes.empty()) { + std::vector<std::string> includes; + lg->AppendIncludeDirectories(includes, cm::remove_BT(fsIncludes), *sf); + + auto includeFlags = + lg->GetIncludeFlags(includes, this, lang, config, false); + + for (auto&& flag : SplitFlags(includeFlags)) { + include_flags.emplace_back(FlagClassification::PrivateFlag, + FlagKind::Include, std::move(flag)); + } + } + } + } + + // source specific flags + { + // include flags + if (cmValue srcIncludes = sf->GetProperty("INCLUDE_DIRECTORIES")) { + std::vector<std::string> includes; + lg->AppendIncludeDirectories(includes, srcIncludes, *sf); + + auto includeFlags = + lg->GetIncludeFlags(includes, this, lang, config, false); + + for (auto&& flag : SplitFlags(includeFlags)) { + include_flags.emplace_back(FlagClassification::PrivateFlag, + FlagKind::Include, std::move(flag)); + } + } + } + // Compute target-wide flags. { FlagClassification cls = FlagClassification::BaselineFlag; @@ -2260,49 +2320,106 @@ } } - std::string const COMPILE_FLAGS("COMPILE_FLAGS"); std::string const COMPILE_OPTIONS("COMPILE_OPTIONS"); - cmGeneratorExpressionInterpreter genexInterpreter(lg, config, this, lang); // Source-specific flags. { - FlagClassification cls = FlagClassification::PrivateFlag; - FlagKind kind = FlagKind::Compile; - - std::string sourceFlags; - - if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) { - lg->AppendFlags(sourceFlags, - genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); - } - - if (cmValue coptions = sf->GetProperty(COMPILE_OPTIONS)) { - lg->AppendCompileOptions( - sourceFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); - } - - for (auto&& flag : SplitFlags(sourceFlags)) { - compile_flags.emplace_back(cls, kind, std::move(flag)); - } - - // Dependency tracking flags. + // Define flags { - if (!sfVars.DependencyFlags.empty()) { - cmRulePlaceholderExpander::RuleVariables vars; - auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); + if (cmValue srcDefines = sf->GetProperty("COMPILE_DEFINITIONS")) { + std::set<std::string> defines; + lg->AppendDefines(defines, srcDefines); - vars.DependencyFile = sfVars.DependencyFile.c_str(); - vars.DependencyTarget = sfVars.DependencyTarget.c_str(); + std::string defineFlags; + lg->JoinDefines(defines, defineFlags, lang); - std::string depfileFlags = sfVars.DependencyFlags; - rulePlaceholderExpander->ExpandRuleVariables(lg, depfileFlags, vars); - for (auto&& flag : SplitFlags(depfileFlags)) { - compile_flags.emplace_back(FlagClassification::LocationFlag, - FlagKind::BuildSystem, std::move(flag)); + for (auto&& flag : SplitFlags(defineFlags)) { + define_flags.emplace_back(FlagClassification::PrivateFlag, + FlagKind::Definition, std::move(flag)); } } } + + // Compile flags. + { + std::string const COMPILE_FLAGS("COMPILE_FLAGS"); + + FlagClassification cls = FlagClassification::PrivateFlag; + FlagKind kind = FlagKind::Compile; + + std::string sourceFlags; + + if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) { + lg->AppendFlags(sourceFlags, + genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); + } + + if (cmValue coptions = sf->GetProperty(COMPILE_OPTIONS)) { + lg->AppendCompileOptions( + sourceFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); + } + + for (auto&& flag : SplitFlags(sourceFlags)) { + compile_flags.emplace_back(cls, kind, std::move(flag)); + } + } + } + + // File set specific flags + if (fileSet) { + // Define flags + { + auto fsDefines = fileSet->BelongsTo(this) + ? fileSet->GetCompileDefinitions(config, lang) + : fileSet->GetInterfaceCompileDefinitions(config, lang); + if (!fsDefines.empty()) { + std::set<std::string> defines; + lg->AppendDefines(defines, fsDefines); + + std::string defineFlags; + lg->JoinDefines(defines, defineFlags, lang); + + for (auto&& flag : SplitFlags(defineFlags)) { + define_flags.emplace_back(FlagClassification::PrivateFlag, + FlagKind::Definition, std::move(flag)); + } + } + } + + // Compile flags. + { + auto options = fileSet->BelongsTo(this) + ? fileSet->GetCompileOptions(config, lang) + : fileSet->GetInterfaceCompileOptions(config, lang); + if (!options.empty()) { + std::string compileFlags; + lg->AppendCompileOptions(compileFlags, cm::remove_BT(options)); + + for (auto&& flag : SplitFlags(compileFlags)) { + compile_flags.emplace_back(FlagClassification::PrivateFlag, + FlagKind::Compile, std::move(flag)); + } + } + } + } + + // Dependency tracking flags. + { + if (!sfVars.DependencyFlags.empty()) { + cmRulePlaceholderExpander::RuleVariables vars; + auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); + + vars.DependencyFile = sfVars.DependencyFile.c_str(); + vars.DependencyTarget = sfVars.DependencyTarget.c_str(); + + std::string depfileFlags = sfVars.DependencyFlags; + rulePlaceholderExpander->ExpandRuleVariables(lg, depfileFlags, vars); + for (auto&& flag : SplitFlags(depfileFlags)) { + compile_flags.emplace_back(FlagClassification::LocationFlag, + FlagKind::BuildSystem, std::move(flag)); + } + } } // Precompiled headers. @@ -2313,7 +2430,8 @@ std::string pchFlags; // Add precompile headers compile options. - if (!sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!((fileSet && sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + sf->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { if (!pchSources.empty()) { std::string pchOptions; auto pchIt = pchSources.find(sf->GetFullPath()); @@ -2343,7 +2461,7 @@ std::string bmiFlags; auto const* fs = this->GetFileSetForSource(config, sf); - if (fs && fs->GetType() == "CXX_MODULES"_s) { + if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) { if (lang != "CXX"_s) { mf->IssueMessage( MessageType::FATAL_ERROR, @@ -2572,11 +2690,10 @@ switch (this->GetPolicyStatusCMP0104()) { case cmPolicies::WARN: if (!this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile()) { - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0104) + - "\nCUDA_ARCHITECTURES is empty for target \"" + this->GetName() + - "\"."); + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0104, {}, + cmStrCat("CUDA_ARCHITECTURES is empty for target \""_s, + this->GetName(), "\"."_s)); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -2793,6 +2910,42 @@ } } +void cmGeneratorTarget::AddRustTargetFlags(std::string& flags) const +{ + cmValue const edition = this->GetProperty("Rust_EDITION"); + if (edition && !edition->empty()) { + flags += " --edition=" + *edition; + } +} + +void cmGeneratorTarget::AddSwiftTargetFlags(std::string& flags) const +{ + if (cmValue version = GetProperty("Swift_LANGUAGE_VERSION")) { + if (cmSystemTools::VersionCompare( + cmSystemTools::OP_GREATER_EQUAL, + this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"), + "4.2")) { + flags += " -swift-version " + *version; + } + } + + if (!this->GetGlobalGenerator()->IsXcode() && + cmSystemTools::VersionCompare( + cmSystemTools::OP_GREATER_EQUAL, + this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"), + "5.8")) { + // Note: The Xcode generator sets the `SWIFT_PACKAGE_NAME` BuildSettings + // attribute + std::string const packageName = this->GetSwiftPackageName(); + if (!packageName.empty()) { + std::string const packageFlag = + this->Makefile->GetSafeDefinition("CMAKE_Swift_PACKAGE_NAME_FLAG"); + // Add the package name to the flags + flags += " " + packageFlag + " " + packageName; + } + } +} + void cmGeneratorTarget::AddCUDAToolkitFlags(std::string& flags) const { std::string const& compiler = @@ -4080,8 +4233,16 @@ void cmGeneratorTarget::GetTargetObjectNames( std::string const& config, std::vector<std::string>& objects) const { + this->GetTargetObjectNames( + config, [](cmSourceFile const&) -> bool { return true; }, objects); +} + +void cmGeneratorTarget::GetTargetObjectNames( + std::string const& config, std::function<bool(cmSourceFile const&)> filter, + std::vector<std::string>& objects) const +{ this->GetTargetObjectLocations( - config, + config, filter, [&objects](cmObjectLocation const& buildLoc, cmObjectLocation const&) { objects.push_back(buildLoc.GetPath()); }); @@ -4092,6 +4253,15 @@ std::function<void(cmObjectLocation const&, cmObjectLocation const&)> cb) const { + this->GetTargetObjectLocations( + config, [](cmSourceFile const&) -> bool { return true; }, cb); +} + +void cmGeneratorTarget::GetTargetObjectLocations( + std::string const& config, std::function<bool(cmSourceFile const&)> filter, + std::function<void(cmObjectLocation const&, cmObjectLocation const&)> cb) + const +{ std::vector<cmSourceFile const*> objectSources; this->GetObjectSources(objectSources, config); std::map<cmSourceFile const*, cmObjectLocations> mapping; @@ -4108,15 +4278,17 @@ auto const installUseShortPaths = this->GetUseShortObjectNamesForInstall(); for (cmSourceFile const* src : objectSources) { - // Find the object file name corresponding to this source file. - auto map_it = mapping.find(src); - auto const& buildLoc = map_it->second.GetLocation(buildUseShortPaths); - auto const& installLoc = - map_it->second.GetInstallLocation(installUseShortPaths, config); - // It must exist because we populated the mapping just above. - assert(!buildLoc.GetPath().empty()); - assert(!installLoc.GetPath().empty()); - cb(buildLoc, installLoc); + if (filter(*src)) { + // Find the object file name corresponding to this source file. + auto map_it = mapping.find(src); + auto const& buildLoc = map_it->second.GetLocation(buildUseShortPaths); + auto const& installLoc = + map_it->second.GetInstallLocation(installUseShortPaths, config); + // It must exist because we populated the mapping just above. + assert(!buildLoc.GetPath().empty()); + assert(!installLoc.GetPath().empty()); + cb(buildLoc, installLoc); + } } // We need to compute the relative path from the root of @@ -4124,11 +4296,14 @@ std::string rootObjectDir = this->GetObjectDirectory(config); rootObjectDir = cmSystemTools::CollapseFullPath(rootObjectDir); auto ispcObjects = this->GetGeneratedISPCObjects(config); - for (std::string const& output : ispcObjects) { - auto relativePathFromObjectDir = output.substr(rootObjectDir.size()); - cmObjectLocation ispcLoc(relativePathFromObjectDir); - // FIXME: apply short path to this object if needed. - cb(ispcLoc, ispcLoc); + for (auto const& output : ispcObjects) { + if (filter(*output.first)) { + auto relativePathFromObjectDir = + output.second.substr(rootObjectDir.size()); + cmObjectLocation ispcLoc(relativePathFromObjectDir); + // FIXME: apply short path to this object if needed. + cb(ispcLoc, ispcLoc); + } } } @@ -4392,8 +4567,9 @@ return iter->second; } -void cmGeneratorTarget::AddISPCGeneratedObject(std::vector<std::string>&& objs, - std::string const& config) +void cmGeneratorTarget::AddISPCGeneratedObject( + std::vector<std::pair<cmSourceFile const*, std::string>>&& objs, + std::string const& config) { std::string config_upper; if (!config.empty()) { @@ -4407,8 +4583,8 @@ } } -std::vector<std::string> cmGeneratorTarget::GetGeneratedISPCObjects( - std::string const& config) const +std::vector<std::pair<cmSourceFile const*, std::string>> +cmGeneratorTarget::GetGeneratedISPCObjects(std::string const& config) const { std::string config_upper; if (!config.empty()) { @@ -4416,7 +4592,7 @@ } auto iter = this->ISPCGeneratedObjects.find(config_upper); if (iter == this->ISPCGeneratedObjects.end()) { - return std::vector<std::string>{}; + return std::vector<std::pair<cmSourceFile const*, std::string>>{}; } return iter->second; } @@ -5017,7 +5193,7 @@ } pos = lib.find_last_not_of(" \t\r\n"); if (pos != std::string::npos) { - lib = lib.substr(0, pos + 1); + lib.resize(pos + 1); } if (lib != item) { cmake* cm = this->LocalGenerator->GetCMakeInstance(); @@ -5154,8 +5330,6 @@ namespace { bool CreateCxxStdlibTarget(cmMakefile* makefile, cmLocalGenerator* lg, std::string const& targetName, - std::string const& cxxTargetName, - std::string const& stdLevel, std::vector<std::string> const& configs) { #ifndef CMAKE_BOOTSTRAP @@ -5201,20 +5375,17 @@ metadata = std::move(*parseResult.Meta); } - auto const localTargetName = cmStrCat("__cmake_cxx_std_", stdLevel); - cmStandardLevelResolver standardResolver(makefile); auto* stdlibTgt = makefile->AddLibrary( - localTargetName, cmStateEnums::STATIC_LIBRARY, {}, true); + "@cmake_cxx_std", cmStateEnums::STATIC_LIBRARY, {}, true); cmCxxModuleMetadata::PopulateTarget(*stdlibTgt, *metadata, configs); - standardResolver.AddRequiredTargetFeature(stdlibTgt, - cmStrCat("cxx_std_", stdLevel)); + cmStandardLevelResolver standardResolver(makefile); + standardResolver.AddRequiredTargetFeature(stdlibTgt, "cxx_std_20"); auto gt = cm::make_unique<cmGeneratorTarget>(stdlibTgt, lg); for (auto const& config : configs) { gt->ComputeCompileFeatures(config); } lg->AddGeneratorTarget(std::move(gt)); - makefile->AddAlias(cxxTargetName, localTargetName); #endif // CMAKE_BOOTSTRAP @@ -5222,11 +5393,8 @@ } } // namespace -bool cmGeneratorTarget::ApplyCXXStdTargets() +bool cmGeneratorTarget::ApplyCXXStdTarget() { - cmStandardLevelResolver standardResolver(this->Makefile); - cmStandardLevel const cxxStd23 = - *standardResolver.LanguageStandardLevel("CXX", "23"); std::vector<std::string> const& configs = this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); auto std_prop = this->GetProperty("CXX_MODULE_STD"); @@ -5290,35 +5458,18 @@ return true; } - for (auto const& config : configs) { - if (this->HaveCxxModuleSupport(config) != Cxx20SupportLevel::Supported) { - continue; - } - - cm::optional<cmStandardLevel> explicitLevel = - this->GetExplicitStandardLevel("CXX", config); - if (!explicitLevel || *explicitLevel < cxxStd23) { - continue; - } - - auto const stdLevel = - standardResolver.GetLevelString("CXX", *explicitLevel); - auto const cxxTargetName = cmStrCat("__CMAKE::CXX", stdLevel); - - // Create the __CMAKE::CXX## target if it doesn't already exist - if (!this->Makefile->FindTargetToUse(cxxTargetName) && - !CreateCxxStdlibTarget(this->Makefile, this->LocalGenerator, - this->GetName(), cxxTargetName, stdLevel, - configs)) { - return false; - } - - this->Target->AppendProperty( - "LINK_LIBRARIES", - cmStrCat("$<BUILD_LOCAL_INTERFACE:$<$<CONFIG:", config, - ">:", cxxTargetName, ">>")); + // Create the single, unreferenceable import std target if it doesn't + // already exist. BMI compatibility handles per-consumer standard level + // differences by creating synthetic targets as needed. + if (!this->Makefile->FindTargetToUse("@cmake_cxx_std") && + !CreateCxxStdlibTarget(this->Makefile, this->LocalGenerator, + this->GetName(), configs)) { + return false; } + this->Target->AppendProperty("LINK_LIBRARIES", + "$<BUILD_LOCAL_INTERFACE:@cmake_cxx_std>"); + // Check the experimental feature here. A toolchain may have // skipped the check in the toolchain preparation logic. if (!cmExperimental::HasSupportEnabled( @@ -5334,118 +5485,173 @@ return true; } -bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, - std::string const& config) +cmCxxModuleUsageEffects const& cmGeneratorTarget::GetCxxModuleUsageEffects( + std::string const& config) const { + + auto iter = this->CxxModuleUsageEffects.find(config); + if (iter == this->CxxModuleUsageEffects.end()) { + auto result = this->CxxModuleUsageEffects.emplace( + std::pair<std::string, cmCxxModuleUsageEffects>{ + config, cmCxxModuleUsageEffects(this, config) }); + return result.first->second; + } + + return iter->second; +} + +cmGeneratorTarget const* cmGeneratorTarget::GetTargetForCxxModules( + std::string const& config, cmGeneratorTarget const& bmiConsumer) const +{ + auto const& consumingUsage = bmiConsumer.GetCxxModuleUsageEffects(config); + auto const& owningUsage = this->GetCxxModuleUsageEffects(config); + if (consumingUsage.GetHash() == owningUsage.GetHash()) { + if (this->IsImported()) { + return this->GetCxxSyntheticTarget(config, *this); + } + return this; + } + + return this->GetCxxSyntheticTarget(config, bmiConsumer); +} + +cmGeneratorTarget const* cmGeneratorTarget::GetCxxSyntheticTarget( + std::string const& config, cmGeneratorTarget const& bmiConsumer) const +{ + auto const& usageHash = + bmiConsumer.GetCxxModuleUsageEffects(config).GetHash(); + auto cached = this->SynthCxxTargets.find(usageHash); + if (cached != this->SynthCxxTargets.end()) { + return cached->second; + } + + auto targetNameBase = this->GetName(); + if (this->IsImported()) { + cmSystemTools::ReplaceString(targetNameBase, "::", "__"); + } + auto const targetName = + cmStrCat(targetNameBase, "@synth_", this->SynthCxxTargets.size()); + std::vector<std::string> allConfigs = this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + auto const* model = this->Target; + auto* mf = this->Makefile; + auto* lg = this->GetLocalGenerator(); + auto* tgt = + mf->AddSynthesizedTarget(cmStateEnums::INTERFACE_LIBRARY, targetName); + + // Copy relevant information from the existing target. + + // Copy policies to the target. + tgt->CopyPolicyStatuses(model); + + // Copy file sets. + { + for (auto const* gfs : + this->GetInterfaceFileSets(cm::FileSetMetadata::CXX_MODULES)) { + auto* newFs = + tgt + ->GetOrCreateFileSet(gfs->GetName(), gfs->GetType(), + cm::FileSetMetadata::Visibility::Public) + .first; + newFs->CopyEntries(gfs->GetFileSet()); + } + } + + // Copy properties which effect consumer compatibility. + // CopyUsageEffects now uses the consumer's full (own + transitive) + // compile features and options. + tgt->CopyUsageEffects(&bmiConsumer, config); + + auto const& preprocessorCompileOptions = + this->GetCxxModuleUsageEffects(config).GetPreprocessorCompileOptions(); + for (auto it = preprocessorCompileOptions.rbegin(); + it != preprocessorCompileOptions.rend(); ++it) { + tgt->InsertCompileOption(*it, true); + } + + // Copy properties which don't effect consumer compatibility + tgt->CopyCxxModulesEntries(model); + + // Copy other properties which may affect the C++ module BMI generation. + tgt->CopyCxxModulesProperties(model); + + tgt->AddLinkLibrary(*mf, cmStrCat("$<COMPILE_ONLY:", model->GetName(), '>'), + GENERAL_LibraryType); + + // Create the generator target and attach it to the local generator. + auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg); + auto* syntheticTarget = gtp.get(); + + // See `localGen->ComputeTargetCompileFeatures()` call in + // `cmGlobalGenerator::Compute` for where non-synthetic targets resolve + // this. + for (auto const& innerConfig : allConfigs) { + gtp->ComputeCompileFeatures(innerConfig); + } + // See `cmGlobalGenerator::ApplyCXXStdTarget` in + // `cmGlobalGenerator::Compute` for non-synthetic target resolutions. + if (!gtp->ApplyCXXStdTarget()) { + return nullptr; + } + + lg->AddGeneratorTarget(std::move(gtp)); + this->SynthCxxTargets[usageHash] = syntheticTarget; + for (auto const& innerConfig : allConfigs) { + if (!syntheticTarget->DiscoverSyntheticTargets(innerConfig, + &bmiConsumer)) { + return nullptr; + } + } + + return syntheticTarget; +} + +bool cmGeneratorTarget::DiscoverSyntheticTargets( + std::string const& config, cmGeneratorTarget const* bmiConsumer) +{ + auto& configInfo = this->Configs[config]; cmOptionalLinkImplementation impl; this->ComputeLinkImplementationLibraries(config, impl, UseTo::Link); - cmCxxModuleUsageEffects usage(this); + if (!bmiConsumer) { + bmiConsumer = this; + } - auto& SyntheticDeps = this->Configs[config].SyntheticDeps; + auto& SyntheticDeps = configInfo.SyntheticDeps; for (auto const& entry : impl.Libraries) { auto const* gt = entry.Target; - if (!gt || !gt->IsImported()) { + if (!gt || !gt->HaveInterfaceCxx20ModuleSources()) { continue; } - if (gt->HaveCxx20ModuleSources()) { - cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512); - constexpr size_t HASH_TRUNCATION = 12; - auto dirhash = hasher.HashString( - gt->GetLocalGenerator()->GetCurrentBinaryDirectory()); - std::string safeName = gt->GetName(); - cmSystemTools::ReplaceString(safeName, ":", "_"); - auto targetIdent = - hasher.HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash())); - std::string targetName = - cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION)); + // Visual Studio generators do not currently support BMI-only compilation, + // so they can't generate synthetic targets. For consuming native modules, + // skip so that the native target is used directly. For imported modules, + // create the synth target anyway and diagnose later, in the VS generator. + if (!gt->IsImported() && this->GlobalGenerator->IsVisualStudio()) { + continue; + } - // Check the cache to see if this instance of the imported target has - // already been created. - auto cached = cache.CxxModuleTargets.find(targetName); - cmGeneratorTarget const* synthDep = nullptr; - if (cached == cache.CxxModuleTargets.end()) { - auto const* model = gt->Target; - auto* mf = gt->Makefile; - auto* lg = gt->GetLocalGenerator(); - auto* tgt = mf->AddSynthesizedTarget(cmStateEnums::INTERFACE_LIBRARY, - targetName); - - // Copy relevant information from the existing IMPORTED target. - - // Copy policies to the target. - tgt->CopyPolicyStatuses(model); - - // Copy file sets. - { - auto fsNames = model->GetAllFileSetNames(); - for (auto const& fsName : fsNames) { - auto const* fs = model->GetFileSet(fsName); - if (!fs) { - mf->IssueMessage(MessageType::INTERNAL_ERROR, - cmStrCat("Failed to find file set named '", - fsName, "' on target '", - tgt->GetName(), '\'')); - continue; - } - auto* newFs = tgt - ->GetOrCreateFileSet(fs->GetName(), fs->GetType(), - fs->GetVisibility()) - .first; - newFs->CopyEntries(fs); - } - } - - // Copy imported C++ module properties. - tgt->CopyImportedCxxModulesEntries(model); - - // Copy other properties which may affect the C++ module BMI - // generation. - tgt->CopyImportedCxxModulesProperties(model); - - tgt->AddLinkLibrary(*mf, - cmStrCat("$<COMPILE_ONLY:", model->GetName(), '>'), - GENERAL_LibraryType); - - // Apply usage requirements to the target. - usage.ApplyToTarget(tgt); - - // Create the generator target and attach it to the local generator. - auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg); - - synthDep = gtp.get(); - cache.CxxModuleTargets[targetName] = synthDep; - - // See `localGen->ComputeTargetCompileFeatures()` call in - // `cmGlobalGenerator::Compute` for where non-synthetic targets resolve - // this. - for (auto const& innerConfig : allConfigs) { - gtp->ComputeCompileFeatures(innerConfig); - } - // See `cmGlobalGenerator::ApplyCXXStdTargets` in - // `cmGlobalGenerator::Compute` for non-synthetic target resolutions. - if (!gtp->ApplyCXXStdTargets()) { - return false; - } - - gtp->DiscoverSyntheticTargets(cache, config); - - lg->AddGeneratorTarget(std::move(gtp)); - } else { - synthDep = cached->second; - } - - SyntheticDeps[gt].push_back(synthDep); + auto const* dep = gt->GetTargetForCxxModules(config, *bmiConsumer); + if (!dep) { + return false; + } + if (dep->IsSynthetic()) { + SyntheticDeps[gt].insert(dep); } } return true; } +cmGeneratorTarget::SyntheticDepsMap const& cmGeneratorTarget::GetSyntheticDeps( + std::string const& config) const +{ + return this->Configs[config].SyntheticDeps; +} + bool cmGeneratorTarget::HasPackageReferences() const { return this->IsInBuildSystem() && @@ -5746,324 +5952,6 @@ return this->IsCSharpOnly() ? ManagedType::Managed : ManagedType::Native; } -bool cmGeneratorTarget::AddHeaderSetVerification() -{ - for (bool const isInterface : { false, true }) { - if (!this->GetPropertyAsBool(isInterface ? "VERIFY_INTERFACE_HEADER_SETS" - : "VERIFY_PRIVATE_HEADER_SETS")) { - continue; - } - - if (this->GetType() != cmStateEnums::STATIC_LIBRARY && - this->GetType() != cmStateEnums::SHARED_LIBRARY && - (this->GetType() != cmStateEnums::MODULE_LIBRARY || isInterface) && - this->GetType() != cmStateEnums::UNKNOWN_LIBRARY && - this->GetType() != cmStateEnums::OBJECT_LIBRARY && - this->GetType() != cmStateEnums::INTERFACE_LIBRARY && - this->GetType() != cmStateEnums::EXECUTABLE) { - continue; - } - - char const* headerSetsProperty = isInterface - ? "INTERFACE_HEADER_SETS_TO_VERIFY" - : "PRIVATE_HEADER_SETS_TO_VERIFY"; - - auto verifyValue = this->GetProperty(headerSetsProperty); - bool const all = verifyValue.IsEmpty(); - std::set<std::string> verifySet; - if (!all) { - cmList verifyList{ verifyValue }; - verifySet.insert(verifyList.begin(), verifyList.end()); - } - - cmTarget* verifyTarget = nullptr; - std::string const verifyTargetName = - cmStrCat(this->GetName(), - isInterface ? "_verify_interface_header_sets" - : "_verify_private_header_sets"); - - char const* allVerifyTargetName = isInterface - ? "all_verify_interface_header_sets" - : "all_verify_private_header_sets"; - cmTarget* allVerifyTarget = - this->GlobalGenerator->GetMakefiles().front()->FindTargetToUse( - allVerifyTargetName, { cmStateEnums::TargetDomain::NATIVE }); - - auto fileSetEntries = isInterface - ? this->Target->GetInterfaceHeaderSetsEntries() - : this->Target->GetHeaderSetsEntries(); - - std::set<cmFileSet*> fileSets; - for (auto const& entry : fileSetEntries) { - for (auto const& name : cmList{ entry.Value }) { - if (all || verifySet.count(name)) { - fileSets.insert(this->Target->GetFileSet(name)); - verifySet.erase(name); - } - } - } - - if (isInterface) { - cmPolicies::PolicyStatus const cmp0209 = this->GetPolicyStatusCMP0209(); - if (cmp0209 != cmPolicies::NEW && - this->GetType() == cmStateEnums::EXECUTABLE && - !this->GetPropertyAsBool("ENABLE_EXPORTS")) { - if (cmp0209 == cmPolicies::WARN && !fileSets.empty()) { - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0209), - "\n" - "Executable target \"", - this->GetName(), - "\" has interface header file sets, but it does not " - "enable exports. Those headers would be verified under " - "CMP0209 NEW behavior.\n")); - } - continue; - } - } - - if (!verifySet.empty()) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Property ", headerSetsProperty, " of target \"", - this->GetName(), - "\" contained the following header sets that are nonexistent " - "or not ", - isInterface ? "INTERFACE" : "PRIVATE", ":\n ", - cmJoin(verifySet, "\n "))); - return false; - } - - cm::optional<std::set<std::string>> languages; - for (auto* fileSet : fileSets) { - auto dirCges = fileSet->CompileDirectoryEntries(); - auto fileCges = fileSet->CompileFileEntries(); - - static auto const contextSensitive = - [](std::unique_ptr<cmCompiledGeneratorExpression> const& cge) { - return cge->GetHadContextSensitiveCondition(); - }; - bool dirCgesContextSensitive = false; - bool fileCgesContextSensitive = false; - - std::vector<std::string> dirs; - std::map<std::string, std::vector<std::string>> filesPerDir; - bool first = true; - for (auto const& config : this->Makefile->GetGeneratorConfigs( - cmMakefile::GeneratorConfigQuery::IncludeEmptyConfig)) { - cm::GenEx::Context context(this->LocalGenerator, config); - if (first || dirCgesContextSensitive) { - dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, this); - dirCgesContextSensitive = - std::any_of(dirCges.begin(), dirCges.end(), contextSensitive); - } - if (first || fileCgesContextSensitive) { - filesPerDir.clear(); - for (auto const& fileCge : fileCges) { - fileSet->EvaluateFileEntry(dirs, filesPerDir, fileCge, context, - this); - if (fileCge->GetHadContextSensitiveCondition()) { - fileCgesContextSensitive = true; - } - } - } - - for (auto const& files : filesPerDir) { - for (auto const& file : files.second) { - std::string filename = this->GenerateHeaderSetVerificationFile( - *this->Makefile->GetOrCreateSource(file), files.first, - verifyTargetName, languages); - if (filename.empty()) { - continue; - } - - if (!verifyTarget) { - { - cmMakefile::PolicyPushPop polScope(this->Makefile); - this->Makefile->SetPolicy(cmPolicies::CMP0119, - cmPolicies::NEW); - verifyTarget = this->Makefile->AddLibrary( - verifyTargetName, cmStateEnums::OBJECT_LIBRARY, {}, true); - } - - if (isInterface) { - // Link to the original target so that we pick up its - // interface compile options just like a consumer would. - // This also ensures any generated headers in the original - // target will be created. - verifyTarget->AddLinkLibrary( - *this->Makefile, this->GetName(), - cmTargetLinkLibraryType::GENERAL_LibraryType); - } else { - // For private file sets, we need to simulate compiling the - // same way as the original target. That includes linking to - // the same things so we pick up the same transitive - // properties. For the <LANG>_... properties, we don't care if - // we set them for languages this target won't eventually use. - // The verify header sets feature currently only supports the - // C and C++ languages, so we just always set those here for - // simplicity rather than working out all languages the target - // has to compile for. - static std::vector<std::string> propertiesToCopy = { - "COMPILE_DEFINITIONS", "COMPILE_FEATURES", - "COMPILE_FLAGS", "COMPILE_OPTIONS", - "DEFINE_SYMBOL", "INCLUDE_DIRECTORIES", - "LINK_LIBRARIES", "C_STANDARD", - "C_STANDARD_REQUIRED", "C_EXTENSIONS", - "CXX_STANDARD", "CXX_STANDARD_REQUIRED", - "CXX_EXTENSIONS" - }; - for (std::string const& prop : propertiesToCopy) { - cmValue propValue = this->Target->GetProperty(prop); - if (propValue.IsSet()) { - verifyTarget->SetProperty(prop, propValue); - } - } - // The original target might have generated headers. Since - // we only link to the original target for compilation, - // there's nothing to force such generation to happen yet. - // Our verify target must depend on the original target to - // ensure such generated files will be created. - verifyTarget->AddUtility(this->GetName(), false, - this->Makefile); - verifyTarget->AddCodegenDependency(this->GetName()); - } - - verifyTarget->SetProperty("AUTOMOC", "OFF"); - verifyTarget->SetProperty("AUTORCC", "OFF"); - verifyTarget->SetProperty("AUTOUIC", "OFF"); - verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON"); - verifyTarget->SetProperty("UNITY_BUILD", "OFF"); - verifyTarget->SetProperty("CXX_SCAN_FOR_MODULES", "OFF"); - - if (isInterface) { - verifyTarget->FinalizeTargetConfiguration( - this->Makefile->GetCompileDefinitionsEntries()); - } else { - // Private verification only needs to add the directory scope - // definitions here - for (auto const& def : - this->Makefile->GetCompileDefinitionsEntries()) { - verifyTarget->InsertCompileDefinition(def); - } - } - - if (!allVerifyTarget) { - allVerifyTarget = - this->GlobalGenerator->GetMakefiles() - .front() - ->AddNewUtilityTarget(allVerifyTargetName, true); - } - - allVerifyTarget->AddUtility(verifyTargetName, false); - } - - if (fileCgesContextSensitive) { - filename = cmStrCat("$<$<CONFIG:", config, ">:", filename, '>'); - } - verifyTarget->AddSource(filename); - } - } - - if (!dirCgesContextSensitive && !fileCgesContextSensitive) { - break; - } - first = false; - } - } - - if (verifyTarget) { - this->LocalGenerator->AddGeneratorTarget( - cm::make_unique<cmGeneratorTarget>(verifyTarget, - this->LocalGenerator)); - } - } - - return true; -} - -std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile( - cmSourceFile& source, std::string const& dir, - std::string const& verifyTargetName, - cm::optional<std::set<std::string>>& languages) const -{ - std::string extension; - std::string language = source.GetOrDetermineLanguage(); - - if (source.GetPropertyAsBool("SKIP_LINTING")) { - return std::string{}; - } - - if (language.empty()) { - if (!languages) { - languages.emplace(); - for (auto const& tgtSource : this->GetAllConfigSources()) { - auto const& tgtSourceLanguage = - tgtSource.Source->GetOrDetermineLanguage(); - if (tgtSourceLanguage == "CXX") { - languages->insert("CXX"); - break; // C++ overrides everything else, so we don't need to keep - // checking. - } - if (tgtSourceLanguage == "C") { - languages->insert("C"); - } - } - - if (languages->empty()) { - std::vector<std::string> languagesVector; - this->GlobalGenerator->GetEnabledLanguages(languagesVector); - languages->insert(languagesVector.begin(), languagesVector.end()); - } - } - - if (languages->count("CXX")) { - language = "CXX"; - } else if (languages->count("C")) { - language = "C"; - } - } - - if (language == "C") { - extension = ".c"; - } else if (language == "CXX") { - extension = ".cxx"; - } else { - return ""; - } - - std::string headerFilename = dir; - if (!headerFilename.empty()) { - headerFilename += '/'; - } - headerFilename += source.GetLocation().GetName(); - - auto filename = - cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', - verifyTargetName, '/', headerFilename, extension); - auto* verificationSource = this->Makefile->GetOrCreateSource(filename); - source.SetSpecialSourceType( - cmSourceFile::SpecialSourceType::HeaderSetVerificationSource); - verificationSource->SetProperty("LANGUAGE", language); - - cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(filename)); - - cmGeneratedFileStream fout(filename); - fout.SetCopyIfDifferent(true); - // The IWYU "associated" pragma tells include-what-you-use to - // consider the headerFile as part of the entire language - // unit within include-what-you-use and as a result allows - // one to get IWYU advice for headers. - // Also suppress clang-tidy include checks in generated code. - fout - << "/* NOLINTNEXTLINE(misc-header-include-cycle,misc-include-cleaner) */\n" - << "#include <" << headerFilename << "> /* IWYU pragma: associated */\n"; - fout.close(); - - return filename; -} - std::string cmGeneratorTarget::GetImportedXcFrameworkPath( std::string const& config) const { @@ -6151,28 +6039,14 @@ return have_direct || have_via_target_objects; } -bool cmGeneratorTarget::HaveCxx20ModuleSources(std::string* errorMessage) const +bool cmGeneratorTarget::HaveInterfaceCxx20ModuleSources() const { - auto const& fs_names = this->Target->GetAllFileSetNames(); - return std::any_of( - fs_names.begin(), fs_names.end(), - [this, errorMessage](std::string const& name) -> bool { - auto const* file_set = this->Target->GetFileSet(name); - if (!file_set) { - auto message = cmStrCat("Target \"", this->Target->GetName(), - "\" is tracked to have file set \"", name, - "\", but it was not found."); - if (errorMessage) { - *errorMessage = std::move(message); - } else { - this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, message); - } - return false; - } - - auto const& fs_type = file_set->GetType(); - return fs_type == "CXX_MODULES"_s; - }); + return !this->GetInterfaceFileSets(cm::FileSetMetadata::CXX_MODULES).empty(); +} +bool cmGeneratorTarget::HaveCxx20ModuleSources() const +{ + return !this->GetFileSets(cm::FileSetMetadata::CXX_MODULES).empty() || + !this->GetInterfaceFileSets(cm::FileSetMetadata::CXX_MODULES).empty(); } cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport( @@ -6314,21 +6188,6 @@ return lang == "Fortran"_s || this->NeedCxxModuleSupport(lang, config); } -cmFileSet const* cmGeneratorTarget::GetFileSetForSource( - std::string const& config, cmSourceFile const* sf) const -{ - this->BuildFileSetInfoCache(config); - - auto const& path = sf->GetFullPath(); - auto const& per_config = this->Configs[config]; - - auto const fsit = per_config.FileSetCache.find(path); - if (fsit == per_config.FileSetCache.end()) { - return nullptr; - } - return fsit->second; -} - bool cmGeneratorTarget::NeedDyndepForSource(std::string const& lang, std::string const& config, cmSourceFile const* sf) const @@ -6345,7 +6204,7 @@ // Any file in `CXX_MODULES` file sets need scanned (it being `CXX` is // enforced elsewhere). auto const* fs = this->GetFileSetForSource(config, sf); - if (fs && fs->GetType() == "CXX_MODULES"_s) { + if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) { return true; } @@ -6353,6 +6212,12 @@ if (targetDyndep == CxxModuleSupport::Unavailable) { return false; } + if (fs) { + auto const fsProp = fs->GetProperty("CXX_SCAN_FOR_MODULES"); + if (fsProp.IsSet()) { + return fsProp.IsOn(); + } + } auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES"); if (sfProp.IsSet()) { return sfProp.IsOn(); @@ -6403,6 +6268,40 @@ return policyAnswer; } +bool cmGeneratorTarget::HasFileSets() const +{ + return !this->FileSets->Empty(); +} + +std::vector<cmGeneratorFileSet const*> const& +cmGeneratorTarget::GetAllFileSets() const +{ + return this->FileSets->GetAllFileSets(); +} + +std::vector<cmGeneratorFileSet const*> const& cmGeneratorTarget::GetFileSets( + cm::string_view type) const +{ + return this->FileSets->GetFileSets(type); +} +std::vector<cmGeneratorFileSet const*> const& +cmGeneratorTarget::GetInterfaceFileSets(cm::string_view type) const +{ + return this->FileSets->GetInterfaceFileSets(type); +} + +cmGeneratorFileSet const* cmGeneratorTarget::GetFileSet( + std::string const& name) const +{ + return this->FileSets->GetFileSet(name); +} + +cmGeneratorFileSet const* cmGeneratorTarget::GetFileSetForSource( + std::string const& config, cmSourceFile const* sf) const +{ + return this->FileSets->GetFileSetForSource(config, sf); +} + std::string cmGeneratorTarget::BuildDatabasePath( std::string const& lang, std::string const& config) const { @@ -6428,53 +6327,26 @@ "_build_database.json"); } -void cmGeneratorTarget::BuildFileSetInfoCache(std::string const& config) const +std::string cmGeneratorTarget::GetSwiftPackageName() const { - auto& per_config = this->Configs[config]; - - if (per_config.BuiltFileSetCache) { - return; + std::string packageName; + if (cmValue projectName = this->GetProperty("Swift_PACKAGE_NAME")) { + packageName = *projectName; + } else if (this->GetPolicyStatusCMP0216() == cmPolicies::NEW) { + packageName = this->Makefile->GetSafeDefinition("PROJECT_NAME"); } - - auto const* tgt = this->Target; - - for (auto const& name : tgt->GetAllFileSetNames()) { - auto const* file_set = tgt->GetFileSet(name); - if (!file_set) { - tgt->GetMakefile()->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), - "\" is tracked to have file set \"", name, - "\", but it was not found.")); - continue; - } - - cm::GenEx::Context context(this->LocalGenerator, config); - - auto fileEntries = file_set->CompileFileEntries(); - auto directoryEntries = file_set->CompileDirectoryEntries(); - auto directories = - file_set->EvaluateDirectoryEntries(directoryEntries, context, this); - - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - file_set->EvaluateFileEntry(directories, files, entry, context, this); - } - - for (auto const& it : files) { - for (auto const& filename : it.second) { - auto collapsedFile = cmSystemTools::CollapseFullPath(filename); - per_config.FileSetCache[collapsedFile] = file_set; - } - } - } - - per_config.BuiltFileSetCache = true; + return packageName; } std::string cmGeneratorTarget::GetSwiftModuleName() const { - return this->GetPropertyOrDefault("Swift_MODULE_NAME", this->GetName()); + if (cmValue name = this->GetProperty("Swift_MODULE_NAME")) { + return *name; + } + // Hyphens are not valid in Swift module identifiers. + std::string name = this->GetName(); + std::replace(name.begin(), name.end(), '-', '_'); + return name; } std::string cmGeneratorTarget::GetSwiftModuleFileName() const
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index a32ecba..4503247 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h
@@ -19,6 +19,7 @@ #include <cm/string_view> #include "cmAlgorithms.h" +#include "cmCxxModuleUsageEffects.h" #include "cmLinkItem.h" #include "cmList.h" #include "cmListFileCache.h" @@ -27,6 +28,7 @@ #include "cmSourceFile.h" #include "cmStandardLevel.h" #include "cmStateTypes.h" +#include "cmTargetPropertyEntry.h" #include "cmValue.h" namespace cm { @@ -36,16 +38,14 @@ } } -class cmake; enum class cmBuildStep; -class cmCompiledGeneratorExpression; class cmComputeLinkInformation; class cmCustomCommand; -class cmFileSet; +class cmGeneratorFileSets; +class cmGeneratorFileSet; class cmGlobalGenerator; class cmLocalGenerator; class cmMakefile; -struct cmSyntheticTargetCache; class cmTarget; struct cmGeneratorExpressionDAGChecker; @@ -53,6 +53,8 @@ class cmGeneratorTarget { public: + using TargetPropertyEntry = cm::TargetPropertyEntry; + cmGeneratorTarget(cmTarget*, cmLocalGenerator* lg); ~cmGeneratorTarget(); @@ -143,6 +145,7 @@ SourceKindCustomCommand, SourceKindExternalObject, SourceKindCxxModuleSource, + SourceKindRustMainCrateRoot, SourceKindExtra, SourceKindHeader, SourceKindIDL, @@ -222,6 +225,11 @@ void GetManifests(std::vector<cmSourceFile const*>&, std::string const& config) const; + void GetRustMainCrateRoot(std::vector<cmSourceFile const*>&, + std::string const& config) const; + + cmSourceFile const* GetRustMainCrateRoot(std::string const& config) const; + std::set<cmLinkItem> const& GetUtilityItems() const; void ComputeObjectMapping(); @@ -311,11 +319,18 @@ its object file directory for the build. */ void GetTargetObjectNames(std::string const& config, std::vector<std::string>& objects) const; + void GetTargetObjectNames(std::string const& config, + std::function<bool(cmSourceFile const&)> filter, + std::vector<std::string>& objects) const; /** Get the build and install locations of objects for a given context. */ void GetTargetObjectLocations( std::string const& config, std::function<void(cmObjectLocation const&, cmObjectLocation const&)> cb) const; + void GetTargetObjectLocations( + std::string const& config, std::function<bool(cmSourceFile const&)> filter, + std::function<void(cmObjectLocation const&, cmObjectLocation const&)> cb) + const; /** What hierarchy level should the reported directory contain */ enum BundleDirectoryLevel @@ -613,6 +628,10 @@ void AddISPCTargetFlags(std::string& flags) const; + void AddRustTargetFlags(std::string& flags) const; + + void AddSwiftTargetFlags(std::string& flags) const; + std::string GetFeatureSpecificLinkRuleVariable( std::string const& var, std::string const& lang, std::string const& config) const; @@ -623,6 +642,9 @@ std::string GetClangTidyExportFixesDirectory(std::string const& lang) const; + /** Return the Swift package name for this target. */ + std::string GetSwiftPackageName() const; + /** Return the swift module name for this target. */ std::string GetSwiftModuleName() const; @@ -1014,8 +1036,6 @@ std::string const& report, std::string const& compatibilityType) const; - class TargetPropertyEntry; - std::string EvaluateInterfaceProperty( std::string const& prop, cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const; @@ -1102,24 +1122,41 @@ std::vector<std::string> GetGeneratedISPCHeaders( std::string const& config) const; - void AddISPCGeneratedObject(std::vector<std::string>&& objs, - std::string const& config); - std::vector<std::string> GetGeneratedISPCObjects( - std::string const& config) const; + void AddISPCGeneratedObject( + std::vector<std::pair<cmSourceFile const*, std::string>>&& objs, + std::string const& config); + std::vector<std::pair<cmSourceFile const*, std::string>> + GetGeneratedISPCObjects(std::string const& config) const; void AddSystemIncludeDirectory(std::string const& inc, std::string const& lang); bool AddHeaderSetVerification(); - std::string GenerateHeaderSetVerificationFile( + cm::optional<std::string> GenerateHeaderSetVerificationFile( cmSourceFile& source, std::string const& dir, std::string const& verifyTargetName, - cm::optional<std::set<std::string>>& languages) const; + cm::optional<cm::optional<std::string>>& defaultLanguage) const; + + cm::optional<std::string> ResolveHeaderLanguage( + cmSourceFile& source, + cm::optional<cm::optional<std::string>>& defaultLanguage) const; + + cm::optional<std::string> GenerateStubForLanguage( + std::string const& language, std::string const& headerFilename, + std::string const& verifyTargetName, cmSourceFile& source) const; std::string GetImportedXcFrameworkPath(std::string const& config) const; - bool ApplyCXXStdTargets(); - bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, - std::string const& config); + bool ApplyCXXStdTarget(); + cmCxxModuleUsageEffects const& GetCxxModuleUsageEffects( + std::string const& config) const; + cmGeneratorTarget const* GetTargetForCxxModules( + std::string const& config, cmGeneratorTarget const& bmiConsumer) const; + bool DiscoverSyntheticTargets( + std::string const& config, cmGeneratorTarget const* bmiConsumer = nullptr); + + using SyntheticDepsMap = + std::map<cmGeneratorTarget const*, std::set<cmGeneratorTarget const*>>; + SyntheticDepsMap const& GetSyntheticDeps(std::string const& config) const; class CustomTransitiveProperty : public TransitiveProperty { @@ -1148,6 +1185,9 @@ private: void AddSourceCommon(std::string const& src, bool before = false); + cmGeneratorTarget const* GetCxxSyntheticTarget( + std::string const& config, cmGeneratorTarget const& bmiConsumer) const; + std::string CreateFortranModuleDirectory( std::string const& working_dir) const; mutable bool FortranModuleDirectoryCreated = false; @@ -1357,7 +1397,8 @@ std::unordered_map<std::string, std::vector<std::string>> ISPCGeneratedHeaders; - std::unordered_map<std::string, std::vector<std::string>> + std::unordered_map<std::string, + std::vector<std::pair<cmSourceFile const*, std::string>>> ISPCGeneratedObjects; enum class LinkInterfaceField @@ -1494,6 +1535,22 @@ bool HaveFortranSources() const; bool HaveFortranSources(std::string const& config) const; + // File sets support queries + + bool HasFileSets() const; + cmGeneratorFileSets const* GetGeneratorFileSets() const + { + return this->FileSets.get(); + } + std::vector<cmGeneratorFileSet const*> const& GetAllFileSets() const; + std::vector<cmGeneratorFileSet const*> const& GetFileSets( + cm::string_view type) const; + std::vector<cmGeneratorFileSet const*> const& GetInterfaceFileSets( + cm::string_view type) const; + cmGeneratorFileSet const* GetFileSet(std::string const& name) const; + cmGeneratorFileSet const* GetFileSetForSource(std::string const& config, + cmSourceFile const* sf) const; + // C++20 module support queries. /** @@ -1501,11 +1558,9 @@ * * This will inspect the target itself to see if C++20 module * support is expected to work based on its sources. - * - * If `errorMessage` is given a non-`nullptr`, any error message will be - * stored in it, otherwise the error will be reported directly. */ - bool HaveCxx20ModuleSources(std::string* errorMessage = nullptr) const; + bool HaveInterfaceCxx20ModuleSources() const; + bool HaveCxx20ModuleSources() const; enum class Cxx20SupportLevel { @@ -1518,6 +1573,7 @@ // C++20 modules are available and working. Supported, }; + /** * Query whether the target has C++20 module support available (regardless of * whether it is required or not). @@ -1530,8 +1586,6 @@ bool NeedCxxModuleSupport(std::string const& lang, std::string const& config) const; bool NeedDyndep(std::string const& lang, std::string const& config) const; - cmFileSet const* GetFileSetForSource(std::string const& config, - cmSourceFile const* sf) const; bool NeedDyndepForSource(std::string const& lang, std::string const& config, cmSourceFile const* sf) const; enum class CxxModuleSupport @@ -1557,45 +1611,17 @@ static MsvcCharSet GetMsvcCharSet(std::string const& singleDefine); private: - void BuildFileSetInfoCache(std::string const& config) const; struct InfoByConfig { - bool BuiltFileSetCache = false; - std::map<std::string, cmFileSet const*> FileSetCache; - std::map<cmGeneratorTarget const*, std::vector<cmGeneratorTarget const*>> + std::map<cmGeneratorTarget const*, std::set<cmGeneratorTarget const*>> SyntheticDeps; std::map<cmSourceFile const*, ClassifiedFlags> SourceFlags; }; + mutable std::map<std::string, cmGeneratorTarget*> SynthCxxTargets; + mutable std::map<std::string, cmCxxModuleUsageEffects> CxxModuleUsageEffects; mutable std::map<std::string, InfoByConfig> Configs; + std::unique_ptr<cmGeneratorFileSets> FileSets; bool PchReused = false; mutable bool ComputingPchReuse = false; mutable bool PchReuseCycleDetected = false; }; - -class cmGeneratorTarget::TargetPropertyEntry -{ -protected: - static cmLinkItem NoLinkItem; - -public: - TargetPropertyEntry(cmLinkItem const& item); - virtual ~TargetPropertyEntry() = default; - - static std::unique_ptr<TargetPropertyEntry> Create( - cmake& cmakeInstance, const BT<std::string>& propertyValue, - bool evaluateForBuildsystem = false); - static std::unique_ptr<TargetPropertyEntry> CreateFileSet( - std::vector<std::string> dirs, bool contextSensitiveDirs, - std::unique_ptr<cmCompiledGeneratorExpression> entryCge, - cmFileSet const* fileSet, cmLinkItem const& item = NoLinkItem); - - virtual std::string const& Evaluate( - cm::GenEx::Context const& context, cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker) const = 0; - - virtual cmListFileBacktrace GetBacktrace() const = 0; - virtual std::string const& GetInput() const = 0; - virtual bool GetHadContextSensitiveCondition() const; - - cmLinkItem const& LinkItem; -};
diff --git a/Source/cmGeneratorTarget_HeaderSetVerification.cxx b/Source/cmGeneratorTarget_HeaderSetVerification.cxx new file mode 100644 index 0000000..18ba58e --- /dev/null +++ b/Source/cmGeneratorTarget_HeaderSetVerification.cxx
@@ -0,0 +1,427 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <algorithm> +#include <array> +#include <initializer_list> +#include <map> +#include <ostream> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmFileSetMetadata.h" +#include "cmGenExContext.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" +#include "cmGlobalGenerator.h" +#include "cmList.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmSourceFile.h" +#include "cmSourceFileLocation.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetLinkLibraryType.h" +#include "cmValue.h" + +bool cmGeneratorTarget::AddHeaderSetVerification() +{ + for (bool const isInterface : { false, true }) { + if (!this->GetPropertyAsBool(isInterface ? "VERIFY_INTERFACE_HEADER_SETS" + : "VERIFY_PRIVATE_HEADER_SETS")) { + continue; + } + + if (this->GetType() != cmStateEnums::STATIC_LIBRARY && + this->GetType() != cmStateEnums::SHARED_LIBRARY && + (this->GetType() != cmStateEnums::MODULE_LIBRARY || isInterface) && + this->GetType() != cmStateEnums::UNKNOWN_LIBRARY && + this->GetType() != cmStateEnums::OBJECT_LIBRARY && + this->GetType() != cmStateEnums::INTERFACE_LIBRARY && + this->GetType() != cmStateEnums::EXECUTABLE) { + continue; + } + + char const* headerSetsProperty = isInterface + ? "INTERFACE_HEADER_SETS_TO_VERIFY" + : "PRIVATE_HEADER_SETS_TO_VERIFY"; + + auto verifyValue = this->GetProperty(headerSetsProperty); + bool const all = verifyValue.IsEmpty(); + std::set<std::string> verifySet; + if (!all) { + cmList verifyList{ verifyValue }; + verifySet.insert(verifyList.begin(), verifyList.end()); + } + + cmTarget* verifyTarget = nullptr; + std::string const verifyTargetName = + cmStrCat(this->GetName(), + isInterface ? "_verify_interface_header_sets" + : "_verify_private_header_sets"); + + char const* allVerifyTargetName = isInterface + ? "all_verify_interface_header_sets" + : "all_verify_private_header_sets"; + cmTarget* allVerifyTarget = + this->GlobalGenerator->GetMakefiles().front()->FindTargetToUse( + allVerifyTargetName, { cmStateEnums::TargetDomain::NATIVE }); + + auto fileSetEntries = isInterface + ? this->GetInterfaceFileSets(cm::FileSetMetadata::HEADERS) + : this->GetFileSets(cm::FileSetMetadata::HEADERS); + + std::set<cmGeneratorFileSet const*> fileSets; + for (auto const& fileSet : fileSetEntries) { + if (all || verifySet.count(fileSet->GetName())) { + verifySet.erase(fileSet->GetName()); + if (fileSet->GetProperty("SKIP_LINTING").IsOn()) { + continue; + } + fileSets.insert(fileSet); + } + } + + if (isInterface) { + cmPolicies::PolicyStatus const cmp0209 = this->GetPolicyStatusCMP0209(); + if (cmp0209 != cmPolicies::NEW && + this->GetType() == cmStateEnums::EXECUTABLE && + !this->GetPropertyAsBool("ENABLE_EXPORTS")) { + if (cmp0209 == cmPolicies::WARN && !fileSets.empty()) { + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0209, {}, + cmStrCat("Executable target \""_s, this->GetName(), + "\" has interface header file sets, but it does not " + "enable exports. Those headers would be verified under " + "CMP0209 NEW behavior.\n"_s)); + } + continue; + } + } + + if (!verifySet.empty()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Property ", headerSetsProperty, " of target \"", + this->GetName(), + "\" contained the following header sets that are nonexistent " + "or not ", + isInterface ? "INTERFACE" : "PRIVATE", ":\n ", + cmJoin(verifySet, "\n "))); + return false; + } + + cm::optional<cm::optional<std::string>> defaultLanguage; + + // First, collect all verification stubs before creating the target, + // so we know whether to create an OBJECT library or not. + std::vector<std::string> stubSources; + for (auto const* fileSet : fileSets) { + auto const& dirCges = fileSet->CompileDirectoryEntries(); + auto const& fileCges = fileSet->CompileFileEntries(); + + static auto const contextSensitive = + [](std::unique_ptr<cmCompiledGeneratorExpression> const& cge) { + return cge->GetHadContextSensitiveCondition(); + }; + bool dirCgesContextSensitive = false; + bool fileCgesContextSensitive = false; + + std::vector<std::string> dirs; + std::map<std::string, std::vector<std::string>> filesPerDir; + bool first = true; + for (auto const& config : this->Makefile->GetGeneratorConfigs( + cmMakefile::GeneratorConfigQuery::IncludeEmptyConfig)) { + cm::GenEx::Context context(this->LocalGenerator, config); + if (first || dirCgesContextSensitive) { + dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, this); + dirCgesContextSensitive = + std::any_of(dirCges.begin(), dirCges.end(), contextSensitive); + } + if (first || fileCgesContextSensitive) { + filesPerDir.clear(); + for (auto const& fileCge : fileCges) { + fileSet->EvaluateFileEntry(dirs, filesPerDir, fileCge, context, + this); + if (fileCge->GetHadContextSensitiveCondition()) { + fileCgesContextSensitive = true; + } + } + } + + for (auto const& files : filesPerDir) { + for (auto const& file : files.second) { + cm::optional<std::string> filenameOpt = + this->GenerateHeaderSetVerificationFile( + *this->Makefile->GetOrCreateSource(file), files.first, + verifyTargetName, defaultLanguage); + if (!filenameOpt) { + continue; + } + std::string filename = *filenameOpt; + + if (fileCgesContextSensitive) { + filename = cmStrCat("$<$<CONFIG:", config, ">:", filename, '>'); + } + stubSources.emplace_back(std::move(filename)); + } + } + + if (!dirCgesContextSensitive && !fileCgesContextSensitive) { + break; + } + first = false; + } + } + + if (stubSources.empty()) { + // No headers to verify. Create a utility target so the target + // name always exists (e.g. for build system dependencies) without + // needing a placeholder source. This avoids warnings from tools + // like Xcode's libtool about empty static libraries. + verifyTarget = + this->Makefile->AddNewUtilityTarget(verifyTargetName, true); + } else { + // Create an OBJECT library to compile the verification stubs. + { + cmMakefile::PolicyPushPop polScope(this->Makefile); + this->Makefile->SetPolicy(cmPolicies::CMP0119, cmPolicies::NEW); + verifyTarget = this->Makefile->AddLibrary( + verifyTargetName, cmStateEnums::OBJECT_LIBRARY, {}, true); + } + + if (isInterface) { + // Link to the original target so that we pick up its + // interface compile options just like a consumer would. + // This also ensures any generated headers in the original + // target will be created. + verifyTarget->AddLinkLibrary( + *this->Makefile, this->GetName(), + cmTargetLinkLibraryType::GENERAL_LibraryType); + } else { + // For private file sets, we need to simulate compiling the + // same way as the original target. That includes linking to + // the same things so we pick up the same transitive + // properties. For the <LANG>_... properties, we don't care if + // we set them for languages this target won't eventually use. + // Copy language-standard properties for all supported + // languages. We don't care if we set properties for languages + // this target won't eventually use. + static std::array<std::string, 19> const propertiesToCopy{ { + "COMPILE_DEFINITIONS", "COMPILE_FEATURES", + "COMPILE_FLAGS", "COMPILE_OPTIONS", + "DEFINE_SYMBOL", "INCLUDE_DIRECTORIES", + "LINK_LIBRARIES", "C_STANDARD", + "C_STANDARD_REQUIRED", "C_EXTENSIONS", + "CXX_STANDARD", "CXX_STANDARD_REQUIRED", + "CXX_EXTENSIONS", "OBJC_STANDARD", + "OBJC_STANDARD_REQUIRED", "OBJC_EXTENSIONS", + "OBJCXX_STANDARD", "OBJCXX_STANDARD_REQUIRED", + "OBJCXX_EXTENSIONS", + } }; + for (std::string const& prop : propertiesToCopy) { + cmValue propValue = this->Target->GetProperty(prop); + if (propValue.IsSet()) { + verifyTarget->SetProperty(prop, propValue); + } + } + // The original target might have generated headers. Since + // we only link to the original target for compilation, + // there's nothing to force such generation to happen yet. + // Our verify target must depend on the original target to + // ensure such generated files will be created. + verifyTarget->AddUtility(this->GetName(), false, this->Makefile); + verifyTarget->AddCodegenDependency(this->GetName()); + } + + verifyTarget->SetProperty("AUTOMOC", "OFF"); + verifyTarget->SetProperty("AUTORCC", "OFF"); + verifyTarget->SetProperty("AUTOUIC", "OFF"); + verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON"); + verifyTarget->SetProperty("UNITY_BUILD", "OFF"); + verifyTarget->SetProperty("CXX_SCAN_FOR_MODULES", "OFF"); + + if (isInterface) { + verifyTarget->FinalizeTargetConfiguration( + this->Makefile->GetCompileDefinitionsEntries()); + } else { + // Private verification only needs to add the directory scope + // definitions here + for (auto const& def : + this->Makefile->GetCompileDefinitionsEntries()) { + verifyTarget->InsertCompileDefinition(def); + } + } + + for (auto const& source : stubSources) { + verifyTarget->AddSource(source); + } + } + if (!allVerifyTarget) { + allVerifyTarget = + this->GlobalGenerator->GetMakefiles().front()->AddNewUtilityTarget( + allVerifyTargetName, true); + } + allVerifyTarget->AddUtility(verifyTargetName, false); + + this->LocalGenerator->AddGeneratorTarget( + cm::make_unique<cmGeneratorTarget>(verifyTarget, this->LocalGenerator)); + } + return true; +} + +cm::optional<std::string> cmGeneratorTarget::GenerateHeaderSetVerificationFile( + cmSourceFile& source, std::string const& dir, + std::string const& verifyTargetName, + cm::optional<cm::optional<std::string>>& defaultLanguage) const +{ + if (source.GetPropertyAsBool("SKIP_LINTING")) { + return cm::nullopt; + } + + cm::optional<std::string> language = + this->ResolveHeaderLanguage(source, defaultLanguage); + if (!language) { + return cm::nullopt; + } + + std::string headerFilename = dir; + if (!headerFilename.empty()) { + headerFilename += '/'; + } + headerFilename += source.GetLocation().GetName(); + + return this->GenerateStubForLanguage(*language, headerFilename, + verifyTargetName, source); +} + +cm::optional<std::string> cmGeneratorTarget::ResolveHeaderLanguage( + cmSourceFile& source, + cm::optional<cm::optional<std::string>>& defaultLanguage) const +{ + static std::array<cm::string_view, 4> const supportedLangs{ { + "C", + "CXX", + "OBJC", + "OBJCXX", + } }; + auto isSupported = [](cm::string_view lang) -> bool { + return std::find(supportedLangs.begin(), supportedLangs.end(), lang) != + supportedLangs.end(); + }; + + // If the source has an explicit language, validate and return it. + std::string language = source.GetOrDetermineLanguage(); + if (!language.empty()) { + if (!isSupported(language)) { + return cm::nullopt; + } + return cm::optional<std::string>(std::move(language)); + } + + /* + Compute and cache the default language for unlanguaged headers. + The lattice join is run once per file set, not once per header. + Lattice: OBJCXX + / \ + CXX OBJC + \ / + C + */ + if (!defaultLanguage) { + std::set<std::string> langs; + for (AllConfigSource const& tgtSource : this->GetAllConfigSources()) { + std::string const& lang = tgtSource.Source->GetOrDetermineLanguage(); + if (isSupported(lang)) { + langs.insert(lang); + } + } + if (langs.empty()) { + std::vector<std::string> languagesVector; + this->GlobalGenerator->GetEnabledLanguages(languagesVector); + for (std::string const& lang : languagesVector) { + if (isSupported(lang)) { + langs.insert(lang); + } + } + } + + cm::optional<std::string> resolved; + if (langs.count("OBJCXX") || (langs.count("CXX") && langs.count("OBJC"))) { + resolved = "OBJCXX"; // promote + } else if (langs.count("CXX")) { + resolved = "CXX"; + } else if (langs.count("OBJC")) { + resolved = "OBJC"; + } else if (langs.count("C")) { + resolved = "C"; + } + defaultLanguage = resolved; + } + + return *defaultLanguage; +} + +cm::optional<std::string> cmGeneratorTarget::GenerateStubForLanguage( + std::string const& language, std::string const& headerFilename, + std::string const& verifyTargetName, cmSourceFile& source) const +{ + static std::array<std::pair<cm::string_view, cm::string_view>, 4> const + langToExt = { { + { "C", ".c" }, + { "CXX", ".cxx" }, + { "OBJC", ".m" }, + { "OBJCXX", ".mm" }, + } }; + + // NOLINTNEXTLINE(readability-qualified-auto) + auto const it = + std::find_if(langToExt.begin(), langToExt.end(), + [&](std::pair<cm::string_view, cm::string_view> const& p) { + return p.first == language; + }); + if (it == langToExt.end()) { + return cm::nullopt; + } + + std::string filename = + cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', + verifyTargetName, '/', headerFilename, it->second); + + cmSourceFile* verificationSource = + this->Makefile->GetOrCreateSource(filename); + source.SetSpecialSourceType( + cmSourceFile::SpecialSourceType::HeaderSetVerificationSource); + verificationSource->SetProperty("LANGUAGE", language); + + cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(filename)); + + cmGeneratedFileStream fout(filename); + fout.SetCopyIfDifferent(true); + // The IWYU "associated" pragma tells include-what-you-use to + // consider the headerFile as part of the entire language + // unit within include-what-you-use and as a result allows + // one to get IWYU advice for headers. + // Also suppress clang-tidy include checks in generated code. + fout + << "/* NOLINTNEXTLINE(misc-header-include-cycle,misc-include-cleaner) */\n" + << "#include <" << headerFilename << "> /* IWYU pragma: associated */\n"; + fout.close(); + + return cm::optional<std::string>(std::move(filename)); +}
diff --git a/Source/cmGeneratorTarget_IncludeDirectories.cxx b/Source/cmGeneratorTarget_IncludeDirectories.cxx index c91d25b..b8047e8 100644 --- a/Source/cmGeneratorTarget_IncludeDirectories.cxx +++ b/Source/cmGeneratorTarget_IncludeDirectories.cxx
@@ -97,7 +97,7 @@ void AddLangSpecificImplicitIncludeDirectories( cmGeneratorTarget const* target, std::string const& lang, std::string const& config, std::string const& propertyName, - IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries) + IncludeDirectoryFallBack mode, cm::EvaluatedTargetPropertyEntries& entries) { if (auto const* libraries = target->GetLinkImplementationLibraries(config, UseTo::Compile)) { @@ -113,7 +113,7 @@ } if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { auto* lg = dependency->GetLocalGenerator(); - EvaluatedTargetPropertyEntry entry{ library, library.Backtrace }; + cm::EvaluatedTargetPropertyEntry entry{ library, library.Backtrace }; if (lang == "Swift") { entry.Values.emplace_back( @@ -141,12 +141,12 @@ } void processIncludeDirectories(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, + cm::EvaluatedTargetPropertyEntries& entries, std::vector<BT<std::string>>& includes, std::unordered_set<std::string>& uniqueIncludes, bool debugIncludes) { - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + for (cm::EvaluatedTargetPropertyEntry& entry : entries.Entries) { cmLinkItem const& item = entry.LinkItem; std::string const& targetName = item.AsStr(); bool const fromImported = item.Target && item.Target->IsImported(); @@ -237,8 +237,9 @@ this->DebugIncludesDone = true; - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, context, &dagChecker, this->IncludeDirectoriesEntries); + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, + this->IncludeDirectoriesEntries); if (lang == "Swift") { AddLangSpecificImplicitIncludeDirectories( @@ -266,7 +267,7 @@ } AddInterfaceEntries(this, "INTERFACE_INCLUDE_DIRECTORIES", context, - &dagChecker, entries, IncludeRuntimeInterface::Yes); + &dagChecker, entries, cm::IncludeRuntimeInterface::Yes); processIncludeDirectories(this, entries, includes, uniqueIncludes, debugIncludes);
diff --git a/Source/cmGeneratorTarget_Link.cxx b/Source/cmGeneratorTarget_Link.cxx index 8650b78..f8341f7 100644 --- a/Source/cmGeneratorTarget_Link.cxx +++ b/Source/cmGeneratorTarget_Link.cxx
@@ -24,6 +24,7 @@ #include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" +#include "cmDiagnostics.h" #include "cmGenExContext.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionDAGChecker.h" @@ -1298,8 +1299,8 @@ ", is marked as being deprecated by the owner. The message provided by " "the developer is: \n" << resolved.Target->GetDeprecation() << "\n"; /* clang-format on */ - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, w.str(), bt); + this->LocalGenerator->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, w.str(), + bt); } // Skip targets that will not really be linked. This is probably a
diff --git a/Source/cmGeneratorTarget_LinkDirectories.cxx b/Source/cmGeneratorTarget_LinkDirectories.cxx index 8f11a05..d2360d4 100644 --- a/Source/cmGeneratorTarget_LinkDirectories.cxx +++ b/Source/cmGeneratorTarget_LinkDirectories.cxx
@@ -5,7 +5,6 @@ /* clang-format on */ #include <map> -#include <sstream> #include <string> #include <unordered_set> #include <utility> @@ -30,49 +29,44 @@ namespace { void processLinkDirectories(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, + cm::EvaluatedTargetPropertyEntries& entries, std::vector<BT<std::string>>& directories, std::unordered_set<std::string>& uniqueDirectories, bool debugDirectories) { - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + for (cm::EvaluatedTargetPropertyEntry& entry : entries.Entries) { cmLinkItem const& item = entry.LinkItem; std::string const& targetName = item.AsStr(); std::string usedDirectories; for (std::string& entryDirectory : entry.Values) { if (!cmSystemTools::FileIsFullPath(entryDirectory)) { - std::ostringstream e; - bool noMessage = false; - MessageType messageType = MessageType::FATAL_ERROR; if (!targetName.empty()) { - /* clang-format off */ - e << "Target \"" << targetName << "\" contains relative " - "path in its INTERFACE_LINK_DIRECTORIES:\n" - " \"" << entryDirectory << "\""; - /* clang-format on */ - } else { - switch (tgt->GetPolicyStatusCMP0081()) { - case cmPolicies::WARN: { - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0081) << "\n"; - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - noMessage = true; - break; - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - e << "Found relative path while evaluating link directories of " - "\"" - << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n"; + tgt->GetLocalGenerator()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", targetName, + "\" contains relative path" + " in its INTERFACE_LINK_DIRECTORIES:\n \"", + entryDirectory, "\"")); + return; } - if (!noMessage) { - tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); - if (messageType == MessageType::FATAL_ERROR) { + switch (tgt->GetPolicyStatusCMP0081()) { + case cmPolicies::WARN: + tgt->GetLocalGenerator()->IssuePolicyWarning( + cmPolicies::CMP0081, {}, + cmStrCat("Found relative path while evaluating" + " link directories of \"", + tgt->GetName(), "\":\n \"", entryDirectory, "\"\n")); + break; + case cmPolicies::OLD: + break; + case cmPolicies::NEW: + tgt->GetLocalGenerator()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Found relative path while evaluating" + " link directories of \"", + tgt->GetName(), "\":\n \"", entryDirectory, "\"\n")); return; - } } } @@ -135,11 +129,12 @@ this->DebugLinkDirectoriesDone = true; - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, context, &dagChecker, this->LinkDirectoriesEntries); + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, + this->LinkDirectoriesEntries); AddInterfaceEntries(this, "INTERFACE_LINK_DIRECTORIES", context, &dagChecker, - entries, IncludeRuntimeInterface::Yes, + entries, cm::IncludeRuntimeInterface::Yes, this->GetPolicyStatusCMP0099() == cmPolicies::NEW ? UseTo::Link : UseTo::Compile);
diff --git a/Source/cmGeneratorTarget_Options.cxx b/Source/cmGeneratorTarget_Options.cxx index 2244e46..2fee725 100644 --- a/Source/cmGeneratorTarget_Options.cxx +++ b/Source/cmGeneratorTarget_Options.cxx
@@ -49,14 +49,14 @@ auto const DL_END = "</DEVICE_LINK>"_s; void processOptions(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries const& entries, + cm::EvaluatedTargetPropertyEntries const& entries, std::vector<BT<std::string>>& options, std::unordered_set<std::string>& uniqueOptions, bool debugOptions, char const* logName, OptionsParse parse, bool processDeviceOptions = false) { bool splitOption = !processDeviceOptions; - for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) { + for (cm::EvaluatedTargetPropertyEntry const& entry : entries.Entries) { std::string usedOptions; for (std::string const& opt : entry.Values) { if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) { @@ -222,7 +222,7 @@ MsvcCharSetInfo GetMsvcCharSetInfo( cmGeneratorTarget const& tgt, std::string const& lang, - EvaluatedTargetPropertyEntries const& entries) + cm::EvaluatedTargetPropertyEntries const& entries) { using MsvcCharSet = cmGeneratorTarget::MsvcCharSet; @@ -235,7 +235,7 @@ return { MsvcCharSet::None, false }; } - for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) { + for (cm::EvaluatedTargetPropertyEntry const& entry : entries.Entries) { for (std::string const& value : entry.Values) { MsvcCharSet charSet = cmGeneratorTarget::GetMsvcCharSet(value); if (charSet != MsvcCharSet::None) { @@ -289,11 +289,12 @@ this->DebugCompileOptionsDone = true; - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, context, &dagChecker, this->CompileOptionsEntries); + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, + this->CompileOptionsEntries); AddInterfaceEntries(this, "INTERFACE_COMPILE_OPTIONS", context, &dagChecker, - entries, IncludeRuntimeInterface::Yes); + entries, cm::IncludeRuntimeInterface::Yes); processOptions(this, entries, result, uniqueOptions, debugOptions, "compile options", OptionsParse::Shell); @@ -332,11 +333,12 @@ this->DebugCompileFeaturesDone = true; - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, context, &dagChecker, this->CompileFeaturesEntries); + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, + this->CompileFeaturesEntries); AddInterfaceEntries(this, "INTERFACE_COMPILE_FEATURES", context, &dagChecker, - entries, IncludeRuntimeInterface::Yes); + entries, cm::IncludeRuntimeInterface::Yes); processOptions(this, entries, result, uniqueFeatures, debugFeatures, "compile features", OptionsParse::None); @@ -382,11 +384,12 @@ this->DebugCompileDefinitionsDone = true; - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, context, &dagChecker, this->CompileDefinitionsEntries); + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, + this->CompileDefinitionsEntries); AddInterfaceEntries(this, "INTERFACE_COMPILE_DEFINITIONS", context, - &dagChecker, entries, IncludeRuntimeInterface::Yes); + &dagChecker, entries, cm::IncludeRuntimeInterface::Yes); // Add the character set definition MsvcCharSetInfo charSetInfo = GetMsvcCharSetInfo(*this, language, entries); @@ -449,11 +452,12 @@ this->DebugPrecompileHeadersDone = true; - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, context, &dagChecker, this->PrecompileHeadersEntries); + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, + this->PrecompileHeadersEntries); AddInterfaceEntries(this, "INTERFACE_PRECOMPILE_HEADERS", context, - &dagChecker, entries, IncludeRuntimeInterface::Yes); + &dagChecker, entries, cm::IncludeRuntimeInterface::Yes); std::vector<BT<std::string>> list; processOptions(this, entries, list, uniqueOptions, debugDefines, @@ -507,11 +511,12 @@ this->DebugLinkOptionsDone = true; - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, context, &dagChecker, this->LinkOptionsEntries); + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, + this->LinkOptionsEntries); AddInterfaceEntries(this, "INTERFACE_LINK_OPTIONS", context, &dagChecker, - entries, IncludeRuntimeInterface::Yes, + entries, cm::IncludeRuntimeInterface::Yes, this->GetPolicyStatusCMP0099() == cmPolicies::NEW ? UseTo::Link : UseTo::Compile); @@ -684,12 +689,12 @@ this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, context, }; - EvaluatedTargetPropertyEntries entries; + cm::EvaluatedTargetPropertyEntries entries; if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) { std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create( *this->LocalGenerator->GetCMakeInstance(), *linkOptions); entries.Entries.emplace_back( - EvaluateTargetPropertyEntry(this, context, &dagChecker, *entry)); + cm::EvaluateTargetPropertyEntry(this, context, &dagChecker, *entry)); } processOptions(this, entries, result, uniqueOptions, false, "static library link options", OptionsParse::Shell); @@ -729,7 +734,7 @@ this, "LINK_DEPENDS", nullptr, nullptr, context, }; - EvaluatedTargetPropertyEntries entries; + cm::EvaluatedTargetPropertyEntries entries; if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) { cmList depends{ *linkDepends }; for (auto const& depend : depends) { @@ -740,7 +745,7 @@ } } AddInterfaceEntries(this, "INTERFACE_LINK_DEPENDS", context, &dagChecker, - entries, IncludeRuntimeInterface::Yes, + entries, cm::IncludeRuntimeInterface::Yes, this->GetPolicyStatusCMP0099() == cmPolicies::NEW ? UseTo::Link : UseTo::Compile);
diff --git a/Source/cmGeneratorTarget_Sources.cxx b/Source/cmGeneratorTarget_Sources.cxx index 5278408..f4d2f23 100644 --- a/Source/cmGeneratorTarget_Sources.cxx +++ b/Source/cmGeneratorTarget_Sources.cxx
@@ -4,7 +4,11 @@ #include "cmGeneratorTarget.h" /* clang-format on */ +#include "cmConfigure.h" + +#include <algorithm> #include <cstddef> +#include <functional> #include <map> #include <memory> #include <set> @@ -16,15 +20,16 @@ #include <cm/string_view> #include <cmext/algorithm> -#include <cmext/string_view> #include "cmsys/RegularExpression.hxx" #include "cmEvaluatedTargetProperty.h" -#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGenExContext.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGlobalGenerator.h" #include "cmLinkItem.h" #include "cmList.h" @@ -32,6 +37,7 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceGroup.h" @@ -48,7 +54,7 @@ void AddObjectEntries(cmGeneratorTarget const* headTarget, cm::GenEx::Context const& context, cmGeneratorExpressionDAGChecker* dagChecker, - EvaluatedTargetPropertyEntries& entries) + cm::EvaluatedTargetPropertyEntries& entries) { if (cmLinkImplementationLibraries const* impl = headTarget->GetLinkImplementationLibraries(context.Config, @@ -68,7 +74,7 @@ ge.Parse(std::move(genex)); cge->SetEvaluateForBuildsystem(true); - EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace); + cm::EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace); cmExpandList(cge->Evaluate(context, dagChecker, headTarget), ee.Values); if (cge->GetHadContextSensitiveCondition()) { @@ -80,97 +86,29 @@ } } -void addFileSetEntry(cmGeneratorTarget const* headTarget, - cm::GenEx::Context const& context, - cmGeneratorExpressionDAGChecker* dagChecker, - cmFileSet const* fileSet, - EvaluatedTargetPropertyEntries& entries) -{ - auto dirCges = fileSet->CompileDirectoryEntries(); - auto dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, headTarget, - dagChecker); - bool contextSensitiveDirs = false; - for (auto const& dirCge : dirCges) { - if (dirCge->GetHadContextSensitiveCondition()) { - contextSensitiveDirs = true; - break; - } - } - cmake* cm = headTarget->GetLocalGenerator()->GetCMakeInstance(); - for (auto& entryCge : fileSet->CompileFileEntries()) { - auto targetPropEntry = - cmGeneratorTarget::TargetPropertyEntry::CreateFileSet( - dirs, contextSensitiveDirs, std::move(entryCge), fileSet); - entries.Entries.emplace_back(EvaluateTargetPropertyEntry( - headTarget, context, dagChecker, *targetPropEntry)); - EvaluatedTargetPropertyEntry const& entry = entries.Entries.back(); - for (auto const& file : entry.Values) { - auto* sf = headTarget->Makefile->GetOrCreateSource(file); - if (fileSet->GetType() == "HEADERS"_s) { - sf->SetProperty("HEADER_FILE_ONLY", "TRUE"); - } - -#ifndef CMAKE_BOOTSTRAP - std::string e; - std::string w; - auto path = sf->ResolveFullPath(&e, &w); - if (!w.empty()) { - cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); - } - if (path.empty()) { - if (!e.empty()) { - cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); - } - return; - } - bool found = false; - for (auto const& sg : headTarget->Makefile->GetSourceGroups()) { - if (sg->MatchChildrenFiles(path)) { - found = true; - break; - } - } - if (!found) { - if (fileSet->GetType() == "HEADERS"_s) { - headTarget->Makefile->GetOrCreateSourceGroup("Header Files") - ->AddGroupFile(path); - } - } -#endif - } - } -} - void AddFileSetEntries(cmGeneratorTarget const* headTarget, + cmGeneratorFileSets const* fileSets, cm::GenEx::Context const& context, cmGeneratorExpressionDAGChecker* dagChecker, - EvaluatedTargetPropertyEntries& entries) + cm::EvaluatedTargetPropertyEntries& entries) { - for (auto const& entry : headTarget->Target->GetHeaderSetsEntries()) { - for (auto const& name : cmList{ entry.Value }) { - auto const* headerSet = headTarget->Target->GetFileSet(name); - addFileSetEntry(headTarget, context, dagChecker, headerSet, entries); - } - } - for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) { - for (auto const& name : cmList{ entry.Value }) { - auto const* cxxModuleSet = headTarget->Target->GetFileSet(name); - addFileSetEntry(headTarget, context, dagChecker, cxxModuleSet, entries); - } - } + auto sources = fileSets->GetSources(context, headTarget, dagChecker); + entries = + EvaluateTargetPropertyEntries(headTarget, context, dagChecker, sources); } -bool processSources(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, +bool processSources(cmGeneratorTarget const* tgt, std::string const& config, + cm::EvaluatedTargetPropertyEntries& entries, std::vector<BT<std::string>>& srcs, std::unordered_set<std::string>& uniqueSrcs, - bool debugSources) + bool debugSources, + std::function<void(cmSourceFile*)> postProcess = {}) { cmMakefile* mf = tgt->Target->GetMakefile(); bool contextDependent = entries.HadContextSensitiveCondition; - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + for (cm::EvaluatedTargetPropertyEntry& entry : entries.Entries) { if (entry.ContextDependent) { contextDependent = true; } @@ -183,13 +121,13 @@ std::string e; std::string w; std::string fullPath = sf->ResolveFullPath(&e, &w); - cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); + cmLocalGenerator const* const lg = tgt->GetLocalGenerator(); if (!w.empty()) { - cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); + lg->IssuePolicyWarning(cmPolicies::CMP0115, {}, w, entry.Backtrace); } if (fullPath.empty()) { if (!e.empty()) { - cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); + lg->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); } return contextDependent; } @@ -209,6 +147,10 @@ return contextDependent; } src = fullPath; + + if (postProcess) { + postProcess(sf); + } } std::string usedSources; for (std::string const& src : entry.Values) { @@ -217,6 +159,41 @@ if (debugSources) { usedSources += cmStrCat(" * ", src, '\n'); } + } else { + auto const& fileSets = + tgt->GetGeneratorFileSets()->GetAllFileSetsForSource(config, src); + if (fileSets.empty()) { + continue; + } + auto fileMustBeUnique = [&fileSets]() -> bool { + return std::none_of( + fileSets.begin(), fileSets.end(), + [](cmGeneratorFileSet const* fileSet) { + return cm::FileSetMetadata::GetAttributes(fileSet->GetType()) + .contains(cm::FileSetMetadata::FileSetAttributes:: + FilesInMultipleFileSets); + }); + }; + if (fileMustBeUnique()) { + auto const* fileSet = *fileSets.begin(); + switch (tgt->GetPolicyStatusCMP0211()) { + case cmPolicies::WARN: + tgt->GetLocalGenerator()->IssuePolicyWarning( + cmPolicies::CMP0211, {}, + cmStrCat("In target \"", tgt->GetName(), "\" the file\n ", + src, "\nalready belongs to file set \"", + fileSet->GetName(), "\".")); + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + default: + tgt->GetLocalGenerator()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("In target \"", tgt->GetName(), "\" the file\n ", + src, "\nalready belongs to file set \"", + fileSet->GetName(), "\".")); + } + } } } if (!usedSources.empty()) { @@ -250,28 +227,30 @@ this, "SOURCES", nullptr, nullptr, context, }; - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, context, &dagChecker, this->SourceEntries); + cm::EvaluatedTargetPropertyEntries entries = + cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, + this->SourceEntries); std::unordered_set<std::string> uniqueSrcs; bool contextDependentDirectSources = - processSources(this, entries, files, uniqueSrcs, debugSources); + processSources(this, config, entries, files, uniqueSrcs, debugSources); // Collect INTERFACE_SOURCES of all direct link-dependencies. - EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries; - AddInterfaceEntries(this, "INTERFACE_SOURCES", context, &dagChecker, - linkInterfaceSourcesEntries, IncludeRuntimeInterface::No, - UseTo::Compile); - bool contextDependentInterfaceSources = processSources( - this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources); + cm::EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries; + cm::AddInterfaceEntries(this, "INTERFACE_SOURCES", context, &dagChecker, + linkInterfaceSourcesEntries, + cm::IncludeRuntimeInterface::No, UseTo::Compile); + bool contextDependentInterfaceSources = + processSources(this, config, linkInterfaceSourcesEntries, files, + uniqueSrcs, debugSources); // Collect TARGET_OBJECTS of direct object link-dependencies. bool contextDependentObjects = false; if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) { - EvaluatedTargetPropertyEntries linkObjectsEntries; + cm::EvaluatedTargetPropertyEntries linkObjectsEntries; AddObjectEntries(this, context, &dagChecker, linkObjectsEntries); - contextDependentObjects = processSources(this, linkObjectsEntries, files, - uniqueSrcs, debugSources); + contextDependentObjects = processSources(this, config, linkObjectsEntries, + files, uniqueSrcs, debugSources); // Note that for imported targets or multi-config generators supporting // cross-config builds the paths to the object files must be per-config, // so contextDependentObjects will be true here even if object libraries @@ -279,14 +258,50 @@ } // Collect this target's file sets. - EvaluatedTargetPropertyEntries fileSetEntries; - AddFileSetEntries(this, context, &dagChecker, fileSetEntries); + cmGeneratorExpressionDAGChecker fsDagChecker{ + this, "SOURCES", nullptr, nullptr, context, + }; + + cm::EvaluatedTargetPropertyEntries fileSetEntries; + AddFileSetEntries(this, this->FileSets.get(), context, &fsDagChecker, + fileSetEntries); + auto processFileSetEntry = [this, &config](cmSourceFile* sf) { + cmGeneratorFileSet const* fileSet = this->GetFileSetForSource(config, sf); + if (fileSet && fileSet->GetType() == cm::FileSetMetadata::HEADERS) { + sf->SetProperty("HEADER_FILE_ONLY", "TRUE"); +#if !defined(CMAKE_BOOTSTRAP) + cmMakefile* mf = this->Target->GetMakefile(); + std::string const& path = sf->GetFullPath(); + bool found = false; + for (auto const& sg : mf->GetSourceGroups()) { + if (sg->MatchChildrenFiles(path)) { + found = true; + break; + } + } + if (!found) { + mf->GetOrCreateSourceGroup("Header Files")->AddGroupFile(path); + } +#endif + } + }; bool contextDependentFileSets = - processSources(this, fileSetEntries, files, uniqueSrcs, debugSources); + processSources(this, config, fileSetEntries, files, uniqueSrcs, + debugSources, processFileSetEntry); + + // Collect file sets INTERFACE_SOURCES of all direct link-dependencies. + cm::EvaluatedTargetPropertyEntries linkInterfaceFileSetsEntries; + cm::AddInterfaceFileSetsEntries(this, cm::FileSetMetadata::SOURCES, + "INTERFACE_SOURCES", context, &fsDagChecker, + linkInterfaceFileSetsEntries); + bool contextDependentInterfaceFileSets = + processSources(this, config, linkInterfaceFileSetsEntries, files, + uniqueSrcs, debugSources); // Determine if sources are context-dependent or not. if (!contextDependentDirectSources && !contextDependentInterfaceSources && - !contextDependentObjects && !contextDependentFileSets) { + !contextDependentObjects && !contextDependentFileSets && + !contextDependentInterfaceFileSets) { this->SourcesAreContextDependent = Tribool::False; } else { this->SourcesAreContextDependent = Tribool::True; @@ -385,6 +400,12 @@ cmsys::RegularExpression header_regex(CM_HEADER_REGEX); std::vector<cmSourceFile*> badObjLib; + cmValue const rustMainCrateRootProp = + this->GetProperty("Rust_MAIN_CRATE_ROOT"); + cmSourceFile const* rustMainCrateRootSf = rustMainCrateRootProp + ? this->Makefile->GetOrCreateSource(rustMainCrateRootProp) + : nullptr; + std::set<cmSourceFile*> emitted; for (BT<std::string> const& s : srcs) { // Create each source at most once. @@ -396,11 +417,11 @@ // Compute the kind (classification) of this source file. SourceKind kind; std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); - cmFileSet const* fs = this->GetFileSetForSource(config, sf); + cmGeneratorFileSet const* fs = this->GetFileSetForSource(config, sf); if (sf->GetCustomCommand()) { kind = SourceKindCustomCommand; } else if (!this->Target->IsNormal() && !this->Target->IsImported() && - fs && (fs->GetType() == "CXX_MODULES"_s)) { + fs && (fs->GetType() == cm::FileSetMetadata::CXX_MODULES)) { kind = SourceKindCxxModuleSource; } else if (this->Target->GetType() == cmStateEnums::UTILITY || this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY @@ -417,7 +438,28 @@ } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { kind = SourceKindExternalObject; } else if (!sf->GetOrDetermineLanguage().empty()) { - kind = SourceKindObjectSource; + if (sf->GetOrDetermineLanguage() == "Rust") { + // NOLINTNEXTLINE(bugprone-branch-clone) + if (this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { + // There is no main crate root for object libraries. + kind = SourceKindObjectSource; + } else if (!rustMainCrateRootSf) { + // We do not have a main crate root source file, we use the first + // Rust source file for it. + rustMainCrateRootSf = sf; + kind = SourceKindRustMainCrateRoot; + } else if (rustMainCrateRootSf == sf) { + // Current source file is the main crate root defined in the target + // Rust_MAIN_CRATE_ROOT property. + kind = SourceKindRustMainCrateRoot; + } else { + // Any other Rust source file is treated as a normal object, but will + // be built into a .rlib. Maybe in the future this could be changed? + kind = SourceKindObjectSource; + } + } else { + kind = SourceKindObjectSource; + } } else if (ext == "def") { kind = SourceKindModuleDefinition; if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
diff --git a/Source/cmGeneratorTarget_TargetPropertyEntry.cxx b/Source/cmGeneratorTarget_TargetPropertyEntry.cxx deleted file mode 100644 index 2280e93..0000000 --- a/Source/cmGeneratorTarget_TargetPropertyEntry.cxx +++ /dev/null
@@ -1,183 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -/* clang-format off */ -#include "cmGeneratorTarget.h" -/* clang-format on */ - -#include <map> -#include <string> -#include <utility> -#include <vector> - -#include <cm/memory> - -#include "cmFileSet.h" -#include "cmGeneratorExpression.h" -#include "cmLinkItem.h" -#include "cmList.h" -#include "cmListFileCache.h" - -class cmake; - -cmLinkItem cmGeneratorTarget::TargetPropertyEntry::NoLinkItem; - -class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryString(BT<std::string> propertyValue, - cmLinkItem const& item = NoLinkItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , PropertyValue(std::move(propertyValue)) - { - } - - std::string const& Evaluate(cm::GenEx::Context const&, - cmGeneratorTarget const*, - cmGeneratorExpressionDAGChecker*) const override - { - return this->PropertyValue.Value; - } - - cmListFileBacktrace GetBacktrace() const override - { - return this->PropertyValue.Backtrace; - } - std::string const& GetInput() const override - { - return this->PropertyValue.Value; - } - -private: - BT<std::string> PropertyValue; -}; - -class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge, - cmLinkItem const& item = NoLinkItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , ge(std::move(cge)) - { - } - - std::string const& Evaluate( - cm::GenEx::Context const& context, cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker) const override - { - return this->ge->Evaluate(context, dagChecker, headTarget); - } - - cmListFileBacktrace GetBacktrace() const override - { - return this->ge->GetBacktrace(); - } - - std::string const& GetInput() const override { return this->ge->GetInput(); } - - bool GetHadContextSensitiveCondition() const override - { - return this->ge->GetHadContextSensitiveCondition(); - } - -private: - std::unique_ptr<cmCompiledGeneratorExpression> const ge; -}; - -class TargetPropertyEntryFileSet - : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryFileSet( - std::vector<std::string> dirs, bool contextSensitiveDirs, - std::unique_ptr<cmCompiledGeneratorExpression> entryCge, - cmFileSet const* fileSet, cmLinkItem const& item = NoLinkItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , BaseDirs(std::move(dirs)) - , ContextSensitiveDirs(contextSensitiveDirs) - , EntryCge(std::move(entryCge)) - , FileSet(fileSet) - { - } - - std::string const& Evaluate( - cm::GenEx::Context const& context, cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker) const override - { - std::map<std::string, std::vector<std::string>> filesPerDir; - this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir, - this->EntryCge, context, headTarget, - dagChecker); - - std::vector<std::string> files; - for (auto const& it : filesPerDir) { - files.insert(files.end(), it.second.begin(), it.second.end()); - } - - static std::string filesStr; - filesStr = cmList::to_string(files); - return filesStr; - } - - cmListFileBacktrace GetBacktrace() const override - { - return this->EntryCge->GetBacktrace(); - } - - std::string const& GetInput() const override - { - return this->EntryCge->GetInput(); - } - - bool GetHadContextSensitiveCondition() const override - { - return this->ContextSensitiveDirs || - this->EntryCge->GetHadContextSensitiveCondition(); - } - -private: - std::vector<std::string> const BaseDirs; - bool const ContextSensitiveDirs; - std::unique_ptr<cmCompiledGeneratorExpression> const EntryCge; - cmFileSet const* FileSet; -}; - -std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry> -cmGeneratorTarget::TargetPropertyEntry::Create( - cmake& cmakeInstance, const BT<std::string>& propertyValue, - bool evaluateForBuildsystem) -{ - if (cmGeneratorExpression::Find(propertyValue.Value) != std::string::npos) { - cmGeneratorExpression ge(cmakeInstance, propertyValue.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(propertyValue.Value); - cge->SetEvaluateForBuildsystem(evaluateForBuildsystem); - return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>( - cm::make_unique<TargetPropertyEntryGenex>(std::move(cge))); - } - - return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>( - cm::make_unique<TargetPropertyEntryString>(propertyValue)); -} - -std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry> -cmGeneratorTarget::TargetPropertyEntry::CreateFileSet( - std::vector<std::string> dirs, bool contextSensitiveDirs, - std::unique_ptr<cmCompiledGeneratorExpression> entryCge, - cmFileSet const* fileSet, cmLinkItem const& item) -{ - return cm::make_unique<TargetPropertyEntryFileSet>( - std::move(dirs), contextSensitiveDirs, std::move(entryCge), fileSet, item); -} - -cmGeneratorTarget::TargetPropertyEntry::TargetPropertyEntry( - cmLinkItem const& item) - : LinkItem(item) -{ -} - -bool cmGeneratorTarget::TargetPropertyEntry::GetHadContextSensitiveCondition() - const -{ - return false; -}
diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx index 7497afa..db8f8ec 100644 --- a/Source/cmGetCMakePropertyCommand.cxx +++ b/Source/cmGetCMakePropertyCommand.cxx
@@ -8,6 +8,7 @@ #include "cmGlobalGenerator.h" #include "cmList.h" #include "cmMakefile.h" +#include "cmPolicies.h" #include "cmState.h" #include "cmValue.h" @@ -27,7 +28,9 @@ if (cmValue varsProp = status.GetMakefile().GetProperty("VARIABLES")) { output = *varsProp; } - } else if (args[1] == "MACROS") { + } else if (args[1] == "MACROS" && + status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) != + cmPolicies::NEW) { output.clear(); if (cmValue macrosProp = status.GetMakefile().GetProperty("MACROS")) { output = *macrosProp;
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index 8c9c658..5c1d1c4 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -10,15 +10,19 @@ #include <vector> #include <cm/optional> +#include <cmext/string_view> #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmGeneratedFileStream.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorOptions.h" #include "cmGeneratorTarget.h" #include "cmGlobalGhsMultiGenerator.h" #include "cmLinkLineComputer.h" // IWYU pragma: keep #include "cmList.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmLocalGhsMultiGenerator.h" #include "cmMakefile.h" @@ -503,6 +507,34 @@ } } +void cmGhsMultiTargetGenerator::WriteFileSetProperty( + std::ostream& fout, cmGeneratorFileSet const* fileSet, + std::string const& lang, cm::string_view propName, cm::string_view propFlag) +{ + if (!fileSet) { + return; + } + + std::vector<BT<std::string>> entries; + if (propName == "COMPILE_OPTIONS"_s) { + entries = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetCompileOptions(this->ConfigName, lang) + : fileSet->GetInterfaceCompileOptions(this->ConfigName, lang); + } else if (propName == "COMPILE_DEFINITIONS"_s) { + entries = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetCompileDefinitions(this->ConfigName, lang) + : fileSet->GetInterfaceCompileDefinitions(this->ConfigName, lang); + } else if (propName == "INCLUDE_DIRECTORIES"_s) { + entries = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetIncludeDirectories(this->ConfigName, lang) + : fileSet->GetInterfaceIncludeDirectories(this->ConfigName, lang); + } + + for (auto const& entry : entries) { + fout << " " << propFlag << entry.Value << '\n'; + } +} + void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) { /* vector of all sources for this target */ @@ -642,9 +674,20 @@ *fout << comment << fname << WriteObjectLangOverride(si) << '\n'; if (compile) { + // lookup for the associated file set, if any. + auto const* fileSet = + this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( + this->ConfigName, si); + + this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), + "INCLUDE_DIRECTORIES"_s, ""); this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); + this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), + "COMPILE_DEFINITIONS"_s, ""); this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); + this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), + "COMPILE_OPTIONS"_s, ""); /* to avoid clutter in the GUI only print out the objectName if it * has been renamed */
diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h index 91ca105..fa07103 100644 --- a/Source/cmGhsMultiTargetGenerator.h +++ b/Source/cmGhsMultiTargetGenerator.h
@@ -8,6 +8,8 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmGhsMultiGpj.h" class cmCustomCommand; @@ -17,6 +19,7 @@ class cmLocalGhsMultiGenerator; class cmMakefile; class cmSourceFile; +class cmGeneratorFileSet; class cmGhsMultiTargetGenerator { @@ -65,6 +68,10 @@ void WriteSourceProperty(std::ostream& fout, cmSourceFile const* sf, std::string const& propName, std::string const& propFlag); + void WriteFileSetProperty(std::ostream& fout, + cmGeneratorFileSet const* fileSet, + std::string const& lang, cm::string_view propName, + cm::string_view propFlag); static std::string WriteObjectLangOverride(cmSourceFile const* sourceFile); bool DetermineIfIntegrityApp();
diff --git a/Source/cmGlobalFastbuildGenerator.cxx b/Source/cmGlobalFastbuildGenerator.cxx index 9013c5e..e906e6f 100644 --- a/Source/cmGlobalFastbuildGenerator.cxx +++ b/Source/cmGlobalFastbuildGenerator.cxx
@@ -1201,9 +1201,9 @@ WriteVariable("UnityOutputPath", Quote(Unity.UnityOutputPath), 2); WriteVariable("UnityOutputPattern", Quote(Unity.UnityOutputPattern), 2); WriteArray("UnityInputFiles", Wrap(Unity.UnityInputFiles), 2); - if (!Unity.UnityInputIsolatedFiles.empty()) { - WriteArray("UnityInputIsolatedFiles", - Wrap(Unity.UnityInputIsolatedFiles), 2); + if (!Unity.UnityInputExcludedFiles.empty()) { + WriteArray("UnityInputExcludedFiles", + Wrap(Unity.UnityInputExcludedFiles), 2); } if (UsingRelativePaths) { WriteVariable("UseRelativePaths_Experimental", "true", 2);
diff --git a/Source/cmGlobalFastbuildGenerator.h b/Source/cmGlobalFastbuildGenerator.h index f2aa938..07e8772 100644 --- a/Source/cmGlobalFastbuildGenerator.h +++ b/Source/cmGlobalFastbuildGenerator.h
@@ -233,7 +233,7 @@ std::string UnityOutputPath; std::vector<std::string> UnityInputFiles; std::string UnityOutputPattern; - std::vector<std::string> UnityInputIsolatedFiles; + std::vector<std::string> UnityInputExcludedFiles; FastbuildUnityNode() : FastbuildTargetBase(FastbuildTargetType::UNITY) {
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index e4f50d3..13cee3e 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx
@@ -27,6 +27,7 @@ #include "cmAlgorithms.h" #include "cmArgumentParserTypes.h" #include "cmBuildArgs.h" +#include "cmBuildSbomGenerator.h" #include "cmCMakePath.h" #include "cmCPackPropertiesGenerator.h" #include "cmComputeTargetDepends.h" @@ -34,6 +35,7 @@ #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" #include "cmCustomCommandTypes.h" +#include "cmDiagnostics.h" #include "cmDuration.h" #include "cmExperimental.h" #include "cmExportBuildFileGenerator.h" @@ -41,9 +43,10 @@ #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" +#include "cmInstallExportGenerator.h" #include "cmInstallGenerator.h" #include "cmInstallRuntimeDependencySet.h" -#include "cmInstallSbomExportGenerator.h" +#include "cmInstallSbomGenerator.h" #include "cmLinkLineComputer.h" #include "cmList.h" #include "cmListFileCache.h" @@ -60,8 +63,8 @@ #include "cmStateDirectory.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" -#include "cmSyntheticTargetCache.h" #include "cmSystemTools.h" +#include "cmTargetExport.h" #include "cmValue.h" #include "cmVersion.h" #include "cmWorkingDirectory.h" @@ -311,6 +314,77 @@ this->BuildExportSets[gen->GetMainExportFileName()] = gen; } +cmExportFileGenerator::ExportInfo cmGlobalGenerator::FindBuildExportInfo( + cmGeneratorTarget const* target) const +{ + cmExportFileGenerator::ExportInfo info; + for (auto const& exp : this->BuildExportSets) { + if (auto rec = exp.second->FindRecordForTarget(target)) { + info.Files.push_back(exp.first); + info.Sets.insert(rec->Name.empty() ? exp.first : rec->Name); + info.Namespaces.insert(rec->Namespace); + } + } + return info; +} + +cmExportFileGenerator::ExportInfo cmGlobalGenerator::FindInstallExportInfo( + cmGeneratorTarget const* target) const +{ + cmExportFileGenerator::ExportInfo info; + auto const& name = target->GetName(); + for (auto const& exp : this->ExportSets) { + auto const& exportSet = exp.second; + auto const& targets = exportSet.GetTargetExports(); + bool const contains = + std::any_of(targets.begin(), targets.end(), + [&name](std::unique_ptr<cmTargetExport> const& te) { + return te->TargetName == name; + }); + if (!contains) { + continue; + } + auto const* installs = exportSet.GetInstallations(); + if (!installs || installs->empty()) { + continue; + } + info.Sets.insert(exp.first); + for (auto const* install : *installs) { + info.Files.push_back(install->GetDestinationFile()); + info.Namespaces.insert(install->GetNamespace()); + } + } + return info; +} + +#ifndef CMAKE_BOOTSTRAP +cmSbomBuilder::SbomInfo cmGlobalGenerator::FindBuildSbomInfo( + cmGeneratorTarget const* target) const +{ + cmSbomBuilder::SbomInfo info; + for (cmBuildSbomGenerator const* g : this->BuildSbomGenerators) { + if (g->CoversTarget(target)) { + info.Packages.push_back(g->GetPackageName()); + } + } + std::sort(info.Packages.begin(), info.Packages.end()); + return info; +} + +cmSbomBuilder::SbomInfo cmGlobalGenerator::FindInstallSbomInfo( + cmGeneratorTarget const* target) const +{ + cmSbomBuilder::SbomInfo info; + for (cmInstallSbomGenerator const* g : this->InstallSbomGenerators) { + if (g->CoversTarget(target)) { + info.Packages.push_back(g->GetPackageName()); + } + } + std::sort(info.Packages.begin(), info.Packages.end()); + return info; +} +#endif + void cmGlobalGenerator::AddBuildExportExportSet( cmExportBuildFileGenerator* gen) { @@ -318,6 +392,17 @@ this->AddBuildExportSet(gen); } +void cmGlobalGenerator::AddBuildSbomGenerator(cmBuildSbomGenerator* gen) +{ + this->BuildSbomGenerators.push_back(gen); +} + +void cmGlobalGenerator::AddInstallSbomGenerator( + cmInstallSbomGenerator const* gen) +{ + this->InstallSbomGenerators.push_back(gen); +} + void cmGlobalGenerator::ForceLinkerLanguages() { } @@ -412,6 +497,24 @@ return !cm::contains(this->BuildExportExportSets, filename); } +bool cmGlobalGenerator::IsBuildSbomFile(std::string const& filepath) const +{ + return std::any_of(this->BuildSbomGenerators.begin(), + this->BuildSbomGenerators.end(), + [&filepath](cmBuildSbomGenerator const* g) { + return g->GetOutputFile() == filepath; + }); +} + +bool cmGlobalGenerator::IsInstallSbomFile(std::string const& filepath) const +{ + return std::any_of(this->InstallSbomGenerators.begin(), + this->InstallSbomGenerators.end(), + [&filepath](cmInstallSbomGenerator const* g) { + return g->GetInstallFile() == filepath; + }); +} + // Find the make program for the generator, required for try compiles bool cmGlobalGenerator::FindMakeProgram(cmMakefile* mf) { @@ -550,7 +653,7 @@ if (lang == "NONE") { this->SetLanguageEnabled("NONE", mf); } else { - if (!cm::contains(this->LanguagesReady, lang)) { + if (!cm::contains(this->LanguagesReadyForTryCompile, lang)) { std::ostringstream e; e << "The test project needs language " << lang << " which is not enabled."; @@ -747,8 +850,11 @@ this->SetLanguageEnabled("NONE", mf); continue; } - std::string loadedLang = cmStrCat("CMAKE_", lang, "_COMPILER_LOADED"); - if (!mf->GetDefinition(loadedLang)) { + // Compiler information may have already been detected and saved. + // Load it if we have not enabled the language anywhere yet, or + // if we have not loaded compiler information in this directory. + if (!this->GetLanguageEnabled(lang) || + !mf->GetDefinition(cmStrCat("CMAKE_", lang, "_COMPILER_LOADED"))) { fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake"); // If the existing build tree was already configured with this @@ -904,12 +1010,12 @@ if (needSetLanguageEnabledMaps[lang]) { this->SetLanguageEnabledMaps(lang, mf); } - this->LanguagesReady.insert(lang); + + // At this point we have enough info for a try compile. + this->LanguagesReadyForTryCompile.insert(lang); // Test the compiler for the language just setup // (but only if a compiler has been actually found) - // At this point we should have enough info for a try compile - // which is used in the backward stuff // If the language is untested then test it now with a try compile. if (needTestLanguage[lang]) { if (!this->CMakeInstance->GetIsInTryCompile()) { @@ -1018,14 +1124,11 @@ case cmPolicies::WARN: if (!this->CMakeInstance->GetIsInTryCompile() && mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0089")) { - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0089) << "\n" - "Converting " << lang << - R"( compiler id "XLClang" to "XL" for compatibility.)" - ; - /* clang-format on */ - mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + mf->IssuePolicyWarning( + cmPolicies::CMP0089, {}, + cmStrCat( + "Converting "_s, lang, + R"( compiler id "XLClang" to "XL" for compatibility.)"_s)); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -1043,14 +1146,10 @@ case cmPolicies::WARN: if (!this->CMakeInstance->GetIsInTryCompile() && mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0129")) { - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0129) << "\n" - "Converting " << lang << - R"( compiler id "LCC" to "GNU" for compatibility.)" - ; - /* clang-format on */ - mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + mf->IssuePolicyWarning( + cmPolicies::CMP0129, {}, + cmStrCat("Converting "_s, lang, + R"( compiler id "LCC" to "GNU" for compatibility.)"_s)); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -1086,6 +1185,13 @@ { std::string const& lang = source.GetLanguage(); if (!lang.empty()) { + if (lang == "Rust") { + // Rust source file can be compiled into different type of outputs. So + // we need to change the extension based on the Rust_EMIT property. + if (cmValue const rustEmit = source.GetRustEmitProperty()) { + return this->GetRustEmitOutputExtension(rustEmit); + } + } return this->GetLanguageOutputExtension(lang); } // if no language is found then check to see if it is already an @@ -1110,6 +1216,16 @@ return ""; } +std::string cmGlobalGenerator::GetRustEmitOutputExtension( + std::string const& emitValue) const +{ + auto const it = this->RustEmitToOutputExtension.find(emitValue); + if (it != this->RustEmitToOutputExtension.end()) { + return it->second; + } + return ""; +} + cm::string_view cmGlobalGenerator::GetLanguageFromExtension( cm::string_view ext) const { @@ -1210,6 +1326,19 @@ } } + if (l == "Rust") { + std::string const emitValues = + mf->GetSafeDefinition("CMAKE_Rust_EMIT_VALUES"); + cmList emitList{ emitValues }; + for (std::string const& v : emitList) { + std::string emitOutputExtension = + cmStrCat("CMAKE_Rust_EMIT_", v, "_OUTPUT_EXTENSION"); + if (cmValue outputExtension = mf->GetDefinition(emitOutputExtension)) { + this->RustEmitToOutputExtension[v] = outputExtension; + } + } + } + // The map was originally filled by SetLanguageEnabledFlag, but // since then the compiler- and platform-specific files have been // loaded which might have added more entries. @@ -1308,7 +1437,7 @@ if (this->ExtraGenerator && !this->CMakeInstance->GetIsInTryCompile()) { this->CMakeInstance->IssueMessage( - MessageType::DEPRECATION_WARNING, + MessageType::WARNING, cmStrCat("Support for \"Extra Generators\" like\n ", this->ExtraGenerator->GetName(), "\nis deprecated and will be removed from a future version " @@ -1416,11 +1545,15 @@ void cmGlobalGenerator::ComputeBuildFileGenerators() { for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) { - std::vector<std::unique_ptr<cmExportBuildFileGenerator>> const& gens = - this->Makefiles[i]->GetExportBuildFileGenerators(); - for (std::unique_ptr<cmExportBuildFileGenerator> const& g : gens) { - g->Compute(this->LocalGenerators[i].get()); + cmLocalGenerator* lg = this->LocalGenerators[i].get(); + for (auto const& g : this->Makefiles[i]->GetExportBuildFileGenerators()) { + g->Compute(lg); } +#ifndef CMAKE_BOOTSTRAP + for (auto const& g : this->Makefiles[i]->GetBuildSbomGenerators()) { + g->Compute(lg); + } +#endif } } @@ -1546,13 +1679,12 @@ } } - // We now have all targets set up and std levels constructed. Add - // `__CMAKE::CXX*` targets as link dependencies to all targets which need - // them. + // We now have all targets set up. Add the `@cmake_cxx_std` target as a link + // dependency to all targets which need it. // // Synthetic targets performed this inside of // `cmLocalGenerator::DiscoverSyntheticTargets` - if (!this->ApplyCXXStdTargets()) { + if (!this->ApplyCXXStdTarget()) { return false; } @@ -1597,10 +1729,10 @@ bool sbomEnabled = cmExperimental::HasSupportEnabled( *this->Makefiles[0], cmExperimental::Feature::GenerateSbom); - // Automatically generate SBOM files if enabled. + // Automatically generate one SBOM per export set not already tied to an + // explicit install(SBOM) call. cmValue sbomFormat = this->GetGlobalSetting("CMAKE_INSTALL_SBOM_FORMATS"); - if (sbomFormat.IsSet() && !this->Makefiles[0]->ExplicitlyGeneratesSbom() && - sbomEnabled && !isTryCompile) { + if (sbomFormat.IsSet() && sbomEnabled && !isTryCompile) { std::string location = this->Makefiles[0]->GetSafeDefinition("CMAKE_INSTALL_LIBDIR"); if (location.empty()) { @@ -1608,17 +1740,27 @@ } std::string projectName = this->LocalGenerators[0]->GetProjectName(); - cmSbomArguments sbomDefaultArgs; - sbomDefaultArgs.ProjectName = projectName; for (auto& exportSet : this->ExportSets) { + bool isCovered = + std::any_of(this->InstallSbomGenerators.cbegin(), + this->InstallSbomGenerators.cend(), + [&exportSet](cmInstallSbomGenerator const* g) { + return g->CoversExportSet(&exportSet.second); + }); + if (isCovered) { + continue; + } + cmSbomArguments sbomDefaultArgs; + sbomDefaultArgs.ProjectName = projectName; sbomDefaultArgs.PackageName = exportSet.first; std::string dest = cmStrCat(location, "/sbom/", projectName); this->Makefiles[0]->AddInstallGenerator( - cm::make_unique<cmInstallSbomExportGenerator>( - &exportSet.second, dest, "", std::vector<std::string>(), "", + cm::make_unique<cmInstallSbomGenerator>( + std::vector<cmExportSet*>{ &exportSet.second }, dest, "", + std::vector<std::string>(), "", cmInstallGenerator::SelectMessageLevel(this->Makefiles[0].get()), - false, std::move(sbomDefaultArgs), "", - this->Makefiles[0]->GetBacktrace())); + false, std::move(sbomDefaultArgs), + cmInstallGenerator::CaptureContext(this->Makefiles[0].get()))); } } #endif @@ -1728,6 +1870,17 @@ return; } } +#ifndef CMAKE_BOOTSTRAP + for (auto& sbomGen : this->BuildSbomGenerators) { + if (!sbomGen->GenerateForBuild()) { + if (!cmSystemTools::GetErrorOccurredFlag()) { + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, + "Could not write SBOM file."); + } + return; + } + } +#endif // Update rule hashes. this->CheckRuleHashes(); @@ -1753,8 +1906,8 @@ for (std::string const& t : this->CMP0068WarnTargets) { w << ' ' << t << '\n'; } - this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, - w.str()); + this->GetCMakeInstance()->IssueDiagnostic(cmDiagnostics::CMD_POLICY, + w.str()); } } @@ -1846,12 +1999,12 @@ entry->second = index++; } -bool cmGlobalGenerator::ApplyCXXStdTargets() +bool cmGlobalGenerator::ApplyCXXStdTarget() { for (auto const& gen : this->LocalGenerators) { - // tgt->ApplyCXXStd can create targets itself, so we need iterators which - // won't be invalidated by that target creation + // tgt->ApplyCXXStdTarget can create a target itself, so we need iterators + // which won't be invalidated by that target creation auto const& genTgts = gen->GetGeneratorTargets(); std::vector<cmGeneratorTarget*> existingTgts; existingTgts.reserve(genTgts.size()); @@ -1860,7 +2013,7 @@ } for (auto const& tgt : existingTgts) { - if (!tgt->ApplyCXXStdTargets()) { + if (!tgt->ApplyCXXStdTarget()) { return false; } } @@ -1871,8 +2024,6 @@ bool cmGlobalGenerator::DiscoverSyntheticTargets() { - cmSyntheticTargetCache cache; - for (auto const& gen : this->LocalGenerators) { // Because DiscoverSyntheticTargets() adds generator targets, we need to // cache the existing list of generator targets before starting. @@ -1887,7 +2038,7 @@ tgt->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); for (auto const& config : configs) { - if (!tgt->DiscoverSyntheticTargets(cache, config)) { + if (!tgt->DiscoverSyntheticTargets(config)) { return false; } } @@ -2542,8 +2693,7 @@ return cm::make_unique<cmLocalGenerator>(this, mf); } -void cmGlobalGenerator::EnableLanguagesFromGenerator(cmGlobalGenerator* gen, - cmMakefile* mf) +void cmGlobalGenerator::SetupTryCompile(cmGlobalGenerator* gen, cmMakefile* mf) { this->SetConfiguredFilesPath(gen); this->TryCompileOuterMakefile = mf; @@ -2551,15 +2701,7 @@ gen->GetCMakeInstance()->GetCacheDefinition("CMAKE_MAKE_PROGRAM"); this->GetCMakeInstance()->AddCacheEntry( "CMAKE_MAKE_PROGRAM", make, "make program", cmStateEnums::FILEPATH); - // copy the enabled languages - this->GetCMakeInstance()->GetState()->SetEnabledLanguages( - gen->GetCMakeInstance()->GetState()->GetEnabledLanguages()); - this->LanguagesReady = gen->LanguagesReady; - this->ExtensionToLanguage = gen->ExtensionToLanguage; - this->IgnoreExtensions = gen->IgnoreExtensions; - this->LanguageToOutputExtension = gen->LanguageToOutputExtension; - this->LanguageToLinkerPreference = gen->LanguageToLinkerPreference; - this->OutputExtensions = gen->OutputExtensions; + this->LanguagesReadyForTryCompile = gen->LanguagesReadyForTryCompile; } void cmGlobalGenerator::SetConfiguredFilesPath(cmGlobalGenerator* gen) @@ -3068,29 +3210,20 @@ return; } - MessageType messageType = MessageType::AUTHOR_WARNING; - std::ostringstream e; - bool issueMessage = false; switch (policyStatus) { case cmPolicies::WARN: - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0171) << '\n'; - issueMessage = true; - CM_FALLTHROUGH; + tgt->GetMakefile()->IssuePolicyWarning( + cmPolicies::CMP0171, {}, "The target name \"codegen\" is reserved.", + tgt->GetBacktrace()); + break; case cmPolicies::OLD: break; case cmPolicies::NEW: - issueMessage = true; - messageType = MessageType::FATAL_ERROR; - break; - } - if (issueMessage) { - e << "The target name \"codegen\" is reserved."; - this->GetCMakeInstance()->IssueMessage(messageType, e.str(), - tgt->GetBacktrace()); - if (messageType == MessageType::FATAL_ERROR) { + this->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, "The target name \"codegen\" is reserved.", + tgt->GetBacktrace()); cmSystemTools::SetFatalErrorOccurred(); - return; - } + break; } } @@ -3423,6 +3556,31 @@ std::string module_languages[] = { "CXX" }; + // Handle config-less builds. + if (configs.empty()) { + std::vector<std::string> all_lang_paths; + for (auto const& lang : module_languages) { + auto comment = cmStrCat("Combining module command databases for ", lang); + auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database_", + lang, ".json"); + mf->GetOrCreateGeneratedSource(output); + AddMergeTarget( + cmStrCat(TargetPrefix, '-', lang), comment.c_str(), output, + [this, lang]() { return this->PerLanguageModuleDbs[lang]; }); + all_lang_paths.emplace_back(std::move(output)); + } + + // Add the overall target. + auto const* comment = "Combining module command databases"; + auto output = + cmStrCat(mf->GetHomeOutputDirectory(), "/build_database.json"); + mf->GetOrCreateGeneratedSource(output); + AddMergeTarget(std::string{ TargetPrefix }, comment, output, + [all_lang_paths]() { return all_lang_paths; }); + + return true; + } + // Add per-configuration targets. for (auto const& config : configs) { // Add per-language targets. @@ -3697,8 +3855,8 @@ if (d.Load(dir)) { unsigned long n = d.GetNumberOfFiles(); for (unsigned long i = 0; i < n; ++i) { - char const* f = d.GetFile(i); - if (strcmp(f, ".") != 0 && strcmp(f, "..") != 0) { + std::string const& f = d.GetFileName(i); + if (f != "." && f != "..") { dc.All.insert(f); } }
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 7e6e4b4..7f941e3 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h
@@ -24,8 +24,10 @@ #include "cmBuildOptions.h" #include "cmCustomCommandLines.h" #include "cmDuration.h" +#include "cmExportFileGenerator.h" #include "cmExportSet.h" #include "cmLocalGenerator.h" +#include "cmSbomBuilder.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -50,6 +52,8 @@ class cmDirectoryId; class cmExportBuildFileGenerator; class cmExternalMakefileProjectGenerator; +class cmBuildSbomGenerator; +class cmInstallSbomGenerator; class cmGeneratorTarget; class cmInstallRuntimeDependencySet; class cmLinkLineComputer; @@ -240,11 +244,7 @@ void ResolveLanguageCompiler(std::string const& lang, cmMakefile* mf, bool optional) const; - /** - * Try to determine system information, get it from another generator - */ - void EnableLanguagesFromGenerator(cmGlobalGenerator* gen, cmMakefile* mf); - + void SetupTryCompile(cmGlobalGenerator* gen, cmMakefile* mf); /** * Try running cmake and building a file. This is used for dynamically * loaded commands, not as part of the usual build process. @@ -369,6 +369,8 @@ std::string GetLanguageOutputExtension(cmSourceFile const&) const; //! What is the object file extension for a given language? std::string GetLanguageOutputExtension(std::string const& lang) const; + //! What is the object file extension for a given --emit option in Rust? + std::string GetRustEmitOutputExtension(std::string const& emitValue) const; //! What is the configurations directory variable called? virtual char const* GetCMakeCFGIntDir() const { return "."; } @@ -633,9 +635,54 @@ { return this->BuildExportSets; } + /** Scan all build-tree exports in the project and report which of them + * reference `target`. Used both by cmExportBuildFileGenerator (to resolve + * out-of-export link references) and by cmSbomBuilder (to record which + * export sets a target appears in for SBOM dependency tracking). */ + cmExportFileGenerator::ExportInfo FindBuildExportInfo( + cmGeneratorTarget const* target) const; + + /** Same as FindBuildExportInfo, but searches install-tree export sets + * (those registered via install(EXPORT ...)). */ + cmExportFileGenerator::ExportInfo FindInstallExportInfo( + cmGeneratorTarget const* target) const; + + /** Scan all build-tree SBOMs and report which of them cover `target`. */ + cmSbomBuilder::SbomInfo FindBuildSbomInfo( + cmGeneratorTarget const* target) const; + + /** Same as FindBuildSbomInfo, but searches install-tree SBOMs. */ + cmSbomBuilder::SbomInfo FindInstallSbomInfo( + cmGeneratorTarget const* target) const; void AddBuildExportSet(cmExportBuildFileGenerator* gen); void AddBuildExportExportSet(cmExportBuildFileGenerator* gen); + void AddBuildSbomGenerator(cmBuildSbomGenerator* gen); + std::vector<cmBuildSbomGenerator*> const& GetBuildSbomGenerators() const + { + return this->BuildSbomGenerators; + } + + // Project-wide registry of install(SBOM) generators. + void AddInstallSbomGenerator(cmInstallSbomGenerator const* gen); + std::vector<cmInstallSbomGenerator const*> const& GetInstallSbomGenerators() + const + { + return this->InstallSbomGenerators; + } + bool IsExportedTargetsFile(std::string const& filename) const; + + /** True if any registered cmBuildSbomGenerator already targets this + * output file path. Used to diagnose duplicate `export(SBOM ...)` + * calls that would otherwise silently clobber each other's output. */ + bool IsBuildSbomFile(std::string const& filepath) const; + + /** True if any registered cmInstallSbomGenerator already targets this + * install file path (DESTINATION + filename). Used to diagnose + * duplicate `install(SBOM ...)` calls that would otherwise silently + * clobber each other at install time. */ + bool IsInstallSbomFile(std::string const& filepath) const; + cmExportBuildFileGenerator* GetExportedTargetsFile( std::string const& filename) const; void AddCMP0068WarnTarget(std::string const& target); @@ -753,7 +800,7 @@ virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; - bool ApplyCXXStdTargets(); + bool ApplyCXXStdTarget(); bool DiscoverSyntheticTargets(); bool AddHeaderSetVerification(); @@ -822,6 +869,8 @@ cmExportSetMap ExportSets; std::map<std::string, cmExportBuildFileGenerator*> BuildExportSets; std::map<std::string, cmExportBuildFileGenerator*> BuildExportExportSets; + std::vector<cmBuildSbomGenerator*> BuildSbomGenerators; + std::vector<cmInstallSbomGenerator const*> InstallSbomGenerators; std::map<std::string, std::string> AliasTargets; @@ -868,13 +917,12 @@ std::map<cmGeneratorTarget const*, size_t> TargetOrderIndex; cmMakefile* TryCompileOuterMakefile; - // If you add a new map here, make sure it is copied - // in EnableLanguagesFromGenerator std::map<std::string, bool> IgnoreExtensions; - std::set<std::string> LanguagesReady; // Ready for try_compile + std::set<std::string> LanguagesReadyForTryCompile; std::set<std::string> LanguagesInProgress; std::map<std::string, std::string> OutputExtensions; std::map<std::string, std::string> LanguageToOutputExtension; + std::map<std::string, std::string> RustEmitToOutputExtension; #if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L std::map<std::string, std::string, std::less<void>> ExtensionToLanguage; #else
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index fe45795..128f77d 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -16,6 +16,7 @@ #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" +#include "cmDiagnostics.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGhsMultiGpj.h" @@ -679,8 +680,8 @@ std::ostringstream msg; msg << "Any pre-check scripts, such as those generated for file(GLOB " "CONFIGURE_DEPENDS), will not be run by gbuild."; - this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, - msg.str()); + this->GetCMakeInstance()->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, + msg.str()); } // Sort the list of input files and remove duplicates.
diff --git a/Source/cmGlobalMinGWMakefileGenerator.cxx b/Source/cmGlobalMinGWMakefileGenerator.cxx index e6687ce..2ff9b93 100644 --- a/Source/cmGlobalMinGWMakefileGenerator.cxx +++ b/Source/cmGlobalMinGWMakefileGenerator.cxx
@@ -2,7 +2,6 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmGlobalMinGWMakefileGenerator.h" -#include "cmMakefile.h" #include "cmState.h" #include "cmSystemTools.h" #include "cmake.h"
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index b145641..e0db27c 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -26,6 +26,7 @@ #include "cmCustomCommand.h" #include "cmCxxModuleMapper.h" +#include "cmDiagnostics.h" #include "cmDyndepCollation.h" #include "cmFortranParser.h" #include "cmGeneratedFileStream.h" @@ -43,6 +44,7 @@ #include "cmOutputConverter.h" #include "cmRange.h" #include "cmScanDepFormat.h" +#include "cmScriptGenerator.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateDirectory.h" @@ -52,6 +54,8 @@ #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetDepend.h" +#include "cmTest.h" +#include "cmTestGenerator.h" #include "cmValue.h" #include "cmVersion.h" #include "cmake.h" @@ -639,6 +643,7 @@ this->cmGlobalGenerator::Generate(); this->WriteAssumedSourceDependencies(); + this->WriteTestPrepTargets(); this->WriteTargetAliases(*this->GetCommonFileStream()); this->WriteFolderTargets(*this->GetCommonFileStream()); this->WriteBuiltinTargets(*this->GetCommonFileStream()); @@ -1291,6 +1296,80 @@ } } +void cmGlobalNinjaGenerator::WriteTestPrepTargets() +{ + auto writeConfig = [this](std::string const& config, std::ostream& os) { + struct TestPrepTarget + { + std::string Comment; + cmNinjaDeps ExplicitDeps; + }; + + std::map<std::string, TestPrepTarget> testPrepTargets; + for (auto const& localGen : this->LocalGenerators) { + auto* lg = static_cast<cmLocalNinjaGenerator*>(localGen.get()); + auto const& testGenerators = lg->GetMakefile()->GetTestGenerators(); + for (auto const& tester : testGenerators) { + cmTestGenerator::BuildDependencies testDeps; + if (!tester->GetBuildDependencies(lg, config, testDeps)) { + continue; + } + std::string const depName = this->ConvertToNinjaPath( + cmStrCat("test_prep/", tester->GetTest()->GetName())); + // Merge with existing target if multiple tests have the same name + auto& testPrepTarget = testPrepTargets[depName]; + testPrepTarget.Comment = cmStrCat("Build dependencies for test ", + tester->GetTest()->GetName()); + + for (cmGeneratorTarget* depTarget : testDeps.Targets) { + this->AppendTargetOutputs(depTarget, testPrepTarget.ExplicitDeps, + config, DependOnTargetArtifact); + } + std::transform(testDeps.Files.begin(), testDeps.Files.end(), + std::back_inserter(testPrepTarget.ExplicitDeps), + this->MapToNinjaPath()); + } + } + + std::vector<std::string> allDeps; + for (auto& prepTarget : testPrepTargets) { + cmNinjaBuild build("phony"); + build.Comment = prepTarget.second.Comment; + build.Outputs.push_back(prepTarget.first); + + // Avoid duplicate dependencies when merging test_prep/ targets + auto& explicitDeps = prepTarget.second.ExplicitDeps; + std::sort(explicitDeps.begin(), explicitDeps.end()); + explicitDeps.erase(std::unique(explicitDeps.begin(), explicitDeps.end()), + explicitDeps.end()); + build.ExplicitDeps = std::move(explicitDeps); + + allDeps.push_back(prepTarget.first); + this->WriteBuild(os, build); + } + + cmNinjaBuild build("phony"); + build.Comment = "Build dependencies for all tests"; + build.Outputs.push_back(this->ConvertToNinjaPath("test_prep/all")); + build.ExplicitDeps = std::move(allDeps); + this->WriteBuild(os, build); + return true; + }; + + if (!this->Makefiles.front()->IsOn("CMAKE_TEST_BUILD_DEPENDS")) { + return; + } + if (this->IsMultiConfig()) { + for (std::string const& config : this->GetConfigNames()) { + if (!writeConfig(config, *this->GetConfigFileStream(config))) { + return; + } + } + } else { + writeConfig(std::string(), *this->GetCommonFileStream()); + } +} + std::string cmGlobalNinjaGenerator::OrderDependsTargetForTarget( cmGeneratorTarget const* target, std::string const& /*config*/) const { @@ -1382,17 +1461,18 @@ std::string const& targetConfig) { if (depTarget->CanCompileSources()) { auto headers = depTarget->GetGeneratedISPCHeaders(targetConfig); + auto const mapToNinjaPath = gg->MapToNinjaPath(); if (!headers.empty()) { std::transform(headers.begin(), headers.end(), headers.begin(), - gg->MapToNinjaPath()); + mapToNinjaPath); outputDeps.insert(outputDeps.end(), headers.begin(), headers.end()); } auto objs = depTarget->GetGeneratedISPCObjects(targetConfig); - if (!objs.empty()) { - std::transform(objs.begin(), objs.end(), objs.begin(), - gg->MapToNinjaPath()); - outputDeps.insert(outputDeps.end(), objs.begin(), objs.end()); - } + std::transform( + objs.begin(), objs.end(), std::back_inserter(outputDeps), + [&mapToNinjaPath]( + std::pair<cmSourceFile const*, std::string> const& obj) + -> std::string { return mapToNinjaPath(obj.second); }); } }; @@ -1925,8 +2005,8 @@ << "\n"; msg << "Any pre-check scripts, such as those generated for file(GLOB " "CONFIGURE_DEPENDS), will not be run by Ninja."; - this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, - msg.str()); + this->GetCMakeInstance()->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, + msg.str()); } std::sort(reBuild.ImplicitDeps.begin(), reBuild.ImplicitDeps.end()); @@ -2030,8 +2110,7 @@ fout << " file(REMOVE_RECURSE\n"; for (std::string const& acf : it->second.AdditionalCleanFiles) { fout << " " - << cmOutputConverter::EscapeForCMake( - this->ConvertToNinjaPath(acf)) + << cmScriptGenerator::Quote(this->ConvertToNinjaPath(acf)) << '\n'; } fout << " )\n"; @@ -2587,8 +2666,8 @@ std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, std::vector<std::string> const& forward_modules_from_target_dirs, - std::string const& arg_lang, std::string const& arg_modmapfmt, - cmCxxModuleExportInfo const& export_info) + std::string const& native_target_dir, std::string const& arg_lang, + std::string const& arg_modmapfmt, cmCxxModuleExportInfo const& export_info) { // Setup path conversions. { @@ -2748,6 +2827,47 @@ } } + // If this is a synthetic target for a non-imported target, read PRIVATE + // module info from the native target + if (!native_target_dir.empty()) { + std::string const modules_info_path = + cmStrCat(native_target_dir, '/', arg_lang, "Modules.json"); + Json::Value native_modules_info; + cmsys::ifstream modules_file(modules_info_path.c_str(), + std::ios::in | std::ios::binary); + if (!modules_file) { + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to open ", + modules_info_path, + " for module information")); + return false; + } + Json::Reader reader; + if (!reader.parse(modules_file, native_modules_info, false)) { + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", + modules_info_path, + reader.getFormattedErrorMessages())); + return false; + } + if (native_modules_info.isObject()) { + Json::Value const& native_target_modules = + native_modules_info["modules"]; + if (native_target_modules.isObject()) { + for (auto i = native_target_modules.begin(); + i != native_target_modules.end(); ++i) { + Json::Value const& visible_module = *i; + if (visible_module.isObject()) { + auto is_private = visible_module["is-private"].asBool(); + // Only add private modules since others are discovered by the + // synthetic target's own scan rules + if (is_private) { + target_modules[i.key().asString()] = visible_module; + } + } + } + } + } + } + cmGeneratedFileStream ddf(arg_dd); ddf << "ninja_dyndep_version = 1.0\n"; @@ -3041,6 +3161,7 @@ tdi_forward_modules_from_target_dir.asString()); } } + std::string const native_target_dir = tdi["native-target-dir"].asString(); std::string const compilerId = tdi["compiler-id"].asString(); std::string const simulateId = tdi["compiler-simulate-id"].asString(); std::string const compilerFrontendVariant = @@ -3064,8 +3185,9 @@ # endif return gg.WriteDyndepFile(dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis, module_dir, linked_target_dirs, - forward_modules_from_target_dirs, arg_lang, - arg_modmapfmt, *export_info) + forward_modules_from_target_dirs, + native_target_dir, arg_lang, arg_modmapfmt, + *export_info) ? 0 : 1; }
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 152c45c..7ade62a 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h
@@ -433,7 +433,8 @@ std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, std::vector<std::string> const& forward_modules_from_target_dirs, - std::string const& arg_lang, std::string const& arg_modmapfmt, + std::string const& native_target_dir, std::string const& arg_lang, + std::string const& arg_modmapfmt, cmCxxModuleExportInfo const& export_info); virtual std::string BuildAlias(std::string const& alias, @@ -523,6 +524,7 @@ void WriteDisclaimer(std::ostream& os) const; void WriteAssumedSourceDependencies(); + void WriteTestPrepTargets(); void WriteTargetAliases(std::ostream& os); void WriteFolderTargets(std::ostream& os);
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index bd0a3e8..5580bd8 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -19,8 +19,6 @@ #include "cmsys/RegularExpression.hxx" #include "cmCryptoHash.h" -#include "cmDocumentationEntry.h" -#include "cmExperimental.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmGlobalVisualStudio7Generator.h" @@ -744,6 +742,16 @@ return empty; } +bool cmGlobalVisualStudio10Generator::IsClangClToolset() const +{ + std::string const& toolset = this->GetPlatformToolsetString(); + static cmsys::RegularExpression llvmToolset( + "^[Ll][Ll][Vv][Mm](_v[0-9]+(_xp)?)?$"); + static cmsys::RegularExpression clangClToolset( + "^[Cc][Ll][Aa][Nn][Gg]([Cc][Ll]$|_[0-9])"); + return llvmToolset.find(toolset) || clangClToolset.find(toolset); +} + std::string const& cmGlobalVisualStudio10Generator::GetPlatformToolsetVersionProps() const {
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index 00124d3..8f312cd 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -71,6 +71,9 @@ char const* GetPlatformToolset() const; std::string const& GetPlatformToolsetString() const; + /** Return true when using a Visual Studio clang-cl toolset. */ + bool IsClangClToolset() const; + /** The toolset version props file, if any. */ std::string const& GetPlatformToolsetVersionProps() const;
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index ec6702b..88c83e6 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -14,7 +14,6 @@ #include <windows.h> #include "cmGeneratedFileStream.h" -#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmList.h" @@ -22,7 +21,6 @@ #include "cmLocalVisualStudio7Generator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h"
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index 1cfb821..7ff23e9 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -360,8 +360,8 @@ { cmStateEnums::TargetType const type = target.GetType(); if (type != cmStateEnums::EXECUTABLE && - type != cmStateEnums::SHARED_LIBRARY) { - // deployment only valid on executables and shared libraries. + type != cmStateEnums::SHARED_LIBRARY && type != cmStateEnums::UTILITY) { + // deployment only valid on executables, shared libraries, and utilities. return false; }
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index f0cca84..bfbb24f 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -23,6 +23,7 @@ #include "cmCallVisualStudioMacro.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" +#include "cmDiagnostics.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmLocalGenerator.h" @@ -300,8 +301,8 @@ if (this->FindTarget(startup)) { return startup; } - root->GetMakefile()->IssueMessage( - MessageType::AUTHOR_WARNING, + root->GetMakefile()->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Directory property VS_STARTUP_PROJECT specifies target " "'", startup, "' that does not exist. Ignoring."));
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index ab76a42..8e5235a 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -777,6 +777,12 @@ bool cmGlobalVisualStudioVersionedGenerator::IsScanDependenciesSupported() const { + if (this->IsClangClToolset()) { + // FIXME(#27977): The ClangCL toolset does not support + // ScanSourceForModuleDependencies yet. + return false; + } + // Supported from Visual Studio 17.6 Preview 7. if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS17) { return true;
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 5ac06eb..0215e34 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -26,8 +26,11 @@ #include "cmCustomCommandGenerator.h" #include "cmCustomCommandLines.h" #include "cmCustomCommandTypes.h" +#include "cmDiagnostics.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorOptions.h" #include "cmGeneratorTarget.h" #include "cmGlobalGeneratorFactory.h" @@ -927,7 +930,6 @@ this->FileRefs.clear(); this->ExternalLibRefs.clear(); this->FileRefToBuildFileMap.clear(); - this->FileRefToEmbedBuildFileMap.clear(); this->CommandsVisited.clear(); } @@ -1091,6 +1093,12 @@ break; } + // lookup for the associated file set, if any. + //// sources are independent of the config but needed here + auto const& config = this->CurrentConfigurationTypes[0]; + auto const* fileSet = + gtgt->GetGeneratorFileSets()->GetFileSetForSource(config, sf); + // Explicitly add the explicit language flag before any other flag // so user flags can override it. gtgt->AddExplicitLanguageFlags(flags, *sf); @@ -1104,6 +1112,15 @@ lg->AppendCompileOptions( flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); } + // Add flags from file set properties. + if (fileSet) { + auto options = fileSet->BelongsTo(gtgt) + ? fileSet->GetCompileOptions(config, lang) + : fileSet->GetInterfaceCompileOptions(config, lang); + if (!options.empty()) { + lg->AppendCompileOptions(flags, cm::remove_BT(options)); + } + } // Add per-source definitions. BuildObjectListOrString flagsBuild(this, false); @@ -1114,8 +1131,18 @@ genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS).c_str(), true); } + // Add file set preprocessor definitions + if (fileSet) { + auto fsDefines = fileSet->BelongsTo(gtgt) + ? fileSet->GetCompileDefinitions(config, lang) + : fileSet->GetInterfaceCompileDefinitions(config, lang); + if (!fsDefines.empty()) { + this->AppendDefines(flagsBuild, cm::remove_BT(fsDefines), true); + } + } - if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) { + if ((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) { this->AppendDefines(flagsBuild, "CMAKE_SKIP_PRECOMPILE_HEADERS", true); } @@ -1126,8 +1153,17 @@ flags += flagsBuild.GetString(); } - // Add per-source include directories. std::vector<std::string> includes; + // Add include directories from file set properties. + if (fileSet) { + auto fsIncludes = fileSet->BelongsTo(gtgt) + ? fileSet->GetIncludeDirectories(config, lang) + : fileSet->GetInterfaceIncludeDirectories(config, lang); + if (!fsIncludes.empty()) { + lg->AppendIncludeDirectories(includes, cm::remove_BT(fsIncludes), *sf); + } + } + // Add per-source include directories. std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); if (cmValue cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) { lg->AppendIncludeDirectories( @@ -2095,7 +2131,8 @@ realDepends.reserve(ccg.GetDepends().size()); for (auto const& d : ccg.GetDepends()) { std::string dep; - if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) { + if (this->CurrentLocalGenerator->GetRealDependency( + d, configName, dep, cc.GetCMP0212Status())) { realDepends.emplace_back(std::move(dep)); } } @@ -2464,7 +2501,8 @@ realDepends.reserve(ccg.GetDepends().size()); for (auto const& d : ccg.GetDepends()) { std::string dep; - if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) { + if (this->CurrentLocalGenerator->GetRealDependency( + d, configName, dep, command.GetCMP0212Status())) { realDepends.emplace_back(std::move(dep)); } } @@ -2678,13 +2716,22 @@ case cmSwiftCompileMode::Singlefile: break; case cmSwiftCompileMode::Unknown: - this->CurrentLocalGenerator->IssueMessage( - MessageType::AUTHOR_WARNING, + this->CurrentLocalGenerator->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Unknown Swift_COMPILATION_MODE on target '", gtgt->GetName(), '\'')); break; } } + + // Add SWIFT_PACKAGE_NAME + if (this->XcodeVersion >= 150) { + std::string const packageName = gtgt->GetSwiftPackageName(); + if (!packageName.empty()) { + buildSettings->AddAttribute("SWIFT_PACKAGE_NAME", + this->CreateString(packageName)); + } + } } std::string extraLinkOptions; @@ -3860,6 +3907,10 @@ if (!IsLinkPhaseLibraryExtension(libExt)) { canUseLinkPhase = false; } + // We can't add non-absolute PBXFileReferences to the link phase + if (!cmSystemTools::FileIsFullPath(libItem.Value.Value)) { + canUseLinkPhase = false; + } } } if (canUseLinkPhase) { @@ -4275,7 +4326,8 @@ } if ((!libName.Target || libName.Target->IsImported()) && (isFramework || isXcFramework || - IsLinkPhaseLibraryExtension(cleanPath))) { + IsLinkPhaseLibraryExtension(cleanPath)) && + cmSystemTools::FileIsFullPath(cleanPath)) { // Create file reference for embedding auto it = this->ExternalLibRefs.find(cleanPath); if (it == this->ExternalLibRefs.end()) { @@ -4376,14 +4428,9 @@ " is missing product reference")); continue; } - auto it = this->FileRefToEmbedBuildFileMap.find(fileRefObject); - if (it == this->FileRefToEmbedBuildFileMap.end()) { - buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile); - buildFile->AddAttribute("fileRef", fileRefObject); - this->FileRefToEmbedBuildFileMap[fileRefObject] = buildFile; - } else { - buildFile = it->second; - } + buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile); + buildFile->SetComment(xcTarget->GetComment()); + buildFile->AddAttribute("fileRef", fileRefObject); } else if (cmSystemTools::IsPathToFramework(relFile) || cmSystemTools::IsPathToMacOSSharedLibrary(relFile) || cmSystemTools::FileIsDirectory(filePath)) {
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 114caec..908dc7b 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h
@@ -381,7 +381,6 @@ std::map<std::string, cmXCodeObject*> ExternalLibRefs; std::map<cmGeneratorTarget const*, cmXCodeObject*> XCodeObjectMap; std::map<cmXCodeObject*, cmXCodeObject*> FileRefToBuildFileMap; - std::map<cmXCodeObject*, cmXCodeObject*> FileRefToEmbedBuildFileMap; std::vector<std::string> Architectures; std::string ObjectDirArchDefault; std::string ObjectDirArch;
diff --git a/Source/cmHexFileConverter.cxx b/Source/cmHexFileConverter.cxx index 3036473..2202a83 100644 --- a/Source/cmHexFileConverter.cxx +++ b/Source/cmHexFileConverter.cxx
@@ -45,6 +45,10 @@ success = false; break; } + if (outBufCount >= sizeof(outBuf)) { + success = false; + break; + } outBuf[outBufCount] = static_cast<char>(convertedByte & 0xff); outBufCount++; }
diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index c8c314c..6102b46 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx
@@ -10,6 +10,7 @@ #include <cmext/string_view> #include "cmConditionEvaluator.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmExpandedCommandArgument.h" #include "cmFunctionBlocker.h" @@ -78,7 +79,7 @@ this->GetStartingContext().FilePath, func.Line() }); if (this->ElseSeen) { - mf.GetCMakeInstance()->IssueMessage( + mf.IssueMessage( MessageType::FATAL_ERROR, "A duplicate ELSE command was found inside an IF block.", elseBT); cmSystemTools::SetFatalErrorOccurred(); @@ -100,17 +101,17 @@ cmListFileContext{ func.OriginalName(), this->GetStartingContext().FilePath, func.Line() }); if (this->ElseSeen) { - mf.GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, - "An ELSEIF command was found after an ELSE command.", elseifBT); + mf.IssueMessage(MessageType::FATAL_ERROR, + "An ELSEIF command was found after an ELSE command.", + elseifBT); cmSystemTools::SetFatalErrorOccurred(); return true; } if (func.Arguments().empty()) { - mf.GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, - "ELSEIF called with no arguments, it will be skipped. ", elseifBT); + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + "ELSEIF called with no arguments, it will be skipped.", elseifBT); } if (this->HasRun) { @@ -137,7 +138,7 @@ if (!errorString.empty()) { std::string err = cmStrCat(cmIfCommandError(expandedArguments), errorString); - mf.GetCMakeInstance()->IssueMessage(messType, err, elseifBT); + mf.IssueMessage(messType, err, elseifBT); if (messType == MessageType::FATAL_ERROR) { cmSystemTools::SetFatalErrorOccurred(); return true;
diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx index e9d9e15..bb2cffa 100644 --- a/Source/cmIncludeCommand.cxx +++ b/Source/cmIncludeCommand.cxx
@@ -5,6 +5,7 @@ #include <map> #include <utility> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" @@ -48,7 +49,8 @@ } bool optional = false; - bool noPolicyScope = false; + cm::PolicyScope policyScope = cm::PolicyScope::Local; + cm::DiagnosticScope diagnosticScope = cm::DiagnosticScope::Local; std::string fname = args[0]; std::string resultVarName; @@ -72,7 +74,9 @@ return false; } } else if (args[i] == "NO_POLICY_SCOPE") { - noPolicyScope = true; + policyScope = cm::PolicyScope::None; + } else if (args[i] == "NO_DIAGNOSTIC_SCOPE") { + diagnosticScope = cm::DiagnosticScope::None; } else if (i > 1) // compat.: in previous cmake versions the second // parameter was ignored if it wasn't "OPTIONAL" { @@ -84,9 +88,8 @@ } if (fname.empty()) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, - "include() given empty file name (ignored)."); + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, "include() given empty file name (ignored)."); return true; } @@ -103,10 +106,7 @@ status.GetMakefile().GetPolicyStatus(ModulePolicy->second); switch (PolicyStatus) { case cmPolicies::WARN: { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(ModulePolicy->second), - '\n')); + status.GetMakefile().IssuePolicyWarning(ModulePolicy->second); CM_FALLTHROUGH; } case cmPolicies::OLD: @@ -161,8 +161,8 @@ } } - bool readit = - status.GetMakefile().ReadDependentFile(listFile, noPolicyScope); + bool const readit = status.GetMakefile().ReadDependentFile( + listFile, policyScope, diagnosticScope); // add the location of the included file if a result variable was given if (!resultVarName.empty()) {
diff --git a/Source/cmInstallAndroidMKExportGenerator.cxx b/Source/cmInstallAndroidMKExportGenerator.cxx index 12b212f..8f3fc21 100644 --- a/Source/cmInstallAndroidMKExportGenerator.cxx +++ b/Source/cmInstallAndroidMKExportGenerator.cxx
@@ -6,9 +6,9 @@ #include <cm/memory> +#include "cmDiagnosticContext.h" #include "cmExportInstallAndroidMKGenerator.h" #include "cmExportInstallFileGenerator.h" -#include "cmListFileCache.h" class cmExportSet; @@ -16,12 +16,12 @@ cmExportSet* exportSet, std::string destination, std::string filePermissions, std::vector<std::string> const& configurations, std::string component, MessageLevel message, bool excludeFromAll, std::string filename, - std::string targetNamespace, cmListFileBacktrace backtrace) + std::string targetNamespace, cmDiagnosticContext context) : cmInstallExportGenerator(exportSet, std::move(destination), std::move(filePermissions), configurations, std::move(component), message, excludeFromAll, std::move(filename), std::move(targetNamespace), - std::string{}, std::move(backtrace)) + std::string{}, std::move(context)) { this->EFGen = cm::make_unique<cmExportInstallAndroidMKGenerator>(this); }
diff --git a/Source/cmInstallAndroidMKExportGenerator.h b/Source/cmInstallAndroidMKExportGenerator.h index 1167a8a..485e6b2 100644 --- a/Source/cmInstallAndroidMKExportGenerator.h +++ b/Source/cmInstallAndroidMKExportGenerator.h
@@ -7,8 +7,8 @@ #include "cmInstallExportGenerator.h" +class cmDiagnosticContext; class cmExportSet; -class cmListFileBacktrace; /** \class cmInstallAndroidMKExportGenerator * \brief Generate rules for creating Android .mk export files. @@ -21,7 +21,7 @@ std::string filePermissions, std::vector<std::string> const& configurations, std::string component, MessageLevel message, bool excludeFromAll, std::string filename, - std::string targetNamespace, cmListFileBacktrace backtrace); + std::string targetNamespace, cmDiagnosticContext context); cmInstallAndroidMKExportGenerator(cmInstallAndroidMKExportGenerator const&) = delete; ~cmInstallAndroidMKExportGenerator() override;
diff --git a/Source/cmInstallCMakeConfigExportGenerator.cxx b/Source/cmInstallCMakeConfigExportGenerator.cxx index 4f5b82e..e023201 100644 --- a/Source/cmInstallCMakeConfigExportGenerator.cxx +++ b/Source/cmInstallCMakeConfigExportGenerator.cxx
@@ -6,9 +6,9 @@ #include <cm/memory> +#include "cmDiagnosticContext.h" #include "cmExportInstallCMakeConfigGenerator.h" #include "cmExportInstallFileGenerator.h" -#include "cmListFileCache.h" class cmExportSet; @@ -17,12 +17,12 @@ std::vector<std::string> const& configurations, std::string component, MessageLevel message, bool excludeFromAll, std::string filename, std::string targetNamespace, std::string cxxModulesDirectory, bool exportOld, - bool exportPackageDependencies, cmListFileBacktrace backtrace) + bool exportPackageDependencies, cmDiagnosticContext context) : cmInstallExportGenerator( exportSet, std::move(destination), std::move(filePermissions), configurations, std::move(component), message, excludeFromAll, std::move(filename), std::move(targetNamespace), - std::move(cxxModulesDirectory), std::move(backtrace)) + std::move(cxxModulesDirectory), std::move(context)) , ExportOld(exportOld) , ExportPackageDependencies(exportPackageDependencies) {
diff --git a/Source/cmInstallCMakeConfigExportGenerator.h b/Source/cmInstallCMakeConfigExportGenerator.h index 6ca5c9a..d655dad 100644 --- a/Source/cmInstallCMakeConfigExportGenerator.h +++ b/Source/cmInstallCMakeConfigExportGenerator.h
@@ -8,8 +8,8 @@ #include "cmInstallExportGenerator.h" +class cmDiagnosticContext; class cmExportSet; -class cmListFileBacktrace; /** \class cmInstallCMakeConfigExportGenerator * \brief Generate rules for creating CMake export files. @@ -24,7 +24,7 @@ MessageLevel message, bool excludeFromAll, std::string filename, std::string targetNamespace, std::string cxxModulesDirectory, bool exportOld, bool exportPackageDependencies, - cmListFileBacktrace backtrace); + cmDiagnosticContext context); cmInstallCMakeConfigExportGenerator( cmInstallCMakeConfigExportGenerator const&) = delete; ~cmInstallCMakeConfigExportGenerator() override;
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 3a6150f..2ddf39a 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx
@@ -8,7 +8,6 @@ #include <iterator> #include <map> #include <set> -#include <sstream> #include <utility> #include <cm/memory> @@ -21,6 +20,8 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" #include "cmCMakePath.h" +#include "cmDiagnosticContext.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmExperimental.h" #include "cmExportSet.h" @@ -31,6 +32,7 @@ #include "cmInstallCommandArguments.h" #include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" +#include "cmInstallDirs.h" #include "cmInstallFileSetGenerator.h" #include "cmInstallFilesGenerator.h" #include "cmInstallGenerator.h" @@ -39,7 +41,7 @@ #include "cmInstallPackageInfoExportGenerator.h" #include "cmInstallRuntimeDependencySet.h" #include "cmInstallRuntimeDependencySetGenerator.h" -#include "cmInstallSbomExportGenerator.h" +#include "cmInstallSbomGenerator.h" #include "cmInstallScriptGenerator.h" #include "cmInstallTargetGenerator.h" #include "cmList.h" @@ -58,8 +60,6 @@ #include "cmTargetExport.h" #include "cmValue.h" -class cmListFileBacktrace; - namespace { struct RuntimeDependenciesArgs @@ -94,11 +94,24 @@ { this->DefaultComponentName = this->Makefile->GetSafeDefinition( "CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"); + if (this->DefaultComponentName == "<PROJECT_NAME>") { + cmValue projectName = this->Makefile->GetDefinition("PROJECT_NAME"); + if (!projectName->empty()) { + this->DefaultComponentName = projectName; + } else { + this->DefaultComponentName = "Unspecified"; + } + } if (this->DefaultComponentName.empty()) { this->DefaultComponentName = "Unspecified"; } } + cmDiagnosticContext CaptureContext() const + { + return cmInstallGenerator::CaptureContext(this->Makefile); + } + void SetError(std::string const& err) { this->Status.SetError(err); } bool MakeFilesFullPath(char const* modeName, @@ -108,12 +121,8 @@ std::vector<std::string> const& relFiles, std::vector<std::string>& absFiles); - std::string GetDestination(cmInstallCommandArguments const* args, - std::string const& varName, - std::string const& guess) const; std::string GetRuntimeDestination( cmInstallCommandArguments const* args) const; - std::string GetSbinDestination(cmInstallCommandArguments const* args) const; std::string GetArchiveDestination( cmInstallCommandArguments const* args) const; std::string GetLibraryDestination( @@ -122,24 +131,6 @@ cmInstallCommandArguments const* args) const; std::string GetIncludeDestination( cmInstallCommandArguments const* args) const; - std::string GetSysconfDestination( - cmInstallCommandArguments const* args) const; - std::string GetSharedStateDestination( - cmInstallCommandArguments const* args) const; - std::string GetLocalStateDestination( - cmInstallCommandArguments const* args) const; - std::string GetRunStateDestination( - cmInstallCommandArguments const* args) const; - std::string GetDataRootDestination( - cmInstallCommandArguments const* args) const; - std::string GetDataDestination(cmInstallCommandArguments const* args) const; - std::string GetInfoDestination(cmInstallCommandArguments const* args) const; - std::string GetLocaleDestination( - cmInstallCommandArguments const* args) const; - std::string GetManDestination(cmInstallCommandArguments const* args) const; - std::string GetDocDestination(cmInstallCommandArguments const* args) const; - std::string GetProgramExecutablesDestination( - cmInstallCommandArguments const* args) const; std::string GetDestinationForType(cmInstallCommandArguments const* args, std::string const& type) const; @@ -150,7 +141,7 @@ std::unique_ptr<cmInstallTargetGenerator> CreateInstallTargetGenerator( cmTarget& target, cmInstallCommandArguments const& args, bool impLib, - cmListFileBacktrace const& backtrace, std::string const& destination, + cmDiagnosticContext context, std::string const& destination, bool forceOpt = false, bool namelink = false) { cmInstallGenerator::MessageLevel message = @@ -161,17 +152,16 @@ auto g = cm::make_unique<cmInstallTargetGenerator>( target.GetName(), destination, impLib, args.GetPermissions(), args.GetConfigurations(), component, message, args.GetExcludeFromAll(), - args.GetOptional() || forceOpt, backtrace); + args.GetOptional() || forceOpt, std::move(context)); target.AddInstallGenerator(g.get()); return g; } std::unique_ptr<cmInstallTargetGenerator> CreateInstallTargetGenerator( cmTarget& target, cmInstallCommandArguments const& args, bool impLib, - cmListFileBacktrace const& backtrace, bool forceOpt = false, - bool namelink = false) + cmDiagnosticContext context, bool forceOpt = false, bool namelink = false) { - return CreateInstallTargetGenerator(target, args, impLib, backtrace, + return CreateInstallTargetGenerator(target, args, impLib, std::move(context), args.GetDestination(), forceOpt, namelink); } @@ -187,7 +177,7 @@ absFiles, destination, programs, args.GetPermissions(), args.GetConfigurations(), args.GetComponent(), message, args.GetExcludeFromAll(), args.GetRename(), args.GetOptional(), - mf->GetBacktrace()); + cmInstallGenerator::CaptureContext(mf)); } std::unique_ptr<cmInstallFilesGenerator> CreateInstallFilesGenerator( @@ -199,16 +189,16 @@ } std::unique_ptr<cmInstallFileSetGenerator> CreateInstallFileSetGenerator( - Helper& helper, cmTarget& target, cmFileSetDestinations dests, + Helper& helper, cmTarget& target, cmInstallCommandFileSetArguments const& args) { cmInstallGenerator::MessageLevel message = cmInstallGenerator::SelectMessageLevel(helper.Makefile); return cm::make_unique<cmInstallFileSetGenerator>( - target.GetName(), args.GetFileSet(), std::move(dests), + target.GetName(), args.GetFileSet(), args.GetDestination(), args.GetPermissions(), args.GetConfigurations(), args.GetComponent(), message, args.GetExcludeFromAll(), args.GetOptional(), - helper.Makefile->GetBacktrace()); + helper.CaptureContext()); } void AddInstallRuntimeDependenciesGenerator( @@ -266,8 +256,7 @@ runtimeDependenciesArgsRef.GetComponent(), runtimeDependenciesArgsRef.GetPermissions(), cmInstallGenerator::SelectMessageLevel(helper.Makefile), - runtimeDependenciesArgsRef.GetExcludeFromAll(), - helper.Makefile->GetBacktrace()); + runtimeDependenciesArgsRef.GetExcludeFromAll(), helper.CaptureContext()); helper.Makefile->AddInstallGenerator( std::move(libraryRuntimeDependenciesGenerator)); if (dllPlatform) { @@ -286,7 +275,7 @@ frameworkArgs.GetDestination(), frameworkArgs.GetConfigurations(), frameworkArgs.GetComponent(), frameworkArgs.GetPermissions(), cmInstallGenerator::SelectMessageLevel(helper.Makefile), - frameworkArgs.GetExcludeFromAll(), helper.Makefile->GetBacktrace()); + frameworkArgs.GetExcludeFromAll(), helper.CaptureContext()); helper.Makefile->AddInstallGenerator( std::move(frameworkRuntimeDependenciesGenerator)); installsFramework = true; @@ -312,6 +301,18 @@ return true; } +void CheckAbsoluteDestination(Helper& helper, std::string const& destination) +{ + // Check for an absolute destination. + if (cmGeneratorExpression::Find(destination) == std::string::npos && + cmSystemTools::FileIsFullPath(destination)) { + helper.Makefile->IssueDiagnostic( + cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION, + cmStrCat("INSTALL command given absolute DESTINATION path:\n ", + destination)); + } +} + bool HandleScriptMode(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -319,10 +320,10 @@ std::string component = helper.DefaultComponentName; int componentCount = 0; - bool doing_script = false; - bool doing_code = false; - bool exclude_from_all = false; - bool all_components = false; + bool doingScript = false; + bool doingCode = false; + bool excludeFromAll = false; + bool allComponents = false; // Scan the args once for COMPONENT. Only allow one. // @@ -333,9 +334,9 @@ component = args[i]; } if (args[i] == "EXCLUDE_FROM_ALL") { - exclude_from_all = true; + excludeFromAll = true; } else if (args[i] == "ALL_COMPONENTS") { - all_components = true; + allComponents = true; } } @@ -346,7 +347,7 @@ return false; } - if (all_components && componentCount == 1) { + if (allComponents && componentCount == 1) { status.SetError("ALL_COMPONENTS and COMPONENT are mutually exclusive"); return false; } @@ -356,16 +357,16 @@ // for (std::string const& arg : args) { if (arg == "SCRIPT") { - doing_script = true; - doing_code = false; + doingScript = true; + doingCode = false; } else if (arg == "CODE") { - doing_script = false; - doing_code = true; + doingScript = false; + doingCode = true; } else if (arg == "COMPONENT") { - doing_script = false; - doing_code = false; - } else if (doing_script) { - doing_script = false; + doingScript = false; + doingCode = false; + } else if (doingScript) { + doingScript = false; std::string script = arg; if (!cmHasLiteralPrefix(script, "$<INSTALL_PREFIX>")) { if (!cmSystemTools::FileIsFullPath(script)) { @@ -379,23 +380,23 @@ } helper.Makefile->AddInstallGenerator( cm::make_unique<cmInstallScriptGenerator>( - script, false, component, exclude_from_all, all_components, + script, false, component, excludeFromAll, allComponents, helper.Makefile->GetBacktrace())); - } else if (doing_code) { - doing_code = false; + } else if (doingCode) { + doingCode = false; std::string const& code = arg; helper.Makefile->AddInstallGenerator( cm::make_unique<cmInstallScriptGenerator>( - code, true, component, exclude_from_all, all_components, + code, true, component, excludeFromAll, allComponents, helper.Makefile->GetBacktrace())); } } - if (doing_script) { + if (doingScript) { status.SetError("given no value for SCRIPT argument."); return false; } - if (doing_code) { + if (doingCode) { status.SetError("given no value for CODE argument."); return false; } @@ -712,11 +713,11 @@ cmTarget* target = helper.Makefile->FindLocalNonAliasTarget(tgt); if (!target) { // If no local target has been found, find it in the global scope. - cmTarget* const global_target = + cmTarget* const globalTarget = helper.Makefile->GetGlobalGenerator()->FindTarget( tgt, { cmStateEnums::TargetDomain::NATIVE }); - if (global_target && !global_target->IsImported()) { - target = global_target; + if (globalTarget && !globalTarget->IsImported()) { + target = globalTarget; } } if (target) { @@ -843,21 +844,21 @@ if (!archiveArgs.GetDestination().empty()) { // The import library uses the ARCHIVE properties. archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, true, helper.Makefile->GetBacktrace()); + target, archiveArgs, true, helper.CaptureContext()); artifactsSpecified = true; } if (!runtimeArgs.GetDestination().empty()) { // The DLL uses the RUNTIME properties. runtimeGenerator = CreateInstallTargetGenerator( - target, runtimeArgs, false, helper.Makefile->GetBacktrace()); + target, runtimeArgs, false, helper.CaptureContext()); artifactsSpecified = true; } if (!archiveGenerator && !runtimeGenerator) { archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, true, helper.Makefile->GetBacktrace(), + target, archiveArgs, true, helper.CaptureContext(), helper.GetArchiveDestination(nullptr)); runtimeGenerator = CreateInstallTargetGenerator( - target, runtimeArgs, false, helper.Makefile->GetBacktrace(), + target, runtimeArgs, false, helper.CaptureContext(), helper.GetRuntimeDestination(nullptr)); } if (runtimeDependencySet && runtimeGenerator) { @@ -880,7 +881,7 @@ // Use the FRAMEWORK properties. if (!frameworkArgs.GetDestination().empty()) { frameworkGenerator = CreateInstallTargetGenerator( - target, frameworkArgs, false, helper.Makefile->GetBacktrace()); + target, frameworkArgs, false, helper.CaptureContext()); } else { status.SetError( cmStrCat("TARGETS given no FRAMEWORK DESTINATION for shared " @@ -895,14 +896,14 @@ } if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) { libraryGenerator = CreateInstallTargetGenerator( - target, libraryArgs, false, helper.Makefile->GetBacktrace(), + target, libraryArgs, false, helper.CaptureContext(), helper.GetLibraryDestination(&libraryArgs)); libraryGenerator->SetNamelinkMode( cmInstallTargetGenerator::NamelinkModeSkip); } if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) { namelinkGenerator = CreateInstallTargetGenerator( - target, libraryArgs, false, helper.Makefile->GetBacktrace(), + target, libraryArgs, false, helper.CaptureContext(), helper.GetLibraryDestination(&libraryArgs), false, true); namelinkGenerator->SetNamelinkMode( cmInstallTargetGenerator::NamelinkModeOnly); @@ -919,7 +920,7 @@ if (importlinkMode != cmInstallTargetGenerator::NamelinkModeOnly) { archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, true, helper.Makefile->GetBacktrace(), + target, archiveArgs, true, helper.CaptureContext(), helper.GetLibraryDestination(&archiveArgs)); archiveGenerator->SetImportlinkMode( cmInstallTargetGenerator::NamelinkModeSkip); @@ -927,7 +928,7 @@ if (importlinkMode != cmInstallTargetGenerator::NamelinkModeSkip) { importlinkGenerator = CreateInstallTargetGenerator( - target, archiveArgs, true, helper.Makefile->GetBacktrace(), + target, archiveArgs, true, helper.CaptureContext(), helper.GetLibraryDestination(&archiveArgs), false, true); importlinkGenerator->SetImportlinkMode( cmInstallTargetGenerator::NamelinkModeOnly); @@ -957,7 +958,7 @@ // Use the FRAMEWORK properties. if (!frameworkArgs.GetDestination().empty()) { frameworkGenerator = CreateInstallTargetGenerator( - target, frameworkArgs, false, helper.Makefile->GetBacktrace()); + target, frameworkArgs, false, helper.CaptureContext()); } else { status.SetError( cmStrCat("TARGETS given no FRAMEWORK DESTINATION for static " @@ -971,7 +972,7 @@ artifactsSpecified = true; } archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, false, helper.Makefile->GetBacktrace(), + target, archiveArgs, false, helper.CaptureContext(), helper.GetArchiveDestination(&archiveArgs)); } } break; @@ -979,7 +980,7 @@ // Modules use LIBRARY properties. if (!libraryArgs.GetDestination().empty()) { libraryGenerator = CreateInstallTargetGenerator( - target, libraryArgs, false, helper.Makefile->GetBacktrace()); + target, libraryArgs, false, helper.CaptureContext()); libraryGenerator->SetNamelinkMode(namelinkMode); namelinkOnly = (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly); @@ -1007,7 +1008,7 @@ } objectGenerator = CreateInstallTargetGenerator( - target, objectArgs, false, helper.Makefile->GetBacktrace()); + target, objectArgs, false, helper.CaptureContext()); } else { // Installing an OBJECT library without a destination transforms // it to an INTERFACE library. It installs no files but can be @@ -1019,7 +1020,7 @@ // Application bundles use the BUNDLE properties. if (!bundleArgs.GetDestination().empty()) { bundleGenerator = CreateInstallTargetGenerator( - target, bundleArgs, false, helper.Makefile->GetBacktrace()); + target, bundleArgs, false, helper.CaptureContext()); } if (!bundleGenerator) { status.SetError(cmStrCat("TARGETS given no BUNDLE DESTINATION for " @@ -1039,7 +1040,7 @@ artifactsSpecified = true; } runtimeGenerator = CreateInstallTargetGenerator( - target, runtimeArgs, false, helper.Makefile->GetBacktrace(), + target, runtimeArgs, false, helper.CaptureContext(), helper.GetRuntimeDestination(&runtimeArgs)); if (runtimeDependencySet) { runtimeDependencySet->AddExecutable(runtimeGenerator.get()); @@ -1055,7 +1056,7 @@ // The import library uses the ARCHIVE properties. artifactsSpecified = true; archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, true, helper.Makefile->GetBacktrace(), true); + target, archiveArgs, true, helper.CaptureContext(), true); } } break; case cmStateEnums::INTERFACE_LIBRARY: @@ -1095,10 +1096,11 @@ helper.Makefile, absFiles, privateHeaderArgs, false, helper.GetIncludeDestination(&privateHeaderArgs)); } else { - std::ostringstream e; - e << "Target " << target.GetName() << " has " - << "PRIVATE_HEADER files but no PRIVATE_HEADER DESTINATION."; - helper.Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + helper.Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("Target ", target.GetName(), + " has PRIVATE_HEADER files" + " but no PRIVATE_HEADER DESTINATION.")); } } @@ -1117,10 +1119,11 @@ helper.Makefile, absFiles, publicHeaderArgs, false, helper.GetIncludeDestination(&publicHeaderArgs)); } else { - std::ostringstream e; - e << "Target " << target.GetName() << " has " - << "PUBLIC_HEADER files but no PUBLIC_HEADER DESTINATION."; - helper.Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + helper.Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("Target ", target.GetName(), + " has PUBLIC_HEADER files" + " but no PUBLIC_HEADER DESTINATION.")); } } @@ -1137,8 +1140,8 @@ resourceGenerator = CreateInstallFilesGenerator( helper.Makefile, absFiles, resourceArgs, false); } else if (!target.IsAppBundleOnApple()) { - helper.Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, + helper.Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Target ", target.GetName(), " has RESOURCE files but no RESOURCE DESTINATION.")); } @@ -1147,11 +1150,8 @@ if (!namelinkOnly) { for (std::size_t i = 0; i < fileSetArgs.size(); i++) { - cmFileSetDestinations dests; - dests.Headers = helper.GetIncludeDestination(&fileSetArgs[i]); - dests.CXXModules = fileSetArgs[i].GetDestination(); - fileSetGenerators.push_back(CreateInstallFileSetGenerator( - helper, target, std::move(dests), fileSetArgs[i])); + fileSetGenerators.push_back( + CreateInstallFileSetGenerator(helper, target, fileSetArgs[i])); installsFileSet[i] = true; } } @@ -1164,7 +1164,7 @@ cxxModuleBmiArgs.GetConfigurations(), cxxModuleBmiArgs.GetComponent(), cmInstallGenerator::SelectMessageLevel(target.GetMakefile()), cxxModuleBmiArgs.GetExcludeFromAll(), cxxModuleBmiArgs.GetOptional(), - helper.Makefile->GetBacktrace()); + helper.CaptureContext()); target.SetHaveInstallRule(true); } @@ -1378,11 +1378,11 @@ cmTarget* target = helper.Makefile->FindTargetToUse(tgt); if (!target || !target->IsImported()) { // If no local target has been found, find it in the global scope. - cmTarget* const global_target = + cmTarget* const globalTarget = helper.Makefile->GetGlobalGenerator()->FindTarget( tgt, { cmStateEnums::TargetDomain::NATIVE }); - if (global_target && global_target->IsImported()) { - target = global_target; + if (globalTarget && globalTarget->IsImported()) { + target = globalTarget; } } if (target) { @@ -1421,7 +1421,7 @@ typeArgs.GetConfigurations(), typeArgs.GetComponent(), cmInstallGenerator::SelectMessageLevel(helper.Makefile), typeArgs.GetExcludeFromAll(), typeArgs.GetOptional(), - helper.Makefile->GetBacktrace()); + helper.CaptureContext()); }; // Generate install script code to install the given targets. @@ -1648,21 +1648,21 @@ DoingType }; Doing doing = DoingDirs; - bool in_match_mode = false; + bool inMatchMode = false; bool optional = false; - bool exclude_from_all = false; - bool message_never = false; + bool excludeFromAll = false; + bool messageNever = false; std::vector<std::string> dirs; cm::optional<std::string> destination; - std::string permissions_file; - std::string permissions_dir; + std::string permissionsFile; + std::string permissionsDir; std::vector<std::string> configurations; std::string component = helper.DefaultComponentName; - std::string literal_args; + std::string literalArgs; std::string type; for (unsigned int i = 1; i < args.size(); ++i) { if (args[i] == "DESTINATION") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; @@ -1671,7 +1671,7 @@ // Switch to setting the destination property. doing = DoingDestination; } else if (args[i] == "TYPE") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; @@ -1680,7 +1680,7 @@ // Switch to setting the type. doing = DoingType; } else if (args[i] == "OPTIONAL") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; @@ -1690,44 +1690,44 @@ optional = true; doing = DoingNone; } else if (args[i] == "MESSAGE_NEVER") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; } // Mark the rule as quiet. - message_never = true; + messageNever = true; doing = DoingNone; } else if (args[i] == "PATTERN") { // Switch to a new pattern match rule. doing = DoingPattern; - in_match_mode = true; + inMatchMode = true; } else if (args[i] == "REGEX") { // Switch to a new regex match rule. doing = DoingRegex; - in_match_mode = true; + inMatchMode = true; } else if (args[i] == "EXCLUDE") { // Add this property to the current match rule. - if (!in_match_mode || doing == DoingPattern || doing == DoingRegex) { + if (!inMatchMode || doing == DoingPattern || doing == DoingRegex) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" before a PATTERN or REGEX is given.")); return false; } - literal_args += " EXCLUDE"; + literalArgs += " EXCLUDE"; doing = DoingNone; } else if (args[i] == "PERMISSIONS") { - if (!in_match_mode) { + if (!inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" before a PATTERN or REGEX is given.")); return false; } // Switch to setting the current match permissions property. - literal_args += " PERMISSIONS"; + literalArgs += " PERMISSIONS"; doing = DoingPermsMatch; } else if (args[i] == "FILE_PERMISSIONS") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; @@ -1736,7 +1736,7 @@ // Switch to setting the file permissions property. doing = DoingPermsFile; } else if (args[i] == "DIRECTORY_PERMISSIONS") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; @@ -1745,27 +1745,27 @@ // Switch to setting the directory permissions property. doing = DoingPermsDir; } else if (args[i] == "USE_SOURCE_PERMISSIONS") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; } // Add this option literally. - literal_args += " USE_SOURCE_PERMISSIONS"; + literalArgs += " USE_SOURCE_PERMISSIONS"; doing = DoingNone; } else if (args[i] == "FILES_MATCHING") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; } // Add this option literally. - literal_args += " FILES_MATCHING"; + literalArgs += " FILES_MATCHING"; doing = DoingNone; } else if (args[i] == "CONFIGURATIONS") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; @@ -1774,7 +1774,7 @@ // Switch to setting the configurations property. doing = DoingConfigurations; } else if (args[i] == "COMPONENT") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; @@ -1783,12 +1783,12 @@ // Switch to setting the component property. doing = DoingComponent; } else if (args[i] == "EXCLUDE_FROM_ALL") { - if (in_match_mode) { + if (inMatchMode) { status.SetError(cmStrCat(args[0], " does not allow \"", args[i], "\" after PATTERN or REGEX.")); return false; } - exclude_from_all = true; + excludeFromAll = true; doing = DoingNone; } else if (doing == DoingDirs) { // If the given directory is not a full path, convert it to one by @@ -1823,9 +1823,7 @@ // generator expressions if (cmGeneratorExpression::Find(args[i]) == cm::string_view::npos && args[i] != cmCMakePath(args[i]).Normal().String()) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0177)); + status.GetMakefile().IssuePolicyWarning(cmPolicies::CMP0177); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -1847,14 +1845,14 @@ // leading slash and trailing end-of-string in the matched // string to make sure the pattern matches only whole file // names. - literal_args += " REGEX \"/"; + literalArgs += " REGEX \"/"; std::string regex = cmsys::Glob::PatternToRegex(args[i], false); cmSystemTools::ReplaceString(regex, "\\", "\\\\"); - literal_args += regex; - literal_args += "$\""; + literalArgs += regex; + literalArgs += "$\""; doing = DoingNone; } else if (doing == DoingRegex) { - literal_args += " REGEX \""; + literalArgs += " REGEX \""; // Match rules are case-insensitive on some platforms. #if defined(_WIN32) || defined(__APPLE__) std::string regex = cmSystemTools::LowerCase(args[i]); @@ -1862,8 +1860,8 @@ std::string regex = args[i]; #endif cmSystemTools::ReplaceString(regex, "\\", "\\\\"); - literal_args += regex; - literal_args += "\""; + literalArgs += regex; + literalArgs += "\""; doing = DoingNone; } else if (doing == DoingComponent) { component = args[i]; @@ -1871,7 +1869,7 @@ } else if (doing == DoingPermsFile) { // Check the requested permission. if (!cmInstallCommandArguments::CheckPermissions(args[i], - permissions_file)) { + permissionsFile)) { status.SetError(cmStrCat(args[0], " given invalid file permission \"", args[i], "\".")); return false; @@ -1879,15 +1877,14 @@ } else if (doing == DoingPermsDir) { // Check the requested permission. if (!cmInstallCommandArguments::CheckPermissions(args[i], - permissions_dir)) { + permissionsDir)) { status.SetError(cmStrCat( args[0], " given invalid directory permission \"", args[i], "\".")); return false; } } else if (doing == DoingPermsMatch) { // Check the requested permission. - if (!cmInstallCommandArguments::CheckPermissions(args[i], - literal_args)) { + if (!cmInstallCommandArguments::CheckPermissions(args[i], literalArgs)) { status.SetError( cmStrCat(args[0], " given invalid permission \"", args[i], "\".")); return false; @@ -1924,14 +1921,17 @@ } cmInstallGenerator::MessageLevel message = - cmInstallGenerator::SelectMessageLevel(helper.Makefile, message_never); + cmInstallGenerator::SelectMessageLevel(helper.Makefile, messageNever); + + // Check for CMD_INSTALL_ABSOLUTE_DESTINATION diagnostics. + CheckAbsoluteDestination(helper, *destination); // Create the directory install generator. helper.Makefile->AddInstallGenerator( cm::make_unique<cmInstallDirectoryGenerator>( - dirs, *destination, permissions_file, permissions_dir, configurations, - component, message, exclude_from_all, literal_args, optional, - helper.Makefile->GetBacktrace())); + dirs, *destination, permissionsFile, permissionsDir, configurations, + component, message, excludeFromAll, literalArgs, optional, + helper.CaptureContext())); // Tell the global generator about any installation component names // specified. @@ -1950,12 +1950,12 @@ cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); std::string exp; - std::string name_space; + std::string exportNamespace; bool exportOld = false; std::string filename; ica.Bind("EXPORT_ANDROID_MK"_s, exp); - ica.Bind("NAMESPACE"_s, name_space); + ica.Bind("NAMESPACE"_s, exportNamespace); ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); ica.Bind("FILE"_s, filename); @@ -1980,6 +1980,9 @@ return false; } + // Check for CMD_INSTALL_ABSOLUTE_DESTINATION diagnostics. + CheckAbsoluteDestination(helper, ica.GetDestination()); + // Check the file name. std::string fname = filename; if (fname.find_first_of(":/\\") != std::string::npos) { @@ -2027,8 +2030,8 @@ cm::make_unique<cmInstallAndroidMKExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), std::move(fname), std::move(name_space), - helper.Makefile->GetBacktrace())); + ica.GetExcludeFromAll(), std::move(fname), std::move(exportNamespace), + helper.CaptureContext())); return true; #else @@ -2148,6 +2151,9 @@ } } + // Check for CMD_INSTALL_ABSOLUTE_DESTINATION diagnostics. + CheckAbsoluteDestination(helper, dest); + if (arguments.Appendix.empty()) { // Get additional export information from variables. GetExportArgumentFromVariable( // BR @@ -2178,7 +2184,7 @@ installCommandArgs.GetConfigurations(), installCommandArgs.GetComponent(), message, installCommandArgs.GetExcludeFromAll(), std::move(arguments), - cxxModulesDirectory, helper.Makefile->GetBacktrace())); + cxxModulesDirectory, helper.CaptureContext())); return true; } @@ -2193,17 +2199,17 @@ cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); std::string exp; - std::string name_space; + std::string exportNamespace; bool exportOld = false; std::string filename; - std::string cxx_modules_directory; + std::string cxxModulesDirectory; bool exportPackageDependencies = false; ica.Bind("EXPORT"_s, exp); - ica.Bind("NAMESPACE"_s, name_space); + ica.Bind("NAMESPACE"_s, exportNamespace); ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); ica.Bind("FILE"_s, filename); - ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory); + ica.Bind("CXX_MODULES_DIRECTORY"_s, cxxModulesDirectory); if (cmExperimental::HasSupportEnabled( status.GetMakefile(), @@ -2293,8 +2299,7 @@ cm::optional<cm::string_view> const directive = MatchExport(pie, exp); if (directive) { if (!HandleMappedPackageInfo(exportSet, *directive, helper, ica, - status, message, - cxx_modules_directory)) { + status, message, cxxModulesDirectory)) { return false; } } @@ -2303,14 +2308,17 @@ } #endif + // Check for CMD_INSTALL_ABSOLUTE_DESTINATION diagnostics. + CheckAbsoluteDestination(helper, ica.GetDestination()); + // Create the export install generator. helper.Makefile->AddInstallGenerator( cm::make_unique<cmInstallCMakeConfigExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), std::move(fname), std::move(name_space), - std::move(cxx_modules_directory), exportOld, exportPackageDependencies, - helper.Makefile->GetBacktrace())); + ica.GetExcludeFromAll(), std::move(fname), std::move(exportNamespace), + std::move(cxxModulesDirectory), exportOld, exportPackageDependencies, + helper.CaptureContext())); return true; } @@ -2363,6 +2371,9 @@ } } + // Check for CMD_INSTALL_ABSOLUTE_DESTINATION diagnostics. + CheckAbsoluteDestination(helper, dest); + cmExportSet& exportSet = helper.Makefile->GetGlobalGenerator()->GetExportSets()[exportName]; @@ -2380,7 +2391,7 @@ &exportSet, dest, ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, ica.GetExcludeFromAll(), std::move(arguments), std::move(cxxModulesDirectory), - helper.Makefile->GetBacktrace())); + helper.CaptureContext())); return true; #else @@ -2520,16 +2531,13 @@ cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); cmSbomArguments arguments; - ArgumentParser::NonEmpty<std::string> exportName; - ArgumentParser::NonEmpty<std::string> cxxModulesDirectory; + ArgumentParser::NonEmpty<std::vector<std::string>> exportNames; arguments.Bind(ica); - ica.Bind("EXPORT"_s, exportName); + ica.Bind("EXPORTS"_s, exportNames); // ica.Bind("CXX_MODULES_DIRECTORY"_s, cxxModulesDirectory); TODO? std::vector<std::string> unknownArgs; - ica.Parse(args, &unknownArgs); - ArgumentParser::ParseResult result = ica.Parse(args, &unknownArgs); if (!result.Check(args[0], &unknownArgs, status)) { return false; @@ -2545,8 +2553,8 @@ return false; } - if (exportName.empty()) { - status.SetError(cmStrCat(args[0], " missing EXPORT.")); + if (exportNames.empty()) { + status.SetError(cmStrCat(args[0], " missing EXPORTS.")); return false; } @@ -2565,25 +2573,42 @@ } } - cmExportSet& exportSet = - helper.Makefile->GetGlobalGenerator()->GetExportSets()[exportName]; + // Check for CMD_INSTALL_ABSOLUTE_DESTINATION diagnostics. + CheckAbsoluteDestination(helper, dest); + + cmGlobalGenerator* gg = helper.Makefile->GetGlobalGenerator(); + + std::string const fpath = + cmStrCat(dest, '/', arguments.GetPackageFileName()); + if (gg->IsInstallSbomFile(fpath)) { + status.SetError(cmStrCat("SBOM command already specified for the file "_s, + cmSystemTools::GetFilenameNameView(fpath), '.')); + return false; + } + + cmExportSetMap& allExportSets = gg->GetExportSets(); + std::vector<cmExportSet*> sets; + sets.reserve(exportNames.size()); + for (std::string const& name : exportNames) { + sets.push_back(&allExportSets[name]); + } cmInstallGenerator::MessageLevel message = cmInstallGenerator::SelectMessageLevel(helper.Makefile); // Tell the global generator about any installation component names - // specified + // specified. helper.Makefile->GetGlobalGenerator()->AddInstallComponent( ica.GetComponent()); - helper.Makefile->SetExplicitlyGeneratesSbom(true); - // Create the export install generator. - helper.Makefile->AddInstallGenerator( - cm::make_unique<cmInstallSbomExportGenerator>( - &exportSet, dest, ica.GetPermissions(), ica.GetConfigurations(), - ica.GetComponent(), message, ica.GetExcludeFromAll(), - std::move(arguments), std::move(cxxModulesDirectory), - helper.Makefile->GetBacktrace())); + // Create the SBOM install generator. + auto sbomGen = cm::make_unique<cmInstallSbomGenerator>( + std::move(sets), dest, ica.GetPermissions(), ica.GetConfigurations(), + ica.GetComponent(), message, ica.GetExcludeFromAll(), std::move(arguments), + helper.CaptureContext()); + cmInstallSbomGenerator const* rawPtr = sbomGen.get(); + helper.Makefile->AddInstallGenerator(std::move(sbomGen)); + helper.Makefile->GetGlobalGenerator()->AddInstallSbomGenerator(rawPtr); return true; #else @@ -2626,42 +2651,31 @@ return true; } -std::string Helper::GetDestination(cmInstallCommandArguments const* args, - std::string const& varName, - std::string const& guess) const +std::string Helper::GetRuntimeDestination( + cmInstallCommandArguments const* args) const { if (args && !args->GetDestination().empty()) { return args->GetDestination(); } - std::string val = this->Makefile->GetSafeDefinition(varName); - if (!val.empty()) { - return val; - } - return guess; -} - -std::string Helper::GetRuntimeDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_BINDIR", "bin"); -} - -std::string Helper::GetSbinDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_SBINDIR", "sbin"); + return cm::InstallDirs::GetRuntimeDirectory(this->Makefile); } std::string Helper::GetArchiveDestination( cmInstallCommandArguments const* args) const { - return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib"); + if (args && !args->GetDestination().empty()) { + return args->GetDestination(); + } + return cm::InstallDirs::GetArchiveDirectory(this->Makefile); } std::string Helper::GetLibraryDestination( cmInstallCommandArguments const* args) const { - return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib"); + if (args && !args->GetDestination().empty()) { + return args->GetDestination(); + } + return cm::InstallDirs::GetLibraryDirectory(this->Makefile); } std::string Helper::GetCxxModulesBmiDestination( @@ -2676,81 +2690,10 @@ std::string Helper::GetIncludeDestination( cmInstallCommandArguments const* args) const { - return this->GetDestination(args, "CMAKE_INSTALL_INCLUDEDIR", "include"); -} - -std::string Helper::GetSysconfDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_SYSCONFDIR", "etc"); -} - -std::string Helper::GetSharedStateDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_SHAREDSTATEDIR", "com"); -} - -std::string Helper::GetLocalStateDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_LOCALSTATEDIR", "var"); -} - -std::string Helper::GetRunStateDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_RUNSTATEDIR", - this->GetLocalStateDestination(nullptr) + - "/run"); -} - -std::string Helper::GetDataRootDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_DATAROOTDIR", "share"); -} - -std::string Helper::GetDataDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_DATADIR", - this->GetDataRootDestination(nullptr)); -} - -std::string Helper::GetInfoDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_INFODIR", - this->GetDataRootDestination(nullptr) + "/info"); -} - -std::string Helper::GetLocaleDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_LOCALEDIR", - this->GetDataRootDestination(nullptr) + - "/locale"); -} - -std::string Helper::GetManDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_MANDIR", - this->GetDataRootDestination(nullptr) + "/man"); -} - -std::string Helper::GetDocDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_DOCDIR", - this->GetDataRootDestination(nullptr) + "/doc"); -} - -std::string Helper::GetProgramExecutablesDestination( - cmInstallCommandArguments const* args) const -{ - return this->GetDestination(args, "CMAKE_INSTALL_LIBEXECDIR", "libexec"); + if (args && !args->GetDestination().empty()) { + return args->GetDestination(); + } + return cm::InstallDirs::GetIncludeDirectory(this->Makefile); } std::string Helper::GetDestinationForType( @@ -2759,49 +2702,7 @@ if (args && !args->GetDestination().empty()) { return args->GetDestination(); } - if (type == "BIN") { - return this->GetRuntimeDestination(nullptr); - } - if (type == "SBIN") { - return this->GetSbinDestination(nullptr); - } - if (type == "SYSCONF") { - return this->GetSysconfDestination(nullptr); - } - if (type == "SHAREDSTATE") { - return this->GetSharedStateDestination(nullptr); - } - if (type == "LOCALSTATE") { - return this->GetLocalStateDestination(nullptr); - } - if (type == "RUNSTATE") { - return this->GetRunStateDestination(nullptr); - } - if (type == "LIB") { - return this->GetLibraryDestination(nullptr); - } - if (type == "INCLUDE") { - return this->GetIncludeDestination(nullptr); - } - if (type == "DATA") { - return this->GetDataDestination(nullptr); - } - if (type == "INFO") { - return this->GetInfoDestination(nullptr); - } - if (type == "LOCALE") { - return this->GetLocaleDestination(nullptr); - } - if (type == "MAN") { - return this->GetManDestination(nullptr); - } - if (type == "DOC") { - return this->GetDocDestination(nullptr); - } - if (type == "LIBEXEC") { - return this->GetProgramExecutablesDestination(nullptr); - } - return ""; + return cm::InstallDirs::GetDirectoryForType(this->Makefile, type); } } // namespace
diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx index 23b38f6..84815ce 100644 --- a/Source/cmInstallCommandArguments.cxx +++ b/Source/cmInstallCommandArguments.cxx
@@ -12,7 +12,6 @@ #include "cmCMakePath.h" #include "cmGeneratorExpression.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmRange.h" #include "cmStringAlgorithms.h" @@ -48,9 +47,7 @@ // generator expressions if (cmGeneratorExpression::Find(arg) == cm::string_view::npos && arg != cmCMakePath(arg).Normal().String()) { - makefile.IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0177)); + makefile.IssuePolicyWarning(cmPolicies::CMP0177); } return ArgumentParser::Continue::No; }; @@ -100,11 +97,7 @@ if (this->GenericArguments) { return this->GenericArguments->GetComponent(); } - if (!this->DefaultComponentName.empty()) { - return this->DefaultComponentName; - } - static std::string unspecifiedComponent = "Unspecified"; - return unspecifiedComponent; + return this->DefaultComponentName; } std::string const& cmInstallCommandArguments::GetNamelinkComponent() const
diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 7bf861e..c8b1ad9 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h
@@ -22,7 +22,7 @@ this->GenericArguments = args; } - // Compute destination path.and check permissions + // Compute destination path and check permissions. bool Finalize(); std::string const& GetDestination() const;
diff --git a/Source/cmInstallCxxModuleBmiGenerator.cxx b/Source/cmInstallCxxModuleBmiGenerator.cxx index 5f7ea52..c11c3f5 100644 --- a/Source/cmInstallCxxModuleBmiGenerator.cxx +++ b/Source/cmInstallCxxModuleBmiGenerator.cxx
@@ -5,24 +5,23 @@ #include <ostream> #include <utility> +#include "cmDiagnosticContext.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" -#include "cmListFileCache.h" #include "cmLocalGenerator.h" -#include "cmOutputConverter.h" #include "cmScriptGenerator.h" #include "cmStringAlgorithms.h" cmInstallCxxModuleBmiGenerator::cmInstallCxxModuleBmiGenerator( - std::string target, std::string const& dest, std::string file_permissions, + std::string target, std::string const& dest, std::string filePermissions, std::vector<std::string> const& configurations, std::string const& component, - MessageLevel message, bool exclude_from_all, bool optional, - cmListFileBacktrace backtrace) + MessageLevel message, bool excludeFromAll, bool optional, + cmDiagnosticContext context) : cmInstallGenerator(dest, configurations, component, message, - exclude_from_all, false, std::move(backtrace)) + excludeFromAll, false, std::move(context)) , TargetName(std::move(target)) - , FilePermissions(std::move(file_permissions)) + , FilePermissions(std::move(filePermissions)) , Optional(optional) { this->ActionsPerConfig = true; @@ -47,19 +46,22 @@ std::string cmInstallCxxModuleBmiGenerator::GetScriptLocation( std::string const& config) const { - char const* config_name = config.c_str(); + char const* configName = config.c_str(); if (config.empty()) { - config_name = "noconfig"; + configName = "noconfig"; } + return cmStrCat(this->Target->GetCMFSupportDirectory(), - "/install-cxx-module-bmi-", config_name, ".cmake"); + "/install-cxx-module-bmi-", configName, ".cmake"); } std::string cmInstallCxxModuleBmiGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate(this->Destination, - this->LocalGenerator, config); + std::string dest = cmGeneratorExpression::Evaluate( + this->Destination, this->LocalGenerator, config); + this->CheckAbsoluteDestination(dest, this->LocalGenerator); + return dest; } void cmInstallCxxModuleBmiGenerator::GenerateScriptForConfig( @@ -69,8 +71,6 @@ if (loc.empty()) { return; } - os << indent << "include(\"" - << cmOutputConverter::EscapeForCMake( - loc, cmOutputConverter::WrapQuotes::NoWrap) - << "\" OPTIONAL)\n"; + os << indent << "include(" << cmScriptGenerator::Quote(loc) + << " OPTIONAL)\n"; }
diff --git a/Source/cmInstallCxxModuleBmiGenerator.h b/Source/cmInstallCxxModuleBmiGenerator.h index b3b5d37..cb34266 100644 --- a/Source/cmInstallCxxModuleBmiGenerator.h +++ b/Source/cmInstallCxxModuleBmiGenerator.h
@@ -10,8 +10,8 @@ #include "cmInstallGenerator.h" +class cmDiagnosticContext; class cmGeneratorTarget; -class cmListFileBacktrace; class cmLocalGenerator; /** \class cmInstallCxxModuleBmiGenerator @@ -21,10 +21,10 @@ { public: cmInstallCxxModuleBmiGenerator( - std::string target, std::string const& dest, std::string file_permissions, + std::string target, std::string const& dest, std::string filePermissions, std::vector<std::string> const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - bool optional, cmListFileBacktrace backtrace); + std::string const& component, MessageLevel message, bool excludeFromAll, + bool optional, cmDiagnosticContext context); ~cmInstallCxxModuleBmiGenerator() override; bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx index cf1fac9..ea43e5b 100644 --- a/Source/cmInstallDirectoryGenerator.cxx +++ b/Source/cmInstallDirectoryGenerator.cxx
@@ -5,10 +5,10 @@ #include <algorithm> #include <utility> +#include "cmDiagnosticContext.h" #include "cmGeneratorExpression.h" #include "cmInstallType.h" #include "cmList.h" -#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" @@ -16,16 +16,16 @@ cmInstallDirectoryGenerator::cmInstallDirectoryGenerator( std::vector<std::string> const& dirs, std::string const& dest, - std::string file_permissions, std::string dir_permissions, + std::string filePermissions, std::string dirPermissions, std::vector<std::string> const& configurations, std::string const& component, - MessageLevel message, bool exclude_from_all, std::string literal_args, - bool optional, cmListFileBacktrace backtrace) + MessageLevel message, bool excludeFromAll, std::string literalArgs, + bool optional, cmDiagnosticContext context) : cmInstallGenerator(dest, configurations, component, message, - exclude_from_all, false, std::move(backtrace)) + excludeFromAll, false, std::move(context)) , Directories(dirs) - , FilePermissions(std::move(file_permissions)) - , DirPermissions(std::move(dir_permissions)) - , LiteralArguments(std::move(literal_args)) + , FilePermissions(std::move(filePermissions)) + , DirPermissions(std::move(dirPermissions)) + , LiteralArguments(std::move(literalArgs)) , Optional(optional) { // We need per-config actions if destination have generator expressions. @@ -107,17 +107,19 @@ std::vector<std::string> const& dirs) { // Write code to install the directories. - char const* no_rename = nullptr; + char const* noRename = nullptr; this->AddInstallRule(os, this->GetDestination(config), cmInstallType_DIRECTORY, dirs, this->Optional, this->FilePermissions.c_str(), - this->DirPermissions.c_str(), no_rename, + this->DirPermissions.c_str(), noRename, this->LiteralArguments.c_str(), indent); } std::string cmInstallDirectoryGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate(this->Destination, - this->LocalGenerator, config); + std::string dest = cmGeneratorExpression::Evaluate( + this->Destination, this->LocalGenerator, config); + this->CheckAbsoluteDestination(dest, this->LocalGenerator); + return dest; }
diff --git a/Source/cmInstallDirectoryGenerator.h b/Source/cmInstallDirectoryGenerator.h index b02824a..7f85748 100644 --- a/Source/cmInstallDirectoryGenerator.h +++ b/Source/cmInstallDirectoryGenerator.h
@@ -10,7 +10,7 @@ #include "cmInstallGenerator.h" -class cmListFileBacktrace; +class cmDiagnosticContext; class cmLocalGenerator; /** \class cmInstallDirectoryGenerator @@ -21,10 +21,10 @@ public: cmInstallDirectoryGenerator( std::vector<std::string> const& dirs, std::string const& dest, - std::string file_permissions, std::string dir_permissions, + std::string filePermissions, std::string dirPermissions, std::vector<std::string> const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - std::string literal_args, bool optional, cmListFileBacktrace backtrace); + std::string const& component, MessageLevel message, bool excludeFromAll, + std::string literalArgs, bool optional, cmDiagnosticContext context); ~cmInstallDirectoryGenerator() override; bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallDirs.cxx b/Source/cmInstallDirs.cxx new file mode 100644 index 0000000..0c17bc2 --- /dev/null +++ b/Source/cmInstallDirs.cxx
@@ -0,0 +1,160 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmInstallDirs.h" + +#include <cmext/string_view> + +#include "cmMakefile.h" +#include "cmStringAlgorithms.h" +#include "cmValue.h" + +namespace { +std::string GetDirectory(cmMakefile const* makefile, + std::string const& varName, std::string const& guess) +{ + cmValue value = makefile->GetDefinition(varName); + if (!value.IsEmpty()) { + return value; + } + return guess; +} +} // namespace + +namespace cm { +namespace InstallDirs { +std::string GetRuntimeDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_BINDIR", "bin"); +} + +std::string GetSbinDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_SBINDIR", "sbin"); +} + +std::string GetArchiveDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_LIBDIR", "lib"); +} + +std::string GetLibraryDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_LIBDIR", "lib"); +} + +std::string GetIncludeDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_INCLUDEDIR", "include"); +} + +std::string GetSysconfDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_SYSCONFDIR", "etc"); +} + +std::string GetSharedStateDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_SHAREDSTATEDIR", "com"); +} + +std::string GetLocalStateDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_LOCALSTATEDIR", "var"); +} + +std::string GetRunStateDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_RUNSTATEDIR", + cmStrCat(GetLocalStateDirectory(makefile), "/run")); +} + +std::string GetDataRootDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_DATAROOTDIR", "share"); +} + +std::string GetDataDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_DATADIR", + GetDataRootDirectory(makefile)); +} + +std::string GetInfoDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_INFODIR", + cmStrCat(GetDataRootDirectory(makefile), "/info")); +} + +std::string GetLocaleDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_LOCALEDIR", + cmStrCat(GetDataRootDirectory(makefile), "/locale")); +} + +std::string GetManDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_MANDIR", + cmStrCat(GetDataRootDirectory(makefile), "/man")); +} + +std::string GetDocDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_DOCDIR", + cmStrCat(GetDataRootDirectory(makefile), "/doc")); +} + +std::string GetLibExecDirectory(cmMakefile const* makefile) +{ + return GetDirectory(makefile, "CMAKE_INSTALL_LIBEXECDIR", "libexec"); +} + +std::string GetDirectoryForType(cmMakefile const* makefile, + cm::string_view type) +{ + if (type == "BIN"_s) { + return GetRuntimeDirectory(makefile); + } + if (type == "SBIN"_s) { + return GetSbinDirectory(makefile); + } + if (type == "SYSCONF"_s) { + return GetSysconfDirectory(makefile); + } + if (type == "SHAREDSTATE"_s) { + return GetSharedStateDirectory(makefile); + } + if (type == "LOCALSTATE"_s) { + return GetLocalStateDirectory(makefile); + } + if (type == "RUNSTATE"_s) { + return GetRunStateDirectory(makefile); + } + if (type == "LIB"_s) { + return GetLibraryDirectory(makefile); + } + if (type == "INCLUDE"_s) { + return GetIncludeDirectory(makefile); + } + if (type == "DATA"_s) { + return GetDataDirectory(makefile); + } + if (type == "INFO"_s) { + return GetInfoDirectory(makefile); + } + if (type == "LOCALE"_s) { + return GetLocaleDirectory(makefile); + } + if (type == "MAN"_s) { + return GetManDirectory(makefile); + } + if (type == "DOC"_s) { + return GetDocDirectory(makefile); + } + if (type == "LIBEXEC"_s) { + return GetLibExecDirectory(makefile); + } + return std::string{}; +} + +} // namespace InstallDirs +} // namespace cm
diff --git a/Source/cmInstallDirs.h b/Source/cmInstallDirs.h new file mode 100644 index 0000000..73cc564 --- /dev/null +++ b/Source/cmInstallDirs.h
@@ -0,0 +1,34 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +#pragma once + +#include <string> + +#include <cm/string_view> + +class cmMakefile; + +namespace cm { + +namespace InstallDirs { +std::string GetRuntimeDirectory(cmMakefile const*); +std::string GetSbinDirectory(cmMakefile const*); +std::string GetArchiveDirectory(cmMakefile const*); +std::string GetLibraryDirectory(cmMakefile const*); +std::string GetIncludeDirectory(cmMakefile const*); +std::string GetSysconfDirectory(cmMakefile const*); +std::string GetSharedStateDirectory(cmMakefile const*); +std::string GetLocalStateDirectory(cmMakefile const*); +std::string GetRunStateDirectory(cmMakefile const*); +std::string GetDataRootDirectory(cmMakefile const*); +std::string GetDataDirectory(cmMakefile const*); +std::string GetInfoDirectory(cmMakefile const*); +std::string GetLocaleDirectory(cmMakefile const*); +std::string GetManDirectory(cmMakefile const*); +std::string GetDocDirectory(cmMakefile const*); +std::string GetLibExecDirectory(cmMakefile const*); + +std::string GetDirectoryForType(cmMakefile const*, cm::string_view type); +} +}
diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index 8886439..007694e 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx
@@ -7,10 +7,10 @@ #include <utility> #include "cmCryptoHash.h" +#include "cmDiagnosticContext.h" #include "cmExportInstallFileGenerator.h" #include "cmExportSet.h" #include "cmInstallType.h" -#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmScriptGenerator.h" #include "cmStringAlgorithms.h" @@ -21,10 +21,10 @@ std::vector<std::string> const& configurations, std::string component, MessageLevel message, bool excludeFromAll, std::string filename, std::string targetNamespace, std::string cxxModulesDirectory, - cmListFileBacktrace backtrace) + cmDiagnosticContext context) : cmInstallGenerator(std::move(destination), configurations, std::move(component), message, excludeFromAll, false, - std::move(backtrace)) + std::move(context)) , ExportSet(exportSet) , FilePermissions(std::move(filePermissions)) , FileName(std::move(filename)) @@ -122,8 +122,8 @@ std::vector<std::string> files; for (auto const& i : this->EFGen->GetConfigImportFiles()) { files.push_back(i.second); - std::string config_test = this->CreateConfigTest(i.first); - os << indent << "if(" << config_test << ")\n"; + std::string configTest = this->CreateConfigTest(i.first); + os << indent << "if(" << configTest << ")\n"; this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files, false, this->FilePermissions.c_str(), nullptr, nullptr, nullptr, indent.Next()); @@ -182,8 +182,8 @@ } for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) { files.push_back(i.second); - std::string config_test = this->CreateConfigTest(i.first); - os << indent << "if(" << config_test << ")\n"; + std::string configTest = this->CreateConfigTest(i.first); + os << indent << "if(" << configTest << ")\n"; this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files, false, this->FilePermissions.c_str(), nullptr, nullptr, nullptr, indent.Next()); @@ -191,8 +191,8 @@ files.clear(); } for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) { - std::string config_test = this->CreateConfigTest(i.first); - os << indent << "if(" << config_test << ")\n"; + std::string configTest = this->CreateConfigTest(i.first); + os << indent << "if(" << configTest << ")\n"; this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, i.second, false, this->FilePermissions.c_str(), nullptr, nullptr, nullptr, indent.Next());
diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index eb2d5dd..167cf10 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h
@@ -11,9 +11,9 @@ #include "cmInstallGenerator.h" +class cmDiagnosticContext; class cmExportInstallFileGenerator; class cmExportSet; -class cmListFileBacktrace; class cmLocalGenerator; /** \class cmInstallExportGenerator @@ -29,7 +29,7 @@ bool excludeFromAll, std::string filename, std::string targetNamespace, std::string cxxModulesDirectory, - cmListFileBacktrace backtrace); + cmDiagnosticContext context); cmInstallExportGenerator(cmInstallExportGenerator const&) = delete; ~cmInstallExportGenerator() override;
diff --git a/Source/cmInstallFileSetGenerator.cxx b/Source/cmInstallFileSetGenerator.cxx index 7763f43..250f207 100644 --- a/Source/cmInstallFileSetGenerator.cxx +++ b/Source/cmInstallFileSetGenerator.cxx
@@ -4,37 +4,39 @@ #include <algorithm> #include <map> +#include <memory> #include <string> #include <utility> #include <vector> #include <cm/string_view> -#include <cmext/string_view> -#include "cmFileSet.h" +#include "cmDiagnosticContext.h" +#include "cmFileSetMetadata.h" #include "cmGenExContext.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" +#include "cmInstallDirs.h" #include "cmInstallType.h" #include "cmList.h" -#include "cmListFileCache.h" #include "cmLocalGenerator.h" +#include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" cmInstallFileSetGenerator::cmInstallFileSetGenerator( - std::string targetName, std::string fileSetName, cmFileSetDestinations dests, - std::string file_permissions, std::vector<std::string> const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - bool optional, cmListFileBacktrace backtrace) - : cmInstallGenerator("", configurations, component, message, - exclude_from_all, false, std::move(backtrace)) + std::string targetName, std::string fileSetName, std::string destination, + std::string filePermissions, std::vector<std::string> const& configurations, + std::string const& component, MessageLevel message, bool excludeFromAll, + bool optional, cmDiagnosticContext context) + : cmInstallGenerator(std::move(destination), configurations, component, + message, excludeFromAll, false, std::move(context)) , TargetName(std::move(targetName)) , FileSetName(std::move(fileSetName)) - , FilePermissions(std::move(file_permissions)) - , FileSetDestinations(std::move(dests)) + , FilePermissions(std::move(filePermissions)) , Optional(optional) { this->ActionsPerConfig = true; @@ -54,8 +56,7 @@ lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName); } - auto const& target = *this->Target->Target; - this->FileSet = target.GetFileSet(this->FileSetName); + this->FileSet = this->Target->GetFileSet(this->FileSetName); if (!this->FileSet) { // No file set of the given name was ever provided for this target, nothing @@ -63,15 +64,17 @@ return true; } + auto const& target = *this->Target->Target; cmList interfaceFileSetEntries{ target.GetSafeProperty( - cmTarget::GetInterfaceFileSetsPropertyName(this->FileSet->GetType())) }; + target.GetInterfaceFileSetsPropertyName(this->FileSet->GetType())) }; if (std::find(interfaceFileSetEntries.begin(), interfaceFileSetEntries.end(), this->FileSetName) != interfaceFileSetEntries.end()) { - if (this->FileSet->GetType() == "HEADERS"_s) { - this->Destination = this->FileSetDestinations.Headers; - } else { - this->Destination = this->FileSetDestinations.CXXModules; + if (this->FileSet->GetType() == cm::FileSetMetadata::HEADERS) { + if (this->Destination.empty()) { + this->Destination = + cm::InstallDirs::GetIncludeDirectory(lg->GetMakefile()); + } } } else { // File set of the given name was provided but it's private, so give up @@ -90,11 +93,30 @@ return true; } +std::string cmInstallFileSetGenerator::GetDestination() const +{ + this->CheckAbsoluteDestination(this->Destination, this->LocalGenerator); + return this->Destination; +} + std::string cmInstallFileSetGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate(this->Destination, - this->LocalGenerator, config); + return this->GetDestination(this->Target, config).UnescapedDestination; +} + +cmInstallFileSetGenerator::DestinationContext +cmInstallFileSetGenerator::GetDestination(cmGeneratorTarget* gte, + std::string const& config) const +{ + cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(this->Destination); + + std::string const dest = cge->Evaluate(gte->LocalGenerator, config, gte); + this->CheckAbsoluteDestination(dest, gte->LocalGenerator); + + return { dest, cge->GetHadContextSensitiveCondition() }; } void cmInstallFileSetGenerator::GenerateScriptForConfig( @@ -121,19 +143,7 @@ cmInstallFileSetGenerator::CalculateFilesPerDir( std::string const& config) const { - std::map<std::string, std::vector<std::string>> result; - cm::GenEx::Context context(this->LocalGenerator, config); - auto dirCges = this->FileSet->CompileDirectoryEntries(); - auto dirs = - this->FileSet->EvaluateDirectoryEntries(dirCges, context, this->Target); - - auto fileCges = this->FileSet->CompileFileEntries(); - for (auto const& fileCge : fileCges) { - this->FileSet->EvaluateFileEntry(dirs, result, fileCge, context, - this->Target); - } - - return result; + return this->FileSet->GetFiles(context, this->Target).first; }
diff --git a/Source/cmInstallFileSetGenerator.h b/Source/cmInstallFileSetGenerator.h index ba30414..72b9682 100644 --- a/Source/cmInstallFileSetGenerator.h +++ b/Source/cmInstallFileSetGenerator.h
@@ -9,49 +9,52 @@ #include "cmInstallGenerator.h" +class cmDiagnosticContext; +class cmExportInstallCMakeConfigGenerator; +class cmGeneratorFileSet; class cmGeneratorTarget; -class cmFileSet; -class cmListFileBacktrace; class cmLocalGenerator; -struct cmFileSetDestinations -{ - std::string Headers; - std::string CXXModules; -}; - class cmInstallFileSetGenerator : public cmInstallGenerator { public: cmInstallFileSetGenerator(std::string targetName, std::string fileSetName, - cmFileSetDestinations dests, - std::string file_permissions, + std::string destination, + std::string filePermissions, std::vector<std::string> const& configurations, std::string const& component, MessageLevel message, - bool exclude_from_all, bool optional, - cmListFileBacktrace backtrace); + bool excludeFromAll, bool optional, + cmDiagnosticContext context); ~cmInstallFileSetGenerator() override; bool Compute(cmLocalGenerator* lg) override; + struct DestinationContext + { + std::string UnescapedDestination; + bool HadContextSensitiveCondition; + }; std::string GetDestination(std::string const& config) const; - std::string GetDestination() const { return this->Destination; } + DestinationContext GetDestination(cmGeneratorTarget* gt, + std::string const& config) const; bool GetOptional() const { return this->Optional; } std::string GetFileSetName() const { return this->FileSetName; } - cmFileSet const* GetFileSet() const { return this->FileSet; }; + cmGeneratorFileSet const* GetFileSet() const { return this->FileSet; }; cmGeneratorTarget* GetTarget() const { return this->Target; } protected: + friend cmExportInstallCMakeConfigGenerator; + std::string GetDestination() const; + void GenerateScriptForConfig(std::ostream& os, std::string const& config, Indent indent) override; private: std::string TargetName; cmLocalGenerator* LocalGenerator; - cmFileSet const* FileSet; + cmGeneratorFileSet const* FileSet; std::string const FileSetName; std::string const FilePermissions; - cmFileSetDestinations FileSetDestinations; bool const Optional; cmGeneratorTarget* Target;
diff --git a/Source/cmInstallFilesCommand.cxx b/Source/cmInstallFilesCommand.cxx index 8490f69..7e2fb69 100644 --- a/Source/cmInstallFilesCommand.cxx +++ b/Source/cmInstallFilesCommand.cxx
@@ -125,7 +125,8 @@ cmInstallGenerator::SelectMessageLevel(&makefile); makefile.AddInstallGenerator(cm::make_unique<cmInstallFilesGenerator>( files, destination, false, no_permissions, no_configurations, no_component, - message, no_exclude_from_all, no_rename, false, makefile.GetBacktrace())); + message, no_exclude_from_all, no_rename, false, + cmInstallGenerator::CaptureContext(makefile))); } /**
diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx index 6da1862..387914a 100644 --- a/Source/cmInstallFilesGenerator.cxx +++ b/Source/cmInstallFilesGenerator.cxx
@@ -4,21 +4,21 @@ #include <utility> +#include "cmDiagnosticContext.h" #include "cmGeneratorExpression.h" #include "cmInstallType.h" #include "cmList.h" -#include "cmListFileCache.h" cmInstallFilesGenerator::cmInstallFilesGenerator( std::vector<std::string> const& files, std::string const& dest, - bool programs, std::string file_permissions, + bool programs, std::string filePermissions, std::vector<std::string> const& configurations, std::string const& component, - MessageLevel message, bool exclude_from_all, std::string rename, - bool optional, cmListFileBacktrace backtrace) + MessageLevel message, bool excludeFromAll, std::string rename, bool optional, + cmDiagnosticContext context) : cmInstallGenerator(dest, configurations, component, message, - exclude_from_all, false, std::move(backtrace)) + excludeFromAll, false, std::move(context)) , Files(files) - , FilePermissions(std::move(file_permissions)) + , FilePermissions(std::move(filePermissions)) , Rename(std::move(rename)) , Programs(programs) , Optional(optional) @@ -54,8 +54,10 @@ std::string cmInstallFilesGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate(this->Destination, - this->LocalGenerator, config); + std::string dest = cmGeneratorExpression::Evaluate( + this->Destination, this->LocalGenerator, config); + this->CheckAbsoluteDestination(dest, this->LocalGenerator); + return dest; } std::string cmInstallFilesGenerator::GetRename(std::string const& config) const @@ -83,11 +85,11 @@ std::vector<std::string> const& files) { // Write code to install the files. - char const* no_dir_permissions = nullptr; + char const* noDirPermissions = nullptr; this->AddInstallRule( os, this->GetDestination(config), (this->Programs ? cmInstallType_PROGRAMS : cmInstallType_FILES), files, - this->Optional, this->FilePermissions.c_str(), no_dir_permissions, + this->Optional, this->FilePermissions.c_str(), noDirPermissions, this->GetRename(config).c_str(), nullptr, indent); }
diff --git a/Source/cmInstallFilesGenerator.h b/Source/cmInstallFilesGenerator.h index 5ab2f50..5f61384 100644 --- a/Source/cmInstallFilesGenerator.h +++ b/Source/cmInstallFilesGenerator.h
@@ -10,7 +10,7 @@ #include "cmInstallGenerator.h" -class cmListFileBacktrace; +class cmDiagnosticContext; class cmLocalGenerator; /** \class cmInstallFilesGenerator @@ -21,11 +21,11 @@ public: cmInstallFilesGenerator(std::vector<std::string> const& files, std::string const& dest, bool programs, - std::string file_permissions, + std::string filePermissions, std::vector<std::string> const& configurations, std::string const& component, MessageLevel message, - bool exclude_from_all, std::string rename, - bool optional, cmListFileBacktrace backtrace); + bool excludeFromAll, std::string rename, + bool optional, cmDiagnosticContext context); ~cmInstallFilesGenerator() override; bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx index cc9fd8a..912e474 100644 --- a/Source/cmInstallGenerator.cxx +++ b/Source/cmInstallGenerator.cxx
@@ -7,21 +7,23 @@ #include <cm/string_view> +#include "cmDiagnostics.h" +#include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" cmInstallGenerator::cmInstallGenerator( std::string destination, std::vector<std::string> const& configurations, - std::string component, MessageLevel message, bool exclude_from_all, - bool all_components, cmListFileBacktrace backtrace) + std::string component, MessageLevel message, bool excludeFromAll, + bool allComponents, cmDiagnosticContext context) : cmScriptGenerator("CMAKE_INSTALL_CONFIG_NAME", configurations) , Destination(std::move(destination)) , Component(std::move(component)) , Message(message) - , ExcludeFromAll(exclude_from_all) - , AllComponents(all_components) - , Backtrace(std::move(backtrace)) + , ExcludeFromAll(excludeFromAll) + , AllComponents(allComponents) + , Context{ std::move(context) } { } @@ -43,10 +45,10 @@ void cmInstallGenerator::AddInstallRule( std::ostream& os, std::string const& dest, cmInstallType type, std::vector<std::string> const& files, bool optional /* = false */, - char const* permissions_file /* = nullptr */, - char const* permissions_dir /* = nullptr */, - char const* rename /* = nullptr */, char const* literal_args /* = nullptr */, - Indent indent, char const* files_var /* = nullptr */) + char const* permissionsFile /* = nullptr */, + char const* permissionsDir /* = nullptr */, + char const* rename /* = nullptr */, char const* literalArgs /* = nullptr */, + Indent indent, char const* filesVar /* = nullptr */) { // Use the FILE command to install the file. std::string stype; @@ -92,9 +94,8 @@ } os << "\")\n"; } - if (files_var) { - os << indent << "foreach(_cmake_abs_file IN LISTS " << files_var - << ")\n"; + if (filesVar) { + os << indent << "foreach(_cmake_abs_file IN LISTS " << filesVar << ")\n"; os << indent.Next() << "get_filename_component(_cmake_abs_file_name " "\"${_cmake_abs_file}\" NAME)\n"; @@ -136,11 +137,11 @@ os << " MESSAGE_NEVER"; break; } - if (permissions_file && *permissions_file) { - os << " PERMISSIONS" << permissions_file; + if (permissionsFile && *permissionsFile) { + os << " PERMISSIONS" << permissionsFile; } - if (permissions_dir && *permissions_dir) { - os << " DIR_PERMISSIONS" << permissions_dir; + if (permissionsDir && *permissionsDir) { + os << " DIR_PERMISSIONS" << permissionsDir; } if (rename && *rename) { os << " RENAME \"" << rename << "\""; @@ -152,25 +153,25 @@ for (std::string const& f : files) { os << "\n" << indent << " \"" << f << "\""; } - if (files_var) { - os << " ${" << files_var << "}"; + if (filesVar) { + os << " ${" << filesVar << "}"; } os << "\n" << indent << " "; - if (!(literal_args && *literal_args)) { + if (!(literalArgs && *literalArgs)) { os << " "; } } - if (literal_args && *literal_args) { - os << literal_args; + if (literalArgs && *literalArgs) { + os << literalArgs; } os << ")\n"; } std::string cmInstallGenerator::CreateComponentTest( - std::string const& component, bool exclude_from_all, bool all_components) + std::string const& component, bool excludeFromAll, bool allComponents) { - if (all_components) { - if (exclude_from_all) { + if (allComponents) { + if (excludeFromAll) { return "CMAKE_INSTALL_COMPONENT"; } return {}; @@ -179,7 +180,7 @@ std::string result = "CMAKE_INSTALL_COMPONENT STREQUAL \""; result += component; result += "\""; - if (!exclude_from_all) { + if (!excludeFromAll) { result += " OR NOT CMAKE_INSTALL_COMPONENT"; } @@ -191,12 +192,12 @@ // Track indentation. Indent indent; - std::string component_test = this->CreateComponentTest( + std::string componentTest = this->CreateComponentTest( this->Component, this->ExcludeFromAll, this->AllComponents); // Begin this block of installation. - if (!component_test.empty()) { - os << indent << "if(" << component_test << ")\n"; + if (!componentTest.empty()) { + os << indent << "if(" << componentTest << ")\n"; } // Generate the script possibly with per-configuration code. @@ -204,7 +205,7 @@ this->AllComponents ? indent : indent.Next()); // End this block of installation. - if (!component_test.empty()) { + if (!componentTest.empty()) { os << indent << "endif()\n\n"; } } @@ -217,6 +218,10 @@ std::string cmInstallGenerator::ConvertToAbsoluteDestination( std::string const& dest) { + if (dest == ".") { + return "${CMAKE_INSTALL_PREFIX}"; + } + std::string result; if (!dest.empty() && !cmSystemTools::FileIsFullPath(dest)) { result = "${CMAKE_INSTALL_PREFIX}/"; @@ -225,6 +230,18 @@ return result; } +void cmInstallGenerator::CheckAbsoluteDestination(std::string const& dest, + cmLocalGenerator* lg) const +{ + if (!cmSystemTools::FileIsFullPath(dest)) { + return; + } + lg->IssueDiagnostic( + cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION, + cmStrCat("INSTALL command given absolute DESTINATION path:\n ", dest), + this->Context); +} + cmInstallGenerator::MessageLevel cmInstallGenerator::SelectMessageLevel( cmMakefile* mf, bool never) { @@ -244,6 +261,15 @@ return MessageDefault; } +cmDiagnosticContext cmInstallGenerator::CaptureContext(cmMakefile const& mf) +{ + cmDiagnosticContext context{ mf.GetBacktrace() }; + context.RecordDiagnostic(cmDiagnostics::CMD_AUTHOR, mf.GetStateSnapshot()); + context.RecordDiagnostic(cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION, + mf.GetStateSnapshot()); + return context; +} + std::string cmInstallGenerator::GetDestDirPath(std::string const& file) { // Construct the path of the file on disk after installation on
diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h index 572d192..e8f2cd3 100644 --- a/Source/cmInstallGenerator.h +++ b/Source/cmInstallGenerator.h
@@ -9,12 +9,13 @@ #include <string> #include <vector> +#include "cmDiagnosticContext.h" #include "cmInstallType.h" -#include "cmListFileCache.h" #include "cmScriptGenerator.h" class cmLocalGenerator; class cmMakefile; +class cmListFileBacktrace; /** \class cmInstallGenerator * \brief Support class for generating install scripts. @@ -34,8 +35,8 @@ cmInstallGenerator(std::string destination, std::vector<std::string> const& configurations, std::string component, MessageLevel message, - bool exclude_from_all, bool all_components, - cmListFileBacktrace backtrace); + bool excludeFromAll, bool allComponents, + cmDiagnosticContext context); ~cmInstallGenerator() override; cmInstallGenerator(cmInstallGenerator const&) = delete; @@ -48,14 +49,16 @@ void AddInstallRule( std::ostream& os, std::string const& dest, cmInstallType type, std::vector<std::string> const& files, bool optional = false, - char const* permissions_file = nullptr, - char const* permissions_dir = nullptr, char const* rename = nullptr, - char const* literal_args = nullptr, Indent indent = Indent(), - char const* files_var = nullptr); + char const* permissionsFile = nullptr, + char const* permissionsDir = nullptr, char const* rename = nullptr, + char const* literalArgs = nullptr, Indent indent = Indent(), + char const* filesVar = nullptr); /** Get the install destination as it should appear in the installation script. */ static std::string ConvertToAbsoluteDestination(std::string const& dest); + void CheckAbsoluteDestination(std::string const& dest, + cmLocalGenerator* lg) const; /** Test if this generator installs something for a given configuration. */ bool InstallsForConfig(std::string const& config); @@ -63,6 +66,13 @@ /** Select message level from CMAKE_INSTALL_MESSAGE or 'never'. */ static MessageLevel SelectMessageLevel(cmMakefile* mf, bool never = false); + /** Capture context for generator. */ + static cmDiagnosticContext CaptureContext(cmMakefile const& mf); + static cmDiagnosticContext CaptureContext(cmMakefile const* mf) + { + return cmInstallGenerator::CaptureContext(*mf); + } + virtual bool Compute(cmLocalGenerator*) { return true; } std::string const& GetComponent() const { return this->Component; } @@ -70,7 +80,15 @@ bool GetExcludeFromAll() const { return this->ExcludeFromAll; } bool GetAllComponentsFlag() const { return this->AllComponents; } - cmListFileBacktrace const& GetBacktrace() const { return this->Backtrace; } + cmListFileBacktrace const& GetBacktrace() const + { + return this->Context.GetBacktrace(); + } + + cmDiagnosticContext const& GetDiagnosticContext() const + { + return this->Context; + } static std::string GetDestDirPath(std::string const& file); @@ -78,8 +96,8 @@ void GenerateScript(std::ostream& os) override; std::string CreateComponentTest(std::string const& component, - bool exclude_from_all, - bool all_components = false); + bool excludeFromAll, + bool allComponents = false); using TweakMethod = std::function<void(std::ostream& os, Indent indent, @@ -98,5 +116,5 @@ MessageLevel const Message; bool const ExcludeFromAll; bool const AllComponents; - cmListFileBacktrace const Backtrace; + cmDiagnosticContext const Context; };
diff --git a/Source/cmInstallGetRuntimeDependenciesGenerator.cxx b/Source/cmInstallGetRuntimeDependenciesGenerator.cxx index 4292f8f..c4e16f4 100644 --- a/Source/cmInstallGetRuntimeDependenciesGenerator.cxx +++ b/Source/cmInstallGetRuntimeDependenciesGenerator.cxx
@@ -13,6 +13,7 @@ #include <cm/string_view> #include <cmext/string_view> +#include "cmDiagnosticContext.h" #include "cmGeneratorExpression.h" #include "cmInstallRuntimeDependencySet.h" #include "cmListFileCache.h" @@ -84,10 +85,10 @@ std::vector<std::string> postExcludeFiles, std::string libraryComponent, std::string frameworkComponent, bool noInstallRPath, char const* depsVar, char const* rpathPrefix, std::vector<std::string> const& configurations, - MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace, + MessageLevel message, bool excludeFromAll, cmListFileBacktrace backtrace, cmPolicies::PolicyStatus policyStatusCMP0207) - : cmInstallGenerator("", configurations, "", message, exclude_from_all, - false, std::move(backtrace)) + : cmInstallGenerator("", configurations, "", message, excludeFromAll, false, + cmDiagnosticContext{ std::move(backtrace) }) , RuntimeDependencySet(runtimeDependencySet) , Directories(std::move(directories)) , PreIncludeRegexes(std::move(preIncludeRegexes))
diff --git a/Source/cmInstallGetRuntimeDependenciesGenerator.h b/Source/cmInstallGetRuntimeDependenciesGenerator.h index 9670dcf..413d2b8 100644 --- a/Source/cmInstallGetRuntimeDependenciesGenerator.h +++ b/Source/cmInstallGetRuntimeDependenciesGenerator.h
@@ -27,7 +27,7 @@ std::vector<std::string> postExcludeFiles, std::string libraryComponent, std::string frameworkComponent, bool noInstallRPath, char const* depsVar, char const* rpathPrefix, std::vector<std::string> const& configurations, - MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace, + MessageLevel message, bool excludeFromAll, cmListFileBacktrace backtrace, cmPolicies::PolicyStatus policyStatusCMP0207); bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallImportedRuntimeArtifactsGenerator.cxx b/Source/cmInstallImportedRuntimeArtifactsGenerator.cxx index 9552748..2461181 100644 --- a/Source/cmInstallImportedRuntimeArtifactsGenerator.cxx +++ b/Source/cmInstallImportedRuntimeArtifactsGenerator.cxx
@@ -13,7 +13,6 @@ #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmInstallType.h" -#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -32,14 +31,14 @@ cmInstallImportedRuntimeArtifactsGenerator:: cmInstallImportedRuntimeArtifactsGenerator( std::string targetName, std::string const& dest, - std::string file_permissions, + std::string filePermissions, std::vector<std::string> const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - bool optional, cmListFileBacktrace backtrace) + std::string const& component, MessageLevel message, bool excludeFromAll, + bool optional, cmDiagnosticContext context) : cmInstallGenerator(dest, configurations, component, message, - exclude_from_all, false, std::move(backtrace)) + excludeFromAll, false, std::move(context)) , TargetName(std::move(targetName)) - , FilePermissions(std::move(file_permissions)) + , FilePermissions(std::move(filePermissions)) , Optional(optional) { this->ActionsPerConfig = true; @@ -61,8 +60,10 @@ std::string cmInstallImportedRuntimeArtifactsGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate( + std::string dest = cmGeneratorExpression::Evaluate( this->Destination, this->Target->GetLocalGenerator(), config); + this->CheckAbsoluteDestination(dest, this->Target->GetLocalGenerator()); + return dest; } void cmInstallImportedRuntimeArtifactsGenerator::GenerateScriptForConfig(
diff --git a/Source/cmInstallImportedRuntimeArtifactsGenerator.h b/Source/cmInstallImportedRuntimeArtifactsGenerator.h index 90c13a8..2920567 100644 --- a/Source/cmInstallImportedRuntimeArtifactsGenerator.h +++ b/Source/cmInstallImportedRuntimeArtifactsGenerator.h
@@ -6,8 +6,8 @@ #include <string> #include <vector> +#include "cmDiagnosticContext.h" #include "cmInstallGenerator.h" -#include "cmListFileCache.h" class cmGeneratorTarget; @@ -16,10 +16,10 @@ public: cmInstallImportedRuntimeArtifactsGenerator( std::string targetName, std::string const& dest, - std::string file_permissions, + std::string filePermissions, std::vector<std::string> const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - bool optional, cmListFileBacktrace backtrace = cmListFileBacktrace()); + std::string const& component, MessageLevel message, bool excludeFromAll, + bool optional, cmDiagnosticContext context = {}); ~cmInstallImportedRuntimeArtifactsGenerator() override = default; bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallPackageInfoExportGenerator.cxx b/Source/cmInstallPackageInfoExportGenerator.cxx index 55dcbd1..fe7b77f 100644 --- a/Source/cmInstallPackageInfoExportGenerator.cxx +++ b/Source/cmInstallPackageInfoExportGenerator.cxx
@@ -6,9 +6,9 @@ #include <cm/memory> +#include "cmDiagnosticContext.h" #include "cmExportInstallFileGenerator.h" #include "cmExportInstallPackageInfoGenerator.h" -#include "cmListFileCache.h" #include "cmPackageInfoArguments.h" class cmExportSet; @@ -17,12 +17,12 @@ cmExportSet* exportSet, std::string destination, std::string filePermissions, std::vector<std::string> const& configurations, std::string component, MessageLevel message, bool excludeFromAll, cmPackageInfoArguments arguments, - std::string cxxModulesDirectory, cmListFileBacktrace backtrace) + std::string cxxModulesDirectory, cmDiagnosticContext context) : cmInstallExportGenerator( exportSet, std::move(destination), std::move(filePermissions), configurations, std::move(component), message, excludeFromAll, arguments.GetPackageFileName(), arguments.GetNamespace(), - std::move(cxxModulesDirectory), std::move(backtrace)) + std::move(cxxModulesDirectory), std::move(context)) { this->EFGen = cm::make_unique<cmExportInstallPackageInfoGenerator>( this, std::move(arguments));
diff --git a/Source/cmInstallPackageInfoExportGenerator.h b/Source/cmInstallPackageInfoExportGenerator.h index 0209f82..b323705 100644 --- a/Source/cmInstallPackageInfoExportGenerator.h +++ b/Source/cmInstallPackageInfoExportGenerator.h
@@ -7,8 +7,8 @@ #include "cmInstallExportGenerator.h" +class cmDiagnosticContext; class cmExportSet; -class cmListFileBacktrace; class cmPackageInfoArguments; /** \class cmInstallPackageInfoGenerator @@ -23,7 +23,7 @@ std::vector<std::string> const& configurations, std::string component, MessageLevel message, bool excludeFromAll, cmPackageInfoArguments arguments, std::string cxxModulesDirectory, - cmListFileBacktrace backtrace); + cmDiagnosticContext context); cmInstallPackageInfoExportGenerator( cmInstallPackageInfoExportGenerator const&) = delete; ~cmInstallPackageInfoExportGenerator() override;
diff --git a/Source/cmInstallProgramsCommand.cxx b/Source/cmInstallProgramsCommand.cxx index 63f40e2..474bbc4 100644 --- a/Source/cmInstallProgramsCommand.cxx +++ b/Source/cmInstallProgramsCommand.cxx
@@ -48,18 +48,18 @@ static void FinalAction(cmMakefile& makefile, std::string const& dest, std::vector<std::string> const& args) { - bool files_mode = false; + bool filesMode = false; if (!args.empty() && args[0] == "FILES") { - files_mode = true; + filesMode = true; } std::vector<std::string> files; // two different options - if (args.size() > 1 || files_mode) { + if (args.size() > 1 || filesMode) { // for each argument, get the programs auto s = args.begin(); - if (files_mode) { + if (filesMode) { // Skip the FILES argument in files mode. ++s; } @@ -99,7 +99,8 @@ cmInstallGenerator::SelectMessageLevel(&makefile); makefile.AddInstallGenerator(cm::make_unique<cmInstallFilesGenerator>( files, destination, true, no_permissions, no_configurations, no_component, - message, no_exclude_from_all, no_rename, false, makefile.GetBacktrace())); + message, no_exclude_from_all, no_rename, false, + cmInstallGenerator::CaptureContext(makefile))); } /**
diff --git a/Source/cmInstallRuntimeDependencySetGenerator.cxx b/Source/cmInstallRuntimeDependencySetGenerator.cxx index 0f9197a..2798678 100644 --- a/Source/cmInstallRuntimeDependencySetGenerator.cxx +++ b/Source/cmInstallRuntimeDependencySetGenerator.cxx
@@ -7,14 +7,13 @@ #include <utility> #include <vector> +#include "cmDiagnosticContext.h" #include "cmGeneratorExpression.h" #include "cmInstallGenerator.h" #include "cmInstallType.h" -#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" #include "cmScriptGenerator.h" #include "cmStringAlgorithms.h" #include "cmake.h" @@ -25,11 +24,11 @@ std::string installNameDir, bool noInstallName, char const* depsVar, char const* rpathPrefix, char const* tmpVarPrefix, std::string destination, std::vector<std::string> const& configurations, std::string component, - std::string permissions, MessageLevel message, bool exclude_from_all, - cmListFileBacktrace backtrace) + std::string permissions, MessageLevel message, bool excludeFromAll, + cmDiagnosticContext context) : cmInstallGenerator(std::move(destination), configurations, - std::move(component), message, exclude_from_all, false, - std::move(backtrace)) + std::move(component), message, excludeFromAll, false, + std::move(context)) , Type(type) , DependencySet(dependencySet) , InstallRPaths(std::move(installRPaths)) @@ -69,7 +68,7 @@ MessageType::FATAL_ERROR, "INSTALL_NAME_DIR argument must not evaluate to an " "empty string", - this->Backtrace); + this->Context.GetBacktrace()); return; } if (installNameDir.back() != '/') { @@ -142,7 +141,7 @@ << GetDestDirPath( ConvertToAbsoluteDestination(this->GetDestination(config))) << "/${" << this->TmpVarPrefix << "_dep_name}\" NEW_RPATH " - << cmOutputConverter::EscapeForCMake(evaluatedRPath) << ")\n"; + << cmScriptGenerator::Quote(evaluatedRPath) << ")\n"; } } } @@ -229,8 +228,8 @@ << "\" ${" << this->TmpVarPrefix << "_rpath_args}\n"; if (!this->NoInstallRPath) { for (auto const& rpath : evaluatedRPaths) { - os << indent2 << " -add_rpath " - << cmOutputConverter::EscapeForCMake(rpath) << "\n"; + os << indent2 << " -add_rpath " << cmScriptGenerator::Quote(rpath) + << '\n'; } } if (!this->NoInstallName) { @@ -270,6 +269,8 @@ std::string cmInstallRuntimeDependencySetGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate(this->Destination, - this->LocalGenerator, config); + std::string dest = cmGeneratorExpression::Evaluate( + this->Destination, this->LocalGenerator, config); + this->CheckAbsoluteDestination(dest, this->LocalGenerator); + return dest; }
diff --git a/Source/cmInstallRuntimeDependencySetGenerator.h b/Source/cmInstallRuntimeDependencySetGenerator.h index f8a7a4f..ba2f74d 100644 --- a/Source/cmInstallRuntimeDependencySetGenerator.h +++ b/Source/cmInstallRuntimeDependencySetGenerator.h
@@ -8,8 +8,8 @@ #include "cmInstallGenerator.h" +class cmDiagnosticContext; class cmInstallRuntimeDependencySet; -class cmListFileBacktrace; class cmLocalGenerator; class cmInstallRuntimeDependencySetGenerator : public cmInstallGenerator @@ -27,8 +27,8 @@ std::string installNameDir, bool noInstallName, char const* depsVar, char const* rpathPrefix, char const* tmpVarPrefix, std::string destination, std::vector<std::string> const& configurations, std::string component, - std::string permissions, MessageLevel message, bool exclude_from_all, - cmListFileBacktrace backtrace); + std::string permissions, MessageLevel message, bool excludeFromAll, + cmDiagnosticContext context); bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallSbomBuilder.cxx b/Source/cmInstallSbomBuilder.cxx new file mode 100644 index 0000000..5561ff5 --- /dev/null +++ b/Source/cmInstallSbomBuilder.cxx
@@ -0,0 +1,42 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmInstallSbomBuilder.h" + +#include <utility> + +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSbomArguments.h" + +cmInstallSbomBuilder::cmInstallSbomBuilder( + cmSbomArguments args, std::vector<cmExportSet*> exportSets, + cmLocalGenerator* lg) + : cmSbomBuilder(std::move(args), std::move(exportSets), lg) +{ +} + +bool cmInstallSbomBuilder::Generate(std::ostream& os) +{ + if (!this->LocalGenerator) { + return false; + } + return this->GenerateForTargets(os, cmGeneratorExpression::InstallInterface); +} + +cmExportFileGenerator::ExportInfo cmInstallSbomBuilder::FindExportInfoFor( + cmGeneratorTarget const* target) const +{ + return target->GetLocalGenerator() + ->GetGlobalGenerator() + ->FindInstallExportInfo(target); +} + +cmSbomBuilder::SbomInfo cmInstallSbomBuilder::FindSbomInfoFor( + cmGeneratorTarget const* target) const +{ + return target->GetLocalGenerator() + ->GetGlobalGenerator() + ->FindInstallSbomInfo(target); +}
diff --git a/Source/cmInstallSbomBuilder.h b/Source/cmInstallSbomBuilder.h new file mode 100644 index 0000000..cb436c7 --- /dev/null +++ b/Source/cmInstallSbomBuilder.h
@@ -0,0 +1,32 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <vector> + +#include "cmExportFileGenerator.h" +#include "cmSbomBuilder.h" + +class cmExportSet; +class cmLocalGenerator; +class cmSbomArguments; + +/** Install-tree SBOM (`install(SBOM ...)`). Covers the targets in the + * associated export sets. */ +class cmInstallSbomBuilder final : public cmSbomBuilder +{ +public: + cmInstallSbomBuilder(cmSbomArguments args, + std::vector<cmExportSet*> exportSets, + cmLocalGenerator* lg = nullptr); + + bool Generate(std::ostream& os) override; + +protected: + cmExportFileGenerator::ExportInfo FindExportInfoFor( + cmGeneratorTarget const* target) const override; + SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const override; +};
diff --git a/Source/cmInstallSbomExportGenerator.cxx b/Source/cmInstallSbomExportGenerator.cxx deleted file mode 100644 index 01af7ed..0000000 --- a/Source/cmInstallSbomExportGenerator.cxx +++ /dev/null
@@ -1,30 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying -file LICENSE.rst or https://cmake.org/licensing for details. */ -#include "cmInstallSbomExportGenerator.h" - -#include <utility> - -#include <cm/memory> - -#include "cmExportInstallFileGenerator.h" -#include "cmExportInstallSbomGenerator.h" -#include "cmListFileCache.h" -#include "cmSbomArguments.h" - -class cmExportSet; - -cmInstallSbomExportGenerator::cmInstallSbomExportGenerator( - cmExportSet* exportSet, std::string destination, std::string filePermissions, - std::vector<std::string> const& configurations, std::string component, - MessageLevel message, bool excludeFromAll, cmSbomArguments args, - std::string cxxModulesDirectory, cmListFileBacktrace backtrace) - : cmInstallExportGenerator( - exportSet, std::move(destination), std::move(filePermissions), - configurations, std::move(component), message, excludeFromAll, - args.GetPackageFileName(), args.GetNamespace(), - std::move(cxxModulesDirectory), std::move(backtrace)) -{ - this->EFGen = cm::make_unique<cmExportInstallSbomGenerator>(this, args); -} - -cmInstallSbomExportGenerator::~cmInstallSbomExportGenerator() = default;
diff --git a/Source/cmInstallSbomExportGenerator.h b/Source/cmInstallSbomExportGenerator.h deleted file mode 100644 index 23d7318..0000000 --- a/Source/cmInstallSbomExportGenerator.h +++ /dev/null
@@ -1,31 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#pragma once - -#include <string> -#include <vector> - -#include "cmInstallExportGenerator.h" - -class cmExportSet; -class cmListFileBacktrace; -class cmSbomArguments; - -class cmInstallSbomExportGenerator final : public cmInstallExportGenerator -{ -public: - cmInstallSbomExportGenerator(cmExportSet* exportSet, std::string destination, - std::string filePermissions, - std::vector<std::string> const& configurations, - std::string component, MessageLevel message, - bool excludeFromAll, cmSbomArguments arguments, - std::string cxxModulesDirectory, - cmListFileBacktrace backtrace); - cmInstallSbomExportGenerator(cmInstallSbomExportGenerator const&) = delete; - ~cmInstallSbomExportGenerator() override; - - cmInstallSbomExportGenerator& operator=( - cmInstallSbomExportGenerator const&) = delete; - - char const* InstallSubcommand() const override { return "SBOM"; } -};
diff --git a/Source/cmInstallSbomGenerator.cxx b/Source/cmInstallSbomGenerator.cxx new file mode 100644 index 0000000..0224d06 --- /dev/null +++ b/Source/cmInstallSbomGenerator.cxx
@@ -0,0 +1,88 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmInstallSbomGenerator.h" + +#include <utility> +#include <vector> + +#include <cm/memory> + +#include "cmCryptoHash.h" +#include "cmDiagnosticContext.h" +#include "cmGeneratedFileStream.h" +#include "cmInstallSbomBuilder.h" +#include "cmInstallType.h" +#include "cmLocalGenerator.h" +#include "cmSbomArguments.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +cmInstallSbomGenerator::cmInstallSbomGenerator( + std::vector<cmExportSet*> exportSets, std::string destination, + std::string filePermissions, std::vector<std::string> const& configurations, + std::string component, MessageLevel message, bool excludeFromAll, + cmSbomArguments args, cmDiagnosticContext context) + : cmInstallGenerator(std::move(destination), configurations, + std::move(component), message, excludeFromAll, false, + std::move(context)) + , FilePermissions(std::move(filePermissions)) + , SbomFileName(args.GetPackageFileName()) + , SbomFilePath(cmStrCat(this->Destination, '/', this->SbomFileName)) + , Builder(cm::make_unique<cmInstallSbomBuilder>(std::move(args), + std::move(exportSets))) +{ +} + +cmInstallSbomGenerator::~cmInstallSbomGenerator() = default; + +bool cmInstallSbomGenerator::Compute(cmLocalGenerator* lg) +{ + this->LocalGenerator = lg; + this->Builder->Compute(lg); + return true; +} + +bool cmInstallSbomGenerator::CoversTarget( + cmGeneratorTarget const* target) const +{ + return this->Builder->CoversTarget(target); +} + +std::string const& cmInstallSbomGenerator::GetPackageName() const +{ + return this->Builder->GetPackageName(); +} + +bool cmInstallSbomGenerator::CoversExportSet(cmExportSet const* set) const +{ + return this->Builder->CoversExportSet(set); +} + +void cmInstallSbomGenerator::GenerateScript(std::ostream& os) +{ + // Choose a temporary directory in the build tree to hold the generated SBOM. + cmCryptoHash hasher(cmCryptoHash::AlgoMD5); + std::string const tempDir = + cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), + "/CMakeFiles/Sbom/", hasher.HashString(this->Destination)); + + cmSystemTools::MakeDirectory(tempDir); + + this->TempSbomFilePath = cmStrCat(tempDir, '/', this->SbomFileName); + + // Generate the SBOM file now, at cmake generate time. + cmGeneratedFileStream sbomStream(this->TempSbomFilePath); + this->Builder->Generate(sbomStream); + + // Emit the cmake_install.cmake script to copy the file at install time. + this->cmInstallGenerator::GenerateScript(os); +} + +void cmInstallSbomGenerator::GenerateScriptActions(std::ostream& os, + Indent indent) +{ + std::vector<std::string> files{ this->TempSbomFilePath }; + this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), nullptr, nullptr, + nullptr, indent); +}
diff --git a/Source/cmInstallSbomGenerator.h b/Source/cmInstallSbomGenerator.h new file mode 100644 index 0000000..fd8dcde --- /dev/null +++ b/Source/cmInstallSbomGenerator.h
@@ -0,0 +1,68 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <memory> +#include <string> +#include <vector> + +#include "cmInstallGenerator.h" + +class cmDiagnosticContext; +class cmExportSet; +class cmGeneratorTarget; +class cmInstallSbomBuilder; +class cmLocalGenerator; +class cmSbomArguments; + +/** \class cmInstallSbomGenerator + * \brief Generate installation rules for SBOM files. + * + * Thin cmInstallGenerator subclass that owns a cmInstallSbomBuilder. + * At generate time it writes the SBOM into a temporary file in the build tree; + * the cmake_install.cmake script then copies that file to the install + * destination. + */ +class cmInstallSbomGenerator : public cmInstallGenerator +{ +public: + cmInstallSbomGenerator(std::vector<cmExportSet*> exportSets, + std::string destination, std::string filePermissions, + std::vector<std::string> const& configurations, + std::string component, MessageLevel message, + bool excludeFromAll, cmSbomArguments args, + cmDiagnosticContext context); + cmInstallSbomGenerator(cmInstallSbomGenerator const&) = delete; + ~cmInstallSbomGenerator() override; + + cmInstallSbomGenerator& operator=(cmInstallSbomGenerator const&) = delete; + + bool Compute(cmLocalGenerator* lg) override; + + std::string const& GetInstallFile() const { return this->SbomFilePath; } + + /** True if this SBOM directly describes `target`. Used by peer SBOMs to + * attribute cross-references when install(export) provenance is absent. */ + bool CoversTarget(cmGeneratorTarget const* target) const; + std::string const& GetPackageName() const; + + /** True if `set` is one of the export sets this SBOM was built from. + * Used by the autogen path to skip sets already explicitly tied to an + * install(SBOM). */ + bool CoversExportSet(cmExportSet const* set) const; + +protected: + void GenerateScript(std::ostream& os) override; + void GenerateScriptActions(std::ostream& os, Indent indent) override; + +private: + std::string TempSbomFilePath; + std::string const FilePermissions; + std::string const SbomFileName; + std::string const SbomFilePath; + cmLocalGenerator* LocalGenerator = nullptr; + std::unique_ptr<cmInstallSbomBuilder> Builder; +};
diff --git a/Source/cmInstallScriptGenerator.cxx b/Source/cmInstallScriptGenerator.cxx index 7173305..685fbdb 100644 --- a/Source/cmInstallScriptGenerator.cxx +++ b/Source/cmInstallScriptGenerator.cxx
@@ -6,18 +6,18 @@ #include <utility> #include <vector> +#include "cmDiagnosticContext.h" #include "cmGeneratorExpression.h" #include "cmLocalGenerator.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmScriptGenerator.h" cmInstallScriptGenerator::cmInstallScriptGenerator( std::string script, bool code, std::string const& component, - bool exclude_from_all, bool all_components, cmListFileBacktrace backtrace) + bool excludeFromAll, bool allComponents, cmListFileBacktrace backtrace) : cmInstallGenerator("", std::vector<std::string>(), component, - MessageDefault, exclude_from_all, all_components, - std::move(backtrace)) + MessageDefault, excludeFromAll, allComponents, + cmDiagnosticContext{ std::move(backtrace) }) , Script(std::move(script)) , Code(code) { @@ -36,9 +36,7 @@ if (this->ActionsPerConfig) { switch (this->LocalGenerator->GetPolicyStatus(cmPolicies::CMP0087)) { case cmPolicies::WARN: - this->LocalGenerator->IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0087)); + this->LocalGenerator->IssuePolicyWarning(cmPolicies::CMP0087); CM_FALLTHROUGH; case cmPolicies::OLD: break;
diff --git a/Source/cmInstallScriptGenerator.h b/Source/cmInstallScriptGenerator.h index 104d432..fe7ca8f 100644 --- a/Source/cmInstallScriptGenerator.h +++ b/Source/cmInstallScriptGenerator.h
@@ -20,7 +20,7 @@ public: cmInstallScriptGenerator( std::string script, bool code, std::string const& component, - bool exclude_from_all, bool all_components, + bool excludeFromAll, bool allComponents, cmListFileBacktrace backtrace = cmListFileBacktrace()); ~cmInstallScriptGenerator() override;
diff --git a/Source/cmInstallScriptHandler.cxx b/Source/cmInstallScriptHandler.cxx index 10f795b..99a0694 100644 --- a/Source/cmInstallScriptHandler.cxx +++ b/Source/cmInstallScriptHandler.cxx
@@ -33,74 +33,87 @@ using InstallScript = cmInstallScriptHandler::InstallScript; using InstallScriptRunner = cmInstallScriptHandler::InstallScriptRunner; -cmInstallScriptHandler::cmInstallScriptHandler(std::string _binaryDir, - std::string _component, - std::string _config, - std::vector<std::string>& args) - : binaryDir(std::move(_binaryDir)) - , component(std::move(_component)) +cmInstallScriptHandler::cmInstallScriptHandler( + std::string binaryDir, std::vector<std::string> components, + std::string installConfig, std::vector<std::string>& args) + : Components(std::move(components)) + , BinaryDir(std::move(binaryDir)) { - std::string const& file = - cmStrCat(this->binaryDir, "/CMakeFiles/InstallScripts.json"); - this->parallel = false; + if (this->Components.empty()) { + this->Components.emplace_back(std::string{}); + } - auto addScript = [this, &args](std::string script, + std::string const& file = + cmStrCat(this->BinaryDir, "/CMakeFiles/InstallScripts.json"); + this->Parallel = false; + + auto addScript = [this, &args](std::string script, std::string component, std::string config) -> void { - this->scripts.push_back({ script, config, args }); + this->Scripts.push_back({ script, config, args }); + if (!component.empty()) { + this->Scripts.back().command.insert( + this->Scripts.back().command.end() - 1, + cmStrCat("-DCMAKE_INSTALL_COMPONENT=", component)); + } if (!config.empty()) { - this->scripts.back().command.insert( - this->scripts.back().command.end() - 1, + this->Scripts.back().command.insert( + this->Scripts.back().command.end() - 1, cmStrCat("-DCMAKE_INSTALL_CONFIG_NAME=", config)); } - this->scripts.back().command.emplace_back(script); - this->directories.push_back(cmSystemTools::GetFilenamePath(script)); + this->Scripts.back().command.emplace_back(script); + this->Directories.push_back(cmSystemTools::GetFilenamePath(script)); }; int compare = 1; if (cmSystemTools::FileExists(file)) { cmSystemTools::FileTimeCompare( - cmStrCat(this->binaryDir, "/CMakeFiles/cmake.check_cache"), file, + cmStrCat(this->BinaryDir, "/CMakeFiles/cmake.check_cache"), file, &compare); } if (compare < 1) { Json::CharReaderBuilder rbuilder; - auto JsonReader = + auto jsonReader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader()); std::vector<char> content; Json::Value value; cmJSONState state(file, &value); - this->parallel = value["Parallel"].asBool(); - if (this->parallel) { + this->Parallel = value["Parallel"].asBool(); + if (this->Parallel) { args.insert(args.end() - 1, "-DCMAKE_INSTALL_LOCAL_ONLY=1"); } - if (_config.empty() && value.isMember("Configs")) { + if (installConfig.empty() && value.isMember("Configs")) { for (auto const& config : value["Configs"]) { - this->configs.push_back(config.asCString()); + this->Configs.push_back(config.asCString()); } } else { - this->configs.push_back(_config); + this->Configs.push_back(installConfig); } for (auto const& script : value["InstallScripts"]) { - for (auto const& config : configs) { - addScript(script.asCString(), config); + for (auto const& component : Components) { + for (auto const& config : Configs) { + addScript(script.asCString(), component, config); + } } - if (!this->parallel) { + if (!this->Parallel) { break; } } } else { - addScript(cmStrCat(this->binaryDir, "/cmake_install.cmake"), _config); + for (auto const& component : Components) { + addScript(cmStrCat(this->BinaryDir, "/cmake_install.cmake"), component, + installConfig); + } } } bool cmInstallScriptHandler::IsParallel() { - return this->parallel; + return this->Parallel; } std::vector<InstallScript> cmInstallScriptHandler::GetScripts() const { - return this->scripts; + return this->Scripts; } int cmInstallScriptHandler::Install(unsigned int j, @@ -109,27 +122,27 @@ cm::uv_loop_ptr loop; loop.init(); std::vector<InstallScriptRunner> runners; - runners.reserve(this->scripts.size()); + runners.reserve(this->Scripts.size()); - std::vector<std::string> instrument_arg; + std::vector<std::string> instrumentArg; if (instrumentation.HasQuery()) { - instrument_arg = { cmSystemTools::GetCTestCommand(), - "--instrument", - "--command-type", - "install", - "--build-dir", - this->binaryDir, - "--config", - "", - "--" }; + instrumentArg = { cmSystemTools::GetCTestCommand(), + "--instrument", + "--command-type", + "install", + "--build-dir", + this->BinaryDir, + "--config", + "", + "--" }; } - for (auto& script : this->scripts) { - if (!instrument_arg.empty()) { - instrument_arg[7] = script.config; // --config <script.config> + for (auto& script : this->Scripts) { + if (!instrumentArg.empty()) { + instrumentArg[7] = script.config; // --config <script.config> } - script.command.insert(script.command.begin(), instrument_arg.begin(), - instrument_arg.end()); + script.command.insert(script.command.begin(), instrumentArg.begin(), + instrumentArg.end()); runners.emplace_back(script); } std::size_t working = 0; @@ -155,31 +168,32 @@ uv_run(loop, UV_RUN_DEFAULT); // Write install manifest - std::string install_manifest; - if (this->component.empty()) { - install_manifest = "install_manifest.txt"; - } else { - cmsys::RegularExpression regEntry; - if (regEntry.compile("^[a-zA-Z0-9_.+-]+$") && - regEntry.find(this->component)) { - install_manifest = - cmStrCat("install_manifest_", this->component, ".txt"); + std::string installManifest; + for (auto const& component : this->Components) { + if (component.empty()) { + installManifest = "install_manifest.txt"; } else { - cmCryptoHash md5(cmCryptoHash::AlgoMD5); - md5.Initialize(); - install_manifest = - cmStrCat("install_manifest_", md5.HashString(this->component), ".txt"); + cmsys::RegularExpression regEntry; + if (regEntry.compile("^[a-zA-Z0-9_.+-]+$") && regEntry.find(component)) { + installManifest = cmStrCat("install_manifest_", component, ".txt"); + } else { + cmCryptoHash md5(cmCryptoHash::AlgoMD5); + md5.Initialize(); + installManifest = + cmStrCat("install_manifest_", md5.HashString(component), ".txt"); + } } - } - cmGeneratedFileStream fout(cmStrCat(this->binaryDir, '/', install_manifest)); - fout.SetCopyIfDifferent(true); - for (auto const& dir : this->directories) { - auto local_manifest = cmStrCat(dir, "/install_local_manifest.txt"); - if (cmSystemTools::FileExists(local_manifest)) { - cmsys::ifstream fin(local_manifest.c_str()); - std::string line; - while (std::getline(fin, line)) { - fout << line << "\n"; + cmGeneratedFileStream fout( + cmStrCat(this->BinaryDir, '/', installManifest)); + fout.SetCopyIfDifferent(true); + for (auto const& dir : this->Directories) { + auto localManifest = cmStrCat(dir, "/install_local_manifest.txt"); + if (cmSystemTools::FileExists(localManifest)) { + cmsys::ifstream fin(localManifest.c_str()); + std::string line; + while (std::getline(fin, line)) { + fout << line << "\n"; + } } } } @@ -188,34 +202,34 @@ InstallScriptRunner::InstallScriptRunner(InstallScript const& script) { - this->name = cmSystemTools::RelativePath( + this->Name = cmSystemTools::RelativePath( cmSystemTools::GetLogicalWorkingDirectory(), script.path); - this->command = script.command; + this->Command = script.command; } void InstallScriptRunner::start(cm::uv_loop_ptr& loop, std::function<void()> callback) { cmUVProcessChainBuilder builder; - builder.AddCommand(this->command) + builder.AddCommand(this->Command) .SetExternalLoop(*loop) .SetMergedBuiltinStreams(); - this->chain = cm::make_unique<cmUVProcessChain>(builder.Start()); - this->streamHandler = cmUVStreamRead( - this->chain->OutputStream(), + this->Chain = cm::make_unique<cmUVProcessChain>(builder.Start()); + this->StreamHandler = cmUVStreamRead( + this->Chain->OutputStream(), [this](std::vector<char> data) { std::string strdata; cmProcessOutput(cmProcessOutput::Auto) .DecodeText(data.data(), data.size(), strdata); - this->output.push_back(strdata); + this->Output.push_back(strdata); }, std::move(callback)); } void InstallScriptRunner::printResult(std::size_t n, std::size_t total) { - cmSystemTools::Stdout(cmStrCat('[', n, '/', total, "] ", this->name, '\n')); - for (auto const& line : this->output) { + cmSystemTools::Stdout(cmStrCat('[', n, '/', total, "] ", this->Name, '\n')); + for (auto const& line : this->Output) { cmSystemTools::Stdout(line); } }
diff --git a/Source/cmInstallScriptHandler.h b/Source/cmInstallScriptHandler.h index 6f2a7d7..9ab8f8e 100644 --- a/Source/cmInstallScriptHandler.h +++ b/Source/cmInstallScriptHandler.h
@@ -21,7 +21,7 @@ { public: cmInstallScriptHandler() = default; - cmInstallScriptHandler(std::string, std::string, std::string, + cmInstallScriptHandler(std::string, std::vector<std::string>, std::string, std::vector<std::string>&); bool IsParallel(); int Install(unsigned int j, cmInstrumentation& instrumentation); @@ -40,18 +40,18 @@ void printResult(std::size_t n, std::size_t total); private: - std::vector<std::string> command; - std::vector<std::string> output; - std::string name; - std::unique_ptr<cmUVProcessChain> chain; - std::unique_ptr<cmUVStreamReadHandle> streamHandler; + std::vector<std::string> Command; + std::vector<std::string> Output; + std::string Name; + std::unique_ptr<cmUVProcessChain> Chain; + std::unique_ptr<cmUVStreamReadHandle> StreamHandler; }; private: - std::vector<InstallScript> scripts; - std::vector<std::string> configs; - std::vector<std::string> directories; - std::string binaryDir; - std::string component; - bool parallel; + std::vector<InstallScript> Scripts; + std::vector<std::string> Configs; + std::vector<std::string> Directories; + std::vector<std::string> Components; + std::string BinaryDir; + bool Parallel; };
diff --git a/Source/cmInstallSubdirectoryGenerator.cxx b/Source/cmInstallSubdirectoryGenerator.cxx index 34de2f6..ce7fdb0 100644 --- a/Source/cmInstallSubdirectoryGenerator.cxx +++ b/Source/cmInstallSubdirectoryGenerator.cxx
@@ -7,6 +7,7 @@ #include <utility> #include <vector> +#include "cmDiagnosticContext.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" @@ -18,7 +19,8 @@ cmMakefile* makefile, std::string binaryDirectory, cmListFileBacktrace backtrace) : cmInstallGenerator("", std::vector<std::string>(), "", MessageDefault, - false, false, std::move(backtrace)) + false, false, + cmDiagnosticContext{ std::move(backtrace) }) , Makefile(makefile) , BinaryDirectory(std::move(binaryDirectory)) {
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index fb7956e..bb704f5 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx
@@ -15,15 +15,17 @@ #include <cm/optional> #include "cmComputeLinkInformation.h" +#include "cmDiagnosticContext.h" +#include "cmDiagnostics.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmInstallType.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmObjectLocation.h" -#include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmScriptGenerator.h" #include "cmStateTypes.h" @@ -128,13 +130,13 @@ cmInstallTargetGenerator::cmInstallTargetGenerator( std::string targetName, std::string const& dest, bool implib, - std::string file_permissions, std::vector<std::string> const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - bool optional, cmListFileBacktrace backtrace) + std::string filePermissions, std::vector<std::string> const& configurations, + std::string const& component, MessageLevel message, bool excludeFromAll, + bool optional, cmDiagnosticContext context) : cmInstallGenerator(dest, configurations, component, message, - exclude_from_all, false, std::move(backtrace)) + excludeFromAll, false, std::move(context)) , TargetName(std::move(targetName)) - , FilePermissions(std::move(file_permissions)) + , FilePermissions(std::move(filePermissions)) , ImportLibrary(implib) , Optional(optional) { @@ -177,9 +179,9 @@ // Write code to install the target file. char const* no_dir_permissions = nullptr; bool optional = this->Optional || this->ImportLibrary; - std::string literal_args; + std::string literalArgs; if (files.UseSourcePermissions) { - literal_args += " USE_SOURCE_PERMISSIONS"; + literalArgs += " USE_SOURCE_PERMISSIONS"; } if (files.Rename) { if (files.From.size() != files.To.size()) { @@ -199,16 +201,16 @@ } this->AddInstallRule(os, dest, files.Type, FileNames, optional, this->FilePermissions.c_str(), no_dir_permissions, - files.To[i].c_str(), literal_args.c_str(), indent); + files.To[i].c_str(), literalArgs.c_str(), indent); } } else { char const* no_rename = nullptr; if (!files.FromDir.empty()) { - literal_args += " FILES_FROM_DIR \"" + files.FromDir + "\""; + literalArgs += " FILES_FROM_DIR \"" + files.FromDir + "\""; } this->AddInstallRule(os, dest, files.Type, files.From, optional, this->FilePermissions.c_str(), no_dir_permissions, - no_rename, literal_args.c_str(), indent); + no_rename, literalArgs.c_str(), indent); } // Add post-installation tweaks. @@ -401,12 +403,15 @@ // Assume the NamelinkModeSkip instance will warn and install. return files; case NamelinkModeSkip: { - std::string e = "Target '" + this->Target->GetName() + - "' was changed to a FRAMEWORK sometime after install(). " - "This may result in the wrong install DESTINATION. " - "Set the FRAMEWORK property earlier."; - this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, e, this->GetBacktrace()); + cmake const* const cm = + this->Target->GetGlobalGenerator()->GetCMakeInstance(); + cm->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("Target '", this->Target->GetName(), + "' was changed to a FRAMEWORK sometime after install(). " + "This may result in the wrong install DESTINATION. " + "Set the FRAMEWORK property earlier."), + this->GetBacktrace()); } break; } @@ -476,8 +481,11 @@ std::string cmInstallTargetGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate( - this->Destination, this->Target->GetLocalGenerator(), config); + cmLocalGenerator* lg = this->Target->GetLocalGenerator(); + std::string dest = + cmGeneratorExpression::Evaluate(this->Destination, lg, config); + this->CheckAbsoluteDestination(dest, lg); + return dest; } std::string cmInstallTargetGenerator::GetInstallFilename( @@ -714,9 +722,8 @@ default: { // Get the install RPATH from the link information and // escape any CMake syntax in the install RPATH. - std::string escapedNewRpath = - cmOutputConverter::EscapeForCMake(cli->GetChrpathString()); - os << indent << " RPATH " << escapedNewRpath << ")\n"; + os << indent << " RPATH " + << cmScriptGenerator::Quote(cli->GetChrpathString()) << ")\n"; break; } } @@ -829,14 +836,11 @@ return; } - // Escape any CMake syntax in the RPATHs. - std::string escapedOldRpath = cmOutputConverter::EscapeForCMake(oldRpath); - std::string escapedNewRpath = cmOutputConverter::EscapeForCMake(newRpath); - // Write a rule to run chrpath to set the install-tree RPATH os << indent << "file(RPATH_CHANGE\n" << indent << " FILE \"" << toDestDirPath << "\"\n" - << indent << " OLD_RPATH " << escapedOldRpath << "\n"; + << indent << " OLD_RPATH " << cmScriptGenerator::Quote(oldRpath) + << "\n"; // CMP0095: ``RPATH`` entries are properly escaped in the intermediary // CMake install script. @@ -848,7 +852,8 @@ os << indent << " NEW_RPATH \"" << newRpath << "\""; break; default: - os << indent << " NEW_RPATH " << escapedNewRpath; + os << indent << " NEW_RPATH " + << cmScriptGenerator::Quote(newRpath); break; } @@ -976,12 +981,14 @@ std::string::npos); if (potentially_affected) { + cmake const* const cm = + this->Target->GetGlobalGenerator()->GetCMakeInstance(); std::ostringstream w; w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0095) << "\n"; w << "RPATH entries for target '" << this->Target->GetName() << "' " << "will not be escaped in the intermediary " << "cmake_install.cmake script."; - this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace()); + cm->IssueDiagnostic(cmDiagnostics::CMD_POLICY, w.str(), + this->GetBacktrace()); } }
diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h index 7421d0d..cb44125 100644 --- a/Source/cmInstallTargetGenerator.h +++ b/Source/cmInstallTargetGenerator.h
@@ -8,9 +8,9 @@ #include <string> #include <vector> +#include "cmDiagnosticContext.h" #include "cmInstallGenerator.h" #include "cmInstallType.h" -#include "cmListFileCache.h" class cmGeneratorTarget; @@ -20,12 +20,12 @@ class cmInstallTargetGenerator : public cmInstallGenerator { public: - cmInstallTargetGenerator( - std::string targetName, std::string const& dest, bool implib, - std::string file_permissions, - std::vector<std::string> const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - bool optional, cmListFileBacktrace backtrace = cmListFileBacktrace()); + cmInstallTargetGenerator(std::string targetName, std::string const& dest, + bool implib, std::string filePermissions, + std::vector<std::string> const& configurations, + std::string const& component, MessageLevel message, + bool excludeFromAll, bool optional, + cmDiagnosticContext context = {}); ~cmInstallTargetGenerator() override; /** Select the policy for installing shared library linkable name
diff --git a/Source/cmInstallTargetsCommand.cxx b/Source/cmInstallTargetsCommand.cxx index 66d40b0..6b18410 100644 --- a/Source/cmInstallTargetsCommand.cxx +++ b/Source/cmInstallTargetsCommand.cxx
@@ -26,7 +26,7 @@ cmMakefile::cmTargetMap& tgts = mf.GetTargets(); auto s = args.begin(); ++s; - std::string runtime_dir = "/bin"; + std::string runtimeDir = "/bin"; for (; s != args.end(); ++s) { if (*s == "RUNTIME_DIRECTORY") { ++s; @@ -36,12 +36,12 @@ return false; } - runtime_dir = *s; + runtimeDir = *s; } else { auto ti = tgts.find(*s); if (ti != tgts.end()) { ti->second.SetInstallPath(args[0]); - ti->second.SetRuntimeInstallPath(runtime_dir); + ti->second.SetRuntimeInstallPath(runtimeDir); ti->second.SetHaveInstallRule(true); } else { std::string str = "Cannot find target: \"" + *s + "\" to install.";
diff --git a/Source/cmInstrumentation.cxx b/Source/cmInstrumentation.cxx index 013f05a..f8dfc27 100644 --- a/Source/cmInstrumentation.cxx +++ b/Source/cmInstrumentation.cxx
@@ -12,6 +12,7 @@ #include <cm/memory> #include <cm/optional> +#include <cm/string_view> #include <cmext/algorithm> #include <cm3p/json/reader.h> @@ -372,11 +373,11 @@ if (d.Load(this->dataDir)) { for (unsigned int i = 0; i < d.GetNumberOfFiles(); i++) { std::string fpath = d.GetFilePath(i); - std::string fname = d.GetFile(i); + std::string const& fname = d.GetFileName(i); if (fname.rfind('.', 0) == 0 || d.FileIsDirectory(i)) { continue; } - files.push_back(snippet(std::move(fname), std::move(fpath))); + files.push_back(snippet(fname, std::move(fpath))); } } @@ -434,6 +435,7 @@ // Delete old content and trace files this->RemoveOldFiles("content"); + this->RemoveOldFiles("compile-trace"); this->RemoveOldFiles("trace"); this->indexLock.Release(); @@ -586,7 +588,8 @@ std::string const& name, std::string const& command, std::vector<std::string> const& args, int64_t result, std::chrono::steady_clock::time_point steadyStart, - std::chrono::system_clock::time_point systemStart, std::string config) + std::chrono::system_clock::time_point systemStart, std::string config, + cm::optional<std::string> output) { // Store command info Json::Value root(this->preTestStats); @@ -597,6 +600,10 @@ root["result"] = static_cast<Json::Value::Int64>(result); root["config"] = config; root["workingDir"] = cmSystemTools::GetLogicalWorkingDirectory(); + if (this->HasOption(cmInstrumentationQuery::Option::CaptureOutput)) { + root["stdout"] = output ? *output : ""; + root["stderr"] = ""; + } // Post-Command this->InsertTimingData(root, steadyStart, systemStart); @@ -626,7 +633,7 @@ int cmInstrumentation::InstrumentCommand( std::string command_type, std::vector<std::string> const& command, - std::function<int()> const& callback, + std::function<cmInstrumentation::CommandResult()> const& callback, cm::optional<std::map<std::string, std::string>> data, cm::optional<std::map<std::string, std::string>> arrayData, LoadQueriesAfter reloadQueriesAfterCommand) @@ -635,7 +642,7 @@ // Always begin gathering data for configure in case cmake_instrumentation // command creates a query if (!this->hasQuery && reloadQueriesAfterCommand == LoadQueriesAfter::No) { - return callback(); + return callback().ExitCode; } // Store command info @@ -646,6 +653,49 @@ if (!command_str.empty()) { root["command"] = command_str; } + root["role"] = command_type; + root["workingDir"] = cmSystemTools::GetLogicalWorkingDirectory(); + + if (data.has_value()) { + for (auto const& item : data.value()) { + if (item.first == "role" && !item.second.empty()) { + command_type = item.second; + root["role"] = command_type; + } else if (item.first == "showOnly") { + root[item.first] = item.second == "1" ? true : false; + } else if (!item.second.empty()) { + root[item.first] = item.second; + } + } + } + if (arrayData.has_value()) { + for (auto const& item : arrayData.value()) { + root[item.first] = Json::arrayValue; + std::stringstream ss(item.second); + std::string element; + while (getline(ss, element, ',')) { + root[item.first].append(element); + } + } + } + // Create empty config entry if config not found + if (!root.isMember("config") && + (command_type == "compile" || command_type == "link" || + command_type == "custom" || command_type == "install")) { + root["config"] = ""; + } + + // Check existing compile trace json to check for modifications + std::string compileTraceFile; + if (this->HasOption(cmInstrumentationQuery::Option::CompileTrace) && + command_type == "compile") { + compileTraceFile = this->GetCompileTraceFile( + command, root["outputs"], root["workingDir"].asString()); + } + long int oldCompileTraceTimestamp = !compileTraceFile.empty() && + cmSystemTools::FileExists(compileTraceFile, true) + ? cmSystemTools::ModifiedTime(compileTraceFile) + : -1; // Pre-Command auto steady_start = std::chrono::steady_clock::now(); @@ -660,7 +710,16 @@ } // Execute Command - int ret = callback(); + cmInstrumentation::CommandResult callbackResult = callback(); + int ret = callbackResult.ExitCode; + if (this->HasOption(cmInstrumentationQuery::Option::CaptureOutput)) { + if (callbackResult.StdOut) { + root["stdout"] = *callbackResult.StdOut; + } + if (callbackResult.StdErr) { + root["stderr"] = *callbackResult.StdErr; + } + } // Exit early if configure didn't generate a query if (reloadQueriesAfterCommand == LoadQueriesAfter::Yes) { @@ -685,52 +744,18 @@ this->InsertDynamicSystemInformation(root, "after"); } - // Gather additional data - if (data.has_value()) { - for (auto const& item : data.value()) { - if (item.first == "role" && !item.second.empty()) { - command_type = item.second; - } else if (item.first == "showOnly") { - root[item.first] = item.second == "1" ? true : false; - } else if (!item.second.empty()) { - root[item.first] = item.second; - } - } - } - // See SpawnBuildDaemon(); this data is currently meaningless for build. root["result"] = command_type == "build" ? Json::nullValue : ret; - // Create empty config entry if config not found - if (!root.isMember("config") && - (command_type == "compile" || command_type == "link" || - command_type == "custom" || command_type == "install")) { - root["config"] = ""; - } - - if (arrayData.has_value()) { - for (auto const& item : arrayData.value()) { - if (item.first == "targetLabels" && command_type != "link") { - continue; - } - root[item.first] = Json::arrayValue; - std::stringstream ss(item.second); - std::string element; - while (getline(ss, element, ',')) { - root[item.first].append(element); - } - if (item.first == "outputs") { - root["outputSizes"] = Json::arrayValue; - for (auto const& output : root["outputs"]) { - root["outputSizes"].append( - static_cast<Json::Value::UInt64>(cmSystemTools::FileLength( - cmStrCat(this->binaryDir, '/', output.asCString())))); - } - } + // Output Sizes + if (root.isMember("outputs")) { + root["outputSizes"] = Json::arrayValue; + for (auto const& output : root["outputs"]) { + root["outputSizes"].append( + static_cast<Json::Value::UInt64>(cmSystemTools::FileLength( + cmStrCat(this->binaryDir, '/', output.asCString())))); } } - root["role"] = command_type; - root["workingDir"] = cmSystemTools::GetLogicalWorkingDirectory(); auto addCMakeContent = [this](Json::Value& root_) -> void { std::string contentFile = @@ -744,14 +769,25 @@ addCMakeContent(root); } - // Write Json - cmsys::SystemInformation& info = this->GetSystemInformation(); + // Compute file name properties std::chrono::system_clock::time_point endTime = system_start + std::chrono::milliseconds(root["duration"].asUInt64()); - std::string const& file_name = cmStrCat( - command_type, '-', - this->ComputeSuffixHash(cmStrCat(command_str, info.GetProcessId())), '-', - this->ComputeSuffixTime(endTime), ".json"); + cmsys::SystemInformation& info = this->GetSystemInformation(); + std::string const commandHash = + this->ComputeSuffixHash(cmStrCat(command_str, info.GetProcessId())); + std::string const suffixTime = this->ComputeSuffixTime(endTime); + + // Compile Trace + if (this->HasOption(cmInstrumentationQuery::Option::CompileTrace) && + command_type == "compile") { + this->CollectCompileTraceFile(root, compileTraceFile, + oldCompileTraceTimestamp, commandHash, + suffixTime); + } + + // Write JSON + std::string const& file_name = + cmStrCat(command_type, '-', commandHash, '-', suffixTime, ".json"); // Don't write configure snippet until generate time if (command_type == "configure") { @@ -916,8 +952,11 @@ return 0; }; int ret = this->InstrumentCommand( - "build", {}, [waitForBuild]() { return waitForBuild(); }, cm::nullopt, - cm::nullopt, LoadQueriesAfter::Yes); + "build", {}, + [waitForBuild]() -> cmInstrumentation::CommandResult { + return { waitForBuild(), cm::nullopt, cm::nullopt }; + }, + cm::nullopt, cm::nullopt, LoadQueriesAfter::Yes); this->buildLock.Release(); this->CollectTimingData(cmInstrumentationQuery::Hook::PostBuild); return ret; @@ -1031,14 +1070,80 @@ } std::string dst = cmStrCat(dst_dir, '/', snippet_str); - cmsys::Status copied = cmSystemTools::CopyFileAlways(snippet_path, dst); - if (!copied) { + if (!cmSystemTools::CopyFileAlways(snippet_path, dst)) { error_msg = cmStrCat("Failed to copy ", snippet_path, " to ", dst); cmSystemTools::Error(error_msg); } } } +std::string cmInstrumentation::GetCompileTraceFile( + std::vector<std::string> const& command, Json::Value const& outputs, + std::string const& workingDir) +{ + cm::string_view const prefix = "-ftime-trace="; + std::string traceFile; + for (auto it = command.rbegin(); it != command.rend(); ++it) { + std::string const& arg = *it; + if (cmHasPrefix(arg, prefix)) { + traceFile = arg.substr(prefix.size()); + } + } + if (traceFile.empty() && !outputs.empty()) { + std::string outputPath = outputs[0].asString(); + cm::string_view ext = + cmSystemTools::GetFilenameLastExtensionView(outputPath); + if (!outputPath.empty() && !ext.empty()) { + traceFile = cmStrCat( + outputPath.substr(0, outputPath.size() - ext.size()), ".json"); + } + } + + if (cmSystemTools::FileIsFullPath(traceFile)) { + return traceFile; + } + if (cmSystemTools::FileExists(cmStrCat(workingDir, '/', traceFile), true)) { + return cmStrCat(workingDir, '/', traceFile); + } + if (cmSystemTools::FileExists(cmStrCat(this->binaryDir, '/', traceFile), + true)) { + return cmStrCat(this->binaryDir, '/', traceFile); + } + + return traceFile; +} + +void cmInstrumentation::CollectCompileTraceFile(Json::Value& root, + std::string traceFile, + long int oldTimestamp, + std::string const& commandHash, + std::string const& suffixTime) +{ + if (traceFile.empty()) { + root["traceFile"] = Json::nullValue; + return; + } + if (!cmSystemTools::FileExists(traceFile, true) || + cmSystemTools::ModifiedTime(traceFile) == oldTimestamp) { + root["traceFile"] = Json::nullValue; + return; + } + cm::string_view ext = cmSystemTools::GetFilenameLastExtensionView(traceFile); + std::string candidateName = cmSystemTools::GetFilenameName(traceFile); + std::string copiedName = + cmStrCat(candidateName.substr(0, candidateName.size() - ext.size()), '-', + commandHash, '-', suffixTime, ext); + std::string const copiedFile = cmStrCat("compile-trace/", copiedName); + std::string const destination = cmStrCat(this->dataDir, '/', copiedFile); + cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(destination)); + if (!cmSystemTools::CopyFileAlways(traceFile, destination)) { + cmSystemTools::Error(cmStrCat("Failed to copy compile trace file ", + traceFile, " to ", destination)); + return; + } + root["traceFile"] = copiedFile; +} + void cmInstrumentation::WriteTraceFile(Json::Value const& index, std::string const& trace_name) {
diff --git a/Source/cmInstrumentation.h b/Source/cmInstrumentation.h index 6c67e20..fc468b0 100644 --- a/Source/cmInstrumentation.h +++ b/Source/cmInstrumentation.h
@@ -42,9 +42,15 @@ LoadQueriesAfter loadQueries = LoadQueriesAfter::Yes); void LoadQueries(); void CheckCDashVariable(); + struct CommandResult + { + int ExitCode; + cm::optional<std::string> StdOut; + cm::optional<std::string> StdErr; + }; int InstrumentCommand( std::string command_type, std::vector<std::string> const& command, - std::function<int()> const& callback, + std::function<CommandResult()> const& callback, cm::optional<std::map<std::string, std::string>> options = cm::nullopt, cm::optional<std::map<std::string, std::string>> arrayOptions = cm::nullopt, @@ -55,7 +61,8 @@ int64_t result, std::chrono::steady_clock::time_point steadyStart, std::chrono::system_clock::time_point systemStart, - std::string config); + std::string config, + cm::optional<std::string> output = cm::nullopt); void GetPreTestStats(); bool HasQuery() const; bool HasOption(cmInstrumentationQuery::Option option) const; @@ -112,6 +119,14 @@ static bool IsInstrumentableTargetType(cmStateEnums::TargetType type); void PrepareDataForCDash(std::string const& data_dir, std::string const& index_path); + std::string GetCompileTraceFile(std::vector<std::string> const& command, + Json::Value const& outputs, + std::string const& workingDir); + void CollectCompileTraceFile(Json::Value& root, std::string traceFile, + long int oldTimestamp, + std::string const& commandHash, + std::string const& suffixTime); + void RemoveCompileTraceFile(Json::Value const& snippetData); void RemoveOldFiles(std::string const& dataSubdir); void WriteTraceFile(Json::Value const& index, std::string const& trace_name); Json::Value BuildTraceEvent(std::vector<uint64_t>& workers,
diff --git a/Source/cmInstrumentationCommand.cxx b/Source/cmInstrumentationCommand.cxx index 5b0bb2d..636478e 100644 --- a/Source/cmInstrumentationCommand.cxx +++ b/Source/cmInstrumentationCommand.cxx
@@ -81,7 +81,7 @@ if (!cmInstrumentationQuery::ValidDataVersion(version)) { status.SetError( cmStrCat("given an unsupported DATA_VERSION \"", versionString, - "\" (the only currently supported version is 1.0).")); + "\" (the maximum currently supported version is 1.1).")); return false; }
diff --git a/Source/cmInstrumentationQuery.cxx b/Source/cmInstrumentationQuery.cxx index fac5c2c..0c98d3d 100644 --- a/Source/cmInstrumentationQuery.cxx +++ b/Source/cmInstrumentationQuery.cxx
@@ -15,8 +15,13 @@ #include "cmStringAlgorithms.h" std::vector<std::string> const cmInstrumentationQuery::OptionString{ - "staticSystemInformation", "dynamicSystemInformation", "cdashSubmit", - "cdashVerbose", "trace" + "staticSystemInformation", + "dynamicSystemInformation", + "captureOutput", + "compileTrace", + "cdashSubmit", + "cdashVerbose", + "trace" }; std::vector<std::string> const cmInstrumentationQuery::HookString{ "postGenerate", "preBuild", "postBuild", "preCMakeBuild", @@ -161,13 +166,13 @@ bool cmInstrumentationQuery::ValidDataVersion(Version version) { auto const latest = LatestDataVersion(); - return version.Major == latest.Major && version.Minor == latest.Minor; + return version.Major == latest.Major && version.Minor <= latest.Minor; } Version cmInstrumentationQuery::LatestDataVersion() { Version latest; latest.Major = 1; - latest.Minor = 0; + latest.Minor = 1; return latest; }
diff --git a/Source/cmInstrumentationQuery.h b/Source/cmInstrumentationQuery.h index 37bbd0c..c7b7cab 100644 --- a/Source/cmInstrumentationQuery.h +++ b/Source/cmInstrumentationQuery.h
@@ -16,6 +16,8 @@ { StaticSystemInformation, DynamicSystemInformation, + CaptureOutput, + CompileTrace, CDashSubmit, CDashVerbose, Trace
diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h index 269c224..da8d6ec 100644 --- a/Source/cmJSONHelpers.h +++ b/Source/cmJSONHelpers.h
@@ -105,6 +105,18 @@ -> bool { return func(out.*member, value, state); }, required); } + template <typename U, typename M, typename F, typename C> + Object& Bind(cm::string_view name, M U::*member, F func, C constant, + bool required = true) + { + return this->BindPrivate( + name, + [func, member, constant](T& out, Json::Value const* value, + cmJSONState* state) -> bool { + return func(out.*member, value, constant, state); + }, + required); + } template <typename M, typename F> Object& Bind(cm::string_view name, std::nullptr_t, F func, bool required = true)
diff --git a/Source/cmLinkItemGraphVisitor.cxx b/Source/cmLinkItemGraphVisitor.cxx index e8bf85a..d79b74b 100644 --- a/Source/cmLinkItemGraphVisitor.cxx +++ b/Source/cmLinkItemGraphVisitor.cxx
@@ -134,8 +134,6 @@ auto const& utilityItems = target.GetUtilityItems(); for (auto const& item : utilityItems) { auto const& name = item.AsStr(); - if (dependencies.find(name) == dependencies.cend()) { - dependencies[name] = Dependency(DependencyType::Utility, item); - } + dependencies.emplace(name, Dependency(DependencyType::Utility, item)); } }
diff --git a/Source/cmList.cxx b/Source/cmList.cxx index af1d89a..c7401cd 100644 --- a/Source/cmList.cxx +++ b/Source/cmList.cxx
@@ -9,21 +9,26 @@ #include <cstddef> #include <functional> #include <iterator> -#include <set> #include <stdexcept> #include <utility> #include <cm/memory> +#include <cm/optional> #include "cmsys/RegularExpression.hxx" #include "cmAlgorithms.h" +#include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmListFileCache.h" +#include "cmMakefile.h" #include "cmRange.h" +#include "cmState.h" +#include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmStringReplaceHelper.h" #include "cmSystemTools.h" +#include "cmValue.h" cm::string_view cmList::element_separator{ ";" }; @@ -77,6 +82,157 @@ cmsys::RegularExpression& Regex; bool const IncludeMatches; }; + +// Hash of call site (FilePath:Line) for unique variable names across recursive +// calls. +std::string OutputVarFor(cm::string_view prefix, cmMakefile& makefile) +{ + cmListFileContext context = makefile.GetBacktrace().Top(); + std::size_t hash = + std::hash<std::string>{}(cmStrCat(context.FilePath, ":", context.Line)); + return cmStrCat(prefix, hash, "_"); +} + +void RequireFunction(cmMakefile const& makefile, + std::string const& functionName, + std::string const& errorPrefix) +{ + cm::optional<cmStateEnums::CommandType> type = + makefile.GetState()->GetCommandType(functionName); + if (!type) { + throw cmList::transform_error( + cmStrCat(errorPrefix, ": unknown function \"", functionName, "\".")); + } + if (*type == cmStateEnums::CommandType::Macro) { + throw cmList::transform_error( + cmStrCat(errorPrefix, ": macro \"", functionName, + "\" may not be used here;" + " define it as a function() instead.")); + } +} + +class PredicateEvaluator +{ +public: + PredicateEvaluator(std::string functionName, cmMakefile& makefile, + std::string errorPrefix = "sub-command TRANSFORM, " + "selector PREDICATE") + : FunctionName(std::move(functionName)) + , Makefile(&makefile) + , ErrorPrefix(std::move(errorPrefix)) + , OutputVar(OutputVarFor("_cmake_predicate_out_", makefile)) + { + RequireFunction(makefile, this->FunctionName, this->ErrorPrefix); + } + + bool operator()(std::string const& value) + { + this->Makefile->RemoveDefinition(this->OutputVar); + + cmListFileContext context = this->Makefile->GetBacktrace().Top(); + std::vector<cmListFileArgument> funcArgs; + funcArgs.emplace_back(value, cmListFileArgument::Quoted, context.Line); + funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted, + context.Line); + cmListFileFunction func{ this->FunctionName, context.Line, context.Line, + std::move(funcArgs) }; + + cmExecutionStatus status(*this->Makefile); + if (!this->Makefile->ExecuteCommand(func, status) || + status.GetNestedError()) { + throw cmList::transform_error( + cmStrCat(this->ErrorPrefix, ": function \"", this->FunctionName, + "\" failed during execution.")); + } + + cmValue result = this->Makefile->GetDefinition(this->OutputVar); + if (!result) { + throw cmList::transform_error( + cmStrCat(this->ErrorPrefix, ": function \"", this->FunctionName, + "\" did not set the output variable.")); + } + + bool boolResult = cmIsOn(*result); + this->Makefile->RemoveDefinition(this->OutputVar); + return boolResult; + } + +private: + std::string FunctionName; + cmMakefile* Makefile = nullptr; + std::string ErrorPrefix; + std::string OutputVar; +}; + +class MatchesPredicate +{ +public: + MatchesPredicate(PredicateEvaluator& evaluator, cmList::FilterMode mode) + : Evaluator(evaluator) + , IncludeMatches(mode == cmList::FilterMode::INCLUDE) + { + } + + bool operator()(std::string const& target) + { + return this->Evaluator(target) ^ this->IncludeMatches; + } + +private: + PredicateEvaluator& Evaluator; + bool IncludeMatches; +}; + +class ComparatorEvaluator +{ +public: + ComparatorEvaluator(std::string functionName, cmMakefile& makefile) + : FunctionName(std::move(functionName)) + , Makefile(&makefile) + , OutputVar(OutputVarFor("_cmake_comparator_out_", makefile)) + { + RequireFunction(makefile, this->FunctionName, + "sub-command SORT, COMPARATOR"); + } + + bool operator()(std::string const& a, std::string const& b) + { + this->Makefile->RemoveDefinition(this->OutputVar); + + cmListFileContext context = this->Makefile->GetBacktrace().Top(); + std::vector<cmListFileArgument> funcArgs; + funcArgs.emplace_back(a, cmListFileArgument::Quoted, context.Line); + funcArgs.emplace_back(b, cmListFileArgument::Quoted, context.Line); + funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted, + context.Line); + cmListFileFunction func{ this->FunctionName, context.Line, context.Line, + std::move(funcArgs) }; + + cmExecutionStatus status(*this->Makefile); + if (!this->Makefile->ExecuteCommand(func, status) || + status.GetNestedError()) { + throw cmList::transform_error( + cmStrCat("sub-command SORT, COMPARATOR: function \"", + this->FunctionName, "\" failed during execution.")); + } + + cmValue result = this->Makefile->GetDefinition(this->OutputVar); + if (!result) { + throw cmList::transform_error( + cmStrCat("sub-command SORT, COMPARATOR: function \"", + this->FunctionName, "\" did not set the output variable.")); + } + + bool boolResult = cmIsOn(*result); + this->Makefile->RemoveDefinition(this->OutputVar); + return boolResult; + } + +private: + std::string FunctionName; + cmMakefile* Makefile = nullptr; + std::string OutputVar; +}; } cmList& cmList::filter(cm::string_view pattern, FilterMode mode) @@ -95,6 +251,23 @@ return *this; } +cmList& cmList::filter(std::string const& functionName, FilterMode mode, + cmMakefile& makefile) +{ + try { + PredicateEvaluator evaluator(functionName, makefile, + "sub-command FILTER, mode PREDICATE"); + + auto it = std::remove_if(this->Values.begin(), this->Values.end(), + MatchesPredicate{ evaluator, mode }); + this->Values.erase(it, this->Values.end()); + } catch (transform_error& e) { + throw std::invalid_argument(e.what()); + } + + return *this; +} + namespace { class StringSorter { @@ -142,6 +315,13 @@ { } + StringSorter(cmList::SortConfiguration config, ComparisonFunction comparator) + : Filters{ nullptr, this->GetCaseFilter(config.Case) } + , SortMethod(std::move(comparator)) + , Descending(config.Order == OrderMode::DESCENDING) + { + } + std::string ApplyFilter(std::string const& argument) { std::string result = argument; @@ -201,6 +381,38 @@ return *this; } +cmList& cmList::sort(SortConfiguration cfg, cmMakefile& makefile) +{ + SortConfiguration config{ cfg }; + + if (config.Order == SortConfiguration::OrderMode::DEFAULT) { + config.Order = SortConfiguration::OrderMode::ASCENDING; + } + if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) { + config.Case = SortConfiguration::CaseSensitivity::SENSITIVE; + } + + try { + ComparatorEvaluator evaluator(config.ComparatorFunction, makefile); + StringSorter sorter( + config, [&evaluator](std::string const& a, std::string const& b) { + bool result = evaluator(a, b); + if (result && evaluator(b, a)) { + throw cmList::transform_error( + "sub-command SORT, COMPARATOR: function does not induce a strict " + "weak ordering. The comparator returned TRUE for both (a, b) and " + "(b, a)."); + } + return result; + }); + std::sort(this->Values.begin(), this->Values.end(), sorter); + } catch (transform_error& e) { + throw std::invalid_argument(e.what()); + } + + return *this; +} + namespace { using transform_type = std::function<std::string(std::string const&)>; using transform_error = cmList::transform_error; @@ -266,6 +478,26 @@ cmsys::RegularExpression Regex; }; +class TransformSelectorPredicate : public TransformSelector +{ +public: + TransformSelectorPredicate(std::string const& functionName, + cmMakefile& makefile) + : TransformSelector("PREDICATE") + , Evaluator(functionName, makefile) + { + } + + bool Validate(std::size_t) override { return true; } + + bool InSelection(std::string const& value) override + { + return this->Evaluator(value); + } + +private: + PredicateEvaluator Evaluator; +}; class TransformSelectorIndexes : public TransformSelector { public: @@ -388,49 +620,36 @@ class TransformAction { public: + // Public because an inherited constructor keeps the base's access. + explicit TransformAction(TransformSelector& selector) + : Selector(selector) + { + } virtual ~TransformAction() = default; - void Initialize(TransformSelector* selector) { this->Selector = selector; } - virtual void Initialize(TransformSelector*, std::string const&) {} - virtual void Initialize(TransformSelector*, std::string const&, - std::string const&) + std::string operator()(std::string const& s) { + return this->Selector.InSelection(s) ? this->ApplyTo(s) : s; } - virtual void Initialize(TransformSelector* selector, - std::vector<std::string> const&) - { - this->Initialize(selector); - } - - virtual std::string operator()(std::string const& s) = 0; protected: - TransformSelector* Selector; + virtual std::string ApplyTo(std::string const& s) = 0; + + TransformSelector& Selector; }; class TransformActionAppend : public TransformAction { public: - using TransformAction::Initialize; - - void Initialize(TransformSelector* selector, - std::string const& append) override + TransformActionAppend(TransformSelector& selector, std::string append) + : TransformAction(selector) + , Append(std::move(append)) { - TransformAction::Initialize(selector); - this->Append = append; - } - void Initialize(TransformSelector* selector, - std::vector<std::string> const& append) override - { - this->Initialize(selector, append.front()); } - std::string operator()(std::string const& s) override +protected: + std::string ApplyTo(std::string const& s) override { - if (this->Selector->InSelection(s)) { - return cmStrCat(s, this->Append); - } - - return s; + return cmStrCat(s, this->Append); } private: @@ -439,27 +658,16 @@ class TransformActionPrepend : public TransformAction { public: - using TransformAction::Initialize; - - void Initialize(TransformSelector* selector, - std::string const& prepend) override + TransformActionPrepend(TransformSelector& selector, std::string prepend) + : TransformAction(selector) + , Prepend(std::move(prepend)) { - TransformAction::Initialize(selector); - this->Prepend = prepend; - } - void Initialize(TransformSelector* selector, - std::vector<std::string> const& prepend) override - { - this->Initialize(selector, prepend.front()); } - std::string operator()(std::string const& s) override +protected: + std::string ApplyTo(std::string const& s) override { - if (this->Selector->InSelection(s)) { - return cmStrCat(this->Prepend, s); - } - - return s; + return cmStrCat(this->Prepend, s); } private: @@ -468,64 +676,59 @@ class TransformActionToUpper : public TransformAction { public: - std::string operator()(std::string const& s) override - { - if (this->Selector->InSelection(s)) { - return cmSystemTools::UpperCase(s); - } + using TransformAction::TransformAction; - return s; +protected: + std::string ApplyTo(std::string const& s) override + { + return cmSystemTools::UpperCase(s); } }; class TransformActionToLower : public TransformAction { public: - std::string operator()(std::string const& s) override - { - if (this->Selector->InSelection(s)) { - return cmSystemTools::LowerCase(s); - } + using TransformAction::TransformAction; - return s; +protected: + std::string ApplyTo(std::string const& s) override + { + return cmSystemTools::LowerCase(s); } }; class TransformActionStrip : public TransformAction { public: - std::string operator()(std::string const& s) override - { - if (this->Selector->InSelection(s)) { - return cmTrimWhitespace(s); - } + using TransformAction::TransformAction; - return s; +protected: + std::string ApplyTo(std::string const& s) override + { + return cmTrimWhitespace(s); } }; class TransformActionGenexStrip : public TransformAction { public: - std::string operator()(std::string const& s) override - { - if (this->Selector->InSelection(s)) { - return cmGeneratorExpression::Preprocess( - s, cmGeneratorExpression::StripAllGeneratorExpressions); - } + using TransformAction::TransformAction; - return s; +protected: + std::string ApplyTo(std::string const& s) override + { + return cmGeneratorExpression::Preprocess( + s, cmGeneratorExpression::StripAllGeneratorExpressions); } }; class TransformActionReplace : public TransformAction { public: - using TransformAction::Initialize; - - void Initialize(TransformSelector* selector, std::string const& regex, - std::string const& replace) override + TransformActionReplace(TransformSelector& selector, std::string const& regex, + std::string const& replace) + : TransformAction(selector) + // Makefile is legitimately null when cmList is used directly from C++; + // cmStringReplaceHelper handles that. + , ReplaceHelper(cm::make_unique<cmStringReplaceHelper>(regex, replace, + selector.Makefile)) { - TransformAction::Initialize(selector); - this->ReplaceHelper = cm::make_unique<cmStringReplaceHelper>( - regex, replace, selector->Makefile); - if (!this->ReplaceHelper->IsRegularExpressionValid()) { throw transform_error( cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile " @@ -537,94 +740,122 @@ this->ReplaceHelper->GetError(), '.')); } } - void Initialize(TransformSelector* selector, - std::vector<std::string> const& args) override + +protected: + std::string ApplyTo(std::string const& s) override { - this->Initialize(selector, args[0], args[1]); - } + std::string output; - std::string operator()(std::string const& s) override - { - if (this->Selector->InSelection(s)) { - // Scan through the input for all matches. - std::string output; - - if (!this->ReplaceHelper->Replace(s, output)) { - throw transform_error( - cmStrCat("sub-command TRANSFORM, action REPLACE: ", - this->ReplaceHelper->GetError(), '.')); - } - - return output; + if (!this->ReplaceHelper->Replace(s, output)) { + throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ", + this->ReplaceHelper->GetError(), '.')); } - return s; + return output; } private: std::unique_ptr<cmStringReplaceHelper> ReplaceHelper; }; -// Descriptor of action -// Arity: number of arguments required for the action -// Transform: Object implementing the action -struct ActionDescriptor +class TransformActionApply : public TransformAction { - ActionDescriptor(cmList::TransformAction action) - : Action(action) +public: + TransformActionApply(TransformSelector& selector, std::string functionName, + cmMakefile& makefile) + : TransformAction(selector) + , FunctionName(std::move(functionName)) + , Makefile(&makefile) + , OutputVar(OutputVarFor("_cmake_transform_apply_out_", makefile)) { - } - ActionDescriptor(cmList::TransformAction action, std::string name, - std::size_t arity, - std::unique_ptr<TransformAction> transform) - : Action(action) - , Name(std::move(name)) - , Arity(arity) - , Transform(std::move(transform)) - { + RequireFunction(makefile, this->FunctionName, + "sub-command TRANSFORM, action APPLY"); } - operator cmList::TransformAction() const { return this->Action; } +protected: + std::string ApplyTo(std::string const& s) override + { + // Unset the output variable before calling + this->Makefile->RemoveDefinition(this->OutputVar); - cmList::TransformAction Action; - std::string Name; - std::size_t Arity = 0; - std::unique_ptr<TransformAction> Transform; + // Build the function call: functionName(s, outputVar) + cmListFileContext context = this->Makefile->GetBacktrace().Top(); + std::vector<cmListFileArgument> funcArgs; + funcArgs.emplace_back(s, cmListFileArgument::Quoted, context.Line); + funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted, + context.Line); + cmListFileFunction func{ this->FunctionName, context.Line, context.Line, + std::move(funcArgs) }; + + cmExecutionStatus status(*this->Makefile); + if (!this->Makefile->ExecuteCommand(func, status) || + status.GetNestedError()) { + throw transform_error( + cmStrCat("sub-command TRANSFORM, action APPLY: function \"", + this->FunctionName, "\" failed during execution.")); + } + + // Read back the output variable + cmValue result = this->Makefile->GetDefinition(this->OutputVar); + if (!result) { + throw transform_error( + cmStrCat("sub-command TRANSFORM, action APPLY: function \"", + this->FunctionName, "\" did not set the output variable.")); + } + + // Copy the result before cleaning up (RemoveDefinition invalidates the + // cmValue pointer). + std::string output = *result; + + this->Makefile->RemoveDefinition(this->OutputVar); + + return output; + } + +private: + std::string FunctionName; + cmMakefile* Makefile = nullptr; + std::string OutputVar; }; -// Build a set of supported actions. -using ActionDescriptorSet = std::set< - ActionDescriptor, - std::function<bool(cmList::TransformAction, cmList::TransformAction)>>; +// Arity: number of arguments required for the action. +// +// Keep this a bare aggregate of literal types: CMake still builds as C++11, +// where a member initializer, a constructor, or a cm::string_view member +// would break the constexpr table below. +struct ActionDescriptor +{ + cmList::TransformAction Action; + char const* Name; + std::size_t Arity; +}; -ActionDescriptorSet Descriptors([](cmList::TransformAction x, - cmList::TransformAction y) { - return x < y; -}); +constexpr ActionDescriptor Descriptors[] = { + { cmList::TransformAction::APPEND, "APPEND", 1 }, + { cmList::TransformAction::PREPEND, "PREPEND", 1 }, + { cmList::TransformAction::TOUPPER, "TOUPPER", 0 }, + { cmList::TransformAction::TOLOWER, "TOLOWER", 0 }, + { cmList::TransformAction::STRIP, "STRIP", 0 }, + { cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0 }, + { cmList::TransformAction::REPLACE, "REPLACE", 2 }, + { cmList::TransformAction::APPLY, "APPLY", 1 }, +}; -ActionDescriptorSet::iterator TransformConfigure( +ActionDescriptor const& TransformConfigure( cmList::TransformAction action, std::unique_ptr<cmList::TransformSelector>& selector, std::size_t arity) { - if (Descriptors.empty()) { - Descriptors.emplace(cmList::TransformAction::APPEND, "APPEND", 1, - cm::make_unique<TransformActionAppend>()); - Descriptors.emplace(cmList::TransformAction::PREPEND, "PREPEND", 1, - cm::make_unique<TransformActionPrepend>()); - Descriptors.emplace(cmList::TransformAction::TOUPPER, "TOUPPER", 0, - cm::make_unique<TransformActionToUpper>()); - Descriptors.emplace(cmList::TransformAction::TOLOWER, "TOLOWER", 0, - cm::make_unique<TransformActionToLower>()); - Descriptors.emplace(cmList::TransformAction::STRIP, "STRIP", 0, - cm::make_unique<TransformActionStrip>()); - Descriptors.emplace(cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0, - cm::make_unique<TransformActionGenexStrip>()); - Descriptors.emplace(cmList::TransformAction::REPLACE, "REPLACE", 2, - cm::make_unique<TransformActionReplace>()); + // Not indexed by the enum value: this table is in registration order, and + // cmList.h declares TOLOWER before TOUPPER. + ActionDescriptor const* descriptor = nullptr; + for (auto const& candidate : Descriptors) { + if (candidate.Action == action) { + descriptor = &candidate; + break; + } } - auto descriptor = Descriptors.find(action); - if (descriptor == Descriptors.end()) { + if (!descriptor) { throw transform_error(cmStrCat(" sub-command TRANSFORM, ", static_cast<int>(action), " invalid action.")); @@ -639,7 +870,56 @@ selector = cm::make_unique<TransformNoSelector>(); } - return descriptor; + return *descriptor; +} + +// Precondition: TransformConfigure has validated the arity, so args is +// indexed unchecked. +std::unique_ptr<TransformAction> MakeTransformAction( + ActionDescriptor const& descriptor, TransformSelector& selector, + std::vector<std::string> const& args) +{ + switch (descriptor.Action) { + case cmList::TransformAction::APPEND: + return cm::make_unique<TransformActionAppend>(selector, args[0]); + case cmList::TransformAction::PREPEND: + return cm::make_unique<TransformActionPrepend>(selector, args[0]); + case cmList::TransformAction::TOUPPER: + return cm::make_unique<TransformActionToUpper>(selector); + case cmList::TransformAction::TOLOWER: + return cm::make_unique<TransformActionToLower>(selector); + case cmList::TransformAction::STRIP: + return cm::make_unique<TransformActionStrip>(selector); + case cmList::TransformAction::GENEX_STRIP: + return cm::make_unique<TransformActionGenexStrip>(selector); + case cmList::TransformAction::REPLACE: + return cm::make_unique<TransformActionReplace>(selector, args[0], + args[1]); + case cmList::TransformAction::APPLY: + // APPLY needs a cmMakefile, which this factory does not receive; only + // the cmMakefile overload of cmList::transform can build it. + break; + } + + throw transform_error( + "sub-command TRANSFORM, action APPLY requires cmMakefile context."); +} + +void TransformValues(cmList::container_type& values, + cmList::TransformAction action, + std::vector<std::string> const& args, + std::unique_ptr<cmList::TransformSelector>& selector) +{ + ActionDescriptor const& descriptor = + TransformConfigure(action, selector, args.size()); + + auto& sel = static_cast<TransformSelector&>(*selector); + std::unique_ptr<TransformAction> transformer = + MakeTransformAction(descriptor, sel, args); + + sel.Transform(values, [&transformer](std::string const& s) -> std::string { + return (*transformer)(s); + }); } } @@ -742,18 +1022,18 @@ return std::unique_ptr<cmList::TransformSelector>(selector.release()); } +std::unique_ptr<cmList::TransformSelector> +cmList::TransformSelector::NewPREDICATE(std::string const& functionName, + cmMakefile& makefile) +{ + return std::unique_ptr<cmList::TransformSelector>( + new TransformSelectorPredicate(functionName, makefile)); +} + cmList& cmList::transform(TransformAction action, std::unique_ptr<TransformSelector> selector) { - auto descriptor = TransformConfigure(action, selector, 0); - - descriptor->Transform->Initialize( - static_cast<::TransformSelector*>(selector.get())); - - static_cast<::TransformSelector&>(*selector).Transform( - this->Values, [&descriptor](std::string const& s) -> std::string { - return (*descriptor->Transform)(s); - }); + TransformValues(this->Values, action, {}, selector); return *this; } @@ -761,15 +1041,7 @@ cmList& cmList::transform(TransformAction action, std::string const& arg, std::unique_ptr<TransformSelector> selector) { - auto descriptor = TransformConfigure(action, selector, 1); - - descriptor->Transform->Initialize( - static_cast<::TransformSelector*>(selector.get()), arg); - - static_cast<::TransformSelector&>(*selector).Transform( - this->Values, [&descriptor](std::string const& s) -> std::string { - return (*descriptor->Transform)(s); - }); + TransformValues(this->Values, action, { arg }, selector); return *this; } @@ -778,15 +1050,7 @@ std::string const& arg2, std::unique_ptr<TransformSelector> selector) { - auto descriptor = TransformConfigure(action, selector, 2); - - descriptor->Transform->Initialize( - static_cast<::TransformSelector*>(selector.get()), arg1, arg2); - - static_cast<::TransformSelector&>(*selector).Transform( - this->Values, [&descriptor](std::string const& s) -> std::string { - return (*descriptor->Transform)(s); - }); + TransformValues(this->Values, action, { arg1, arg2 }, selector); return *this; } @@ -795,15 +1059,33 @@ std::vector<std::string> const& args, std::unique_ptr<TransformSelector> selector) { - auto descriptor = TransformConfigure(action, selector, args.size()); + TransformValues(this->Values, action, args, selector); - descriptor->Transform->Initialize( - static_cast<::TransformSelector*>(selector.get()), args); + return *this; +} - static_cast<::TransformSelector&>(*selector).Transform( - this->Values, [&descriptor](std::string const& s) -> std::string { - return (*descriptor->Transform)(s); - }); +cmList& cmList::transform(TransformAction action, std::string const& arg, + cmMakefile& makefile, + std::unique_ptr<TransformSelector> selector) +{ + // This overload performs APPLY unconditionally. Without this check the + // other arity-1 actions, APPEND and PREPEND, would pass the arity + // validation below and then silently run APPLY instead. + if (action != TransformAction::APPLY) { + throw transform_error( + "sub-command TRANSFORM: only action APPLY accepts a cmMakefile."); + } + + // Validates the arity and defaults the selector. + TransformConfigure(action, selector, 1); + + auto& sel = static_cast<::TransformSelector&>(*selector); + TransformActionApply applyAction(sel, arg, makefile); + + sel.Transform(this->Values, + [&applyAction](std::string const& s) -> std::string { + return applyAction(s); + }); return *this; }
diff --git a/Source/cmList.h b/Source/cmList.h index 74a3420..cccfb2c 100644 --- a/Source/cmList.h +++ b/Source/cmList.h
@@ -821,8 +821,11 @@ EXCLUDE }; // Includes or removes items from the list - // Throw std::invalid_argument if regular expression is invalid + // Throw std::invalid_argument if regular expression is invalid or predicate + // function is unknown / does not set its output variable cmList& filter(cm::string_view regex, FilterMode mode); + cmList& filter(std::string const& functionName, FilterMode mode, + cmMakefile& makefile); cmList& reverse() { @@ -845,6 +848,7 @@ STRING, FILE_BASENAME, NATURAL, + COMPARATOR, } Compare = CompareMethod::DEFAULT; enum class CaseSensitivity @@ -854,6 +858,8 @@ INSENSITIVE, } Case = CaseSensitivity::DEFAULT; + std::string ComparatorFunction; + // declare the default constructor to work-around clang bug SortConfiguration(); @@ -866,6 +872,7 @@ } }; cmList& sort(SortConfiguration config = SortConfiguration{}); + cmList& sort(SortConfiguration config, cmMakefile& makefile); // exception raised on error during transform operations class transform_error : public std::runtime_error @@ -886,6 +893,7 @@ struct AT; struct FOR; struct REGEX; + struct PREDICATE; virtual ~TransformSelector() = default; @@ -911,6 +919,12 @@ template <typename Type> static std::unique_ptr<TransformSelector> New(std::string&&); + // NewPREDICATE is public (unlike NewAT/NewFOR/NewREGEX) because it takes + // a cmMakefile& parameter that cannot be dispatched through the existing + // New<Type>() templates. + static std::unique_ptr<TransformSelector> NewPREDICATE( + std::string const& functionName, cmMakefile& makefile); + cmMakefile* Makefile = nullptr; private: @@ -941,7 +955,8 @@ TOUPPER, STRIP, GENEX_STRIP, - REPLACE + REPLACE, + APPLY }; // Transforms the list by applying an action @@ -956,6 +971,9 @@ cmList& transform(TransformAction action, std::vector<std::string> const& args, std::unique_ptr<TransformSelector> = {}); + cmList& transform(TransformAction action, std::string const& arg, + cmMakefile& makefile, + std::unique_ptr<TransformSelector> = {}); std::string join(cm::string_view glue) const {
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index 60bd083..21d4f67 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx
@@ -19,7 +19,6 @@ #include "cmExecutionStatus.h" #include "cmList.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmRange.h" #include "cmStringAlgorithms.h" @@ -36,10 +35,9 @@ case cmPolicies::WARN: { // Default is to warn and use old behavior OLD behavior is to allow // compatibility, so issue a warning and use the previous behavior. - std::string warn = - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0121), - " Invalid list index \"", arg, "\"."); - mf.IssueMessage(MessageType::AUTHOR_WARNING, warn); + mf.IssuePolicyWarning( + cmPolicies::CMP0121, {}, + cmStrCat("Invalid list index \""_s, arg, "\"."_s)); CM_FALLTHROUGH; } case cmPolicies::OLD: @@ -481,7 +479,8 @@ { "TOLOWER", cmList::TransformAction::TOLOWER, 0 }, { "STRIP", cmList::TransformAction::STRIP, 0 }, { "GENEX_STRIP", cmList::TransformAction::GENEX_STRIP, 0 }, - { "REPLACE", cmList::TransformAction::REPLACE, 2 } }, + { "REPLACE", cmList::TransformAction::REPLACE, 2 }, + { "APPLY", cmList::TransformAction::APPLY, 1 } }, [](std::string const& x, std::string const& y) { return x < y; } }; @@ -519,6 +518,7 @@ std::string const REGEX{ "REGEX" }; std::string const AT{ "AT" }; std::string const FOR{ "FOR" }; + std::string const PREDICATE{ "PREDICATE" }; std::string const OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" }; std::unique_ptr<cmList::TransformSelector> selector; std::string outputName = listName; @@ -526,7 +526,8 @@ try { // handle optional arguments while (args.size() > index) { - if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) && + if ((args[index] == REGEX || args[index] == AT || args[index] == FOR || + args[index] == PREDICATE) && selector) { status.SetError( cmStrCat("sub-command TRANSFORM, selector already specified (", @@ -652,6 +653,21 @@ continue; } + // PREDICATE selector + if (args[index] == PREDICATE) { + if (args.size() == ++index) { + status.SetError("sub-command TRANSFORM, selector PREDICATE expects " + "'function name' argument."); + return false; + } + + selector = cmList::TransformSelector::NewPREDICATE( + args[index], status.GetMakefile()); + + index += 1; + continue; + } + // output variable if (args[index] == OUTPUT_VARIABLE) { if (args.size() == ++index) { @@ -683,7 +699,12 @@ } selector->Makefile = &status.GetMakefile(); - list->transform(descriptor->Action, arguments, std::move(selector)); + if (descriptor->Action == cmList::TransformAction::APPLY) { + list->transform(descriptor->Action, arguments.front(), + status.GetMakefile(), std::move(selector)); + } else { + list->transform(descriptor->Action, arguments, std::move(selector)); + } status.GetMakefile().AddDefinition(outputName, list->to_string()); return true; } catch (cmList::transform_error& e) { @@ -703,6 +724,7 @@ using SortConfig = cmList::SortConfiguration; SortConfig sortConfig; + bool hasComparator = false; size_t argumentIndex = 2; std::string const messageHint = "sub-command SORT "; @@ -710,6 +732,12 @@ while (argumentIndex < args.size()) { std::string const& option = args[argumentIndex++]; if (option == "COMPARE") { + if (hasComparator) { + status.SetError(cmStrCat(messageHint, + "option \"COMPARE\" is incompatible " + "with \"COMPARATOR\".")); + return false; + } if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { std::string error = cmStrCat(messageHint, "option \"", option, "\" has been specified multiple times."); @@ -783,6 +811,27 @@ option, "\".")); return false; } + } else if (option == "COMPARATOR") { + if (hasComparator) { + status.SetError(cmStrCat(messageHint, "option \"", option, + "\" has been specified multiple times.")); + return false; + } + if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { + status.SetError(cmStrCat(messageHint, + "option \"COMPARATOR\" is incompatible " + "with \"COMPARE\".")); + return false; + } + if (argumentIndex < args.size()) { + sortConfig.ComparatorFunction = args[argumentIndex++]; + sortConfig.Compare = SortConfig::CompareMethod::COMPARATOR; + hasComparator = true; + } else { + status.SetError(cmStrCat(messageHint, "missing argument for option \"", + option, "\".")); + return false; + } } else { status.SetError( cmStrCat(messageHint, "option \"", option, "\" is unknown.")); @@ -798,6 +847,17 @@ return true; } + if (hasComparator) { + try { + status.GetMakefile().AddDefinition( + listName, list->sort(sortConfig, status.GetMakefile()).to_string()); + return true; + } catch (std::invalid_argument& e) { + status.SetError(e.what()); + return false; + } + } + status.GetMakefile().AddDefinition(listName, list->sort(sortConfig).to_string()); return true; @@ -945,25 +1005,46 @@ } std::string const& mode = args[3]; - if (mode != "REGEX") { - status.SetError("sub-command FILTER does not recognize mode " + mode); - return false; - } - if (args.size() != 5) { - status.SetError("sub-command FILTER, mode REGEX " - "requires five arguments."); - return false; - } - std::string const& pattern = args[4]; + if (mode == "REGEX") { + if (args.size() != 5) { + status.SetError("sub-command FILTER, mode REGEX " + "requires five arguments."); + return false; + } + std::string const& pattern = args[4]; - try { - status.GetMakefile().AddDefinition( - listName, list->filter(pattern, filterMode).to_string()); - return true; - } catch (std::invalid_argument& e) { - status.SetError(e.what()); - return false; + try { + status.GetMakefile().AddDefinition( + listName, list->filter(pattern, filterMode).to_string()); + return true; + } catch (std::invalid_argument& e) { + status.SetError(e.what()); + return false; + } } + + if (mode == "PREDICATE") { + if (args.size() != 5) { + status.SetError("sub-command FILTER, mode PREDICATE " + "requires five arguments."); + return false; + } + std::string const& functionName = args[4]; + + try { + status.GetMakefile().AddDefinition( + listName, + list->filter(functionName, filterMode, status.GetMakefile()) + .to_string()); + return true; + } catch (std::invalid_argument& e) { + status.SetError(e.what()); + return false; + } + } + + status.SetError("sub-command FILTER does not recognize mode " + mode); + return false; } } // namespace
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 036788a..d7be27e 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx
@@ -13,12 +13,14 @@ #include <cm/string_view> +#include "cmDiagnostics.h" #include "cmList.h" #include "cmListFileLexer.h" +#include "cmMakefile.h" #include "cmMessageType.h" -#include "cmMessenger.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmake.h" namespace { @@ -48,12 +50,12 @@ { public: cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, - cmMessenger* messenger); + cmMakefile const* mf, std::string const& filename); cmListFileParser(cmListFileParser const&) = delete; cmListFileParser& operator=(cmListFileParser const&) = delete; - bool ParseFile(char const* filename); - bool ParseString(cm::string_view str, char const* virtual_filename); + bool ParseFile(); + bool ParseString(cm::string_view str); private: bool Parse(); @@ -74,8 +76,8 @@ cmListFile* ListFile; cmListFileBacktrace Backtrace; - cmMessenger* Messenger; - char const* FileName = nullptr; + cmMakefile const* Makefile; + std::string const& FileName; std::unique_ptr<cmListFileLexer, void (*)(cmListFileLexer*)> Lexer; std::string FunctionName; long FunctionLine; @@ -84,44 +86,52 @@ }; cmListFileParser::cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, - cmMessenger* messenger) + cmMakefile const* mf, + std::string const& filename) : ListFile(lf) , Backtrace(std::move(lfbt)) - , Messenger(messenger) + , Makefile(mf) + , FileName(filename) , Lexer(cmListFileLexer_New(), cmListFileLexer_Delete) { } void cmListFileParser::IssueFileOpenError(std::string const& text) const { - this->Messenger->IssueMessage(MessageType::FATAL_ERROR, text, - this->Backtrace); + if (this->Makefile) { + this->Makefile->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, + text, this->Backtrace); + } } void cmListFileParser::IssueError(std::string const& text) const { - cmListFileContext lfc; - lfc.FilePath = this->FileName; - lfc.Line = cmListFileLexer_GetCurrentLine(this->Lexer.get()); - cmListFileBacktrace lfbt = this->Backtrace; - lfbt = lfbt.Push(lfc); - this->Messenger->IssueMessage(MessageType::FATAL_ERROR, text, lfbt); + if (this->Makefile) { + cmListFileContext lfc; + lfc.FilePath = this->FileName; + lfc.Line = cmListFileLexer_GetCurrentLine(this->Lexer.get()); + cmListFileBacktrace lfbt = this->Backtrace; + lfbt = lfbt.Push(lfc); + this->Makefile->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, + text, lfbt); + } cmSystemTools::SetFatalErrorOccurred(); } -bool cmListFileParser::ParseFile(char const* filename) +bool cmListFileParser::ParseFile() { - this->FileName = filename; + std::string const* filename = &this->FileName; #ifdef _WIN32 std::string expandedFileName = cmsys::Encoding::ToNarrow( - cmSystemTools::ConvertToWindowsExtendedPath(filename)); - filename = expandedFileName.c_str(); + cmSystemTools::ConvertToWindowsExtendedPath(*filename)); + filename = &expandedFileName; #endif // Open the file. cmListFileLexer_BOM bom; - if (!cmListFileLexer_SetFileName(this->Lexer.get(), filename, &bom)) { + if (!cmListFileLexer_SetFileName(this->Lexer.get(), filename->c_str(), + &bom)) { this->IssueFileOpenError("cmListFileCache: error can not open file."); return false; } @@ -137,11 +147,8 @@ return this->Parse(); } -bool cmListFileParser::ParseString(cm::string_view str, - char const* virtual_filename) +bool cmListFileParser::ParseString(cm::string_view str) { - this->FileName = virtual_filename; - if (!cmListFileLexer_SetString(this->Lexer.get(), str.data(), str.length())) { this->IssueFileOpenError("cmListFileCache: cannot allocate buffer."); @@ -194,10 +201,12 @@ // Check if all functions are nested properly. if (auto badNesting = this->CheckNesting()) { - this->Messenger->IssueMessage( - MessageType::FATAL_ERROR, - "Flow control statements are not properly nested.", - this->Backtrace.Push(*badNesting)); + if (this->Makefile) { + this->Makefile->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + "Flow control statements are not properly nested.", + this->Backtrace.Push(*badNesting)); + } cmSystemTools::SetFatalErrorOccurred(); return false; } @@ -286,16 +295,18 @@ } } - cmListFileContext lfc; - lfc.FilePath = this->FileName; - lfc.Line = line; - cmListFileBacktrace lfbt = this->Backtrace; - lfbt = lfbt.Push(lfc); - this->Messenger->IssueMessage( - MessageType::FATAL_ERROR, - "Parse error. Function missing ending \")\". " - "End of file reached.", - lfbt); + if (this->Makefile) { + cmListFileContext lfc; + lfc.FilePath = this->FileName; + lfc.Line = line; + cmListFileBacktrace lfbt = this->Backtrace; + lfbt = lfbt.Push(lfc); + this->Makefile->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + "Parse error. Function missing ending \")\". " + "End of file reached.", + lfbt); + } return false; } @@ -320,10 +331,15 @@ "\n" "Argument not separated from preceding token by whitespace."); if (isError) { - this->Messenger->IssueMessage(MessageType::FATAL_ERROR, msg, lfbt); + if (this->Makefile) { + this->Makefile->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, msg, lfbt); + } return false; } - this->Messenger->IssueMessage(MessageType::AUTHOR_WARNING, msg, lfbt); + if (this->Makefile) { + this->Makefile->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, msg, lfbt); + } return true; } @@ -422,7 +438,7 @@ } // anonymous namespace -bool cmListFile::ParseFile(char const* filename, cmMessenger* messenger, +bool cmListFile::ParseFile(std::string const& filename, cmMakefile const* mf, cmListFileBacktrace const& lfbt) { if (!cmSystemTools::FileExists(filename) || @@ -430,28 +446,17 @@ return false; } - bool parseError = false; - - { - cmListFileParser parser(this, lfbt, messenger); - parseError = !parser.ParseFile(filename); - } - - return !parseError; + cmListFileParser parser(this, lfbt, mf, filename); + return parser.ParseFile(); } -bool cmListFile::ParseString(cm::string_view str, char const* virtual_filename, - cmMessenger* messenger, +bool cmListFile::ParseString(cm::string_view str, + std::string const& virtual_filename, + cmMakefile const* mf, cmListFileBacktrace const& lfbt) { - bool parseError = false; - - { - cmListFileParser parser(this, lfbt, messenger); - parseError = !parser.ParseString(str, virtual_filename); - } - - return !parseError; + cmListFileParser parser(this, lfbt, mf, virtual_filename); + return parser.ParseString(str); } #include "cmConstStack.tcc"
diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index fd3b7b4..337fc26 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h
@@ -6,6 +6,7 @@ #include <iosfwd> #include <memory> +#include <set> #include <string> #include <utility> #include <vector> @@ -24,7 +25,7 @@ * cmake list files. */ -class cmMessenger; +class cmMakefile; struct cmListFileArgument { @@ -205,6 +206,37 @@ std::ostream& operator<<(std::ostream& os, BT<std::string> const& s); +namespace cm { +// Helpers for basic type retrieval +template <typename T> +T remove_BT(BT<T> const& bt) +{ + return bt.Value; +} + +template <typename T> +std::vector<T> remove_BT(std::vector<BT<T>> const& container) +{ + std::vector<T> result; + result.reserve(container.size()); + for (auto const& entry : container) { + result.emplace_back(entry.Value); + } + + return result; +} +template <typename T> +std::set<T> remove_BT(std::set<BT<T>> const& container) +{ + std::set<T> result; + for (auto const& entry : container) { + result.emplace(entry.Value); + } + + return result; +} +} + // Wrap type T as a value with potentially multiple backtraces. For purposes // of ordering and equality comparison, only the original value is used. The // backtrace is considered incidental. @@ -238,11 +270,11 @@ struct cmListFile { - bool ParseFile(char const* path, cmMessenger* messenger, + bool ParseFile(std::string const& path, cmMakefile const* mf, cmListFileBacktrace const& lfbt); - bool ParseString(cm::string_view str, char const* virtual_filename, - cmMessenger* messenger, cmListFileBacktrace const& lfbt); + bool ParseString(cm::string_view str, std::string const& virtual_filename, + cmMakefile const* mf, cmListFileBacktrace const& lfbt); std::vector<cmListFileFunction> Functions; };
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 2af0dc0..844046f 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx
@@ -32,9 +32,11 @@ #include "cmCustomCommandGenerator.h" #include "cmCustomCommandLines.h" #include "cmCustomCommandTypes.h" +#include "cmFileSetMetadata.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionEvaluationFile.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmInstallGenerator.h" @@ -48,6 +50,7 @@ #include "cmObjectLocation.h" #include "cmRange.h" #include "cmRulePlaceholderExpander.h" +#include "cmScriptGenerator.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" @@ -231,15 +234,33 @@ { return cm::make_unique<cmRulePlaceholderExpander>( buildStep, this->Compilers, this->VariableMappings, this->CompilerSysroot, - this->LinkerSysroot); + this->LinkerSysroot, + this->GetState()->UseWatcomWMake() || this->GetState()->UseBorlandMake() + ? cmRulePlaceholderExpander::UseShortPaths::Yes + : cmRulePlaceholderExpander::UseShortPaths::No); } cmLocalGenerator::~cmLocalGenerator() = default; -void cmLocalGenerator::IssueMessage(MessageType t, - std::string const& text) const +void cmLocalGenerator::IssueMessage(MessageType type, std::string const& text, + cmListFileBacktrace const& bt) const { - this->GetCMakeInstance()->IssueMessage(t, text, this->DirectoryBacktrace); + this->GetMakefile()->IssueMessage(type, text, bt); +} + +void cmLocalGenerator::IssueDiagnostic( + cmDiagnosticCategory category, std::string const& text, + cmDiagnosticContext const& context) const +{ + this->GetMakefile()->IssueDiagnostic(category, text, context); +} + +void cmLocalGenerator::IssuePolicyWarning(cmPolicies::PolicyID policy, + cm::string_view preface, + cm::string_view postface, + cmListFileBacktrace const& bt) const +{ + this->GetMakefile()->IssuePolicyWarning(policy, preface, postface, bt); } void cmLocalGenerator::ComputeObjectMaxPath() @@ -261,14 +282,14 @@ w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax << ", which is less than the minimum of 128. " "The value will be ignored."; - this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + this->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, w.str()); } } else { std::ostringstream w; w << "CMAKE_OBJECT_PATH_MAX is set to \"" << *plen << "\", which fails to parse as a positive integer. " "The value will be ignored."; - this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + this->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, w.str()); } } this->ObjectMaxPathViolations.clear(); @@ -389,8 +410,7 @@ // TODO: Use add_subdirectory instead? std::string outP = i.GetDirectory().GetCurrentBinary(); outP = this->MaybeRelativeToCurBinDir(outP); - outP = cmOutputConverter::EscapeForCMake(outP); - fout << "subdirs(" << outP << ")\n"; + fout << "subdirs(" << cmScriptGenerator::Quote(outP) << ")\n"; } // Add directory labels property @@ -401,13 +421,13 @@ if (labels || directoryLabels) { fout << "set_directory_properties(PROPERTIES LABELS "; if (labels) { - fout << cmOutputConverter::EscapeForCMake(*labels); + fout << cmScriptGenerator::Quote(*labels); } if (labels && directoryLabels) { fout << ";"; } if (directoryLabels) { - fout << cmOutputConverter::EscapeForCMake(*directoryLabels); + fout << cmScriptGenerator::Quote(*directoryLabels); } fout << ")\n"; } @@ -707,9 +727,7 @@ if (haveInstallAfterSubdirectory && this->Makefile->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0082")) { - std::ostringstream e; - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0082) << "\n"; - this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + this->IssuePolicyWarning(cmPolicies::CMP0082); } CM_FALLTHROUGH; case cmPolicies::OLD: { @@ -1552,7 +1570,9 @@ } } break; case cmStateEnums::EXECUTABLE: { - if (linkLanguage != "Swift") { + if (linkLanguage != "Swift" || + (this->IsSplitSwiftBuild() && + target->GetPolicyStatusCMP0214() == cmPolicies::NEW)) { std::string exeFlags; this->AddTargetTypeLinkerFlags(exeFlags, target, linkLanguage, config); if (!exeFlags.empty()) { @@ -1696,8 +1716,8 @@ case cmSwiftCompileMode::Singlefile: break; case cmSwiftCompileMode::Unknown: { - this->IssueMessage( - MessageType::AUTHOR_WARNING, + this->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Unknown Swift_COMPILATION_MODE on target '", target->GetName(), '\'')); } @@ -2103,14 +2123,7 @@ (compilerId == "OpenWatcom" || compilerSimulateId == "OpenWatcom"); if (lang == "Swift") { - if (cmValue v = target->GetProperty("Swift_LANGUAGE_VERSION")) { - if (cmSystemTools::VersionCompare( - cmSystemTools::OP_GREATER_EQUAL, - this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"), - "4.2")) { - this->AppendFlags(flags, "-swift-version " + *v); - } - } + target->AddSwiftTargetFlags(flags); } else if (lang == "CUDA") { target->AddCUDAArchitectureFlags(compileOrLink, config, flags); target->AddCUDAToolkitFlags(flags); @@ -2130,6 +2143,8 @@ } } else if (lang == "HIP") { target->AddHIPArchitectureFlags(compileOrLink, config, flags); + } else if (lang == "Rust") { + target->AddRustTargetFlags(flags); } // Add VFS Overlay for Clang compilers @@ -2307,7 +2322,8 @@ bool cmLocalGenerator::GetRealDependency(std::string const& inName, std::string const& config, - std::string& dep) + std::string& dep, + cmPolicies::PolicyStatus cmp0212) { // Older CMake code may specify the dependency using the target // output file rather than the target name. Such code would have @@ -2321,12 +2337,21 @@ if (name.empty()) { return false; } - if (cmHasSuffix(name, ".exe"_s)) { - name = cmSystemTools::GetFilenameWithoutLastExtension(name); - } // Look for a CMake target with the given name. - if (cmGeneratorTarget* target = this->FindGeneratorTargetToUse(name)) { + cmGeneratorTarget* target = this->FindGeneratorTargetToUse(name); + if (!target && cmHasSuffix(name, ".exe"_s) && cmp0212 != cmPolicies::NEW) { + // If it doesn't exist, try to strip the `.exe` suffix per CMP0212. + std::string strippedName = + cmSystemTools::GetFilenameWithoutLastExtension(name); + if (cmGeneratorTarget* strippedTarget = + this->FindGeneratorTargetToUse(strippedName)) { + name = strippedName; + target = strippedTarget; + } + } + + if (target) { // make sure it is not just a coincidence that the target name // found is part of the inName if (cmSystemTools::FileIsFullPath(inName)) { @@ -2614,13 +2639,10 @@ if (!this->Makefile->GetCMakeInstance()->GetIsInTryCompile() && this->Makefile->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0181")) { - this->Makefile->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0181), - "\nSince the policy is not set, the contents of variable '", - name, - "' will " - "be used as is."), + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0181, {}, + cmStrCat("Since the policy is not set, the contents of variable '"_s, + name, "' will be used as is."_s), target->GetBacktrace()); } CM_FALLTHROUGH; @@ -2685,8 +2707,11 @@ cmStrCat(headerDir, '/', ispcSource, *ispcHeaderSuffixProp); target->AddISPCGeneratedHeader(headerPath, config); if (extra_objects) { - std::vector<std::string> objs = detail::ComputeISPCExtraObjects( - objectName, rootObjectDir, ispcArchSuffixes); + std::vector<std::pair<cmSourceFile const*, std::string>> objs; + for (auto& obj : detail::ComputeISPCExtraObjects( + objectName, rootObjectDir, ispcArchSuffixes)) { + objs.push_back({ sf, std::move(obj) }); + } target->AddISPCGeneratedObject(std::move(objs), config); } } @@ -2721,9 +2746,12 @@ for (std::string const& lang : langs) { auto langSources = std::count_if( - sources.begin(), sources.end(), [lang](cmSourceFile* sf) { + sources.begin(), sources.end(), + [&target, &config, &lang](cmSourceFile* sf) { + auto const* const fileSet = target->GetFileSetForSource(config, sf); return lang == sf->GetLanguage() && - !sf->GetProperty("SKIP_PRECOMPILE_HEADERS"); + !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + sf->GetProperty("SKIP_PRECOMPILE_HEADERS")); }); if (langSources == 0) { continue; @@ -3269,6 +3297,13 @@ std::vector<cmSourceFile*> sources; target->GetSourceFiles(sources, configs[ci]); for (cmSourceFile* sf : sources) { + cmGeneratorFileSet const* fileSet = + target->GetFileSetForSource(configs[ci], sf); + if (fileSet && + !cm::FileSetMetadata::GetAttributes(fileSet->GetType()) + .contains(cm::FileSetMetadata::FileSetAttributes::UnityBuild)) { + continue; + } // Files which need C++ scanning cannot participate in unity builds as // there is a single place in TUs that may perform module-dependency bits // and a unity source cannot `#include` them in-order and represent a @@ -3307,18 +3342,38 @@ for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX", "CUDA" }) { std::vector<UnityBatchedSource> filtered_sources; - std::copy_if(unitySources.begin(), unitySources.end(), - std::back_inserter(filtered_sources), - [&](UnityBatchedSource const& ubs) -> bool { - cmSourceFile* sf = ubs.Source; - return sf->GetLanguage() == lang && - !sf->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") && - !sf->GetPropertyAsBool("HEADER_FILE_ONLY") && - !sf->GetProperty("COMPILE_OPTIONS") && - !sf->GetProperty("COMPILE_DEFINITIONS") && - !sf->GetProperty("COMPILE_FLAGS") && - !sf->GetProperty("INCLUDE_DIRECTORIES"); - }); + std::copy_if( + unitySources.begin(), unitySources.end(), + std::back_inserter(filtered_sources), + [&](UnityBatchedSource const& ubs) -> bool { + cmSourceFile* sf = ubs.Source; + if (sf->GetLanguage() != lang) { + return false; + } + for (auto idx : ubs.Configs) { + cmGeneratorFileSet const* fileSet = + target->GetFileSetForSource(configs[idx], sf); + if (fileSet && + (fileSet->GetProperty("SKIP_UNITY_BUILD_INCLUSION").IsOn() || + fileSet->GetProperty(fileSet->BelongsTo(target) + ? "COMPILE_OPTIONS" + : "INTERFACE_COMPILE_OPTIONS") || + fileSet->GetProperty(fileSet->BelongsTo(target) + ? "COMPILE_DEFINITIONS" + : "INTERFACE_COMPILE_DEFINITIONS") || + fileSet->GetProperty(fileSet->BelongsTo(target) + ? "INCLUDE_DIRECTORIES" + : "INTERFACE_INCLUDE_DIRECTORIES"))) { + return false; + } + } + return !sf->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") && + !sf->GetPropertyAsBool("HEADER_FILE_ONLY") && + !sf->GetProperty("COMPILE_OPTIONS") && + !sf->GetProperty("COMPILE_DEFINITIONS") && + !sf->GetProperty("COMPILE_FLAGS") && + !sf->GetProperty("INCLUDE_DIRECTORIES"); + }); std::vector<UnitySource> unity_files; if (!unityMode || *unityMode == "BATCH") { @@ -3392,12 +3447,12 @@ this->Makefile->GetSafeDefinition( cmStrCat("CMAKE_EXECUTABLE_CREATE_", lang, "_FLAGS")) && this->GlobalGenerator->ShouldWarnCMP0210(lang)) { - this->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0210), "\n", - "For compatibility with older versions of CMake, ", - "CMAKE_", lang, "_LINK_FLAGS will be ignored for all ", - "non-EXECUTABLE targets which use these flags.")); + this->IssuePolicyWarning( + cmPolicies::CMP0210, {}, + cmStrCat("For compatibility with older versions of CMake, CMAKE_"_s, + lang, + "_LINK_FLAGS will be ignored for all non-EXECUTABLE " + "targets which use these flags."_s)); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -3590,8 +3645,12 @@ } char const* PICValue = target->GetLinkPIEProperty(config); - if (!PICValue) { - // POSITION_INDEPENDENT_CODE is not set + if (!PICValue && lang != "Rust") { + // POSITION_INDEPENDENT_CODE is not set, note that for Rust we do not + // return as the compiler tends to enable PIE all the time, which is the + // opposite of what C & C++ compilers do. So instead of letting the rust + // compiler decide on its own whether PIE should be enabled, we explicit + // set it. return; } @@ -3885,6 +3944,17 @@ } } +void cmLocalGenerator::AppendDefines( + std::set<std::string>& defines, + std::vector<BT<std::string>> const& defines_vec) const +{ + std::set<BT<std::string>> tmp; + this->AppendDefines(tmp, defines_vec); + for (BT<std::string> const& i : tmp) { + defines.emplace(i.Value); + } +} + void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines, std::string const& defines_list) const { @@ -4668,6 +4738,20 @@ *hasSourceExtension = keptSourceExtension; } + if (source.GetLanguage() == "Rust") { + cmValue const rustEmit = source.GetRustEmitProperty(); + // Rust requires any rlib to start with lib prefix on all platforms to + // allow linking to them as crate. So we enforce having lib prefix for rust + // "object" files. + if (rustEmit == "link") { + cmCMakePath objectPath(objectName); + std::string const objectFileName = + "lib" + objectPath.GetFileName().String(); + objectPath.ReplaceFileName(objectFileName); + objectName = objectPath.String(); + } + } + // Convert to a safe name. return this->CreateSafeUniqueObjectFileName(objectName, dir_max); } @@ -5184,9 +5268,15 @@ // transform targets into the suffixes auto pos = ispcTarget.find('-'); auto target_suffix = ispcTarget.substr(0, pos); + // ISPC uses underscores in output file suffixes where the target name + // has dots (e.g. "avx10.2dmr" produces files with "_avx10_2dmr" suffix) + std::replace(target_suffix.begin(), target_suffix.end(), '.', '_'); if (target_suffix == "avx1") { // when targeting avx1 ISPC uses the 'avx' output string target_suffix = "avx"; + } else if (target_suffix == "sse4_1" || target_suffix == "sse4_2") { + // when targeting sse4.1 or sse4.2 ISPC uses the 'sse4' output string + target_suffix = "sse4"; } ispcTarget = target_suffix; }
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 50e710f..b8574a8 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h
@@ -15,8 +15,11 @@ #include <vector> #include <cm/optional> +#include <cm/string_view> #include "cmCustomCommandTypes.h" +#include "cmDiagnosticContext.h" +#include "cmDiagnostics.h" #include "cmGeneratorOptions.h" #include "cmGeneratorTarget.h" #include "cmListFileCache.h" @@ -265,6 +268,8 @@ */ void AppendDefines(std::set<std::string>& defines, std::string const& defines_list) const; + void AppendDefines(std::set<std::string>& defines, + std::vector<BT<std::string>> const& defines_vec) const; void AppendDefines(std::set<BT<std::string>>& defines, std::string const& defines_list) const; void AppendDefines(std::set<BT<std::string>>& defines, @@ -309,7 +314,7 @@ * used for dependencies of custom commands. */ bool GetRealDependency(std::string const& name, std::string const& config, - std::string& dep); + std::string& dep, cmPolicies::PolicyStatus cmp0212); /** Called from command-line hook to clear dependencies. */ virtual void ClearDependencies(cmMakefile* /* mf */, bool /* verbose */) {} @@ -569,7 +574,35 @@ bool IsNinjaMulti() const; bool IsWindowsVSIDE() const; - void IssueMessage(MessageType t, std::string const& text) const; + void IssueMessage(MessageType type, std::string const& text) const + { + this->IssueMessage(type, text, this->DirectoryBacktrace); + } + void IssueMessage(MessageType type, std::string const& text, + cmListFileBacktrace const& bt) const; + void IssueDiagnostic(cmDiagnosticCategory category, + std::string const& text) const + { + this->IssueDiagnostic(category, text, + cmDiagnosticContext{ this->DirectoryBacktrace }); + } + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmListFileBacktrace const& bt) const + { + this->IssueDiagnostic(category, text, cmDiagnosticContext{ bt }); + } + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmDiagnosticContext const& context) const; + void IssuePolicyWarning(cmPolicies::PolicyID policy, cm::string_view preface, + cm::string_view postface, + cmListFileBacktrace const& bt) const; + void IssuePolicyWarning(cmPolicies::PolicyID policy, + cm::string_view preface = {}, + cm::string_view postface = {}) const + { + this->IssuePolicyWarning(policy, preface, postface, + this->DirectoryBacktrace); + } void CreateEvaluationFileOutputs(); void CreateEvaluationFileOutputs(std::string const& config);
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index b6cc3a6..b29807f 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx
@@ -19,6 +19,7 @@ #include "cmCryptoHash.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" +#include "cmDiagnostics.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" @@ -28,7 +29,6 @@ #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmNinjaTargetGenerator.h" #include "cmNinjaTypes.h" #include "cmPolicies.h" @@ -414,7 +414,8 @@ { for (std::string const& i : ccg.GetDepends()) { std::string dep; - if (this->GetRealDependency(i, config, dep)) { + if (this->GetRealDependency(i, config, dep, + ccg.GetCC().GetCMP0212Status())) { ninjaDeps.push_back( this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(dep)); } @@ -712,8 +713,8 @@ this->GetBinaryDirectory() || this->Makefile->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0116")) { - this->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, + this->GetCMakeInstance()->IssueDiagnostic( + cmDiagnostics::CMD_POLICY, cmPolicies::GetPolicyWarning(cmPolicies::CMP0116), cc->GetBacktrace()); }
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index d9b2c0f..ab6d345 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -37,6 +37,7 @@ #include "cmOutputConverter.h" #include "cmRange.h" #include "cmRulePlaceholderExpander.h" +#include "cmScriptGenerator.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateSnapshot.h" @@ -956,7 +957,8 @@ for (std::string const& d : ccg.GetDepends()) { // Lookup the real name of the dependency in case it is a CMake target. std::string dep; - if (this->GetRealDependency(d, this->GetConfigName(), dep)) { + if (this->GetRealDependency(d, this->GetConfigName(), dep, + ccg.GetCC().GetCMP0212Status())) { depends.push_back(std::move(dep)); } } @@ -1154,7 +1156,7 @@ fout << "file(REMOVE_RECURSE\n"; for (std::string const& file : files) { std::string fc = this->MaybeRelativeToCurBinDir(file); - fout << " " << cmOutputConverter::EscapeForCMake(fc) << '\n'; + fout << " " << cmScriptGenerator::Quote(fc) << '\n'; } fout << ")\n"; } @@ -1215,7 +1217,7 @@ for (std::string const& cfl : cleanFiles) { std::string fc = rootLG->MaybeRelativeToCurBinDir( cmSystemTools::CollapseFullPath(cfl, currentBinaryDir)); - fout << " " << cmOutputConverter::EscapeForCMake(fc) << '\n'; + fout << " " << cmScriptGenerator::Quote(fc) << '\n'; } fout << ")\n"; } @@ -1358,10 +1360,10 @@ // we must shorten the combined string by 4 characters // keep no more than 24 characters from the second string if (static_cast<int>(str2.size()) > keep) { - str2 = str2.substr(0, keep); + str2.resize(keep); } if (static_cast<int>(str1.size()) + static_cast<int>(str2.size()) > size) { - str1 = str1.substr(0, size - str2.size()); + str1.resize(size - str2.size()); } char buffer[12]; int ni = 0; @@ -2027,8 +2029,7 @@ "set(CMAKE_TARGET_DEFINITIONS_" << lang << '\n'; for (std::string const& define : defines) { - cmakefileStream << " " << cmOutputConverter::EscapeForCMake(define) - << '\n'; + cmakefileStream << " " << cmScriptGenerator::Quote(define) << '\n'; } cmakefileStream << " )\n"; } @@ -2068,8 +2069,7 @@ if (!transformRules.empty()) { cmakefileStream << "\nset(CMAKE_INCLUDE_TRANSFORMS\n"; for (std::string const& tr : transformRules) { - cmakefileStream << " " << cmOutputConverter::EscapeForCMake(tr) - << '\n'; + cmakefileStream << " " << cmScriptGenerator::Quote(tr) << '\n'; } cmakefileStream << " )\n"; }
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index a6bf9c8..996b1cc 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -24,6 +24,8 @@ #include "cmCustomCommandLines.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorOptions.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -1497,6 +1499,9 @@ } cmGeneratorExpressionInterpreter genexInterpreter(lg, config, gt, lang); + // lookup for the associated file set, if any. + auto const* fileSet = + gt->GetGeneratorFileSets()->GetFileSetForSource(config, &sf); bool needfc = false; if (!objectName.empty()) { @@ -1515,9 +1520,20 @@ genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); needfc = true; } + // Add flags from file set properties. + if (fileSet) { + auto options = fileSet->BelongsTo(gt) + ? fileSet->GetCompileOptions(config, lang) + : fileSet->GetInterfaceCompileOptions(config, lang); + if (!options.empty()) { + lg->AppendCompileOptions(fc.CompileFlags, cm::remove_BT(options)); + } + } // Add precompile headers compile options. std::string const pchSource = gt->GetPchSource(config, lang); - if (!pchSource.empty() && !sf.GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!pchSource.empty() && + !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + sf.GetProperty("SKIP_PRECOMPILE_HEADERS"))) { std::string pchOptions; if (sf.GetFullPath() == pchSource) { pchOptions = gt->GetPchCreateCompileOptions(config, lang); @@ -1571,10 +1587,36 @@ genexInterpreter.Evaluate(*ccdefs, COMPILE_DEFINITIONS); needfc = true; } + // Add file set preprocessor definitions + if (fileSet) { + auto defines = fileSet->BelongsTo(gt) + ? fileSet->GetCompileDefinitions(config, lang) + : fileSet->GetInterfaceCompileDefinitions(config, lang); + if (!defines.empty()) { + if (!fc.CompileDefs.empty()) { + fc.CompileDefs += ';'; + } + fc.CompileDefs += cmList::to_string(defines); + needfc = true; + } + } + // Add file set include directories definitions + if (fileSet) { + auto includes = fileSet->BelongsTo(gt) + ? fileSet->GetIncludeDirectories(config, lang) + : fileSet->GetInterfaceIncludeDirectories(config, lang); + if (!includes.empty()) { + fc.IncludeDirs = cmList::to_string(includes); + needfc = true; + } + } std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); if (cmValue cincs = sf.GetProperty(INCLUDE_DIRECTORIES)) { - fc.IncludeDirs = genexInterpreter.Evaluate(*cincs, INCLUDE_DIRECTORIES); + if (!fc.IncludeDirs.empty()) { + fc.IncludeDirs += ';'; + } + fc.IncludeDirs += genexInterpreter.Evaluate(*cincs, INCLUDE_DIRECTORIES); needfc = true; } @@ -1597,7 +1639,11 @@ !cm::contains(acs.Configs, ci) || (gt->GetPropertyAsBool("UNITY_BUILD") && sf.GetProperty("UNITY_SOURCE_FILE") && - !sf.GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION")); + !((fileSet && + (!cm::FileSetMetadata::GetAttributes(fileSet->GetType()) + .contains(cm::FileSetMetadata::FileSetAttributes::UnityBuild) || + fileSet->GetProperty("SKIP_UNITY_BUILD_INCLUSION").IsOn())) || + sf.GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION"))); if (fc.ExcludedFromBuild) { needfc = true; } @@ -1870,7 +1916,8 @@ for (std::string const& d : ccg.GetDepends()) { // Get the real name of the dependency in case it is a CMake target. std::string dep; - if (this->GetRealDependency(d, config, dep)) { + if (this->GetRealDependency(d, config, dep, + command.GetCMP0212Status())) { fout << this->ConvertToXMLOutputPath(dep) << ";"; } }
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index c1506a4..9025c59 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx
@@ -10,6 +10,7 @@ #include <cmext/algorithm> #include <cmext/string_view> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmFunctionBlocker.h" #include "cmList.h" @@ -18,6 +19,7 @@ #include "cmPolicies.h" #include "cmRange.h" #include "cmState.h" +#include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -37,6 +39,7 @@ std::vector<std::string> Args; std::vector<cmListFileFunction> Functions; cmPolicies::PolicyMap Policies; + cmDiagnostics::DiagnosticMap Diagnostics; std::string FilePath; }; @@ -60,9 +63,6 @@ return false; } - cmMakefile::MacroPushPop macroScope(&makefile, this->FilePath, - this->Policies); - // set the value of argc std::string argcDef = std::to_string(expandedArgs.size()); @@ -70,6 +70,21 @@ std::string expandedArgn = cmList::to_string(cmMakeRange(expIt, expandedArgs.end())); std::string expandedArgv = cmList::to_string(expandedArgs); + { + cmPolicies::PolicyStatus cmp0219 = + makefile.CheckCMP0219(this->Args[0], expandedArgs); + if (cmp0219 == cmPolicies::NEW) { + // Escape macro argument backslashes so that callees receive the same + // values the caller had. + for (std::string& expandedArg : expandedArgs) { + cmSystemTools::ReplaceString(expandedArg, "\\", "\\\\"); + } + cmSystemTools::ReplaceString(expandedArgn, "\\", "\\\\"); + cmSystemTools::ReplaceString(expandedArgv, "\\", "\\\\"); + } else if (cmp0219 == cmPolicies::WARN) { + makefile.IssueCMP0219Warning(this->Args[0], expandedArgs); + } + } std::vector<std::string> variables; variables.reserve(this->Args.size() - 1); for (unsigned int j = 1; j < this->Args.size(); ++j) { @@ -80,6 +95,10 @@ for (unsigned int j = 0; j < expandedArgs.size(); ++j) { argVs.emplace_back(cmStrCat("${ARGV", j, '}')); } + + cmMakefile::MacroPushPop macroScope(&makefile, this->FilePath, + this->Policies, this->Diagnostics); + // Invoke all the functions that were collected in the block. // for each function for (cmListFileFunction const& func : this->Functions) { @@ -133,6 +152,10 @@ inStatus.SetBreakInvoked(); return true; } + if (status.GetContinueInvoked()) { + inStatus.SetContinueInvoked(); + return true; + } if (status.HasExitCode()) { inStatus.SetExitCode(status.GetExitCode()); return true; @@ -168,15 +191,20 @@ cmExecutionStatus& status) { cmMakefile& mf = status.GetMakefile(); - mf.AppendProperty("MACROS", this->Args[0]); + if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) != + cmPolicies::NEW) { + + mf.AppendProperty("MACROS", this->Args[0]); + } // create a new command and add it to cmake cmMacroHelperCommand f; f.Args = this->Args; f.Functions = std::move(functions); f.FilePath = this->GetStartingContext().FilePath; mf.RecordPolicies(f.Policies); + mf.RecordDiagnostics(f.Diagnostics); return mf.GetState()->AddScriptedCommand( - this->Args[0], + this->Args[0], cmStateEnums::CommandType::Macro, BT<cmState::Command>(std::move(f), mf.GetBacktrace().Push(this->GetStartingContext())), mf);
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 16ed8d6..fabf334 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx
@@ -5,6 +5,7 @@ #include "cmMakefile.h" #include <algorithm> +#include <array> #include <cassert> #include <cstdio> #include <cstdlib> @@ -29,6 +30,9 @@ #include "cmsys/RegularExpression.hxx" #include "cmsys/String.h" +#ifndef CMAKE_BOOTSTRAP +# include "cmBuildSbomGenerator.h" +#endif #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" #include "cmCustomCommandTypes.h" @@ -109,6 +113,15 @@ FileScopeBase(cmMakefile* mf) : Makefile(mf) { +#if !defined(CMAKE_BOOTSTRAP) + this->Makefile->GetGlobalGenerator()->GetFileLockPool().PushFileScope(); +#endif + } + ~FileScopeBase() + { +#if !defined(CMAKE_BOOTSTRAP) + this->Makefile->GetGlobalGenerator()->GetFileLockPool().PopFileScope(); +#endif } void PushListFileVars(std::string const& newCurrent) { @@ -174,8 +187,9 @@ this->StateSnapshot.GetState()->CreatePolicyScopeSnapshot( this->StateSnapshot); - // Enter a policy level for this directory. + // Enter a policy and diagnostic level for this directory. this->PushPolicy(); + this->PushDiagnostic(); // push empty loop block this->PushLoopBlockBarrier(); @@ -212,7 +226,8 @@ return std::string(buf); } -void cmMakefile::IssueMessage(MessageType t, std::string const& text) const +void cmMakefile::IssueMessage(MessageType t, std::string const& text, + cmListFileBacktrace const& bt) const { if (!this->ExecutionStatusStack.empty()) { if ((t == MessageType::FATAL_ERROR) || @@ -220,7 +235,38 @@ this->ExecutionStatusStack.back()->SetNestedError(); } } - this->GetCMakeInstance()->IssueMessage(t, text, this->Backtrace); + this->GetCMakeInstance()->IssueMessage(t, text, bt); +} + +void cmMakefile::IssueDiagnostic(cmDiagnosticCategory category, + std::string const& text, + cmDiagnosticContext const& context) const +{ + if (!this->ExecutionStatusStack.empty()) { + cmDiagnosticAction const action = this->GetDiagnosticAction(category); + if (action >= cmDiagnosticAction::SendError) { + this->ExecutionStatusStack.back()->SetNestedError(); + } + } + this->GetCMakeInstance()->IssueDiagnostic(category, text, + this->GetStateSnapshot(), context); +} + +void cmMakefile::IssuePolicyWarning(cmPolicies::PolicyID policy, + cm::string_view preface, + cm::string_view postface, + cmListFileBacktrace const& bt) const +{ + std::string msg = cmPolicies::GetPolicyWarning(policy); + if (!preface.empty() && !postface.empty()) { + msg = cmStrCat(preface, '\n', msg, '\n', postface); + } else if (!preface.empty()) { + msg = cmStrCat(preface, '\n', msg); + } else if (!postface.empty()) { + msg = cmStrCat(msg, '\n', postface); + } + this->IssueDiagnostic(cmDiagnostics::CMD_POLICY, msg, + cmDiagnosticContext{ bt }); } Message::LogLevel cmMakefile::GetCurrentLogLevel() const @@ -262,7 +308,7 @@ { // Warn if a <PackageName>_ROOT variable we may use is set. if ((rootDef || rootEnv) && this->WarnedCMP0074.insert(rootVar).second) { - auto e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0074), '\n'); + std::string e; if (rootDef) { e += cmStrCat("CMake variable ", rootVar, " is set to:\n ", *rootDef, '\n'); @@ -272,7 +318,7 @@ *rootEnv, '\n'); } e += "For compatibility, CMake is ignoring the variable."; - this->IssueMessage(MessageType::AUTHOR_WARNING, e); + this->IssuePolicyWarning(cmPolicies::CMP0074, {}, e); } } @@ -281,7 +327,7 @@ { // Warn if a <PACKAGENAME>_ROOT variable we may use is set. if ((rootDef || rootEnv) && this->WarnedCMP0144.insert(rootVar).second) { - auto e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0144), '\n'); + std::string e; if (rootDef) { e += cmStrCat("CMake variable ", rootVar, " is set to:\n ", *rootDef, '\n'); @@ -292,7 +338,7 @@ } e += "For compatibility, find_package is ignoring the variable, but " "code in a .cmake module might still use it."; - this->IssueMessage(MessageType::AUTHOR_WARNING, e); + this->IssuePolicyWarning(cmPolicies::CMP0144, {}, e); } } @@ -619,7 +665,8 @@ { public: IncludeScope(cmMakefile* mf, std::string const& filenametoread, - bool noPolicyScope); + cm::PolicyScope policyScope, + cm::DiagnosticScope diagnosticScope); ~IncludeScope(); void Quiet() { this->ReportError = false; } @@ -627,15 +674,18 @@ IncludeScope& operator=(IncludeScope const&) = delete; private: - bool NoPolicyScope; + cm::PolicyScope PolicyScope; + cm::DiagnosticScope DiagnosticScope; bool ReportError = true; }; cmMakefile::IncludeScope::IncludeScope(cmMakefile* mf, std::string const& filenametoread, - bool noPolicyScope) + cm::PolicyScope policyScope, + cm::DiagnosticScope diagnosticScope) : FileScopeBase(mf) - , NoPolicyScope(noPolicyScope) + , PolicyScope(policyScope) + , DiagnosticScope(diagnosticScope) { this->Makefile->Backtrace = this->Makefile->Backtrace.Push( cmListFileContext::FromListFilePath(filenametoread)); @@ -645,16 +695,23 @@ this->Makefile->StateSnapshot = this->Makefile->GetState()->CreateIncludeFileSnapshot( this->Makefile->StateSnapshot, filenametoread); - if (!this->NoPolicyScope) { + if (this->PolicyScope == cm::PolicyScope::Local) { this->Makefile->PushPolicy(); } + if (this->DiagnosticScope == cm::DiagnosticScope::Local) { + this->Makefile->PushDiagnostic(); + } this->PushListFileVars(filenametoread); } cmMakefile::IncludeScope::~IncludeScope() { this->PopListFileVars(); - if (!this->NoPolicyScope) { + if (this->DiagnosticScope == cm::DiagnosticScope::Local) { + // Pop the scope we pushed for the script. + this->Makefile->PopDiagnostic(); + } + if (this->PolicyScope == cm::PolicyScope::Local) { // Pop the scope we pushed for the script. this->Makefile->PopPolicy(); } @@ -666,12 +723,13 @@ } bool cmMakefile::ReadDependentFile(std::string const& filename, - bool noPolicyScope) + cm::PolicyScope policyScope, + cm::DiagnosticScope diagnosticScope) { std::string filenametoread = cmSystemTools::CollapseFullPath( filename, this->GetCurrentSourceDirectory()); - IncludeScope incScope(this, filenametoread, noPolicyScope); + IncludeScope incScope(this, filenametoread, policyScope, diagnosticScope); #ifdef CMake_ENABLE_DEBUGGER if (this->GetCMakeInstance()->GetDebugAdapter()) { @@ -681,8 +739,7 @@ #endif cmListFile listFile; - if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), - this->Backtrace)) { + if (!listFile.ParseFile(filenametoread, this, this->Backtrace)) { #ifdef CMake_ENABLE_DEBUGGER if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); @@ -804,8 +861,7 @@ #endif cmListFile listFile; - if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), - this->Backtrace)) { + if (!listFile.ParseFile(filenametoread, this, this->Backtrace)) { #ifdef CMake_ENABLE_DEBUGGER if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); @@ -839,8 +895,7 @@ ListFileScope scope(this, filenametoread); cmListFile listFile; - if (!listFile.ParseString(content, virtualFileName.c_str(), - this->GetMessenger(), this->Backtrace)) { + if (!listFile.ParseString(content, virtualFileName, this, this->Backtrace)) { return false; } @@ -966,6 +1021,20 @@ this->ExportBuildFileGenerators.emplace_back(std::move(gen)); } +#ifndef CMAKE_BOOTSTRAP +std::vector<std::unique_ptr<cmBuildSbomGenerator>> const& +cmMakefile::GetBuildSbomGenerators() const +{ + return this->BuildSbomGenerators; +} + +void cmMakefile::AddBuildSbomGenerator( + std::unique_ptr<cmBuildSbomGenerator> gen) +{ + this->BuildSbomGenerators.emplace_back(std::move(gen)); +} +#endif + namespace { struct file_not_persistent { @@ -1430,18 +1499,9 @@ } } -bool cmMakefile::ExplicitlyGeneratesSbom() const -{ - return this->ExplicitSbomGenerator; -} - -void cmMakefile::SetExplicitlyGeneratesSbom(bool status) -{ - this->ExplicitSbomGenerator = status; -} - void cmMakefile::PushFunctionScope(std::string const& fileName, - cmPolicies::PolicyMap const& pm) + cmPolicies::PolicyMap const& pm, + cmDiagnostics::DiagnosticMap dm) { this->StateSnapshot = this->GetState()->CreateFunctionCallSnapshot( this->StateSnapshot, fileName); @@ -1456,10 +1516,12 @@ this->PushFunctionBlockerBarrier(); this->PushPolicy(true, pm); + this->PushDiagnostic(true, dm); } void cmMakefile::PopFunctionScope(bool reportError) { + this->PopDiagnostic(); this->PopPolicy(); this->PopSnapshot(reportError); @@ -1474,7 +1536,8 @@ } void cmMakefile::PushMacroScope(std::string const& fileName, - cmPolicies::PolicyMap const& pm) + cmPolicies::PolicyMap const& pm, + cmDiagnostics::DiagnosticMap dm) { this->StateSnapshot = this->GetState()->CreateMacroCallSnapshot(this->StateSnapshot, fileName); @@ -1483,10 +1546,12 @@ this->PushFunctionBlockerBarrier(); this->PushPolicy(true, pm); + this->PushDiagnostic(true, dm); } void cmMakefile::PopMacroScope(bool reportError) { + this->PopDiagnostic(); this->PopPolicy(); this->PopSnapshot(reportError); @@ -1519,9 +1584,6 @@ this->Snapshot = this->GG->GetCMakeInstance()->GetCurrentSnapshot(); this->GG->GetCMakeInstance()->SetCurrentSnapshot(this->Snapshot); this->GG->SetCurrentMakefile(mf); -#if !defined(CMAKE_BOOTSTRAP) - this->GG->GetFileLockPool().PushFileScope(); -#endif } ~BuildsystemFileScope() @@ -1529,9 +1591,6 @@ this->PopListFileVars(); this->Makefile->PopFunctionBlockerBarrier(this->ReportError); this->Makefile->PopSnapshot(this->ReportError); -#if !defined(CMAKE_BOOTSTRAP) - this->GG->GetFileLockPool().PopFileScope(); -#endif this->GG->SetCurrentMakefile(this->CurrentMakefile); this->GG->GetCMakeInstance()->SetCurrentSnapshot(this->Snapshot); } @@ -1593,8 +1652,7 @@ #endif cmListFile listFile; - if (!listFile.ParseFile(currentStart.c_str(), this->GetMessenger(), - this->Backtrace)) { + if (!listFile.ParseFile(currentStart, this, this->Backtrace)) { #ifdef CMake_ENABLE_DEBUGGER if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); @@ -1670,16 +1728,15 @@ } // if no project command is found, add one if (!hasProject) { - this->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, + this->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, "No project() command is present. The top-level CMakeLists.txt " "file must contain a literal, direct call to the project() command. " "Add a line of code such as\n" " project(ProjectName)\n" "near the top of the file, but after cmake_minimum_required().\n" "CMake is pretending there is a \"project(Project)\" command on " - "the first line.", - this->Backtrace); + "the first line."); cmListFileFunction project{ "project", 0, 0, { { "Project", cmListFileArgument::Unquoted, 0 } } }; @@ -1902,12 +1959,11 @@ case cmPolicies::WARN: if (this->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0126") && this->IsNormalDefinitionSet(name)) { - this->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0126), - "\nFor compatibility with older versions of CMake, normal " - "variable \"", - name, "\" will be removed from the current scope.")); + this->IssuePolicyWarning( + cmPolicies::CMP0126, {}, + cmStrCat("For compatibility with older versions of CMake, " + "normal variable \""_s, + name, "\" will be removed from the current scope."_s)); } CM_FALLTHROUGH; case cmPolicies::OLD: @@ -1935,12 +1991,15 @@ // check to see if we need to print a warning // if strict mode is on and the variable has // not been "cleared"/initialized with a set(foo ) call - if (this->GetCMakeInstance()->GetWarnUninitialized() && + cmDiagnosticAction const action = + this->GetDiagnosticAction(cmDiagnostics::CMD_UNINITIALIZED); + if (action != cmDiagnostics::Ignore && !this->VariableInitialized(variable)) { if (this->CheckSystemVars || (sourceFilename && this->IsProjectFile(sourceFilename))) { - this->IssueMessage(MessageType::AUTHOR_WARNING, - cmStrCat("uninitialized variable '", variable, '\'')); + this->IssueDiagnostic( + cmDiagnostics::CMD_UNINITIALIZED, + cmStrCat("uninitialized variable '", variable, '\'')); } } } @@ -2573,6 +2632,83 @@ return call; } +namespace { +cmPolicies::PolicyStatus CheckCMP0219Impl(cmPolicies::PolicyStatus status, + std::string const& calleeName, + bool hasBackslashes, + std::set<std::string>& warned) +{ + if (status == cmPolicies::WARN && hasBackslashes && + warned.insert(calleeName).second) { + return cmPolicies::WARN; + } + // Suppress WARN when there are no backslashes or already warned. + return status == cmPolicies::NEW ? cmPolicies::NEW : cmPolicies::OLD; +} +} + +cmPolicies::PolicyStatus cmMakefile::CheckCMP0219( + std::string const& calleeName, std::vector<std::string> const& args) +{ + bool const hasBackslashes = + std::any_of(args.begin(), args.end(), [](std::string const& s) { + return s.find('\\') != std::string::npos; + }); + return CheckCMP0219Impl(GetPolicyStatus(cmPolicies::CMP0219), calleeName, + hasBackslashes, WarnedCMP0219); +} + +cmPolicies::PolicyStatus cmMakefile::CheckCMP0219( + std::string const& calleeName, std::vector<cmListFileArgument> const& args) +{ + bool const hasBackslashes = + std::any_of(args.begin(), args.end(), [](cmListFileArgument const& s) { + return s.Value.find('\\') != std::string::npos; + }); + return CheckCMP0219Impl(GetPolicyStatus(cmPolicies::CMP0219), calleeName, + hasBackslashes, WarnedCMP0219); +} + +void cmMakefile::IssueCMP0219Warning( + std::string const& calleeName, std::vector<std::string> const& args) const +{ + std::string oldArgs; + for (std::string const& arg : args) { + if (arg.find('\\') == std::string::npos) { + continue; + } + if (!oldArgs.empty()) { + oldArgs += '\n'; + } + oldArgs += cmStrCat(" \"", arg, '"'); + } + + std::string newArgs = oldArgs; + cmSystemTools::ReplaceString(newArgs, "\\", "\\\\"); + + this->IssueDiagnostic( + cmDiagnostics::CMD_POLICY, + cmStrCat( + cmPolicies::GetPolicyWarning(cmPolicies::CMP0219), '\n', "Command \"", + calleeName, "\" called with arguments containing backslashes.\n", + "Since the policy is not set, backslashes in the arguments:\n", oldArgs, + "\n", "will be interpreted as escape sequences for compatibility.\n", + "Set the policy to NEW to instead pass\n", newArgs, "\n", + "so that argument parsing will preserve the original values.")); +} + +void cmMakefile::IssueCMP0219Warning( + std::string const& calleeName, + std::vector<cmListFileArgument> const& args) const +{ + std::vector<std::string> stringArgs; + stringArgs.reserve(args.size()); + for (cmListFileArgument const& arg : args) { + stringArgs.push_back(arg.Value); + } + this->IssueCMP0219Warning(calleeName, stringArgs); +} + MessageType cmMakefile::ExpandVariablesInStringImpl( std::string& errorstr, std::string& source, bool escapeQuotes, bool noEscapes, bool atOnly, char const* filename, long line, @@ -3177,8 +3313,8 @@ } if (!duplicate_languages.empty()) { auto quantity = duplicate_languages.size() == 1 ? " has"_s : "s have"_s; - this->IssueMessage( - MessageType::AUTHOR_WARNING, + this->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Languages to be enabled may not be specified more " "than once at the same time. The following language", quantity, " been specified multiple times: ", @@ -3287,7 +3423,7 @@ if (cmakeArgs) { // FIXME: Workaround to ignore unused CLI variables in try-compile. // - // Ideally we should use SetArgs for options like --no-warn-unused-cli. + // Ideally we should use SetArgs for options like -Wno-unused-cli. // However, there is a subtle problem when certain arguments are passed to // a macro wrapping around try_compile or try_run that does not escape // semicolons in its parameters but just passes ${ARGV} or ${ARGN}. In @@ -3306,21 +3442,21 @@ // the value VAR=a is sufficient for the try_compile or try_run to get the // correct result. Calling SetArgs here would break such projects that // previously built. Instead we work around the issue by never reporting - // unused arguments and ignoring options such as --no-warn-unused-cli. - cm.SetWarnUnusedCli(false); + // unused arguments and ignoring options such as -Wno-unused-cli. + cm.GetCurrentSnapshot().SetDiagnostic(cmDiagnostics::CMD_UNUSED_CLI, + cmDiagnostics::Ignore, true); // cm.SetArgs(*cmakeArgs, true); cm.SetCacheArgs(*cmakeArgs); } // to save time we pass the EnableLanguage info directly - cm.GetGlobalGenerator()->EnableLanguagesFromGenerator( - this->GetGlobalGenerator(), this); - if (this->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { - cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", "TRUE", "", - cmStateEnums::INTERNAL); - } else { - cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", "FALSE", "", - cmStateEnums::INTERNAL); + cm.GetGlobalGenerator()->SetupTryCompile(this->GetGlobalGenerator(), this); + for (unsigned dc = 1; dc < cmDiagnostics::CategoryCount; ++dc) { + auto const category = static_cast<cmDiagnosticCategory>(dc); + if (this->GetDiagnosticAction(category) == cmDiagnostics::Ignore) { + cm.GetCurrentSnapshot().SetDiagnostic(category, cmDiagnostics::Ignore, + false); + } } if (cm.Configure() != 0) { this->IssueMessage(MessageType::FATAL_ERROR, @@ -3459,6 +3595,27 @@ result = moduleInCMakeRoot; } +#if defined(_WIN32) || defined(__APPLE__) + if (!result.empty()) { + std::string const requestedName = + cmSystemTools::GetFilenameName(std::string{ filename }); + std::string actualName; + cmsys::Status const status = + cmSystemTools::ReadNameOnDisk(result, actualName); + if (status && actualName != requestedName) { + this->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("The module name\n ", requestedName, '\n', + "does not match the case of the module file name on disk\n" + " ", + cmSystemTools::GetFilenamePath(result), '/', actualName, '\n', + "This may fail on case-sensitive file systems. " + "Use the module name\n ", + actualName, "\ninstead.")); + } + } +#endif + return result; } @@ -3805,8 +3962,8 @@ } if (!this->StateSnapshot.RaiseScope(var, varDef)) { - this->IssueMessage( - MessageType::AUTHOR_WARNING, + this->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Cannot set \"", var, "\": current scope has no parent.")); return; } @@ -3837,11 +3994,11 @@ bool global) { // Create the target. - std::unique_ptr<cmTarget> target( - new cmTarget(name, type, - global ? cmTarget::Visibility::ImportedGlobally - : cmTarget::Visibility::Imported, - this, cmTarget::PerConfig::Yes)); + auto target = + cm::make_unique<cmTarget>(name, type, + global ? cmTarget::Visibility::ImportedGlobally + : cmTarget::Visibility::Imported, + this, cmTarget::PerConfig::Yes); // Add to the set of available imported targets. this->ImportedTargets[name] = target.get(); @@ -3857,9 +4014,9 @@ std::string const& name) { auto foreign_name = cmStrCat("@foreign_", origin, "::", name); - std::unique_ptr<cmTarget> target(new cmTarget( + auto target = cm::make_unique<cmTarget>( foreign_name, cmStateEnums::TargetType::INTERFACE_LIBRARY, - cmTarget::Visibility::Foreign, this, cmTarget::PerConfig::Yes)); + cmTarget::Visibility::Foreign, this, cmTarget::PerConfig::Yes); this->ImportedTargets[foreign_name] = target.get(); this->GetGlobalGenerator()->IndexTarget(target.get()); @@ -4094,18 +4251,45 @@ } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0151 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0155 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. id == cmPolicies::CMP0083 || id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104 || id == cmPolicies::CMP0123 || id == cmPolicies::CMP0126 || id == cmPolicies::CMP0128 || - id == cmPolicies::CMP0136 || id == cmPolicies::CMP0141)) && - (!this->IsSet("CMAKE_WARN_DEPRECATED") || - this->IsOn("CMAKE_WARN_DEPRECATED"))) { - this->IssueMessage(MessageType::DEPRECATION_WARNING, - cmPolicies::GetPolicyDeprecatedWarning(id)); + id == cmPolicies::CMP0136 || id == cmPolicies::CMP0141 || + id == cmPolicies::CMP0155))) { + std::unique_ptr<PolicyPushPop> ps; + std::unique_ptr<DiagnosticPushPop> ds; + + cmPolicies::PolicyStatus const cmp0218 = + this->GetPolicyStatus(cmPolicies::CMP0218); + if (cmp0218 != cmPolicies::NEW) { + if (cmp0218 != cmPolicies::OLD) { + // Suppress warnings about using old variables. + ps = cm::make_unique<PolicyPushPop>(this); + this->SetPolicy(cmPolicies::CMP0218, cmPolicies::OLD); + } + + ds = cm::make_unique<DiagnosticPushPop>(this); + + // Use old variables to determine diagnostic action. + cmValue const warn = this->GetDefinition("CMAKE_WARN_DEPRECATED"); + if (warn.IsSet() && !warn.IsOn()) { + this->SetDiagnostic(cmDiagnostics::CMD_DEPRECATED, + cmDiagnostics::Ignore); + } else if (this->IsOn("CMAKE_ERROR_DEPRECATED")) { + this->SetDiagnostic(cmDiagnostics::CMD_DEPRECATED, + cmDiagnostics::SendError); + } else { + this->SetDiagnostic(cmDiagnostics::CMD_DEPRECATED, + cmDiagnostics::Warn); + } + } + + this->IssueDiagnostic(cmDiagnostics::CMD_DEPRECATED, + cmPolicies::GetPolicyDeprecatedWarning(id)); } this->StateSnapshot.SetPolicy(id, status); @@ -4144,18 +4328,80 @@ } } +cmDiagnosticAction cmMakefile::GetDiagnosticAction( + cmDiagnosticCategory category) const +{ + return this->StateSnapshot.GetDiagnostic(category); +} + +bool cmMakefile::SetDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, bool recursive) +{ + this->StateSnapshot.SetDiagnostic(category, action, recursive); + return true; +} + +bool cmMakefile::PromoteDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, bool recursive) +{ + this->StateSnapshot.PromoteDiagnostic(category, action, recursive); + return true; +} + +bool cmMakefile::DemoteDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, bool recursive) +{ + this->StateSnapshot.DemoteDiagnostic(category, action, recursive); + return true; +} + +cmMakefile::DiagnosticPushPop::DiagnosticPushPop(cmMakefile* m) + : Makefile(m) +{ + this->Makefile->PushDiagnostic(); +} + +cmMakefile::DiagnosticPushPop::~DiagnosticPushPop() +{ + this->Makefile->PopDiagnostic(); +} + +void cmMakefile::PushDiagnostic(bool weak, cmDiagnostics::DiagnosticMap dm) +{ + this->StateSnapshot.PushDiagnostic(dm, weak); +} + +void cmMakefile::PopDiagnostic() +{ + if (!this->StateSnapshot.PopDiagnostic()) { + this->IssueMessage(MessageType::FATAL_ERROR, + "cmake_diagnostic POP without matching PUSH"); + } +} + void cmMakefile::PopSnapshot(bool reportError) { - // cmStateSnapshot manages nested policy scopes within it. + // cmStateSnapshot manages nested policy/diagnostic scopes within it. // Since the scope corresponding to the snapshot is closing, - // reject any still-open nested policy scopes with an error. - while (this->StateSnapshot.CanPopPolicyScope()) { - if (reportError) { - this->IssueMessage(MessageType::FATAL_ERROR, - "cmake_policy PUSH without matching POP"); - reportError = false; + // reject any still-open nested policy/diagnostic scopes with an error. + for (;;) { + if (this->StateSnapshot.CanPopPolicyScope()) { + if (reportError) { + this->IssueMessage(MessageType::FATAL_ERROR, + "cmake_policy PUSH without matching POP"); + reportError = false; + } + this->PopPolicy(); + } else if (this->StateSnapshot.CanPopDiagnosticScope()) { + if (reportError) { + this->IssueMessage(MessageType::FATAL_ERROR, + "cmake_diagnostic PUSH without matching POP"); + reportError = false; + } + this->PopDiagnostic(); + } else { + break; } - this->PopPolicy(); } this->StateSnapshot = this->GetState()->Pop(this->StateSnapshot); @@ -4207,12 +4453,22 @@ } } +void cmMakefile::RecordDiagnostics(cmDiagnostics::DiagnosticMap& dm) const +{ + /* Record the setting of every diagnostic category. */ + for (unsigned n = 0; n < cmDiagnostics::CategoryCount; ++n) { + cmDiagnosticCategory const dc = static_cast<cmDiagnosticCategory>(n); + dm[dc] = this->GetDiagnosticAction(dc); + } +} + cmMakefile::FunctionPushPop::FunctionPushPop(cmMakefile* mf, std::string const& fileName, - cmPolicies::PolicyMap const& pm) + cmPolicies::PolicyMap const& pm, + cmDiagnostics::DiagnosticMap dm) : Makefile(mf) { - this->Makefile->PushFunctionScope(fileName, pm); + this->Makefile->PushFunctionScope(fileName, pm, dm); } cmMakefile::FunctionPushPop::~FunctionPushPop() @@ -4222,10 +4478,11 @@ cmMakefile::MacroPushPop::MacroPushPop(cmMakefile* mf, std::string const& fileName, - cmPolicies::PolicyMap const& pm) + cmPolicies::PolicyMap const& pm, + cmDiagnostics::DiagnosticMap dm) : Makefile(mf) { - this->Makefile->PushMacroScope(fileName, pm); + this->Makefile->PushMacroScope(fileName, pm, dm); } cmMakefile::MacroPushPop::~MacroPushPop()
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index f2f06d1..a2a0668 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h
@@ -25,6 +25,8 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" +#include "cmDiagnosticContext.h" +#include "cmDiagnostics.h" #include "cmFindPackageStack.h" #include "cmFunctionBlocker.h" #include "cmListFileCache.h" @@ -51,6 +53,7 @@ class cmCustomCommandLines; class cmExecutionStatus; class cmExpandedCommandArgument; +class cmBuildSbomGenerator; class cmExportBuildFileGenerator; class cmGeneratorExpressionEvaluationFile; class cmGlobalGenerator; @@ -64,6 +67,20 @@ class cmVariableWatch; class cmake; +namespace cm { +enum class PolicyScope : bool +{ + None, + Local, +}; + +enum class DiagnosticScope : bool +{ + None, + Local, +}; +} + /** A type-safe wrapper for a string representing a directory id. */ class cmDirectoryId { @@ -108,8 +125,10 @@ bool ReadListFileAsString(std::string const& content, std::string const& virtualFileName); - bool ReadDependentFile(std::string const& filename, - bool noPolicyScope = true); + bool ReadDependentFile( + std::string const& filename, + cm::PolicyScope policyScope = cm::PolicyScope::None, + cm::DiagnosticScope DiagnosticScope = cm::DiagnosticScope::None); /** * Add a function blocker to this makefile @@ -391,6 +410,20 @@ void RecordPolicies(cmPolicies::PolicyMap& pm) const; //@} + //@{ + /** + * Set, Push, Pop diagnostics for CMake. + */ + bool SetDiagnostic(cmDiagnosticCategory category, cmDiagnosticAction action, + bool recursive = false); + bool PromoteDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, bool recursive = false); + bool DemoteDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, bool recursive = false); + cmDiagnosticAction GetDiagnosticAction(cmDiagnosticCategory category) const; + void RecordDiagnostics(cmDiagnostics::DiagnosticMap& dm) const; + //@} + /** Update CMAKE_PARENT_LIST_FILE based on CMP0198 policy status. */ void UpdateParentListFileVariable(); @@ -409,6 +442,21 @@ }; friend class PolicyPushPop; + /** Helper class to push and pop diagnostics automatically. */ + class DiagnosticPushPop + { + public: + DiagnosticPushPop(cmMakefile* m); + ~DiagnosticPushPop(); + + DiagnosticPushPop(DiagnosticPushPop const&) = delete; + DiagnosticPushPop& operator=(DiagnosticPushPop const&) = delete; + + private: + cmMakefile* Makefile; + }; + friend class DiagnosticPushPop; + /** Helper class to push and pop variables scopes automatically. */ class VariablePushPop { @@ -854,9 +902,6 @@ //! Initialize a makefile from its parent void InitializeFromParent(cmMakefile* parent); - bool ExplicitlyGeneratesSbom() const; - void SetExplicitlyGeneratesSbom(bool status = true); - void AddInstallGenerator(std::unique_ptr<cmInstallGenerator> g); std::vector<std::unique_ptr<cmInstallGenerator>>& GetInstallGenerators() @@ -881,7 +926,8 @@ { public: FunctionPushPop(cmMakefile* mf, std::string const& fileName, - cmPolicies::PolicyMap const& pm); + cmPolicies::PolicyMap const& pm, + cmDiagnostics::DiagnosticMap dm); ~FunctionPushPop(); FunctionPushPop(FunctionPushPop const&) = delete; @@ -898,7 +944,8 @@ { public: MacroPushPop(cmMakefile* mf, std::string const& fileName, - cmPolicies::PolicyMap const& pm); + cmPolicies::PolicyMap const& pm, + cmDiagnostics::DiagnosticMap dm); ~MacroPushPop(); MacroPushPop(MacroPushPop const&) = delete; @@ -912,10 +959,12 @@ }; void PushFunctionScope(std::string const& fileName, - cmPolicies::PolicyMap const& pm); + cmPolicies::PolicyMap const& pm, + cmDiagnostics::DiagnosticMap dm); void PopFunctionScope(bool reportError); void PushMacroScope(std::string const& fileName, - cmPolicies::PolicyMap const& pm); + cmPolicies::PolicyMap const& pm, + cmDiagnostics::DiagnosticMap dm); void PopMacroScope(bool reportError); void PushScope(); void PopScope(); @@ -987,7 +1036,36 @@ cmMakefile* Makefile; }; - void IssueMessage(MessageType t, std::string const& text) const; + void IssueMessage(MessageType t, std::string const& text) const + { + this->IssueMessage(t, text, this->Backtrace); + } + void IssueMessage(MessageType t, std::string const& text, + cmListFileBacktrace const& bt) const; + + void IssueDiagnostic(cmDiagnosticCategory category, + std::string const& text) const + { + this->IssueDiagnostic(category, text, + cmDiagnosticContext{ this->Backtrace }); + } + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmListFileBacktrace backtrace) const + { + this->IssueDiagnostic(category, text, + cmDiagnosticContext{ std::move(backtrace) }); + } + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmDiagnosticContext const& context) const; + void IssuePolicyWarning(cmPolicies::PolicyID policy, cm::string_view preface, + cm::string_view postface, + cmListFileBacktrace const& bt) const; + void IssuePolicyWarning(cmPolicies::PolicyID policy, + cm::string_view preface = {}, + cm::string_view postface = {}) const + { + this->IssuePolicyWarning(policy, preface, postface, this->Backtrace); + } Message::LogLevel GetCurrentLogLevel() const; /** Set whether or not to report a CMP0000 violation. */ @@ -1033,6 +1111,12 @@ void AddExportBuildFileGenerator( std::unique_ptr<cmExportBuildFileGenerator> gen); +#ifndef CMAKE_BOOTSTRAP + std::vector<std::unique_ptr<cmBuildSbomGenerator>> const& + GetBuildSbomGenerators() const; + void AddBuildSbomGenerator(std::unique_ptr<cmBuildSbomGenerator> gen); +#endif + // Maintain a stack of package roots to allow nested PACKAGE_ROOT_PATH // searches std::deque<std::vector<std::string>> FindPackageRootPathStack; @@ -1106,6 +1190,20 @@ cm::optional<std::string> DeferGetCallIds() const; cm::optional<std::string> DeferGetCall(std::string const& id) const; + //! Check CMP0219 policy status for the given callee and arguments. + //! Returns the effective policy status: OLD, NEW, or WARN (only on + //! first occurrence of calleeName with backslashes present). The + //! caller is responsible for issuing any warning when WARN is returned. + cmPolicies::PolicyStatus CheckCMP0219(std::string const& calleeName, + std::vector<std::string> const& args); + cmPolicies::PolicyStatus CheckCMP0219( + std::string const& calleeName, + std::vector<cmListFileArgument> const& args); + void IssueCMP0219Warning(std::string const& calleeName, + std::vector<std::string> const& args) const; + void IssueCMP0219Warning(std::string const& calleeName, + std::vector<cmListFileArgument> const& args) const; + protected: // add link libraries and directories to the target void AddGlobalLinkInformation(cmTarget& target); @@ -1205,6 +1303,10 @@ std::vector<std::unique_ptr<cmExportBuildFileGenerator>> ExportBuildFileGenerators; +#ifndef CMAKE_BOOTSTRAP + std::vector<std::unique_ptr<cmBuildSbomGenerator>> BuildSbomGenerators; +#endif + std::vector<std::unique_ptr<cmGeneratorExpressionEvaluationFile>> EvaluationFiles; @@ -1219,12 +1321,19 @@ TargetMap ImportedTargets; // Internal policy stack management. - void PushPolicy(bool weak = false, - cmPolicies::PolicyMap const& pm = cmPolicies::PolicyMap()); + void PushPolicy(bool weak = false, cmPolicies::PolicyMap const& pm = {}); void PopPolicy(); - void PopSnapshot(bool reportError = true); friend bool cmCMakePolicyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); + + // Internal diagnostic stack management. + void PushDiagnostic(bool weak = false, cmDiagnostics::DiagnosticMap dm = {}); + void PopDiagnostic(); + friend bool cmCMakeDiagnosticCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); + + void PopSnapshot(bool reportError = true); + class IncludeScope; friend class IncludeScope; @@ -1256,13 +1365,13 @@ cmFindPackageStack FindPackageStack; unsigned int FindPackageStackNextIndex = 0; - bool ExplicitSbomGenerator = false; bool DebugFindPkg = false; bool CheckSystemVars; bool CheckCMP0000; std::set<std::string> WarnedCMP0074; std::set<std::string> WarnedCMP0144; + std::set<std::string> WarnedCMP0219; bool IsSourceFileTryCompile; ImportedTargetScope CurrentImportedTargetScope = ImportedTargetScope::Local; };
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 453fbe2..77c040f 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -598,6 +598,16 @@ vars.Launcher = linkerLauncher.c_str(); } + std::string rustMainCrateRootPath; + std::string rustLinkCrates; + std::string rustNativeObjects; + if (CreateRustLinkArguments(linkLanguage, rustMainCrateRootPath, + rustLinkCrates, rustNativeObjects)) { + vars.RustMainCrateRoot = rustMainCrateRootPath.c_str(); + vars.RustLinkCrates = rustLinkCrates.c_str(); + vars.RustNativeObjects = rustNativeObjects.c_str(); + } + if (this->UseLWYU) { cmValue lwyuCheck = this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 09026ca..d04051c 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -755,9 +755,13 @@ // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; + cmMakefileTargetGenerator::ResponseFlagFor responseMode = + this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY + ? cmMakefileTargetGenerator::ResponseFlagFor::Archive + : cmMakefileTargetGenerator::ResponseFlagFor::Link; this->CreateObjectLists(useLinkScript, useArchiveRules, useResponseFileForObjects, buildObjs, depends, - useWatcomQuote, linkLanguage); + useWatcomQuote, linkLanguage, responseMode); if (!this->DeviceLinkObject.empty()) { buildObjs += " " + this->LocalGenerator->ConvertToOutputFormat( @@ -833,6 +837,16 @@ vars.Manifests = manifests.c_str(); vars.Config = this->GetConfigName().c_str(); + std::string rustMainCrateRootPath; + std::string rustLinkCrates; + std::string rustNativeObjects; + if (CreateRustLinkArguments(linkLanguage, rustMainCrateRootPath, + rustLinkCrates, rustNativeObjects)) { + vars.RustMainCrateRoot = rustMainCrateRootPath.c_str(); + vars.RustLinkCrates = rustLinkCrates.c_str(); + vars.RustNativeObjects = rustNativeObjects.c_str(); + } + // Compute the directory portion of the install_name setting. std::string install_name_dir; if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 86c1138..12c5669 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx
@@ -24,15 +24,17 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" -#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGenExContext.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" #include "cmGeneratorOptions.h" #include "cmGeneratorTarget.h" #include "cmGlobalUnixMakefileGenerator3.h" #include "cmLinkLineComputer.h" // IWYU pragma: keep #include "cmList.h" +#include "cmListFileCache.h" #include "cmLocalCommonGenerator.h" #include "cmLocalGenerator.h" #include "cmLocalUnixMakefileGenerator3.h" @@ -45,6 +47,7 @@ #include "cmPolicies.h" #include "cmRange.h" #include "cmRulePlaceholderExpander.h" +#include "cmScriptGenerator.h" #include "cmSourceFile.h" #include "cmSourceFileLocationKind.h" #include "cmState.h" @@ -53,7 +56,6 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmTarget.h" #include "cmValue.h" #include "cmake.h" @@ -238,9 +240,9 @@ // Look for ISPC extra object files generated by this target auto const ispcAdditionalObjs = this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName()); - for (std::string const& ispcObj : ispcAdditionalObjs) { + for (auto const& ispcObj : ispcAdditionalObjs) { this->CleanFiles.insert( - this->LocalGenerator->MaybeRelativeToCurBinDir(ispcObj)); + this->LocalGenerator->MaybeRelativeToCurBinDir(ispcObj.second)); } // add custom commands to the clean rules? @@ -344,38 +346,6 @@ } } - std::map<std::string, std::string> file_set_map; - - auto const* tgt = this->GeneratorTarget->Target; - for (auto const& name : tgt->GetAllFileSetNames()) { - auto const* file_set = tgt->GetFileSet(name); - if (!file_set) { - this->Makefile->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), - "\" is tracked to have file set \"", name, - "\", but it was not found.")); - continue; - } - - auto fileEntries = file_set->CompileFileEntries(); - auto directoryEntries = file_set->CompileDirectoryEntries(); - auto directories = file_set->EvaluateDirectoryEntries( - directoryEntries, context, this->GeneratorTarget); - - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - file_set->EvaluateFileEntry(directories, files, entry, context, - this->GeneratorTarget); - } - - for (auto const& it : files) { - for (auto const& filename : it.second) { - file_set_map[filename] = file_set->GetType(); - } - } - } - std::vector<cmSourceFile const*> objectSources; this->GeneratorTarget->GetObjectSources(objectSources, this->GetConfigName()); @@ -390,20 +360,17 @@ } for (cmSourceFile const* sf : objectSources) { - auto const& path = sf->GetFullPath(); - auto const it = file_set_map.find(path); - if (it != file_set_map.end()) { - auto const& file_set_type = it->second; - if (file_set_type == "CXX_MODULES"_s) { - if (sf->GetLanguage() != "CXX"_s) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat( - "Target \"", tgt->GetName(), "\" contains the source\n ", path, - "\nin a file set of type \"", file_set_type, - R"(" but the source is not classified as a "CXX" source.)")); - } - } + cmGeneratorFileSet const* fileSet = + this->GeneratorTarget->GetFileSetForSource(this->GetConfigName(), sf); + if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES && + sf->GetLanguage() != "CXX"_s) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", this->GeneratorTarget->GetName(), + "\" contains the source\n ", sf->GetFullPath(), + "\nin a file set of type \"", + cm::FileSetMetadata::CXX_MODULES, + R"(" but the source is not classified as a "CXX" source.)")); } } } @@ -697,6 +664,10 @@ std::string const config = this->GetConfigName(); std::string const configUpper = cmSystemTools::UpperCase(config); + // lookup for the associated file set, if any. + auto const* fileSet = + this->GeneratorTarget->GetFileSetForSource(config, &source); + // Add precompile headers dependencies std::vector<std::string> pchArchs = this->GeneratorTarget->GetPchArchs(config, lang); @@ -714,7 +685,9 @@ } } - if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!pchSources.empty() && + !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + source.GetProperty("SKIP_PRECOMPILE_HEADERS"))) { for (std::string const& arch : pchArchs) { std::string const& pchHeader = this->GeneratorTarget->GetPchHeader(config, lang, arch); @@ -804,8 +777,26 @@ << "\n"; } + // Add flags from file set properties. + if (fileSet) { + auto options = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetCompileOptions(config, lang) + : fileSet->GetInterfaceCompileOptions(config, lang); + if (!options.empty()) { + this->LocalGenerator->AppendCompileOptions(flags, + cm::remove_BT(options)); + *this->FlagFileStream << "# Custom options (File Set '" + << fileSet->GetName() << "'): " << relativeObj + << "_OPTIONS = " << cmList::to_string(options) + << "\n" + << "\n"; + } + } + // Add precompile headers compile options. - if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!pchSources.empty() && + !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + source.GetProperty("SKIP_PRECOMPILE_HEADERS"))) { std::string pchOptions; auto const pchIt = pchSources.find(source.GetFullPath()); if (pchIt != pchSources.end()) { @@ -825,9 +816,25 @@ << "\n"; } - // Add include directories from source file properties. + // Add include directories from file set properties. std::vector<std::string> includes; + if (fileSet) { + auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetIncludeDirectories(config, lang) + : fileSet->GetInterfaceIncludeDirectories(config, lang); + if (!fsIncludes.empty()) { + this->LocalGenerator->AppendIncludeDirectories( + includes, cm::remove_BT(fsIncludes), source); + *this->FlagFileStream + << "# Custom include directories (File Set '" << fileSet->GetName() + << "'): " << relativeObj + << "_INCLUDE_DIRECTORIES = " << cmList::to_string(fsIncludes) << "\n" + << "\n"; + } + } + + // Add include directories from source file properties. std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); if (cmValue cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) { std::string const& evaluatedIncludes = @@ -864,6 +871,21 @@ << "\n"; } + // Add file set preprocessor definitions + if (fileSet) { + auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetCompileDefinitions(config, lang) + : fileSet->GetInterfaceCompileDefinitions(config, lang); + if (!fsDefines.empty()) { + this->LocalGenerator->AppendDefines(defines, fsDefines); + *this->FlagFileStream << "# Custom defines (File Set '" + << fileSet->GetName() << "'): " << relativeObj + << "_DEFINES = " << cmList::to_string(fsDefines) + << "\n" + << "\n"; + } + } + // Get the output paths for source and object files. std::string const sourceFile = this->LocalGenerator->ConvertToOutputFormat( source.GetFullPath(), cmOutputConverter::SHELL); @@ -956,6 +978,7 @@ vars.Flags = flags.c_str(); vars.ISPCHeader = ispcHeaderForShell.c_str(); vars.Config = this->GetConfigName().c_str(); + vars.RustEmit = source.GetRustEmitProperty()->c_str(); std::string definesString = cmStrCat("$(", lang, "_DEFINES)"); @@ -1087,10 +1110,14 @@ compilerLauncher = GetCompilerLauncher(lang, config); } + cmValue const fsSkipCodeCheckVal = + fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr; cmValue const srcSkipCodeCheckVal = source.GetProperty("SKIP_LINTING"); - bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet() - ? srcSkipCodeCheckVal.IsOn() - : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING"); + bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet() + ? fsSkipCodeCheckVal.IsOn() + : (srcSkipCodeCheckVal.IsSet() + ? srcSkipCodeCheckVal.IsOn() + : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING")); if (!skipCodeCheck) { std::string const codeCheck = this->GenerateCodeCheckRules( @@ -1464,9 +1491,9 @@ << "set(CMAKE_MULTIPLE_OUTPUT_PAIRS\n"; /* clang-format on */ for (auto const& pi : this->MultipleOutputPairs) { - *this->InfoFileStream - << " " << cmOutputConverter::EscapeForCMake(pi.first) << " " - << cmOutputConverter::EscapeForCMake(pi.second) << "\n"; + *this->InfoFileStream << " " << cmScriptGenerator::Quote(pi.first) + << ' ' << cmScriptGenerator::Quote(pi.second) + << '\n'; } *this->InfoFileStream << " )\n\n"; } @@ -1948,8 +1975,8 @@ } auto const ispcAdditionalObjs = this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName()); - for (std::string const& obj : ispcAdditionalObjs) { - helper.Feed(obj); + for (auto const& obj : ispcAdditionalObjs) { + helper.Feed(obj.second); } helper.Done(); } @@ -2029,6 +2056,12 @@ // Add dependencies on the external object files. cm::append(depends, this->ExternalObjects); + // Add dependency on the Rust main crate root file. + if (cmSourceFile const* mainCrateRoot = + this->GeneratorTarget->GetRustMainCrateRoot(this->GetConfigName())) { + depends.push_back(mainCrateRoot->GetFullPath()); + } + // Add a dependency on the rule file itself. this->LocalGenerator->AppendRuleDepend(depends, this->BuildFileNameFull.c_str()); @@ -2236,9 +2269,9 @@ // Create this response file. std::string const responseFileName = - (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp"; + (responseMode == DeviceLink) ? "deviceLinkLibs.rsp" : "linkLibs.rsp"; std::string const responseLang = - (responseMode == Link) ? linkLanguage : "CUDA"; + (responseMode == DeviceLink) ? "CUDA" : linkLanguage; std::string link_rsp = this->CreateResponseFile( responseFileName, linkLibs, makefile_depends, responseLang); @@ -2275,8 +2308,9 @@ char const* sep = ""; for (unsigned int i = 0; i < object_strings.size(); ++i) { // Number the response files. - std::string responseFileName = cmStrCat( - (responseMode == Link) ? "objects" : "deviceObjects", i + 1, ".rsp"); + std::string responseFileName = + cmStrCat((responseMode == DeviceLink) ? "deviceObjects" : "objects", + i + 1, ".rsp"); // Create this response file. std::string objects_rsp = this->CreateResponseFile( @@ -2301,6 +2335,33 @@ } } +bool cmMakefileTargetGenerator::CreateRustLinkArguments( + std::string const& linkLanguage, std::string& rustMainCrateRootPath, + std::string& rustLinkCrates, std::string& rustNativeObjects) +{ + if (linkLanguage == "Rust") { + this->ComputeRustFlagsForObjects(rustLinkCrates, rustNativeObjects, + this->Objects); + this->ComputeRustFlagsForObjects(rustLinkCrates, rustNativeObjects, + this->ExternalObjects); + + cmSourceFile const* mainCrateRoot = + this->GeneratorTarget->GetRustMainCrateRoot(this->GetConfigName()); + if (!mainCrateRoot) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "Target " + + this->GeneratorTarget->GetName() + + " has no main crate root."); + return false; + } + rustMainCrateRootPath = mainCrateRoot->GetFullPath(); + rustMainCrateRootPath = this->LocalGenerator->ConvertToOutputFormat( + rustMainCrateRootPath, cmOutputConverter::SHELL); + return true; + } + return false; +} + void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, std::string const& lang, std::string const& /*config*/) @@ -2329,9 +2390,12 @@ responseFlag = "@"; } std::string const name = cmStrCat("includes_", lang, ".rsp"); - std::string const arg = std::move(responseFlag) + - this->CreateResponseFile(name, includeFlags, this->FlagFileDepends[lang], - lang); + std::string const includes_rsp = this->CreateResponseFile( + name, includeFlags, this->FlagFileDepends[lang], lang); + std::string const arg = + cmStrCat(std::move(responseFlag), + this->LocalGenerator->ConvertToOutputFormat( + includes_rsp, cmOutputConverter::SHELL)); this->LocalGenerator->AppendFlags(flags, arg); } else { this->LocalGenerator->AppendFlags(flags, includeFlags); @@ -2396,10 +2460,17 @@ responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) { responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG"; + } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Archive) { + responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_ARCHIVE_FLAG"); } if (cmValue const p = this->Makefile->GetDefinition(responseFlagVar)) { responseFlag = *p; + } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Archive) { + responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); + if (cmValue const q = this->Makefile->GetDefinition(responseFlagVar)) { + responseFlag = *q; + } } return responseFlag; }
diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index fe255f4..ce78230 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h
@@ -174,8 +174,9 @@ enum ResponseFlagFor { + Archive, Link, - DeviceLink + DeviceLink, }; /** Create list of flags for link libraries. */ @@ -192,6 +193,11 @@ bool useWatcomQuote, std::string const& linkLanguage, ResponseFlagFor responseMode = ResponseFlagFor::Link); + bool CreateRustLinkArguments(std::string const& linkLanguage, + std::string& rustMainCrateRootPath, + std::string& rustLinkCrates, + std::string& rustNativeObjects); + /** Add commands for generate def files */ void GenDefFile(std::vector<std::string>& real_link_commands);
diff --git a/Source/cmMarkAsAdvancedCommand.cxx b/Source/cmMarkAsAdvancedCommand.cxx index 963076b..08da67e 100644 --- a/Source/cmMarkAsAdvancedCommand.cxx +++ b/Source/cmMarkAsAdvancedCommand.cxx
@@ -2,9 +2,9 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmMarkAsAdvancedCommand.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmState.h" #include "cmStateTypes.h" @@ -68,7 +68,7 @@ "is no longer created when policy CMP0102 is set to NEW. Run \"cmake " "--help-policy CMP0102\" for policy details. Use the cmake_policy " "command to set the policy and suppress this warning."); - mf.IssueMessage(MessageType::AUTHOR_WARNING, err); + mf.IssueDiagnostic(cmDiagnostics::CMD_POLICY, err); } // If it's not in the cache and we're using the new behavior, nothing to
diff --git a/Source/cmMathCommand.cxx b/Source/cmMathCommand.cxx index 85c5281..75d0464 100644 --- a/Source/cmMathCommand.cxx +++ b/Source/cmMathCommand.cxx
@@ -6,10 +6,10 @@ #include <cm3p/kwiml/int.h> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmExprParserHelper.h" #include "cmMakefile.h" -#include "cmMessageType.h" namespace { bool HandleExprCommand(std::vector<std::string> const& args, @@ -111,7 +111,7 @@ std::string const& w = helper.GetWarning(); if (!w.empty()) { - status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, w); + status.GetMakefile().IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, w); } status.GetMakefile().AddDefinition(outputVariable, buffer);
diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index e19f4ef..67b5af5 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx
@@ -3,21 +3,24 @@ #include "cmMessageCommand.h" #include <cassert> -#include <memory> #include <utility> +#include <cm/memory> #include <cm/string_view> #include <cmext/string_view> #include "cmConfigureLog.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmList.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmMessenger.h" +#include "cmPolicies.h" #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmValue.h" #include "cmake.h" #ifdef CMake_ENABLE_DEBUGGER @@ -57,7 +60,7 @@ mf.DisplayStatus(IndentText(std::move(text), mf), -1); } else { mf.GetMessenger()->DisplayMessage( - MessageType::AUTHOR_WARNING, + MessageType::WARNING, cmDiagnostics::CMD_AUTHOR, cmStrCat("Ignored "_s, what, " without CHECK_START"_s), mf.GetBacktrace()); } @@ -95,6 +98,8 @@ auto i = args.cbegin(); + std::unique_ptr<cmMakefile::DiagnosticPushPop> ds; + auto category = cmDiagnostics::CMD_NONE; auto type = MessageType::MESSAGE; auto fatal = false; auto level = Message::LogLevel::LOG_UNDEFINED; @@ -113,16 +118,21 @@ level = Message::LogLevel::LOG_WARNING; ++i; } else if (*i == "AUTHOR_WARNING") { - if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") && - !mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) { - fatal = true; - type = MessageType::AUTHOR_ERROR; - level = Message::LogLevel::LOG_ERROR; - } else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { - type = MessageType::AUTHOR_WARNING; - level = Message::LogLevel::LOG_WARNING; - } else { - return true; + category = cmDiagnostics::CMD_AUTHOR; + switch (mf.GetDiagnosticAction(category)) { + case cmDiagnostics::Ignore: + return true; + case cmDiagnostics::FatalError: + fatal = true; + CM_FALLTHROUGH; + case cmDiagnostics::SendError: + type = MessageType::FATAL_ERROR; + level = Message::LogLevel::LOG_ERROR; + break; + default: + type = MessageType::WARNING; + level = Message::LogLevel::LOG_WARNING; + break; } ++i; } else if (*i == "CHECK_START") { @@ -158,16 +168,48 @@ level = Message::LogLevel::LOG_TRACE; ++i; } else if (*i == "DEPRECATION") { - if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) { - fatal = true; - type = MessageType::DEPRECATION_ERROR; - level = Message::LogLevel::LOG_ERROR; - } else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") || - mf.IsOn("CMAKE_WARN_DEPRECATED")) { - type = MessageType::DEPRECATION_WARNING; - level = Message::LogLevel::LOG_WARNING; - } else { - return true; + category = cmDiagnostics::CMD_DEPRECATED; + + cmPolicies::PolicyStatus const cmp0218 = + mf.GetPolicyStatus(cmPolicies::CMP0218); + if (cmp0218 != cmPolicies::NEW) { + std::unique_ptr<cmMakefile::PolicyPushPop> ps; + + if (cmp0218 != cmPolicies::OLD) { + // Suppress warnings about using old variables. + ps = cm::make_unique<cmMakefile::PolicyPushPop>(&mf); + mf.SetPolicy(cmPolicies::CMP0218, cmPolicies::OLD); + } + + ds = cm::make_unique<cmMakefile::DiagnosticPushPop>(&mf); + + // Use old variables to determine diagnostic action. + if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) { + mf.SetDiagnostic(category, cmDiagnostics::FatalError); + } else { + cmValue const warn = mf.GetDefinition("CMAKE_WARN_DEPRECATED"); + if (warn.IsSet() && !warn.IsOn()) { + mf.SetDiagnostic(category, cmDiagnostics::Ignore); + } else { + mf.SetDiagnostic(category, cmDiagnostics::Warn); + } + } + } + + switch (mf.GetDiagnosticAction(category)) { + case cmDiagnostics::Ignore: + return true; + case cmDiagnostics::FatalError: + fatal = true; + CM_FALLTHROUGH; + case cmDiagnostics::SendError: + type = MessageType::FATAL_ERROR; + level = Message::LogLevel::LOG_ERROR; + break; + default: + type = MessageType::WARNING; + level = Message::LogLevel::LOG_WARNING; + break; } ++i; } else if (*i == "NOTICE") { @@ -194,7 +236,8 @@ case Message::LogLevel::LOG_ERROR: case Message::LogLevel::LOG_WARNING: // we've overridden the message type, above, so display it directly - mf.GetMessenger()->DisplayMessage(type, message, mf.GetBacktrace()); + mf.GetMessenger()->DisplayMessage(type, category, message, + mf.GetBacktrace()); break; case Message::LogLevel::LOG_NOTICE:
diff --git a/Source/cmMessageType.h b/Source/cmMessageType.h index a7b5b07..54ad4cf 100644 --- a/Source/cmMessageType.h +++ b/Source/cmMessageType.h
@@ -7,15 +7,11 @@ enum class MessageType { UNDEFINED, - AUTHOR_WARNING, - AUTHOR_ERROR, FATAL_ERROR, INTERNAL_ERROR, MESSAGE, WARNING, LOG, - DEPRECATION_ERROR, - DEPRECATION_WARNING }; namespace Message {
diff --git a/Source/cmMessenger.cxx b/Source/cmMessenger.cxx index ad439cb..12176ba 100644 --- a/Source/cmMessenger.cxx +++ b/Source/cmMessenger.cxx
@@ -2,9 +2,17 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmMessenger.h" +#include <algorithm> +#include <array> +#include <sstream> +#include <utility> + +#include <cmext/string_view> + #include "cmDocumentationFormatter.h" #include "cmMessageMetadata.h" #include "cmMessageType.h" +#include "cmStateSnapshot.h" #include "cmStdIoTerminal.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -15,9 +23,6 @@ # include "cmSarifLog.h" #endif -#include <sstream> -#include <utility> - #ifdef CMake_ENABLE_DEBUGGER # include "cmDebuggerAdapter.h" #endif @@ -32,28 +37,26 @@ return "Internal Error (please report a bug)"; case MessageType::LOG: return "Debug Log"; - case MessageType::DEPRECATION_ERROR: - return "Deprecation Error"; - case MessageType::DEPRECATION_WARNING: - return "Deprecation Warning"; - case MessageType::AUTHOR_WARNING: - return "Warning (dev)"; - case MessageType::AUTHOR_ERROR: - return "Error (dev)"; default: break; } return "Warning"; } +std::string getDiagnosticCategoryStr(cmDiagnosticCategory category) +{ + std::string out = cmSystemTools::LowerCase( + cmDiagnostics::GetCategoryString(category).substr(4)); + std::replace(out.begin(), out.end(), '_', '-'); + return out; +} + cm::StdIo::TermAttr getMessageColor(MessageType t) { switch (t) { case MessageType::INTERNAL_ERROR: case MessageType::FATAL_ERROR: - case MessageType::AUTHOR_ERROR: return cm::StdIo::TermAttr::ForegroundRed; - case MessageType::AUTHOR_WARNING: case MessageType::WARNING: return cm::StdIo::TermAttr::ForegroundYellow; default: @@ -61,6 +64,17 @@ } } +bool isAuthorDiagnostic(cmDiagnosticCategory category) +{ + while (category != cmDiagnostics::CMD_NONE) { + if (category == cmDiagnostics::CMD_AUTHOR) { + return true; + } + category = cmDiagnostics::CategoryInfo[category].Parent; + } + return false; +} + void printMessageText(std::ostream& msg, std::string const& text) { msg << ":\n"; @@ -69,23 +83,37 @@ formatter.PrintFormatted(msg, text); } -void displayMessage(MessageType t, std::ostringstream& msg) +void displayMessage(MessageType type, cmDiagnosticCategory category, + std::ostringstream& msg) { - // Add a note about warning suppression. - if (t == MessageType::AUTHOR_WARNING) { - msg << "This warning is for project developers. Use -Wno-dev to suppress " - "it."; - } else if (t == MessageType::AUTHOR_ERROR) { - msg << "This error is for project developers. Use -Wno-error=dev to " - "suppress it."; - } + if (isAuthorDiagnostic(category)) { + // Add a note about warning suppression. + std::ostringstream text; + if (type == MessageType::WARNING) { + text << "This warning is for project developers. " + "Use -Wno-author"; + if (category != cmDiagnostics::CMD_AUTHOR) { + text << " or -Wno-" << getDiagnosticCategoryStr(category); + } + } else if (type == MessageType::FATAL_ERROR) { + text << "This error is for project developers. " + "Use -Wno-error=author"; + if (category != cmDiagnostics::CMD_AUTHOR) { + text << " or -Wno-error=" << getDiagnosticCategoryStr(category); + } + } + text << " to suppress it."; - // Add a terminating blank line. - msg << '\n'; + cmDocumentationFormatter formatter; + formatter.PrintFormatted(msg, text.str()); + } else { + // Add a terminating blank line. + msg << '\n'; + } #if !defined(CMAKE_BOOTSTRAP) // Add a C++ stack trace to internal errors. - if (t == MessageType::INTERNAL_ERROR) { + if (type == MessageType::INTERNAL_ERROR) { std::string stack = cmsys::SystemInformation::GetProgramStack(0, 0); if (!stack.empty()) { if (cmHasLiteralPrefix(stack, "WARNING:")) { @@ -98,9 +126,9 @@ // Output the message. cmMessageMetadata md; - md.attrs = getMessageColor(t); - if (t == MessageType::FATAL_ERROR || t == MessageType::INTERNAL_ERROR || - t == MessageType::DEPRECATION_ERROR || t == MessageType::AUTHOR_ERROR) { + md.attrs = getMessageColor(type); + if (type == MessageType::FATAL_ERROR || + type == MessageType::INTERNAL_ERROR) { cmSystemTools::SetErrorOccurred(); md.title = "Error"; } else { @@ -149,65 +177,67 @@ } // anonymous namespace -MessageType cmMessenger::ConvertMessageType(MessageType t) const -{ - if (t == MessageType::AUTHOR_WARNING || t == MessageType::AUTHOR_ERROR) { - if (this->GetDevWarningsAsErrors()) { - return MessageType::AUTHOR_ERROR; - } - return MessageType::AUTHOR_WARNING; - } - if (t == MessageType::DEPRECATION_WARNING || - t == MessageType::DEPRECATION_ERROR) { - if (this->GetDeprecatedWarningsAsErrors()) { - return MessageType::DEPRECATION_ERROR; - } - return MessageType::DEPRECATION_WARNING; - } - return t; -} - -bool cmMessenger::IsMessageTypeVisible(MessageType t) const -{ - if (t == MessageType::DEPRECATION_ERROR) { - return this->GetDeprecatedWarningsAsErrors(); - } - if (t == MessageType::DEPRECATION_WARNING) { - return !this->GetSuppressDeprecatedWarnings(); - } - if (t == MessageType::AUTHOR_ERROR) { - return this->GetDevWarningsAsErrors(); - } - if (t == MessageType::AUTHOR_WARNING) { - return !this->GetSuppressDevWarnings(); - } - - return true; -} - void cmMessenger::IssueMessage(MessageType t, std::string const& text, cmListFileBacktrace const& backtrace) const { - bool force = false; - // override the message type, if needed, for warnings and errors - MessageType override = this->ConvertMessageType(t); - if (override != t) { - t = override; - force = true; - } + this->DisplayMessage(t, cmDiagnostics::CMD_NONE, text, backtrace); +} - if (force || this->IsMessageTypeVisible(t)) { - this->DisplayMessage(t, text, backtrace); +void cmMessenger::IssueDiagnostic(cmDiagnosticCategory category, + std::string const& text, + cmStateSnapshot const& fallbackContext, + cmDiagnosticContext const& context) const +{ + cmDiagnosticAction const action = [&] { + if (context.HasState) { + cmDiagnosticAction const ca = context.DiagnosticState[category]; + if (ca != cmDiagnostics::Undefined) { + return ca; + } + + // If the context has recorded states, but not the state we want, this + // implies that we had the opportunity to record the state and failed to + // do so. Ask users to report this. + std::string msg = + cmStrCat("Stored diagnostic context did not record state for "_s, + cmDiagnostics::GetCategoryString(category), + ". Please report this as a bug.\n"_s); + this->IssueMessage(MessageType::LOG, msg, context.GetBacktrace()); + } + + return fallbackContext.GetDiagnostic(category); + }(); + + switch (action) { + case cmDiagnostics::FatalError: + cmSystemTools::SetFatalErrorOccurred(); + CM_FALLTHROUGH; + case cmDiagnostics::SendError: + cmSystemTools::SetErrorOccurred(); + this->DisplayMessage(MessageType::FATAL_ERROR, category, text, + context.GetBacktrace()); + break; + case cmDiagnostics::Warn: + this->DisplayMessage(MessageType::WARNING, category, text, + context.GetBacktrace()); + break; + default: + return; } } -void cmMessenger::DisplayMessage(MessageType t, std::string const& text, +void cmMessenger::DisplayMessage(MessageType type, + cmDiagnosticCategory category, + std::string const& text, cmListFileBacktrace const& backtrace) const { std::ostringstream msg; // Print the message preamble. - msg << "CMake " << getMessageTypeStr(t); + msg << "CMake " << getMessageTypeStr(type); + if (category != cmDiagnostics::CMD_NONE) { + msg << " (" << getDiagnosticCategoryStr(category) << ')'; + } // Add the immediate context. this->PrintBacktraceTitle(msg, backtrace); @@ -217,16 +247,16 @@ // Add the rest of the context. PrintCallStack(msg, backtrace, this->TopSource); - displayMessage(t, msg); + displayMessage(type, category, msg); #ifndef CMAKE_BOOTSTRAP // Add message to SARIF logs - this->SarifLog.LogMessage(t, text, backtrace); + this->SarifLog.LogMessage(type, text, backtrace); #endif #ifdef CMake_ENABLE_DEBUGGER if (DebuggerAdapter) { - DebuggerAdapter->OnMessageOutput(t, msg.str()); + DebuggerAdapter->OnMessageOutput(type, msg.str()); } #endif }
diff --git a/Source/cmMessenger.h b/Source/cmMessenger.h index ad04b77..60f736b 100644 --- a/Source/cmMessenger.h +++ b/Source/cmMessenger.h
@@ -10,6 +10,8 @@ #include <cm/optional> +#include "cmDiagnosticContext.h" +#include "cmDiagnostics.h" #include "cmListFileCache.h" #include "cmMessageType.h" // IWYU pragma: keep @@ -17,6 +19,8 @@ # include "cmSarifLog.h" #endif +class cmStateSnapshot; + #ifdef CMake_ENABLE_DEBUGGER namespace cmDebugger { class cmDebuggerAdapter; @@ -27,42 +31,19 @@ { public: void IssueMessage( - MessageType t, std::string const& text, + MessageType type, std::string const& text, cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; - void DisplayMessage(MessageType t, std::string const& text, + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmStateSnapshot const& fallbackContext, + cmDiagnosticContext const& context = {}) const; + + void DisplayMessage(MessageType type, cmDiagnosticCategory category, + std::string const& text, cmListFileBacktrace const& backtrace) const; void SetTopSource(cm::optional<std::string> topSource); - void SetSuppressDevWarnings(bool suppress) - { - this->SuppressDevWarnings = suppress; - } - void SetSuppressDeprecatedWarnings(bool suppress) - { - this->SuppressDeprecatedWarnings = suppress; - } - void SetDevWarningsAsErrors(bool error) - { - this->DevWarningsAsErrors = error; - } - void SetDeprecatedWarningsAsErrors(bool error) - { - this->DeprecatedWarningsAsErrors = error; - } - - bool GetSuppressDevWarnings() const { return this->SuppressDevWarnings; } - bool GetSuppressDeprecatedWarnings() const - { - return this->SuppressDeprecatedWarnings; - } - bool GetDevWarningsAsErrors() const { return this->DevWarningsAsErrors; } - bool GetDeprecatedWarningsAsErrors() const - { - return this->DeprecatedWarningsAsErrors; - } - #ifndef CMAKE_BOOTSTRAP cmSarif::ResultsLog const& GetSarifResultsLog() const { return SarifLog; } #endif @@ -79,19 +60,12 @@ #endif private: - bool IsMessageTypeVisible(MessageType t) const; - MessageType ConvertMessageType(MessageType t) const; - cm::optional<std::string> TopSource; #ifndef CMAKE_BOOTSTRAP cmSarif::ResultsLog SarifLog; #endif - bool SuppressDevWarnings = false; - bool SuppressDeprecatedWarnings = false; - bool DevWarningsAsErrors = false; - bool DeprecatedWarningsAsErrors = false; #ifdef CMake_ENABLE_DEBUGGER std::shared_ptr<cmDebugger::cmDebuggerAdapter> DebuggerAdapter; #endif
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index e5cb1c0..3fbc178 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -479,8 +479,9 @@ } if (this->TargetLinkLanguage(config) == "Rust") { - vars.RustSources = "$RUST_SOURCES"; - vars.RustObjectDeps = "$RUST_OBJECT_DEPS"; + vars.RustMainCrateRoot = "$RUST_MAIN_CRATE_ROOT"; + vars.RustLinkCrates = "$RUST_LINK_CRATES"; + vars.RustNativeObjects = "$RUST_NATIVE_OBJECTS"; } std::string responseFlag; @@ -496,8 +497,15 @@ } // build response file name - std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; - cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); + cmValue flag; + if (targetType == cmStateEnums::STATIC_LIBRARY) { + std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_ARCHIVE_FLAG"; + flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); + } + if (!flag) { + std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; + flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); + } if (flag) { responseFlag = *flag; @@ -1039,7 +1047,8 @@ vars["LINK_FLAGS"], frameworkPath, linkPath, genTarget); - this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, nullptr, vars); + this->addPoolNinjaVariable("JOB_POOL_LINK", config, genTarget, nullptr, + vars); vars["MANIFESTS"] = this->GetManifests(config); @@ -1284,46 +1293,42 @@ } else if (this->TargetLinkLanguage(config) == "Rust") { // Use one-step build/link for Rust. // Compute specific libraries to link with. - std::vector<cmSourceFile const*> sources; - gt->GetObjectSources(sources, config); cmLocalGenerator const* lg = this->GetLocalGenerator(); - std::string entry_obj; - - for (auto const& source : sources) { - if (source->GetLanguage() == "Rust") { - if (vars.count("RUST_SOURCES") == 0) { - std::string const sourcePath = - this->GetCompiledSourceNinjaPath(source); - vars["RUST_SOURCES"] = - lg->ConvertToOutputFormat(sourcePath, cmOutputConverter::SHELL); - entry_obj = this->GetObjectFilePath(source, config); - } else { - assert(false && "Rust crate can only have 1 entry"); - } - } - } linkBuild.ExplicitDeps = this->GetObjects(config); - std::stringstream obj_deps; - // Do not try linking to object file created from the crate entry. - for (auto const& obj : linkBuild.ExplicitDeps) { - if (obj != entry_obj) { - obj_deps << " " - << lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL); - } + // First we handle Rust rlib and normal native objects. + this->ComputeRustFlagsForObjects(vars["RUST_LINK_CRATES"], + vars["RUST_NATIVE_OBJECTS"], + linkBuild.ExplicitDeps); + + // Then, we handle the main crate root that is build as part of the link + // step. + cmSourceFile const* mainCrateRoot = gt->GetRustMainCrateRoot(config); + if (!mainCrateRoot) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "Target " + gt->GetName() + + " has no main crate root."); + return; } - - vars["RUST_OBJECT_DEPS"] = obj_deps.str(); + std::string mainCrateRootPath = + this->GetCompiledSourceNinjaPath(mainCrateRoot); + linkBuild.ExplicitDeps.emplace_back(mainCrateRootPath); + mainCrateRootPath = + lg->ConvertToOutputFormat(mainCrateRootPath, cmOutputConverter::SHELL); + vars["RUST_MAIN_CRATE_ROOT"] = mainCrateRootPath; } else { linkBuild.ExplicitDeps = this->GetObjects(config); } - std::vector<std::string> extraISPCObjects = + auto extraISPCObjects = this->GetGeneratorTarget()->GetGeneratedISPCObjects(config); - std::transform(extraISPCObjects.begin(), extraISPCObjects.end(), - std::back_inserter(linkBuild.ExplicitDeps), - this->MapToNinjaPath()); + auto const mapToNinjaPath = this->MapToNinjaPath(); + std::transform( + extraISPCObjects.begin(), extraISPCObjects.end(), + std::back_inserter(linkBuild.ExplicitDeps), + [&mapToNinjaPath](std::pair<cmSourceFile const*, std::string> const& obj) + -> std::string { return mapToNinjaPath(obj.second); }); linkBuild.ImplicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage(config), config); @@ -1367,7 +1372,7 @@ this->TargetLinkLanguage(config), "CURRENT", false); } - this->addPoolNinjaVariable("JOB_POOL_LINK", gt, nullptr, vars); + this->addPoolNinjaVariable("JOB_POOL_LINK", config, gt, nullptr, vars); this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags( vars["LINK_FLAGS"], this->GetGeneratorTarget(), @@ -1497,7 +1502,7 @@ std::transform( ccByproducts.begin(), ccByproducts.end(), std::back_inserter(globalGen->GetByproductsForCleanTarget()), - this->MapToNinjaPath()); + mapToNinjaPath); } } } @@ -1572,9 +1577,15 @@ cmStrCat("CMAKE_", this->TargetLinkLanguage(config)); // build response file name - std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; - - cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); + cmValue flag; + if (targetType == cmStateEnums::STATIC_LIBRARY) { + std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_ARCHIVE_FLAG"; + flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); + } + if (!flag) { + std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; + flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); + } bool const lang_supports_response = !(this->TargetLinkLanguage(config) == "RC" || @@ -1622,10 +1633,11 @@ if (cmComputeLinkInformation* cli = this->GeneratorTarget->GetLinkInformation(config)) { for (auto const& dependency : cli->GetItems()) { - // Both the current target and the linked target must be swift targets - // in order for there to be a swiftmodule to depend on + // Only depend on swiftmodule from targets that actually compile + // Swift sources. A C/C++ target may have Swift as its linker + // language (due to language propagation) without producing one. if (dependency.Target && - dependency.Target->GetLinkerLanguage(config) == "Swift") { + dependency.Target->IsLanguageUsed("Swift", config)) { std::string swiftmodule = this->ConvertToNinjaPath( dependency.Target->GetSwiftModulePath(config)); linkBuild.ImplicitDeps.emplace_back(swiftmodule); @@ -1634,6 +1646,14 @@ } } + // For split Swift builds, ensure the link edge depends on the target's own + // .swiftmodule so the emit-module edge runs even when no other target in + // the build depends on it (e.g. install-only targets). + std::string swiftModuleOutput = this->GetSwiftModuleOutput(config); + if (!swiftModuleOutput.empty()) { + linkBuild.ImplicitDeps.emplace_back(std::move(swiftModuleOutput)); + } + // Ninja should restat after linking if and only if there are byproducts. vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1";
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index b2308b5..26ae977 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx
@@ -26,10 +26,13 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" +#include "cmDiagnostics.h" #include "cmDyndepCollation.h" -#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorOptions.h" #include "cmGeneratorTarget.h" #include "cmGlobalCommonGenerator.h" @@ -127,6 +130,15 @@ withScanning == WithScanning::Yes ? "_scanned_" : "_unscanned_", config); } +std::string cmNinjaTargetGenerator::LanguageEmitModuleRule( + std::string const& lang, std::string const& config) const +{ + return cmStrCat( + lang, "_EMIT_MODULE__", + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), + '_', config); +} + std::string cmNinjaTargetGenerator::LanguagePreprocessAndScanRule( std::string const& lang, std::string const& config) const { @@ -237,8 +249,24 @@ flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); } + auto const* const fileSet = + this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(config, + source); + + if (fileSet) { + auto options = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetCompileOptions(config, language) + : fileSet->GetInterfaceCompileOptions(config, language); + if (!options.empty()) { + this->LocalGenerator->AppendCompileOptions(flags, + cm::remove_BT(options)); + } + } + // Add precompile headers compile options. - if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!pchSources.empty() && + !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { std::string pchOptions; auto pchIt = pchSources.find(source->GetFullPath()); if (pchIt != pchSources.end()) { @@ -253,14 +281,13 @@ flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS)); } - auto const* fs = this->GeneratorTarget->GetFileSetForSource(config, source); - if (fs && fs->GetType() == "CXX_MODULES"_s) { + if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) { if (source->GetLanguage() != "CXX"_s) { this->GetMakefile()->IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Target \"", this->GeneratorTarget->Target->GetName(), "\" contains the source\n ", source->GetFullPath(), - "\nin a file set of type \"", fs->GetType(), + "\nin a file set of type \"", fileSet->GetType(), R"(" but the source is not classified as a "CXX" source.)")); } @@ -330,6 +357,17 @@ genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS)); } + if (auto const* fileSet = + this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( + config, source)) { + auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetCompileDefinitions(config, language) + : fileSet->GetInterfaceCompileDefinitions(config, language); + if (!fsDefines.empty()) { + this->LocalGenerator->AppendDefines(defines, fsDefines); + } + } + std::string definesString = this->GetDefines(language, config); this->LocalGenerator->JoinDefines(defines, definesString, language); @@ -341,6 +379,19 @@ std::string const& config) { std::vector<std::string> includes; + + if (auto const* fileSet = + this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( + config, source)) { + auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) + ? fileSet->GetIncludeDirectories(config, language) + : fileSet->GetInterfaceIncludeDirectories(config, language); + if (!fsIncludes.empty()) { + this->LocalGenerator->AppendIncludeDirectories( + includes, cm::remove_BT(fsIncludes), *source); + } + } + cmGeneratorExpressionInterpreter genexInterpreter( this->LocalGenerator, config, this->GeneratorTarget, language); @@ -532,9 +583,16 @@ cmNinjaVars& vars, std::string const& config) const { cmMakefile* mf = this->GetMakefile(); - if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") || - mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID") || - mf->GetDefinition("MSVC_CUDA_ARCHITECTURE_ID")) { + bool supportsPDB = mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") || + mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID") || + mf->GetDefinition("MSVC_CUDA_ARCHITECTURE_ID"); + if (!supportsPDB) { + std::string const linkLanguage = + this->GeneratorTarget->GetLinkerLanguage(config); + supportsPDB = + mf->IsOn(cmStrCat("CMAKE_", linkLanguage, "_LINKER_SUPPORTS_PDB")); + } + if (supportsPDB) { std::string pdbPath; std::string compilePdbPath = this->ComputeTargetCompilePDB(config); if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE || @@ -642,6 +700,54 @@ return rule; } + +void SetupResponseFile(cmNinjaRule& rule, + cmRulePlaceholderExpander::RuleVariables& vars, + std::string& flags, std::string const& lang, + std::string const& responseFlag) +{ + rule.RspFile = "$RSP_FILE"; + rule.RspContent = + cmStrCat(' ', vars.Defines, ' ', vars.Includes, ' ', flags); + flags = cmStrCat(responseFlag, rule.RspFile); + vars.Defines = ""; + vars.Includes = ""; + // Swift consumes all source files in a module at once, which reaches + // command line length limits pretty quickly. Inject source files into the + // response file in this case as well. + if (lang == "Swift") { + rule.RspContent = cmStrCat(rule.RspContent, ' ', vars.Source); + vars.Source = ""; + } +} + +cmList ExpandRuleCommands(std::string const& command, + cmRulePlaceholderExpander::RuleVariables const& vars, + cmMakefile const* mf, std::string const& lang, + std::string const& launcher, + std::string const& cldeps, + cmLocalGenerator* localGenerator, + cmRulePlaceholderExpander* rulePlaceholderExpander) +{ + std::string const extraCommands = + mf->GetSafeDefinition(cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS")); + cmList commands(command); + if (!commands.empty()) { + commands.front().insert(0, "${CODE_CHECK}"); + commands.front().insert(0, "${LAUNCHER}"); + } + if (!commands.empty()) { + commands.front().insert(0, cldeps); + } + if (!extraCommands.empty()) { + commands.append(extraCommands); + } + for (std::string& cmd : commands) { + cmd = cmStrCat(launcher, cmd); + rulePlaceholderExpander->ExpandRuleVariables(localGenerator, cmd, vars); + } + return commands; +} } void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang, @@ -690,6 +796,7 @@ vars.CudaCompileMode = "$CUDA_COMPILE_MODE"; vars.ISPCHeader = "$ISPC_HEADER_FILE"; vars.Config = "$CONFIG"; + vars.RustEmit = "$RUST_EMIT"; cmMakefile* mf = this->GetMakefile(); @@ -843,20 +950,7 @@ cmNinjaRule rule(this->LanguageCompilerRule(lang, config, withScanning)); // If using a response file, move defines, includes, and flags into it. if (!responseFlag.empty()) { - rule.RspFile = "$RSP_FILE"; - rule.RspContent = - cmStrCat(' ', vars.Defines, ' ', vars.Includes, ' ', flags); - flags = cmStrCat(responseFlag, rule.RspFile); - vars.Defines = ""; - vars.Includes = ""; - - // Swift consumes all source files in a module at once, which reaches - // command line length limits pretty quickly. Inject source files into the - // response file in this case as well. - if (lang == "Swift") { - rule.RspContent = cmStrCat(rule.RspContent, ' ', vars.Source); - vars.Source = ""; - } + SetupResponseFile(rule, vars, flags, lang, responseFlag); } // Tell ninja dependency format so all deps can be loaded into a database @@ -947,28 +1041,9 @@ // Rule for compiling object file. std::string const cmdVar = this->GetCompileTemplateVar(lang); std::string const& compileCmd = mf->GetRequiredDefinition(cmdVar); - cmList compileCmds(compileCmd); - - if (!compileCmds.empty()) { - compileCmds.front().insert(0, "${CODE_CHECK}"); - compileCmds.front().insert(0, "${LAUNCHER}"); - } - - if (!compileCmds.empty()) { - compileCmds.front().insert(0, cldeps); - } - - auto const& extraCommands = this->GetMakefile()->GetSafeDefinition( - cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS")); - if (!extraCommands.empty()) { - compileCmds.append(extraCommands); - } - - for (auto& i : compileCmds) { - i = cmStrCat(launcher, i); - rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), i, - vars); - } + cmList compileCmds = ExpandRuleCommands(compileCmd, vars, mf, lang, launcher, + cldeps, this->GetLocalGenerator(), + rulePlaceholderExpander.get()); rule.Command = this->GetLocalGenerator()->BuildCommandLine(compileCmds, config, config); @@ -977,6 +1052,40 @@ rule.Comment = cmStrCat("Rule for compiling ", lang, " files."); rule.Description = cmStrCat("Building ", lang, " object $out"); this->GetGlobalGenerator()->AddRule(rule); + + // Write a separate emit-module rule for Swift (produces .swiftmodule + // without compile outputs, enabling downstream modules to compile in + // parallel with upstream compilation). + if (lang == "Swift" && withScanning == WithScanning::No) { + std::string const emitModCmdVar = "CMAKE_Swift_EMIT_MODULE"; + cmValue emitModCmdVal = mf->GetDefinition(emitModCmdVar); + if (emitModCmdVal) { + cmNinjaRule emitModRule(this->LanguageEmitModuleRule(lang, config)); + cmRulePlaceholderExpander::RuleVariables emVars = vars; + std::string emFlags = "$FLAGS"; + if (!responseFlag.empty()) { + // Reset placeholders after compile response-file setup. + emVars.Source = "$in"; + emVars.Object = "$out"; + emVars.Defines = "$DEFINES"; + emVars.Includes = "$INCLUDES"; + SetupResponseFile(emitModRule, emVars, emFlags, lang, responseFlag); + } + + emVars.Flags = emFlags.c_str(); + emitModRule.Restat = "1"; + + cmList emitModCmds = ExpandRuleCommands( + *emitModCmdVal, emVars, mf, lang, launcher, cldeps, + this->GetLocalGenerator(), rulePlaceholderExpander.get()); + emitModRule.Command = this->GetLocalGenerator()->BuildCommandLine( + emitModCmds, config, config); + emitModRule.Comment = "Rule for emitting Swift .swiftmodule files."; + emitModRule.Description = + cmStrCat("Emitting Swift .swiftmodule ", "$out"); + this->GetGlobalGenerator()->AddRule(emitModRule); + } + } } void cmNinjaTargetGenerator::WriteObjectBuildStatements( @@ -1050,17 +1159,9 @@ // 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::NEW: - usePrivateGeneratedSources = true; - break; - } - } + bool usePrivateGeneratedSources = this->GeneratorTarget->HasFileSets() && + this->GetGeneratorTarget()->GetPolicyStatusCMP0154() == cmPolicies::NEW; + for (cmCustomCommand const* cc : customCommands) { cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator()); std::vector<std::string> const& ccoutputs = ccg.GetOutputs(); @@ -1073,20 +1174,54 @@ // Skip over outputs that were already detected. std::advance(it, nPreviousOutputs); while (it != ccouts.end()) { - cmFileSet const* fileset = + cmGeneratorFileSet const* fileset = this->GeneratorTarget->GetFileSetForSource( config, this->Makefile->GetOrCreateGeneratedSource(*it)); - bool isVisible = fileset && - cmFileSetVisibilityIsForInterface(fileset->GetVisibility()); - bool isIncludeable = - !fileset || cmFileSetTypeCanBeIncluded(fileset->GetType()); - if (fileset && isVisible && isIncludeable) { - ++it; + + if (!fileset) { + // use private order dependency + ccouts_private.push_back(*it); + it = ccouts.erase(it); continue; } - if (!fileset || isIncludeable) { - ccouts_private.push_back(*it); + + using DependencyMode = cm::FileSetMetadata::DependencyMode; + + cmValue independentFiles = fileset->GetProperty("INDEPENDENT_FILES"); + // retrieve default mode + DependencyMode dependencyMode = + cm::FileSetMetadata::GetDependencyMode(fileset->GetType()); + // if property is defined, try to enforce mode requested + if (independentFiles) { + dependencyMode = cm::FileSetMetadata::GetDependencyMode( + fileset->GetType(), + independentFiles.IsOn() ? DependencyMode::IndependentFiles + : DependencyMode::Includables); } + if (independentFiles.IsOn() && + dependencyMode != DependencyMode::IndependentFiles) { + // requested dependency mode not supported + this->GetMakefile()->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat(R"(the "INDEPENDENT_FILES" property of the file set ")", + fileset->GetName(), R"(" of the target ")", + this->GeneratorTarget->GetName(), + R"(" will be ignored because it is incompatible with )" + R"(the file set type ")", + fileset->GetType(), R"(".)")); + } + if (dependencyMode == DependencyMode::Includables) { + if (fileset->IsForInterface()) { + // use public order dependency + ++it; + } else { + // use private order dependency + ccouts_private.push_back(*it); + it = ccouts.erase(it); + } + continue; + } + // no order dependency is required it = ccouts.erase(it); } } @@ -1156,7 +1291,6 @@ this->GeneratorTarget->GetObjectSources(objectSources, config); std::vector<cmSourceFile const*> swiftSources; - for (cmSourceFile const* sf : objectSources) { if (this->GetLocalGenerator()->IsSplitSwiftBuild() && sf->GetLanguage() == "Swift") { @@ -1166,6 +1300,7 @@ firstForConfig); } } + WriteSwiftObjectBuildStatement(swiftSources, config, fileConfig, firstForConfig); } @@ -1185,12 +1320,12 @@ std::vector<cmSourceFile*> sources; this->GeneratorTarget->GetSourceFiles(sources, config); for (cmSourceFile const* sf : sources) { - cmFileSet const* fs = + cmGeneratorFileSet const* fs = this->GeneratorTarget->GetFileSetForSource(config, sf); if (!fs) { continue; } - if (fs->GetType() != "CXX_MODULES"_s) { + if (fs->GetType() != cm::FileSetMetadata::CXX_MODULES) { continue; } if (sf->GetLanguage().empty()) { @@ -1198,8 +1333,8 @@ MessageType::FATAL_ERROR, cmStrCat("Target \"", this->GeneratorTarget->GetName(), "\" has source file\n ", sf->GetFullPath(), - "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not " - "scheduled for compilation.")); + "\nin a \"FILE_SET TYPE ", cm::FileSetMetadata::CXX_MODULES, + "\" but it is not scheduled for compilation.")); } } } @@ -1249,6 +1384,23 @@ this->WriteTargetDependInfo(language, config); + // Non-imported synthetic targets read module info from their native target + // Add as implicit dependency. + if (this->GeneratorTarget->IsSynthetic()) { + if (cmGeneratorTarget const* native_gt = + this->LocalGenerator->FindGeneratorTargetToUse( + this->GeneratorTarget->Target->GetTemplateName())) { + if (!native_gt->IsImported()) { + std::string native_dir = native_gt->GetSupportDirectory(); + if (this->GetGlobalGenerator()->IsMultiConfig()) { + native_dir = cmStrCat(native_dir, '/', config); + } + build.ImplicitDeps.emplace_back(this->ConvertToNinjaPath( + cmStrCat(native_dir, '/', language, "Modules.json"))); + } + } + } + auto const linked_directories = this->GetLinkedTargetDirectories(language, config); for (std::string const& l : linked_directories.Direct) { @@ -1296,6 +1448,7 @@ this->Configs[config].SwiftOutputMap[""] = deps; cmGeneratedFileStream output(mapFilePath); + output.SetCopyIfDifferent(true); output << this->Configs[config].SwiftOutputMap; // Add flag @@ -1442,10 +1595,17 @@ auto compilerLauncher = this->GetCompilerLauncher(language, config); + cmGeneratorFileSet const* fileSet = + this->GeneratorTarget->GetFileSetForSource(config, source); + + cmValue const fsSkipCodeCheckVal = + fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr; cmValue const srcSkipCodeCheckVal = source->GetProperty("SKIP_LINTING"); - bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet() - ? srcSkipCodeCheckVal.IsOn() - : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING"); + bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet() + ? fsSkipCodeCheckVal.IsOn() + : (srcSkipCodeCheckVal.IsSet() + ? srcSkipCodeCheckVal.IsOn() + : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING")); if (!skipCodeCheck) { auto const cmakeCmd = @@ -1521,7 +1681,9 @@ } } - if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!pchSources.empty() && + !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { for (std::string const& arch : pchArchs) { depList.push_back( this->GeneratorTarget->GetPchHeader(config, language, arch)); @@ -1637,8 +1799,9 @@ scanningFiles.ScanningOutput = cmStrCat(objectFileName, ".ddi"); } - this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), - source, ppBuild.Variables); + this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, + this->GetGeneratorTarget(), source, + ppBuild.Variables); this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), ppBuild, commandLineLengthLimit); @@ -1671,13 +1834,15 @@ this->ConvertToOutputFormatForShell(targetSupportDir); vars["OBJECT_FILE_DIR"] = this->ConvertToOutputFormatForShell(objectFileDir); - this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), - source, vars); + this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, + this->GetGeneratorTarget(), source, vars); - if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!pchSources.empty() && + !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { auto pchIt = pchSources.find(source->GetFullPath()); if (pchIt != pchSources.end()) { - this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER", + this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER", config, this->GetGeneratorTarget(), nullptr, vars); } } @@ -1739,6 +1904,11 @@ } } + if (language == "Rust") { + cmValue const rustEmit = source->GetRustEmitProperty(); + vars["RUST_EMIT"] = rustEmit; + } + if (language == "Swift") { this->EmitSwiftDependencyInfo(source, config); } else { @@ -1869,8 +2039,9 @@ scanningFiles.ScanningOutput = cmStrCat(bmiFileName, ".ddi"); } - this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), - source, ppBuild.Variables); + this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, + this->GetGeneratorTarget(), source, + ppBuild.Variables); this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), ppBuild, commandLineLengthLimit); @@ -1898,8 +2069,8 @@ this->ConvertToOutputFormatForShell(targetSupportDir); vars["OBJECT_FILE_DIR"] = this->ConvertToOutputFormatForShell(bmiFileDir); - this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), - source, vars); + this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, + this->GetGeneratorTarget(), source, vars); bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp"); @@ -1929,7 +2100,8 @@ // - Definitions // - Include paths // - (single-output) output object filename - // - Swiftmodule + // - Swiftmodule (for importable targets), produced either by the compile + // edge or by a separate emit-module edge // // Per-File: // - compile-command @@ -1991,18 +2163,6 @@ return !isMultiThread && compileMode == cmSwiftCompileMode::Wholemodule; }(); - // Without `-emit-library` or `-emit-executable`, targets with a single - // source file parse as a Swift script instead of like normal source. For - // non-executable targets, append this to ensure that they are parsed like a - // normal source. - if (target.GetType() != cmStateEnums::EXECUTABLE) { - this->LocalGenerator->AppendFlags(vars["FLAGS"], "-parse-as-library"); - } - - if (target.GetType() == cmStateEnums::STATIC_LIBRARY) { - this->LocalGenerator->AppendFlags(vars["FLAGS"], "-static"); - } - // Does this swift target emit a module file for importing into other // targets? auto isImportableTarget = [](cmGeneratorTarget const& tgt) -> bool { @@ -2013,19 +2173,33 @@ } return true; }; + bool const targetIsImportable = isImportableTarget(target); - // Swift modules only make sense to emit from things that can be imported. - // Executables that don't export symbols can't be imported, so don't try to - // emit a swiftmodule for them. It will break. - if (isImportableTarget(target)) { - std::string const emitModuleFlag = "-emit-module"; - std::string const modulePathFlag = "-emit-module-path"; - this->LocalGenerator->AppendFlags( - vars["FLAGS"], - { emitModuleFlag, modulePathFlag, - this->LocalGenerator->ConvertToOutputFormat( - moduleFilepath, cmOutputConverter::SHELL) }); - objBuild.Outputs.push_back(moduleFilepath); + // Check if we can emit the module separately (produces .swiftmodule before + // compilation finishes, enabling downstream modules to compile in parallel). + bool const emitModuleSeparately = [&]() -> bool { + if (!targetIsImportable || + !this->GetMakefile()->GetDefinition("CMAKE_Swift_EMIT_MODULE")) { + return false; + } + cmValue prop = + this->GeneratorTarget->GetProperty("Swift_SEPARATE_MODULE_EMISSION"); + if (prop) { + return prop.IsOn(); + } + return this->GeneratorTarget->GetPolicyStatusCMP0215() == cmPolicies::NEW; + }(); + + // Build flags common to both compile and emit-module edges. + if (target.GetType() != cmStateEnums::EXECUTABLE) { + // Without `-emit-library` or `-emit-executable`, targets with a single + // source file parse as a Swift script instead of like normal source. For + // non-executable targets, append this to ensure that they are parsed like + // a normal source. + this->LocalGenerator->AppendFlags(vars["FLAGS"], "-parse-as-library"); + } + if (target.GetType() == cmStateEnums::STATIC_LIBRARY) { + this->LocalGenerator->AppendFlags(vars["FLAGS"], "-static"); } this->LocalGenerator->AppendFlags(vars["FLAGS"], cmStrCat("-module-name ", moduleName)); @@ -2037,7 +2211,6 @@ this->LocalGenerator->AppendFlags( vars["FLAGS"], cmStrCat(libraryLinkNameFlag, ' ', libraryLinkName)); } - this->LocalGenerator->AppendFlags(vars["FLAGS"], this->GetFlags(language, config)); vars["DEFINES"] = this->GetDefines(language, config); @@ -2050,9 +2223,14 @@ this->GetGlobalGenerator()->GetLanguageOutputExtension(language))); objBuild.RspFile = cmStrCat(targetObjectFilename, ".swift.rsp"); + if (targetIsImportable) { + this->Configs[config].SwiftModuleOutput = moduleFilepath; + } + if (targetIsImportable && !emitModuleSeparately) { + objBuild.Outputs.push_back(moduleFilepath); + } + if (isSingleOutput) { - this->LocalGenerator->AppendFlags(vars["FLAGS"], - cmStrCat("-o ", targetObjectFilename)); objBuild.Outputs.push_back(targetObjectFilename); this->Configs[config].Objects.push_back(targetObjectFilename); } @@ -2079,6 +2257,26 @@ this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]); } + // Save common flags for the emit-module edge before adding + // compile-specific flags (-emit-module, -emit-module-path, -o). + std::string const commonFlags = vars["FLAGS"]; + + std::string const moduleOutputPath = + this->LocalGenerator->ConvertToOutputFormat(moduleFilepath, + cmOutputConverter::SHELL); + + if (targetIsImportable && !emitModuleSeparately) { + std::string const emitModuleFlag = "-emit-module"; + std::string const modulePathFlag = "-emit-module-path"; + this->LocalGenerator->AppendFlags( + vars["FLAGS"], { emitModuleFlag, modulePathFlag, moduleOutputPath }); + } + + if (isSingleOutput) { + this->LocalGenerator->AppendFlags(vars["FLAGS"], + cmStrCat("-o ", targetObjectFilename)); + } + if (firstForConfig) { this->ExportSwiftObjectCompileCommand( sources, targetObjectFilename, vars["FLAGS"], vars["DEFINES"], @@ -2102,6 +2300,40 @@ objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); + // Write a separate emit-module build edge that produces .swiftmodule + // without compile outputs. This allows downstream Swift targets to start + // compiling as soon as the module interface is ready, overlapping with + // upstream compilation and linking. + if (emitModuleSeparately) { + cmNinjaBuild modBuild = objBuild; + modBuild.Rule = this->LanguageEmitModuleRule(language, config); + + // Start from common flags (shared with compile edge) and add + // emit-module-specific flags. The emit-module rule template already + // contains -emit-module, so we only need -emit-module-path here. + // Skip if the flags already contain one (e.g. a directory-style path + // set by the target's compile options). + modBuild.Variables["FLAGS"] = commonFlags; + this->LocalGenerator->AppendFlags( + modBuild.Variables["FLAGS"], + cmStrCat("-emit-module-path ", moduleOutputPath)); + + modBuild.RspFile = cmStrCat(moduleFilepath, ".rsp"); + + // Output is just the .swiftmodule + this->EnsureParentDirectoryExists(moduleFilepath); + modBuild.Outputs.clear(); + modBuild.Outputs.push_back(moduleFilepath); + + this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), + modBuild, + this->ForceResponseFile() ? -1 : 0); + + // Both edges share the same -output-file-map; serialize the compile + // edge after emit-module so they do not race on the module .swiftdeps. + objBuild.OrderOnlyDeps.push_back(moduleFilepath); + } + // Write object build this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), objBuild, @@ -2172,6 +2404,22 @@ tdi_forward_modules_from_target_dirs.append(l); } + // Record the native target support directory for non-imported synthetic + // targets + if (this->GeneratorTarget->IsSynthetic()) { + if (cmGeneratorTarget* nativeGT = + this->LocalGenerator->FindGeneratorTargetToUse( + this->GeneratorTarget->Target->GetTemplateName())) { + if (!nativeGT->IsImported()) { + std::string nativeDir = nativeGT->GetSupportDirectory(); + if (this->GetGlobalGenerator()->IsMultiConfig()) { + nativeDir = cmStrCat(nativeDir, '/', config); + } + tdi["native-target-dir"] = nativeDir; + } + } + } + cmDyndepGeneratorCallbacks cb; cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { return this->GetObjectFilePath(sf, cnf); @@ -2451,6 +2699,16 @@ return {}; } +std::string cmNinjaTargetGenerator::GetSwiftModuleOutput( + std::string const& config) const +{ + auto const it = this->Configs.find(config); + if (it != this->Configs.end()) { + return it->second.SwiftModuleOutput; + } + return {}; +} + void cmNinjaTargetGenerator::EnsureDirectoryExists( std::string const& path) const { @@ -2528,15 +2786,22 @@ } void cmNinjaTargetGenerator::addPoolNinjaVariable( - std::string const& pool_property, cmGeneratorTarget* target, - cmSourceFile const* source, cmNinjaVars& vars) + std::string const& pool_property, std::string const& config, + cmGeneratorTarget* target, cmSourceFile const* source, cmNinjaVars& vars) { - // First check the current source properties, then if not found, its target - // ones. Allows to override a target-wide compile pool with a source-specific - // one. + // First check file set properties, then if not found the current source + // properties, then if not found, its target ones. Allows to override a + // target-wide compile pool with file set-specific or source-specific one. cmValue pool = {}; if (source) { - pool = source->GetProperty(pool_property); + cmGeneratorFileSet const* fileSet = + target->GetFileSetForSource(config, source); + if (fileSet) { + pool = fileSet->GetProperty(pool_property); + } + if (!pool) { + pool = source->GetProperty(pool_property); + } } if (!pool) { pool = target->GetProperty(pool_property);
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 39d3026..a019df2 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h
@@ -78,6 +78,8 @@ std::string LanguageCompilerRule(std::string const& lang, std::string const& config, WithScanning withScanning) const; + std::string LanguageEmitModuleRule(std::string const& lang, + std::string const& config) const; std::string LanguagePreprocessAndScanRule(std::string const& lang, std::string const& config) const; std::string LanguageScanRule(std::string const& lang, @@ -214,6 +216,7 @@ void AdditionalCleanFiles(std::string const& config); cmNinjaDeps GetObjects(std::string const& config) const; + std::string GetSwiftModuleOutput(std::string const& config) const; void EnsureDirectoryExists(std::string const& dir) const; void EnsureParentDirectoryExists(std::string const& path) const; @@ -244,6 +247,7 @@ /// @param source may be nullptr. void addPoolNinjaVariable(std::string const& pool_property, + std::string const& config, cmGeneratorTarget* target, cmSourceFile const* source, cmNinjaVars& vars); @@ -276,6 +280,8 @@ Json::Value SwiftOutputMap; cmNinjaDeps ExtraFiles; std::unique_ptr<MacOSXContentGeneratorType> MacOSXContentGenerator; + // Path declared as the .swiftmodule output (compile or emit-module edge). + std::string SwiftModuleOutput; }; std::map<std::string, ByConfig> Configs;
diff --git a/Source/cmOptionCommand.cxx b/Source/cmOptionCommand.cxx index cc20002..2f5de9a 100644 --- a/Source/cmOptionCommand.cxx +++ b/Source/cmOptionCommand.cxx
@@ -2,9 +2,10 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmOptionCommand.h" +#include <cmext/string_view> + #include "cmExecutionStatus.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmState.h" #include "cmStateSnapshot.h" @@ -78,13 +79,11 @@ auto const& existsAfterSet = status.GetMakefile().GetStateSnapshot().GetDefinition(args[0]); if (!existsAfterSet) { - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0077), - "\n" - "For compatibility with older versions of CMake, option " - "is clearing the normal variable '", - args[0], "'.")); + status.GetMakefile().IssuePolicyWarning( + cmPolicies::CMP0077, {}, + cmStrCat("For compatibility with older versions of CMake, " + "option is clearing the normal variable '"_s, + args[0], "'."_s)); } } return true;
diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx index c72dbe4..6826b67 100644 --- a/Source/cmOutputConverter.cxx +++ b/Source/cmOutputConverter.cxx
@@ -155,44 +155,36 @@ } std::string cmOutputConverter::ConvertToOutputForExisting( - std::string const& remote, OutputFormat format, bool useWatcomQuote) const + cm::string_view path, OutputFormat format, bool useWatcomQuote) const { #ifdef _WIN32 - // Cache the Short Paths since we only convert the same few paths anyway and - // calling `GetShortPathNameW` is really expensive. + // Cache the short paths since `GetShortPathNameW` is really expensive. static std::unordered_map<std::string, std::string> shortPathCache{}; - // If this is a windows shell, the result has a space, and the path - // already exists, we can use a short-path to reference it without a - // space. + // If this is a windows shell, the path has a space, and the path already + // exists, we can use a short-path to reference it without a space. if (this->GetState()->UseWindowsShell() && - remote.find_first_of(" #") != std::string::npos && - cmSystemTools::FileExists(remote)) { - - std::string shortPath = [&]() { - auto cachedShortPathIt = shortPathCache.find(remote); - - if (cachedShortPathIt != shortPathCache.end()) { - return cachedShortPathIt->second; + path.find_first_of(" #") != std::string::npos) { + auto origPath = std::string(path); + if (cmSystemTools::FileExists(origPath)) { + auto i = shortPathCache.find(origPath); + if (i == shortPathCache.end()) { + std::string shortPath; + if (!cmSystemTools::GetShortPath(origPath, shortPath)) { + // Fallback for cases when Windows refuses to resolve + // the short path, like for `C:\Program Files\WindowsApps\...`. + shortPath = origPath; + } + i = shortPathCache.emplace(std::move(origPath), std::move(shortPath)) + .first; } - - std::string tmp{}; - cmsys::Status status = cmSystemTools::GetShortPath(remote, tmp); - if (!status) { - // Fallback for cases when Windows refuses to resolve the short path, - // like for C:\Program Files\WindowsApps\... - tmp = remote; - } - shortPathCache[remote] = tmp; - return tmp; - }(); - - return this->ConvertToOutputFormat(shortPath, format, useWatcomQuote); + path = i->second; + } } #endif // Otherwise, perform standard conversion. - return this->ConvertToOutputFormat(remote, format, useWatcomQuote); + return this->ConvertToOutputFormat(path, format, useWatcomQuote); } std::string cmOutputConverter::ConvertToOutputFormat(cm::string_view source,
diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h index 6f6374c..35877f3 100644 --- a/Source/cmOutputConverter.h +++ b/Source/cmOutputConverter.h
@@ -54,7 +54,7 @@ std::string ConvertDirectorySeparatorsForShell(cm::string_view source) const; //! for existing files convert to output path and short path if spaces - std::string ConvertToOutputForExisting(std::string const& remote, + std::string ConvertToOutputForExisting(cm::string_view path, OutputFormat format = SHELL, bool useWatcomQuote = false) const;
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index b043b11..88b6295 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx
@@ -10,6 +10,7 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmList.h" #include "cmMakefile.h" @@ -95,8 +96,8 @@ // The OLD policy behavior doesn't define a variable for an empty or // missing value, and we can't differentiate between those two cases. if (parseFromArgV && (cmp0174 == cmPolicies::WARN)) { - makefile.IssueMessage( - MessageType::AUTHOR_WARNING, + makefile.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("The ", iter.first, " keyword was followed by an empty string or no value at " "all. Policy CMP0174 is not set, so " @@ -140,6 +141,8 @@ // 1 2 3 4 // or // cmake_parse_arguments(PARSE_ARGV N prefix options single multi) + // or + // cmake_parse_arguments(PARSE_ARGN prefix options single multi) if (args.size() < 4) { status.SetError("must be called with at least 4 arguments."); return false; @@ -147,8 +150,9 @@ auto argIter = args.begin(); auto argEnd = args.end(); - bool parseFromArgV = false; - unsigned long argvStart = 0; + bool parseFromArg = false; + unsigned long argStart = 0; + unsigned long argnc = 0; if (*argIter == "PARSE_ARGV") { if (args.size() != 6) { status.GetMakefile().IssueMessage( @@ -157,9 +161,9 @@ cmSystemTools::SetFatalErrorOccurred(); return true; } - parseFromArgV = true; + parseFromArg = true; argIter++; // move past PARSE_ARGV - if (!cmStrToULong(*argIter, &argvStart)) { + if (!cmStrToULong(*argIter, &argStart)) { status.GetMakefile().IssueMessage( MessageType::FATAL_ERROR, cmStrCat("PARSE_ARGV index '", *argIter, @@ -168,6 +172,26 @@ return true; } argIter++; // move past N + } else if (*argIter == "PARSE_ARGN") { + if (args.size() != 5) { + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + "PARSE_ARGN must be called with exactly 5 arguments."); + cmSystemTools::SetFatalErrorOccurred(); + return true; + } + parseFromArg = true; + argIter++; // move past PARSE_ARGN + std::string argncStr = + status.GetMakefile().GetSafeDefinition("_FUNCTION_ARGNC"); + if (!cmStrToULong(argncStr, &argnc)) { + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("PARSE_ARGN called with _FUNCTION_ARGNC='", argncStr, + "' that is not an unsigned integer")); + cmSystemTools::SetFatalErrorOccurred(); + return true; + } } // the first argument is the prefix std::string const prefix = (*argIter++) + "_"; @@ -201,31 +225,38 @@ parser.Bind(list, multiValArgs, duplicateKey); list.clear(); - if (!parseFromArgV) { + if (!parseFromArg) { // Flatten ;-lists in the arguments into a single list as was done // by the original function(CMAKE_PARSE_ARGUMENTS). for (; argIter != argEnd; ++argIter) { list.append(*argIter); } } else { - // in the PARSE_ARGV move read the arguments from ARGC and ARGV# + cm::string_view mode = *args.begin(); + // in the PARSE_ARGV or PARSE_ARGN mode read the arguments from ARGC and + // ARGV# std::string argc = status.GetMakefile().GetSafeDefinition("ARGC"); unsigned long count; if (!cmStrToULong(argc, &count)) { status.GetMakefile().IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("PARSE_ARGV called with ARGC='", argc, + cmStrCat(mode, " called with ARGC='", argc, "' that is not an unsigned integer")); cmSystemTools::SetFatalErrorOccurred(); return true; } - for (unsigned long i = argvStart; i < count; ++i) { + + if (mode == "PARSE_ARGN") { + argStart = count - argnc; + } + + for (unsigned long i = argStart; i < count; ++i) { std::string const argName{ cmStrCat("ARGV", i) }; cmValue arg = status.GetMakefile().GetDefinition(argName); if (!arg) { status.GetMakefile().IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("PARSE_ARGV called with ", argName, " not set")); + cmStrCat(mode, " called with ", argName, " not set")); cmSystemTools::SetFatalErrorOccurred(); return true; } @@ -250,7 +281,7 @@ prefix, status.GetMakefile(), options, singleValArgs, multiValArgs, unparsed, options_set(keywordsSeen.begin(), keywordsSeen.end()), options_set(keywordsMissingValues.begin(), keywordsMissingValues.end()), - parseFromArgV); + parseFromArg); return true; }
diff --git a/Source/cmPkgConfigResolver.cxx b/Source/cmPkgConfigResolver.cxx index 54598c8..4d7ef27 100644 --- a/Source/cmPkgConfigResolver.cxx +++ b/Source/cmPkgConfigResolver.cxx
@@ -227,7 +227,7 @@ } cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolveStrict( - std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env) + std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env) { cm::optional<cmPkgConfigResult> result; cmPkgConfigResult config; @@ -289,11 +289,11 @@ } cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolvePermissive( - std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env) + std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env) { cm::optional<cmPkgConfigResult> result; - cmPkgConfigResult config = ResolveBestEffort(entries, std::move(env)); + cmPkgConfigResult config = ResolveBestEffort(entries, env); auto const& keys = config.Keywords; if (keys.find("Name") == keys.end() || @@ -307,7 +307,7 @@ } cmPkgConfigResult cmPkgConfigResolver::ResolveBestEffort( - std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env) + std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env) { cmPkgConfigResult result;
diff --git a/Source/cmPkgConfigResolver.h b/Source/cmPkgConfigResolver.h index e1d9d0a..992429a 100644 --- a/Source/cmPkgConfigResolver.h +++ b/Source/cmPkgConfigResolver.h
@@ -99,13 +99,13 @@ public: static cm::optional<cmPkgConfigResult> ResolveStrict( - std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env); + std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env); static cm::optional<cmPkgConfigResult> ResolvePermissive( - std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env); + std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env); static cmPkgConfigResult ResolveBestEffort( - std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env); + std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env); static cmPkgConfigVersionReq ParseVersion(std::string const& version);
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index 337f220..5c4ba3f 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx
@@ -10,6 +10,7 @@ #include "cmsys/String.h" +#include "cmDiagnostics.h" #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -259,10 +260,10 @@ minPatch > maxPatch) || (minMajor == maxMajor && minMinor == maxMinor && minPatch == maxPatch && minTweak > maxTweak)) { - mf->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Policy VERSION range \"", version_min, "...", version_max, - "\" specifies a larger minimum than maximum.")); + mf->IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("Policy VERSION range \"", version_min, "...", + version_max, + "\" specifies a later minimum than maximum.")); return false; } @@ -339,8 +340,8 @@ } } else if (majorVer == 3 && minorVer < 10 && warnCompat == WarnCompat::On) { // Warn about policy versions for which support will be removed. - mf->IssueMessage( - MessageType::DEPRECATION_WARNING, + mf->IssueDiagnostic( + cmDiagnostics::CMD_DEPRECATED, "Compatibility with CMake < 3.10 will be removed from " "a future version of CMake.\n" ADVICE_UPDATE_VERSION_ARGUMENT); }
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index d999af7..5861fda 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h
@@ -629,7 +629,36 @@ 4, 3, 0, WARN) \ SELECT(POLICY, CMP0210, \ "CMAKE_<LANG>_LINK_FLAGS adds link flags to all target types.", 4, \ - 3, 0, WARN) + 3, 0, WARN) \ + SELECT(POLICY, CMP0211, \ + "A file may belong to at most one non-HEADERS file set in a " \ + "target.", \ + 4, 4, 0, WARN) \ + SELECT(POLICY, CMP0212, \ + "add_custom_command DEPENDS does not strip .exe suffixes.", 4, 4, 0, \ + WARN) \ + SELECT(POLICY, CMP0213, \ + "file(ARCHIVE_{CREATE,EXTRACT}) encode archive paths as UTF-8 by " \ + "default.", \ + 4, 4, 0, WARN) \ + SELECT(POLICY, CMP0214, \ + "Honor CMAKE_EXE_LINKER_FLAGS for Swift executable targets.", 4, 4, \ + 0, WARN) \ + SELECT(POLICY, CMP0215, \ + "Ninja generators emit Swift modules separately from compilation.", \ + 4, 4, 0, WARN) \ + SELECT(POLICY, CMP0216, "Swift targets have a default project name.", 4, 4, \ + 0, WARN) \ + SELECT(POLICY, CMP0217, \ + "The MACROS directory property does not exist anymore.", 4, 4, 0, \ + WARN) \ + SELECT(POLICY, CMP0218, \ + "The CMAKE_WARN_DEPRECATED and CMAKE_ERROR_DEPRECATED variables " \ + "are ignored.", \ + 4, 4, 0, WARN) \ + SELECT(POLICY, CMP0219, \ + "Macro invocations preserve backslashes in arguments.", 4, 4, 0, \ + WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -684,11 +713,16 @@ F(CMP0203) \ F(CMP0204) \ F(CMP0209) \ - F(CMP0210) + F(CMP0210) \ + F(CMP0211) \ + F(CMP0214) \ + F(CMP0215) \ + F(CMP0216) #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \ F(CMP0116) \ - F(CMP0147) + F(CMP0147) \ + F(CMP0212) /** \class cmPolicies * \brief Handles changes in CMake behavior and policies @@ -715,14 +749,15 @@ /** \brief Always the last entry. * - * Useful mostly to avoid adding a comma the last policy when adding a new - * one. + * Useful mostly to avoid adding a comma the last policy when adding + * a new one. */ CMPCOUNT }; //! convert a string policy ID into a number - static bool GetPolicyID(char const* id, /* out */ cmPolicies::PolicyID& pid); + static bool GetPolicyID(char const* id, + /* out */ cmPolicies::PolicyID& pid); //! Return whether a policy has been removed. static bool IsRemoved(cmPolicies::PolicyID id);
diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 8551132..0976d40 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx
@@ -16,6 +16,7 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmList.h" #include "cmMakefile.h" @@ -81,8 +82,8 @@ cmMakefile& mf = status.GetMakefile(); std::string const& projectName = args[0]; if (parser.HasKeyword(projectName)) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat( "project() called with '", projectName, "' as first argument. The first parameter should be the project name, " @@ -94,8 +95,8 @@ if (mf.IsRootMakefile() && !mf.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, "cmake_minimum_required() should be called prior to this top-level " "project() call. Please see the cmake-commands(7) manual for usage " "documentation of both commands.");
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 71fd8de..40e07a4 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx
@@ -252,7 +252,7 @@ auto StripCR = [](std::string& line) { std::string::size_type cr = line.find('\r'); if (cr != std::string::npos) { - line = line.substr(0, cr); + line.resize(cr); } };
diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx index 6514f80..c86bb26 100644 --- a/Source/cmQtAutoGenGlobalInitializer.cxx +++ b/Source/cmQtAutoGenGlobalInitializer.cxx
@@ -8,11 +8,11 @@ #include <cm/memory> #include "cmCustomCommand.h" +#include "cmDiagnostics.h" #include "cmDuration.h" #include "cmGeneratorTarget.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmProcessOutput.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenInitializer.h" @@ -145,7 +145,7 @@ cmQtAutoGen::Tools(mocDisabled, uicDisabled, rccDisabled), " disabled. Consider adding:\n find_package(Qt", version, " COMPONENTS ", component, ")\nto your CMakeLists.txt file."); - target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg); + target->Makefile->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, msg); } if (mocIsValid || uicIsValid || rccIsValid) { // Create autogen target initializer
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index cdba0c9..6a31383 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx
@@ -31,6 +31,7 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" +#include "cmDiagnostics.h" #include "cmEvaluatedTargetProperty.h" #include "cmGenExContext.h" #include "cmGeneratedFileStream.h" @@ -533,8 +534,8 @@ this->AutogenTarget.Parallel = static_cast<ParallelType>(propInt); } else { // Warn the project author that AUTOGEN_PARALLEL is not valid. - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, + this->Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("AUTOGEN_PARALLEL=\"", prop, "\" for target \"", this->GenTarget->GetName(), "\" is not valid. Using AUTOGEN_PARALLEL=1")); @@ -556,8 +557,8 @@ static_cast<maxCommandLineLengthType>(propInt); } else { // Warn the project author that AUTOGEN_PARALLEL is not valid. - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, + this->Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("AUTOGEN_COMMAND_LINE_LENGTH_MAX=\"", *value, "\" for target \"", this->GenTarget->GetName(), "\" is not valid. Using no limit for " @@ -620,8 +621,8 @@ // CMAKE_AUTOMOC_RELAXED_MODE if (this->Makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE")) { this->Moc.RelaxedMode = true; - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, + this->Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is " "deprecated an will be removed in the future. Consider " "disabling it and converting the target ", @@ -1220,20 +1221,18 @@ for (MUFile const* muf : this->AutogenTarget.FilesGenerated) { files += cmStrCat(" ", Quoted(muf->FullPath), '\n'); } - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0071, {}, cmStrCat( - cmPolicies::GetPolicyWarning(cmPolicies::CMP0071), - "\n" "For compatibility, CMake is excluding the GENERATED source " - "file(s):\n", - files, "from processing by ", + "file(s):\n"_s, + files, "from processing by "_s, cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false), ". If any of the files should be processed, set CMP0071 to NEW. " "If any of the files should not be processed, " - "explicitly exclude them by setting the source file property ", - property, ":\n set_property(SOURCE file.h PROPERTY ", property, - " ON)\n")); + "explicitly exclude them by setting the source file property "_s, + property, ":\n set_property(SOURCE file.h PROPERTY "_s, property, + " ON)"_s)); } } @@ -1252,19 +1251,17 @@ for (cmSourceFile const* sf : this->AutogenTarget.CMP0100HeadersWarn) { files += cmStrCat(" ", Quoted(sf->GetFullPath()), '\n'); } - this->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0100, {}, cmStrCat( - cmPolicies::GetPolicyWarning(cmPolicies::CMP0100), - "\n" - "For compatibility, CMake is excluding the header file(s):\n", - files, "from processing by ", + "For compatibility, CMake is excluding the header file(s):\n"_s, files, + "from processing by "_s, cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false), ". If any of the files should be processed, set CMP0100 to NEW. " "If any of the files should not be processed, " - "explicitly exclude them by setting the source file property ", - property, ":\n set_property(SOURCE file.hh PROPERTY ", property, - " ON)\n")); + "explicitly exclude them by setting the source file property "_s, + property, ":\n set_property(SOURCE file.hh PROPERTY "_s, property, + " ON)"_s)); } // Process qrc files @@ -1372,9 +1369,20 @@ auto const& gen = this->GlobalGen->GetName(); return this->QtVersion >= IntegerVersion(5, 15) && (gen.find("Ninja") != std::string::npos || - gen.find("Make") != std::string::npos); + gen.find("Make") != std::string::npos || + gen.find("Visual Studio") != std::string::npos || gen == "Xcode"); }(); + // Under VS, attach the autogen custom command to the origin target instead + // of creating separate targets, to reduce the number of targets loaded into + // the IDE. The dependencies of the origin target then provide the ordering + // that '_autogen_timestamp_deps' provides otherwise. The conditions match + // those of the PRE_BUILD event below, so that the '_autogen' target keeps + // existing wherever it did before depfiles were enabled for VS. + bool const attachToOrigin = useDepfile && + this->AutogenTarget.DependFiles.empty() && + !this->AutogenTarget.GlobalTarget && this->GlobalGen->IsVisualStudio(); + // Files provided by the autogen target std::vector<std::string> autogenByproducts; std::vector<std::string> timestampByproducts; @@ -1408,6 +1416,12 @@ } } + if (attachToOrigin) { + // Without an autogen target the timestamp command provides all byproducts. + cm::append(timestampByproducts, autogenByproducts); + autogenByproducts.clear(); + } + // Compose target comment std::string autogenComment; { @@ -1453,6 +1467,10 @@ if (this->AutogenTarget.GlobalTarget) { usePRE_BUILD = false; } + // Cannot use PRE_BUILD with depfiles + if (useDepfile) { + usePRE_BUILD = false; + } } // Create the autogen target/command if (usePRE_BUILD) { @@ -1515,8 +1533,9 @@ } else { // Add link library target dependencies to the autogen target - // dependencies - if (this->AutogenTarget.DependOrigin) { + // dependencies. Not needed when attaching to the origin target, which + // already depends on its own link libraries. + if (this->AutogenTarget.DependOrigin && !attachToOrigin) { // add_dependencies/addUtility do not support generator expressions. // We depend only on the libraries found in all configs therefore. std::map<cmGeneratorTarget const*, std::size_t> targetsPartOfAllConfigs; @@ -1553,41 +1572,43 @@ if (useDepfile) { // Create a custom command that generates a timestamp file and // has a depfile assigned. The depfile is created by JobDepFilesMergeT. - // - // Also create an additional '_autogen_timestamp_deps' that the custom - // command will depend on. It will have no sources or commands to - // execute, but it will have dependencies that would originally be - // assigned to the pre-Qt 5.15 'autogen' target. These dependencies will - // serve as a list of order-only dependencies for the custom command, - // without forcing the custom command to re-execute. - // - // The dependency tree would then look like - // '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <- - // '_autogen' target. - auto const timestampTargetName = - cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps"); + if (!attachToOrigin) { + // Also create an additional '_autogen_timestamp_deps' that the custom + // command will depend on. It will have no sources or commands to + // execute, but it will have dependencies that would originally be + // assigned to the pre-Qt 5.15 'autogen' target. These dependencies + // will serve as a list of order-only dependencies for the custom + // command, without forcing the custom command to re-execute. + // + // The dependency tree would then look like + // '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <- + // '_autogen' target. + auto const timestampTargetName = + cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps"); - auto cc = cm::make_unique<cmCustomCommand>(); - cc->SetWorkingDirectory(this->Dir.Work.c_str()); - cc->SetDepends(dependencies); - cc->SetEscapeOldStyle(false); - timestampTarget = this->LocalGen->AddUtilityCommand(timestampTargetName, - true, std::move(cc)); + auto cc = cm::make_unique<cmCustomCommand>(); + cc->SetWorkingDirectory(this->Dir.Work.c_str()); + cc->SetDepends(dependencies); + cc->SetEscapeOldStyle(false); + timestampTarget = this->LocalGen->AddUtilityCommand( + timestampTargetName, true, std::move(cc)); - this->LocalGen->AddGeneratorTarget( - cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen)); + this->LocalGen->AddGeneratorTarget( + cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen)); - // Set FOLDER property on the timestamp target, so it appears in the - // appropriate folder in an IDE or in the file api. - if (!this->TargetsFolder.empty()) { - timestampTarget->SetProperty("FOLDER", this->TargetsFolder); + // Set FOLDER property on the timestamp target, so it appears in the + // appropriate folder in an IDE or in the file api. + if (!this->TargetsFolder.empty()) { + timestampTarget->SetProperty("FOLDER", this->TargetsFolder); + } + + // Make '/timestamp' file depend on '_autogen_timestamp_deps'. + dependencies.clear(); + dependencies.push_back(timestampTargetName); } - // Make '/timestamp' file depend on '_autogen_timestamp_deps' and on the - // moc and uic executables (whichever are enabled). - dependencies.clear(); - dependencies.push_back(timestampTargetName); - + // Make '/timestamp' file depend on the moc and uic executables + // (whichever are enabled). AddAutogenExecutableToDependencies(this->Moc, dependencies); AddAutogenExecutableToDependencies(this->Uic, dependencies); std::string outputFile; @@ -1613,7 +1634,8 @@ for (std::string const& config : this->ConfigsList) { auto tempTimestampFileName = timestampFileWithoutConfig + config; auto tempDepFile = depFileWithoutConfig + config; - outputFileWithConfig.Config[config] = tempTimestampFileName; + outputFileWithConfig.Config[config] = + cmStrCat(this->Dir.Build, '/', tempTimestampFileName); this->AutogenTarget.DepFileRuleName.Config[config] = cmStrCat(this->Dir.RelativeBuild, '/', tempTimestampFileName); this->AutogenTarget.DepFile.Config[config] = tempDepFile; @@ -1631,7 +1653,7 @@ { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile })); this->AddGeneratedSource(outputFile, this->Moc); } - cc = cm::make_unique<cmCustomCommand>(); + auto cc = cm::make_unique<cmCustomCommand>(); cc->SetOutputs(outputFile); cc->SetByproducts(timestampByproducts); cc->SetDepends(dependencies); @@ -1648,48 +1670,57 @@ autogenComment.clear(); } - // Create autogen target - auto cc = cm::make_unique<cmCustomCommand>(); - cc->SetWorkingDirectory(this->Dir.Work.c_str()); - cc->SetByproducts(autogenByproducts); - cc->SetDepends(dependencies); - cc->SetCommandLines(commandLines); - cc->SetEscapeOldStyle(false); - cc->SetComment(autogenComment.c_str()); - cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand( - this->AutogenTarget.Name, true, std::move(cc)); - // Create autogen generator target - this->LocalGen->AddGeneratorTarget( - cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen)); - - // Order the autogen target(s) just before the original target. - cmTarget* orderTarget = timestampTarget ? timestampTarget : autogenTarget; - // Forward origin utilities to autogen target - if (this->AutogenTarget.DependOrigin) { - for (BT<std::pair<std::string, bool>> const& depName : - this->GenTarget->GetUtilities()) { - orderTarget->AddUtility(depName.Value.first, false, this->Makefile); + if (attachToOrigin) { + // Add additional autogen target dependencies to the origin target + for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) { + this->GenTarget->Target->AddUtility(depTarget->GetName(), false, + this->Makefile); } - } + } else { + // Create autogen target + auto cc = cm::make_unique<cmCustomCommand>(); + cc->SetWorkingDirectory(this->Dir.Work.c_str()); + cc->SetByproducts(autogenByproducts); + cc->SetDepends(dependencies); + cc->SetCommandLines(commandLines); + cc->SetEscapeOldStyle(false); + cc->SetComment(autogenComment.c_str()); + cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand( + this->AutogenTarget.Name, true, std::move(cc)); + // Create autogen generator target + this->LocalGen->AddGeneratorTarget( + cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen)); - // Add additional autogen target dependencies to autogen target - for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) { - orderTarget->AddUtility(depTarget->GetName(), false, this->Makefile); - } + // Order the autogen target(s) just before the original target. + cmTarget* orderTarget = + timestampTarget ? timestampTarget : autogenTarget; + // Forward origin utilities to autogen target + if (this->AutogenTarget.DependOrigin) { + for (BT<std::pair<std::string, bool>> const& depName : + this->GenTarget->GetUtilities()) { + orderTarget->AddUtility(depName.Value.first, false, this->Makefile); + } + } - // Set FOLDER property in autogen target - if (!this->TargetsFolder.empty()) { - autogenTarget->SetProperty("FOLDER", this->TargetsFolder); - } + // Add additional autogen target dependencies to autogen target + for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) { + orderTarget->AddUtility(depTarget->GetName(), false, this->Makefile); + } - // Add autogen target to the origin target dependencies - this->GenTarget->Target->AddUtility(this->AutogenTarget.Name, false, - this->Makefile); + // Set FOLDER property in autogen target + if (!this->TargetsFolder.empty()) { + autogenTarget->SetProperty("FOLDER", this->TargetsFolder); + } - // Add autogen target to the global autogen target dependencies - if (this->AutogenTarget.GlobalTarget) { - this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen, - this->AutogenTarget.Name); + // Add autogen target to the origin target dependencies + this->GenTarget->Target->AddUtility(this->AutogenTarget.Name, false, + this->Makefile); + + // Add autogen target to the global autogen target dependencies + if (this->AutogenTarget.GlobalTarget) { + this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen, + this->AutogenTarget.Name); + } } } @@ -1994,7 +2025,7 @@ info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode); info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix); - EvaluatedTargetPropertyEntries InterfaceAutoMocMacroNamesEntries; + cm::EvaluatedTargetPropertyEntries InterfaceAutoMocMacroNamesEntries; if (this->MultiConfig) { for (auto const& cfg : this->ConfigsList) { @@ -2003,10 +2034,10 @@ cmGeneratorExpressionDAGChecker dagChecker{ this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr, context, }; - AddInterfaceEntries(this->GenTarget, "INTERFACE_AUTOMOC_MACRO_NAMES", - context, &dagChecker, - InterfaceAutoMocMacroNamesEntries, - IncludeRuntimeInterface::Yes); + cm::AddInterfaceEntries( + this->GenTarget, "INTERFACE_AUTOMOC_MACRO_NAMES", context, + &dagChecker, InterfaceAutoMocMacroNamesEntries, + cm::IncludeRuntimeInterface::Yes); } } } else { @@ -2016,7 +2047,7 @@ }; AddInterfaceEntries( this->GenTarget, "INTERFACE_AUTOMOC_MACRO_NAMES", context, &dagChecker, - InterfaceAutoMocMacroNamesEntries, IncludeRuntimeInterface::Yes); + InterfaceAutoMocMacroNamesEntries, cm::IncludeRuntimeInterface::Yes); } for (auto const& entry : InterfaceAutoMocMacroNamesEntries.Entries) { @@ -2586,10 +2617,8 @@ if (this->MultiConfig && this->UseBetterGraph) { for (auto const& config : this->ConfigsList) { if (!genVars.ExecutableFeatures.Config[config]) { - if (!genVars.ExecutableFeatures.Config[config]) { - print_err(err); - return false; - } + print_err(err); + return false; } } } else {
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 8561425..95dc5a9 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx
@@ -2116,8 +2116,11 @@ this->Log().Info(GenT::MOC, result.StdOut); } - // Extract dependencies from the dep file moc generated for us - if (this->MocConst().CanOutputDependencies) { + // Extract dependencies from the dep file moc generated for us. + // When a build-system depfile is used, JobDepFilesMergeT handles reading + // and merging all moc dependencies, so skip per-compilation dep reading. + if (this->MocConst().CanOutputDependencies && + this->BaseConst().DepFile.empty()) { std::string const depfile = outputFile + ".d"; if (this->Log().Verbose()) { this->Log().Info(
diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx index e490370..0d3f3bc 100644 --- a/Source/cmRST.cxx +++ b/Source/cmRST.cxx
@@ -20,7 +20,7 @@ : OS(os) , DocRoot(std::move(docroot)) , CMakeDirective("^.. (cmake:)?(" - "command|envvar|genex|signature|variable" + "command|diagnostic|envvar|genex|signature|variable" ")::") , CMakeModuleDirective("^.. cmake-module::[ \t]+([^ \t\n]+)$") , ParsedLiteralDirective("^.. parsed-literal::[ \t]*(.*)$") @@ -34,7 +34,7 @@ , ModuleRST(R"(^#\[(=*)\[\.rst:$)") , CMakeRole("(:cmake)?:(" "cref|" - "command|cpack_gen|generator|genex|" + "command|cpack_gen|diagnostic|generator|genex|" "variable|envvar|module|policy|" "prop_cache|prop_dir|prop_gbl|prop_inst|prop_sf|" "prop_test|prop_tgt|"
diff --git a/Source/cmReturnCommand.cxx b/Source/cmReturnCommand.cxx index c0d7491..265bc0c 100644 --- a/Source/cmReturnCommand.cxx +++ b/Source/cmReturnCommand.cxx
@@ -7,7 +7,6 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -19,14 +18,11 @@ if (!args.empty()) { switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0140)) { case cmPolicies::WARN: - status.GetMakefile().IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat( - cmPolicies::GetPolicyWarning(cmPolicies::CMP0140), - "\n" - "return() checks its arguments when the policy is set to NEW. " - "Since the policy is not set the OLD behavior will be used so " - "the arguments will be ignored.")); + status.GetMakefile().IssuePolicyWarning( + cmPolicies::CMP0140, {}, + "return() checks its arguments when the policy is set to NEW. " + "Since the policy is not set the OLD behavior will be used so " + "the arguments will be ignored."_s); CM_FALLTHROUGH; case cmPolicies::OLD: return true;
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx index 83eef77..7f9f0c3 100644 --- a/Source/cmRulePlaceholderExpander.cxx +++ b/Source/cmRulePlaceholderExpander.cxx
@@ -11,13 +11,26 @@ cmRulePlaceholderExpander::cmRulePlaceholderExpander( cmBuildStep buildStep, std::map<std::string, std::string> compilers, std::map<std::string, std::string> variableMappings, - std::string compilerSysroot, std::string linkerSysroot) + std::string compilerSysroot, std::string linkerSysroot, + UseShortPaths useShortPaths) : BuildStep(buildStep) , Compilers(std::move(compilers)) , VariableMappings(std::move(variableMappings)) , CompilerSysroot(std::move(compilerSysroot)) , LinkerSysroot(std::move(linkerSysroot)) { + if (useShortPaths == UseShortPaths::Yes) { + this->ConvertToOutputForExisting = + [this](cm::string_view path) -> std::string { + return this->OutputConverter->ConvertToOutputForExisting(path); + }; + } else { + this->ConvertToOutputForExisting = + [this](cm::string_view path) -> std::string { + return this->OutputConverter->ConvertToOutputFormat( + path, cmOutputConverter::SHELL); + }; + } } std::string cmRulePlaceholderExpander::ExpandVariable( @@ -30,8 +43,8 @@ } if (this->ReplaceValues->Linker) { if (variable == "CMAKE_LINKER") { - auto result = this->OutputConverter->ConvertToOutputForExisting( - this->ReplaceValues->Linker); + auto result = + this->ConvertToOutputForExisting(this->ReplaceValues->Linker); if (this->ReplaceValues->Launcher) { // Add launcher as part of expansion so that it always appears // immediately before the command itself, regardless of whether the @@ -143,14 +156,24 @@ return this->ReplaceValues->SwiftSources; } } - if (this->ReplaceValues->RustSources) { - if (variable == "RUST_SOURCES") { - return this->ReplaceValues->RustSources; + if (this->ReplaceValues->RustEmit) { + if (variable == "RUST_EMIT") { + return this->ReplaceValues->RustEmit; } } - if (this->ReplaceValues->RustObjectDeps) { - if (variable == "RUST_OBJECT_DEPS") { - return this->ReplaceValues->RustObjectDeps; + if (this->ReplaceValues->RustMainCrateRoot) { + if (variable == "RUST_MAIN_CRATE_ROOT") { + return this->ReplaceValues->RustMainCrateRoot; + } + } + if (this->ReplaceValues->RustLinkCrates) { + if (variable == "RUST_LINK_CRATES") { + return this->ReplaceValues->RustLinkCrates; + } + } + if (this->ReplaceValues->RustNativeObjects) { + if (variable == "RUST_NATIVE_OBJECTS") { + return this->ReplaceValues->RustNativeObjects; } } if (this->ReplaceValues->TargetPDB) { @@ -301,8 +324,9 @@ auto compIt = this->Compilers.find(variable); if (compIt != this->Compilers.end()) { - std::string ret = this->OutputConverter->ConvertToOutputForExisting( - this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER"]); + std::string const& compilerPath = + this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER"]; + std::string ret = this->ConvertToOutputForExisting(compilerPath); std::string const& compilerArg1 = this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER_ARG1"]; std::string const& compilerTarget = @@ -365,7 +389,7 @@ auto mapIt = this->VariableMappings.find(variable); if (mapIt != this->VariableMappings.end()) { if (variable.find("_FLAG") == std::string::npos) { - return this->OutputConverter->ConvertToOutputForExisting(mapIt->second); + return this->ConvertToOutputForExisting(mapIt->second); } return mapIt->second; }
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h index 4b44c64..60fc8f8 100644 --- a/Source/cmRulePlaceholderExpander.h +++ b/Source/cmRulePlaceholderExpander.h
@@ -5,9 +5,12 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <functional> #include <map> #include <string> +#include <cm/string_view> + #include "cmGeneratorOptions.h" #include "cmPlaceholderExpander.h" @@ -16,10 +19,17 @@ class cmRulePlaceholderExpander : public cmPlaceholderExpander { public: + enum class UseShortPaths + { + No, + Yes, + }; + cmRulePlaceholderExpander( cmBuildStep buildStep, std::map<std::string, std::string> compilers, std::map<std::string, std::string> variableMappings, - std::string compilerSysroot, std::string linkerSysroot); + std::string compilerSysroot, std::string linkerSysroot, + UseShortPaths useShortPaths); void SetTargetImpLib(std::string const& targetImpLib) { @@ -69,8 +79,10 @@ char const* SwiftModuleName = nullptr; char const* SwiftOutputFileMapOption = nullptr; char const* SwiftSources = nullptr; - char const* RustSources = nullptr; - char const* RustObjectDeps = nullptr; + char const* RustEmit = nullptr; + char const* RustMainCrateRoot = nullptr; + char const* RustLinkCrates = nullptr; + char const* RustNativeObjects = nullptr; char const* ISPCHeader = nullptr; char const* CudaCompileMode = nullptr; char const* Fatbinary = nullptr; @@ -95,6 +107,7 @@ std::map<std::string, std::string> VariableMappings; std::string CompilerSysroot; std::string LinkerSysroot; + std::function<std::string(cm::string_view)> ConvertToOutputForExisting; cmOutputConverter* OutputConverter = nullptr; RuleVariables const* ReplaceValues = nullptr;
diff --git a/Source/cmSarifLog.cxx b/Source/cmSarifLog.cxx index 7294949..63c00c3 100644 --- a/Source/cmSarifLog.cxx +++ b/Source/cmSarifLog.cxx
@@ -124,14 +124,10 @@ cmSarif::ResultSeverityLevel cmSarif::MessageSeverityLevel(MessageType t) { switch (t) { - case MessageType::AUTHOR_WARNING: case MessageType::WARNING: - case MessageType::DEPRECATION_WARNING: return ResultSeverityLevel::SARIF_WARNING; - case MessageType::AUTHOR_ERROR: case MessageType::FATAL_ERROR: case MessageType::INTERNAL_ERROR: - case MessageType::DEPRECATION_ERROR: return ResultSeverityLevel::SARIF_ERROR; case MessageType::MESSAGE: case MessageType::LOG: @@ -144,20 +140,12 @@ cm::optional<std::string> cmSarif::MessageRuleId(MessageType t) { switch (t) { - case MessageType::AUTHOR_WARNING: - return "CMake.AuthorWarning"; case MessageType::WARNING: return "CMake.Warning"; - case MessageType::DEPRECATION_WARNING: - return "CMake.DeprecationWarning"; - case MessageType::AUTHOR_ERROR: - return "CMake.AuthorError"; case MessageType::FATAL_ERROR: return "CMake.FatalError"; case MessageType::INTERNAL_ERROR: return "CMake.InternalError"; - case MessageType::DEPRECATION_ERROR: - return "CMake.DeprecationError"; case MessageType::MESSAGE: return "CMake.Message"; case MessageType::LOG:
diff --git a/Source/cmSbomArguments.h b/Source/cmSbomArguments.h index aafa84f..17bb3b5 100644 --- a/Source/cmSbomArguments.h +++ b/Source/cmSbomArguments.h
@@ -41,6 +41,7 @@ SbomFormat GetFormat() const; ArgumentParser::NonEmpty<std::string> Format; + ArgumentParser::NonEmpty<std::string> PackageUrl; protected: cm::string_view CommandName() const override; @@ -54,6 +55,7 @@ cmProjectInfoArguments* const base = self; Bind(base, parser, "SBOM"_s, &cmProjectInfoArguments::PackageName); Bind(self, parser, "FORMAT"_s, &cmSbomArguments::Format); + Bind(self, parser, "PACKAGE_URL"_s, &cmSbomArguments::PackageUrl); cmProjectInfoArguments::Bind(parser, self); } };
diff --git a/Source/cmSbomBuilder.cxx b/Source/cmSbomBuilder.cxx new file mode 100644 index 0000000..b4add75 --- /dev/null +++ b/Source/cmSbomBuilder.cxx
@@ -0,0 +1,609 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmSbomBuilder.h" + +#include <algorithm> +#include <array> +#include <functional> +#include <map> +#include <memory> +#include <set> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#include <cm/optional> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmArgumentParserTypes.h" +#include "cmDiagnostics.h" +#include "cmExportFileGenerator.h" +#include "cmExportSet.h" +#include "cmFindPackageStack.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmList.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmSbomArguments.h" +#include "cmSbomObject.h" +#include "cmSpdx.h" +#include "cmSpdxSerializer.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetExport.h" +#include "cmValue.h" + +cmSpdxPackage::PurposeId GetPurpose(cmStateEnums::TargetType type) +{ + switch (type) { + case cmStateEnums::TargetType::EXECUTABLE: + return cmSpdxPackage::PurposeId::APPLICATION; + case cmStateEnums::TargetType::STATIC_LIBRARY: + case cmStateEnums::TargetType::SHARED_LIBRARY: + case cmStateEnums::TargetType::MODULE_LIBRARY: + case cmStateEnums::TargetType::OBJECT_LIBRARY: + case cmStateEnums::TargetType::INTERFACE_LIBRARY: + return cmSpdxPackage::PurposeId::LIBRARY; + case cmStateEnums::TargetType::UTILITY: + return cmSpdxPackage::PurposeId::SOURCE; + case cmStateEnums::TargetType::GLOBAL_TARGET: + case cmStateEnums::TargetType::UNKNOWN_LIBRARY: + default: + return cmSpdxPackage::PurposeId::ARCHIVE; + } +} + +cmSbomBuilder::cmSbomBuilder(cmSbomArguments args, + std::vector<cmExportSet*> exportSets, + cmLocalGenerator* lg) + : LocalGenerator(lg) + , ExportSets(std::move(exportSets)) + , PackageName(std::move(args.PackageName)) + , Namespace(cmStrCat(this->PackageName, "::"_s)) + , PackageVersion(std::move(args.Version)) + , PackageDescription(std::move(args.Description)) + , PackageWebsite(std::move(args.Website)) + , PackageUrl(std::move(args.PackageUrl)) + , PackageLicense(std::move(args.License)) + , PackageFormat(args.GetFormat()) +{ +} + +std::set<cmGeneratorTarget const*> cmSbomBuilder::CollectTargets() const +{ + std::set<cmGeneratorTarget const*> targets; + for (cmExportSet* exportSet : this->ExportSets) { + for (auto const& te : exportSet->GetTargetExports()) { + if (cmGeneratorTarget const* gt = + this->LocalGenerator->FindGeneratorTargetToUse(te->TargetName)) { + targets.emplace(gt); + } + } + } + return targets; +} + +void cmSbomBuilder::Compute(cmLocalGenerator* lg) +{ + this->LocalGenerator = lg; + if (!lg) { + return; + } + for (cmExportSet* es : this->ExportSets) { + es->Compute(lg); + } + // Populate the cache now (rather than at Generate time) so peer SBOMs can + // query CoversTarget() during their own NoteLinkedTarget walks. + this->SbomTargets = this->CollectTargets(); +} + +bool cmSbomBuilder::CoversTarget(cmGeneratorTarget const* target) const +{ + return cm::contains(this->SbomTargets, target); +} + +bool cmSbomBuilder::CoversExportSet(cmExportSet const* set) const +{ + return std::find(this->ExportSets.cbegin(), this->ExportSets.cend(), set) != + this->ExportSets.cend(); +} + +bool cmSbomBuilder::GenerateForTargets( + std::ostream& os, cmGeneratorExpression::PreprocessContext preprocessContext) +{ + cmSbomDocument doc; + doc.Graph.reserve(256); + + cmSpdxCreationInfo const* ci = + insert_back(doc.Graph, this->GenerateCreationInfo()); + cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom(ci)); + std::vector<TargetProperties> targetProps; + targetProps.reserve(this->SbomTargets.size()); + + for (cmGeneratorTarget const* target : this->SbomTargets) { + ImportPropertyMap properties; + this->PopulateLinkLibrariesProperty(target, preprocessContext, properties); + this->PopulateInterfaceLinkLibrariesProperty(target, preprocessContext, + properties); + + targetProps.push_back( + TargetProperties{ insert_back(project->RootElements, + this->GenerateImportTarget(ci, target)), + target, std::move(properties) }); + } + + for (TargetProperties const& target : targetProps) { + this->GenerateProperties(doc, project, ci, target, targetProps); + } + + this->WriteSbom(doc, os); + return true; +} + +void cmSbomBuilder::WriteSbom(cmSbomDocument& doc, std::ostream& os) const +{ + switch (this->PackageFormat) { + case cmSbomArguments::SbomFormat::SPDX_3_0_JSON: + cmSpdxSerializer{}.WriteSbom(os, cmSbomObject(doc)); + break; + case cmSbomArguments::SbomFormat::NONE: + break; + } +} + +bool cmSbomBuilder::AddPackageInformation( + cmSpdxPackage& artifact, std::string const& name, + cmPackageInformation const& package) const +{ + if (name.empty()) { + return false; + } + + cmSpdxOrganization org; + org.SpdxId = cmStrCat("urn:", name, "#Organization"); + org.Name = name; + org.CreationInfo = artifact.CreationInfo; + artifact.OriginatedBy.emplace_back(std::move(org)); + + if (package.Description) { + artifact.Description = *package.Description; + } + + if (package.Version) { + artifact.PackageVersion = *package.Version; + } + + if (package.PackageUrl) { + artifact.PackageUrl = *package.PackageUrl; + } + + if (package.License) { + artifact.CopyrightText = *package.License; + } + + artifact.BuiltTime = cmSystemTools::GetCurrentDateTime("%FT%TZ"); + cmSpdxExternalRef externalRef; + externalRef.Locator = cmStrCat("cmake:find_package(", name, ")"); + externalRef.ExternalRefType = "buildSystem"; + return true; +} + +cmSpdxCreationInfo cmSbomBuilder::GenerateCreationInfo() const +{ + cmSpdxCreationInfo ci; + ci.SpdxId = "_:Build#CreationInfo"; + ci.Created = cmSystemTools::GetCurrentDateTime("%FT%TZ"); + ci.CreatedBy = { "https://gitlab.kitware.com/cmake/cmake" }; + ci.Comment = "This SBOM was generated from the CMakeLists.txt File"; + ci.SpecVersion = "3.0.1"; + return ci; +} + +cmSpdxDocument cmSbomBuilder::GenerateSbom(cmSpdxCreationInfo const* ci) const +{ + cmSpdxDocument proj; + proj.Name = PackageName; + proj.SpdxId = cmStrCat("urn:", PackageName, "#SPDXDocument"); + proj.ProfileConformance = { "core", "software" }; + proj.CreationInfo = ci; + + if (!this->PackageDescription.empty()) { + proj.Description = this->PackageDescription; + } + + if (!this->PackageLicense.empty()) { + proj.DataLicense = this->PackageLicense; + } + + return proj; +} + +cmSpdxPackage cmSbomBuilder::GenerateImportTarget( + cmSpdxCreationInfo const* ci, cmGeneratorTarget const* target) const +{ + cmSpdxPackage package; + package.SpdxId = cmStrCat("urn:", target->GetName(), "#Package"); + package.Name = target->GetName(); + package.PrimaryPurpose = GetPurpose(target->GetType()); + package.CreationInfo = ci; + + if (!this->PackageVersion.empty()) { + package.PackageVersion = this->PackageVersion; + } + + if (!this->PackageWebsite.empty()) { + package.Homepage = this->PackageWebsite; + } + + if (!this->PackageUrl.empty()) { + package.DownloadLocation = this->PackageUrl; + } + + return package; +} + +void cmSbomBuilder::GenerateLinkProperties( + cmSbomDocument& doc, cmSpdxDocument* project, cmSpdxCreationInfo const* ci, + std::string const& libraries, TargetProperties const& current, + std::vector<TargetProperties> const& allTargets) const +{ + auto itProp = current.Properties.find(libraries); + if (itProp == current.Properties.end()) { + return; + } + + std::map<std::string, std::vector<std::string>> allowList = { { "LINK_ONLY", + {} } }; + std::string interfaceLinkLibraries; + if (!cmGeneratorExpression::ForbidGeneratorExpressions( + current.Target, itProp->first, itProp->second, interfaceLinkLibraries, + allowList)) { + return; + } + + auto makeRel = [&](char const* id, char const* desc) { + cmSpdxRelationship r; + r.SpdxId = cmStrCat("urn:", id, "#Relationship"); + r.RelationshipType = cmSpdxRelationship::RelationshipTypeId::DEPENDS_ON; + r.Description = desc; + r.From = current.Package; + r.CreationInfo = ci; + return r; + }; + + auto linkLibraries = makeRel("Static", "Linked Libraries"); + auto linkRequires = makeRel("Dynamic", "Required Runtime Libraries"); + auto buildRequires = makeRel("Shared", "Required Build-Time Libraries"); + + auto addArtifact = + [&](std::string const& name) -> std::pair<bool, cmSpdxPackage const*> { + auto it = this->LinkTargets.find(name); + if (it != this->LinkTargets.end()) { + LinkInfo const& linkInfo = it->second; + if (linkInfo.Package.empty()) { + for (auto const& t : allTargets) { + if (t.Target->GetName() == linkInfo.Component) { + return { true, t.Package }; + } + } + } + std::string pkgName = + cmStrCat(linkInfo.Package, ":", linkInfo.Component); + cmSpdxPackage pkg; + pkg.Name = pkgName; + pkg.SpdxId = cmStrCat("urn:", pkgName, "#Package"); + pkg.CreationInfo = ci; + if (!linkInfo.Package.empty()) { + auto const& pkgIt = this->Requirements.find(linkInfo.Package); + if (pkgIt != this->Requirements.end() && + pkgIt->second.Components.count(linkInfo.Component) > 0) { + this->AddPackageInformation(pkg, pkgIt->first, pkgIt->second); + } + } + return { true, insert_back(project->Elements, std::move(pkg)) }; + } + + cmSpdxPackage pkg; + pkg.SpdxId = cmStrCat("urn:", name, "#Package"); + pkg.Name = name; + pkg.CreationInfo = ci; + return { false, insert_back(project->Elements, std::move(pkg)) }; + }; + + auto handleDependencies = [&](std::vector<std::string> const& names, + cmSpdxRelationship& internalDeps, + cmSpdxRelationship& externalDeps) { + for (auto const& n : names) { + auto res = addArtifact(n); + if (!res.second) { + continue; + } + + if (res.first) { + internalDeps.To.push_back(res.second); + } else { + externalDeps.To.push_back(res.second); + } + } + }; + + handleDependencies(allowList["LINK_ONLY"], linkLibraries, linkRequires); + handleDependencies(cmList{ interfaceLinkLibraries }, linkLibraries, + buildRequires); + + if (!linkLibraries.To.empty()) { + insert_back(doc.Graph, std::move(linkLibraries)); + } + if (!linkRequires.To.empty()) { + insert_back(doc.Graph, std::move(linkRequires)); + } + if (!buildRequires.To.empty()) { + insert_back(doc.Graph, std::move(buildRequires)); + } +} + +bool cmSbomBuilder::GenerateProperties( + cmSbomDocument& doc, cmSpdxDocument* proj, cmSpdxCreationInfo const* ci, + TargetProperties const& current, + std::vector<TargetProperties> const& allTargets) const +{ + this->GenerateLinkProperties(doc, proj, ci, "LINK_LIBRARIES", current, + allTargets); + this->GenerateLinkProperties(doc, proj, ci, "INTERFACE_LINK_LIBRARIES", + current, allTargets); + return true; +} + +bool cmSbomBuilder::PopulateLinkLibrariesProperty( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + static std::array<std::string, 3> const linkIfaceProps = { + { "LINK_LIBRARIES", "LINK_LIBRARIES_DIRECT", + "LINK_LIBRARIES_DIRECT_EXCLUDE" } + }; + bool hadLINK_LIBRARIES = false; + for (std::string const& linkIfaceProp : linkIfaceProps) { + if (cmValue input = target->GetProperty(linkIfaceProp)) { + std::string prepro = + cmGeneratorExpression::Preprocess(*input, preprocessRule); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, target); + properties[linkIfaceProp] = prepro; + hadLINK_LIBRARIES = true; + } + } + } + return hadLINK_LIBRARIES; +} + +bool cmSbomBuilder::AddTargetNamespace(std::string& input, + cmGeneratorTarget const* target, + cmLocalGenerator const* lg) +{ + cmGeneratorTarget::TargetOrString resolved = + target->ResolveTargetReference(input, lg); + + cmGeneratorTarget* tgt = resolved.Target; + if (!tgt) { + input = resolved.String; + return false; + } + + if (tgt->IsImported()) { + input = tgt->GetName(); + return this->NoteLinkedTarget(target, input, tgt); + } + + if (this->SbomTargets.find(tgt) != this->SbomTargets.end()) { + input = this->Namespace + tgt->GetExportName(); + } else { + input = tgt->GetName(); + } + + return this->NoteLinkedTarget(target, input, tgt); +} + +bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target, + std::string const& linkedName, + cmGeneratorTarget const* linkedTarget) +{ + if (cm::contains(this->SbomTargets, linkedTarget)) { + this->LinkTargets.emplace(linkedName, + LinkInfo{ "", linkedTarget->GetExportName() }); + return true; + } + + if (linkedTarget->IsImported()) { + using Package = cm::optional<std::pair<std::string, cmPackageInformation>>; + auto pkgInfo = [](cmTarget* t) -> Package { + cmFindPackageStack pkgStack = t->GetFindPackageStack(); + if (!pkgStack.Empty()) { + return std::make_pair(pkgStack.Top().Name, + *pkgStack.Top().PackageInfo); + } + std::string const pkgName = + t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME"); + if (pkgName.empty()) { + return cm::nullopt; + } + cmPackageInformation package; + return std::make_pair(pkgName, package); + }(linkedTarget->Target); + + if (!pkgInfo) { + target->Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("Target \"", target->GetName(), + "\" references imported target \"", linkedName, + "\" which does not come from any known package.")); + return false; + } + + std::string const& pkgName = pkgInfo->first; + auto const& prefix = cmStrCat(pkgName, "::"); + std::string component; + if (!cmHasPrefix(linkedName, prefix)) { + component = linkedName; + } else { + component = linkedName.substr(prefix.length()); + } + this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component }); + cmPackageInformation& req = + this->Requirements.insert(std::move(*pkgInfo)).first->second; + req.Components.emplace(std::move(component)); + return true; + } + + // Target belongs to another export from this build or install. + // The leaf class chooses which export map to consult. + auto const& exportInfo = this->FindExportInfoFor(linkedTarget); + if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) { + auto const& linkNamespace = *exportInfo.Namespaces.begin(); + if (!cmHasSuffix(linkNamespace, "::")) { + target->Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("Target \"", target->GetName(), "\" references target \"", + linkedName, + "\", whose export does not use the standard namespace " + "separator. The dependency will be recorded by its bare " + "target name without provenance.")); + return false; + } + + std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 }; + std::string component = linkedTarget->GetExportName(); + if (pkgName == this->GetPackageName()) { + this->LinkTargets.emplace(linkedName, LinkInfo{ "", component }); + } else { + this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component }); + this->Requirements[pkgName].Components.emplace(std::move(component)); + } + return true; + } + + if (exportInfo.Sets.empty()) { + // install(export) provenance is unavailable. Fall back to SBOM-level + // attribution: any peer SBOM (of the same build/install mode) that + // covers this target lends its package name. install(export) wins + // when both are available; only reached here when install(export) is + // absent. + auto const& sbomInfo = this->FindSbomInfoFor(linkedTarget); + if (sbomInfo.Packages.empty()) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", target->GetName(), "\" references target \"", + linkedName, + "\" which has no install(EXPORT)/export(EXPORT) namespace " + "and is not covered by any SBOM. An SBOM cannot attribute " + "this dependency. Give \"", + linkedTarget->GetName(), + "\" an install(EXPORT)/export(EXPORT) with a NAMESPACE, or " + "include it in an install(SBOM)/export(SBOM).")); + return false; + } + std::string const& pkgName = sbomInfo.Packages.front(); + if (sbomInfo.Packages.size() > 1) { + target->Makefile->IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat( + "Target \"", target->GetName(), "\" references target \"", + linkedName, + "\" which has no install(EXPORT)/export(EXPORT) namespace and is " + "covered by multiple SBOMs: ", + cmJoin(sbomInfo.Packages, ", "), ". Attributing to \"", pkgName, + "\" (first alphabetically).")); + } + std::string component = linkedTarget->GetExportName(); + this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component }); + this->Requirements[pkgName].Components.emplace(std::move(component)); + return true; + } + + std::ostringstream e; + e << "Target \"" << target->GetName() << "\" references target \"" + << linkedName << "\" "; + if (exportInfo.Sets.size() == 1) { + e << "that is in an export set which is exported multiple times " + "with different namespaces: "; + } else { + e << "that is in multiple export sets: "; + } + e << cmJoin(exportInfo.Files, ", ") << ".\n" + << "An SBOM cannot attribute a dependency exported in more than one " + "export set or with more than one namespace. Consider " + "consolidating the exports of the \"" + << linkedTarget->GetName() << "\" target to a single export."; + target->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return false; +} + +void cmSbomBuilder::ResolveTargetsInGeneratorExpression( + std::string& input, cmGeneratorTarget const* target, + cmLocalGenerator const* lg) +{ + auto err = cmResolveTargetsInGeneratorExpression( + input, [this, target, lg](std::string& name) { + return this->AddTargetNamespace(name, target, lg); + }); + if (err) { + target->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, *err); + } +} + +void cmSbomBuilder::ResolveTargetsInGeneratorExpressions( + std::string& input, cmGeneratorTarget const* target) +{ + cmLocalGenerator const* lg = target->GetLocalGenerator(); + std::vector<std::string> parts; + cmGeneratorExpression::Split(input, parts); + + std::string sep; + input.clear(); + for (std::string& li : parts) { + if (target->IsLinkLookupScope(li, lg)) { + continue; + } + if (cmGeneratorExpression::Find(li) == std::string::npos) { + this->AddTargetNamespace(li, target, lg); + } else { + this->ResolveTargetsInGeneratorExpression(li, target, lg); + } + input += sep + li; + sep = ";"; + } +} + +bool cmSbomBuilder::PopulateInterfaceLinkLibrariesProperty( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + if (!target->IsLinkable()) { + return false; + } + static std::array<std::string, 3> const linkIfaceProps = { + { "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT", + "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" } + }; + bool hadINTERFACE_LINK_LIBRARIES = false; + for (std::string const& linkIfaceProp : linkIfaceProps) { + if (cmValue input = target->GetProperty(linkIfaceProp)) { + std::string prepro = + cmGeneratorExpression::Preprocess(*input, preprocessRule); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, target); + properties[linkIfaceProp] = prepro; + hadINTERFACE_LINK_LIBRARIES = true; + } + } + } + return hadINTERFACE_LINK_LIBRARIES; +}
diff --git a/Source/cmSbomBuilder.h b/Source/cmSbomBuilder.h new file mode 100644 index 0000000..3e2e588 --- /dev/null +++ b/Source/cmSbomBuilder.h
@@ -0,0 +1,179 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "cmExportFileGenerator.h" +#include "cmFindPackageStack.h" +#include "cmGeneratorExpression.h" +#include "cmSbomArguments.h" + +class cmExportSet; +class cmGeneratorTarget; +class cmLocalGenerator; +struct cmSbomDocument; +struct cmSpdxDocument; +struct cmSpdxPackage; +struct cmSpdxCreationInfo; + +/** \class cmSbomBuilder + * \brief Abstract base for SBOM document generators. + * + * Concrete leaves (cmBuildSbomBuilder, cmInstallSbomBuilder) supply two + * pure virtuals: + * + * - Generate(): selects the generator-expression preprocess context + * (BuildInterface vs InstallInterface) and delegates to the shared + * GenerateForTargets() body. + * - FindExportInfoFor(): consults the mode's export map (build-tree vs + * install-tree) to resolve cross-export references. + * + * All shared SPDX assembly, link-graph walking, and serialization lives + * here so that the leaves stay trivial. + */ +class cmSbomBuilder +{ +public: + virtual ~cmSbomBuilder() = default; + + /** Compute phase: wire the local generator, run Compute() on the owned + * export sets, and populate the SbomTargets cache so peer SBOMs can + * query CoversTarget() before any Generate() runs. */ + void Compute(cmLocalGenerator* lg); + + /** Produce the SBOM document on `os`. Implementations pick the + * generator-expression preprocess context (BuildInterface vs + * InstallInterface) and delegate to GenerateForTargets. */ + virtual bool Generate(std::ostream& os) = 0; + + /** Identifier this SBOM publishes itself as (the SPDX document name and + * the namespace under which other SBOMs refer to its contents). */ + std::string const& GetPackageName() const { return this->PackageName; } + + /** True if `target` is one of the targets this SBOM directly describes. */ + bool CoversTarget(cmGeneratorTarget const* target) const; + + /** True if `set` is one of the export sets this SBOM was built from. */ + bool CoversExportSet(cmExportSet const* set) const; + + /** Names of peer SBOMs (same build/install mode) that cover a target. + * Used by NoteLinkedTarget to attribute a cross-reference when no + * install(export) namespace is available. Sorted alphabetically. */ + struct SbomInfo + { + std::vector<std::string> Packages; + }; + +protected: + cmSbomBuilder(cmSbomArguments args, std::vector<cmExportSet*> exportSets, + cmLocalGenerator* lg); + + /** Mode-specific: where does `target` appear in the project's exports? + * Build leaves consult cmGlobalGenerator::FindBuildExportInfo; + * install leaves consult cmGlobalGenerator::FindInstallExportInfo. */ + virtual cmExportFileGenerator::ExportInfo FindExportInfoFor( + cmGeneratorTarget const* target) const = 0; + + /** Mode-specific: which peer SBOMs cover `target`? + * Build leaves consult cmGlobalGenerator::FindBuildSbomInfo; + * install leaves consult cmGlobalGenerator::FindInstallSbomInfo. */ + virtual SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const = 0; + + /** The set of targets the SBOM directly describes - derived from the + * associated export sets. */ + std::set<cmGeneratorTarget const*> CollectTargets() const; + + /** Generate an sbom for the targets in this->SbomTargets. Each leaf class + * calls this internally */ + bool GenerateForTargets( + std::ostream& os, + cmGeneratorExpression::PreprocessContext preprocessContext); + + using ImportPropertyMap = std::map<std::string, std::string>; + + struct TargetProperties + { + cmSpdxPackage const* Package; + cmGeneratorTarget const* Target; + ImportPropertyMap Properties; + }; + + void WriteSbom(cmSbomDocument& doc, std::ostream& os) const; + + cmSpdxCreationInfo GenerateCreationInfo() const; + cmSpdxDocument GenerateSbom(cmSpdxCreationInfo const* ci) const; + cmSpdxPackage GenerateImportTarget(cmSpdxCreationInfo const* ci, + cmGeneratorTarget const* target) const; + + bool AddPackageInformation(cmSpdxPackage& artifact, std::string const& name, + cmPackageInformation const& package) const; + + bool GenerateProperties( + cmSbomDocument& doc, cmSpdxDocument* project, cmSpdxCreationInfo const* ci, + TargetProperties const& current, + std::vector<TargetProperties> const& allTargets) const; + + void GenerateLinkProperties( + cmSbomDocument& doc, cmSpdxDocument* project, cmSpdxCreationInfo const* ci, + std::string const& libraries, TargetProperties const& current, + std::vector<TargetProperties> const& allTargets) const; + + bool NoteLinkedTarget(cmGeneratorTarget const* target, + std::string const& linkedName, + cmGeneratorTarget const* linkedTarget); + + bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target, + cmLocalGenerator const* lg); + + void ResolveTargetsInGeneratorExpression(std::string& input, + cmGeneratorTarget const* target, + cmLocalGenerator const* lg); + void ResolveTargetsInGeneratorExpressions(std::string& input, + cmGeneratorTarget const* target); + + bool PopulateInterfaceLinkLibrariesProperty( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + + bool PopulateLinkLibrariesProperty(cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext, + ImportPropertyMap& properties); + + // Set at construction or via setters + cmLocalGenerator* LocalGenerator = nullptr; + + // Inputs + std::vector<cmExportSet*> ExportSets; + +private: + struct LinkInfo + { + std::string Package; + std::string Component; + }; + + // Metadata + std::string const PackageName; + std::string const Namespace; + std::string const PackageVersion; + std::string const PackageDescription; + std::string const PackageWebsite; + std::string const PackageUrl; + std::string const PackageLicense; + cmSbomArguments::SbomFormat const PackageFormat; + + // Derived from inputs at generate time + std::set<cmGeneratorTarget const*> SbomTargets; + + // Accumulated during generation + std::map<std::string, LinkInfo> LinkTargets; + std::map<std::string, cmPackageInformation> Requirements; +};
diff --git a/Source/cmScriptGenerator.cxx b/Source/cmScriptGenerator.cxx index b3dff8b..5efc243 100644 --- a/Source/cmScriptGenerator.cxx +++ b/Source/cmScriptGenerator.cxx
@@ -3,11 +3,55 @@ #include "cmScriptGenerator.h" #include <algorithm> +#include <cstddef> +#include <sstream> #include <utility> #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +namespace { + +int RequiredBracketLength(cm::string_view s) +{ + for (int n = 0;; ++n) { + std::string const needle = "]" + std::string(n, '='); + + bool ok = true; + std::size_t pos = s.find(needle); + while (pos != cm::string_view::npos) { + std::size_t const next = pos + needle.size(); + if (next == s.size() || s[next] == ']') { + ok = false; + break; + } + pos = s.find(needle, pos + 1); + } + + if (ok) { + return n; + } + } +} + +} // namespace + +std::ostream& operator<<(std::ostream& os, cmScriptGeneratorQuoted const& self) +{ + if (self.BracketLength == -1) { + return os << '"' << self.Value << '"'; + } + auto const sep = std::string(self.BracketLength, '='); + return os << '[' << sep << '[' << self.Value << ']' << sep << ']'; +} + +std::string cmScriptGeneratorQuoted::str() const +{ + std::stringstream out; + out << *this; + return out.str(); +} + cmScriptGenerator::cmScriptGenerator(std::string config_var, std::vector<std::string> configurations) : RuntimeConfigVariable(std::move(config_var)) @@ -28,6 +72,14 @@ this->ConfigurationTypes = nullptr; } +cmScriptGeneratorQuoted cmScriptGenerator::Quote(cm::string_view value) +{ + if (value.find_first_of("\"$\\") == std::string::npos) { + return cmScriptGeneratorQuoted(value, -1); + } + return cmScriptGeneratorQuoted(value, RequiredBracketLength(value)); +} + static void cmScriptGeneratorEncodeConfig(std::string const& config, std::string& result) {
diff --git a/Source/cmScriptGenerator.h b/Source/cmScriptGenerator.h index 616817b..2a6ff4d 100644 --- a/Source/cmScriptGenerator.h +++ b/Source/cmScriptGenerator.h
@@ -8,6 +8,8 @@ #include <string> #include <vector> +#include <cm/string_view> + class cmScriptGeneratorIndent { public: @@ -37,6 +39,26 @@ return os; } +class cmScriptGeneratorQuoted +{ +public: + cmScriptGeneratorQuoted(cm::string_view value, int bracket_length) + : Value(value) + , BracketLength(bracket_length) + { + } + + std::string str() const; + operator std::string() const { return str(); } + +private: + friend std::ostream& operator<<(std::ostream& os, + cmScriptGeneratorQuoted const& self); + + cm::string_view Value; + int BracketLength; +}; + /** \class cmScriptGenerator * \brief Support class for generating install and test scripts. * @@ -54,6 +76,8 @@ void Generate(std::ostream& os, std::string const& config, std::vector<std::string> const& configurationTypes); + static cmScriptGeneratorQuoted Quote(cm::string_view value); + protected: using Indent = cmScriptGeneratorIndent; virtual void GenerateScript(std::ostream& os);
diff --git a/Source/cmSetCommand.cxx b/Source/cmSetCommand.cxx index be9825c..b5907d3 100644 --- a/Source/cmSetCommand.cxx +++ b/Source/cmSetCommand.cxx
@@ -10,10 +10,10 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmList.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmRange.h" #include "cmState.h" #include "cmStateTypes.h" @@ -64,10 +64,11 @@ // if there's extra arguments, warn user // that they are ignored by this command. if (args.size() > 2) { - std::string m = "Only the first value argument is used when setting " - "an environment variable. Argument '" + - args[2] + "' and later are unused."; - status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, m); + status.GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("Only the first value argument is used when setting " + "an environment variable. Argument '", + args[2] + "' and later are unused.")); } return true; } @@ -229,9 +230,10 @@ if (cache) { std::string::size_type cacheStart = args.size() - 3 - (force ? 1 : 0); if (!cmState::StringToCacheEntryType(args[cacheStart + 1], type)) { - std::string m = "implicitly converting '" + args[cacheStart + 1] + - "' to 'STRING' type."; - status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, m); + status.GetMakefile().IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, + cmStrCat("implicitly converting '", + args[cacheStart + 1], + "' to 'STRING' type.")); // Setting this may not be required, since it's // initialized as a string. Keeping this here to // ensure that the type is actually converting to a string.
diff --git a/Source/cmSetDirectoryPropertiesCommand.cxx b/Source/cmSetDirectoryPropertiesCommand.cxx index cdfd068..024c76b 100644 --- a/Source/cmSetDirectoryPropertiesCommand.cxx +++ b/Source/cmSetDirectoryPropertiesCommand.cxx
@@ -4,6 +4,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmPolicies.h" // cmSetDirectoryPropertiesCommand bool cmSetDirectoryPropertiesCommand(std::vector<std::string> const& args, @@ -27,7 +28,9 @@ "Variables and cache variables should be set using SET command"); return false; } - if (prop == "MACROS") { + if (prop == "MACROS" && + status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) != + cmPolicies::NEW) { status.SetError( "Commands and macros cannot be set using SET_CMAKE_PROPERTIES"); return false;
diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index 676a796..292a308 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx
@@ -8,14 +8,15 @@ #include <cm/optional> #include <cm/string_view> +#include <cmext/string_view> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmFileSet.h" #include "cmGlobalGenerator.h" #include "cmInstalledFile.h" #include "cmListFileCache.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmProperty.h" #include "cmRange.h" @@ -345,8 +346,8 @@ auto isProblematic = [&mf, &propertyValue, op](cm::string_view policy) -> bool { if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) { - mf.IssueMessage( - MessageType::AUTHOR_ERROR, + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Policy ", policy, " is set to NEW and the following non-boolean value given " "for property 'GENERATED' is therefore not allowed:\n", @@ -354,15 +355,15 @@ return true; } if (cmIsOff(propertyValue)) { - mf.IssueMessage( - MessageType::AUTHOR_ERROR, + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat("Unsetting the 'GENERATED' property is not allowed under ", policy, "!\n")); return true; } if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) { - mf.IssueMessage( - MessageType::AUTHOR_ERROR, + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat( "Policy ", policy, " is set to NEW and appending to the 'GENERATED' property is " @@ -396,29 +397,24 @@ if (cmp0118PolicyWARN) { if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0118), - "\nAttempt to set property 'GENERATED' with the following " - "non-boolean value (which will be interpreted as \"0\"):\n", + mf.IssuePolicyWarning( + cmPolicies::CMP0118, {}, + cmStrCat("Attempt to set property 'GENERATED' with the following " + "non-boolean value (which will be interpreted as \"0\"):\n"_s, propertyValue, "\nThat exact value will not be retrievable. A value of " "\"0\" will be returned instead.\n" - "This will be an error under policy CMP0118.\n")); + "This will be an error under policy CMP0118."_s)); } if (cmIsOff(propertyValue)) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0118), - "\nUnsetting property 'GENERATED' will not be allowed under " - "policy CMP0118!\n")); + mf.IssuePolicyWarning(cmPolicies::CMP0118, {}, + "Unsetting property 'GENERATED' " + "will not be allowed under policy CMP0118!"_s); } if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0118), - "\nAppending to property 'GENERATED' will not be allowed " - "under policy CMP0118!\n")); + mf.IssuePolicyWarning(cmPolicies::CMP0118, {}, + "Appending to property 'GENERATED' " + "will not be allowed under policy CMP0118!"_s); } }
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index 5f2cf3b..6ee095a 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx
@@ -200,14 +200,12 @@ if (this->IsGenerated || cmSystemTools::FileExists(extPath)) { this->FullPath = extPath; if (cmp0115 == cmPolicies::WARN) { - std::string warning = - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0115), - "\nFile:\n ", extPath); + std::string warning = cmStrCat("File:\n "_s, extPath); if (cmp0115Warning) { *cmp0115Warning = std::move(warning); } else { - makefile->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, warning); + makefile->IssuePolicyWarning(cmPolicies::CMP0115, {}, warning, + cmListFileBacktrace{}); } } return true; @@ -503,3 +501,13 @@ { this->CustomCommand = std::move(cc); } + +cmValue cmSourceFile::GetRustEmitProperty() const +{ + static std::string const s_default = "link"; + cmValue const value = this->GetProperty("Rust_EMIT"); + if (!value || value->empty()) { + return cmValue(s_default); + } + return value; +}
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h index d951793..0d9bc2e 100644 --- a/Source/cmSourceFile.h +++ b/Source/cmSourceFile.h
@@ -184,6 +184,8 @@ void SetObjectLibrary(std::string const& objlib); std::string GetObjectLibrary() const; + cmValue GetRustEmitProperty() const; + private: cmSourceFileLocation Location; cmPropertyMap Properties;
diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index d6690af..d4b1b3d 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx
@@ -127,11 +127,10 @@ state = "disabled"; } if (state) { - makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0128), - "\nFor compatibility with older versions of CMake, " - "compiler extensions won't be ", + makefile->IssuePolicyWarning( + cmPolicies::CMP0128, {}, + cmStrCat("For compatibility with older versions of CMake, " + "compiler extensions won't be "_s, state, '.')); } } @@ -177,12 +176,10 @@ if (cmp0128 == cmPolicies::WARN && makefile->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0128")) { - makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0128), - "\nFor compatibility with older versions of CMake, " - "unnecessary flags for language standard or compiler " - "extensions may be added.")); + makefile->IssuePolicyWarning( + cmPolicies::CMP0128, {}, + "For compatibility with older versions of CMake, unnecessary flags " + "for language standard or compiler extensions may be added."_s); } }
diff --git a/Source/cmState.cxx b/Source/cmState.cxx index 3255650..10d792e 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx
@@ -14,6 +14,7 @@ #include "cmCacheManager.h" #include "cmDefinitions.h" +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmGlobCacheEntry.h" // IWYU pragma: keep #include "cmGlobVerificationManager.h" @@ -274,8 +275,12 @@ this->CacheManager->RemoveCacheEntryProperty(key, propertyName); } -cmStateSnapshot cmState::Reset() +cmStateSnapshot cmState::Reset(cmStateSnapshot const& diagnosticState) { + assert(diagnosticState.CanPopDiagnosticScope()); + cmDiagnostics::DiagnosticMap diagnostics = + *diagnosticState.Position->Diagnostics; + this->GlobalProperties.Clear(); this->PropertyDefinitions = {}; this->GlobVerificationManager->Reset(); @@ -301,6 +306,15 @@ assert(pos->Policies.IsValid()); assert(pos->PolicyRoot.IsValid()); + this->DiagnosticStack.Clear(); + pos->Diagnostics = this->DiagnosticStack.Push(this->DiagnosticStack.Root(), + { diagnostics, false }); + pos->DiagnosticRoot = this->DiagnosticStack.Root(); + pos->DiagnosticScope = this->DiagnosticStack.Root(); + assert(pos->Diagnostics.IsValid()); + assert(pos->DiagnosticRoot.IsValid()); + assert(pos->Diagnostics != pos->DiagnosticRoot); + { std::string srcDir = *cmDefinitions::Get("CMAKE_SOURCE_DIR", pos->Vars, pos->Root); @@ -376,11 +390,6 @@ return this->EnabledLanguages; } -void cmState::SetEnabledLanguages(std::vector<std::string> const& langs) -{ - this->EnabledLanguages = langs; -} - void cmState::ClearEnabledLanguages() { this->EnabledLanguages.clear(); @@ -400,7 +409,10 @@ { assert(name == cmSystemTools::LowerCase(name)); assert(this->BuiltinCommands.find(name) == this->BuiltinCommands.end()); - this->BuiltinCommands.emplace(name, std::move(command)); + this->BuiltinCommands.emplace( + name, + CommandDescriptor{ cmStateEnums::CommandType::Function, + std::move(command) }); } static bool InvokeBuiltinCommand(cmState::BuiltinCommand command, @@ -454,13 +466,8 @@ cmExecutionStatus& status) -> bool { cmMakefile& mf = status.GetMakefile(); switch (mf.GetPolicyStatus(policy)) { - case cmPolicies::WARN: { - std::string warning = cmPolicies::GetPolicyWarning(policy); - if (additionalWarning) { - warning = cmStrCat(warning, '\n', additionalWarning); - } - mf.IssueMessage(MessageType::AUTHOR_WARNING, warning); - } + case cmPolicies::WARN: + mf.IssuePolicyWarning(policy, {}, additionalWarning); CM_FALLTHROUGH; case cmPolicies::OLD: break; @@ -508,8 +515,31 @@ this->AddUnexpectedCommand(name, error); } -bool cmState::AddScriptedCommand(std::string const& name, BT<Command> command, - cmMakefile& mf) +cmState::CommandDescriptor::CommandDescriptor(CommandType type, + Command command) + : Type(type) + , Script(std::move(command)) +{ +} +cmState::CommandDescriptor::CommandDescriptor( + CommandDescriptor&& descriptor) noexcept + : Type(descriptor.Type) + , Script(std::move(descriptor.Script)) +{ +} + +cmState::CommandDescriptor& cmState::CommandDescriptor::operator=( + CommandDescriptor&& descriptor) noexcept +{ + this->Type = descriptor.Type; + this->Script = std::move(descriptor.Script); + + return *this; +} + +bool cmState::AddScriptedCommand(std::string const& name, + cmStateEnums::CommandType type, + BT<Command> command, cmMakefile& mf) { std::string sName = cmSystemTools::LowerCase(name); @@ -524,30 +554,60 @@ } // if the command already exists, give a new name to the old command. - if (Command oldCmd = this->GetCommandByExactName(sName)) { - this->ScriptedCommands["_" + sName] = oldCmd; + if (CommandDescriptor const* oldCmd = + this->GetCommandDescriptorByExactName(sName)) { + this->ScriptedCommands["_" + sName] = *oldCmd; } - this->ScriptedCommands[sName] = std::move(command.Value); + this->ScriptedCommands[sName] = + CommandDescriptor{ type, std::move(command.Value) }; return true; } +cmState::CommandDescriptor const* cmState::GetCommandDescriptorByExactName( + std::string const& name) const +{ + auto pos = this->ScriptedCommands.find(name); + if (pos != this->ScriptedCommands.end()) { + return &pos->second; + } + pos = this->BuiltinCommands.find(name); + if (pos != this->BuiltinCommands.end()) { + return &pos->second; + } + return nullptr; +} + cmState::Command cmState::GetCommand(std::string const& name) const { return this->GetCommandByExactName(cmSystemTools::LowerCase(name)); } +cm::optional<cmStateEnums::CommandType> cmState::GetCommandType( + std::string const& name) const +{ + return this->GetCommandTypeByExactName(cmSystemTools::LowerCase(name)); +} cmState::Command cmState::GetCommandByExactName(std::string const& name) const { - auto pos = this->ScriptedCommands.find(name); - if (pos != this->ScriptedCommands.end()) { - return pos->second; + CommandDescriptor const* descriptor = + this->GetCommandDescriptorByExactName(name); + if (!descriptor) { + return nullptr; } - pos = this->BuiltinCommands.find(name); - if (pos != this->BuiltinCommands.end()) { - return pos->second; + + return descriptor->Script; +} +cm::optional<cmStateEnums::CommandType> cmState::GetCommandTypeByExactName( + std::string const& name) const +{ + CommandDescriptor const* descriptor = + this->GetCommandDescriptorByExactName(name); + if (!descriptor) { + return cm::nullopt; } - return nullptr; + + return descriptor->Type; } std::vector<std::string> cmState::GetCommandNames() const @@ -613,6 +673,9 @@ this->SetGlobalProperty("ENABLED_LANGUAGES", langs); } else if (prop == "CMAKE_ROLE") { this->SetGlobalProperty("CMAKE_ROLE", this->GetRoleString()); + } else if (prop == "_CMAKE_RUNNING_IN_BUILD_TREE") { + this->SetGlobalProperty("_CMAKE_RUNNING_IN_BUILD_TREE", + cmSystemTools::GetCMakeInBuildTree() ? "1" : "0"); } #define STRING_LIST_ELEMENT(F) ";" #F if (prop == "CMAKE_C_KNOWN_FEATURES") { @@ -886,6 +949,13 @@ pos->PolicyScope = this->PolicyStack.Root(); assert(pos->Policies.IsValid()); assert(pos->PolicyRoot.IsValid()); + pos->Diagnostics = + this->DiagnosticStack.Push(this->DiagnosticStack.Root(), { {}, false }); + pos->DiagnosticRoot = this->DiagnosticStack.Root(); + pos->DiagnosticScope = this->DiagnosticStack.Root(); + assert(pos->Diagnostics.IsValid()); + assert(pos->DiagnosticRoot.IsValid()); + assert(pos->Diagnostics != pos->DiagnosticRoot); pos->Vars = this->VarTree.Push(this->VarTree.Root()); assert(pos->Vars.IsValid()); pos->Parent = this->VarTree.Root(); @@ -913,6 +983,11 @@ pos->PolicyScope = originSnapshot.Position->Policies; assert(pos->Policies.IsValid()); assert(pos->PolicyRoot.IsValid()); + pos->Diagnostics = originSnapshot.Position->Diagnostics; + pos->DiagnosticRoot = originSnapshot.Position->Diagnostics; + pos->DiagnosticScope = originSnapshot.Position->Diagnostics; + assert(pos->Diagnostics.IsValid()); + assert(pos->DiagnosticRoot.IsValid()); cmLinkedTree<cmDefinitions>::iterator origin = originSnapshot.Position->Vars; pos->Parent = origin; @@ -939,6 +1014,7 @@ assert(originSnapshot.Position->Vars.IsValid()); pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; + pos->DiagnosticScope = originSnapshot.Position->Diagnostics; return { this, pos }; } @@ -954,6 +1030,7 @@ originSnapshot.Position->ExecutionListFile, fileName); pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; + pos->DiagnosticScope = originSnapshot.Position->Diagnostics; assert(originSnapshot.Position->Vars.IsValid()); cmLinkedTree<cmDefinitions>::iterator origin = originSnapshot.Position->Vars; pos->Parent = origin; @@ -973,6 +1050,7 @@ assert(originSnapshot.Position->Vars.IsValid()); pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; + pos->DiagnosticScope = originSnapshot.Position->Diagnostics; return { this, pos }; } @@ -988,6 +1066,7 @@ assert(originSnapshot.Position->Vars.IsValid()); pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; + pos->DiagnosticScope = originSnapshot.Position->Diagnostics; return { this, pos }; } @@ -1001,6 +1080,7 @@ pos->Keep = false; pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; + pos->DiagnosticScope = originSnapshot.Position->Diagnostics; assert(originSnapshot.Position->Vars.IsValid()); cmLinkedTree<cmDefinitions>::iterator origin = originSnapshot.Position->Vars; @@ -1021,6 +1101,7 @@ originSnapshot.Position->ExecutionListFile, fileName); pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; + pos->DiagnosticScope = originSnapshot.Position->Diagnostics; return { this, pos }; } @@ -1033,6 +1114,7 @@ pos->Keep = false; pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; + pos->DiagnosticScope = originSnapshot.Position->Diagnostics; return { this, pos }; }
diff --git a/Source/cmState.h b/Source/cmState.h index 885ade5..9ed91f5 100644 --- a/Source/cmState.h +++ b/Source/cmState.h
@@ -138,7 +138,6 @@ std::string& value, cmStateEnums::CacheEntryType& type); - cmStateSnapshot Reset(); // Define a property void DefineProperty(std::string const& name, cmProperty::ScopeType scope, std::string const& ShortDescription, @@ -160,7 +159,6 @@ void SetLanguageEnabled(std::string const& l); bool GetLanguageEnabled(std::string const& l) const; std::vector<std::string> GetEnabledLanguages() const; - void SetEnabledLanguages(std::vector<std::string> const& langs); void ClearEnabledLanguages(); bool GetIsGeneratorMultiConfig() const; @@ -173,8 +171,14 @@ // Returns a command from its name, case insensitive, or nullptr Command GetCommand(std::string const& name) const; + // Returns a command type from its name, case insensitive, or cm::nullopt + cm::optional<cmStateEnums::CommandType> GetCommandType( + std::string const& name) const; // Returns a command from its name, or nullptr Command GetCommandByExactName(std::string const& name) const; + // Returns a command type from its name, or cm::nullopt + cm::optional<cmStateEnums::CommandType> GetCommandTypeByExactName( + std::string const& name) const; void AddBuiltinCommand(std::string const& name, Command command); void AddBuiltinCommand(std::string const& name, BuiltinCommand command); @@ -187,7 +191,8 @@ void AddUnexpectedCommand(std::string const& name, char const* error); void AddUnexpectedFlowControlCommand(std::string const& name, char const* error); - bool AddScriptedCommand(std::string const& name, BT<Command> command, + bool AddScriptedCommand(std::string const& name, + cmStateEnums::CommandType type, BT<Command> command, cmMakefile& mf); void RemoveBuiltinCommand(std::string const& name); void RemoveUserDefinedCommands(); @@ -260,6 +265,8 @@ private: friend class cmake; + cmStateSnapshot Reset(cmStateSnapshot const& diagnosticState); + void AddCacheEntry(std::string const& key, cmValue value, std::string const& helpString, cmStateEnums::CacheEntryType type); @@ -276,8 +283,32 @@ cmPropertyDefinitionMap PropertyDefinitions; std::vector<std::string> EnabledLanguages; - std::unordered_map<std::string, Command> BuiltinCommands; - std::unordered_map<std::string, Command> ScriptedCommands; + + class CommandDescriptor + { + public: + using CommandType = cmStateEnums::CommandType; + + CommandDescriptor() + : Type(CommandType::Macro) + , Script(nullptr) + { + } + CommandDescriptor(CommandType type, Command command); + CommandDescriptor(CommandDescriptor&& descriptor) noexcept; + + CommandDescriptor& operator=(CommandDescriptor&& descriptor) noexcept; + CommandDescriptor& operator=(CommandDescriptor const& descriptor) = + default; + + CommandType Type; + Command Script; + }; + CommandDescriptor const* GetCommandDescriptorByExactName( + std::string const& name) const; + + std::unordered_map<std::string, CommandDescriptor> BuiltinCommands; + std::unordered_map<std::string, CommandDescriptor> ScriptedCommands; std::unordered_set<std::string> FlowControlCommands; cmPropertyMap GlobalProperties; std::unique_ptr<cmCacheManager> CacheManager; @@ -288,6 +319,7 @@ cmLinkedTree<std::string> ExecutionListFiles; + cmLinkedTree<cmStateDetail::DiagnosticStackEntry> DiagnosticStack; cmLinkedTree<cmStateDetail::PolicyStackEntry> PolicyStack; cmLinkedTree<cmStateDetail::SnapshotDataType> SnapshotData; cmLinkedTree<cmDefinitions> VarTree;
diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h index 651d993..3c7655e 100644 --- a/Source/cmStatePrivate.h +++ b/Source/cmStatePrivate.h
@@ -11,6 +11,7 @@ #include <vector> #include "cmDefinitions.h" +#include "cmDiagnostics.h" #include "cmLinkedTree.h" #include "cmListFileCache.h" #include "cmPackageState.h" @@ -22,6 +23,7 @@ namespace cmStateDetail { struct BuildsystemDirectoryStateType; struct PolicyStackEntry; +struct DiagnosticStackEntry; extern std::string const PropertySentinel; } // namespace cmStateDetail @@ -32,6 +34,9 @@ cmLinkedTree<cmStateDetail::PolicyStackEntry>::iterator Policies; cmLinkedTree<cmStateDetail::PolicyStackEntry>::iterator PolicyRoot; cmLinkedTree<cmStateDetail::PolicyStackEntry>::iterator PolicyScope; + cmLinkedTree<cmStateDetail::DiagnosticStackEntry>::iterator Diagnostics; + cmLinkedTree<cmStateDetail::DiagnosticStackEntry>::iterator DiagnosticRoot; + cmLinkedTree<cmStateDetail::DiagnosticStackEntry>::iterator DiagnosticScope; cmStateEnums::SnapshotType SnapshotType; cmStateEnums::SnapshotUnwindType UnwindType = cmStateEnums::NO_UNWIND; cmStateEnums::SnapshotUnwindState UnwindState = cmStateEnums::NOT_UNWINDING; @@ -64,6 +69,22 @@ bool Weak; }; +struct cmStateDetail::DiagnosticStackEntry + : public cmDiagnostics::DiagnosticMap +{ + using derived = cmDiagnostics::DiagnosticMap; + DiagnosticStackEntry(bool w = false) + : Weak(w) + { + } + DiagnosticStackEntry(derived d, bool w) + : derived(d) + , Weak(w) + { + } + bool Weak; +}; + struct cmStateDetail::BuildsystemDirectoryStateType { cmStateDetail::PositionType CurrentScope;
diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index 50fc8d1..e568177 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx
@@ -4,6 +4,7 @@ #include "cmStateSnapshot.h" #include <algorithm> +#include <array> #include <cassert> #include <set> #include <string> @@ -168,7 +169,7 @@ return true; } -bool cmStateSnapshot::CanPopPolicyScope() +bool cmStateSnapshot::CanPopPolicyScope() const { return this->Position->Policies != this->Position->PolicyScope; } @@ -224,6 +225,129 @@ return status; } +void cmStateSnapshot::PushDiagnostic(cmDiagnostics::DiagnosticMap entry, + bool weak) +{ + cmStateDetail::PositionType pos = this->Position; + pos->Diagnostics = this->State->DiagnosticStack.Push( + pos->Diagnostics, cmStateDetail::DiagnosticStackEntry(entry, weak)); +} + +bool cmStateSnapshot::PopDiagnostic() +{ + cmStateDetail::PositionType pos = this->Position; + if (pos->Diagnostics == pos->DiagnosticScope) { + return false; + } + pos->Diagnostics = this->State->DiagnosticStack.Pop(pos->Diagnostics); + return true; +} + +bool cmStateSnapshot::CanPopDiagnosticScope() const +{ + return this->Position->Diagnostics != this->Position->DiagnosticScope; +} + +void cmStateSnapshot::SetDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, bool recursive) +{ + assert(action != cmDiagnostics::Undefined); + + auto function = [](cmDiagnosticAction, cmDiagnosticAction) -> bool { + return true; + }; + + this->AlterDiagnostic(category, action, function, recursive); +} + +void cmStateSnapshot::PromoteDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, + bool recursive) +{ + assert(action != cmDiagnostics::Undefined); + + auto function = [](cmDiagnosticAction current, + cmDiagnosticAction desired) -> bool { + return (current < desired); + }; + + this->AlterDiagnostic(category, action, function, recursive); +} + +void cmStateSnapshot::DemoteDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, + bool recursive) +{ + assert(action != cmDiagnostics::Undefined); + + auto function = [](cmDiagnosticAction current, + cmDiagnosticAction desired) -> bool { + return (current > desired); + }; + + this->AlterDiagnostic(category, action, function, recursive); +} + +void cmStateSnapshot::AlterDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, + AlterDiagnosticFunction function, + bool recursive) +{ + if (recursive) { + unsigned i = category; + for (;;) { + this->AlterDiagnostic(static_cast<cmDiagnosticCategory>(i), action, + function, false); + if (++i >= cmDiagnostics::CategoryCount) { + break; + } + if (cmDiagnostics::CategoryInfo[i].Parent < category) { + break; + } + } + } else { + cmDiagnosticAction const oldAction = this->GetDiagnostic(category); + if (function(oldAction, action)) { + // Update the policy stack from the top to the top-most strong entry. + bool previous_was_weak = true; + for (cmLinkedTree<cmStateDetail::DiagnosticStackEntry>::iterator dsi = + this->Position->Diagnostics; + previous_was_weak && dsi != this->Position->DiagnosticRoot; ++dsi) { + (*dsi)[category] = action; + previous_was_weak = dsi->Weak; + } + } + } +} + +cmDiagnosticAction cmStateSnapshot::GetDiagnostic( + cmDiagnosticCategory category, cmDiagnosticAction defaultAction) const +{ + cmLinkedTree<cmStateDetail::BuildsystemDirectoryStateType>::iterator dir = + this->Position->BuildSystemDirectory; + + while (true) { + assert(dir.IsValid()); + cmLinkedTree<cmStateDetail::DiagnosticStackEntry>::iterator leaf = + dir->CurrentScope->Diagnostics; + cmLinkedTree<cmStateDetail::DiagnosticStackEntry>::iterator root = + dir->CurrentScope->DiagnosticRoot; + for (; leaf != root; ++leaf) { + cmDiagnosticAction const action = (*leaf)[category]; + if (action != cmDiagnostics::Undefined) { + return action; + } + } + cmStateDetail::PositionType e = dir->CurrentScope; + cmStateDetail::PositionType p = e->DirectoryParent; + if (p == this->State->SnapshotData.Root()) { + break; + } + dir = p->BuildSystemDirectory; + } + return defaultAction; +} + cmValue cmStateSnapshot::GetDefinition(std::string const& name) const { assert(this->Position->Vars.IsValid());
diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h index d181fad..f727123 100644 --- a/Source/cmStateSnapshot.h +++ b/Source/cmStateSnapshot.h
@@ -10,6 +10,7 @@ #include <cm/string_view> +#include "cmDiagnostics.h" #include "cmPolicies.h" #include "cmStateTypes.h" #include "cmValue.h" @@ -55,7 +56,24 @@ bool parent_scope = false) const; void PushPolicy(cmPolicies::PolicyMap const& entry, bool weak); bool PopPolicy(); - bool CanPopPolicyScope(); + bool CanPopPolicyScope() const; + + void SetDiagnostic(cmDiagnosticCategory category, cmDiagnosticAction action, + bool recursive); + void PromoteDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, bool recursive); + void DemoteDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, bool recursive); + cmDiagnosticAction GetDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction defaultAction) const; + cmDiagnosticAction GetDiagnostic(cmDiagnosticCategory category) const + { + return this->GetDiagnostic( + category, cmDiagnostics::CategoryInfo[category].DefaultAction); + } + void PushDiagnostic(cmDiagnostics::DiagnosticMap entry, bool weak); + bool PopDiagnostic(); + bool CanPopDiagnosticScope() const; cmState* GetState() const; @@ -89,6 +107,12 @@ void InitializeFromParent(); + using AlterDiagnosticFunction = bool (*)(cmDiagnosticAction current, + cmDiagnosticAction desired); + void AlterDiagnostic(cmDiagnosticCategory category, + cmDiagnosticAction action, + AlterDiagnosticFunction function, bool recursive); + cmState* State; cmStateDetail::PositionType Position; };
diff --git a/Source/cmStateTypes.h b/Source/cmStateTypes.h index 6e2d456..1b7043f 100644 --- a/Source/cmStateTypes.h +++ b/Source/cmStateTypes.h
@@ -41,6 +41,12 @@ UNWINDING }; +enum class CommandType +{ + Macro, + Function +}; + // There are multiple overlapping ranges represented here. Be aware that adding // a value to this enumeration may cause failures in numerous places which // assume details about the ordering.
diff --git a/Source/cmStdIoInit.cxx b/Source/cmStdIoInit.cxx index 0d7a3d0..e6e2e16 100644 --- a/Source/cmStdIoInit.cxx +++ b/Source/cmStdIoInit.cxx
@@ -17,6 +17,9 @@ # include "cm_fileno.hxx" #else +# include <cm/string_view> +# include <cmext/string_view> + # include <unistd.h> #endif @@ -118,6 +121,17 @@ #else // On non-Windows platforms, we select the user's locale. std::setlocale(LC_CTYPE, ""); + + // In the "C" locale try switching to a UTF-8 character set. + if (char const* locale = std::setlocale(LC_CTYPE, nullptr)) { + if (locale == "C"_s || locale == "POSIX"_s) { + std::setlocale(LC_CTYPE, "C.UTF-8") +# ifdef __APPLE__ + || std::setlocale(LC_CTYPE, "UTF-8") +# endif + || std::setlocale(LC_CTYPE, "en_US.UTF-8"); + } + } #endif }
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h index 966091a..9e846d5 100644 --- a/Source/cmStringAlgorithms.h +++ b/Source/cmStringAlgorithms.h
@@ -226,9 +226,9 @@ { } cmAlphaNum(char ch) - : View_(this->Digits_, 1) { this->Digits_[0] = ch; + this->View_ = cm::string_view(this->Digits_, 1); } cmAlphaNum(int val); cmAlphaNum(unsigned int val);
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index 1a888c1..f67b074 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx
@@ -5,6 +5,7 @@ #include "cmStringCommand.h" +#include <algorithm> #include <cstdio> #include <cstdlib> #include <exception> @@ -928,6 +929,56 @@ return Json::writeString(writer, value); } +bool JsonPartialMatch(Json::Value const& pattern, Json::Value const& actual) +{ + if (pattern.type() != actual.type()) { + return false; + } + switch (pattern.type()) { + case Json::nullValue: + case Json::intValue: + case Json::uintValue: + case Json::realValue: + case Json::stringValue: + case Json::booleanValue: + return pattern == actual; + + case Json::objectValue: { + std::vector<std::string> const keys = pattern.getMemberNames(); + return std::all_of(keys.begin(), keys.end(), + [&](std::string const& key) { + return actual.isMember(key) && + JsonPartialMatch(pattern[key], actual[key]); + }); + } + + case Json::arrayValue: { + if (actual.size() < pattern.size()) { + return false; + } + std::vector<bool> matched(actual.size(), false); + for (Json::Value const& p : pattern) { + bool found = false; + for (Json::ArrayIndex j = 0; j < actual.size(); ++j) { + if (matched[j]) { + continue; + } + if (JsonPartialMatch(p, actual[j])) { + matched[j] = true; + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; + } + } + return false; +} + #endif bool HandleJSONCommand(std::vector<std::string> const& arguments, @@ -954,11 +1005,12 @@ auto const& mode = args.PopFront("missing mode argument"_s); if (mode != "GET"_s && mode != "GET_RAW"_s && mode != "TYPE"_s && mode != "MEMBER"_s && mode != "LENGTH"_s && mode != "REMOVE"_s && - mode != "SET"_s && mode != "EQUAL"_s && mode != "STRING_ENCODE"_s) { + mode != "SET"_s && mode != "EQUAL"_s && mode != "STRING_ENCODE"_s && + mode != "PARTIAL_EQUAL"_s) { throw json_error(cmStrCat( "got an invalid mode '"_s, mode, "', expected one of GET, GET_RAW, TYPE, MEMBER, LENGTH, REMOVE, SET, " - " EQUAL, STRING_ENCODE"_s)); + " EQUAL, PARTIAL_EQUAL, STRING_ENCODE"_s)); } auto const& jsonstr = args.PopFront("missing json string argument"_s); @@ -1067,6 +1119,12 @@ args.PopFront("missing second json string argument"_s); Json::Value json2 = ReadJson(jsonstr2); makefile.AddDefinitionBool(*outputVariable, json == json2); + } else if (mode == "PARTIAL_EQUAL"_s) { + auto const& jsonstr2 = + args.PopFront("missing second json string argument"_s); + Json::Value json2 = ReadJson(jsonstr2); + makefile.AddDefinitionBool(*outputVariable, + JsonPartialMatch(json, json2)); } }
diff --git a/Source/cmSyntheticTargetCache.h b/Source/cmSyntheticTargetCache.h deleted file mode 100644 index 439f6ec..0000000 --- a/Source/cmSyntheticTargetCache.h +++ /dev/null
@@ -1,15 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <map> -#include <string> - -class cmGeneratorTarget; - -struct cmSyntheticTargetCache -{ - std::map<std::string, cmGeneratorTarget const*> CxxModuleTargets; -};
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index c0049ba..3e84010 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx
@@ -367,6 +367,14 @@ } // namespace +#if defined(_WIN32) || defined(__APPLE__) +cmsys::Status cmSystemTools::ReadNameOnDisk(std::string const& path, + std::string& name) +{ + return ::ReadNameOnDisk(path, name); +} +#endif + #if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE) // For GetEnvironmentVariables # if defined(_WIN32) @@ -847,10 +855,12 @@ std::string* captureStdOut, std::string* captureStdErr, int* retVal, char const* dir, OutputOption outputflag, - cmDuration timeout, Encoding encoding) + cmDuration timeout, Encoding encoding, + std::vector<std::string> env) { cmUVProcessChainBuilder builder; builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin) + .SetEnvironment(std::move(env)) .AddCommand(command); if (dir) { builder.SetWorkingDirectory(dir); @@ -1770,9 +1780,9 @@ unsigned int i; numf = d.GetNumberOfFiles(); for (i = 0; i < numf; i++) { - std::string fname = d.GetFile(i); + std::string const& fname = d.GetFileName(i); if (reg.find(fname)) { - files.push_back(std::move(fname)); + files.push_back(fname); } } } @@ -1792,9 +1802,9 @@ cmsys::Directory d; if (d.Load(startPath)) { for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) { - if ((std::string(d.GetFile(i)) != ".") && - (std::string(d.GetFile(i)) != "..")) { - std::string fname = cmStrCat(startPath, '/', d.GetFile(i)); + std::string const& f = d.GetFileName(i); + if (f != "." && f != "..") { + std::string fname = cmStrCat(startPath, '/', f); if (cmSystemTools::FileIsDirectory(fname)) { fname += finishPath; cmSystemTools::GlobDirs(fname, files); @@ -1814,7 +1824,7 @@ } std::string path = cmSystemTools::GetFilenamePath(glob); std::string ppath = cmSystemTools::GetFilenameName(glob); - ppath = ppath.substr(0, ppath.size() - 1); + ppath.pop_back(); if (path.empty()) { path = "/"; } @@ -1823,14 +1833,13 @@ cmsys::Directory d; if (d.Load(path)) { for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) { - if ((std::string(d.GetFile(i)) != ".") && - (std::string(d.GetFile(i)) != "..")) { + std::string const& sfname = d.GetFileName(i); + if (sfname != "." && sfname != "..") { std::string fname = path; if (path.back() != '/') { fname += "/"; } - fname += d.GetFile(i); - std::string sfname = d.GetFile(i); + fname += sfname; if (type > 0 && cmSystemTools::FileIsDirectory(fname)) { continue; } @@ -2083,6 +2092,9 @@ static Resolver<Policies::LogicalPath> const resolver(RealOS); #endif resolver.Resolve(std::move(p), p); +#ifdef __clang_analyzer__ /* cplusplus.Move */ + p.clear(); +#endif return p; } @@ -2100,167 +2112,30 @@ std::vector<std::string> cmSystemTools::GetEnvironmentVariables() { std::vector<std::string> env; - int cc; # ifdef _WIN32 - // if program starts with main, _wenviron is initially NULL, call to - // _wgetenv and create wide-character string environment - _wgetenv(L""); - for (cc = 0; _wenviron[cc]; ++cc) { - env.emplace_back(cmsys::Encoding::ToNarrow(_wenviron[cc])); + struct EnvDeleter + { + void operator()(wchar_t* p) const { FreeEnvironmentStringsW(p); } + }; + + auto block = std::unique_ptr<wchar_t, EnvDeleter>(GetEnvironmentStringsW()); + if (!block) { + return env; + } + + for (wchar_t const* p = block.get(); *p; p += wcslen(p) + 1) { + if (p[0] != L'=') { + env.emplace_back(cmsys::Encoding::ToNarrow(p)); + } } # else - for (cc = 0; environ[cc]; ++cc) { + for (int cc = 0; environ[cc]; ++cc) { env.emplace_back(environ[cc]); } # endif return env; } -void cmSystemTools::AppendEnv(std::vector<std::string> const& env) -{ - for (std::string const& var : env) { - cmSystemTools::PutEnv(var); - } -} - -void cmSystemTools::EnvDiff::AppendEnv(std::vector<std::string> const& env) -{ - for (std::string const& var : env) { - this->PutEnv(var); - } -} - -void cmSystemTools::EnvDiff::PutEnv(std::string const& env) -{ - auto const eq_loc = env.find('='); - if (eq_loc != std::string::npos) { - std::string name = env.substr(0, eq_loc); - diff[name] = env.substr(eq_loc + 1); - } else { - this->UnPutEnv(env); - } -} - -void cmSystemTools::EnvDiff::UnPutEnv(std::string const& env) -{ - diff[env] = cm::nullopt; -} - -bool cmSystemTools::EnvDiff::ParseOperation(std::string const& envmod) -{ - char path_sep = GetSystemPathlistSeparator(); - - auto apply_diff = [this](std::string const& name, - std::function<void(std::string&)> const& apply) { - cm::optional<std::string> old_value = diff[name]; - std::string output; - if (old_value) { - output = *old_value; - } else { - char const* curval = cmSystemTools::GetEnv(name); - if (curval) { - output = curval; - } - } - apply(output); - diff[name] = output; - }; - - // Split on `=` - auto const eq_loc = envmod.find_first_of('='); - if (eq_loc == std::string::npos) { - cmSystemTools::Error(cmStrCat( - "Error: Missing `=` after the variable name in: ", envmod, '\n')); - return false; - } - - auto const name = envmod.substr(0, eq_loc); - - // Split value on `:` - auto const op_value_start = eq_loc + 1; - auto const colon_loc = envmod.find_first_of(':', op_value_start); - if (colon_loc == std::string::npos) { - cmSystemTools::Error( - cmStrCat("Error: Missing `:` after the operation in: ", envmod, '\n')); - return false; - } - auto const op = envmod.substr(op_value_start, colon_loc - op_value_start); - - auto const value_start = colon_loc + 1; - auto const value = envmod.substr(value_start); - - // Determine what to do with the operation. - if (op == "reset"_s) { - auto entry = diff.find(name); - if (entry != diff.end()) { - diff.erase(entry); - } - } else if (op == "set"_s) { - diff[name] = value; - } else if (op == "unset"_s) { - diff[name] = cm::nullopt; - } else if (op == "string_append"_s) { - apply_diff(name, [&value](std::string& output) { output += value; }); - } else if (op == "string_prepend"_s) { - apply_diff(name, - [&value](std::string& output) { output.insert(0, value); }); - } else if (op == "path_list_append"_s) { - apply_diff(name, [&value, path_sep](std::string& output) { - if (!output.empty()) { - output += path_sep; - } - output += value; - }); - } else if (op == "path_list_prepend"_s) { - apply_diff(name, [&value, path_sep](std::string& output) { - if (!output.empty()) { - output.insert(output.begin(), path_sep); - } - output.insert(0, value); - }); - } else if (op == "cmake_list_append"_s) { - apply_diff(name, [&value](std::string& output) { - if (!output.empty()) { - output += ';'; - } - output += value; - }); - } else if (op == "cmake_list_prepend"_s) { - apply_diff(name, [&value](std::string& output) { - if (!output.empty()) { - output.insert(output.begin(), ';'); - } - output.insert(0, value); - }); - } else { - cmSystemTools::Error(cmStrCat( - "Error: Unrecognized environment manipulation argument: ", op, '\n')); - return false; - } - - return true; -} - -void cmSystemTools::EnvDiff::ApplyToCurrentEnv(std::ostringstream* measurement) -{ - for (auto const& env_apply : diff) { - if (env_apply.second) { - auto const env_update = - cmStrCat(env_apply.first, '=', *env_apply.second); - cmSystemTools::PutEnv(env_update); - if (measurement) { - *measurement << env_update << std::endl; - } - } else { - cmSystemTools::UnsetEnv(env_apply.first.c_str()); - if (measurement) { - // Signify that this variable is being actively unset - *measurement << '#' << env_apply.first << "=\n"; - } - } - } -} - cmSystemTools::SaveRestoreEnvironment::SaveRestoreEnvironment() { this->Env = cmSystemTools::GetEnvironmentVariables(); @@ -2273,14 +2148,16 @@ for (std::string var : currentEnv) { std::string::size_type pos = var.find('='); if (pos != std::string::npos) { - var = var.substr(0, pos); + var.resize(pos); } cmSystemTools::UnsetEnv(var.c_str()); } // Then put back each entry from the original environment: - cmSystemTools::AppendEnv(this->Env); + for (std::string const& var : this->Env) { + cmSystemTools::PutEnv(var); + } } #endif @@ -2359,13 +2236,11 @@ cmHasLiteralSuffix(path, ".dylib")); } -bool cmSystemTools::CreateTar(std::string const& arFileName, - std::vector<std::string> const& files, - std::string const& workingDirectory, - cmTarCompression compressType, bool verbose, - std::string const& mtime, - std::string const& format, int compressionLevel, - int numThreads) +bool cmSystemTools::CreateTar( + std::string const& arFileName, std::vector<std::string> const& files, + std::string const& workingDirectory, cmTarCompression compressType, + std::string const& encoding, bool verbose, std::string const& mtime, + std::string const& format, int compressionLevel, int numThreads) { #if !defined(CMAKE_BOOTSTRAP) cmWorkingDirectory workdir(cmSystemTools::GetLogicalWorkingDirectory()); @@ -2416,7 +2291,7 @@ break; } - cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format, + cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format, encoding, compressionLevel, numThreads); if (!a.Open()) { @@ -2440,6 +2315,7 @@ #else (void)arFileName; (void)files; + (void)encoding; (void)verbose; return false; #endif @@ -2560,6 +2436,9 @@ cmSystemTools::Error(message); } +// Return 'true' if the return value 'r' from a libarchive function indicates +// success or a warning that can be ignored. Return 'false' if it indicates an +// error bool la_diagnostic(struct archive* ar, __LA_SSIZE_T r) { // See archive.h definition of ARCHIVE_OK for return values. @@ -2569,6 +2448,12 @@ } if (r >= ARCHIVE_WARN) { + if (archive_errno(ar) == ENOSPC) { + // If we fall through to the generic error handling, the error message + // will be "Write failed". Explicit handling for better diagnostics + std::cerr << "cmake -E tar: error: No space left on device\n"; + return false; + } char const* warn = archive_error_string(ar); if (!warn) { warn = "unknown warning"; @@ -2609,7 +2494,7 @@ } // See archive.h definition of ARCHIVE_OK for return values. __LA_SSIZE_T const w = archive_write_data_block(aw, buff, size, offset); - if (!la_diagnostic(ar, w)) { + if (!la_diagnostic(aw, w)) { return false; } } @@ -2618,13 +2503,33 @@ # endif } +struct ArchiveReadDeleter +{ + void operator()(struct archive* a) const { archive_read_free(a); } +}; + +struct ArchiveWriteDeleter +{ + void operator()(struct archive* a) const { archive_write_free(a); } +}; + +struct ArchiveMatchDeleter +{ + void operator()(struct archive* a) const { archive_match_free(a); } +}; + bool extract_tar(std::string const& arFileName, - std::vector<std::string> const& files, bool verbose, + std::vector<std::string> const& files, + std::string const& encoding, bool verbose, cmSystemTools::cmTarExtractTimestamps extractTimestamps, bool extract) { - struct archive* a = archive_read_new(); - struct archive* ext = archive_write_disk_new(); + std::unique_ptr<struct archive, ArchiveReadDeleter> a_owner( + archive_read_new()); + std::unique_ptr<struct archive, ArchiveWriteDeleter> ext_owner( + archive_write_disk_new()); + struct archive* a = a_owner.get(); + struct archive* ext = ext_owner.get(); if (extract) { int flags = ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; @@ -2633,16 +2538,25 @@ } if (archive_write_disk_set_options(ext, flags) != ARCHIVE_OK) { ArchiveError("Problem with archive_write_disk_set_options(): ", ext); - archive_write_free(ext); - archive_read_free(a); return false; } } archive_read_support_filter_all(a); archive_read_support_format_all(a); + + if (encoding != "OEM") { + if (archive_read_set_options( + a, cmStrCat("hdrcharset=", encoding).c_str()) != ARCHIVE_OK) { + cmSystemTools::Error( + cmStrCat("Cannot set archive encoding: ", encoding)); + return false; + } + } struct archive_entry* entry; - struct archive* matching = archive_match_new(); + std::unique_ptr<struct archive, ArchiveMatchDeleter> matching_owner( + archive_match_new()); + struct archive* matching = matching_owner.get(); if (!matching) { cmSystemTools::Error("Out of memory"); return false; @@ -2659,10 +2573,6 @@ int r = cm_archive_read_open_filename(a, arFileName.c_str(), 10240); if (r) { ArchiveError("Problem with archive_read_open_filename(): ", a); - archive_write_free(ext); - archive_read_close(a); - archive_read_free(a); - archive_match_free(matching); return false; } for (;;) { @@ -2694,6 +2604,7 @@ r = archive_write_header(ext, entry); if (r == ARCHIVE_OK) { if (!copy_data(a, ext)) { + r = ARCHIVE_FAILED; break; } r = archive_write_finish_entry(ext); @@ -2737,10 +2648,6 @@ return false; } } - archive_match_free(matching); - archive_write_free(ext); - archive_read_close(a); - archive_read_free(a); return r == ARCHIVE_EOF || r == ARCHIVE_OK; } } @@ -2749,14 +2656,16 @@ bool cmSystemTools::ExtractTar(std::string const& arFileName, std::vector<std::string> const& files, cmTarExtractTimestamps extractTimestamps, - bool verbose) + std::string const& encoding, bool verbose) { #if !defined(CMAKE_BOOTSTRAP) - return extract_tar(arFileName, files, verbose, extractTimestamps, true); + return extract_tar(arFileName, files, encoding, verbose, extractTimestamps, + true); #else (void)arFileName; (void)files; (void)extractTimestamps; + (void)encoding; (void)verbose; return false; #endif @@ -2764,14 +2673,15 @@ bool cmSystemTools::ListTar(std::string const& arFileName, std::vector<std::string> const& files, - bool verbose) + std::string const& encoding, bool verbose) { #if !defined(CMAKE_BOOTSTRAP) - return extract_tar(arFileName, files, verbose, cmTarExtractTimestamps::Yes, - false); + return extract_tar(arFileName, files, encoding, verbose, + cmTarExtractTimestamps::Yes, false); #else (void)arFileName; (void)files; + (void)encoding; (void)verbose; return false; #endif @@ -2998,6 +2908,8 @@ return cwd; } +bool cmSystemToolsCMakeInBuildTree = false; + std::string cmSystemToolsLogicalWorkingDirectory = InitLogicalWorkingDirectory(); @@ -3122,6 +3034,7 @@ if (fin && cmSystemTools::GetLineFromStream(fin, src_dir) && cmSystemTools::FileIsDirectory(src_dir)) { cmSystemToolsCMakeRoot = src_dir; + cmSystemToolsCMakeInBuildTree = true; } else { dir = cmSystemTools::GetFilenamePath(dir); src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt"); @@ -3129,6 +3042,7 @@ if (fin2 && cmSystemTools::GetLineFromStream(fin2, src_dir) && cmSystemTools::FileIsDirectory(src_dir)) { cmSystemToolsCMakeRoot = src_dir; + cmSystemToolsCMakeInBuildTree = true; } } if (!cmSystemToolsCMakeRoot.empty() && cmSystemToolsHTMLDoc.empty() && @@ -3146,6 +3060,7 @@ #ifdef CMAKE_BOOTSTRAP // The bootstrap cmake knows its resource locations. cmSystemToolsCMakeRoot = CMAKE_BOOTSTRAP_SOURCE_DIR; + cmSystemToolsCMakeInBuildTree = true; cmSystemToolsCMakeCommand = exe; // The bootstrap cmake does not provide the other tools, // so use the directory where they are about to be built. @@ -3229,6 +3144,11 @@ return cmSystemToolsCMakeRoot; } +bool cmSystemTools::GetCMakeInBuildTree() +{ + return cmSystemToolsCMakeInBuildTree; +} + std::string const& cmSystemTools::GetHTMLDoc() { return cmSystemToolsHTMLDoc;
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index c28a0b2..17a6310 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h
@@ -10,8 +10,6 @@ #include <cstddef> #include <functional> -#include <map> -#include <sstream> #include <string> #include <vector> @@ -293,7 +291,8 @@ char const* dir = nullptr, OutputOption outputflag = OUTPUT_MERGE, cmDuration timeout = cmDuration::zero(), - Encoding encoding = cmProcessOutput::Auto); + Encoding encoding = cmProcessOutput::Auto, + std::vector<std::string> env = {}); static std::string PrintSingleCommand(std::vector<std::string> const&); @@ -450,45 +449,6 @@ /** Get the list of all environment variables */ static std::vector<std::string> GetEnvironmentVariables(); - /** Append multiple variables to the current environment. */ - static void AppendEnv(std::vector<std::string> const& env); - - /** - * Helper class to represent an environment diff directly. This is to avoid - * repeated in-place environment modification (i.e. via setenv/putenv), which - * could be slow. - */ - class EnvDiff - { - public: - /** Append multiple variables to the current environment diff */ - void AppendEnv(std::vector<std::string> const& env); - - /** - * Add a single variable (or remove if no = sign) to the current - * environment diff. - */ - void PutEnv(std::string const& env); - - /** Remove a single variable from the current environment diff. */ - void UnPutEnv(std::string const& env); - - /** - * Apply an ENVIRONMENT_MODIFICATION operation to this diff. Returns - * false and issues an error on parse failure. - */ - bool ParseOperation(std::string const& envmod); - - /** - * Apply this diff to the actual environment, optionally writing out the - * modifications to a CTest-compatible measurement stream. - */ - void ApplyToCurrentEnv(std::ostringstream* measurement = nullptr); - - private: - std::map<std::string, cm::optional<std::string>> diff; - }; - /** Helper class to save and restore the environment. Instantiate this class as an automatic variable on the stack. Its constructor saves a copy of the current @@ -562,18 +522,20 @@ }; static bool ListTar(std::string const& arFileName, - std::vector<std::string> const& files, bool verbose); + std::vector<std::string> const& files, + std::string const& encoding, bool verbose); static bool CreateTar(std::string const& arFileName, std::vector<std::string> const& files, std::string const& workingDirectory, - cmTarCompression compressType, bool verbose, + cmTarCompression compressType, + std::string const& encoding, bool verbose, std::string const& mtime = std::string(), std::string const& format = std::string(), int compressionLevel = 0, int numThreads = 1); static bool ExtractTar(std::string const& arFileName, std::vector<std::string> const& files, cmTarExtractTimestamps extractTimestamps, - bool verbose); + std::string const& encoding, bool verbose); /** Random number generation. */ static unsigned int RandomSeed(); @@ -598,6 +560,7 @@ static std::string const& GetCMakeCursesCommand(); static std::string const& GetCMClDepsCommand(); static std::string const& GetCMakeRoot(); + static bool GetCMakeInBuildTree(); static std::string const& GetHTMLDoc(); /** Get the CMake config directory **/ @@ -710,6 +673,12 @@ /** Return subview of the full filename (i.e. file name without path) */ static cm::string_view GetFilenameNameView(cm::string_view filename); +#if defined(_WIN32) || defined(__APPLE__) + /** Read the on-disk spelling of the full filename's last component. */ + static cmsys::Status ReadNameOnDisk(std::string const& path, + std::string& name); +#endif + /** * Return subview of file extension of a full filename (dot included). * Warning: this is the shortest extension (for example: .gz of .tar.gz)
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 296adf5..a1f69cd 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx
@@ -4,6 +4,7 @@ #include <algorithm> #include <cassert> +#include <functional> #include <iterator> #include <map> #include <set> @@ -21,8 +22,10 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" #include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmFindPackageStack.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmList.h" #include "cmListFileCache.h" @@ -98,18 +101,18 @@ namespace { struct FileSetEntries { - FileSetEntries(cm::static_string_view propertyName) + FileSetEntries(cm::string_view propertyName) : PropertyName(propertyName) { } - cm::static_string_view const PropertyName; + cm::string_view const PropertyName; std::vector<BT<std::string>> Entries; }; struct FileSetType { - FileSetType(cm::static_string_view typeName, + FileSetType(cm::string_view typeName, cm::static_string_view defaultDirectoryProperty, cm::static_string_view defaultPathProperty, cm::static_string_view directoryPrefix, @@ -131,7 +134,7 @@ { } - cm::static_string_view const TypeName; + cm::string_view const TypeName; cm::static_string_view const DefaultDirectoryProperty; cm::static_string_view const DefaultPathProperty; cm::static_string_view const DirectoryPrefix; @@ -157,7 +160,7 @@ cmTargetInternals const* impl, std::string const& prop) const; - void AddFileSet(std::string const& name, cmFileSetVisibility vis, + void AddFileSet(std::string const& name, cm::FileSetMetadata::Visibility vis, cmListFileBacktrace bt); }; @@ -378,6 +381,10 @@ { "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources }, { "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources }, { "Swift_COMPILATION_MODE"_s, IC::CanCompileSources }, + { "Swift_SEPARATE_MODULE_EMISSION"_s, IC::CanCompileSources }, + // ---- Rust + { "Rust_EDITION"_s, IC::CanCompileSources }, + { "Rust_MAIN_CRATE_ROOT"_s, IC::CanCompileSources }, // ---- moc { "AUTOMOC"_s, IC::CanCompileSources }, { "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources }, @@ -589,6 +596,16 @@ #undef COMMON_LANGUAGE_PROPERTIES #undef IC #undef R + +cmValue copyProperty(cmTarget const* src, cmTarget* dst, + std::string const& prop) +{ + cmValue value = src->GetProperty(prop); + // Always set the property; it may have been explicitly unset. + dst->SetProperty(prop, value); + return value; +}; + } class cmTargetInternals @@ -649,8 +666,7 @@ UsageRequirementProperty ImportedCxxModulesCompileOptions; UsageRequirementProperty ImportedCxxModulesLinkLibraries; - FileSetType HeadersFileSets; - FileSetType CxxModulesFileSets; + std::unordered_map<cm::string_view, FileSetType> FileSetTypes; cmTargetInternals(); @@ -704,17 +720,25 @@ "IMPORTED_CXX_MODULES_COMPILE_FEATURES"_s) , ImportedCxxModulesCompileOptions("IMPORTED_CXX_MODULES_COMPILE_OPTIONS"_s) , ImportedCxxModulesLinkLibraries("IMPORTED_CXX_MODULES_LINK_LIBRARIES"_s) - , HeadersFileSets("HEADERS"_s, "HEADER_DIRS"_s, "HEADER_SET"_s, - "HEADER_DIRS_"_s, "HEADER_SET_"_s, "Header"_s, - "The default header set"_s, "Header set"_s, - FileSetEntries("HEADER_SETS"_s), - FileSetEntries("INTERFACE_HEADER_SETS"_s)) - , CxxModulesFileSets("CXX_MODULES"_s, "CXX_MODULE_DIRS"_s, - "CXX_MODULE_SET"_s, "CXX_MODULE_DIRS_"_s, - "CXX_MODULE_SET_"_s, "C++ module"_s, - "The default C++ module set"_s, "C++ module set"_s, - FileSetEntries("CXX_MODULE_SETS"_s), - FileSetEntries("INTERFACE_CXX_MODULE_SETS"_s)) + , FileSetTypes{ { cm::FileSetMetadata::HEADERS, + { cm::FileSetMetadata::HEADERS, "HEADER_DIRS"_s, + "HEADER_SET"_s, "HEADER_DIRS_"_s, "HEADER_SET_"_s, + "Header"_s, "The default header set"_s, "Header set"_s, + FileSetEntries{ "HEADER_SETS"_s }, + FileSetEntries{ "INTERFACE_HEADER_SETS"_s } } }, + { cm::FileSetMetadata::SOURCES, + { cm::FileSetMetadata::SOURCES, "SOURCE_DIRS"_s, + "SOURCE_SET"_s, "SOURCE_DIRS_"_s, "SOURCE_SET_"_s, + "Source"_s, "The default source set"_s, "Source set"_s, + FileSetEntries{ "SOURCE_SETS"_s }, + FileSetEntries{ "INTERFACE_SOURCE_SETS"_s } } }, + { cm::FileSetMetadata::CXX_MODULES, + { cm::FileSetMetadata::CXX_MODULES, "CXX_MODULE_DIRS"_s, + "CXX_MODULE_SET"_s, "CXX_MODULE_DIRS_"_s, + "CXX_MODULE_SET_"_s, "C++ module"_s, + "The default C++ module set"_s, "C++ module set"_s, + FileSetEntries{ "CXX_MODULE_SETS"_s }, + FileSetEntries{ "INTERFACE_CXX_MODULE_SETS"_s } } } } { } @@ -803,13 +827,14 @@ return { did_read, value }; } -void FileSetType::AddFileSet(std::string const& name, cmFileSetVisibility vis, +void FileSetType::AddFileSet(std::string const& name, + cm::FileSetMetadata::Visibility vis, cmListFileBacktrace bt) { - if (cmFileSetVisibilityIsForSelf(vis)) { + if (cm::FileSetMetadata::VisibilityIsForSelf(vis)) { this->SelfEntries.Entries.emplace_back(name, bt); } - if (cmFileSetVisibilityIsForInterface(vis)) { + if (cm::FileSetMetadata::VisibilityIsForInterface(vis)) { this->InterfaceEntries.Entries.emplace_back(name, std::move(bt)); } } @@ -1665,11 +1690,21 @@ return cmMakeRange(this->impl->CompileOptions.Entries); } +cmBTStringRange cmTarget::GetImportedCxxModulesCompileOptionsEntries() const +{ + return cmMakeRange(this->impl->ImportedCxxModulesCompileOptions.Entries); +} + cmBTStringRange cmTarget::GetCompileFeaturesEntries() const { return cmMakeRange(this->impl->CompileFeatures.Entries); } +cmBTStringRange cmTarget::GetImportedCxxModulesCompileFeaturesEntries() const +{ + return cmMakeRange(this->impl->ImportedCxxModulesCompileFeatures.Entries); +} + cmBTStringRange cmTarget::GetCompileDefinitionsEntries() const { return cmMakeRange(this->impl->CompileDefinitions.Entries); @@ -1715,59 +1750,102 @@ return cmMakeRange(this->impl->InterfaceLinkLibrariesDirectExclude.Entries); } +void cmTarget::CopyUsageEffects(cmGeneratorTarget const* gt, + std::string const& config) +{ + // Normal targets cannot be the target of a copy. + assert(!this->IsNormal()); + // Imported targets cannot be the target of a copy. + assert(!this->IsImported()); + + auto const* tgt = gt->Target; + + // Only imported or normal targets can be the source of a copy. + assert(tgt->IsImported() || tgt->IsNormal()); + + this->impl->CompileFeatures.Entries.clear(); + this->impl->CompileOptions.Entries.clear(); + + if (tgt->IsImported()) { + this->impl->CompileFeatures.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileFeatures.Entries)); + this->impl->CompileOptions.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileOptions.Entries)); + } else { + this->impl->CompileFeatures.CopyFromEntries( + cmMakeRange(gt->GetCompileFeatures(config))); + this->impl->CompileOptions.CopyFromEntries( + cmMakeRange(gt->GetCompileOptions(config, "CXX"))); + } + + cmValue langStd = gt->GetLanguageStandard("CXX", config); + if (langStd) { + this->SetProperty("CXX_STANDARD", *langStd); + } + copyProperty(tgt, this, "CXX_EXTENSIONS"); + copyProperty(tgt, this, "CXX_STANDARD_REQUIRED"); +} + void cmTarget::CopyPolicyStatuses(cmTarget const* tgt) { // Normal targets cannot be the target of a copy. assert(!this->IsNormal()); // Imported targets cannot be the target of a copy. assert(!this->IsImported()); - // Only imported targets can be the source of a copy. - assert(tgt->IsImported()); + + // Only imported or normal targets can be the source of a copy. + assert(tgt->IsImported() || tgt->IsNormal()); this->impl->PolicyMap = tgt->impl->PolicyMap; this->impl->TemplateTarget = tgt; } -void cmTarget::CopyImportedCxxModulesEntries(cmTarget const* tgt) +void cmTarget::CopyCxxModulesEntries(cmTarget const* tgt) { // Normal targets cannot be the target of a copy. assert(!this->IsNormal()); // Imported targets cannot be the target of a copy. assert(!this->IsImported()); - // Only imported targets can be the source of a copy. - assert(tgt->IsImported()); + // Only imported or normal targets can be the source of a copy. + assert(tgt->IsImported() || tgt->IsNormal()); this->impl->IncludeDirectories.Entries.clear(); - this->impl->IncludeDirectories.CopyFromEntries( - cmMakeRange(tgt->impl->ImportedCxxModulesIncludeDirectories.Entries)); this->impl->CompileDefinitions.Entries.clear(); - this->impl->CompileDefinitions.CopyFromEntries( - cmMakeRange(tgt->impl->ImportedCxxModulesCompileDefinitions.Entries)); - this->impl->CompileFeatures.Entries.clear(); - this->impl->CompileFeatures.CopyFromEntries( - cmMakeRange(tgt->impl->ImportedCxxModulesCompileFeatures.Entries)); - this->impl->CompileOptions.Entries.clear(); - this->impl->CompileOptions.CopyFromEntries( - cmMakeRange(tgt->impl->ImportedCxxModulesCompileOptions.Entries)); this->impl->LinkLibraries.Entries.clear(); - this->impl->LinkLibraries.CopyFromEntries( - cmMakeRange(tgt->impl->ImportedCxxModulesLinkLibraries.Entries)); + + if (tgt->IsImported()) { + this->impl->IncludeDirectories.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesIncludeDirectories.Entries)); + this->impl->CompileDefinitions.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileDefinitions.Entries)); + this->impl->LinkLibraries.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesLinkLibraries.Entries)); + } else { + this->impl->IncludeDirectories.CopyFromEntries( + cmMakeRange(tgt->impl->IncludeDirectories.Entries)); + this->impl->CompileDefinitions.CopyFromEntries( + cmMakeRange(tgt->impl->CompileDefinitions.Entries)); + this->impl->LinkLibraries.CopyFromEntries( + cmMakeRange(tgt->impl->LinkLibraries.Entries)); + } // Copy the C++ module fileset entries from `tgt`'s `INTERFACE` to this // target's `PRIVATE`. - this->impl->CxxModulesFileSets.SelfEntries.Entries.clear(); - this->impl->CxxModulesFileSets.SelfEntries.Entries = - tgt->impl->CxxModulesFileSets.InterfaceEntries.Entries; + auto& entries = this->impl->FileSetTypes.at(cm::FileSetMetadata::CXX_MODULES) + .SelfEntries.Entries; + entries.clear(); + entries = tgt->impl->FileSetTypes.at(cm::FileSetMetadata::CXX_MODULES) + .InterfaceEntries.Entries; } -void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt) +void cmTarget::CopyCxxModulesProperties(cmTarget const* tgt) { // Normal targets cannot be the target of a copy. assert(!this->IsNormal()); // Imported targets cannot be the target of a copy. assert(!this->IsImported()); - // Only imported targets can be the source of a copy. - assert(tgt->IsImported()); + // Only imported or normal targets can be the source of a copy. + assert(tgt->IsImported() || tgt->IsNormal()); // The list of properties that are relevant here include: // - compilation-specific properties for any language or platform @@ -1801,9 +1879,6 @@ // -- Language // ---- C++ "CXX_COMPILER_LAUNCHER", - "CXX_STANDARD", - "CXX_STANDARD_REQUIRED", - "CXX_EXTENSIONS", "CXX_VISIBILITY_PRESET", "CXX_MODULE_STD", @@ -1840,15 +1915,8 @@ "SYSTEM", }; - auto copyProperty = [this, tgt](std::string const& prop) -> cmValue { - cmValue value = tgt->GetProperty(prop); - // Always set the property; it may have been explicitly unset. - this->SetProperty(prop, value); - return value; - }; - for (auto const& prop : propertiesToCopy) { - copyProperty(prop); + copyProperty(tgt, this, prop); } static cm::static_string_view const perConfigPropertiesToCopy[] = { @@ -1863,12 +1931,13 @@ for (std::string const& configName : configNames) { std::string configUpper = cmSystemTools::UpperCase(configName); for (auto const& perConfigProp : perConfigPropertiesToCopy) { - copyProperty(cmStrCat(perConfigProp, configUpper)); + copyProperty(tgt, this, cmStrCat(perConfigProp, configUpper)); } } if (this->GetGlobalGenerator()->IsXcode()) { - cmValue xcodeGenerateScheme = copyProperty("XCODE_GENERATE_SCHEME"); + cmValue xcodeGenerateScheme = + copyProperty(tgt, this, "XCODE_GENERATE_SCHEME"); // TODO: Make sure these show up on the imported target in the first place // XCODE_ATTRIBUTE_??? @@ -1898,31 +1967,33 @@ }; for (auto const& xcodeProperty : xcodeSchemePropertiesToCopy) { - copyProperty(xcodeProperty); + copyProperty(tgt, this, xcodeProperty); } #endif } } } -cmBTStringRange cmTarget::GetHeaderSetsEntries() const -{ - return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries); +namespace { +std::vector<BT<std::string>> EmptyEntries; } -cmBTStringRange cmTarget::GetCxxModuleSetsEntries() const +cmBTStringRange cmTarget::GetFileSetsEntries(cm::string_view type) const { - return cmMakeRange(this->impl->CxxModulesFileSets.SelfEntries.Entries); + if (cm::contains(this->impl->FileSetTypes, type)) { + return cmMakeRange(this->impl->FileSetTypes.at(type).SelfEntries.Entries); + } + return cmMakeRange(EmptyEntries); } -cmBTStringRange cmTarget::GetInterfaceHeaderSetsEntries() const +cmBTStringRange cmTarget::GetInterfaceFileSetsEntries( + cm::string_view type) const { - return cmMakeRange(this->impl->HeadersFileSets.InterfaceEntries.Entries); -} - -cmBTStringRange cmTarget::GetInterfaceCxxModuleSetsEntries() const -{ - return cmMakeRange(this->impl->CxxModulesFileSets.InterfaceEntries.Entries); + if (cm::contains(this->impl->FileSetTypes, type)) { + return cmMakeRange( + this->impl->FileSetTypes.at(type).InterfaceEntries.Entries); + } + return cmMakeRange(EmptyEntries); } namespace { @@ -2027,10 +2098,8 @@ } else { switch (target->GetPolicyStatus(*this->Policy)) { case cmPolicies::WARN: - context->IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0160) + "\n" + - this->message(prop, target)); + context->IssuePolicyWarning(cmPolicies::CMP0160, {}, + this->message(prop, target)); CM_FALLTHROUGH; case cmPolicies::OLD: readOnly = false; @@ -2057,6 +2126,8 @@ { "MANUALLY_ADDED_DEPENDENCIES", { ROC::All } }, { "NAME", { ROC::All } }, { "SOURCES", { ROC::Imported } }, + { "SOURCE_SETS", { ROC::All } }, + { "INTERFACE_SOURCE_SETS", { ROC::All } }, { "SYMBOLIC", { ROC::All } }, { "TYPE", { ROC::All } }, { "ALIAS_GLOBAL", { ROC::All, cmPolicies::CMP0160 } }, @@ -2116,14 +2187,9 @@ } } - FileSetType* fileSetTypes[] = { - &this->impl->HeadersFileSets, - &this->impl->CxxModulesFileSets, - }; - - for (auto* fileSetType : fileSetTypes) { - if (fileSetType->WriteProperties(this, this->impl.get(), prop, value, - FileSetType::Action::Set)) { + for (auto& fileSetType : this->impl->FileSetTypes) { + if (fileSetType.second.WriteProperties(this, this->impl.get(), prop, value, + FileSetType::Action::Set)) { return; } } @@ -2236,14 +2302,9 @@ } } - FileSetType* fileSetTypes[] = { - &this->impl->HeadersFileSets, - &this->impl->CxxModulesFileSets, - }; - - for (auto* fileSetType : fileSetTypes) { - if (fileSetType->WriteProperties(this, this->impl.get(), prop, value, - FileSetType::Action::Append)) { + for (auto& fileSetType : this->impl->FileSetTypes) { + if (fileSetType.second.WriteProperties(this, this->impl.get(), prop, value, + FileSetType::Action::Append)) { return; } } @@ -2719,13 +2780,9 @@ // Check fileset properties. { - FileSetType* fileSetTypes[] = { - &this->impl->HeadersFileSets, - &this->impl->CxxModulesFileSets, - }; - - for (auto* fileSetType : fileSetTypes) { - auto value = fileSetType->ReadProperties(this, this->impl.get(), prop); + for (auto const& fileSetType : this->impl->FileSetTypes) { + auto value = + fileSetType.second.ReadProperties(this, this->impl.get(), prop); if (value.first) { return value.second; } @@ -3117,10 +3174,8 @@ switch (this->GetPolicyStatus(cmPolicies::CMP0111)) { case cmPolicies::WARN: - this->impl->Makefile->IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0111) + "\n" + - message()); + this->impl->Makefile->IssuePolicyWarning(cmPolicies::CMP0111, {}, + message()); CM_FALLTHROUGH; case cmPolicies::OLD: break; @@ -3148,39 +3203,37 @@ } std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet( - std::string const& name, std::string const& type, cmFileSetVisibility vis) + std::string const& name, std::string const& type, + cm::FileSetMetadata::Visibility vis) { auto result = this->impl->FileSets.emplace( - name, cmFileSet(this->GetMakefile(), name, type, vis)); + name, cmFileSet(this->GetMakefile(), this, name, type, vis)); if (result.second) { auto bt = this->impl->Makefile->GetBacktrace(); - if (type == this->impl->HeadersFileSets.TypeName) { - this->impl->HeadersFileSets.AddFileSet(name, vis, std::move(bt)); - } else if (type == this->impl->CxxModulesFileSets.TypeName) { - this->impl->CxxModulesFileSets.AddFileSet(name, vis, std::move(bt)); + if (cm::contains(this->impl->FileSetTypes, type)) { + this->impl->FileSetTypes.at(type).AddFileSet(name, vis, std::move(bt)); } } return std::make_pair(&result.first->second, result.second); } -std::string cmTarget::GetFileSetsPropertyName(std::string const& type) +std::string cmTarget::GetFileSetsPropertyName(std::string const& type) const { - if (type == "HEADERS") { - return "HEADER_SETS"; - } - if (type == "CXX_MODULES") { - return "CXX_MODULE_SETS"; + if (cm::contains(this->impl->FileSetTypes, type)) { + return std::string{ + this->impl->FileSetTypes.at(type).SelfEntries.PropertyName + }; } return ""; } -std::string cmTarget::GetInterfaceFileSetsPropertyName(std::string const& type) +std::string cmTarget::GetInterfaceFileSetsPropertyName( + std::string const& type) const { - if (type == "HEADERS") { - return "INTERFACE_HEADER_SETS"; - } - if (type == "CXX_MODULES") { - return "INTERFACE_CXX_MODULE_SETS"; + if (cm::contains(this->impl->FileSetTypes, type)) { + return std::string{ + this->impl->FileSetTypes.at(type).InterfaceEntries.PropertyName + }; } return ""; } @@ -3196,7 +3249,12 @@ return result; } -std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const +namespace { +std::vector<std::string> RetrieveFileSetNames( + std::unordered_map<cm::string_view, FileSetType> const& fileSetTypes, + std::function< + std::vector<BT<std::string>> const&(FileSetType const& fileSetType)> + GetFileSets) { std::vector<std::string> result; auto inserter = std::back_inserter(result); @@ -3208,11 +3266,31 @@ } }; - appendEntries(this->impl->HeadersFileSets.InterfaceEntries.Entries); - appendEntries(this->impl->CxxModulesFileSets.InterfaceEntries.Entries); + for (auto const& fileSetType : fileSetTypes) { + appendEntries(GetFileSets(fileSetType.second)); + } return result; } +} + +std::vector<std::string> cmTarget::GetAllPrivateFileSets() const +{ + return RetrieveFileSetNames( + this->impl->FileSetTypes, + [](FileSetType const& fileSetType) -> std::vector<BT<std::string>> const& { + return fileSetType.SelfEntries.Entries; + }); +} + +std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const +{ + return RetrieveFileSetNames( + this->impl->FileSetTypes, + [](FileSetType const& fileSetType) -> std::vector<BT<std::string>> const& { + return fileSetType.InterfaceEntries.Entries; + }); +} bool cmTarget::HasFileSets() const { @@ -3280,12 +3358,12 @@ if (newResult) { // NEW policy found a configuration, OLD did not. cm::string_view newConfig = configFromSuffix(newSuffix); - std::string const err = cmStrCat( - cmPolicies::GetPolicyWarning(cmPolicies::CMP0200), - "\nConfiguration selection for imported target \"", this->GetName(), - "\" failed, but would select configuration \"", newConfig, - "\" under the NEW policy.\n"); - this->GetMakefile()->IssueMessage(MessageType::AUTHOR_WARNING, err); + this->GetMakefile()->IssuePolicyWarning( + cmPolicies::CMP0200, {}, + cmStrCat("Configuration selection for imported target \""_s, + this->GetName(), + "\" failed, but would select configuration \""_s, newConfig, + "\" under the NEW policy."_s)); } return false; @@ -3294,22 +3372,20 @@ cm::string_view oldConfig = configFromSuffix(suffix); if (!newResult) { // NEW policy did not find a configuration, OLD did. - std::string const err = - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0200), - "\nConfiguration selection for imported target \"", - this->GetName(), "\" selected configuration \"", oldConfig, - "\", but would fail under the NEW policy.\n"); - this->GetMakefile()->IssueMessage(MessageType::AUTHOR_WARNING, err); + this->GetMakefile()->IssuePolicyWarning( + cmPolicies::CMP0200, {}, + cmStrCat("Configuration selection for imported target \""_s, + this->GetName(), "\" selected configuration \""_s, oldConfig, + "\", but would fail under the NEW policy."_s)); } else if (suffix != newSuffix) { // OLD and NEW policies found different configurations. cm::string_view newConfig = configFromSuffix(newSuffix); - std::string const err = - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0200), - "\nConfiguration selection for imported target \"", - this->GetName(), "\" selected configuration \"", oldConfig, - "\", but would select configuration \"", newConfig, - "\" under the NEW policy.\n"); - this->GetMakefile()->IssueMessage(MessageType::AUTHOR_WARNING, err); + this->GetMakefile()->IssuePolicyWarning( + cmPolicies::CMP0200, {}, + cmStrCat("Configuration selection for imported target \""_s, + this->GetName(), "\" selected configuration \""_s, oldConfig, + "\", but would select configuration \""_s, newConfig, + "\" under the NEW policy."_s)); } return true; @@ -3504,7 +3580,8 @@ } // Get imported configurations, if specified. - if (cmValue iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS")) { + cmValue const iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS"); + if (!iconfigs.IsEmpty()) { cmList const availableConfigs{ cmSystemTools::UpperCase(*iconfigs) }; if (!mappedConfigs.empty()) {
diff --git a/Source/cmTarget.h b/Source/cmTarget.h index b275e47..f32cf31 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h
@@ -13,6 +13,7 @@ #include <vector> #include <cm/optional> +#include <cm/string_view> #include "cmAlgorithms.h" #include "cmListFileCache.h" @@ -22,9 +23,16 @@ #include "cmTargetLinkLibraryType.h" #include "cmValue.h" +namespace cm { +namespace FileSetMetadata { +enum class Visibility; +} +} + class cmCustomCommand; class cmFileSet; class cmFindPackageStack; +class cmGeneratorTarget; class cmGlobalGenerator; class cmInstallTargetGenerator; class cmMakefile; @@ -33,8 +41,6 @@ class cmTargetExport; class cmTargetInternals; -enum class cmFileSetVisibility; - /** \class cmTarget * \brief Represent a library or executable target loaded from a makefile. * @@ -301,8 +307,10 @@ cmBTStringRange GetIncludeDirectoriesEntries() const; cmBTStringRange GetCompileOptionsEntries() const; + cmBTStringRange GetImportedCxxModulesCompileOptionsEntries() const; cmBTStringRange GetCompileFeaturesEntries() const; + cmBTStringRange GetImportedCxxModulesCompileFeaturesEntries() const; cmBTStringRange GetCompileDefinitionsEntries() const; @@ -320,15 +328,15 @@ cmBTStringRange GetLinkInterfaceDirectEntries() const; cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const; + void CopyUsageEffects(cmGeneratorTarget const* gt, + std::string const& config); void CopyPolicyStatuses(cmTarget const* tgt); - void CopyImportedCxxModulesEntries(cmTarget const* tgt); - void CopyImportedCxxModulesProperties(cmTarget const* tgt); + void CopyCxxModulesEntries(cmTarget const* tgt); + void CopyCxxModulesProperties(cmTarget const* tgt); - cmBTStringRange GetHeaderSetsEntries() const; - cmBTStringRange GetCxxModuleSetsEntries() const; + cmBTStringRange GetFileSetsEntries(cm::string_view type) const; - cmBTStringRange GetInterfaceHeaderSetsEntries() const; - cmBTStringRange GetInterfaceCxxModuleSetsEntries() const; + cmBTStringRange GetInterfaceFileSetsEntries(cm::string_view type) const; enum class ImportArtifactMissingOk { @@ -347,15 +355,16 @@ cmFileSet const* GetFileSet(std::string const& name) const; cmFileSet* GetFileSet(std::string const& name); - std::pair<cmFileSet*, bool> GetOrCreateFileSet(std::string const& name, - std::string const& type, - cmFileSetVisibility vis); + std::pair<cmFileSet*, bool> GetOrCreateFileSet( + std::string const& name, std::string const& type, + cm::FileSetMetadata::Visibility vis); std::vector<std::string> GetAllFileSetNames() const; + std::vector<std::string> GetAllPrivateFileSets() const; std::vector<std::string> GetAllInterfaceFileSets() const; - static std::string GetFileSetsPropertyName(std::string const& type); - static std::string GetInterfaceFileSetsPropertyName(std::string const& type); + std::string GetFileSetsPropertyName(std::string const& type) const; + std::string GetInterfaceFileSetsPropertyName(std::string const& type) const; bool HasFileSets() const;
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index 2c68392..f6cc7bd 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx
@@ -11,7 +11,9 @@ #include <cm/optional> #include <cm/string_view> +#include <cmext/string_view> +#include "cmDiagnostics.h" #include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" @@ -320,8 +322,8 @@ static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left, int right) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, + mf.IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, cmStrCat( "Link library type specifier \"", LinkLibraryTypeNames[left], "\" is followed by specifier \"", LinkLibraryTypeNames[right], @@ -443,17 +445,14 @@ } if (this->WarnRemoteInterface) { - this->Makefile.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat( - cmPolicies::GetPolicyWarning(cmPolicies::CMP0079), "\nTarget\n ", - this->Target->GetName(), - "\nis not created in this " - "directory. For compatibility with older versions of CMake, link " - "library\n ", - lib, - "\nwill be looked up in the directory in which " - "the target was created rather than in this calling directory.")); + this->Makefile.IssuePolicyWarning( + cmPolicies::CMP0079, {}, + cmStrCat("Target\n "_s, this->Target->GetName(), + "\nis not created in this directory. For compatibility " + "with older versions of CMake, link library\n "_s, + lib, + "\nwill be looked up in the directory in which the target " + "was created rather than in this calling directory."_s)); } // Handle (additional) case where the command was called with PRIVATE /
diff --git a/Source/cmTargetPropertyEntry.cxx b/Source/cmTargetPropertyEntry.cxx new file mode 100644 index 0000000..86c2dd2 --- /dev/null +++ b/Source/cmTargetPropertyEntry.cxx
@@ -0,0 +1,109 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +#include "cmTargetPropertyEntry.h" + +#include <string> +#include <utility> + +#include <cm/memory> + +#include "cmGeneratorExpression.h" +#include "cmLinkItem.h" +#include "cmListFileCache.h" + +class cmake; + +namespace cm { +cmLinkItem TargetPropertyEntry::NoLinkItem; + +class TargetPropertyEntryString : public TargetPropertyEntry +{ +public: + TargetPropertyEntryString(BT<std::string> propertyValue, + cmLinkItem const& item = NoLinkItem) + : TargetPropertyEntry(item) + , PropertyValue(std::move(propertyValue)) + { + } + + std::string const& Evaluate(cm::GenEx::Context const&, + cmGeneratorTarget const*, + cmGeneratorExpressionDAGChecker*) const override + { + return this->PropertyValue.Value; + } + + cmListFileBacktrace GetBacktrace() const override + { + return this->PropertyValue.Backtrace; + } + std::string const& GetInput() const override + { + return this->PropertyValue.Value; + } + +private: + BT<std::string> PropertyValue; +}; + +class TargetPropertyEntryGenex : public TargetPropertyEntry +{ +public: + TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge, + cmLinkItem const& item = NoLinkItem) + : TargetPropertyEntry(item) + , ge(std::move(cge)) + { + } + + std::string const& Evaluate( + cm::GenEx::Context const& context, cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker) const override + { + return this->ge->Evaluate(context, dagChecker, headTarget); + } + + cmListFileBacktrace GetBacktrace() const override + { + return this->ge->GetBacktrace(); + } + + std::string const& GetInput() const override { return this->ge->GetInput(); } + + bool GetHadContextSensitiveCondition() const override + { + return this->ge->GetHadContextSensitiveCondition(); + } + +private: + std::unique_ptr<cmCompiledGeneratorExpression> const ge; +}; + +std::unique_ptr<TargetPropertyEntry> TargetPropertyEntry::Create( + cmake& cmakeInstance, const BT<std::string>& propertyValue, + bool evaluateForBuildsystem) +{ + if (cmGeneratorExpression::Find(propertyValue.Value) != std::string::npos) { + cmGeneratorExpression ge(cmakeInstance, propertyValue.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(propertyValue.Value); + cge->SetEvaluateForBuildsystem(evaluateForBuildsystem); + return std::unique_ptr<TargetPropertyEntry>( + cm::make_unique<TargetPropertyEntryGenex>(std::move(cge))); + } + + return std::unique_ptr<TargetPropertyEntry>( + cm::make_unique<TargetPropertyEntryString>(propertyValue)); +} + +TargetPropertyEntry::TargetPropertyEntry(cmLinkItem const& item) + : LinkItem(item) +{ +} + +bool TargetPropertyEntry::GetHadContextSensitiveCondition() const +{ + return false; +} +}
diff --git a/Source/cmTargetPropertyEntry.h b/Source/cmTargetPropertyEntry.h new file mode 100644 index 0000000..4f39224 --- /dev/null +++ b/Source/cmTargetPropertyEntry.h
@@ -0,0 +1,43 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include <memory> +#include <string> + +#include "cmListFileCache.h" + +class cmLinkItem; +class cmake; +class cmGeneratorTarget; +struct cmGeneratorExpressionDAGChecker; + +namespace cm { +namespace GenEx { +struct Context; +} + +class TargetPropertyEntry +{ +protected: + static cmLinkItem NoLinkItem; + +public: + TargetPropertyEntry(cmLinkItem const& item); + virtual ~TargetPropertyEntry() = default; + + static std::unique_ptr<TargetPropertyEntry> Create( + cmake& cmakeInstance, const BT<std::string>& propertyValue, + bool evaluateForBuildsystem = false); + + virtual std::string const& Evaluate( + cm::GenEx::Context const& context, cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker) const = 0; + + virtual cmListFileBacktrace GetBacktrace() const = 0; + virtual std::string const& GetInput() const = 0; + virtual bool GetHadContextSensitiveCondition() const; + + cmLinkItem const& LinkItem; +}; +}
diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx index 7032a56..042170f 100644 --- a/Source/cmTargetSourcesCommand.cxx +++ b/Source/cmTargetSourcesCommand.cxx
@@ -2,7 +2,6 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmTargetSourcesCommand.h" -#include <sstream> #include <utility> #include <cm/optional> @@ -12,6 +11,7 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" #include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGeneratorExpression.h" #include "cmList.h" #include "cmListFileCache.h" @@ -155,40 +155,30 @@ return content; } - bool issueMessage = true; - bool useAbsoluteContent = false; - std::ostringstream e; if (checkCmp0076 == CheckCMP0076::Yes) { switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0076)) { case cmPolicies::WARN: - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0076) << "\n"; - break; + if (isInterfaceContent == IsInterface::Yes) { + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0076, {}, + cmStrCat("An interface source of target \""_s, tgt->GetName(), + "\" has a relative path."_s)); + } else { + this->Makefile->IssuePolicyWarning( + cmPolicies::CMP0076, {}, + cmStrCat("A private source from a directory " + "other than that of target \""_s, + tgt->GetName(), "\" has a relative path."_s)); + } + CM_FALLTHROUGH; case cmPolicies::OLD: - issueMessage = false; + return content; + case cmPolicies::NEW: break; - case cmPolicies::NEW: { - issueMessage = false; - useAbsoluteContent = true; - break; - } } - } else { - issueMessage = false; - useAbsoluteContent = true; } - if (issueMessage) { - if (isInterfaceContent == IsInterface::Yes) { - e << "An interface source of target \"" << tgt->GetName() - << "\" has a relative path."; - } else { - e << "A private source from a directory other than that of target \"" - << tgt->GetName() << "\" has a relative path."; - } - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); - } - - return useAbsoluteContent ? absoluteContent : content; + return absoluteContent; } bool TargetSourcesImpl::HandleFileSetMode( @@ -227,61 +217,93 @@ this->SetError("FILE_SETs may not be added to custom targets"); return false; } - if (this->Target->IsFrameworkOnApple()) { - this->SetError("FILE_SETs may not be added to FRAMEWORK targets"); + + if (!args.Type.empty() && !cm::FileSetMetadata::IsKnownType(args.Type)) { + this->SetError( + cmStrCat("File set TYPE may only be \"", + cmJoin(cm::FileSetMetadata::GetKnownTypes(), "\", \""), '"')); + return false; + } + if (args.Type.empty() && args.FileSet[0] >= 'A' && args.FileSet[0] <= 'Z' && + !cm::FileSetMetadata::IsKnownType(args.FileSet)) { + this->SetError( + cmStrCat("FILE_SET names starting with a capital letter are reserved " + "for built-in file sets and may only be \"", + cmJoin(cm::FileSetMetadata::GetKnownTypes(), "\", \""), '"')); + return false; + } + if (!args.Type.empty() && args.FileSet[0] >= 'A' && args.FileSet[0] <= 'Z' && + args.Type != args.FileSet) { + this->SetError(cmStrCat("FILE_SET name starting with a capital letter " + "must match the TYPE name \"", + args.Type, '"')); return false; } bool const isDefault = args.Type == args.FileSet || - (args.Type.empty() && args.FileSet[0] >= 'A' && args.FileSet[0] <= 'Z'); - std::string type = isDefault ? args.FileSet : args.Type; + (args.Type.empty() && cm::FileSetMetadata::IsKnownType(args.FileSet)); - cmFileSetVisibility visibility = - cmFileSetVisibilityFromName(scope, this->Makefile); + if (!isDefault && !cm::FileSetMetadata::IsValidName(args.FileSet)) { + this->SetError("Non-default file set name must contain only letters, " + "numbers, and underscores, and must not start with a " + "capital letter or underscore"); + return false; + } + + std::string type = isDefault ? args.FileSet : args.Type; + cm::FileSetMetadata::Visibility visibility = + cm::FileSetMetadata::VisibilityFromName(scope, this->Makefile); + + if (this->Target->IsFrameworkOnApple() && + !cm::FileSetMetadata::IsFrameworkSupported(type)) { + this->SetError(cmStrCat(R"(FILE_SETs, of type ")", type, + R"(", may not be added to FRAMEWORK targets)")); + return false; + } auto fileSet = this->Target->GetOrCreateFileSet(args.FileSet, type, visibility); if (fileSet.second) { - if (!isDefault) { - if (!cmFileSet::IsValidName(args.FileSet)) { - this->SetError("Non-default file set name must contain only letters, " - "numbers, and underscores, and must not start with a " - "capital letter or underscore"); - return false; - } - } if (type.empty()) { this->SetError("Must specify a TYPE when creating file set"); return false; } - if (type != "HEADERS"_s && type != "CXX_MODULES"_s) { - this->SetError( - R"(File set TYPE may only be "HEADERS" or "CXX_MODULES")"); - return false; - } - if (cmFileSetVisibilityIsForSelf(visibility) && + if (cm::FileSetMetadata::VisibilityIsForSelf(visibility) && this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY && !this->Target->IsImported()) { - if (type == "CXX_MODULES"_s) { - this->SetError(R"(File set TYPE "CXX_MODULES" may not have "PUBLIC" )" - R"(or "PRIVATE" visibility on INTERFACE libraries.)"); + if (type == cm::FileSetMetadata::CXX_MODULES) { + this->SetError( + cmStrCat(R"(File set TYPE ")", cm::FileSetMetadata::CXX_MODULES, + R"(" may not have "PUBLIC" )" + R"(or "PRIVATE" scope on INTERFACE libraries.)")); return false; } } // FIXME(https://wg21.link/P3470): This condition can go // away when interface-only module units are a thing. - if (cmFileSetVisibilityIsForInterface(visibility) && - !cmFileSetVisibilityIsForSelf(visibility) && + if (cm::FileSetMetadata::VisibilityIsForInterface(visibility) && + !cm::FileSetMetadata::VisibilityIsForSelf(visibility) && !this->Target->IsImported()) { - if (type == "CXX_MODULES"_s) { - this->SetError( - R"(File set TYPE "CXX_MODULES" may not have "INTERFACE" visibility)"); + if (type == cm::FileSetMetadata::CXX_MODULES) { + this->SetError(cmStrCat(R"(File set TYPE ")", + cm::FileSetMetadata::CXX_MODULES, + R"(" may not have "INTERFACE" scope)")); return false; } } + if (cm::FileSetMetadata::VisibilityIsForSelf(visibility) && + this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY && + type == cm::FileSetMetadata::SOURCES) { + this->SetError( + cmStrCat(R"(File set TYPE ")", cm::FileSetMetadata::SOURCES, + R"(" may not have "PUBLIC" )" + R"(or "PRIVATE" scope on INTERFACE libraries.)")); + return false; + } + if (args.BaseDirs.empty()) { args.BaseDirs.emplace_back(this->Makefile->GetCurrentSourceDirectory()); } @@ -295,10 +317,11 @@ } if (visibility != fileSet.first->GetVisibility()) { - this->SetError( - cmStrCat("Scope ", scope, " for file set \"", args.FileSet, - "\" does not match original scope ", - cmFileSetVisibilityToName(fileSet.first->GetVisibility()))); + this->SetError(cmStrCat("Scope ", scope, " for file set \"", + args.FileSet, + "\" does not match original scope ", + cm::FileSetMetadata::VisibilityToName( + fileSet.first->GetVisibility()))); return false; } } @@ -315,16 +338,16 @@ if (!baseDirectories.empty()) { fileSet.first->AddDirectoryEntry( BT<std::string>(baseDirectories, this->Makefile->GetBacktrace())); - if (type == "HEADERS"_s) { + if (type == cm::FileSetMetadata::HEADERS) { for (auto const& dir : cmList{ baseDirectories }) { auto interfaceDirectoriesGenex = cmStrCat("$<BUILD_INTERFACE:", dir, '>'); - if (cmFileSetVisibilityIsForSelf(visibility)) { + if (cm::FileSetMetadata::VisibilityIsForSelf(visibility)) { this->Target->AppendProperty("INCLUDE_DIRECTORIES", interfaceDirectoriesGenex, this->Makefile->GetBacktrace()); } - if (cmFileSetVisibilityIsForInterface(visibility)) { + if (cm::FileSetMetadata::VisibilityIsForInterface(visibility)) { this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", interfaceDirectoriesGenex, this->Makefile->GetBacktrace());
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx index 1620b46..75a81b8 100644 --- a/Source/cmTest.cxx +++ b/Source/cmTest.cxx
@@ -2,6 +2,8 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmTest.h" +#include <utility> + #include "cmMakefile.h" #include "cmProperty.h" #include "cmState.h" @@ -33,6 +35,11 @@ this->Command = command; } +void cmTest::SetBuildDependencies(std::vector<std::string> deps) +{ + this->BuildDependencies = std::move(deps); +} + cmValue cmTest::GetProperty(std::string const& prop) const { cmValue retVal = this->Properties.GetPropertyValue(prop);
diff --git a/Source/cmTest.h b/Source/cmTest.h index 0e62d6e..ee7d935 100644 --- a/Source/cmTest.h +++ b/Source/cmTest.h
@@ -35,6 +35,12 @@ void SetCommand(std::vector<std::string> const& command); std::vector<std::string> const& GetCommand() const { return this->Command; } + void SetBuildDependencies(std::vector<std::string> deps); + std::vector<std::string> const& GetDependencies() const + { + return this->BuildDependencies; + } + //! Set/Get a property of this source file void SetProperty(std::string const& prop, cmValue value); void SetProperty(std::string const& prop, std::nullptr_t) @@ -61,10 +67,10 @@ bool GetOldStyle() const { return this->OldStyle; } void SetOldStyle(bool b) { this->OldStyle = b; } - /** Get if CMP0158 policy is NEW */ - bool GetCMP0158IsNew() const + /** Get the CMP0158 policy setting */ + cmPolicies::PolicyStatus GetCMP0158() const { - return this->PolicyStatusCMP0158 == cmPolicies::NEW; + return this->PolicyStatusCMP0158; } /** Get/Set the CMP0178 policy setting */ @@ -85,6 +91,7 @@ cmPropertyMap Properties; std::string Name; std::vector<std::string> Command; + std::vector<std::string> BuildDependencies; bool CommandExpandLists = false; bool OldStyle;
diff --git a/Source/cmTestDiscovery.h b/Source/cmTestDiscovery.h new file mode 100644 index 0000000..a8d899e --- /dev/null +++ b/Source/cmTestDiscovery.h
@@ -0,0 +1,32 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmArgumentParser.h" + +struct cmTestDiscoveryArgs : ArgumentParser::ParseResult +{ + ArgumentParser::NonEmpty<std::vector<std::string>> Command; + ArgumentParser::NonEmpty<std::vector<std::string>> DiscoveryArgs; + ArgumentParser::NonEmpty<std::string> DiscoveryMatch; + ArgumentParser::MaybeEmpty<std::vector<std::string>> DiscoveryProperties; + ArgumentParser::NonEmpty<std::string> TestName; + ArgumentParser::NonEmpty<std::vector<std::string>> TestArgs; + ArgumentParser::MaybeEmpty<std::vector<std::string>> TestProperties; +}; + +template <typename Args> +auto cmTestDiscoveryParser() -> cmArgumentParser<Args> +{ + static_assert(std::is_base_of<cmTestDiscoveryArgs, Args>::value, ""); + return cmArgumentParser<Args>{} + .Bind("COMMAND"_s, &Args::Command) + .Bind("DISCOVERY_ARGS"_s, &Args::DiscoveryArgs) + .Bind("DISCOVERY_MATCH"_s, &Args::DiscoveryMatch) + .Bind("DISCOVERY_PROPERTIES"_s, &Args::DiscoveryProperties) + .Bind("TEST_NAME"_s, &Args::TestName) + .Bind("TEST_ARGS"_s, &Args::TestArgs) + .Bind("TEST_PROPERTIES"_s, &Args::TestProperties); +}
diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index 83d798c..fc4b2f6 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx
@@ -2,26 +2,26 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmTestGenerator.h" -#include <algorithm> #include <cstddef> // IWYU pragma: keep -#include <iterator> #include <memory> #include <ostream> +#include <set> #include <string> #include <utility> #include <vector> #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" #include "cmList.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmPropertyMap.h" #include "cmRange.h" +#include "cmScriptGenerator.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -38,10 +38,9 @@ case cmPolicies::WARN: // Only warn if a forbidden character is used in the name. if (name.find_first_of("$[] #;\t\n\"\\") != std::string::npos) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0110), - "\nThe following name given to add_test() is invalid if " + mf.IssuePolicyWarning( + cmPolicies::CMP0110, {}, + cmStrCat("The following name given to add_test() is invalid if " "CMP0110 is not set or set to OLD:\n `", name, "´\n")); } @@ -56,18 +55,13 @@ } } -std::size_t countMaxConsecutiveEqualSigns(std::string const& name) +std::string TestName(cmTest* test) { - std::size_t max = 0; - auto startIt = find(name.begin(), name.end(), '='); - auto endIt = startIt; - for (; startIt != name.end(); startIt = find(endIt, name.end(), '=')) { - endIt = - find_if_not(startIt + 1, name.end(), [](char c) { return c == '='; }); - max = - std::max(max, static_cast<std::size_t>(std::distance(startIt, endIt))); + std::string name = test->GetName(); + if (needToQuoteTestName(*test->GetMakefile(), name)) { + name = cmScriptGenerator::Quote(name); } - return max; + return name; } } // End: anonymous namespace @@ -77,7 +71,7 @@ : cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations) , Test(test) { - this->ActionsPerConfig = !test->GetOldStyle(); + this->ActionsPerConfig = test == nullptr || !test->GetOldStyle(); this->TestGenerated = false; this->LG = nullptr; } @@ -91,7 +85,7 @@ bool cmTestGenerator::TestsForConfig(std::string const& config) { - return this->GeneratesForConfig(config); + return this->Test != nullptr && this->GeneratesForConfig(config); } cmTest* cmTestGenerator::GetTest() const @@ -99,10 +93,65 @@ return this->Test; } -void cmTestGenerator::GenerateScriptConfigs(std::ostream& os, Indent indent) +bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg, + std::string const& config, + BuildDependencies& info) { - // Create the tests. - this->cmScriptGenerator::GenerateScriptConfigs(os, indent); + if (this->Test == nullptr || + !cmGeneratorExpression::IsValidTargetName(this->Test->GetName()) || + cmGlobalGenerator::IsReservedTarget(this->Test->GetName())) { + return false; + } + + std::set<cmGeneratorTarget*> dependencies; + + // Get dependencies from generator expressions + cmGeneratorExpression ge(*this->Test->GetMakefile()->GetCMakeInstance(), + this->Test->GetBacktrace()); + for (std::string const& arg : this->Test->GetCommand()) { + auto parsed = ge.Parse(arg); + parsed->Evaluate(lg, config); + for (cmGeneratorTarget* dep : parsed->GetTargets()) { + if (dep && !dep->IsImported()) { + dependencies.insert(dep); + } + } + } + + // Add target executed by test + if (!this->Test->GetCommand().empty()) { + std::string exe = this->Test->GetCommand().front(); + cmGeneratorTarget* target = lg->FindGeneratorTargetToUse(exe); + if (target && target->GetType() == cmStateEnums::EXECUTABLE && + !target->IsImported()) { + dependencies.insert(target); + } + } + + // Add dependencies from BUILD_DEPENDS keyword + for (auto const& depName : this->Test->GetDependencies()) { + if (depName.empty()) { + continue; + } + cmGeneratorTarget* depTarget = lg->FindGeneratorTargetToUse(depName); + if (!depTarget) { + info.Files.push_back(depName); + continue; + } + if (depTarget->IsImported()) { + lg->GetMakefile()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Test \"", this->Test->GetName(), "\" DEPENDS target \"", + depName, "\" which is imported and cannot be built."), + this->Test->GetBacktrace()); + return false; + } + dependencies.insert(depTarget); + } + + info.Targets.insert(info.Targets.end(), dependencies.begin(), + dependencies.end()); + return true; } void cmTestGenerator::GenerateScriptActions(std::ostream& os, Indent indent) @@ -119,39 +168,19 @@ } } -void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, - std::string const& config, - Indent indent) +void cmTestGenerator::GenerateCommand(std::ostream& os, + std::vector<std::string> const& command, + std::string const& config, bool expand, + cmGeneratorExpression& ge, + cmPolicies::PolicyStatus cmp0158, + cmPolicies::PolicyStatus cmp0178) { - this->TestGenerated = true; - - // Set up generator expression evaluation context. - cmGeneratorExpression ge(*this->Test->GetMakefile()->GetCMakeInstance(), - this->Test->GetBacktrace()); - - // Determine if policy CMP0110 is set to NEW. - bool const quote_test_name = - needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); - // Determine the number of equal-signs needed for quoting test name with - // [==[...]==] syntax. - std::string const equalSigns( - 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); - - // Start the test command. - if (quote_test_name) { - os << indent << "add_test([" << equalSigns << "[" << this->Test->GetName() - << "]" << equalSigns << "] "; - } else { - os << indent << "add_test(" << this->Test->GetName() << " "; - } - // Evaluate command line arguments cmList argv{ - this->EvaluateCommandLineArguments(this->Test->GetCommand(), ge, config), + this->EvaluateCommandLineArguments(command, ge, config), // Expand arguments if COMMAND_EXPAND_LISTS is set - this->Test->GetCommandExpandLists() ? cmList::ExpandElements::Yes - : cmList::ExpandElements::No, - cmList::EmptyElements::Yes + expand ? cmList::ExpandElements::Yes : cmList::ExpandElements::No, + cmList::EmptyElements::Yes, }; // Expanding lists on an empty command may have left it empty if (argv.empty()) { @@ -166,42 +195,36 @@ // Use the target file on disk. exe = target->GetFullPath(config); - auto addLauncher = [this, &config, &ge, &os, - target](std::string const& propertyName) { + auto addLauncher = [&](std::string const& propertyName) { cmValue launcher = target->GetProperty(propertyName); if (!cmNonempty(launcher)) { return; } auto const propVal = ge.Parse(*launcher)->Evaluate(this->LG, config); cmList launcherWithArgs(propVal, cmList::ExpandElements::Yes, - this->Test->GetCMP0178() == cmPolicies::NEW + cmp0178 == cmPolicies::NEW ? cmList::EmptyElements::Yes : cmList::EmptyElements::No); if (!launcherWithArgs.empty() && !launcherWithArgs[0].empty()) { - if (this->Test->GetCMP0178() == cmPolicies::WARN) { + if (cmp0178 == cmPolicies::WARN) { cmList argsWithEmptyValuesPreserved( propVal, cmList::ExpandElements::Yes, cmList::EmptyElements::Yes); if (launcherWithArgs != argsWithEmptyValuesPreserved) { - this->Test->GetMakefile()->IssueMessage( - MessageType::AUTHOR_WARNING, + this->LG->GetMakefile()->IssuePolicyWarning( + cmPolicies::CMP0178, cmStrCat("The ", propertyName, " property of target '", target->GetName(), "' contains empty list items. Those empty items are " "being silently discarded to preserve backward " - "compatibility.\n", - cmPolicies::GetPolicyWarning(cmPolicies::CMP0178))); + "compatibility.")); } } std::string launcherExe(launcherWithArgs[0]); cmSystemTools::ConvertToUnixSlashes(launcherExe); - os << cmOutputConverter::EscapeForCMake(launcherExe) << " "; + os << cmScriptGenerator::Quote(launcherExe) << " "; for (std::string const& arg : cmMakeRange(launcherWithArgs).advance(1)) { - if (arg.empty()) { - os << "\"\" "; - } else { - os << cmOutputConverter::EscapeForCMake(arg) << " "; - } + os << cmScriptGenerator::Quote(arg) << " "; } } }; @@ -210,7 +233,7 @@ addLauncher("TEST_LAUNCHER"); // Prepend with the emulator when cross compiling if required. - if (!this->GetTest()->GetCMP0158IsNew() || + if (cmp0158 != cmPolicies::NEW || this->LG->GetMakefile()->IsOn("CMAKE_CROSSCOMPILING")) { addLauncher("CROSSCOMPILING_EMULATOR"); } @@ -220,49 +243,46 @@ } // Generate the command line with full escapes. - os << cmOutputConverter::EscapeForCMake(exe); + os << cmScriptGenerator::Quote(exe); for (auto const& arg : cmMakeRange(argv).advance(1)) { - os << " " << cmOutputConverter::EscapeForCMake(arg); + os << " " << cmScriptGenerator::Quote(arg); } +} - // Finish the test command. +void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, + std::string const& config, + Indent indent) +{ + this->TestGenerated = true; + + // Set up generator expression evaluation context. + cmGeneratorExpression ge(*this->Test->GetMakefile()->GetCMakeInstance(), + this->Test->GetBacktrace()); + + auto const test_name = TestName(this->Test); + os << indent << "add_test(" << test_name << ' '; + this->GenerateCommand( + os, this->Test->GetCommand(), config, this->Test->GetCommandExpandLists(), + ge, this->GetTest()->GetCMP0158(), this->Test->GetCMP0178()); os << ")\n"; // Output properties for the test. - if (quote_test_name) { - os << indent << "set_tests_properties([" << equalSigns << "[" - << this->Test->GetName() << "]" << equalSigns << "] PROPERTIES "; - } else { - os << indent << "set_tests_properties(" << this->Test->GetName() - << " PROPERTIES "; - } + os << indent << "set_tests_properties(" << test_name << " PROPERTIES "; for (auto const& i : this->Test->GetProperties().GetList()) { os << " " << i.first << " " - << cmOutputConverter::EscapeForCMake( + << cmScriptGenerator::Quote( ge.Parse(i.second)->Evaluate(this->LG, config)); } - this->GenerateInternalProperties(os); + + os << ' '; + this->GenerateBacktrace(os, this->Test->GetBacktrace()); os << ")\n"; } void cmTestGenerator::GenerateScriptNoConfig(std::ostream& os, Indent indent) { - // Determine if policy CMP0110 is set to NEW. - bool const quote_test_name = - needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); - // Determine the number of equal-signs needed for quoting test name with - // [==[...]==] syntax. - std::string const equalSigns( - 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); - - if (quote_test_name) { - os << indent << "add_test([" << equalSigns << "[" << this->Test->GetName() - << "]" << equalSigns << "] NOT_AVAILABLE)\n"; - } else { - os << indent << "add_test(" << this->Test->GetName() - << " NOT_AVAILABLE)\n"; - } + os << indent << "add_test(" << TestName(this->Test) << " NOT_AVAILABLE)\n"; } bool cmTestGenerator::NeedsScriptNoConfig() const @@ -277,27 +297,14 @@ { this->TestGenerated = true; - // Determine if policy CMP0110 is set to NEW. - bool const quote_test_name = - needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); - // Determine the number of equal-signs needed for quoting test name with - // [==[...]==] syntax. - std::string const equalSigns( - 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); + auto const test_name = TestName(this->Test); // Get the test command line to be executed. std::vector<std::string> const& command = this->Test->GetCommand(); std::string exe = command[0]; cmSystemTools::ConvertToUnixSlashes(exe); - if (quote_test_name) { - fout << indent << "add_test([" << equalSigns << "[" - << this->Test->GetName() << "]" << equalSigns << "] \"" << exe - << "\""; - } else { - fout << indent << "add_test(" << this->Test->GetName() << " \"" << exe - << "\""; - } + fout << indent << "add_test(" << test_name << " \"" << exe << "\""; for (std::string const& arg : cmMakeRange(command).advance(1)) { // Just double-quote all arguments so they are re-parsed @@ -317,31 +324,23 @@ fout << ")\n"; // Output properties for the test. - if (quote_test_name) { - fout << indent << "set_tests_properties([" << equalSigns << "[" - << this->Test->GetName() << "]" << equalSigns << "] PROPERTIES "; - } else { - fout << indent << "set_tests_properties(" << this->Test->GetName() - << " PROPERTIES "; - } + fout << indent << "set_tests_properties(" << test_name << " PROPERTIES "; for (auto const& i : this->Test->GetProperties().GetList()) { - fout << " " << i.first << " " - << cmOutputConverter::EscapeForCMake(i.second); + fout << " " << i.first << " " << cmScriptGenerator::Quote(i.second); } - this->GenerateInternalProperties(fout); + fout << ' '; + this->GenerateBacktrace(fout, this->Test->GetBacktrace()); fout << ")\n"; } -void cmTestGenerator::GenerateInternalProperties(std::ostream& os) +void cmTestGenerator::GenerateBacktrace(std::ostream& os, + cmListFileBacktrace bt) { - cmListFileBacktrace bt = this->Test->GetBacktrace(); if (bt.Empty()) { return; } - os << " " - << "_BACKTRACE_TRIPLES" - << " \""; + os << "_BACKTRACE_TRIPLES \""; bool prependTripleSeparator = false; while (!bt.Empty()) {
diff --git a/Source/cmTestGenerator.h b/Source/cmTestGenerator.h index b728ba3..0b4feca 100644 --- a/Source/cmTestGenerator.h +++ b/Source/cmTestGenerator.h
@@ -8,9 +8,12 @@ #include <string> #include <vector> +#include "cmPolicies.h" #include "cmScriptGenerator.h" +class cmListFileBacktrace; class cmGeneratorExpression; +class cmGeneratorTarget; class cmLocalGenerator; class cmTest; @@ -21,6 +24,12 @@ class cmTestGenerator : public cmScriptGenerator { public: + struct BuildDependencies + { + std::vector<cmGeneratorTarget*> Targets; + std::vector<std::string> Files; + }; + cmTestGenerator(cmTest* test, std::vector<std::string> const& configurations = std::vector<std::string>()); @@ -30,6 +39,8 @@ cmTestGenerator& operator=(cmTestGenerator const&) = delete; void Compute(cmLocalGenerator* lg); + bool GetBuildDependencies(cmLocalGenerator* lg, std::string const& config, + BuildDependencies& deps); /** Test if this generator installs the test for a given configuration. */ bool TestsForConfig(std::string const& config); @@ -37,13 +48,18 @@ cmTest* GetTest() const; private: - void GenerateInternalProperties(std::ostream& os); std::vector<std::string> EvaluateCommandLineArguments( std::vector<std::string> const& argv, cmGeneratorExpression& ge, std::string const& config) const; protected: - void GenerateScriptConfigs(std::ostream& os, Indent indent) override; + void GenerateBacktrace(std::ostream& os, cmListFileBacktrace bt); + void GenerateCommand(std::ostream& os, + std::vector<std::string> const& command, + std::string const& config, bool expand, + cmGeneratorExpression& ge, + cmPolicies::PolicyStatus cmp0158 = cmPolicies::NEW, + cmPolicies::PolicyStatus cmp0178 = cmPolicies::NEW); void GenerateScriptActions(std::ostream& os, Indent indent) override; void GenerateScriptForConfig(std::ostream& os, std::string const& config, Indent indent) override;
diff --git a/Source/cmTransformDepfile.cxx b/Source/cmTransformDepfile.cxx index e043a58..63d2fe4 100644 --- a/Source/cmTransformDepfile.cxx +++ b/Source/cmTransformDepfile.cxx
@@ -26,6 +26,12 @@ { for (auto c : filename) { switch (c) { + case '$': + fout << "$$"; + break; + case '#': + fout << "\\#"; + break; case ' ': fout << "\\ "; break;
diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx index 05d9361..44fd852 100644 --- a/Source/cmUVProcessChain.cxx +++ b/Source/cmUVProcessChain.cxx
@@ -159,6 +159,13 @@ return *this; } +cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetEnvironment( + std::vector<std::string> env) +{ + this->Environment = std::move(env); + return *this; +} + cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetDetached() { this->Detached = true; @@ -370,6 +377,16 @@ options.cwd = this->Builder->WorkingDirectory.c_str(); } + auto env = std::vector<char const*>{}; + if (!this->Builder->Environment.empty()) { + env.reserve(this->Builder->Environment.size() + 1); + for (auto const& var : this->Builder->Environment) { + env.push_back(var.c_str()); + } + env.push_back(nullptr); + options.env = const_cast<char**>(env.data()); + } + std::array<uv_stdio_container_t, 3> stdio; if (first) { stdio[0] = this->InputStreamData.Stdio;
diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h index 2116eb2..2b9671f 100644 --- a/Source/cmUVProcessChain.h +++ b/Source/cmUVProcessChain.h
@@ -38,6 +38,7 @@ cmUVProcessChainBuilder& SetExternalStream(Stream stdio, int fd); cmUVProcessChainBuilder& SetExternalStream(Stream stdio, FILE* stream); cmUVProcessChainBuilder& SetWorkingDirectory(std::string dir); + cmUVProcessChainBuilder& SetEnvironment(std::vector<std::string> env); cmUVProcessChainBuilder& SetDetached(); uv_loop_t* GetLoop() const; @@ -68,6 +69,7 @@ std::array<StdioConfiguration, 3> Stdio; std::vector<ProcessConfiguration> Processes; std::string WorkingDirectory; + std::vector<std::string> Environment; bool MergedBuiltinStreams = false; bool Detached = false; uv_loop_t* Loop = nullptr;
diff --git a/Source/cmVariableWatch.cxx b/Source/cmVariableWatch.cxx index 5ad00fe..b46bfa3 100644 --- a/Source/cmVariableWatch.cxx +++ b/Source/cmVariableWatch.cxx
@@ -7,16 +7,16 @@ #include <utility> #include <vector> -std::string const& cmVariableWatch::GetAccessAsString(int access_type) +std::string const& cmVariableWatch::GetAccessAsString(AccessType accessType) { static std::array<std::string, 6> const cmVariableWatchAccessStrings = { { "READ_ACCESS", "UNKNOWN_READ_ACCESS", "UNKNOWN_DEFINED_ACCESS", "MODIFIED_ACCESS", "REMOVED_ACCESS", "NO_ACCESS" } }; - if (access_type < 0 || access_type >= cmVariableWatch::NO_ACCESS) { - access_type = cmVariableWatch::NO_ACCESS; + if (accessType >= cmVariableWatch::NO_ACCESS) { + accessType = cmVariableWatch::NO_ACCESS; } - return cmVariableWatchAccessStrings[access_type]; + return cmVariableWatchAccessStrings[accessType]; } cmVariableWatch::cmVariableWatch() = default; @@ -63,7 +63,8 @@ } bool cmVariableWatch::VariableAccessed(std::string const& variable, - int access_type, char const* newValue, + AccessType accessType, + char const* newValue, cmMakefile const* mf) const { auto mit = this->WatchMap.find(variable); @@ -77,7 +78,7 @@ // lockable, and so this ensures we don't attempt to call into freed // memory if (auto it = weak_it.lock()) { - it->Method(variable, access_type, it->ClientData, newValue, mf); + it->Method(variable, accessType, it->ClientData, newValue, mf); } } return true;
diff --git a/Source/cmVariableWatch.h b/Source/cmVariableWatch.h index ffd7192..0d8fff5 100644 --- a/Source/cmVariableWatch.h +++ b/Source/cmVariableWatch.h
@@ -19,8 +19,21 @@ class cmVariableWatch { public: - using WatchMethod = void (*)(std::string const&, int, void*, char const*, - cmMakefile const*); + /** + * Different access types. + */ + enum AccessType : unsigned + { + VARIABLE_READ_ACCESS, + UNKNOWN_VARIABLE_READ_ACCESS, + UNKNOWN_VARIABLE_DEFINED_ACCESS, + VARIABLE_MODIFIED_ACCESS, + VARIABLE_REMOVED_ACCESS, + NO_ACCESS + }; + + using WatchMethod = void (*)(std::string const&, AccessType, void*, + char const*, cmMakefile const*); using DeleteData = void (*)(void*); cmVariableWatch(); @@ -37,26 +50,13 @@ /** * This method is called when variable is accessed */ - bool VariableAccessed(std::string const& variable, int access_type, + bool VariableAccessed(std::string const& variable, AccessType accessType, char const* newValue, cmMakefile const* mf) const; /** - * Different access types. - */ - enum - { - VARIABLE_READ_ACCESS, - UNKNOWN_VARIABLE_READ_ACCESS, - UNKNOWN_VARIABLE_DEFINED_ACCESS, - VARIABLE_MODIFIED_ACCESS, - VARIABLE_REMOVED_ACCESS, - NO_ACCESS - }; - - /** * Return the access as string */ - static std::string const& GetAccessAsString(int access_type); + static std::string const& GetAccessAsString(AccessType accessType); protected: struct Pair
diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx index f5d47c6..7607bd3 100644 --- a/Source/cmVariableWatchCommand.cxx +++ b/Source/cmVariableWatchCommand.cxx
@@ -10,6 +10,7 @@ #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmValue.h" @@ -25,20 +26,19 @@ std::string Command; }; -void cmVariableWatchCommandVariableAccessed(std::string const& variable, - int access_type, void* client_data, - char const* newValue, - cmMakefile const* mf) +void cmVariableWatchCommandVariableAccessed( + std::string const& variable, cmVariableWatch::AccessType accessType, + void* clientData, char const* newValue, cmMakefile const* mf) { cmVariableWatchCallbackData* data = - static_cast<cmVariableWatchCallbackData*>(client_data); + static_cast<cmVariableWatchCallbackData*>(clientData); if (data->InCallback) { return; } data->InCallback = true; - auto accessString = cmVariableWatch::GetAccessAsString(access_type); + auto accessString = cmVariableWatch::GetAccessAsString(accessType); /// Ultra bad!! cmMakefile* makefile = const_cast<cmMakefile*>(mf); @@ -57,6 +57,17 @@ { *currentListFile, cmListFileArgument::Quoted, fakeLineNo }, { stack, cmListFileArgument::Quoted, fakeLineNo } }; + { + cmPolicies::PolicyStatus cmp0219 = + makefile->CheckCMP0219(data->Command, newLFFArgs); + if (cmp0219 == cmPolicies::NEW) { + for (cmListFileArgument& arg : newLFFArgs) { + cmSystemTools::ReplaceString(arg.Value, "\\", "\\\\"); + } + } else if (cmp0219 == cmPolicies::WARN) { + makefile->IssueCMP0219Warning(data->Command, newLFFArgs); + } + } cmListFileFunction newLFF{ data->Command, fakeLineNo, fakeLineNo, std::move(newLFFArgs) };
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index d617dc69..4344bc3 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -27,9 +27,12 @@ #include "cmCryptoHash.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" -#include "cmFileSet.h" +#include "cmDiagnostics.h" +#include "cmFileSetMetadata.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorFileSet.h" +#include "cmGeneratorFileSets.h" #include "cmGeneratorOptions.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -290,13 +293,15 @@ this->Android = gg->TargetsAndroid(); this->WindowsKernelMode = gg->TargetsWindowsKernelModeDriver(); auto scanProp = target->GetProperty("CXX_SCAN_FOR_MODULES"); + bool const scanDepsSupported = gg->IsScanDependenciesSupported(); for (auto const& config : this->Configurations) { if (scanProp.IsSet()) { - this->ScanSourceForModuleDependencies[config] = scanProp.IsOn(); - } else { this->ScanSourceForModuleDependencies[config] = + scanDepsSupported && scanProp.IsOn(); + } else { + this->ScanSourceForModuleDependencies[config] = scanDepsSupported && target->NeedCxxDyndep(config) == - cmGeneratorTarget::CxxModuleSupport::Enabled; + cmGeneratorTarget::CxxModuleSupport::Enabled; } } for (unsigned int& version : this->NsightTegraVersion) { @@ -395,7 +400,7 @@ cmStrCat("The C# target \"", this->GeneratorTarget->GetName(), "\" is of type STATIC_LIBRARY. This is discouraged (and may be " "disabled in future). Make it a SHARED library instead."); - this->Makefile->IssueMessage(MessageType::DEPRECATION_WARNING, message); + this->Makefile->IssueDiagnostic(cmDiagnostics::CMD_DEPRECATED, message); } if (this->Android && @@ -1839,7 +1844,7 @@ for (std::string const& d : ccg.GetDepends()) { std::string dep; - if (lg->GetRealDependency(d, c, dep)) { + if (lg->GetRealDependency(d, c, dep, command.GetCMP0212Status())) { if (!unique_inputs.insert(dep).second) { // already listed continue; @@ -2631,6 +2636,9 @@ case cmGeneratorTarget::SourceKindResx: this->ResxObjs.push_back(si.Source); break; + case cmGeneratorTarget::SourceKindRustMainCrateRoot: + tool = "None"; + break; case cmGeneratorTarget::SourceKindXaml: this->XamlObjs.push_back(si.Source); break; @@ -2688,7 +2696,12 @@ if (useNativeUnityBuild) { e2.Attribute( "IncludeInUnityFile", - si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") + ((fs && + (!cm::FileSetMetadata::GetAttributes(fs->GetType()) + .contains( + cm::FileSetMetadata::FileSetAttributes::UnityBuild) || + fs->GetProperty("SKIP_UNITY_BUILD_INCLUSION").IsOn())) || + si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION")) ? "false" : "true"); e2.Attribute("CustomUnityFile", "true"); @@ -2699,14 +2712,24 @@ } else { // Visual Studio versions prior to 2017 15.8 do not know about unity // builds, thus we exclude the files already part of unity sources. - if (!si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION")) { + if (!((fs && + (!cm::FileSetMetadata::GetAttributes(fs->GetType()) + .contains( + cm::FileSetMetadata::FileSetAttributes::UnityBuild) || + fs->GetProperty("SKIP_UNITY_BUILD_INCLUSION").IsOn())) || + si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION"))) { exclude_configs = all_configs; } } } if (haveUnityBuild && strcmp(tool, "CudaCompile") == 0 && si.Source->GetProperty("UNITY_SOURCE_FILE")) { - if (!si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION")) { + if (!((fs && + (!cm::FileSetMetadata::GetAttributes(fs->GetType()) + .contains( + cm::FileSetMetadata::FileSetAttributes::UnityBuild) || + fs->GetProperty("SKIP_UNITY_BUILD_INCLUSION").IsOn())) || + si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION"))) { exclude_configs = all_configs; } } @@ -2714,15 +2737,16 @@ if (si.Kind == cmGeneratorTarget::SourceKindObjectSource || si.Kind == cmGeneratorTarget::SourceKindUnityBatched) { this->OutputSourceSpecificFlags(e2, si.Source); - } else if (fs && fs->GetType() == "CXX_MODULES"_s) { + } else if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) { 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.")); + "\nin a \"FILE_SET TYPE ", cm::FileSetMetadata::CXX_MODULES, + "\" but it is not scheduled for compilation.")); } - if (si.Source->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) { + if ((fs && fs->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + si.Source->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) { e2.Element("PrecompiledHeader", "NotUsing"); } if (!isCSharp && !exclude_configs.empty()) { @@ -2756,13 +2780,13 @@ } this->FinishWritingSource(e2, toolSettings); - } else if (fs && fs->GetType() == "CXX_MODULES"_s) { + } else if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) { 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.")); + "\nin a \"FILE_SET TYPE ", cm::FileSetMetadata::CXX_MODULES, + "\" but it is not scheduled for compilation.")); } } @@ -2882,14 +2906,36 @@ configDefines += *ccdefs; } + // lookup for the associated file set, if any. + auto const* fileSet = + this->GeneratorTarget->GetFileSetForSource(config, source); + bool fileSetBelongsToTarget = false; + std::vector<std::string> fsOptions; + std::vector<std::string> fsDefines; + std::vector<std::string> fsIncludes; + + if (fileSet) { + fileSetBelongsToTarget = fileSet->BelongsTo(this->GeneratorTarget); + fsOptions = + cm::remove_BT(fileSetBelongsToTarget + ? fileSet->GetCompileOptions(config, lang) + : fileSet->GetInterfaceCompileOptions(config, lang)); + fsDefines = cm::remove_BT( + fileSetBelongsToTarget + ? fileSet->GetCompileDefinitions(config, lang) + : fileSet->GetInterfaceCompileDefinitions(config, lang)); + fsIncludes = cm::remove_BT( + fileSetBelongsToTarget + ? fileSet->GetIncludeDirectories(config, lang) + : fileSet->GetInterfaceIncludeDirectories(config, lang)); + } + bool const shouldScanForModules = lang == "CXX"_s && this->GeneratorTarget->NeedDyndepForSource(lang, config, source); - auto const* fs = - this->GeneratorTarget->GetFileSetForSource(config, source); char const* compileAsPerConfig = compileAs; - if (fs && fs->GetType() == "CXX_MODULES"_s) { + if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) { if (lang == "CXX"_s) { - if (fs->GetType() == "CXX_MODULES"_s) { + if (fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) { isCppModule = true; if (shouldScanForModules && this->GlobalGenerator->IsScanDependenciesSupported()) { @@ -2908,7 +2954,7 @@ cmStrCat( "Target \"", this->GeneratorTarget->Target->GetName(), "\" contains the source\n ", source->GetFullPath(), - "\nin a file set of type \"", fs->GetType(), + "\nin a file set of type \"", fileSet->GetType(), R"(" but the source is not classified as a "CXX" source.)")); } } @@ -2922,8 +2968,9 @@ this->GeneratorTarget->GetLinkerLanguage(config); std::string const& pchSource = this->GeneratorTarget->GetPchSource(config, lang); - bool const skipPCH = - pchSource.empty() || sf.GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS"); + bool const skipPCH = pchSource.empty() || + (fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || + sf.GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS"); bool const makePCH = (sf.GetFullPath() == pchSource); bool const useSharedPCH = !skipPCH && (lang == linkLanguage); bool const useDifferentLangPCH = !skipPCH && (lang != linkLanguage); @@ -2934,9 +2981,10 @@ // if we have flags or defines for this config then // use them - if (!flags.empty() || !options.empty() || !configDefines.empty() || - !includes.empty() || compileAsPerConfig || noWinRT || - !options.empty() || needsPCHFlags || + if (!flags.empty() || !options.empty() || !fsOptions.empty() || + !configDefines.empty() || !fsDefines.empty() || !includes.empty() || + !fsIncludes.empty() || compileAsPerConfig || noWinRT || + needsPCHFlags || (shouldScanForModules != this->ScanSourceForModuleDependencies[config])) { cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; @@ -2966,8 +3014,9 @@ if (compileAsPerConfig) { clOptions.AddFlag("CompileAs", compileAsPerConfig); } - if (shouldScanForModules != - this->ScanSourceForModuleDependencies[config]) { + if (gg->IsScanDependenciesSupported() && + shouldScanForModules != + this->ScanSourceForModuleDependencies[config]) { clOptions.AddFlag("ScanSourceForModuleDependencies", shouldScanForModules ? "true" : "false"); } @@ -3009,8 +3058,8 @@ } } + std::string expandedOptions; if (!options.empty()) { - std::string expandedOptions; if (configDependentOptions) { this->LocalGenerator->AppendCompileOptions( expandedOptions, @@ -3018,6 +3067,12 @@ } else { this->LocalGenerator->AppendCompileOptions(expandedOptions, options); } + } + // Add flags from file set properties. + if (!fsOptions.empty()) { + this->LocalGenerator->AppendCompileOptions(expandedOptions, fsOptions); + } + if (!expandedOptions.empty()) { clOptions.Parse(expandedOptions); } if (clOptions.HasFlag("DisableSpecificWarnings")) { @@ -3033,7 +3088,16 @@ } else { clOptions.AddDefines(configDefines); } + // Add defines from file set properties. + if (!fsDefines.empty()) { + clOptions.AddDefines(fsDefines); + } std::vector<std::string> includeList; + // Add include directories from file set properties. + if (!fsIncludes.empty()) { + this->LocalGenerator->AppendIncludeDirectories(includeList, fsIncludes, + *source); + } if (configDependentIncludes) { this->LocalGenerator->AppendIncludeDirectories( includeList,
diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx index 43d8975..8a3e59a 100644 --- a/Source/cmWhileCommand.cxx +++ b/Source/cmWhileCommand.cxx
@@ -18,9 +18,7 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" -#include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmake.h" class cmWhileFunctionBlocker : public cmFunctionBlocker { @@ -117,19 +115,8 @@ if (!errorString.empty() && !enforceError) { // This error should only be enforced if CMP0130 is NEW. - switch (mf.GetPolicyStatus(cmPolicies::CMP0130)) { - case cmPolicies::WARN: - // Convert the error to a warning and enforce it. - messageType = MessageType::AUTHOR_WARNING; - enforceError = true; - break; - case cmPolicies::OLD: - // OLD behavior is to silently ignore the error. - break; - case cmPolicies::NEW: - // NEW behavior is to enforce the error. - enforceError = true; - break; + if (mf.GetPolicyStatus(cmPolicies::CMP0130) != cmPolicies::OLD) { + enforceError = true; } } @@ -142,12 +129,12 @@ err += "\n"; err += errorString; if (mf.GetPolicyStatus(cmPolicies::CMP0130) == cmPolicies::WARN) { - err = - cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0130), '\n', err); - } - mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT); - if (messageType == MessageType::FATAL_ERROR) { - cmSystemTools::SetFatalErrorOccurred(); + mf.IssuePolicyWarning(cmPolicies::CMP0130, {}, err, whileBT); + } else { + mf.IssueMessage(messageType, err, whileBT); + if (messageType == MessageType::FATAL_ERROR) { + cmSystemTools::SetFatalErrorOccurred(); + } } }
diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 3b311e0..594d504 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx
@@ -38,16 +38,9 @@ #include "cmBuildOptions.h" #include "cmCMakePath.h" #include "cmCMakePresetsGraph.h" +#include "cmCacheDocumentationTable.h" #include "cmCommandLineArgument.h" #include "cmCommands.h" -#ifdef CMake_ENABLE_DEBUGGER -# include "cmDebuggerAdapter.h" -# ifdef _WIN32 -# include "cmDebuggerWindowsPipeConnection.h" -# else //!_WIN32 -# include "cmDebuggerPosixPipeConnection.h" -# endif //_WIN32 -#endif #include "cmDocumentation.h" #include "cmDocumentationEntry.h" #include "cmDuration.h" @@ -57,18 +50,13 @@ #include "cmGlobCacheEntry.h" // IWYU pragma: keep #include "cmGlobalGenerator.h" #include "cmGlobalGeneratorFactory.h" +#include "cmJSONState.h" #include "cmLinkLineComputer.h" +#include "cmList.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#if !defined(CMAKE_BOOTSTRAP) -# include "cmMakefileProfilingData.h" -#endif -#include "cmJSONState.h" -#include "cmList.h" #include "cmMessenger.h" -#ifndef CMAKE_BOOTSTRAP -# include "cmSarifLog.h" -#endif +#include "cmPolicies.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStringAlgorithms.h" @@ -80,17 +68,29 @@ #include "cmVersionConfig.h" #include "cmWorkingDirectory.h" +#ifdef CMake_ENABLE_DEBUGGER +# include "cmDebuggerAdapter.h" +# ifdef _WIN32 +# include "cmDebuggerWindowsPipeConnection.h" +# else //!_WIN32 +# include "cmDebuggerPosixPipeConnection.h" +# endif //_WIN32 +#endif + #if !defined(CMAKE_BOOTSTRAP) # include <unordered_map> # include <cm3p/curl/curl.h> # include <cm3p/json/writer.h> +# include "cmCMakePresetsArgs.h" # include "cmConfigureLog.h" # include "cmFileAPI.h" # include "cmGraphVizWriter.h" # include "cmInstrumentation.h" # include "cmInstrumentationQuery.h" +# include "cmMakefileProfilingData.h" +# include "cmSarifLog.h" # include "cmVariableWatch.h" #endif @@ -168,15 +168,39 @@ cmCommandLineArgument<bool(std::string const& value, cmake* state)>; #ifndef CMAKE_BOOTSTRAP -void cmWarnUnusedCliWarning(std::string const& variable, int /*unused*/, - void* ctx, char const* /*unused*/, +void cmWarnUnusedCliWarning(std::string const& variable, + cmVariableWatch::AccessType /*unused*/, void* ctx, + char const* /*unused*/, cmMakefile const* /*unused*/) { cmake* cm = reinterpret_cast<cmake*>(ctx); cm->MarkCliAsUsed(variable); } + +void cmDeprecatedWatch(std::string const& /*unused*/, + cmVariableWatch::AccessType /*unused*/, + void* /*unused*/, char const* /*unused*/, + cmMakefile const* mf) +{ + if (mf->GetPolicyStatus(cmPolicies::CMP0218) == cmPolicies::WARN) { + mf->IssuePolicyWarning(cmPolicies::CMP0218); + } +} #endif +void warnDeprecated(cm::string_view oldOption, cm::string_view newOption) +{ + std::cerr << "The "_s << oldOption << " option is deprecated. Use "_s + << newOption << " instead.\n"_s; +} + +std::string normalizeCliWarningName(cm::string_view cliName) +{ + std::string out = cmStrCat("CMD_"_s, cmSystemTools::UpperCase(cliName)); + std::replace(out.begin(), out.end(), '-', '_'); + return out; +} + bool cmakeCheckStampFile(std::string const& stampName) { // The stamp file does not exist. Use the stamp dependencies to @@ -271,7 +295,7 @@ } // namespace -cmDocumentationEntry cmake::CMAKE_STANDARD_OPTIONS_TABLE[19] = { +cmDocumentationEntry cmake::CMAKE_STANDARD_OPTIONS_TABLE[15] = { { "-S <path-to-source>", "Explicitly specify a source directory." }, { "-B <path-to-build>", "Explicitly specify a build directory." }, { "-C <initial-cache>", "Pre-load a script to populate the cache." }, @@ -285,18 +309,11 @@ "Specify install directory [CMAKE_INSTALL_PREFIX]." }, { "--project-file <project-file-name>", "Specify an alternate project file name." }, - { "-Wdev", "Enable developer warnings." }, - { "-Wno-dev", "Suppress developer warnings." }, - { "-Werror=dev", "Make developer warnings errors." }, - { "-Wno-error=dev", "Make developer warnings not errors." }, - { "-Wdeprecated", "Enable deprecation warnings." }, - { "-Wno-deprecated", "Suppress deprecation warnings." }, - { "-Werror=deprecated", - "Make deprecated macro and function warnings " - "errors." }, - { "-Wno-error=deprecated", - "Make deprecated macro and function warnings " - "not errors." } + { "-W<category>", "Enable the specified category of warnings." }, + { "-Wno-<category>", "Suppress the specified category of warnings." }, + { "-Werror=<category>", "Make the specified category of warnings errors." }, + { "-Wno-error=<category>", + "Make the specified category of warnings not errors." }, }; cmake::cmake(cmState::Role role, cmState::TryCompile isTryCompile) @@ -471,7 +488,7 @@ void cmake::CleanupCommandsAndMacros() { - this->CurrentSnapshot = this->State->Reset(); + this->CurrentSnapshot = this->State->Reset(this->CurrentSnapshot); this->State->RemoveUserDefinedCommands(); this->CurrentSnapshot.SetDefaultDefinitions(); // FIXME: InstalledFiles probably belongs in the global generator. @@ -479,22 +496,33 @@ } #ifndef CMAKE_BOOTSTRAP -void cmake::SetWarningFromPreset(std::string const& name, - cm::optional<bool> warning, - cm::optional<bool> error) +void cmake::SetDiagnosticsFromPreset( + std::map<cmDiagnosticCategory, bool> const& warnings, + std::map<cmDiagnosticCategory, bool> const& errors) { - if (warning) { - if (*warning) { - this->DiagLevels[name] = std::max(this->DiagLevels[name], DIAG_WARN); - } else { - this->DiagLevels[name] = DIAG_IGNORE; + for (unsigned i = 1; i < cmDiagnostics::CategoryCount; ++i) { + auto const category = static_cast<cmDiagnosticCategory>(i); + + auto const wi = warnings.find(category); + if (wi != warnings.end()) { + if (wi->second) { + this->AlterDiagnostic(&cmStateSnapshot::PromoteDiagnostic, category, + cmDiagnostics::Warn, true); + } else { + this->AlterDiagnostic(&cmStateSnapshot::DemoteDiagnostic, category, + cmDiagnostics::Ignore, true); + } } - } - if (error) { - if (*error) { - this->DiagLevels[name] = DIAG_ERROR; - } else { - this->DiagLevels[name] = std::min(this->DiagLevels[name], DIAG_WARN); + + auto const ei = errors.find(category); + if (ei != errors.end()) { + if (ei->second) { + this->AlterDiagnostic(&cmStateSnapshot::PromoteDiagnostic, category, + cmDiagnostics::SendError, true); + } else { + this->AlterDiagnostic(&cmStateSnapshot::DemoteDiagnostic, category, + cmDiagnostics::Warn, true); + } } } } @@ -604,44 +632,51 @@ return true; }; - auto WarningLambda = [](cm::string_view entry, cmake* state) -> bool { + auto WarningLambda = [](cm::string_view option, cmake* state) -> bool { bool foundNo = false; bool foundError = false; - if (cmHasLiteralPrefix(entry, "no-")) { + cm::string_view cname = option; + if (cmHasLiteralPrefix(cname, "no-")) { foundNo = true; - entry.remove_prefix(3); + cname.remove_prefix(3); } - if (cmHasLiteralPrefix(entry, "error=")) { + if (cmHasLiteralPrefix(cname, "error=")) { foundError = true; - entry.remove_prefix(6); + cname.remove_prefix(6); } - if (entry.empty()) { + if (cname.empty()) { cmSystemTools::Error("No warning name provided."); return false; } - std::string const name = std::string(entry); - if (!foundNo && !foundError) { - // -W<name> - state->DiagLevels[name] = std::max(state->DiagLevels[name], DIAG_WARN); - } else if (foundNo && !foundError) { - // -Wno<name> - state->DiagLevels[name] = DIAG_IGNORE; - } else if (!foundNo && foundError) { - // -Werror=<name> - state->DiagLevels[name] = DIAG_ERROR; + cm::optional<cmDiagnosticCategory> category; + if (cname == "dev"_s) { + warnDeprecated( + option, + cmStrCat("-W"_s, option.substr(0, option.size() - 3), "author"_s)); + category = cmDiagnostics::CMD_AUTHOR; } else { - // -Wno-error=<name> - // This can downgrade an error to a warning, but should not enable - // or disable a warning in the first place. - auto dli = state->DiagLevels.find(name); - if (dli != state->DiagLevels.end()) { - dli->second = std::min(dli->second, DIAG_WARN); + category = + cmDiagnostics::GetDiagnosticCategory(normalizeCliWarningName(cname)); + if (!category) { + cmSystemTools::Error( + cmStrCat("The warning category \""_s, cname, "\" is not known.")); + return false; } } + + if (foundNo) { + state->AlterDiagnostic( + &cmStateSnapshot::DemoteDiagnostic, *category, + foundError ? cmDiagnostics::Warn : cmDiagnostics::Ignore, true); + } else { + state->AlterDiagnostic( + &cmStateSnapshot::PromoteDiagnostic, *category, + foundError ? cmDiagnostics::SendError : cmDiagnostics::Warn, true); + } return true; }; @@ -785,22 +820,38 @@ void cmake::ProcessCacheArg(std::string const& var, std::string const& value, cmStateEnums::CacheEntryType type) { + cmDiagnosticAction const warnUnusedCli = + this->CurrentSnapshot.GetDiagnostic(cmDiagnostics::CMD_UNUSED_CLI); + // The value is transformed if it is a filepath for example, so // we can't compare whether the value is already in the cache until // after we call AddCacheEntry. bool haveValue = false; std::string cachedValue; - if (this->WarnUnusedCli) { + if (warnUnusedCli != cmDiagnostics::Ignore) { if (cmValue v = this->State->GetInitializedCacheValue(var)) { haveValue = true; cachedValue = *v; } } - this->AddCacheEntry( - var, value, "No help, variable specified on the command line.", type); + // See also CMP0218. + if (var == "CMAKE_WARN_DEPRECATED") { + std::cerr << "The CMAKE_WARN_DEPRECATED variable is deprecated. " + "Use -W[no-]deprecated instead.\n"_s; + } else if (var == "CMAKE_ERROR_DEPRECATED") { + std::cerr << "The CMAKE_ERROR_DEPRECATED variable is deprecated. " + "Use -W[no-]error=deprecated instead.\n"_s; + } - if (this->WarnUnusedCli) { + auto const builtIn = cmCacheDocumentationTable::Get(var); + std::string const helpString = builtIn.Summary.empty() + ? std::string("No help, variable specified on the command line.") + : std::string(builtIn.Summary); + + this->AddCacheEntry(var, value, helpString, type); + + if (warnUnusedCli != cmDiagnostics::Ignore) { if (!haveValue || cachedValue != *this->State->GetInitializedCacheValue(var)) { this->WatchUnusedCli(var); @@ -823,7 +874,7 @@ // read in the list file to fill the cache if (!path.empty()) { - this->CurrentSnapshot = this->State->Reset(); + this->CurrentSnapshot = this->State->Reset(this->CurrentSnapshot); cmStateSnapshot snapshot = this->GetCurrentSnapshot(); snapshot.GetDirectory().SetCurrentBinary(this->GetHomeOutputDirectory()); snapshot.GetDirectory().SetCurrentSource(this->GetHomeDirectory()); @@ -967,19 +1018,6 @@ cmSystemTools::GetEnvVar("CMAKE_AUTOGEN_INTERMEDIATE_DIR_STRATEGY"); } -namespace { -enum class ListPresets -{ - None, - Configure, - Build, - Test, - Package, - Workflow, - All, -}; -} - // Parse the args void cmake::SetArgs(std::vector<std::string> const& args) { @@ -993,9 +1031,9 @@ #if !defined(CMAKE_BOOTSTRAP) std::string profilingFormat; std::string profilingOutput; - std::string presetName; - ListPresets listPresets = ListPresets::None; + cmCMakePresetsConfigureArgs presetsArgs; + using ListPresets = cmCMakePresetsConfigureArgs::ListPresetsOption; #endif auto EmptyStringArgLambda = [](std::string const&, cmake* state) -> bool { @@ -1284,22 +1322,26 @@ state->SetTrace(true); return true; } }, - CommandArgument{ "--warn-uninitialized", CommandArgument::Values::Zero, - [](std::string const&, cmake* state) -> bool { - std::cout << "Warn about uninitialized values.\n"; - state->SetWarnUninitialized(true); - return true; - } }, + CommandArgument{ + "--warn-uninitialized", CommandArgument::Values::Zero, + [](std::string const&, cmake* state) -> bool { + warnDeprecated("--warn-uninitialized"_s, "-Wuninitialized"_s); + state->AlterDiagnostic(&cmStateSnapshot::PromoteDiagnostic, + cmDiagnostics::CMD_UNINITIALIZED, + cmDiagnostics::Warn, true); + return true; + } }, CommandArgument{ "--warn-unused-vars", CommandArgument::Values::Zero, IgnoreAndTrueLambda }, // Option was removed. - CommandArgument{ "--no-warn-unused-cli", CommandArgument::Values::Zero, - [](std::string const&, cmake* state) -> bool { - std::cout - << "Not searching for unused variables given on the " - "command line.\n"; - state->SetWarnUnusedCli(false); - return true; - } }, + CommandArgument{ + "--no-warn-unused-cli", CommandArgument::Values::Zero, + [](std::string const&, cmake* state) -> bool { + warnDeprecated("--no-warn-unused-cli"_s, "-Wno-unused-cli"_s); + state->AlterDiagnostic(&cmStateSnapshot::DemoteDiagnostic, + cmDiagnostics::CMD_UNUSED_CLI, + cmDiagnostics::Ignore, true); + return true; + } }, CommandArgument{ "--check-system-vars", CommandArgument::Values::Zero, [](std::string const&, cmake* state) -> bool { @@ -1406,24 +1448,31 @@ arguments.emplace_back("--preset", "No preset specified for --preset", CommandArgument::Values::One, [&](std::string const& value, cmake*) -> bool { - presetName = value; + presetsArgs.PresetName = value; return true; }); arguments.emplace_back( + "--presets-file", "No file specified for --presets-file", + CommandArgument::Values::One, + [&presetsArgs](std::string const& value, cmake*) -> bool { + presetsArgs.PresetsFile = cmSystemTools::ToNormalizedPathOnDisk(value); + return true; + }); + arguments.emplace_back( "--list-presets", CommandArgument::Values::ZeroOrOne, [&](std::string const& value, cmake*) -> bool { if (value.empty() || value == "configure") { - listPresets = ListPresets::Configure; + presetsArgs.ListPresets = ListPresets::Configure; } else if (value == "build") { - listPresets = ListPresets::Build; + presetsArgs.ListPresets = ListPresets::Build; } else if (value == "test") { - listPresets = ListPresets::Test; + presetsArgs.ListPresets = ListPresets::Test; } else if (value == "package") { - listPresets = ListPresets::Package; + presetsArgs.ListPresets = ListPresets::Package; } else if (value == "workflow") { - listPresets = ListPresets::Workflow; + presetsArgs.ListPresets = ListPresets::Workflow; } else if (value == "all") { - listPresets = ListPresets::All; + presetsArgs.ListPresets = ListPresets::All; } else { cmSystemTools::Error( "Invalid value specified for --list-presets.\n" @@ -1550,7 +1599,7 @@ #ifdef CMAKE_BOOTSTRAP false; #else - !presetName.empty(); + !presetsArgs.PresetName.empty(); #endif if (this->State->GetRole() == cmState::Role::Project && !haveSourceDir && @@ -1569,158 +1618,14 @@ this->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory()); } + if (this->State->GetRole() == cmState::Role::Script && havePreset) { + this->IssueMessage(MessageType::FATAL_ERROR, + "Presets are not supported in CMake script mode."); + } + #if !defined(CMAKE_BOOTSTRAP) - if (listPresets != ListPresets::None || !presetName.empty()) { - cmCMakePresetsGraph presetsGraph; - auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory()); - if (result != true) { - std::string errorMsg = - cmStrCat("Could not read presets from ", this->GetHomeDirectory(), - ":\n", presetsGraph.parseState.GetErrorMessage()); - cmSystemTools::Error(errorMsg); - return; - } - - if (listPresets != ListPresets::None) { - if (listPresets == ListPresets::Configure) { - this->PrintPresetList(presetsGraph); - } else if (listPresets == ListPresets::Build) { - presetsGraph.PrintBuildPresetList(); - } else if (listPresets == ListPresets::Test) { - presetsGraph.PrintTestPresetList(); - } else if (listPresets == ListPresets::Package) { - presetsGraph.PrintPackagePresetList(); - } else if (listPresets == ListPresets::Workflow) { - presetsGraph.PrintWorkflowPresetList(); - } else if (listPresets == ListPresets::All) { - presetsGraph.PrintAllPresets(); - } - - this->State->SetRoleToHelpForListPresets(); - return; - } - - auto preset = presetsGraph.ConfigurePresets.find(presetName); - if (preset == presetsGraph.ConfigurePresets.end()) { - cmSystemTools::Error(cmStrCat("No such preset in ", - this->GetHomeDirectory(), ": \"", - presetName, '"')); - this->PrintPresetList(presetsGraph); - return; - } - if (preset->second.Unexpanded.Hidden) { - cmSystemTools::Error(cmStrCat("Cannot use hidden preset in ", - this->GetHomeDirectory(), ": \"", - presetName, '"')); - this->PrintPresetList(presetsGraph); - return; - } - auto const& expandedPreset = preset->second.Expanded; - if (!expandedPreset) { - cmSystemTools::Error(cmStrCat("Could not evaluate preset \"", - preset->second.Unexpanded.Name, - "\": Invalid macro expansion")); - return; - } - if (!expandedPreset->ConditionResult) { - cmSystemTools::Error(cmStrCat("Could not use disabled preset \"", - preset->second.Unexpanded.Name, '"')); - return; - } - - if (!this->State->IsCacheLoaded() && !haveBArg && - !expandedPreset->BinaryDir.empty()) { - this->SetHomeOutputDirectory(expandedPreset->BinaryDir); - } - if (!this->GlobalGenerator && !expandedPreset->Generator.empty()) { - if (!this->CreateAndSetGlobalGenerator(expandedPreset->Generator)) { - return; - } - } - this->UnprocessedPresetVariables = expandedPreset->CacheVariables; - this->UnprocessedPresetEnvironment = expandedPreset->Environment; - - if (!expandedPreset->InstallDir.empty() && - !this->State->GetInitializedCacheValue("CMAKE_INSTALL_PREFIX")) { - this->UnprocessedPresetVariables["CMAKE_INSTALL_PREFIX"] = { - "PATH", expandedPreset->InstallDir - }; - } - if (!expandedPreset->ToolchainFile.empty() && - !this->State->GetInitializedCacheValue("CMAKE_TOOLCHAIN_FILE")) { - this->UnprocessedPresetVariables["CMAKE_TOOLCHAIN_FILE"] = { - "FILEPATH", expandedPreset->ToolchainFile - }; - } - - if (!expandedPreset->ArchitectureStrategy || - expandedPreset->ArchitectureStrategy == - cmCMakePresetsGraph::ArchToolsetStrategy::Set) { - if (!this->GeneratorPlatformSet && - !expandedPreset->Architecture.empty()) { - this->SetGeneratorPlatform(expandedPreset->Architecture); - } - } - if (!expandedPreset->ToolsetStrategy || - expandedPreset->ToolsetStrategy == - cmCMakePresetsGraph::ArchToolsetStrategy::Set) { - if (!this->GeneratorToolsetSet && !expandedPreset->Toolset.empty()) { - this->SetGeneratorToolset(expandedPreset->Toolset); - } - } - - if (!expandedPreset->GraphVizFile.empty()) { - if (this->GraphVizFile.empty()) { - this->SetGraphVizFile( - cmSystemTools::CollapseFullPath(expandedPreset->GraphVizFile)); - } - } - - this->SetWarningFromPreset("dev", expandedPreset->WarnDev, - expandedPreset->ErrorDev); - this->SetWarningFromPreset("deprecated", expandedPreset->WarnDeprecated, - expandedPreset->ErrorDeprecated); - if (expandedPreset->WarnUninitialized == true) { - this->SetWarnUninitialized(true); - } - if (expandedPreset->WarnUnusedCli == false) { - this->SetWarnUnusedCli(false); - } - if (expandedPreset->WarnSystemVars == true) { - this->SetCheckSystemVars(true); - } - if (expandedPreset->DebugOutput == true) { - this->SetDebugOutputOn(true); - } - if (expandedPreset->DebugTryCompile == true) { - this->DebugTryCompileOn(); - } - if (expandedPreset->DebugFind == true) { - this->SetDebugFindOutput(true); - } - if (expandedPreset->TraceMode && - expandedPreset->TraceMode != - cmCMakePresetsGraph::TraceEnableMode::Disable) { - this->SetTrace(true); - if (expandedPreset->TraceMode == - cmCMakePresetsGraph::TraceEnableMode::Expand) { - this->SetTraceExpand(true); - } - } - if (expandedPreset->TraceFormat) { - this->SetTrace(true); - this->SetTraceFormat(*expandedPreset->TraceFormat); - } - if (!expandedPreset->TraceSource.empty()) { - this->SetTrace(true); - for (std::string const& filePaths : expandedPreset->TraceSource) { - this->AddTraceSource(filePaths); - } - } - if (!expandedPreset->TraceRedirect.empty()) { - this->SetTrace(true); - this->SetTraceFile(expandedPreset->TraceRedirect); - } + if (presetsArgs.HasPresetsArg()) { + this->SetArgsFromPreset(presetsArgs, haveBArg); } #endif } @@ -2135,6 +2040,155 @@ } #ifndef CMAKE_BOOTSTRAP +bool cmake::SetArgsFromPreset(cmCMakePresetsConfigureArgs const& args, + bool haveBinaryDirArg) +{ + using ListPresets = cmCMakePresetsConfigureArgs::ListPresetsOption; + + cmCMakePresetsGraph presetsGraph; + auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory(), + args.PresetsFile); + if (result != true) { + std::string errorMsg = + cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ":\n", + presetsGraph.parseState.GetErrorMessage()); + cmSystemTools::Error(errorMsg); + return false; + } + + if (args.ListPresets != ListPresets::None) { + switch (args.ListPresets) { + case ListPresets::Configure: + this->PrintPresetList(presetsGraph); + break; + case ListPresets::Build: + presetsGraph.PrintBuildPresetList(); + break; + case ListPresets::Test: + presetsGraph.PrintTestPresetList(); + break; + case ListPresets::Package: + presetsGraph.PrintPackagePresetList(); + break; + case ListPresets::Workflow: + presetsGraph.PrintWorkflowPresetList(); + break; + case ListPresets::All: + presetsGraph.PrintAllPresets(); + break; + default: + break; + } + + this->State->SetRoleToHelpForListPresets(); + return false; + } + + auto resolveResult = + presetsGraph.ResolvePreset(args.PresetName, presetsGraph.ConfigurePresets); + using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset; + using S = cmCMakePresetsGraph::PresetResolveStatus; + auto resolveError = cmCMakePresetsGraph::FormatPresetError<ConfigurePreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + this->GetHomeDirectory()); + if (resolveError) { + cmSystemTools::Error(*resolveError); + if (resolveResult.StatusCode == S::NotFound || + resolveResult.StatusCode == S::Hidden) { + this->PrintPresetList(presetsGraph); + } + return false; + } + auto const* expandedPreset = resolveResult.Preset; + + if (!this->State->IsCacheLoaded() && !haveBinaryDirArg && + !expandedPreset->BinaryDir.empty()) { + this->SetHomeOutputDirectory(expandedPreset->BinaryDir); + } + if (!this->GlobalGenerator && !expandedPreset->Generator.empty()) { + if (!this->CreateAndSetGlobalGenerator(expandedPreset->Generator)) { + return false; + } + } + this->UnprocessedPresetVariables = expandedPreset->CacheVariables; + this->UnprocessedPresetEnvironment = expandedPreset->Environment; + + if (!expandedPreset->InstallDir.empty() && + !this->State->GetInitializedCacheValue("CMAKE_INSTALL_PREFIX")) { + this->UnprocessedPresetVariables["CMAKE_INSTALL_PREFIX"] = { + "PATH", expandedPreset->InstallDir + }; + } + if (!expandedPreset->ToolchainFile.empty() && + !this->State->GetInitializedCacheValue("CMAKE_TOOLCHAIN_FILE")) { + this->UnprocessedPresetVariables["CMAKE_TOOLCHAIN_FILE"] = { + "FILEPATH", expandedPreset->ToolchainFile + }; + } + + if (!expandedPreset->ArchitectureStrategy || + expandedPreset->ArchitectureStrategy == + cmCMakePresetsGraph::ArchToolsetStrategy::Set) { + if (!this->GeneratorPlatformSet && !expandedPreset->Architecture.empty()) { + this->SetGeneratorPlatform(expandedPreset->Architecture); + } + } + if (!expandedPreset->ToolsetStrategy || + expandedPreset->ToolsetStrategy == + cmCMakePresetsGraph::ArchToolsetStrategy::Set) { + if (!this->GeneratorToolsetSet && !expandedPreset->Toolset.empty()) { + this->SetGeneratorToolset(expandedPreset->Toolset); + } + } + + if (!expandedPreset->GraphVizFile.empty()) { + if (this->GraphVizFile.empty()) { + this->SetGraphVizFile( + cmSystemTools::CollapseFullPath(expandedPreset->GraphVizFile)); + } + } + + this->SetDiagnosticsFromPreset(expandedPreset->Warnings, + expandedPreset->Errors); + if (expandedPreset->WarnSystemVars == true) { + this->SetCheckSystemVars(true); + } + if (expandedPreset->DebugOutput == true) { + this->SetDebugOutputOn(true); + } + if (expandedPreset->DebugTryCompile == true) { + this->DebugTryCompileOn(); + } + if (expandedPreset->DebugFind == true) { + this->SetDebugFindOutput(true); + } + if (expandedPreset->TraceMode && + expandedPreset->TraceMode != + cmCMakePresetsGraph::TraceEnableMode::Disable) { + this->SetTrace(true); + if (expandedPreset->TraceMode == + cmCMakePresetsGraph::TraceEnableMode::Expand) { + this->SetTraceExpand(true); + } + } + if (expandedPreset->TraceFormat) { + this->SetTrace(true); + this->SetTraceFormat(*expandedPreset->TraceFormat); + } + if (!expandedPreset->TraceSource.empty()) { + this->SetTrace(true); + for (std::string const& filePaths : expandedPreset->TraceSource) { + this->AddTraceSource(filePaths); + } + } + if (!expandedPreset->TraceRedirect.empty()) { + this->SetTrace(true); + this->SetTraceFile(expandedPreset->TraceRedirect); + } + + return true; +} + void cmake::PrintPresetList(cmCMakePresetsGraph const& graph) const { std::vector<GeneratorInfo> generators; @@ -2376,78 +2430,87 @@ auto profilingRAII = this->CreateProfilingEntry("project", "configure"); #endif - DiagLevel diagLevel; + // We now need to harmonize the previous initial diagnostic state with any + // changes requested via command line options and/or presets. We do this by + // first applying the prior (cached) state, then applying all deferred + // alterations. - if (this->DiagLevels.count("deprecated") == 1) { + if (cmValue cachedDiagnostics = + this->State->GetCacheEntryValue("CMAKE_DIAGNOSTIC_INIT")) { + for (std::string const& item : cmList{ cachedDiagnostics }) { + std::string::size_type n = item.find('='); + if (n != std::string::npos) { + cm::string_view v = item; + cm::optional<cmDiagnosticCategory> const& category = + cmDiagnostics::GetDiagnosticCategory(v.substr(0, n)); + cm::optional<cmDiagnosticAction> const& action = + cmDiagnostics::GetDiagnosticAction(v.substr(n + 1)); - diagLevel = this->DiagLevels["deprecated"]; - if (diagLevel == DIAG_IGNORE) { - this->SetSuppressDeprecatedWarnings(true); - this->SetDeprecatedWarningsAsErrors(false); - } else if (diagLevel == DIAG_WARN) { - this->SetSuppressDeprecatedWarnings(false); - this->SetDeprecatedWarningsAsErrors(false); - } else if (diagLevel == DIAG_ERROR) { - this->SetSuppressDeprecatedWarnings(false); - this->SetDeprecatedWarningsAsErrors(true); - } - } - - if (this->DiagLevels.count("dev") == 1) { - bool setDeprecatedVariables = false; - - cmValue cachedWarnDeprecated = - this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED"); - cmValue cachedErrorDeprecated = - this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED"); - - // don't overwrite deprecated warning setting from a previous invocation - if (!cachedWarnDeprecated && !cachedErrorDeprecated) { - setDeprecatedVariables = true; - } - - diagLevel = this->DiagLevels["dev"]; - if (diagLevel == DIAG_IGNORE) { - this->SetSuppressDevWarnings(true); - this->SetDevWarningsAsErrors(false); - - if (setDeprecatedVariables) { - this->SetSuppressDeprecatedWarnings(true); - this->SetDeprecatedWarningsAsErrors(false); - } - } else if (diagLevel == DIAG_WARN) { - this->SetSuppressDevWarnings(false); - this->SetDevWarningsAsErrors(false); - - if (setDeprecatedVariables) { - this->SetSuppressDeprecatedWarnings(false); - this->SetDeprecatedWarningsAsErrors(false); - } - } else if (diagLevel == DIAG_ERROR) { - this->SetSuppressDevWarnings(false); - this->SetDevWarningsAsErrors(true); - - if (setDeprecatedVariables) { - this->SetSuppressDeprecatedWarnings(false); - this->SetDeprecatedWarningsAsErrors(true); + if (category && action) { + this->CurrentSnapshot.SetDiagnostic(*category, *action, false); + } } } } - // Cache variables may have already been set by a previous invocation, - // so we cannot rely on command line options alone. Always ensure our - // messenger is in sync with the cache. - cmValue value = this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED"); - this->Messenger->SetSuppressDeprecatedWarnings(value && value.IsOff()); + cmValue cachedWarnDeprecated = + this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED"); + if (cachedWarnDeprecated) { + if (cachedWarnDeprecated.IsOn()) { + this->CurrentSnapshot.PromoteDiagnostic(cmDiagnostics::CMD_DEPRECATED, + cmDiagnostics::Warn, false); + } else { + this->CurrentSnapshot.DemoteDiagnostic(cmDiagnostics::CMD_DEPRECATED, + cmDiagnostics::Ignore, false); + } + } - value = this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED"); - this->Messenger->SetDeprecatedWarningsAsErrors(value.IsOn()); + cmValue cachedErrorDeprecated = + this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED"); + if (cachedErrorDeprecated) { + if (cachedErrorDeprecated.IsOn()) { + this->CurrentSnapshot.PromoteDiagnostic(cmDiagnostics::CMD_DEPRECATED, + cmDiagnostics::SendError, false); + } else { + this->CurrentSnapshot.DemoteDiagnostic(cmDiagnostics::CMD_DEPRECATED, + cmDiagnostics::Warn, false); + } + } - value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_WARNINGS"); - this->Messenger->SetSuppressDevWarnings(value.IsOn()); + for (DiagnosticAlteration const& da : this->DiagnosticAlterations) { + (this->CurrentSnapshot.*da.Alteration)(da.Category, da.DesiredAction, + da.Recurse); + } - value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_ERRORS"); - this->Messenger->SetDevWarningsAsErrors(value && value.IsOff()); + // Now write the diagnostic state back to the cache. + cmList diagnostics; + for (unsigned i = 1; i < cmDiagnostics::CategoryCount; ++i) { + auto const category = static_cast<cmDiagnosticCategory>(i); + auto const action = this->CurrentSnapshot.GetDiagnostic(category); + + diagnostics.emplace_back( + cmStrCat(cmDiagnostics::GetCategoryString(category), '=', + cmDiagnostics::GetActionString(action))); + + if (category == cmDiagnostics::CMD_DEPRECATED) { + std::string const warnValue = + (action >= cmDiagnostics::Warn ? "ON" : "OFF"); + this->AddCacheEntry("CMAKE_WARN_DEPRECATED", warnValue, + "Deprecated. Use -W[no-]deprecated instead.", + cmStateEnums::INTERNAL); + std::string const errorValue = + (action >= cmDiagnostics::SendError ? "ON" : "OFF"); + this->AddCacheEntry("CMAKE_ERROR_DEPRECATED", errorValue, + "Deprecated. Use -W[no-]error=deprecated instead.", + cmStateEnums::INTERNAL); + } + } + + this->AddCacheEntry("CMAKE_DIAGNOSTIC_INIT", cmJoin(diagnostics, ";"_s), + "Set initial state for CMake diagnostics; " + "used to persist state set by command-line options " + "across invocations.", + cmStateEnums::INTERNAL); int ret = this->ActualConfigure(); cmValue delCacheVars = @@ -2684,6 +2747,19 @@ } } + if (!this->State->GetInitializedCacheValue( + "CMAKE_DISABLE_PRECOMPILE_HEADERS")) { + cm::optional<std::string> disablePrecompileHeaders = + cmSystemTools::GetEnvVar("CMAKE_DISABLE_PRECOMPILE_HEADERS"); + if (disablePrecompileHeaders && !disablePrecompileHeaders->empty()) { + std::string message = + "Default value for DISABLE_PRECOMPILE_HEADERS of targets."; + this->AddCacheEntry("CMAKE_DISABLE_PRECOMPILE_HEADERS", + *disablePrecompileHeaders, message, + cmStateEnums::BOOL); + } + } + // reset any system configuration information, except for when we are // InTryCompile. With TryCompile the system info is taken from the parent's // info to save time @@ -2717,7 +2793,10 @@ return 0; }; int ret = this->Instrumentation->InstrumentCommand( - "configure", this->cmdArgs, [doConfigure]() { return doConfigure(); }, + "configure", this->cmdArgs, + [doConfigure]() -> cmInstrumentation::CommandResult { + return { doConfigure(), cm::nullopt, cm::nullopt }; + }, cm::nullopt, cm::nullopt, this->GetIsInTryCompile() ? cmInstrumentation::LoadQueriesAfter::No : cmInstrumentation::LoadQueriesAfter::Yes); @@ -2992,6 +3071,9 @@ if (!sarifLogFileWriter.ConfigureForCMakeRun(*this)) { return -1; } + + this->VariableWatch->AddWatch("CMAKE_WARN_DEPRECATED", cmDeprecatedWatch); + this->VariableWatch->AddWatch("CMAKE_ERROR_DEPRECATED", cmDeprecatedWatch); #endif // Log the trace format version to the desired output @@ -3166,7 +3248,10 @@ }; int ret = this->Instrumentation->InstrumentCommand( - "generate", this->cmdArgs, [doGenerate]() { return doGenerate(); }); + "generate", this->cmdArgs, + [doGenerate]() -> cmInstrumentation::CommandResult { + return { doGenerate(), cm::nullopt, cm::nullopt }; + }); if (ret != 0) { return ret; } @@ -3189,9 +3274,7 @@ std::cout << "Generate graphviz: " << this->GraphVizFile << '\n'; this->GenerateGraphViz(this->GraphVizFile); } - if (this->WarnUnusedCli) { - this->RunCheckForUnusedVariables(); - } + this->RunCheckForUnusedVariables(); if (cmSystemTools::GetErrorOccurredFlag()) { #if !defined(CMAKE_BOOTSTRAP) this->FileAPI->WriteReplies(cmFileAPI::IndexFor::FailedGenerate); @@ -3219,16 +3302,6 @@ this->State->AddCacheEntry(key, value, helpString, static_cast<cmStateEnums::CacheEntryType>(type)); this->UnwatchUnusedCli(key); - - if (key == "CMAKE_WARN_DEPRECATED"_s) { - this->Messenger->SetSuppressDeprecatedWarnings(value && value.IsOff()); - } else if (key == "CMAKE_ERROR_DEPRECATED"_s) { - this->Messenger->SetDeprecatedWarningsAsErrors(value.IsOn()); - } else if (key == "CMAKE_SUPPRESS_DEVELOPER_WARNINGS"_s) { - this->Messenger->SetSuppressDevWarnings(value.IsOn()); - } else if (key == "CMAKE_SUPPRESS_DEVELOPER_ERRORS"_s) { - this->Messenger->SetDevWarningsAsErrors(value && value.IsOff()); - } } bool cmake::DoWriteGlobVerifyTarget() const @@ -3836,6 +3909,14 @@ this->Messenger->IssueMessage(t, text, backtrace); } +void cmake::IssueDiagnostic(cmDiagnosticCategory category, + std::string const& text, + cmStateSnapshot const& state, + cmDiagnosticContext const& context) const +{ + this->Messenger->IssueDiagnostic(category, text, state, context); +} + std::vector<std::string> cmake::GetDebugConfigs() { cmList configs; @@ -3854,18 +3935,28 @@ int cmake::Build(cmBuildArgs buildArgs, std::vector<std::string> targets, std::vector<std::string> nativeOptions, - cmBuildOptions& buildOptions, std::string const& presetName, - bool listPresets, std::vector<std::string> const& args) + cmBuildOptions& buildOptions, + cmCMakePresetsArgs const& presetsArgs, + std::vector<std::string> const& args) { buildArgs.timeout = cmDuration::zero(); #if !defined(CMAKE_BOOTSTRAP) - if (!presetName.empty() || listPresets) { - this->SetHomeDirectory(cmSystemTools::GetLogicalWorkingDirectory()); - this->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory()); - + if (presetsArgs.HasPresetsArg()) { + // If the binary directory was specified, use it to find + // the source directory so we can locate the presets file. + if (!buildArgs.binaryDir.empty() && + this->SetDirectoriesFromFile(buildArgs.binaryDir)) { + // HomeDirectory is now the source directory (found in CMakeCache.txt) + } else { + // Otherwise we assume this command was called from the source directory. + this->SetHomeDirectory(cmSystemTools::GetLogicalWorkingDirectory()); + this->SetHomeOutputDirectory( + cmSystemTools::GetLogicalWorkingDirectory()); + } cmCMakePresetsGraph settingsFile; - auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory()); + auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory(), + presetsArgs.PresetsFile); if (result != true) { cmSystemTools::Error( cmStrCat("Could not read presets from ", this->GetHomeDirectory(), @@ -3873,44 +3964,23 @@ return 1; } - if (listPresets) { + if (presetsArgs.ListPresets) { settingsFile.PrintBuildPresetList(); return 0; } - auto presetPair = settingsFile.BuildPresets.find(presetName); - if (presetPair == settingsFile.BuildPresets.end()) { - cmSystemTools::Error(cmStrCat("No such build preset in ", - this->GetHomeDirectory(), ": \"", - presetName, '"')); + auto resolveResult = settingsFile.ResolvePreset(presetsArgs.PresetName, + settingsFile.BuildPresets); + auto resolveError = + cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::BuildPreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + this->GetHomeDirectory()); + if (resolveError) { + cmSystemTools::Error(*resolveError); settingsFile.PrintBuildPresetList(); return 1; } - - if (presetPair->second.Unexpanded.Hidden) { - cmSystemTools::Error(cmStrCat("Cannot use hidden build preset in ", - this->GetHomeDirectory(), ": \"", - presetName, '"')); - settingsFile.PrintBuildPresetList(); - return 1; - } - - auto const& expandedPreset = presetPair->second.Expanded; - if (!expandedPreset) { - cmSystemTools::Error(cmStrCat("Could not evaluate build preset \"", - presetName, - "\": Invalid macro expansion")); - settingsFile.PrintBuildPresetList(); - return 1; - } - - if (!expandedPreset->ConditionResult) { - cmSystemTools::Error(cmStrCat("Cannot use disabled build preset in ", - this->GetHomeDirectory(), ": \"", - presetName, '"')); - settingsFile.PrintBuildPresetList(); - return 1; - } + auto const* expandedPreset = resolveResult.Preset; auto configurePresetPair = settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset); @@ -4118,8 +4188,10 @@ // Block the instrumentation build daemon from spawning during this build. // This lock will be released when the process exits at the end of the build. instrumentation.LockBuildDaemon(); - int buildresult = - instrumentation.InstrumentCommand("cmakeBuild", args, doBuild); + int buildresult = instrumentation.InstrumentCommand( + "cmakeBuild", args, [doBuild]() -> cmInstrumentation::CommandResult { + return { doBuild(), cm::nullopt, cm::nullopt }; + }); instrumentation.CollectTimingData( cmInstrumentationQuery::Hook::PostCMakeBuild); #else @@ -4231,8 +4303,7 @@ } #endif -int cmake::Workflow(std::string const& presetName, - WorkflowListPresets listPresets, WorkflowFresh fresh) +int cmake::Workflow(cmCMakePresetsWorkflowArgs const& args) { int exitStatus = 0; #ifndef CMAKE_BOOTSTRAP @@ -4240,7 +4311,8 @@ this->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory()); cmCMakePresetsGraph settingsFile; - auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory()); + auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory(), + args.PresetsFile); if (result != true) { cmSystemTools::Error(cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ":\n", @@ -4248,24 +4320,24 @@ return 1; } - if (listPresets == WorkflowListPresets::Yes) { + if (args.ListPresets) { settingsFile.PrintWorkflowPresetList(); return 0; } - auto presetPair = settingsFile.WorkflowPresets.find(presetName); + auto presetPair = settingsFile.WorkflowPresets.find(args.PresetName); if (presetPair == settingsFile.WorkflowPresets.end()) { cmSystemTools::Error(cmStrCat("No such workflow preset in ", - this->GetHomeDirectory(), ": \"", presetName, - '"')); + this->GetHomeDirectory(), ": \"", + args.PresetName, '"')); settingsFile.PrintWorkflowPresetList(); return 1; } if (presetPair->second.Unexpanded.Hidden) { cmSystemTools::Error(cmStrCat("Cannot use hidden workflow preset in ", - this->GetHomeDirectory(), ": \"", presetName, - '"')); + this->GetHomeDirectory(), ": \"", + args.PresetName, '"')); settingsFile.PrintWorkflowPresetList(); return 1; } @@ -4273,15 +4345,16 @@ auto const& expandedPreset = presetPair->second.Expanded; if (!expandedPreset) { cmSystemTools::Error(cmStrCat("Could not evaluate workflow preset \"", - presetName, "\": Invalid macro expansion")); + args.PresetName, + "\": Invalid macro expansion")); settingsFile.PrintWorkflowPresetList(); return 1; } if (!expandedPreset->ConditionResult) { cmSystemTools::Error(cmStrCat("Cannot use disabled workflow preset in ", - this->GetHomeDirectory(), ": \"", presetName, - '"')); + this->GetHomeDirectory(), ": \"", + args.PresetName, '"')); settingsFile.PrintWorkflowPresetList(); return 1; } @@ -4304,6 +4377,15 @@ } }; + auto buildPresetCommand = [&args](std::vector<std::string> cmd, + std::string const& presetName) { + cmd.insert(cmd.end(), { "--preset", presetName }); + if (!args.PresetsFile.empty()) { + cmd.insert(cmd.end(), { "--presets-file", args.PresetsFile }); + } + return cmd; + }; + std::vector<CalculatedStep> steps; steps.reserve(expandedPreset->Steps.size()); int stepNumber = 1; @@ -4317,13 +4399,13 @@ if (!configurePreset) { return 1; } - std::vector<std::string> args{ cmSystemTools::GetCMakeCommand(), - "--preset", step.PresetName }; - if (fresh == WorkflowFresh::Yes) { - args.emplace_back("--fresh"); + std::vector<std::string> configureCmdArgs = buildPresetCommand( + { cmSystemTools::GetCMakeCommand() }, step.PresetName); + if (args.Fresh) { + configureCmdArgs.emplace_back("--fresh"); } steps.emplace_back(stepNumber, "configure"_s, step.PresetName, - buildWorkflowStep(args)); + buildWorkflowStep(configureCmdArgs)); } break; case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Build: { auto const* buildPreset = this->FindPresetForWorkflow( @@ -4331,10 +4413,10 @@ if (!buildPreset) { return 1; } - steps.emplace_back( - stepNumber, "build"_s, step.PresetName, - buildWorkflowStep({ cmSystemTools::GetCMakeCommand(), "--build", - "--preset", step.PresetName })); + std::vector<std::string> buildCmdArgs = buildPresetCommand( + { cmSystemTools::GetCMakeCommand(), "--build" }, step.PresetName); + steps.emplace_back(stepNumber, "build"_s, step.PresetName, + buildWorkflowStep(buildCmdArgs)); } break; case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Test: { auto const* testPreset = this->FindPresetForWorkflow( @@ -4342,10 +4424,10 @@ if (!testPreset) { return 1; } - steps.emplace_back( - stepNumber, "test"_s, step.PresetName, - buildWorkflowStep({ cmSystemTools::GetCTestCommand(), "--preset", - step.PresetName })); + std::vector<std::string> testCmdArgs = buildPresetCommand( + { cmSystemTools::GetCTestCommand() }, step.PresetName); + steps.emplace_back(stepNumber, "test"_s, step.PresetName, + buildWorkflowStep(testCmdArgs)); } break; case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Package: { auto const* packagePreset = this->FindPresetForWorkflow( @@ -4353,10 +4435,10 @@ if (!packagePreset) { return 1; } - steps.emplace_back( - stepNumber, "package"_s, step.PresetName, - buildWorkflowStep({ cmSystemTools::GetCPackCommand(), "--preset", - step.PresetName })); + std::vector<std::string> packageCmdArgs = buildPresetCommand( + { cmSystemTools::GetCPackCommand() }, step.PresetName); + steps.emplace_back(stepNumber, "package"_s, step.PresetName, + buildWorkflowStep(packageCmdArgs)); } break; } stepNumber++; @@ -4417,115 +4499,38 @@ void cmake::RunCheckForUnusedVariables() { #ifndef CMAKE_BOOTSTRAP - bool haveUnused = false; - std::ostringstream msg; - msg << "Manually-specified variables were not used by the project:"; - for (auto const& it : this->UsedCliVariables) { - if (!it.second) { - haveUnused = true; - msg << "\n " << it.first; + cmDiagnosticAction const action = + this->CurrentSnapshot.GetDiagnostic(cmDiagnostics::CMD_UNUSED_CLI); + if (action != cmDiagnostics::Ignore) { + bool haveUnused = false; + std::ostringstream msg; + msg << "Manually-specified variables were not used by the project:"; + for (auto const& it : this->UsedCliVariables) { + if (!it.second) { + haveUnused = true; + msg << "\n " << it.first; + } } - } - if (haveUnused) { - this->IssueMessage(MessageType::WARNING, msg.str()); + if (haveUnused) { + this->IssueDiagnostic(cmDiagnostics::CMD_UNUSED_CLI, msg.str()); + } } #endif } -bool cmake::GetSuppressDevWarnings() const +void cmake::AlterDiagnostic(DiagnosticAlterationMethod alteration, + cmDiagnosticCategory category, + cmDiagnosticAction desiredAction, bool recurse) { - return this->Messenger->GetSuppressDevWarnings(); -} - -void cmake::SetSuppressDevWarnings(bool b) -{ - std::string value; - - // equivalent to -Wno-dev - if (b) { - value = "TRUE"; + // In Project mode, we need to defer applying any diagnostic changes until + // after reading the prior state from the cache. In all other modes, changes + // should be applied immediately. + if (this->State->GetRole() == cmState::Role::Project) { + this->DiagnosticAlterations.emplace_back( // clang-format: break + DiagnosticAlteration{ alteration, category, desiredAction, recurse }); + } else { + (this->CurrentSnapshot.*alteration)(category, desiredAction, recurse); } - // equivalent to -Wdev - else { - value = "FALSE"; - } - - this->AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", value, - "Suppress Warnings that are meant for" - " the author of the CMakeLists.txt files.", - cmStateEnums::INTERNAL); -} - -bool cmake::GetSuppressDeprecatedWarnings() const -{ - return this->Messenger->GetSuppressDeprecatedWarnings(); -} - -void cmake::SetSuppressDeprecatedWarnings(bool b) -{ - std::string value; - - // equivalent to -Wno-deprecated - if (b) { - value = "FALSE"; - } - // equivalent to -Wdeprecated - else { - value = "TRUE"; - } - - this->AddCacheEntry("CMAKE_WARN_DEPRECATED", value, - "Whether to issue warnings for deprecated " - "functionality.", - cmStateEnums::INTERNAL); -} - -bool cmake::GetDevWarningsAsErrors() const -{ - return this->Messenger->GetDevWarningsAsErrors(); -} - -void cmake::SetDevWarningsAsErrors(bool b) -{ - std::string value; - - // equivalent to -Werror=dev - if (b) { - value = "FALSE"; - } - // equivalent to -Wno-error=dev - else { - value = "TRUE"; - } - - this->AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_ERRORS", value, - "Suppress errors that are meant for" - " the author of the CMakeLists.txt files.", - cmStateEnums::INTERNAL); -} - -bool cmake::GetDeprecatedWarningsAsErrors() const -{ - return this->Messenger->GetDeprecatedWarningsAsErrors(); -} - -void cmake::SetDeprecatedWarningsAsErrors(bool b) -{ - std::string value; - - // equivalent to -Werror=deprecated - if (b) { - value = "TRUE"; - } - // equivalent to -Wno-error=deprecated - else { - value = "FALSE"; - } - - this->AddCacheEntry("CMAKE_ERROR_DEPRECATED", value, - "Whether to issue deprecation errors for macros" - " and functions.", - cmStateEnums::INTERNAL); } void cmake::SetDebugFindOutputPkgs(std::string const& args)
diff --git a/Source/cmake.h b/Source/cmake.h index 38aef9e..8e81cf4 100644 --- a/Source/cmake.h +++ b/Source/cmake.h
@@ -19,6 +19,8 @@ #include <cmext/string_view> #include "cmBuildArgs.h" +#include "cmDiagnosticContext.h" +#include "cmDiagnostics.h" #include "cmDocumentationEntry.h" // IWYU pragma: keep #include "cmGeneratedFileStream.h" #include "cmInstalledFile.h" @@ -47,6 +49,9 @@ #endif class cmExternalMakefileProjectGeneratorFactory; +class cmCMakePresetsArgs; +class cmCMakePresetsConfigureArgs; +class cmCMakePresetsWorkflowArgs; class cmFileAPI; class cmInstrumentation; class cmFileTimeCache; @@ -85,13 +90,6 @@ class cmake { public: - enum DiagLevel - { - DIAG_IGNORE, - DIAG_WARN, - DIAG_ERROR - }; - /** \brief Describes the working modes of cmake */ enum WorkingMode { @@ -260,7 +258,9 @@ bool CreateAndSetGlobalGenerator(std::string const& name); #ifndef CMAKE_BOOTSTRAP - //! Print list of configure presets + bool SetArgsFromPreset(cmCMakePresetsConfigureArgs const& args, + bool haveBinaryDirArg); + void PrintPresetList(cmCMakePresetsGraph const& graph) const; #endif @@ -382,9 +382,9 @@ bool GetIsInTryCompile() const; #ifndef CMAKE_BOOTSTRAP - void SetWarningFromPreset(std::string const& name, - cm::optional<bool> warning, - cm::optional<bool> error); + void SetDiagnosticsFromPreset( + std::map<cmDiagnosticCategory, bool> const& warnings, + std::map<cmDiagnosticCategory, bool> const& errors); void ProcessPresetVariables(); void PrintPresetVariables(); void ProcessPresetEnvironment(); @@ -557,10 +557,6 @@ //! Use trace from another ::cmake instance. void SetTraceRedirect(cmake* other); - bool GetWarnUninitialized() const { return this->WarnUninitialized; } - void SetWarnUninitialized(bool b) { this->WarnUninitialized = b; } - bool GetWarnUnusedCli() const { return this->WarnUnusedCli; } - void SetWarnUnusedCli(bool b) { this->WarnUnusedCli = b; } bool GetCheckSystemVars() const { return this->CheckSystemVars; } void SetCheckSystemVars(bool b) { this->CheckSystemVars = b; } bool GetIgnoreCompileWarningAsError() const @@ -606,60 +602,31 @@ } #endif - /** - * Get the state of the suppression of developer (author) warnings. - * Returns false, by default, if developer warnings should be shown, true - * otherwise. - */ - bool GetSuppressDevWarnings() const; - /** - * Set the state of the suppression of developer (author) warnings. - */ - void SetSuppressDevWarnings(bool v); - - /** - * Get the state of the suppression of deprecated warnings. - * Returns false, by default, if deprecated warnings should be shown, true - * otherwise. - */ - bool GetSuppressDeprecatedWarnings() const; - /** - * Set the state of the suppression of deprecated warnings. - */ - void SetSuppressDeprecatedWarnings(bool v); - - /** - * Get the state of treating developer (author) warnings as errors. - * Returns false, by default, if warnings should not be treated as errors, - * true otherwise. - */ - bool GetDevWarningsAsErrors() const; - /** - * Set the state of treating developer (author) warnings as errors. - */ - void SetDevWarningsAsErrors(bool v); - - /** - * Get the state of treating deprecated warnings as errors. - * Returns false, by default, if warnings should not be treated as errors, - * true otherwise. - */ - bool GetDeprecatedWarningsAsErrors() const; - /** - * Set the state of treating developer (author) warnings as errors. - */ - void SetDeprecatedWarningsAsErrors(bool v); - /** Display a message to the user. */ void IssueMessage( MessageType t, std::string const& text, cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmStateSnapshot const& state, + cmDiagnosticContext const& context = {}) const; + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmDiagnosticContext const& context = {}) const + { + this->IssueDiagnostic(category, text, this->CurrentSnapshot, context); + } + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmListFileBacktrace backtrace) const + { + this->IssueDiagnostic(category, text, this->CurrentSnapshot, + cmDiagnosticContext{ std::move(backtrace) }); + } //! run the --build option int Build(cmBuildArgs buildArgs, std::vector<std::string> targets, std::vector<std::string> nativeOptions, - cmBuildOptions& buildOptions, std::string const& presetName, - bool listPresets, std::vector<std::string> const& args); + cmBuildOptions& buildOptions, + cmCMakePresetsArgs const& presetsArgs, + std::vector<std::string> const& args); enum class DryRun { @@ -671,18 +638,7 @@ bool Open(std::string const& dir, DryRun dryRun); //! run the --workflow option - enum class WorkflowListPresets - { - No, - Yes, - }; - enum class WorkflowFresh - { - No, - Yes, - }; - int Workflow(std::string const& presetName, WorkflowListPresets listPresets, - WorkflowFresh fresh); + int Workflow(cmCMakePresetsWorkflowArgs const& args); void UnwatchUnusedCli(std::string const& var); void WatchUnusedCli(std::string const& var); @@ -767,7 +723,6 @@ void AddDefaultGenerators(); void AddDefaultExtraGenerators(); - std::map<std::string, DiagLevel> DiagLevels; std::string GeneratorInstance; std::string GeneratorPlatform; std::string GeneratorToolset; @@ -818,8 +773,6 @@ #ifndef CMAKE_BOOTSTRAP std::unique_ptr<cmConfigureLog> ConfigureLog; #endif - bool WarnUninitialized = false; - bool WarnUnusedCli = true; bool CheckSystemVars = false; bool IgnoreCompileWarningAsError = false; bool IgnoreLinkWarningAsError = false; @@ -864,6 +817,23 @@ cmStateSnapshot CurrentSnapshot; std::unique_ptr<cmMessenger> Messenger; + using DiagnosticAlterationMethod = + void (cmStateSnapshot::*)(cmDiagnosticCategory, cmDiagnosticAction, bool); + + struct DiagnosticAlteration + { + DiagnosticAlterationMethod const Alteration; + cmDiagnosticCategory const Category; + cmDiagnosticAction const DesiredAction; + bool const Recurse; + }; + + std::vector<DiagnosticAlteration> DiagnosticAlterations; + + void AlterDiagnostic(DiagnosticAlterationMethod alteration, + cmDiagnosticCategory category, + cmDiagnosticAction desiredAction, bool recurse); + #ifndef CMAKE_BOOTSTRAP bool SarifFileOutput = false; std::string SarifFilePath; @@ -917,7 +887,7 @@ void SetScriptModeExitCode(int code) { ScriptModeExitCode = code; } int GetScriptModeExitCode() const { return ScriptModeExitCode.value_or(-1); } - static cmDocumentationEntry CMAKE_STANDARD_OPTIONS_TABLE[19]; + static cmDocumentationEntry CMAKE_STANDARD_OPTIONS_TABLE[15]; }; #define FOR_EACH_C90_FEATURE(F) F(c_function_prototypes)
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index 35346af..b9545c9 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx
@@ -44,6 +44,7 @@ #include "cmcmd.h" #ifndef CMAKE_BOOTSTRAP +# include "cmCMakePresetsArgs.h" # include "cmDocumentation.h" #endif @@ -73,8 +74,10 @@ "Run 'cmake --help' for more information." }; -cmDocumentationEntry const cmDocumentationOptions[35] = { +cmDocumentationEntry const cmDocumentationOptions[] = { { "--preset <preset>,--preset=<preset>", "Specify a configure preset." }, + { "--presets-file <file>,--presets-file=<file>", + "Specify the path to a presets file." }, { "--list-presets[=<type>]", "List available presets." }, { "--workflow [<options>]", "Run a workflow preset." }, { "-E", "CMake command mode. Run \"cmake -E\" for a summary of commands." }, @@ -118,8 +121,6 @@ "Trace only this CMake file/module. Multiple options allowed." }, { "--trace-redirect=<file>", "Redirect trace output to a file instead of stderr." }, - { "--warn-uninitialized", "Warn about uninitialized values." }, - { "--no-warn-unused-cli", "Don't warn about command line options." }, { "--check-system-vars", "Find problems with variable usage in system files." }, { "--compile-no-warning-as-error", @@ -476,8 +477,7 @@ bool foundNonClean = false; PackageResolveMode resolveMode = PackageResolveMode::Default; buildArgs.verbose = cmSystemTools::HasEnv("VERBOSE"); - std::string presetName; - bool listPresets = false; + cmCMakePresetsArgs presetsArgs; auto jLambda = extract_job_number_lambda_builder(buildArgs.binaryDir, buildArgs.jobs, "-j"); @@ -518,15 +518,22 @@ buildArgs.verbose = true; return true; }; + auto presetFileLambda = [&](std::string const& value) -> bool { + presetsArgs.PresetsFile = cmSystemTools::ToNormalizedPathOnDisk(value); + return true; + }; using CommandArgument = cmCommandLineArgument<bool(std::string const& value)>; std::vector<CommandArgument> arguments = { - CommandArgument{ "--preset", CommandArgument::Values::One, - CommandArgument::setToValue(presetName) }, + CommandArgument{ "--preset", "No preset specified for --preset", + CommandArgument::Values::One, + CommandArgument::setToValue(presetsArgs.PresetName) }, + CommandArgument{ "--presets-file", "No file specified for --presets-file", + CommandArgument::Values::One, presetFileLambda }, CommandArgument{ "--list-presets", CommandArgument::Values::Zero, - CommandArgument::setToTrue(listPresets) }, + CommandArgument::setToTrue(presetsArgs.ListPresets) }, CommandArgument{ "-j", CommandArgument::Values::ZeroOrOne, CommandArgument::RequiresSeparator::No, jLambda }, CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne, @@ -623,7 +630,7 @@ } } - if (buildArgs.binaryDir.empty() && presetName.empty() && !listPresets) { + if (buildArgs.binaryDir.empty() && !presetsArgs.HasPresetsArg()) { /* clang-format off */ std::cerr << "Usage: cmake --build <dir> " @@ -634,6 +641,8 @@ " <dir> = Project binary directory to be built.\n" " --preset <preset>, --preset=<preset>\n" " = Specify a build preset.\n" + " --presets-file <file>, --presets-file=<file>\n" + " = Specify the path to a presets file.\n" " --list-presets[=<type>]\n" " = List available build presets.\n" " --parallel [<jobs>], -j [<jobs>]\n" @@ -673,7 +682,7 @@ std::vector<std::string> cmd; cm::append(cmd, av, av + ac); return cm.Build(buildArgs, std::move(targets), std::move(nativeOptions), - buildOptions, presetName, listPresets, cmd); + buildOptions, presetsArgs, cmd); #endif } @@ -801,7 +810,7 @@ assert(1 < ac); std::string config; - std::string component; + std::vector<std::string> components; std::string defaultDirectoryPermissions; std::string prefix; std::string dir; @@ -809,6 +818,17 @@ bool strip = false; bool verbose = cmSystemTools::HasEnv("VERBOSE"); + auto componentLambda = [&components](std::string const& value) -> bool { + if (!value.empty()) { + cmList values{ value }; + for (auto const& v : values) { + components.emplace_back(v); + } + return true; + } + return false; + }; + auto jLambda = extract_job_number_lambda_builder(dir, jobs, "-j"); auto parallelLambda = extract_job_number_lambda_builder(dir, jobs, "--parallel"); @@ -824,12 +844,13 @@ std::vector<CommandArgument> arguments = { CommandArgument{ "--config", CommandArgument::Values::One, CommandArgument::setToValue(config) }, - CommandArgument{ "--component", CommandArgument::Values::One, - CommandArgument::setToValue(component) }, + CommandArgument{ "--component", CommandArgument::Values::OneOrMore, + componentLambda }, CommandArgument{ "--default-directory-permissions", CommandArgument::Values::One, CommandArgument::setToValue(defaultDirectoryPermissions) }, - CommandArgument{ "-j", CommandArgument::Values::One, jLambda }, + CommandArgument{ "-j", CommandArgument::Values::One, + CommandArgument::RequiresSeparator::No, jLambda }, CommandArgument{ "--parallel", CommandArgument::Values::One, parallelLambda }, CommandArgument{ "--prefix", CommandArgument::Values::One, @@ -876,6 +897,7 @@ " <dir> = Project binary directory to install.\n" " --config <cfg> = For multi-configuration tools, choose <cfg>.\n" " --component <comp> = Component-based install. Only install <comp>.\n" + " May be passed multiple components. t\n" " --default-directory-permissions <permission> \n" " Default install permission. Use default permission <permission>.\n" " -j <jobs> --parallel <jobs>\n" @@ -896,10 +918,6 @@ args.emplace_back("-DCMAKE_INSTALL_PREFIX=" + prefix); } - if (!component.empty()) { - args.emplace_back("-DCMAKE_INSTALL_COMPONENT=" + component); - } - if (strip) { args.emplace_back("-DCMAKE_INSTALL_DO_STRIP=1"); } @@ -919,7 +937,7 @@ args.emplace_back("-P"); cmInstrumentation instrumentation(dir); - auto handler = cmInstallScriptHandler(dir, component, config, args); + auto handler = cmInstallScriptHandler(dir, components, config, args); int ret = 0; if (!jobs && handler.IsParallel()) { jobs = 1; @@ -959,7 +977,9 @@ std::vector<std::string> cmd; cm::append(cmd, av, av + ac); ret = instrumentation.InstrumentCommand( - "cmakeInstall", cmd, [doInstall]() { return doInstall(); }); + "cmakeInstall", cmd, [doInstall]() -> cmInstrumentation::CommandResult { + return { doInstall(), cm::nullopt, cm::nullopt }; + }); instrumentation.CollectTimingData( cmInstrumentationQuery::Hook::PostCMakeInstall); return ret; @@ -972,28 +992,26 @@ std::cerr << "This cmake does not support --workflow\n"; return -1; #else - using WorkflowListPresets = cmake::WorkflowListPresets; - using WorkflowFresh = cmake::WorkflowFresh; - std::string presetName; - auto listPresets = WorkflowListPresets::No; - auto fresh = WorkflowFresh::No; + cmCMakePresetsWorkflowArgs presetsArgs; using CommandArgument = cmCommandLineArgument<bool(std::string const& value)>; std::vector<CommandArgument> arguments = { - CommandArgument{ "--preset", CommandArgument::Values::One, - CommandArgument::setToValue(presetName) }, + CommandArgument{ "--preset", "No preset specified for --preset", + CommandArgument::Values::One, + CommandArgument::setToValue(presetsArgs.PresetName) }, + CommandArgument{ "--presets-file", "No file specified for --presets-file", + CommandArgument::Values::One, + [&presetsArgs](std::string const& value) -> bool { + presetsArgs.PresetsFile = + cmSystemTools::ToNormalizedPathOnDisk(value); + return true; + } }, CommandArgument{ "--list-presets", CommandArgument::Values::Zero, - [&listPresets](std::string const&) -> bool { - listPresets = WorkflowListPresets::Yes; - return true; - } }, + CommandArgument::setToTrue(presetsArgs.ListPresets) }, CommandArgument{ "--fresh", CommandArgument::Values::Zero, - [&fresh](std::string const&) -> bool { - fresh = WorkflowFresh::Yes; - return true; - } }, + CommandArgument::setToTrue(presetsArgs.Fresh) } }; std::vector<std::string> inputArgs; @@ -1020,23 +1038,23 @@ } if (!(matched && parsed)) { if (!matched) { - presetName.clear(); - listPresets = WorkflowListPresets::No; + presetsArgs.Clear(); std::cerr << "Unknown argument " << arg << std::endl; } break; } } - if (presetName.empty() && listPresets == WorkflowListPresets::No) { + if (!presetsArgs.HasPresetsArg()) { /* clang-format off */ std::cerr << "Usage: cmake --workflow <options>\n" "Options:\n" - " --preset <preset> = Workflow preset to execute.\n" - " --list-presets = List available workflow presets.\n" - " --fresh = Configure a fresh build tree, removing any " - "existing cache file.\n" + " --preset <preset> = Workflow preset to execute.\n" + " --presets-file <file> = Path to a presets file.\n" + " --list-presets = List available workflow presets.\n" + " --fresh = Configure a fresh build tree, removing any " + "existing cache file.\n" ; /* clang-format on */ return 1; @@ -1051,7 +1069,7 @@ cmakemainProgressCallback(msg, prog, &cm); }); - return cm.Workflow(presetName, listPresets, fresh); + return cm.Workflow(presetsArgs); #endif }
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 7d95c73..6610da0 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx
@@ -17,6 +17,7 @@ #include "cmCommandLineArgument.h" #include "cmCryptoHash.h" #include "cmDuration.h" +#include "cmEnvironment.h" #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" #include "cmList.h" @@ -105,13 +106,19 @@ cat [--] <files>... - concat the files and print them to the standard output chdir dir cmd [args...] - run command in a given directory compare_files [--ignore-eol] file1 file2 - - check if file1 is same as file2 - copy <file>... destination - copy files to destination (either file or directory) - copy_directory <dir>... destination - copy content of <dir>... directories to 'destination' directory - copy_directory_if_different <dir>... destination - copy changed content of <dir>... directories to 'destination' directory - copy_directory_if_newer <dir>... destination - copy newer content of <dir>... directories to 'destination' directory - copy_if_different <file>... destination - copy files if it has changed - copy_if_newer <file>... destination - copy files if source is newer than destination + - check if file1 is same as file2 + copy <file>... destination | -t <destination> <file>... + - copy files to destination (either file or directory) + copy_directory <dir>... destination | -t <destination> <dir>... + - copy content of <dir>... directories to 'destination' directory + copy_directory_if_different <dir>... destination | -t <destination> <dir>... + - copy changed content of <dir>... directories to 'destination' directory + copy_directory_if_newer <dir>... destination | -t <destination> <dir>... + - copy newer content of <dir>... directories to 'destination' directory + copy_if_different <file>... destination | -t <destination> <file>... + - copy files if source has changed + copy_if_newer <file>... destination | -t <destination> <file>... + - copy files if source is newer than destination echo [<string>...] - displays arguments as text echo_append [<string>...] - displays arguments as text but no new line env [--unset=NAME ...] [NAME=VALUE ...] [--] <command> [<arg>...] @@ -203,7 +210,7 @@ return true; } -void cmCatFile(std::string const& fileToAppend) +void cmCatFile(cm::optional<std::string> fileToAppend) { #ifdef _WIN32 _setmode(fileno(stdin), _O_BINARY); @@ -211,8 +218,8 @@ #endif std::streambuf* buf = std::cin.rdbuf(); cmsys::ifstream source; - if (fileToAppend != "-") { - source.open(fileToAppend.c_str(), (std::ios::binary | std::ios::in)); + if (fileToAppend.has_value()) { + source.open(fileToAppend->c_str(), (std::ios::binary | std::ios::in)); buf = source.rdbuf(); } std::cout << buf; @@ -632,13 +639,16 @@ // Create the default manifest ruleset file when not found if (!cmSystemTools::FileExists("cstat_sel_checks.txt")) { - std::string ichecks_cmd = cmSystemTools::GetFilenamePath(orig_cmd[0]); - ichecks_cmd = cmStrCat(ichecks_cmd, "/ichecks --default stdchecks"); + std::vector<std::string> ichecks_cmd; + ichecks_cmd.emplace_back( + cmStrCat(cmSystemTools::GetFilenamePath(orig_cmd[0]), "/ichecks")); + ichecks_cmd.emplace_back("--default"); + ichecks_cmd.emplace_back("stdchecks"); if (!cmSystemTools::RunSingleCommand(ichecks_cmd, &stdOut, &stdErr, &ret, nullptr, cmSystemTools::OUTPUT_NONE)) { - std::cerr << "Error generating default manifest file '" << ichecks_cmd - << "'. " << stdOut << '\n'; + std::cerr << "Error generating default manifest file '" << ichecks_cmd[0] + << "': " << stdOut << '\n'; return 1; } } @@ -1002,8 +1012,26 @@ { // IF YOU ADD A NEW COMMAND, DOCUMENT IT ABOVE and in cmakemain.cxx if (args.size() > 1) { - // Copy file - if (args[1] == "copy" && args.size() > 3) { + // Copy file, copy file if different, copy file if newer. + if ((args[1] == "copy" || args[1] == "copy_if_different" || + args[1] == "copy_if_newer") && + args.size() > 3) { + using CopyFn = cmsys::SystemTools::CopyStatus (*)(std::string const&, + std::string const&); + CopyFn copyFn; + std::string copyErrPrefix; + if (args[1] == "copy") { + copyFn = cmSystemTools::CopyFileAlways; + copyErrPrefix = "Error copying file"; + } else if (args[1] == "copy_if_different") { + copyFn = cmSystemTools::CopyFileIfDifferent; + copyErrPrefix = "Error copying file (if different) from"; + } else { + copyFn = cmSystemTools::CopyFileIfNewer; + copyErrPrefix = "Error copying file (if newer) from"; + } + std::string const& cmdName = args[1]; + using CommandArgument = cmCommandLineArgument<bool(std::string const& value)>; @@ -1038,14 +1066,14 @@ files.pop_back(); } if (targetArg && (!cmSystemTools::FileIsDirectory(*targetArg))) { - std::cerr << "Error: Target (for copy command) \"" << *targetArg - << "\" is not a directory.\n"; + std::cerr << "Error: Target (for " << cmdName << " command) \"" + << *targetArg << "\" is not a directory.\n"; return 1; } if (!targetArg) { if (files.size() < 2) { - std::cerr - << "Error: No files or target specified (for copy command).\n"; + std::cerr << "Error: No files or target specified (for " << cmdName + << " command).\n"; return 1; } targetArg = files.back(); @@ -1054,10 +1082,9 @@ // If error occurs we want to continue copying next files. bool return_value = false; for (auto const& file : files) { - cmsys::SystemTools::CopyStatus const status = - cmSystemTools::CopyFileAlways(file, *targetArg); + cmsys::SystemTools::CopyStatus const status = copyFn(file, *targetArg); if (!status) { - std::cerr << "Error copying file \"" << file << "\" to \"" + std::cerr << copyErrPrefix << " \"" << file << "\" to \"" << *targetArg << "\": " << status.GetString() << '\n'; return_value = true; } @@ -1065,77 +1092,68 @@ return return_value; } - // Copy file if different. - if (args[1] == "copy_if_different" && args.size() > 3) { - // If multiple source files specified, - // then destination must be directory - if ((args.size() > 4) && - (!cmSystemTools::FileIsDirectory(args.back()))) { - std::cerr << "Error: Target (for copy_if_different command) \"" - << args.back() << "\" is not a directory.\n"; - return 1; - } - // If error occurs we want to continue copying next files. - bool return_value = false; - for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { - cmsys::SystemTools::CopyStatus const status = - cmSystemTools::CopyFileIfDifferent(arg, args.back()); - if (!status) { - std::cerr << "Error copying file (if different) from \"" << arg - << "\" to \"" << args.back() - << "\": " << status.GetString() << '\n'; - return_value = true; - } - } - return return_value; - } - - // Copy file if newer. - if (args[1] == "copy_if_newer" && args.size() > 3) { - // If multiple source files specified, - // then destination must be directory - if ((args.size() > 4) && - (!cmSystemTools::FileIsDirectory(args.back()))) { - std::cerr << "Error: Target (for copy_if_newer command) \"" - << args.back() << "\" is not a directory.\n"; - return 1; - } - // If error occurs we want to continue copying next files. - bool return_value = false; - for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { - cmsys::SystemTools::CopyStatus const status = - cmSystemTools::CopyFileIfNewer(arg, args.back()); - if (!status) { - std::cerr << "Error copying file (if newer) from \"" << arg - << "\" to \"" << args.back() - << "\": " << status.GetString() << '\n'; - return_value = true; - } - } - return return_value; - } - // Copy directory contents - if ((args[1] == "copy_directory" || - args[1] == "copy_directory_if_different" || - args[1] == "copy_directory_if_newer") && - args.size() > 3) { - // If error occurs we want to continue copying next files. - bool return_value = false; - + if (args[1] == "copy_directory" || + args[1] == "copy_directory_if_different" || + args[1] == "copy_directory_if_newer") { cmsys::SystemTools::CopyWhen when = cmsys::SystemTools::CopyWhen::Always; if (args[1] == "copy_directory_if_different") { when = cmsys::SystemTools::CopyWhen::OnlyIfDifferent; } else if (args[1] == "copy_directory_if_newer") { when = cmsys::SystemTools::CopyWhen::OnlyIfNewer; } + std::string const& cmdName = args[1]; - for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { + using CommandArgument = + cmCommandLineArgument<bool(std::string const& value)>; + + cm::optional<std::string> targetArg; + std::vector<CommandArgument> argParsers{ + { "-t", CommandArgument::Values::One, + CommandArgument::setToValue(targetArg) }, + }; + + std::vector<std::string> dirs; + for (decltype(args.size()) i = 2; i < args.size(); i++) { + std::string const& arg = args[i]; + bool matched = false; + for (auto const& m : argParsers) { + if (m.matches(arg)) { + matched = true; + if (m.parse(arg, i, args)) { + break; + } + return 1; // failed to parse + } + } + if (!matched) { + dirs.push_back(arg); + } + } + + if (!targetArg) { + if (dirs.size() < 2) { + std::cerr << "Error: No directories or target specified (for " + << cmdName << " command).\n"; + return 1; + } + targetArg = dirs.back(); + dirs.pop_back(); + } + if (dirs.empty()) { + std::cerr << "Error: No source directories specified (for " << cmdName + << " command).\n"; + return 1; + } + + // If error occurs we want to continue copying next files. + bool return_value = false; + for (auto const& dir : dirs) { cmsys::Status const status = - cmSystemTools::CopyADirectory(arg, args.back(), when); + cmSystemTools::CopyADirectory(dir, *targetArg, when); if (!status) { - std::cerr << "Error copying directory from \"" << arg << "\" to \"" - << args.back() << "\": " << status.GetString() << '\n'; + std::cerr << "Error copying directory from \"" << dir << "\" to \"" + << *targetArg << "\": " << status.GetString() << '\n'; return_value = true; } } @@ -1250,7 +1268,7 @@ if (args[1] == "env") { #ifndef CMAKE_BOOTSTRAP - cmSystemTools::EnvDiff env; + auto envdiff = cmEnvironmentModification{}; #endif auto ai = args.cbegin() + 2; @@ -1268,7 +1286,7 @@ #ifdef CMAKE_BOOTSTRAP cmSystemTools::UnPutEnv(a.substr(8)); #else - env.UnPutEnv(a.substr(8)); + envdiff.Add(a.substr(8) + "=unset:"); #endif } else if (a == "--modify") { #ifdef CMAKE_BOOTSTRAP @@ -1281,7 +1299,7 @@ return 1; } std::string const& op = *ai; - if (!env.ParseOperation(op)) { + if (!envdiff.Add(op)) { std::cerr << "cmake -E env: invalid parameter to --modify: " << op << '\n'; return 1; @@ -1297,7 +1315,10 @@ #ifdef CMAKE_BOOTSTRAP cmSystemTools::PutEnv(a); #else - env.PutEnv(a); + auto const pos = a.find('='); + std::string const& name = a.substr(0, pos); + std::string const& value = a.substr(pos + 1); + envdiff.Add(cmStrCat(name, "=set:", value)); #endif } else { // This is the beginning of the command. @@ -1310,16 +1331,19 @@ return 1; } + auto env = cmEnvironment{}; #ifndef CMAKE_BOOTSTRAP - env.ApplyToCurrentEnv(); + env.Update(cmSystemTools::GetEnvironmentVariables()); + envdiff.ApplyTo(env); #endif // Execute command from remaining arguments. std::vector<std::string> cmd(ai, ae); int retval; - if (cmSystemTools::RunSingleCommand(cmd, nullptr, nullptr, &retval, - nullptr, - cmSystemTools::OUTPUT_PASSTHROUGH)) { + if (cmSystemTools::RunSingleCommand( + cmd, nullptr, nullptr, &retval, nullptr, + cmSystemTools::OUTPUT_PASSTHROUGH, cmDuration::zero(), + cmProcessOutput::Auto, env.GetVariables())) { return retval; } return 1; @@ -1535,7 +1559,13 @@ } // Command to concat files into one - if (args[1] == "cat" && args.size() >= 3) { + if (args[1] == "cat") { + if (args.size() == 2) { + // Destroy console buffers to drop cout/cerr encoding transform. + console.reset(); + cmCatFile(cm::nullopt); + return 0; + } int return_value = 0; bool doing_options = true; for (auto const& arg : cmMakeRange(args).advance(2)) { @@ -1543,7 +1573,7 @@ doing_options = false; // Destroy console buffers to drop cout/cerr encoding transform. console.reset(); - cmCatFile(arg); + cmCatFile(cm::nullopt); } else if (doing_options && cmHasPrefix(arg, '-')) { if (arg == "--") { doing_options = false; @@ -1897,6 +1927,7 @@ cmSystemTools::TarCompressAuto; int nCompress = 0; bool doing_options = true; + std::string encoding = "UTF-8"; for (auto const& arg : cmMakeRange(args).advance(4)) { if (doing_options && cmHasLiteralPrefix(arg, "--")) { if (arg == "--") { @@ -1934,6 +1965,13 @@ } numThreads = static_cast<decltype(numThreads)>(numThreadsLong); + } else if (cmHasLiteralPrefix(arg, "--cmake-tar-encoding=")) { + encoding = arg.substr(21); + if (encoding.empty()) { + cmSystemTools::Error( + "Encoding value is empty - it must be filled if passed"); + return 1; + } } else if (cmHasLiteralPrefix(arg, "--cmake-tar-compression-level=")) { std::string const& compressionLevelStr = arg.substr(30); @@ -2083,7 +2121,7 @@ } if (action == cmSystemTools::TarActionList) { - if (!cmSystemTools::ListTar(outFile, files, verbose)) { + if (!cmSystemTools::ListTar(outFile, files, encoding, verbose)) { cmSystemTools::Error("Problem listing tar: " + outFile); return 1; } @@ -2091,15 +2129,15 @@ if (files.empty()) { std::cerr << "tar: No files or directories specified\n"; } - if (!cmSystemTools::CreateTar(outFile, files, {}, compress, verbose, - mtime, format, compressionLevel, + if (!cmSystemTools::CreateTar(outFile, files, {}, compress, encoding, + verbose, mtime, format, compressionLevel, numThreads)) { cmSystemTools::Error(cmStrCat("Problem creating tar:\n ", outFile)); return 1; } } else if (action == cmSystemTools::TarActionExtract) { if (!cmSystemTools::ExtractTar(outFile, files, extractTimestamps, - verbose)) { + encoding, verbose)) { cmSystemTools::Error( cmStrCat("Problem extracting tar:\n ", outFile)); return 1;
diff --git a/Source/ctest.cxx b/Source/ctest.cxx index fbfd978..8e600b0 100644 --- a/Source/ctest.cxx +++ b/Source/ctest.cxx
@@ -29,6 +29,8 @@ cmDocumentationEntry const cmDocumentationOptions[] = { { "--preset <preset>, --preset=<preset>", "Read arguments from a test preset." }, + { "--presets-file <file>, --presets-file=<file>", + "Load test presets from the given file." }, { "--list-presets", "List available test presets." }, { "-C <cfg>, --build-config <cfg>", "Choose configuration to test." }, { "--progress", "Enable short progress output from tests." }, @@ -94,6 +96,13 @@ { "-T <action>, --test-action <action>", "Sets the dashboard action to " "perform" }, + { "--source-dir <path-to-source>", + "Specify the project source directory. When combined with " + "-T Configure, this allows CTest to perform an initial configure " + "step for an empty binary directory." }, + { "--build-dir <path-to-build>", + "Alias for --test-dir. Provided as a more intuitive name when " + "using -T Configure to bootstrap a build directory." }, { "--group <group>", "Specify what build group on the dashboard you'd like to " "submit results to." }, @@ -124,33 +133,41 @@ { "--no-subproject-summary", "Disable timing summary information for " "subprojects." }, - { "--test-dir <dir>", "Specify the directory in which to look for tests." }, - { "--build-and-test", "Configure, build and run a test." }, - { "--build-target", "Specify a specific target to build." }, + { "--test-dir <path-to-build>", + "Specify the directory in which to look for tests." }, + { "--build-and-test <path-to-source> <path-to-build>", + "Configure, build and run a test." }, + { "--build-target <tgt>", "Specify a specific target to build." }, { "--build-nocmake", "Run the build without running cmake first." }, - { "--build-run-dir", "Specify directory to run programs from." }, + { "--build-run-dir <dir>", "Specify directory to run programs from." }, { "--build-two-config", "Run CMake twice" }, - { "--build-exe-dir", "Specify the directory for the executable." }, - { "--build-generator", "Specify the generator to use." }, - { "--build-generator-platform", "Specify the generator-specific platform." }, - { "--build-generator-toolset", "Specify the generator-specific toolset." }, - { "--build-project", "Specify the name of the project to build." }, - { "--build-makeprogram", "Specify the make program to use." }, + { "--build-exe-dir <dir>", "Specify the directory for the executable." }, + { "--build-generator <generator-name>", "Specify the generator to use." }, + { "--build-generator-platform <platform-name>", + "Specify the generator-specific platform." }, + { "--build-generator-toolset <toolset-name>", + "Specify the generator-specific toolset." }, + { "--build-project <project-name>", + "Specify the name of the project to build." }, + { "--build-makeprogram <program-name>", "Specify the make program to use." }, { "--build-noclean", "Skip the make clean step." }, - { "--build-config-sample", - "A sample executable to use to determine the configuration" }, - { "--build-options", "Add extra options to the build step." }, - - { "--test-command", "The test to run with the --build-and-test option." }, - { "--test-timeout", "The time limit in seconds, internal use only." }, - { "--test-load", "CPU load threshold for starting new parallel tests." }, + { "--build-config-sample <exe-name>", + "A sample executable to use to determine the configuration." }, + { "--build-options [<options>...]", "Add extra options to the build step." }, + { "--test-command <command>", + "The test to run with the --build-and-test option." }, + { "--test-timeout <timeout>", + "The time limit in seconds, internal use only." }, + { "--test-load <level>", + "CPU load threshold for starting new parallel tests." }, { "--tomorrow-tag", "Nightly or experimental starts with next day tag." }, - { "--overwrite", "Overwrite CTest configuration option." }, + { "--overwrite <option-name>", "Overwrite CTest configuration option." }, { "--extra-submit <file>[;<file>]", "Submit extra files to the dashboard." }, { "--http-header <header>", "Append HTTP header when submitting" }, { "--schedule-random", "Use a random order for scheduling tests" }, - { "--schedule-random-seed", "Override seed for random order of tests" }, - { "--submit-index", + { "--schedule-random-seed <seed>", + "Override seed for random order of tests" }, + { "--submit-index <index>", "Submit individual dashboard tests with specific index" }, { "--timeout <seconds>", "Set the default test timeout." }, { "--stop-time <time>", @@ -159,7 +176,11 @@ { "--no-compress-output", "Do not compress test output when submitting." }, { "--print-labels", "Print all available test labels." }, { "--no-tests=<[error|ignore]>", - "Regard no tests found either as 'error' or 'ignore' it." } + "Regard no tests found either as 'error' or 'ignore' it." }, + { "--collect-instrumentation <build>", + "Manually collect instrumentation data from the specified build " + "directory." }, + { "--", "Forward extra arguments to test executables." }, }; } // anonymous namespace @@ -209,12 +230,13 @@ return 1; } + bool const haveTestfile = cmSystemTools::FileExists("CTestTestfile.cmake") || + cmSystemTools::FileExists("DartTestfile.txt"); + // If there is a testing input file, check for documentation options // only if there are actually arguments. We want running without // arguments to run tests. - if (argc > 1 || - !(cmSystemTools::FileExists("CTestTestfile.cmake") || - cmSystemTools::FileExists("DartTestfile.txt"))) { + if (argc > 1 || !haveTestfile) { if (argc == 1) { std::cerr << "*********************************\n" "No test configuration file found!\n" @@ -228,7 +250,8 @@ doc.SetSection("Name", cmDocumentationName); doc.SetSection("Usage", cmDocumentationUsage); doc.PrependSection("Options", cmDocumentationOptions); - return !doc.PrintRequestedDocumentation(std::cout); + return !doc.PrintRequestedDocumentation(std::cout) || + (argc == 1 && !haveTestfile); } }
diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx index be312cc..cde43ca 100644 --- a/Source/kwsys/CommandLineArguments.cxx +++ b/Source/kwsys/CommandLineArguments.cxx
@@ -242,7 +242,7 @@ break; default: std::cerr << "Got unknown argument type: \"" << cs->ArgumentType - << "\"" << std::endl; + << '"' << std::endl; this->Internals->LastArgument--; return 0; } @@ -259,7 +259,7 @@ CommandLineArguments_DEBUG("Store unused argument " << arg); this->Internals->UnusedArguments.push_back(arg); } else { - std::cerr << "Got unknown argument: \"" << arg << "\"" << std::endl; + std::cerr << "Got unknown argument: \"" << arg << '"' << std::endl; this->Internals->LastArgument--; return 0; } @@ -566,7 +566,7 @@ } if (cnt > 0) { for (cc = 0; cc < maxlen; cc++) { - str << " "; + str << ' '; } } CommandLineArguments::Internal::String::size_type skip = len; @@ -751,8 +751,8 @@ static_cast<std::vector<std::string>*>(cs->Variable), var); break; default: - std::cerr << "Got unknown variable type: \"" << cs->VariableType - << "\"" << std::endl; + std::cerr << "Got unknown variable type: \"" << cs->VariableType << '"' + << std::endl; this->Internals->LastArgument--; return false; }
diff --git a/Source/kwsys/EncodingCXX.cxx b/Source/kwsys/EncodingCXX.cxx index 885a1f7..1d1eed8 100644 --- a/Source/kwsys/EncodingCXX.cxx +++ b/Source/kwsys/EncodingCXX.cxx
@@ -19,6 +19,7 @@ # include "String.h" #endif +#include <algorithm> #include <cstdlib> #include <cstring> #include <vector> @@ -131,13 +132,10 @@ MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), int(str.size()), nullptr, 0); if (wlength > 0) { - wchar_t* wdata = new wchar_t[wlength]; + wstr.resize(wlength); int r = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), - int(str.size()), wdata, wlength); - if (r > 0) { - wstr = std::wstring(wdata, wlength); - } - delete[] wdata; + int(str.size()), &wstr[0], wlength); + wstr.resize(static_cast<size_t>((std::max)(0, r))); } #else size_t pos = 0; @@ -164,14 +162,11 @@ WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(), int(str.size()), nullptr, 0, nullptr, nullptr); if (length > 0) { - char* data = new char[length]; + nstr.resize(length); int r = WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(), - int(str.size()), data, length, nullptr, nullptr); - if (r > 0) { - nstr = std::string(data, length); - } - delete[] data; + int(str.size()), &nstr[0], length, nullptr, nullptr); + nstr.resize(static_cast<size_t>((std::max)(0, r))); } #else size_t pos = 0; @@ -198,10 +193,9 @@ return wstr; } ++length; - std::vector<wchar_t> wchars(length); - if (kwsysEncoding_mbstowcs(wchars.data(), cstr, length) > 0) { - wstr = wchars.data(); - } + wstr.resize(length); + length = kwsysEncoding_mbstowcs(&wstr[0], cstr, length); + wstr.resize(length); return wstr; } @@ -213,45 +207,78 @@ return str; } ++length; - std::vector<char> chars(length); - if (kwsysEncoding_wcstombs(chars.data(), wcstr, length) > 0) { - str = chars.data(); - } + str.resize(length); + length = kwsysEncoding_wcstombs(&str[0], wcstr, length); + str.resize(length); return str; } #if defined(_WIN32) -// Convert local paths to UNC style paths +// Convert local paths to UNC style paths: +// * https://web.archive.org/web/20250329050419/ +// https://cpp.arsenmk.com/2015/12/handling-long-paths-on-windows.html + +namespace { +// UNC prefix for local paths. +std::wstring const unc_local_prefix = L"\\\\?\\"; + +// UNC prefix for remote paths. +std::wstring const unc_remote_prefix = L"\\\\?\\UNC\\"; + +// The longest UNC prefix replaces two leading backslashes. +size_t const unc_max_space = unc_remote_prefix.length() - 2; +} + std::wstring Encoding::ToWindowsExtendedPath(std::string const& source) { return ToWindowsExtendedPath(ToWide(source)); } -// Convert local paths to UNC style paths std::wstring Encoding::ToWindowsExtendedPath(char const* source) { return ToWindowsExtendedPath(ToWide(source)); } -// Convert local paths to UNC style paths std::wstring Encoding::ToWindowsExtendedPath(std::wstring const& wsource) { // Resolve any relative paths + std::wstring wfull; DWORD wfull_len; - /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that - * won't return a large enough buffer size if the input is too small */ - wfull_len = GetFullPathNameW(wsource.c_str(), 0, nullptr, nullptr) + 3; - std::vector<wchar_t> wfull(wfull_len); - GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], nullptr); + // Try with a stack-allocated buffer first. + wchar_t local_buf[512]; + size_t const local_buf_len = sizeof(local_buf) / sizeof(local_buf[0]); + wfull_len = + GetFullPathNameW(wsource.c_str(), local_buf_len, local_buf, nullptr); + if (wfull_len <= local_buf_len) { + // The stack-allocated buffer was large enough. It holds the result. - /* This should get the correct size without any extra padding from the - * previous size workaround. */ - wfull_len = static_cast<DWORD>(wcslen(&wfull[0])); + // Reserve room for the prefix added below. + wfull.reserve(wfull_len + unc_max_space); + + // Store the absolute path to be returned. + wfull.assign(local_buf, wfull_len); + } else { + // The stack-allocated buffer was too small, but now we know how + // much to allocate on the heap. + + // Some versions of GetFullPathNameW return a slightly too-small size. + wfull_len += 3; + + // Reserve room for the prefix added below. + wfull.reserve(wfull_len + unc_max_space); + + // Try again with a heap-allocated buffer. + wfull.resize(wfull_len, L'\0'); + wfull_len = + GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], nullptr); + wfull.resize(wfull_len); + } if (wfull_len >= 2 && kwsysString_isalpha(wfull[0]) && wfull[1] == L':') { /* C:\Foo\bar\FooBar.txt */ - return L"\\\\?\\" + std::wstring(&wfull[0]); + wfull.insert(0, unc_local_prefix); + return wfull; } else if (wfull_len >= 2 && wfull[0] == L'\\' && wfull[1] == L'\\') { /* Starts with \\ */ if (wfull_len >= 4 && wfull[2] == L'?' && @@ -259,24 +286,27 @@ if (wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' && wfull[6] == L'C' && wfull[7] == L'\\') { /* \\?\UNC\Foo\bar\FooBar.txt */ - return std::wstring(&wfull[0]); + return wfull; } else if (wfull_len >= 6 && kwsysString_isalpha(wfull[4]) && wfull[5] == L':') { /* \\?\C:\Foo\bar\FooBar.txt */ - return std::wstring(&wfull[0]); + return wfull; } else if (wfull_len >= 5) { /* \\?\Foo\bar\FooBar.txt */ - return L"\\\\?\\UNC\\" + std::wstring(&wfull[4]); + wfull.replace(0, 4, unc_remote_prefix); + return wfull; } } else if (wfull_len >= 4 && wfull[2] == L'.' && wfull[3] == L'\\') { /* Starts with \\.\ a device name */ if (wfull_len >= 6 && kwsysString_isalpha(wfull[4]) && wfull[5] == L':') { /* \\.\C:\Foo\bar\FooBar.txt */ - return L"\\\\?\\" + std::wstring(&wfull[4]); + wfull.replace(0, 4, unc_local_prefix); + return wfull; } else if (wfull_len >= 5) { /* \\.\Foo\bar\ Device name is left unchanged */ - return std::wstring(&wfull[0]); + return wfull; } } else if (wfull_len >= 3) { /* \\Foo\bar\FooBar.txt */ - return L"\\\\?\\UNC\\" + std::wstring(&wfull[2]); + wfull.replace(0, 2, unc_remote_prefix); + return wfull; } }
diff --git a/Source/kwsys/FStream.hxx.in b/Source/kwsys/FStream.hxx.in index d6d6f84..25c2d33 100644 --- a/Source/kwsys/FStream.hxx.in +++ b/Source/kwsys/FStream.hxx.in
@@ -28,15 +28,13 @@ template <typename CharType, typename Traits> class basic_filebuf : public std::basic_filebuf<CharType, Traits> { -# if _MSC_VER >= 1400 public: typedef std::basic_filebuf<CharType, Traits> my_base_type; - basic_filebuf* open(char const* s, std::ios_base::openmode mode) + basic_filebuf* open(std::string const& s, std::ios_base::openmode mode) { std::wstring const wstr = Encoding::ToWindowsExtendedPath(s); - return static_cast<basic_filebuf*>(my_base_type::open(wstr.c_str(), mode)); + return static_cast<basic_filebuf*>(my_base_type::open(wstr, mode)); } -# endif }; # else @@ -46,23 +44,23 @@ std::wstring cmode; bool plus = false; if (mode & std::ios_base::app) { - cmode += L"a"; + cmode += L'a'; plus = mode & std::ios_base::in ? true : false; } else if (mode & std::ios_base::trunc || (mode & std::ios_base::out && (mode & std::ios_base::in) == 0)) { - cmode += L"w"; + cmode += L'w'; plus = mode & std::ios_base::in ? true : false; } else { - cmode += L"r"; + cmode += L'r'; plus = mode & std::ios_base::out ? true : false; } if (plus) { - cmode += L"+"; + cmode += L'+'; } if (mode & std::ios_base::binary) { - cmode += L"b"; + cmode += L'b'; } else { - cmode += L"t"; + cmode += L't'; } return cmode; }; @@ -85,7 +83,7 @@ buf_ = 0; } - bool _open(char const* file_name, std::ios_base::openmode mode) + bool _open(std::string const& file_name, std::ios_base::openmode mode) { if (_is_open() || file_) { return false; @@ -182,7 +180,7 @@ this->buf_ = static_cast<internal_buffer_type*>(internal_stream_type::rdbuf()); } - explicit basic_fstream(char const* file_name, + explicit basic_fstream(std::string const& file_name, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) : internal_stream_type(new internal_buffer_type()) @@ -192,7 +190,7 @@ open(file_name, mode); } - void open(char const* file_name, + void open(std::string const& file_name, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { @@ -226,7 +224,7 @@ this->buf_ = static_cast<internal_buffer_type*>(internal_stream_type::rdbuf()); } - explicit basic_ifstream(char const* file_name, + explicit basic_ifstream(std::string const& file_name, std::ios_base::openmode mode = std::ios_base::in) : internal_stream_type(new internal_buffer_type()) { @@ -235,7 +233,7 @@ open(file_name, mode); } - void open(char const* file_name, + void open(std::string const& file_name, std::ios_base::openmode mode = std::ios_base::in) { mode = mode | std::ios_base::in; @@ -271,7 +269,7 @@ this->buf_ = static_cast<internal_buffer_type*>(internal_stream_type::rdbuf()); } - explicit basic_ofstream(char const* file_name, + explicit basic_ofstream(std::string const& file_name, std::ios_base::openmode mode = std::ios_base::out) : internal_stream_type(new internal_buffer_type()) { @@ -279,7 +277,7 @@ static_cast<internal_buffer_type*>(internal_stream_type::rdbuf()); open(file_name, mode); } - void open(char const* file_name, + void open(std::string const& file_name, std::ios_base::openmode mode = std::ios_base::out) { mode = mode | std::ios_base::out;
diff --git a/Source/kwsys/Glob.cxx b/Source/kwsys/Glob.cxx index 3ac75f6..9d48bc2 100644 --- a/Source/kwsys/Glob.cxx +++ b/Source/kwsys/Glob.cxx
@@ -125,11 +125,11 @@ std::string::const_iterator k = bracket_first; // Open the regex block. - regex += "["; + regex += '['; // A regex range complement uses '^' instead of '!'. if (k != bracket_last && *k == '!') { - regex += "^"; + regex += '^'; ++k; } @@ -137,7 +137,7 @@ for (; k != bracket_last; ++k) { // Backslashes must be escaped. if (*k == '\\') { - regex += "\\"; + regex += '\\'; } // Store this character. @@ -145,7 +145,7 @@ } // Close the regex block. - regex += "]"; + regex += ']'; // Jump to the end of the bracket string. i = bracket_last; @@ -156,7 +156,7 @@ if (!(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('0' <= ch && ch <= '9'))) { // Escape the non-alphanumeric character. - regex += "\\"; + regex += '\\'; } #if defined(KWSYS_GLOB_CASE_INDEPENDENT) else { @@ -174,7 +174,7 @@ } if (require_whole_string) { - regex += "$"; + regex += '$'; } return regex; } @@ -189,7 +189,7 @@ if (!errorMessage.empty()) { messages->push_back(Message(Glob::warning, "Error listing directory '" + dir + - "'! Reason: '" + errorMessage + "'")); + "'! Reason: '" + errorMessage + '\'')); } } return true; @@ -198,7 +198,7 @@ std::string realname; std::string fname; for (cc = 0; cc < d.GetNumberOfFiles(); cc++) { - fname = d.GetFile(cc); + fname = d.GetFileName(cc); if (fname == "." || fname == "..") { continue; } @@ -206,7 +206,7 @@ if (start == 0) { realname = dir + fname; } else { - realname = dir + "/" + fname; + realname = dir + '/' + fname; } #if defined(KWSYS_GLOB_CASE_INDEPENDENT) @@ -229,7 +229,7 @@ messages->push_back( Message(Glob::error, "Canonical path generation from path '" + dir + - "' failed! Reason: '" + realPathErrorMessage + "'")); + "' failed! Reason: '" + realPathErrorMessage + '\'')); } return false; } @@ -257,9 +257,9 @@ std::find(this->VisitedSymlinks.begin(), this->VisitedSymlinks.end(), canonicalPath); pathIt != this->VisitedSymlinks.end(); ++pathIt) { - message += *pathIt + "\n"; + message += *pathIt + '\n'; } - message += canonicalPath + "/" + fname; + message += canonicalPath + '/' + fname; messages->push_back(Message(Glob::cyclicRecursion, message)); } } else { @@ -305,7 +305,7 @@ std::string realname; std::string fname; for (cc = 0; cc < d.GetNumberOfFiles(); cc++) { - fname = d.GetFile(cc); + fname = d.GetFileName(cc); if (fname == "." || fname == "..") { continue; } @@ -313,7 +313,7 @@ if (start == 0) { realname = dir + fname; } else { - realname = dir + "/" + fname; + realname = dir + '/' + fname; } #if defined(KWSYS_GLOB_CASE_INDEPENDENT) @@ -353,7 +353,8 @@ if (!kwsys::SystemTools::FileIsFullPath(expr)) { expr = kwsys::SystemTools::GetCurrentWorkingDirectory(); - expr += "/" + inexpr; + expr += '/'; + expr += inexpr; } std::string fexpr = expr; @@ -416,7 +417,7 @@ // Handle network paths if (skip > 0) { - this->ProcessDirectory(0, fexpr.substr(0, skip) + "/", messages); + this->ProcessDirectory(0, fexpr.substr(0, skip) + '/', messages); } else { this->ProcessDirectory(0, "/", messages); }
diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c index 26ec57d..07e19cd 100644 --- a/Source/kwsys/ProcessWin32.c +++ b/Source/kwsys/ProcessWin32.c
@@ -1706,16 +1706,17 @@ } /* Create inherited copies of the handles. */ - (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput, - si->hStdInput, 1)) || - (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput, - si->hStdOutput, 0)) || - (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError, - si->hStdError, 0)) || - /* Create the process. */ - (!CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, creationFlags, 0, 0, - &si->StartupInfo, &cp->ProcessInformation[index]) && - (error = GetLastError())); + (void)((error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput, + si->hStdInput, 1)) || + (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput, + si->hStdOutput, 0)) || + (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError, + si->hStdError, 0)) || + /* Create the process. */ + (!CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, creationFlags, 0, + 0, &si->StartupInfo, + &cp->ProcessInformation[index]) && + (error = GetLastError()))); /* Close the inherited copies of the handles. */ if (si->StartupInfo.hStdInput != si->hStdInput) { @@ -2354,7 +2355,7 @@ if (hNT) { /* Get pointers to the needed API functions. */ self->P_ZwQuerySystemInformation = - ((ZwQuerySystemInformationType)GetProcAddress( + ((ZwQuerySystemInformationType)(void*)GetProcAddress( hNT, "ZwQuerySystemInformation")); } if (!self->P_ZwQuerySystemInformation) { @@ -2418,12 +2419,13 @@ static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self) { - return self->CurrentInfo ? self->CurrentInfo->ProcessId : -1; + return self->CurrentInfo ? (int)self->CurrentInfo->ProcessId : -1; } static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self) { - return self->CurrentInfo ? self->CurrentInfo->InheritedFromProcessId : -1; + return self->CurrentInfo ? (int)self->CurrentInfo->InheritedFromProcessId + : -1; } static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self) @@ -2435,12 +2437,12 @@ HMODULE hKernel = GetModuleHandleW(L"kernel32.dll"); if (hKernel) { self->P_CreateToolhelp32Snapshot = - ((CreateToolhelp32SnapshotType)GetProcAddress( + ((CreateToolhelp32SnapshotType)(void*)GetProcAddress( hKernel, "CreateToolhelp32Snapshot")); self->P_Process32First = - ((Process32FirstType)GetProcAddress(hKernel, "Process32First")); + ((Process32FirstType)(void*)GetProcAddress(hKernel, "Process32First")); self->P_Process32Next = - ((Process32NextType)GetProcAddress(hKernel, "Process32Next")); + ((Process32NextType)(void*)GetProcAddress(hKernel, "Process32Next")); } return (self->P_CreateToolhelp32Snapshot && self->P_Process32First && self->P_Process32Next) @@ -2488,12 +2490,12 @@ static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self) { - return self->Snapshot ? self->CurrentEntry.th32ProcessID : -1; + return self->Snapshot ? (int)self->CurrentEntry.th32ProcessID : -1; } static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self) { - return self->Snapshot ? self->CurrentEntry.th32ParentProcessID : -1; + return self->Snapshot ? (int)self->CurrentEntry.th32ParentProcessID : -1; } static void kwsysProcessKill(DWORD pid)
diff --git a/Source/kwsys/Status.cxx b/Source/kwsys/Status.cxx index 1c343bc..4cadd8f 100644 --- a/Source/kwsys/Status.cxx +++ b/Source/kwsys/Status.cxx
@@ -46,7 +46,7 @@ #ifdef _WIN32 case Kind::Windows: { LPWSTR message = NULL; - DWORD size = FormatMessageW( + FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, this->Windows_, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx index 0a472d3..743c224 100644 --- a/Source/kwsys/SystemInformation.cxx +++ b/Source/kwsys/SystemInformation.cxx
@@ -602,7 +602,7 @@ oss << this->GetNumberOfPhysicalCPU() << " core "; if (this->GetModelName().empty()) { oss << this->GetProcessorClockFrequency() << " MHz " - << this->GetVendorString() << " " << this->GetExtendedProcessorName(); + << this->GetVendorString() << ' ' << this->GetExtendedProcessorName(); } else { oss << this->GetModelName(); } @@ -679,7 +679,7 @@ std::string SystemInformation::GetOSDescription() { std::ostringstream oss; - oss << this->GetOSName() << " " << this->GetOSRelease() << " " + oss << this->GetOSName() << ' ' << this->GetOSRelease() << ' ' << this->GetOSVersion(); return oss.str(); @@ -961,7 +961,7 @@ oss << std::endl << "=========================================================" << std::endl - << "Process id " << getpid() << " "; + << "Process id " << getpid() << ' '; switch (sigNo) { case SIGINT: oss << "Caught SIGINT"; @@ -977,7 +977,7 @@ case SIGFPE: oss << "Caught SIGFPE at " << (sigInfo->si_addr ? "" : "0x") - << sigInfo->si_addr << " "; + << sigInfo->si_addr << ' '; switch (sigInfo->si_code) { # if defined(FPE_INTDIV) case FPE_INTDIV: @@ -1025,7 +1025,7 @@ case SIGSEGV: oss << "Caught SIGSEGV at " << (sigInfo->si_addr ? "" : "0x") - << sigInfo->si_addr << " "; + << sigInfo->si_addr << ' '; switch (sigInfo->si_code) { case SEGV_MAPERR: oss << "address not mapped to object"; @@ -1043,7 +1043,7 @@ case SIGBUS: oss << "Caught SIGBUS at " << (sigInfo->si_addr ? "" : "0x") - << sigInfo->si_addr << " "; + << sigInfo->si_addr << ' '; switch (sigInfo->si_code) { case BUS_ADRALN: oss << "invalid address alignment"; @@ -1083,7 +1083,7 @@ case SIGILL: oss << "Caught SIGILL at " << (sigInfo->si_addr ? "" : "0x") - << sigInfo->si_addr << " "; + << sigInfo->si_addr << ' '; switch (sigInfo->si_code) { case ILL_ILLOPC: oss << "illegal opcode"; @@ -1248,8 +1248,8 @@ { # if defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP) os << std::hex << sp.GetAddress() << " : " << sp.GetFunction() << " [(" - << sp.GetBinary() << ") " << sp.GetSourceFile() << ":" << std::dec - << sp.GetLineNumber() << "]"; + << sp.GetBinary() << ") " << sp.GetSourceFile() << ':' << std::dec + << sp.GetLineNumber() << ']'; # elif defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE) void* addr = sp.GetAddress(); char** syminfo = backtrace_symbols(&addr, 1); @@ -3601,7 +3601,7 @@ std::string cpuflags = this->ExtractValueFromCpuInfoFile(buffer, "flags"); if (!cpurev.empty()) { // now we can match every flags as space + flag + space - cpuflags = " " + cpuflags + " "; + cpuflags = ' ' + cpuflags + ' '; if ((cpuflags.find(" fpu ") != std::string::npos)) { this->Features.HasFPU = true; } @@ -3919,8 +3919,8 @@ // Old windows.h headers do not provide GetSystemTimes. using GetSystemTimesType = BOOL(WINAPI*)(LPFILETIME, LPFILETIME, LPFILETIME); static GetSystemTimesType pGetSystemTimes = - (GetSystemTimesType)GetProcAddress(GetModuleHandleW(L"kernel32"), - "GetSystemTimes"); + (GetSystemTimesType)(void*)GetProcAddress(GetModuleHandleW(L"kernel32"), + "GetSystemTimes"); FILETIME idleTime, kernelTime, userTime; if (pGetSystemTimes && pGetSystemTimes(&idleTime, &kernelTime, &userTime)) { unsigned __int64 const idleTicks = fileTimeToUInt64(idleTime); @@ -4469,7 +4469,7 @@ using GetLogicalProcessorInformationType = BOOL(WINAPI*)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); static GetLogicalProcessorInformationType pGetLogicalProcessorInformation = - reinterpret_cast<GetLogicalProcessorInformationType>(GetProcAddress( + reinterpret_cast<GetLogicalProcessorInformationType>((void*)GetProcAddress( GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation")); if (!pGetLogicalProcessorInformation) { @@ -5068,7 +5068,7 @@ size_t pos = buffer.find("System RAM:"); if (pos == std::string::npos) return false; - pos = buffer.find(":", pos); + pos = buffer.find(':', pos); size_t pos2 = buffer.find("M (", pos); if (pos2 == std::string::npos) return false; @@ -5141,7 +5141,7 @@ pos2 = buffer.find(" Stepping", pos); if (pos2 != std::string::npos) { - pos2 = buffer.find(" ", pos2 + 1); + pos2 = buffer.find(' ', pos2 + 1); if (pos2 != std::string::npos && pos2 < pos3) { this->ChipID.Revision = atoi(buffer.substr(pos2 + 1, pos3 - pos2).c_str()); @@ -5330,7 +5330,8 @@ this->OSName = "Windows"; - OSVERSIONINFOEXW osvi = { sizeof(osvi) }; + OSVERSIONINFOEXW osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(osvi); # ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx # pragma warning(push) # ifdef __INTEL_COMPILER @@ -5354,8 +5355,8 @@ # endif // Produce the release like it is displayed in `cmd` - this->OSRelease = std::to_string(osvi.dwMajorVersion) + "." + - std::to_string(osvi.dwMinorVersion) + "." + + this->OSRelease = std::to_string(osvi.dwMajorVersion) + '.' + + std::to_string(osvi.dwMinorVersion) + '.' + std::to_string(osvi.dwBuildNumber & 0xFFFF); struct VersionNames
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 7e23c16..b7e0960 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx
@@ -563,7 +563,7 @@ // actual case of servers and shares. if (path_components.size() > 2 && path_components[0] == "//") { casePath += path_components[idx++]; - casePath += "/"; + casePath += '/'; casePath += path_components[idx++]; sep = "/"; } @@ -2053,7 +2053,7 @@ static void ConvertVMSToUnix(std::string& path) { std::string::size_type rootEnd = path.find(":["); - std::string::size_type pathEnd = path.find("]"); + std::string::size_type pathEnd = path.find(']'); if (rootEnd != std::string::npos) { std::string root = path.substr(0, rootEnd); std::string pathPart = path.substr(rootEnd + 2, pathEnd - rootEnd - 2); @@ -2065,7 +2065,7 @@ } pos0++; } - path = "/" + root + "/" + pathPart; + path = '/' + root + '/' + pathPart; } } #endif @@ -2660,15 +2660,15 @@ } for (size_t fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) { - if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") != 0 && - strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..") != 0) { + std::string const& filename = dir.GetFileName(fileNum); + if (filename != "." && filename != "..") { std::string fullPath = source; - fullPath += "/"; - fullPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + fullPath += '/'; + fullPath += filename; if (SystemTools::FileIsDirectory(fullPath)) { std::string fullDestPath = destination; - fullDestPath += "/"; - fullDestPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + fullDestPath += '/'; + fullDestPath += filename; status = SystemTools::CopyADirectory(fullPath, fullDestPath, when); if (!status.IsSuccess()) { return status; @@ -2841,11 +2841,11 @@ size_t fileNum; for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) { - if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") != 0 && - strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..") != 0) { + std::string const& filename = dir.GetFileName(fileNum); + if (filename != "." && filename != "..") { std::string fullPath = source; - fullPath += "/"; - fullPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + fullPath += '/'; + fullPath += filename; if (SystemTools::FileIsDirectory(fullPath) && !SystemTools::FileIsSymlink(fullPath)) { status = SystemTools::RemoveADirectory(fullPath); @@ -3035,9 +3035,13 @@ size_t length = inName.size(); char const* name = inName.c_str(); + if (length == 0) { + return name; + } + size_t last = length - 1; if (last > 0 && (name[last] == '/' || name[last] == '\\') && - strcmp(name, "/") != 0 && name[last - 1] != ':') { + name[last - 1] != ':') { if (last < sizeof(local_buffer)) { memcpy(local_buffer, name, last); local_buffer[last] = '\0'; @@ -3076,6 +3080,10 @@ bool SystemTools::FileIsExecutable(std::string const& inName) { + if (inName.empty()) { + return false; + } + #ifdef _WIN32 char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; std::string string_buffer; @@ -3158,44 +3166,6 @@ #endif } -Status SystemTools::CreateSymlink(std::string const& origName, - std::string const& newName) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - DWORD flags; - if (FileIsDirectory(origName)) { - flags = SYMBOLIC_LINK_FLAG_DIRECTORY; - } else { - flags = 0; - } - - std::wstring origPath = Encoding::ToWindowsExtendedPath(origName); - std::wstring newPath = Encoding::ToWindowsExtendedPath(newName); - - Status status; - if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(), - flags | - SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) { - status = Status::Windows_GetLastError(); - } - // Older Windows versions do not understand - // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE - if (status.GetWindows() == ERROR_INVALID_PARAMETER) { - status = Status::Success(); - if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(), flags)) { - status = Status::Windows_GetLastError(); - } - } - - return status; -#else - if (symlink(origName.c_str(), newName.c_str()) < 0) { - return Status::POSIX_errno(); - } - return Status::Success(); -#endif -} - Status SystemTools::ReadSymlink(std::string const& newName, std::string& origName) { @@ -3970,7 +3940,7 @@ std::string temp = dir; if (need_slash) { - temp += "/"; + temp += '/'; } temp += filename_base; @@ -4000,11 +3970,11 @@ break; } - filename_dir_bases = filename_dir_base + "/" + filename_dir_bases; + filename_dir_bases = filename_dir_base + '/' + filename_dir_bases; temp = dir; if (need_slash) { - temp += "/"; + temp += '/'; } temp += filename_dir_bases; @@ -4128,7 +4098,7 @@ { std::string str(s); if (str.find_first_of("0123456789") == 0) { - str = "_" + str; + str = '_' + str; } std::string permited_chars("_" @@ -4523,10 +4493,10 @@ res += " Advanced Server"; } - res += " "; + res += ' '; snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMajorVersion); res += buffer; - res += "."; + res += '.'; snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMinorVersion); res += buffer; } @@ -4549,26 +4519,26 @@ res += " Service Pack 6a (Build "; snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF); res += buffer; - res += ")"; + res += ')'; } else // Windows NT 4.0 prior to SP6a { - res += " "; + res += ' '; res += osvi.szCSDVersion; res += " (Build "; snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF); res += buffer; - res += ")"; + res += ')'; } RegCloseKey(hKey); } else // Windows NT 3.51 and earlier or Windows 2000 and later { - res += " "; + res += ' '; res += osvi.szCSDVersion; res += " (Build "; snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF); res += buffer; - res += ")"; + res += ')'; } break;
diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index 796924e..12b3740 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in
@@ -786,13 +786,6 @@ double percent_bin = 0.05); /** - * Create a symbolic link if the platform supports it. Returns whether - * creation succeeded. - */ - static Status CreateSymlink(std::string const& origName, - std::string const& newName); - - /** * Read the contents of a symbolic link. Returns whether reading * succeeded. */
diff --git a/Source/kwsys/testProcess.c b/Source/kwsys/testProcess.c index 3c61ada..1ab746b 100644 --- a/Source/kwsys/testProcess.c +++ b/Source/kwsys/testProcess.c
@@ -133,9 +133,16 @@ #ifdef CRASH_USING_ABORT abort(); #else +# if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Warray-bounds" +# endif assert(invalidAddress); /* Quiet Clang scan-build. */ /* Provoke deliberate crash by writing to the invalid address. */ *invalidAddress = 0; +# if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic pop +# endif #endif fprintf(stdout, "Output after crash on stdout from crash test.\n"); fprintf(stderr, "Output after crash on stderr from crash test.\n");
diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx index b94b8f6..e48525d 100644 --- a/Source/kwsys/testSystemTools.cxx +++ b/Source/kwsys/testSystemTools.cxx
@@ -445,29 +445,6 @@ res = false; } - std::string const testBadSymlink(testNewDir + "/badSymlink.txt"); - std::string const testBadSymlinkTgt(testNewDir + "/missing/symlinkTgt.txt"); - kwsys::Status const symlinkStatus = - kwsys::SystemTools::CreateSymlink(testBadSymlinkTgt, testBadSymlink); -#if defined(_WIN32) - // Under Windows, the user may not have enough privileges to create symlinks - if (symlinkStatus.GetWindows() != ERROR_PRIVILEGE_NOT_HELD) -#endif - { - if (!symlinkStatus.IsSuccess()) { - std::cerr << "CreateSymlink for: " << testBadSymlink << " -> " - << testBadSymlinkTgt - << " failed: " << symlinkStatus.GetString() << std::endl; - res = false; - } - - if (!kwsys::SystemTools::Touch(testBadSymlink, false)) { - std::cerr << "Problem with Touch (no create) for: " << testBadSymlink - << std::endl; - res = false; - } - } - if (!kwsys::SystemTools::Touch(testNewDir, false)) { std::cerr << "Problem with Touch (no create) for: " << testNewDir << std::endl;
diff --git a/Templates/CTestScript.cmake.in b/Templates/CTestScript.cmake.in index 54bfda4..4282a01 100644 --- a/Templates/CTestScript.cmake.in +++ b/Templates/CTestScript.cmake.in
@@ -48,6 +48,7 @@ set(CTEST_RESOURCE_SPEC_FILE "@CTEST_RESOURCE_SPEC_FILE@") set(CTEST_TEST_LOAD "@CTEST_TEST_LOAD@") set(CTEST_TEST_TIMEOUT "@DART_TESTING_TIMEOUT@") +set(CTEST_TEST_COVERAGE_TOOL "@COVERAGE_TOOL@") # CTest Coverage Step set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@")
diff --git a/Templates/TestDriver.cxx.in b/Templates/TestDriver.cxx.in index e7693e2..70ec551 100644 --- a/Templates/TestDriver.cxx.in +++ b/Templates/TestDriver.cxx.in
@@ -88,6 +88,7 @@ { int i; int testNum = 0; + int show_only; int partial_match; int run_all; char *arg; @@ -117,14 +118,22 @@ ac--; av++; } + show_only = 0; partial_match = 0; run_all = 0; arg = CM_NULL; /* NOLINT */ /* If partial match or running all tests are requested. */ if (testToRun == -1 && ac > 1) { + show_only = (strcmp(av[1], "-N") == 0) ? 1 : 0; partial_match = (strcmp(av[1], "-R") == 0) ? 1 : 0; run_all = (strcmp(av[1], "-A") == 0) ? 1 : 0; } + if (show_only == 1) { + for (i = 0; i < NumTests; ++i) { + printf("%s\n", cmakeGeneratedFunctionMapEntries[i].name); + } + return 0; + } if (partial_match != 0 && ac < 3) { printf("-R needs an additional parameter.\n"); return -1;
diff --git a/Tests/CMakeGUI/CMakeGUITest.cmake b/Tests/CMakeGUI/CMakeGUITest.cmake index b3df6d1..7f0d187 100644 --- a/Tests/CMakeGUI/CMakeGUITest.cmake +++ b/Tests/CMakeGUI/CMakeGUITest.cmake
@@ -151,7 +151,7 @@ run_cmake_gui_test(presetArg:preset ARGS - -S "${CMakeGUITest_BINARY_DIR}/presetArg-preset/src" + -S "${CMakeGUITest_BINARY_DIR}/presetArg-preset/src/" "--preset=ninja" ) run_cmake_gui_test(presetArg:presetBinary
diff --git a/Tests/CMakeGUI/CMakeGUITest.cxx b/Tests/CMakeGUI/CMakeGUITest.cxx index 095e378..99c006b 100644 --- a/Tests/CMakeGUI/CMakeGUITest.cxx +++ b/Tests/CMakeGUI/CMakeGUITest.cxx
@@ -2,7 +2,6 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "CMakeGUITest.h" -#include "QCMake.h" #include <QApplication> #include <QEventLoop> #include <QFile> @@ -21,6 +20,7 @@ #include "CatchShow.h" #include "FirstConfigure.h" +#include "QCMake.h" using WindowSetupHelper = std::function<void(CMakeSetupDialog*)>; Q_DECLARE_METATYPE(WindowSetupHelper) @@ -287,6 +287,14 @@ /*Advanced=*/false, }, QCMakeProperty{ + /*Key=*/"SOURCE_DIR_NAME_VARIABLE", + /*Value=*/"src", + /*Strings=*/{}, + /*Help=*/"", + /*Type=*/QCMakeProperty::STRING, + /*Advanced=*/false, + }, + QCMakeProperty{ /*Key=*/"STRING_VARIABLE", /*Value=*/"String value", /*Strings=*/{},
diff --git a/Tests/CMakeGUI/presetArg-noPresetBinaryChange/CMakePresets.json.in b/Tests/CMakeGUI/presetArg-noPresetBinaryChange/CMakePresets.json.in index d78d69d..2fe18fb 100644 --- a/Tests/CMakeGUI/presetArg-noPresetBinaryChange/CMakePresets.json.in +++ b/Tests/CMakeGUI/presetArg-noPresetBinaryChange/CMakePresets.json.in
@@ -18,6 +18,10 @@ "type": "FILEPATH", "value": "${sourceDir}/CMakeLists.txt" }, + "SOURCE_DIR_NAME_VARIABLE": { + "type": "STRING", + "value": "${sourceDirName}" + }, "ON_VARIABLE": { "type": "BOOL", "value": "ON"
diff --git a/Tests/CMakeGUI/presetArg-preset/CMakePresets.json.in b/Tests/CMakeGUI/presetArg-preset/CMakePresets.json.in index d78d69d..2fe18fb 100644 --- a/Tests/CMakeGUI/presetArg-preset/CMakePresets.json.in +++ b/Tests/CMakeGUI/presetArg-preset/CMakePresets.json.in
@@ -18,6 +18,10 @@ "type": "FILEPATH", "value": "${sourceDir}/CMakeLists.txt" }, + "SOURCE_DIR_NAME_VARIABLE": { + "type": "STRING", + "value": "${sourceDirName}" + }, "ON_VARIABLE": { "type": "BOOL", "value": "ON"
diff --git a/Tests/CMakeGUI/presetArg-presetBinary/CMakePresets.json.in b/Tests/CMakeGUI/presetArg-presetBinary/CMakePresets.json.in index d78d69d..2fe18fb 100644 --- a/Tests/CMakeGUI/presetArg-presetBinary/CMakePresets.json.in +++ b/Tests/CMakeGUI/presetArg-presetBinary/CMakePresets.json.in
@@ -18,6 +18,10 @@ "type": "FILEPATH", "value": "${sourceDir}/CMakeLists.txt" }, + "SOURCE_DIR_NAME_VARIABLE": { + "type": "STRING", + "value": "${sourceDirName}" + }, "ON_VARIABLE": { "type": "BOOL", "value": "ON"
diff --git a/Tests/CMakeGUI/presetArg-presetBinaryChange/CMakePresets.json.in b/Tests/CMakeGUI/presetArg-presetBinaryChange/CMakePresets.json.in index 6fe20d6..482eeb5 100644 --- a/Tests/CMakeGUI/presetArg-presetBinaryChange/CMakePresets.json.in +++ b/Tests/CMakeGUI/presetArg-presetBinaryChange/CMakePresets.json.in
@@ -18,6 +18,10 @@ "type": "FILEPATH", "value": "${sourceDir}/CMakeLists.txt" }, + "SOURCE_DIR_NAME_VARIABLE": { + "type": "STRING", + "value": "${sourceDirName}" + }, "ON_VARIABLE": { "type": "BOOL", "value": "ON"
diff --git a/Tests/CMakeGUI/presetArg-presetConfigExists/CMakePresets.json.in b/Tests/CMakeGUI/presetArg-presetConfigExists/CMakePresets.json.in index d78d69d..2fe18fb 100644 --- a/Tests/CMakeGUI/presetArg-presetConfigExists/CMakePresets.json.in +++ b/Tests/CMakeGUI/presetArg-presetConfigExists/CMakePresets.json.in
@@ -18,6 +18,10 @@ "type": "FILEPATH", "value": "${sourceDir}/CMakeLists.txt" }, + "SOURCE_DIR_NAME_VARIABLE": { + "type": "STRING", + "value": "${sourceDirName}" + }, "ON_VARIABLE": { "type": "BOOL", "value": "ON"
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index cbb7fdf..959cf3c 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt
@@ -4,6 +4,7 @@ set(CMakeLib_TESTS testAssert.cxx testArgumentParser.cxx + testCacheDocumentationTable.cxx testCTestBinPacker.cxx testCTestResourceAllocator.cxx testCTestResourceSpec.cxx @@ -18,6 +19,7 @@ testRange.cxx testOptional.cxx testPathResolver.cxx + testScriptGenerator.cxx testSpdxSerializer.cxx testStdIo.cxx testString.cxx
diff --git a/Tests/CMakeLib/testCMExtAlgorithm.cxx b/Tests/CMakeLib/testCMExtAlgorithm.cxx index 53b0302..0f00bd3 100644 --- a/Tests/CMakeLib/testCMExtAlgorithm.cxx +++ b/Tests/CMakeLib/testCMExtAlgorithm.cxx
@@ -1,5 +1,8 @@ #include <iostream> +#include <map> #include <memory> +#include <string> +#include <tuple> #include <utility> #include <vector> @@ -107,11 +110,79 @@ } } } + +void testKeys() +{ + { + std::map<std::string, int> m{ { "one", 1 }, { "two", 2 } }; + std::vector<std::string> ref{ "one", "two" }; + + auto res = cm::keys(m); + if (res != ref) { + ++failed; + } + } + { + std::map<int, int> m{ { 1, 1 }, { 2, 2 } }; + std::vector<int> ref{ 1, 2 }; + + auto res = cm::keys(m); + if (res != ref) { + ++failed; + } + } + { + std::vector<std::tuple<std::string, int, int>> m; + m.emplace_back("one", 1, 10); + m.emplace_back("two", 2, 20); + std::vector<std::string> ref{ "one", "two" }; + + auto res = cm::keys(m); + if (res != ref) { + ++failed; + } + } +} + +void testValues() +{ + { + std::map<std::string, int> m{ { "one", 1 }, { "two", 2 } }; + std::vector<int> ref{ 1, 2 }; + + auto res = cm::values(m); + if (res != ref) { + ++failed; + } + } + { + std::map<int, std::string> m{ { 1, "one" }, { 2, "two" } }; + std::vector<std::string> ref{ "one", "two" }; + + auto res = cm::values(m); + if (res != ref) { + ++failed; + } + } + { + std::vector<std::tuple<std::string, int, int>> m; + m.emplace_back("one", 1, 10); + m.emplace_back("two", 2, 20); + std::vector<int> ref{ 1, 2 }; + + auto res = cm::values(m); + if (res != ref) { + ++failed; + } + } +} } int testCMExtAlgorithm(int /*unused*/, char* /*unused*/[]) { testAppend(); + testKeys(); + testValues(); return failed; }
diff --git a/Tests/CMakeLib/testCacheDocumentationTable.cxx b/Tests/CMakeLib/testCacheDocumentationTable.cxx new file mode 100644 index 0000000..79d4c7b --- /dev/null +++ b/Tests/CMakeLib/testCacheDocumentationTable.cxx
@@ -0,0 +1,496 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +#include <cstddef> +#include <string> + +#include <cm/string_view> + +#include "cmCacheDocumentationTable.h" +#include "cmCachePatternTable.h" + +#include "testCommon.h" + +namespace { + +// The table is consulted by cmake::ProcessCacheArg via a binary search +// (std::lower_bound). That requires the entries to be sorted strictly +// ascending by Name in byte-wise (ASCII) order. These tests are the +// regression guard for that invariant against future hand-edits. + +bool testNonEmpty() +{ + ASSERT_TRUE(cmCacheDocumentationTable::EntriesSize() > 0); + ASSERT_TRUE(cmCacheDocumentationTable::EntriesBegin() != + cmCacheDocumentationTable::EntriesEnd()); + return true; +} + +bool testSortedAndUnique() +{ + auto const* const first = cmCacheDocumentationTable::EntriesBegin(); + auto const* const last = cmCacheDocumentationTable::EntriesEnd(); + for (auto const* it = first; it != last; ++it) { + if (it == first) { + continue; + } + auto const& prev = *(it - 1); + auto const& curr = *it; + if (!(prev.Name < curr.Name)) { + std::cout << "Entries are not strictly ascending: '" << prev.Name + << "' is followed by '" << curr.Name << "' at index " + << static_cast<std::size_t>(it - first) << '\n'; + if (prev.Name == curr.Name) { + std::cout << " (duplicate Name)\n"; + } else { + std::cout << " (out-of-order Name)\n"; + } + return false; + } + } + return true; +} + +bool testNonEmptyFields() +{ + for (auto const* it = cmCacheDocumentationTable::EntriesBegin(); + it != cmCacheDocumentationTable::EntriesEnd(); ++it) { + if (it->Name.empty()) { + std::cout << "Empty Name at index " + << static_cast<std::size_t>( + it - cmCacheDocumentationTable::EntriesBegin()) + << '\n'; + return false; + } + if (it->Summary.empty()) { + std::cout << "Empty Summary for entry '" << it->Name << "'\n"; + return false; + } + } + return true; +} + +bool testNameCharacters() +{ + // Names must be printable ASCII without whitespace. Anything else + // would either be a copy/paste mishap or would cause std::lower_bound + // to behave in surprising ways relative to user input. + for (auto const* it = cmCacheDocumentationTable::EntriesBegin(); + it != cmCacheDocumentationTable::EntriesEnd(); ++it) { + for (char const c : it->Name) { + auto const u = static_cast<unsigned char>(c); + if (u <= 0x20 || u >= 0x7f) { + std::cout << "Entry '" << it->Name + << "' contains a non-printable or whitespace character\n"; + return false; + } + } + } + return true; +} + +bool testSummaryIsSingleLine() +{ + // Summaries are rendered as one-line tooltips in cmake-gui / ccmake. + // Embedded newlines or carriage returns break that rendering. + for (auto const* it = cmCacheDocumentationTable::EntriesBegin(); + it != cmCacheDocumentationTable::EntriesEnd(); ++it) { + for (char const c : it->Summary) { + if (c == '\n' || c == '\r') { + std::cout << "Summary for entry '" << it->Name + << "' contains an embedded newline or carriage return\n"; + return false; + } + } + } + return true; +} + +bool testGetRoundTrip() +{ + // For every entry in the table, Get(Name) must return the same Summary. + // This catches a regression where the binary-search predicate or the + // sort invariant silently drifts apart. + for (auto const* it = cmCacheDocumentationTable::EntriesBegin(); + it != cmCacheDocumentationTable::EntriesEnd(); ++it) { + auto const got = cmCacheDocumentationTable::Get(it->Name); + if (got.Summary != it->Summary) { + std::cout << "Get(\"" << it->Name + << "\").Summary did not round-trip to the table's Summary\n"; + return false; + } + } + // And an obviously-unknown key must return the sentinel. + auto const missing = cmCacheDocumentationTable::Get( + cm::string_view("__definitely_not_a_cmake_variable__")); + if (!missing.Summary.empty()) { + std::cout << "Get() returned a non-empty Summary for an unknown key\n"; + return false; + } + return true; +} + +// --------------------------------------------------------------------------- +// Pattern table tests. +// --------------------------------------------------------------------------- +// +// The pattern table is consulted by cmCacheDocumentationTable::Get on the +// exact-match miss path. The tests below pin down its structural +// invariants and the most important matcher behaviors (lazy +// lookahead-anchored matching, fixed-length digit classes, exact wins over +// pattern, permissive matching with restriction-in-prose). + +// Count the angle-bracketed placeholders in a pattern string. A +// placeholder is the inclusive run from a ``<`` to the next ``>``. +std::size_t CountPlaceholders(cm::string_view pattern) +{ + std::size_t n = 0; + for (std::size_t i = 0; i < pattern.size(); ++i) { + if (pattern[i] == '<') { + ++n; + auto const close = pattern.find('>', i); + if (close == cm::string_view::npos) { + return n; // malformed; let testPatternTableFieldsValid flag it + } + i = close; + } + } + return n; +} + +// Total length of the literal (non-placeholder) characters in a pattern. +std::size_t LiteralLength(cm::string_view pattern) +{ + std::size_t total = 0; + for (std::size_t i = 0; i < pattern.size(); ++i) { + if (pattern[i] == '<') { + auto const close = pattern.find('>', i); + if (close == cm::string_view::npos) { + break; + } + i = close; + continue; + } + ++total; + } + return total; +} + +bool testPatternTableNonEmpty() +{ + ASSERT_TRUE(cmCachePatternTable::EntriesSize() > 0); + ASSERT_TRUE(cmCachePatternTable::EntriesBegin() != + cmCachePatternTable::EntriesEnd()); + return true; +} + +bool testPatternTableFieldsValid() +{ + // Per-entry invariants: non-empty Pattern and Summary, at least one + // placeholder, single-line printable ASCII Summary of bounded length, + // and (a stricter rule derived from the matcher design) no two adjacent + // placeholders with no separating literal -- the matcher's + // lookahead-anchored semantics require a literal anchor between + // placeholders to be unambiguous. + for (auto const* it = cmCachePatternTable::EntriesBegin(); + it != cmCachePatternTable::EntriesEnd(); ++it) { + if (it->Pattern.empty()) { + std::cout << "Empty Pattern at index " + << static_cast<std::size_t>( + it - cmCachePatternTable::EntriesBegin()) + << '\n'; + return false; + } + if (it->Summary.empty()) { + std::cout << "Empty Summary for pattern '" << it->Pattern << "'\n"; + return false; + } + if (CountPlaceholders(it->Pattern) == 0) { + std::cout << "Pattern '" << it->Pattern + << "' has no <...> placeholder; " + "use cmCacheDocumentationTable for exact-match entries\n"; + return false; + } + if (it->Summary.size() > 200) { + std::cout << "Summary for pattern '" << it->Pattern + << "' exceeds the 200-character tooltip limit\n"; + return false; + } + for (char const c : it->Summary) { + auto const u = static_cast<unsigned char>(c); + if (u < 0x20 || u >= 0x7f) { + std::cout << "Summary for pattern '" << it->Pattern + << "' contains a non-printable or non-ASCII character\n"; + return false; + } + } + // Adjacent placeholders are forbidden (matcher cannot disambiguate + // them without a separating literal anchor). Look for ``><``. + for (std::size_t i = 1; i < it->Pattern.size(); ++i) { + if (it->Pattern[i] == '<' && it->Pattern[i - 1] == '>') { + std::cout << "Pattern '" << it->Pattern + << "' contains adjacent placeholders; insert a literal " + "separator between them\n"; + return false; + } + } + } + return true; +} + +bool testPatternTableOrdered() +{ + // kPatterns[] must obey ``(numPlaceholders ASC, totalLiteralLen DESC, + // Pattern ASC)``. This determinism guarantees that a contributor + // re-ordering the array cannot silently flip which of several equally + // valid pattern matches wins for a given input. + auto const* const first = cmCachePatternTable::EntriesBegin(); + auto const* const last = cmCachePatternTable::EntriesEnd(); + for (auto const* it = first; it != last; ++it) { + if (it == first) { + continue; + } + auto const& prev = *(it - 1); + auto const& curr = *it; + auto const np_prev = CountPlaceholders(prev.Pattern); + auto const np_curr = CountPlaceholders(curr.Pattern); + auto const ll_prev = LiteralLength(prev.Pattern); + auto const ll_curr = LiteralLength(curr.Pattern); + bool ordered; + if (np_prev != np_curr) { + ordered = np_prev < np_curr; + } else if (ll_prev != ll_curr) { + ordered = ll_prev > ll_curr; + } else { + ordered = prev.Pattern < curr.Pattern; + } + if (!ordered) { + std::cout << "Pattern table out of canonical order between '" + << prev.Pattern << "' (placeholders=" << np_prev + << ", literal_len=" << ll_prev << ") and '" << curr.Pattern + << "' (placeholders=" << np_curr << ", literal_len=" << ll_curr + << "). Expected (numPlaceholders ASC, totalLiteralLen DESC, " + "Pattern ASC).\n"; + return false; + } + } + return true; +} + +bool testPatternMatchLangPositive() +{ + // CMAKE_<LANG>_CLANG_TIDY must match for several different <LANG> + // values, and all must return the same static Summary pointer (the + // permissive matcher returns the pattern entry's storage verbatim). + auto const cxx = cmCacheDocumentationTable::Get("CMAKE_CXX_CLANG_TIDY"); + auto const hip = cmCacheDocumentationTable::Get("CMAKE_HIP_CLANG_TIDY"); + auto const fortran = + cmCacheDocumentationTable::Get("CMAKE_Fortran_CLANG_TIDY"); + ASSERT_TRUE(!cxx.Summary.empty()); + ASSERT_TRUE(!hip.Summary.empty()); + ASSERT_TRUE(!fortran.Summary.empty()); + // Same static-storage Summary pointer. + ASSERT_TRUE(cxx.Summary.data() == hip.Summary.data()); + ASSERT_TRUE(cxx.Summary.data() == fortran.Summary.data()); + return true; +} + +bool testPatternMatchConfigPositive() +{ + // CMAKE_<LANG>_FLAGS_<CONFIG> matches both standard configurations and + // user-defined ones (the matcher does not enforce a canonical CONFIG + // set; it accepts any identifier). + auto const debug = cmCacheDocumentationTable::Get("CMAKE_CXX_FLAGS_DEBUG"); + auto const release = + cmCacheDocumentationTable::Get("CMAKE_CXX_FLAGS_RELEASE"); + auto const coverage = + cmCacheDocumentationTable::Get("CMAKE_CXX_FLAGS_COVERAGE"); + ASSERT_TRUE(!debug.Summary.empty()); + ASSERT_TRUE(!release.Summary.empty()); + ASSERT_TRUE(!coverage.Summary.empty()); + ASSERT_TRUE(debug.Summary.data() == release.Summary.data()); + ASSERT_TRUE(debug.Summary.data() == coverage.Summary.data()); + return true; +} + +bool testPatternMatchNNNN() +{ + // <NNNN> is exactly 4 ASCII digits. 3-digit, 5-digit, and non-digit + // inputs must all miss. + ASSERT_TRUE(!cmCacheDocumentationTable::Get("CMAKE_POLICY_DEFAULT_CMP0048") + .Summary.empty()); + ASSERT_TRUE(cmCacheDocumentationTable::Get("CMAKE_POLICY_DEFAULT_CMP48") + .Summary.empty()); + ASSERT_TRUE(cmCacheDocumentationTable::Get("CMAKE_POLICY_DEFAULT_CMP00048") + .Summary.empty()); + ASSERT_TRUE(cmCacheDocumentationTable::Get("CMAKE_POLICY_DEFAULT_CMPxyz0") + .Summary.empty()); + return true; +} + +bool testPatternMatchN() +{ + // <n> is exactly 1 ASCII digit. Multi-digit and non-digit must miss. + ASSERT_TRUE( + !cmCacheDocumentationTable::Get("CMAKE_MATCH_5").Summary.empty()); + ASSERT_TRUE( + cmCacheDocumentationTable::Get("CMAKE_MATCH_12").Summary.empty()); + ASSERT_TRUE(cmCacheDocumentationTable::Get("CMAKE_MATCH_a").Summary.empty()); + return true; +} + +bool testPatternMatchMultiPlaceholder() +{ + // CMAKE_CXX_FLAGS_DEBUG must match the two-placeholder pattern + // ``CMAKE_<LANG>_FLAGS_<CONFIG>``, not the one-placeholder pattern + // ``CMAKE_<LANG>_FLAGS`` (which fails full-input consumption on a + // longer input by design). + auto const got = cmCacheDocumentationTable::Get("CMAKE_CXX_FLAGS_DEBUG"); + ASSERT_TRUE(!got.Summary.empty()); + // Locate the two-placeholder entry and assert pointer equality. + cm::string_view two_ph_summary; + for (auto const* it = cmCachePatternTable::EntriesBegin(); + it != cmCachePatternTable::EntriesEnd(); ++it) { + if (it->Pattern == "CMAKE_<LANG>_FLAGS_<CONFIG>") { + two_ph_summary = it->Summary; + break; + } + } + ASSERT_TRUE(!two_ph_summary.empty()); + ASSERT_TRUE(got.Summary.data() == two_ph_summary.data()); + return true; +} + +bool testPatternMatchPackage() +{ + // The single <PackageName>_ROOT pattern covers both mixed-case + // (CMP0074) and upper-case (CMP0144) spellings: the identifier + // lexical class ``[A-Za-z][A-Za-z0-9_]*`` accepts both. + ASSERT_TRUE(!cmCacheDocumentationTable::Get("Boost_ROOT").Summary.empty()); + ASSERT_TRUE(!cmCacheDocumentationTable::Get("Qt6_ROOT").Summary.empty()); + ASSERT_TRUE(!cmCacheDocumentationTable::Get("BOOST_ROOT").Summary.empty()); + return true; +} + +bool testExactWinsOverPattern() +{ + // CMAKE_CUDA_STANDARD is in the exact-match table and would also match + // the pattern CMAKE_<LANG>_STANDARD. The exact entry must win. + auto const got = cmCacheDocumentationTable::Get("CMAKE_CUDA_STANDARD"); + ASSERT_TRUE(!got.Summary.empty()); + // Find the exact entry's Summary in cmCacheDocumentationTable. + cm::string_view exact_summary; + for (auto const* it = cmCacheDocumentationTable::EntriesBegin(); + it != cmCacheDocumentationTable::EntriesEnd(); ++it) { + if (it->Name == "CMAKE_CUDA_STANDARD") { + exact_summary = it->Summary; + break; + } + } + ASSERT_TRUE(!exact_summary.empty()); + // Pointer equality: the exact-match path returns the table's static + // storage; the pattern-match path would return cmCachePatternTable's + // (different) storage. + ASSERT_TRUE(got.Summary.data() == exact_summary.data()); + return true; +} + +bool testPatternMatchNegativeUnknown() +{ + // A name that matches no pattern at all returns the sentinel. + auto const got = + cmCacheDocumentationTable::Get("CMAKE_TOTAL_NONSENSE_VARIABLE_XYZZY"); + ASSERT_TRUE(got.Summary.empty()); + return true; +} + +bool testPatternUnderscoreInLang() +{ + // The lazy matcher allows ``<LANG>`` to contain underscores (the + // identifier class is ``[A-Za-z][A-Za-z0-9_]*``); the lookahead-anchor + // search finds the right boundary. This pins the design decision: + // permissive matching with restriction-in-prose, not regex-strict + // language enforcement. A made-up ``MY_WEIRD_LANG`` is still picked + // up by the CMAKE_<LANG>_CLANG_TIDY pattern. + auto const got = + cmCacheDocumentationTable::Get("CMAKE_MY_WEIRD_LANG_CLANG_TIDY"); + ASSERT_TRUE(!got.Summary.empty()); + return true; +} + +bool testEveryPatternReachable() +{ + // For each pattern we synthesize a canonical instantiation by + // replacing each placeholder with a class-appropriate token: + // ``Aa`` for identifier-class placeholders (LANG / CONFIG / + // PackageName / PROJECT-NAME / an-attribute / FEATURE / TYPE), + // ``0000`` for the fixed-length ``<NNNN>``, and ``0`` for the + // fixed-length ``<n>``. The matcher's lazy + lookahead-anchored + // semantics then either commit (Summary non-empty -> reachable) or + // reject (Summary empty -> dead pattern, test fails). + for (auto const* it = cmCachePatternTable::EntriesBegin(); + it != cmCachePatternTable::EntriesEnd(); ++it) { + std::string instance; + cm::string_view const pat = it->Pattern; + for (std::size_t i = 0; i < pat.size();) { + if (pat[i] != '<') { + instance.push_back(pat[i]); + ++i; + continue; + } + auto const close = pat.find('>', i); + if (close == cm::string_view::npos) { + std::cout << "Malformed pattern '" << pat << "' (unterminated '<')\n"; + return false; + } + cm::string_view const ph = pat.substr(i, close - i + 1); + if (ph == "<NNNN>") { + instance += "0000"; + } else if (ph == "<n>") { + instance += "0"; + } else { + // Identifier-class placeholder (Ident or IdentHyphen). ``Aa`` + // satisfies both lexical classes and is short enough not to + // accidentally collide with any anchor literal in the table. + instance += "Aa"; + } + i = close + 1; + } + auto const got = cmCacheDocumentationTable::Get(instance); + if (got.Summary.empty()) { + std::cout << "Dead pattern '" << pat << "': canonical instantiation '" + << instance + << "' is not matched by any entry in the pattern table\n"; + return false; + } + } + return true; +} + +} + +int testCacheDocumentationTable(int /*unused*/, char* /*unused*/[]) +{ + return runTests({ + testNonEmpty, + testSortedAndUnique, + testNonEmptyFields, + testNameCharacters, + testSummaryIsSingleLine, + testGetRoundTrip, + // Pattern-table cases. + testPatternTableNonEmpty, + testPatternTableFieldsValid, + testPatternTableOrdered, + testPatternMatchLangPositive, + testPatternMatchConfigPositive, + testPatternMatchNNNN, + testPatternMatchN, + testPatternMatchMultiPlaceholder, + testPatternMatchPackage, + testExactWinsOverPattern, + testPatternMatchNegativeUnknown, + testPatternUnderscoreInLang, + testEveryPatternReachable, + }); +}
diff --git a/Tests/CMakeLib/testDebugger.h b/Tests/CMakeLib/testDebugger.h index c27086d..7b43b19 100644 --- a/Tests/CMakeLib/testDebugger.h +++ b/Tests/CMakeLib/testDebugger.h
@@ -8,7 +8,6 @@ #include "cmDebuggerAdapter.h" #include "cmDebuggerProtocol.h" #include "cmListFileCache.h" -#include "cmMessenger.h" #include <cmcppdap/include/dap/io.h> #include <cmcppdap/include/dap/session.h> #include <cmcppdap/include/dap/types.h> @@ -72,13 +71,12 @@ Client->bind(server2client, client2server); Debugger->bind(client2server, server2client); } - std::vector<cmListFileFunction> CreateListFileFunctions(char const* str, - char const* filename) + std::vector<cmListFileFunction> CreateListFileFunctions( + char const* str, std::string const& filename) { - cmMessenger messenger; cmListFileBacktrace backtrace; cmListFile listfile; - listfile.ParseString(str, filename, &messenger, backtrace); + listfile.ParseString(str, filename, nullptr, backtrace); return listfile.Functions; } };
diff --git a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx index d7c4134..a7af6e8 100644 --- a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx +++ b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx
@@ -7,6 +7,7 @@ #include <memory> #include <stdexcept> #include <string> +#include <thread> #include <cm3p/cppdap/future.h> #include <cm3p/cppdap/io.h> @@ -173,7 +174,157 @@ return true; } +bool testProtocolWithPipesAbruptDisconnect() +{ + std::promise<void> debuggerConnectionCreatedPromise; + std::future<void> debuggerConnectionCreatedFuture = + debuggerConnectionCreatedPromise.get_future(); + + std::future<void> startedListeningFuture; + + std::promise<void> adapterFinishedPromise; + std::future<void> adapterFinishedFuture = + adapterFinishedPromise.get_future(); + + std::promise<bool> initializedEventReceivedPromise; + std::future<bool> initializedEventReceivedFuture = + initializedEventReceivedPromise.get_future(); + + std::promise<bool> exitedEventReceivedPromise; + std::future<bool> exitedEventReceivedFuture = + exitedEventReceivedPromise.get_future(); + + std::promise<bool> terminatedEventReceivedPromise; + std::future<bool> terminatedEventReceivedFuture = + terminatedEventReceivedPromise.get_future(); + + std::promise<bool> threadStartedPromise; + std::future<bool> threadStartedFuture = threadStartedPromise.get_future(); + + std::promise<bool> threadExitedPromise; + std::future<bool> threadExitedFuture = threadExitedPromise.get_future(); + + auto futureTimeout = std::chrono::seconds(60); + auto disconnectTimeout = std::chrono::seconds(10); + +#ifdef _WIN32 + std::string namedPipe = R"(\\.\pipe\LOCAL\CMakeDebuggerPipe3_)" + + cmCryptoHash(cmCryptoHash::AlgoSHA256) + .HashString(cmsys::SystemTools::GetCurrentWorkingDirectory()); +#else + std::string namedPipe = "CMakeDebuggerPipe3"; +#endif + + std::unique_ptr<dap::Session> client = dap::Session::create(); + client->registerHandler([&](dap::InitializedEvent /*unused*/) { + initializedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](dap::ExitedEvent /*unused*/) { + exitedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](dap::TerminatedEvent const& /*unused*/) { + terminatedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](dap::ThreadEvent const& e) { + if (e.reason == "started") { + threadStartedPromise.set_value(true); + } else if (e.reason == "exited") { + threadExitedPromise.set_value(true); + } + }); + + // Raw thread (not ScopedThread): we need to be able to detach on + // failure so the test process can exit even if the regression + // returns. With the fix in place, ReportExitCode()'s wait on + // DisconnectEvent unblocks when the SessionThread detects EOF on the + // pipe; without the fix, it blocks forever and the adapter + // destructor hangs in SessionThread.join(). + std::thread debuggerThread([&]() { + try { + auto connection = + std::make_shared<cmDebugger::cmDebuggerPipeConnection>(namedPipe); + startedListeningFuture = connection->StartedListening.get_future(); + debuggerConnectionCreatedPromise.set_value(); + std::shared_ptr<cmDebugger::cmDebuggerAdapter> debuggerAdapter = + std::make_shared<cmDebugger::cmDebuggerAdapter>( + connection, dap::file(stdout, false)); + // Sends thread-exited / exited / terminated events and then + // blocks on DisconnectEvent. The test closes the client side of + // the pipe instead of sending a DisconnectRequest, so the only + // thing that will unblock this wait is the SessionThread's EOF + // handling in cmDebuggerAdapter. + debuggerAdapter->ReportExitCode(0); + // Adapter destructed here; joins SessionThread. + } catch (std::runtime_error const&) { + // Swallowed: connection failures shouldn't hang the test. + } + adapterFinishedPromise.set_value(); + }); + + ASSERT_TRUE(debuggerConnectionCreatedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(startedListeningFuture.wait_for(futureTimeout) == + std::future_status::ready); + + auto client2Debugger = + std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe); + client2Debugger->WaitForConnection(); + client->bind(client2Debugger, client2Debugger); + + // Drive the full handshake so that the debugger SessionThread is up + // and ReportExitCode is blocked on DisconnectEvent. + dap::CMakeInitializeRequest initializeRequest; + auto initializeResponse = client->send(initializeRequest).get(); + ASSERT_TRUE(!initializeResponse.error); + + dap::LaunchRequest launchRequest; + auto launchResponse = client->send(launchRequest).get(); + ASSERT_TRUE(!launchResponse.error); + + dap::ConfigurationDoneRequest configurationDoneRequest; + auto configurationDoneResponse = + client->send(configurationDoneRequest).get(); + ASSERT_TRUE(!configurationDoneResponse.error); + + ASSERT_TRUE(initializedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(terminatedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(threadStartedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(threadExitedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(exitedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + + // Abruptly close the client side without sending DisconnectRequest. + // Regression check for the busy-loop bug: the debugger adapter must + // detect EOF on the pipe and shut down on its own. + // + // Use ShutdownForTesting() rather than close(). The client dap::Session + // has its own recvThread still blocked in read() on this same socket; + // on Linux, ::close() on an fd does not wake a sibling thread's + // in-flight ::read(), which would deadlock the test. ShutdownForTesting + // calls shutdown(SHUT_RDWR) to signal EOF on the socket endpoint + // without freeing the fd, so both reads wake up naturally. + client2Debugger->ShutdownForTesting(); + + bool finishedInTime = adapterFinishedFuture.wait_for(disconnectTimeout) == + std::future_status::ready; + if (!finishedInTime) { + // Bug reproduced: the SessionThread is spinning on EOF and + // ReportExitCode is blocked on DisconnectEvent. Detach so the test + // process can exit instead of hanging in std::thread's destructor. + debuggerThread.detach(); + ASSERT_TRUE(finishedInTime); + } + debuggerThread.join(); + + return true; +} + int testDebuggerAdapterPipe(int, char*[]) { - return runTests({ testProtocolWithPipes }); + return runTests( + { testProtocolWithPipes, testProtocolWithPipesAbruptDisconnect }); }
diff --git a/Tests/CMakeLib/testDebuggerBreakpointManager.cxx b/Tests/CMakeLib/testDebuggerBreakpointManager.cxx index 3fff39e..8c11b39 100644 --- a/Tests/CMakeLib/testDebuggerBreakpointManager.cxx +++ b/Tests/CMakeLib/testDebuggerBreakpointManager.cxx
@@ -17,11 +17,29 @@ #include "cmDebuggerBreakpointManager.h" #include "cmDebuggerSourceBreakpoint.h" // IWYU pragma: keep +#if defined(_WIN32) || defined(__APPLE__) +# include "cmsys/FStream.hxx" + +# include "cmSystemTools.h" + +# include "testConfig.h" +#endif + #include "testCommon.h" #include "testDebugger.h" class cmListFileFunction; +// Fictional absolute path used by the pre-existing breakpoint tests. +// Must be absolute on all platforms so that ToNormalizedPathOnDisk +// (used inside the breakpoint manager) does not resolve it relative +// to the working directory. +#ifdef _WIN32 +static std::string const kTestSourcePath = "C:/CMakeLists.txt"; +#else +static std::string const kTestSourcePath = "/CMakeLists.txt"; +#endif + static bool testHandleBreakpointRequestBeforeFileIsLoaded() { // Arrange @@ -30,7 +48,7 @@ helper.Debugger.get()); helper.bind(); dap::SetBreakpointsRequest setBreakpointRequest; - std::string sourcePath = "C:/CMakeLists.txt"; + std::string sourcePath = kTestSourcePath; setBreakpointRequest.source.path = sourcePath; dap::array<dap::SourceBreakpoint> sourceBreakpoints(3); sourceBreakpoints[0].line = 1; @@ -73,10 +91,9 @@ cmDebugger::cmDebuggerBreakpointManager breakpointManager( helper.Debugger.get()); helper.bind(); - std::string sourcePath = "C:/CMakeLists.txt"; + std::string sourcePath = kTestSourcePath; std::vector<cmListFileFunction> functions = helper.CreateListFileFunctions( - "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n", - sourcePath.c_str()); + "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n", sourcePath); breakpointManager.SourceFileLoaded(sourcePath, functions); dap::SetBreakpointsRequest setBreakpointRequest; @@ -139,7 +156,7 @@ helper.Debugger.get()); helper.bind(); dap::SetBreakpointsRequest setBreakpointRequest; - std::string sourcePath = "C:/CMakeLists.txt"; + std::string sourcePath = kTestSourcePath; setBreakpointRequest.source.path = sourcePath; dap::array<dap::SourceBreakpoint> sourceBreakpoints(5); sourceBreakpoints[0].line = 1; @@ -149,8 +166,7 @@ sourceBreakpoints[4].line = 5; setBreakpointRequest.breakpoints = sourceBreakpoints; std::vector<cmListFileFunction> functions = helper.CreateListFileFunctions( - "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n", - sourcePath.c_str()); + "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n", sourcePath); auto got = helper.Client->send(setBreakpointRequest).get(); // Act @@ -175,9 +191,223 @@ return true; } +#if defined(_WIN32) || defined(__APPLE__) + +// RAII guard for temp directory cleanup on both success and assertion failure. +struct TempDirGuard +{ + std::string Path; + ~TempDirGuard() + { + if (!Path.empty()) { + cmSystemTools::RemoveADirectory(Path); + } + } +}; + +static bool testBreakpointCaseMismatch_SourceLoadedFirst() +{ + std::string tempDir = std::string(BUILD_DIR) + "/cmakedbg-bp1-XXXXXX"; + if (!cmSystemTools::MakeTempDirectory(tempDir)) { + std::cout << "Failed to create temp directory\n"; + return false; + } + TempDirGuard guard; + guard.Path = tempDir; + + std::string createdFile = tempDir + "/TestCase.cmake"; + { + cmsys::ofstream f(createdFile.c_str()); + } + + std::string canonicalPath = + cmSystemTools::ToNormalizedPathOnDisk(createdFile); + std::string lowerFile = tempDir + "/testcase.cmake"; + std::string normalizedLower = + cmSystemTools::ToNormalizedPathOnDisk(lowerFile); + + // Skip when ToNormalizedPathOnDisk does not correct on-disk case: + // a case-sensitive filesystem, or CYGWIN (treated as POSIX, no probe). + if (normalizedLower != canonicalPath) { + std::cout << "Skipping: ToNormalizedPathOnDisk does not correct case\n"; + return true; + } + + // Arrange + DebuggerTestHelper helper; + std::atomic<bool> noBreakpointEvents(true); + helper.Client->registerHandler( + [&](dap::BreakpointEvent const&) { noBreakpointEvents.store(false); }); + cmDebugger::cmDebuggerBreakpointManager breakpointManager( + helper.Debugger.get()); + helper.bind(); + + std::vector<cmListFileFunction> functions = + helper.CreateListFileFunctions("set(var1 foo)\n", canonicalPath); + + // Act: load source with lowercase path, set breakpoints with uppercase. + breakpointManager.SourceFileLoaded(lowerFile, functions); + + dap::SetBreakpointsRequest req; + req.source.path = tempDir + "/TESTCASE.CMAKE"; + dap::array<dap::SourceBreakpoint> bps(1); + bps[0].line = 1; + req.breakpoints = bps; + auto got = helper.Client->send(req).get(); + + // Assert: breakpoint verified because both paths normalize to canonicalPath. + ASSERT_TRUE(!got.error); + ASSERT_TRUE(got.response.breakpoints.size() == 1); + ASSERT_BREAKPOINT(got.response.breakpoints[0], 0, 1, canonicalPath, true); + ASSERT_TRUE(noBreakpointEvents.load()); + ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 1); + + return true; +} + +static bool testBreakpointCaseMismatch_BreakpointsSetFirst() +{ + std::string tempDir = std::string(BUILD_DIR) + "/cmakedbg-bp2-XXXXXX"; + if (!cmSystemTools::MakeTempDirectory(tempDir)) { + std::cout << "Failed to create temp directory\n"; + return false; + } + TempDirGuard guard; + guard.Path = tempDir; + + std::string createdFile = tempDir + "/TestCase.cmake"; + { + cmsys::ofstream f(createdFile.c_str()); + } + + std::string canonicalPath = + cmSystemTools::ToNormalizedPathOnDisk(createdFile); + std::string lowerFile = tempDir + "/testcase.cmake"; + std::string normalizedLower = + cmSystemTools::ToNormalizedPathOnDisk(lowerFile); + + // Skip when ToNormalizedPathOnDisk does not correct on-disk case: + // a case-sensitive filesystem, or CYGWIN (treated as POSIX, no probe). + if (normalizedLower != canonicalPath) { + std::cout << "Skipping: ToNormalizedPathOnDisk does not correct case\n"; + return true; + } + + // Arrange + DebuggerTestHelper helper; + std::vector<dap::BreakpointEvent> breakpointEvents; + std::atomic<int> remainingEvents(1); + std::promise<void> allEventsPromise; + std::future<void> allEventsFuture = allEventsPromise.get_future(); + helper.Client->registerHandler([&](dap::BreakpointEvent const& event) { + breakpointEvents.emplace_back(event); + if (--remainingEvents == 0) { + allEventsPromise.set_value(); + } + }); + cmDebugger::cmDebuggerBreakpointManager breakpointManager( + helper.Debugger.get()); + helper.bind(); + + // Act: set breakpoints with uppercase path before file is loaded. + dap::SetBreakpointsRequest req; + req.source.path = tempDir + "/TESTCASE.CMAKE"; + dap::array<dap::SourceBreakpoint> bps(1); + bps[0].line = 1; + req.breakpoints = bps; + auto got = helper.Client->send(req).get(); + + ASSERT_TRUE(!got.error); + ASSERT_TRUE(got.response.breakpoints.size() == 1); + ASSERT_BREAKPOINT(got.response.breakpoints[0], 0, 1, canonicalPath, false); + + // Act: load source with lowercase path. + std::vector<cmListFileFunction> functions = + helper.CreateListFileFunctions("set(var1 foo)\n", canonicalPath); + breakpointManager.SourceFileLoaded(lowerFile, functions); + + ASSERT_TRUE(allEventsFuture.wait_for(std::chrono::seconds(10)) == + std::future_status::ready); + + // Assert: breakpoint event fires with verified status. + ASSERT_TRUE(breakpointEvents.size() == 1); + ASSERT_BREAKPOINT(breakpointEvents[0].breakpoint, 0, 1, canonicalPath, true); + + return true; +} + +static bool testGetBreakpoints_CaseMismatch() +{ + std::string tempDir = std::string(BUILD_DIR) + "/cmakedbg-bp3-XXXXXX"; + if (!cmSystemTools::MakeTempDirectory(tempDir)) { + std::cout << "Failed to create temp directory\n"; + return false; + } + TempDirGuard guard; + guard.Path = tempDir; + + std::string createdFile = tempDir + "/TestCase.cmake"; + { + cmsys::ofstream f(createdFile.c_str()); + } + + std::string canonicalPath = + cmSystemTools::ToNormalizedPathOnDisk(createdFile); + std::string lowerFile = tempDir + "/testcase.cmake"; + std::string normalizedLower = + cmSystemTools::ToNormalizedPathOnDisk(lowerFile); + + // Skip when ToNormalizedPathOnDisk does not correct on-disk case: + // a case-sensitive filesystem, or CYGWIN (treated as POSIX, no probe). + if (normalizedLower != canonicalPath) { + std::cout << "Skipping: ToNormalizedPathOnDisk does not correct case\n"; + return true; + } + + // Arrange + DebuggerTestHelper helper; + cmDebugger::cmDebuggerBreakpointManager breakpointManager( + helper.Debugger.get()); + helper.bind(); + + std::vector<cmListFileFunction> functions = + helper.CreateListFileFunctions("set(var1 foo)\n", canonicalPath); + + breakpointManager.SourceFileLoaded(canonicalPath, functions); + + dap::SetBreakpointsRequest req; + req.source.path = canonicalPath; + dap::array<dap::SourceBreakpoint> bps(1); + bps[0].line = 1; + req.breakpoints = bps; + auto got = helper.Client->send(req).get(); + ASSERT_TRUE(!got.error); + ASSERT_TRUE(got.response.breakpoints.size() == 1); + ASSERT_BREAKPOINT(got.response.breakpoints[0], 0, 1, canonicalPath, true); + + // Act: query breakpoints with a different-case path. + auto breakpoints = + breakpointManager.GetBreakpoints(tempDir + "/TESTCASE.CMAKE", 1); + + // Assert: found despite case mismatch. + ASSERT_TRUE(breakpoints.size() == 1); + ASSERT_TRUE(breakpoints[0] == 0); + + return true; +} + +#endif + int testDebuggerBreakpointManager(int, char*[]) { - return runTests({ testHandleBreakpointRequestBeforeFileIsLoaded, - testHandleBreakpointRequestAfterFileIsLoaded, - testSourceFileLoadedAfterHandleBreakpointRequest }); + return runTests({ + testHandleBreakpointRequestBeforeFileIsLoaded, + testHandleBreakpointRequestAfterFileIsLoaded, + testSourceFileLoadedAfterHandleBreakpointRequest, +#if defined(_WIN32) || defined(__APPLE__) + testBreakpointCaseMismatch_SourceLoadedFirst, + testBreakpointCaseMismatch_BreakpointsSetFirst, + testGetBreakpoints_CaseMismatch, +#endif + }); }
diff --git a/Tests/CMakeLib/testDebuggerVariablesHelper.cxx b/Tests/CMakeLib/testDebuggerVariablesHelper.cxx index aa21c58..e408d4c 100644 --- a/Tests/CMakeLib/testDebuggerVariablesHelper.cxx +++ b/Tests/CMakeLib/testDebuggerVariablesHelper.cxx
@@ -18,6 +18,7 @@ #include "cmDebuggerVariablesHelper.h" #include "cmDebuggerVariablesManager.h" #include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmMakefile.h" @@ -501,8 +502,9 @@ std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); auto dummies = CreateDummies("Foo"); - cmFileSet fileSet(dummies.Makefile.get(), "Foo", "HEADERS", - cmFileSetVisibility::Public); + cmFileSet fileSet(dummies.Makefile.get(), + dummies.Makefile->GetOrderedTargets()[0], "Foo", "HEADERS", + cm::FileSetMetadata::Visibility::Public); BT<std::string> directory; directory.Value = "c:/"; fileSet.AddDirectoryEntry(directory); @@ -546,8 +548,9 @@ std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); auto dummies = CreateDummies("Foo"); - cmFileSet fileSet(dummies.Makefile.get(), "Foo", "HEADERS", - cmFileSetVisibility::Public); + cmFileSet fileSet(dummies.Makefile.get(), + dummies.Makefile->GetOrderedTargets()[0], "Foo", "HEADERS", + cm::FileSetMetadata::Visibility::Public); BT<std::string> directory; directory.Value = "c:/"; fileSet.AddDirectoryEntry(directory);
diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx index 983ff06..474919c 100644 --- a/Tests/CMakeLib/testList.cxx +++ b/Tests/CMakeLib/testList.cxx
@@ -790,6 +790,31 @@ result = false; } } + { + // No cmMakefile through this overload, so APPLY must throw. + cmList list({ "AA", "BB" }); + + try { + list.transform(cmList::TransformAction::APPLY, + std::vector<std::string>{ "someFunction" }); +#ifndef __clang_analyzer__ /* clang-analyzer cannot see throw skips this */ + result = false; +#endif + } catch (cmList::transform_error const&) { + } + } + { + // No cmMakefile through this overload, so APPLY must throw. + cmList list({ "AA", "BB" }); + + try { + list.transform(cmList::TransformAction::APPLY, "someFunction"); +#ifndef __clang_analyzer__ /* clang-analyzer cannot see throw skips this */ + result = false; +#endif + } catch (cmList::transform_error const&) { + } + } checkResult(result);
diff --git a/Tests/CMakeLib/testScriptGenerator.cxx b/Tests/CMakeLib/testScriptGenerator.cxx new file mode 100644 index 0000000..defa5f8 --- /dev/null +++ b/Tests/CMakeLib/testScriptGenerator.cxx
@@ -0,0 +1,35 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +#include <string> + +#include "cmScriptGenerator.h" + +#include "testCommon.h" + +#define TEST_QUOTE(input, expected_output) \ + ASSERT_EQUAL(cmScriptGenerator::Quote(input).str(), (expected_output)) + +namespace { + +bool quote() +{ + TEST_QUOTE("", "\"\""); + TEST_QUOTE("\"", "[[\"]]"); + TEST_QUOTE("\\", "[[\\]]"); + TEST_QUOTE("$", "[[$]]"); + TEST_QUOTE("$]", "[=[$]]=]"); + TEST_QUOTE("$]=", "[[$]=]]"); + TEST_QUOTE("$]=]", "[==[$]=]]==]"); + TEST_QUOTE("$]==]", "[=[$]==]]=]"); + return true; +} + +} // namespace + +int testScriptGenerator(int /*unused*/, char* /*unused*/[]) +{ + return runTests({ + quote, + }); +}
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 01f6604..8020d96 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt
@@ -131,7 +131,9 @@ set(CMake_TEST_RESOURCES TRUE) endif() # For some Windows toolchains there is no resource support. - if(WATCOM OR BORLAND OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC") + if(WATCOM OR BORLAND + OR (CMAKE_C_COMPILER_ID STREQUAL "OrangeC" AND + CMAKE_C_COMPILER_VERSION VERSION_LESS "7.0")) set(CMake_TEST_RESOURCES FALSE) endif() @@ -262,6 +264,26 @@ unset(_sysctl_stdout) endif() + if(NOT DEFINED CMake_TEST_FILESYSTEM_CASE) + if(CMAKE_HOST_WIN32) + set(CMake_TEST_FILESYSTEM_CASE 1) + elseif(CMAKE_HOST_APPLE) + execute_process( + COMMAND sw_vers -productVersion + OUTPUT_VARIABLE sw_vers_productVersion + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _sw_vers_result + ) + if(sw_vers_productVersion VERSION_GREATER_EQUAL 10.11) + set(CMake_TEST_FILESYSTEM_CASE 1) + endif() + endif() + endif() + + if(CMake_TEST_CUDA AND NOT CMake_TEST_CUDA MATCHES "^(NVIDIA|Clang)$") + message(FATAL_ERROR "CMake_TEST_CUDA should be \"NVIDIA\" or \"Clang\", got \"${CMake_TEST_CUDA}\"") + endif() + #--------------------------------------------------------------------------- # Add tests below here. @@ -424,6 +446,7 @@ string(REPLACE ";" "$<SEMICOLON>" TEST_STDS_CUDA "${CMake_TEST_CUDA_STANDARDS}") string(REPLACE ";" "$<SEMICOLON>" TEST_STDS_HIP "${CMake_TEST_HIP_STANDARDS}") set(CompileFeatures_BUILD_OPTIONS + -DCMake_TEST_CXX=${CMake_TEST_CXX} -DCMake_TEST_C_STANDARDS=${TEST_STDS_C} -DCMake_TEST_CXX_STANDARDS=${TEST_STDS_CXX} -DCMake_TEST_CUDA=${CMake_TEST_CUDA} @@ -433,11 +456,13 @@ ) ADD_TEST_MACRO(CompileFeatures CompileFeatures) set_property(TEST CompileFeatures APPEND PROPERTY LABELS "CUDA" "HIP") + set_property(TEST CompileFeatures APPEND PROPERTY LABELS "PellesC") ADD_TEST_MACRO(CMakeCommands.target_compile_features) if(CMake_TEST_RESOURCES) ADD_TEST_MACRO(VSResource VSResource) + set_property(TEST VSResource APPEND PROPERTY LABELS "PellesC") if(CMAKE_GENERATOR MATCHES "Ninja") add_test_macro(VSResourceNinjaForceRSP VSResourceNinjaForceRSP) endif() @@ -464,6 +489,7 @@ if(CMake_TEST_Rust) ADD_TEST_MACRO(RustOnly RustOnly) ADD_TEST_MACRO(RustMix RustMix) + ADD_TEST_MACRO(RustPie RustPie) endif() if(CMAKE_Fortran_COMPILER) ADD_TEST_MACRO(FortranOnly FortranOnly) @@ -523,6 +549,7 @@ endif() ADD_TEST_MACRO(COnly COnly) + set_property(TEST COnly APPEND PROPERTY LABELS "PellesC") ADD_TEST_MACRO(CxxOnly CxxOnly) ADD_TEST_MACRO(CxxSubdirC CxxSubdirC) ADD_TEST_MACRO(OutDir runtime/OutDir) @@ -1207,7 +1234,8 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --version OUTPUT_VARIABLE _TMP_VERSION ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result_dpkg_version) if(_TMP_VERSION MATCHES "dpkg-shlibdeps version ([0-9]+\\.[0-9]+\\.[0-9]+)") set(SHLIBDEPS_EXECUTABLE_VERSION "${CMAKE_MATCH_1}") else() @@ -1952,6 +1980,7 @@ COMMAND sw_vers -productVersion OUTPUT_VARIABLE OSX_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _sw_vers_result ) if(OSX_VERSION MATCHES "^([0-9]+\\.[0-9]+)") set(CMake_TEST_XCTest_DEPLOYMENT_TARGET "${CMAKE_MATCH_1}") @@ -2084,9 +2113,10 @@ set_property(TEST MSVCRuntimeLibrary.Fortran APPEND PROPERTY LABELS "Fortran") endif() endif() - if(MSVC OR + if(MSVC OR CMAKE_C_COMPILER_ID MATCHES "PellesC" OR "${CMAKE_GENERATOR}" MATCHES "(MSYS|MinGW) Makefiles") ADD_TEST_MACRO(ModuleDefinition example_exe) + set_property(TEST ModuleDefinition APPEND PROPERTY LABELS "PellesC") endif() if(CMAKE_C_COMPILER_ID MATCHES "Watcom" AND WIN32) @@ -2133,6 +2163,11 @@ ADD_TEST_MACRO(VSMASM VSMASM) endif() + if(CMAKE_C_COMPILER_ID STREQUAL "PellesC") + ADD_TEST_MACRO(PellesASM PellesASM) + set_property(TEST PellesASM APPEND PROPERTY LABELS "PellesC") + endif() + if(CMAKE_GENERATOR MATCHES "Visual Studio") if(NOT MSVC60) ADD_TEST_MACRO(SBCS SBCS) @@ -2457,7 +2492,7 @@ --build-options "-DUNUSED_CLI_VARIABLE=Unused") set_tests_properties(WarnUnusedCliUnused PROPERTIES - PASS_REGULAR_EXPRESSION "CMake Warning:.*Manually-specified variables were not used by the project:.* UNUSED_CLI_VARIABLE") + PASS_REGULAR_EXPRESSION "CMake Warning \\(unused-cli\\):.*Manually-specified variables were not used by the project:.* UNUSED_CLI_VARIABLE") list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/WarnUnusedCliUnused") add_test(WarnUnusedCliUsed ${CMAKE_CTEST_COMMAND} @@ -3014,7 +3049,7 @@ ) #make sure all 3 subdirs were added set_tests_properties(CTestTestSubdir PROPERTIES - PASS_REGULAR_EXPRESSION "0 tests failed out of 3") + PASS_REGULAR_EXPRESSION "100% tests passed out of 3") configure_file( "${CMake_SOURCE_DIR}/Tests/CTestTestDepends/test.cmake.in"
diff --git a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt index c0ca2c7..0d44e21 100644 --- a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt
@@ -14,6 +14,13 @@ include(CheckCXXSymbolExists) +set(CMAKE_REQUIRED_FLAGS "-DCSE_REQUIRED_FLAG") +check_cxx_symbol_exists(required_flag_symbol "cm_cse.h" CSE_RESULT_REQUIRED_FLAG) +if(NOT CSE_RESULT_REQUIRED_FLAG) + message(SEND_ERROR "CheckCXXSymbolExists did not honor CMAKE_REQUIRED_FLAGS") +endif() +unset(CMAKE_REQUIRED_FLAGS) + foreach(_config_type Release RelWithDebInfo MinSizeRel Debug) set(CMAKE_TRY_COMPILE_CONFIGURATION ${_config_type}) unset(CSE_RESULT_${_config_type} CACHE)
diff --git a/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt index 0a8be7a..218364a 100644 --- a/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt
@@ -14,6 +14,13 @@ include(CheckSymbolExists) +set(CMAKE_REQUIRED_FLAGS "-DCSE_REQUIRED_FLAG") +check_symbol_exists(required_flag_symbol "cm_cse.h" CSE_RESULT_REQUIRED_FLAG) +if(NOT CSE_RESULT_REQUIRED_FLAG) + message(SEND_ERROR "CheckSymbolExists did not honor CMAKE_REQUIRED_FLAGS") +endif() +unset(CMAKE_REQUIRED_FLAGS) + foreach(_config_type Release RelWithDebInfo MinSizeRel Debug) set(CMAKE_TRY_COMPILE_CONFIGURATION ${_config_type}) unset(CSE_RESULT_${_config_type} CACHE)
diff --git a/Tests/CMakeOnly/CheckSymbolExists/cm_cse.h b/Tests/CMakeOnly/CheckSymbolExists/cm_cse.h index 4f41c76..7c2d1c4 100644 --- a/Tests/CMakeOnly/CheckSymbolExists/cm_cse.h +++ b/Tests/CMakeOnly/CheckSymbolExists/cm_cse.h
@@ -3,4 +3,8 @@ int non_existent_function_for_symbol_test(); +#ifdef CSE_REQUIRED_FLAG +# define required_flag_symbol 1 +#endif + #endif
diff --git a/Tests/CMakeTests/MessageTest.cmake.in b/Tests/CMakeTests/MessageTest.cmake.in index a9833b9..4b8489d 100644 --- a/Tests/CMakeTests/MessageTest.cmake.in +++ b/Tests/CMakeTests/MessageTest.cmake.in
@@ -25,6 +25,6 @@ message(FATAL_ERROR "message(WARNING) did not appear properly") endif() -if(NOT "${err}" MATCHES "CMake Warning \\(dev\\) at[^\n]*:\r?\n message-author") +if(NOT "${err}" MATCHES "CMake Warning \\(author\\) at[^\n]*:\r?\n message-author") message(FATAL_ERROR "message(AUTHOR_WARNING) did not appear properly") endif()
diff --git a/Tests/CMakeTests/ProcessorCountTest.cmake.in b/Tests/CMakeTests/ProcessorCountTest.cmake.in index f92dcc4..a2595c7 100644 --- a/Tests/CMakeTests/ProcessorCountTest.cmake.in +++ b/Tests/CMakeTests/ProcessorCountTest.cmake.in
@@ -1,5 +1,8 @@ include(ProcessorCount) +set(CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL ANY) +# ProcessorCount() must tolerate execute_process() failures + ProcessorCount(processor_count) message("### 1. This line should be the first line of text in the test output.")
diff --git a/Tests/COnly/libc2.c b/Tests/COnly/libc2.c index 28d46f3..c36bc6d 100644 --- a/Tests/COnly/libc2.c +++ b/Tests/COnly/libc2.c
@@ -1,5 +1,8 @@ #include "libc2.h" +#ifdef __POCC__ +CM_TEST_LIB_EXPORT +#endif float LibC2Func(void) { return 1.0;
diff --git a/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake b/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake index 07281ed..e2664b6 100644 --- a/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake +++ b/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake
@@ -390,26 +390,23 @@ file(GLOB_RECURSE symlink_files RELATIVE "${CPackComponentsForAll_BINARY_DIR}" "${CPackComponentsForAll_BINARY_DIR}/*/symlink_*") foreach(check_symlink IN LISTS symlink_files) - get_filename_component(symlink_name "${check_symlink}" NAME) - execute_process(COMMAND ls -la "${check_symlink}" - WORKING_DIRECTORY "${CPackComponentsForAll_BINARY_DIR}" - OUTPUT_VARIABLE SYMLINK_POINT_ - OUTPUT_STRIP_TRAILING_WHITESPACE) + cmake_path(GET check_symlink FILENAME symlink_name) + file(READ_SYMLINK "${check_symlink}" symlink_dst) if("${symlink_name}" STREQUAL "symlink_samedir_path" OR "${symlink_name}" STREQUAL "symlink_samedir_path_current_dir" OR "${symlink_name}" STREQUAL "symlink_samedir_path_longer") - string(REGEX MATCH "^.*${whitespaces}->${whitespaces}depth_three$" check_symlink "${SYMLINK_POINT_}") + string(REGEX MATCH "^depth_three$" check_symlink "${symlink_dst}") elseif("${symlink_name}" STREQUAL "symlink_subdir_path") - string(REGEX MATCH "^.*${whitespaces}->${whitespaces}depth_two/depth_three$" check_symlink "${SYMLINK_POINT_}") + string(REGEX MATCH "^depth_two/depth_three$" check_symlink "${symlink_dst}") elseif("${symlink_name}" STREQUAL "symlink_parentdir_path") - string(REGEX MATCH "^.*${whitespaces}->${whitespaces}../$" check_symlink "${SYMLINK_POINT_}") + string(REGEX MATCH "^\.\.$" check_symlink "${symlink_dst}") elseif("${symlink_name}" STREQUAL "symlink_to_non_relocatable_path") - string(REGEX MATCH "^.*${whitespaces}->${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/non_relocatable/depth_two$" check_symlink "${SYMLINK_POINT_}") + string(REGEX MATCH "^${CPACK_PACKAGING_INSTALL_PREFIX}/non_relocatable/depth_two$" check_symlink "${symlink_dst}") elseif("${symlink_name}" STREQUAL "symlink_outside_package") - string(REGEX MATCH "^.*${whitespaces}->${whitespaces}outside_package$" check_symlink "${SYMLINK_POINT_}") + string(REGEX MATCH "^outside_package$" check_symlink "${symlink_dst}") elseif("${symlink_name}" STREQUAL "symlink_outside_wdr") - string(REGEX MATCH "^.*${whitespaces}->${whitespaces}/outside_package_wdr$" check_symlink "${SYMLINK_POINT_}") + string(REGEX MATCH "^/outside_package_wdr$" check_symlink "${symlink_dst}") elseif("${symlink_name}" STREQUAL "symlink_other_relocatable_path" OR "${symlink_name}" STREQUAL "symlink_from_non_relocatable_path" OR "${symlink_name}" STREQUAL "symlink_relocatable_subpath") @@ -419,7 +416,7 @@ endif() if(NOT check_symlink) - message(FATAL_ERROR "symlink points to unexpected location '${SYMLINK_POINT_}'") + message(FATAL_ERROR "symlink points to unexpected location '${symlink_dst}'") endif() endforeach()
diff --git a/Tests/CPackNSISGenerator/CMakeLists.txt b/Tests/CPackNSISGenerator/CMakeLists.txt index 569a478..ed4a264 100644 --- a/Tests/CPackNSISGenerator/CMakeLists.txt +++ b/Tests/CPackNSISGenerator/CMakeLists.txt
@@ -40,6 +40,7 @@ set(CPACK_NSIS_BRANDING_TEXT "CMake branding text") set(CPACK_NSIS_BRANDING_TEXT_TRIM_POSITION "RIGHT") set(CPACK_NSIS_IGNORE_LICENSE_PAGE ON) +set(CPACK_NSIS_IGNORE_COMPONENTS_PAGE ON) set(CPACK_NSIS_CRC_CHECK "off") include(CPack)
diff --git a/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake b/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake index e811deb..66a1124 100644 --- a/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake +++ b/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake
@@ -70,6 +70,15 @@ message(FATAL_ERROR "License found in the project") endif() +# components page should not be present +file(STRINGS "${project_file}" line REGEX "!insertmacro MUI_PAGE_COMPONENTS") +string(FIND "${line}" "MUI_PAGE_COMPONENTS" output_index) +if("${output_index}" EQUAL "-1") + message(STATUS "Components page not found in the project") +else() + message(FATAL_ERROR "Components page found in the project") +endif() + file(STRINGS "${project_file}" line REGEX [[\\_CON\\]]) string(FIND "${line}" [[\_CON\]] output_index) message(STATUS "Found CON component reserved directory name as _CON")
diff --git a/Tests/CTestUpdateCommon.cmake b/Tests/CTestUpdateCommon.cmake index 2ecc088..e4d1090 100644 --- a/Tests/CTestUpdateCommon.cmake +++ b/Tests/CTestUpdateCommon.cmake
@@ -246,6 +246,10 @@ if("${r}" MATCHES "PriorRevision") message(FATAL_ERROR "Found PriorRevision in no update test") endif() + if(DEFINED NO_UPDATE_REVISION AND + NOT "${r}" MATCHES "<Revision>${NO_UPDATE_REVISION}</Revision>") + message(FATAL_ERROR "Missing expected <Revision> in no update test") + endif() if("${r}" MATCHES "<Revision>") set(found_revisions TRUE) endif()
diff --git a/Tests/CTestUpdateGIT.cmake.in b/Tests/CTestUpdateGIT.cmake.in index 4921304..0ff403a 100644 --- a/Tests/CTestUpdateGIT.cmake.in +++ b/Tests/CTestUpdateGIT.cmake.in
@@ -347,6 +347,44 @@ rewind_source(dash-source) #----------------------------------------------------------------------------- +# Test no update with a VERSION_ONLY option. +message("Running CTest Dashboard Script (No update argument)...") + +set(ctest_update_args VERSION_ONLY) +create_dashboard_script(dash-binary-no-update-arg + "# git command configuration +set(CTEST_GIT_COMMAND \"${GIT}\") +") +unset(ctest_update_args) + +# Run the dashboard script with CTest. +set(NO_UPDATE 1) +run_dashboard_script(dash-binary-no-update-arg) +unset(NO_UPDATE) + +rewind_source(dash-source) + +#----------------------------------------------------------------------------- +# Test reporting a specific revision with VERSION_OVERRIDE. +message("Running CTest Dashboard Script (Version override)...") + +set(ctest_update_args VERSION_OVERRIDE qwertyuiop) +create_dashboard_script(dash-binary-version-override + "# git command configuration +set(CTEST_GIT_COMMAND \"${GIT}\") +") +unset(ctest_update_args) + +# Run the dashboard script with CTest. +set(NO_UPDATE 1) +set(NO_UPDATE_REVISION qwertyuiop) +run_dashboard_script(dash-binary-version-override) +unset(NO_UPDATE_REVISION) +unset(NO_UPDATE) + +rewind_source(dash-source) + +#----------------------------------------------------------------------------- # Test ctest_update(QUIET) message("Running CTest Dashboard Script (update quietly)...")
diff --git a/Tests/CompileFeatures/CMakeLists.txt b/Tests/CompileFeatures/CMakeLists.txt index 1a65699..08a31c4 100644 --- a/Tests/CompileFeatures/CMakeLists.txt +++ b/Tests/CompileFeatures/CMakeLists.txt
@@ -1,12 +1,15 @@ - cmake_minimum_required(VERSION 3.10) +project(CompileFeatures LANGUAGES NONE) -project(CompileFeatures) - +enable_language(C) set(ext_C c) -set(ext_CXX cpp) set(std_C 90 99 11 17 23) -set(std_CXX 98 11 14 17 20 23 26) + +if(CMake_TEST_CXX) + enable_language(CXX) + set(ext_CXX cpp) + set(std_CXX 98 11 14 17 20 23 26) +endif() if(CMake_TEST_CUDA) enable_language(CUDA) @@ -25,8 +28,21 @@ string(TOLOWER "${lang}_std_${std}" feature) if("${std}" IN_LIST CMake_TEST_${lang}_STANDARDS OR CMAKE_${lang}${std}_STANDARD_COMPILE_OPTION) + # Test <lang>_std_<std> feature. add_library(test_${feature} OBJECT ${feature}.${ext_${lang}}) target_compile_features(test_${feature} PRIVATE ${feature}) + # Test <lang>_STANDARD <std> property. + if(NOT "${lang}_STANDARD_${std}" MATCHES "^(CUDA_STANDARD_03|HIP_STANDARD_98)$") + add_library(test_${lang}_STANDARD_${std} OBJECT ${lang}_STANDARD_${std}.${ext_${lang}}) + set_property(TARGET test_${lang}_STANDARD_${std} PROPERTY ${lang}_STANDARD ${std}) + if(CMAKE_${lang}${std}_STANDARD_COMPILE_OPTION STREQUAL [[]] + OR "${CMAKE_${lang}_COMPILER_ID};${CMAKE_${lang}_SIMULATE_ID};${CMAKE_${lang}_COMPILER_FRONTEND_VARIANT};${lang};${std}" MATCHES "^(Clang;MSVC;GNU;CXX;(98|11)|Intel;MSVC;(MSVC)?;CXX;11)$") + target_compile_definitions(test_${lang}_STANDARD_${std} PRIVATE ${lang}_STD_MISSING) + elseif(CMAKE_${lang}_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC" + AND CMAKE_${lang}${std}_STANDARD_COMPILE_OPTION MATCHES [[^-std:c\+*latest$]]) + target_compile_definitions(test_${lang}_STANDARD_${std} PRIVATE ${lang}_STD_LATEST) + endif() + endif() endif() endforeach() endforeach() @@ -343,10 +359,7 @@ # always add a target "CompileFeatures" if ((NOT CXX_expected_features) OR (NOT cxx_auto_type IN_LIST CXX_expected_features)) - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp" - "int main(int,char**) { return 0; }\n" - ) - add_executable(CompileFeatures "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp") + add_executable(CompileFeatures main.c) else() # these tests only work if at least cxx_auto_type is available add_executable(CompileFeatures main.cpp)
diff --git a/Tests/CompileFeatures/CUDA_STANDARD_11.cu b/Tests/CompileFeatures/CUDA_STANDARD_11.cu new file mode 100644 index 0000000..5418e00 --- /dev/null +++ b/Tests/CompileFeatures/CUDA_STANDARD_11.cu
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_11 +# error "CUDA_STANDARD 11 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD < CXX_STD_11 +# error "CUDA_STANDARD 11 not honored" +#endif
diff --git a/Tests/CompileFeatures/CUDA_STANDARD_14.cu b/Tests/CompileFeatures/CUDA_STANDARD_14.cu new file mode 100644 index 0000000..415ff81 --- /dev/null +++ b/Tests/CompileFeatures/CUDA_STANDARD_14.cu
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_14 +# error "CUDA_STANDARD 14 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_11 +# error "CUDA_STANDARD 14 not honored" +#endif
diff --git a/Tests/CompileFeatures/CUDA_STANDARD_17.cu b/Tests/CompileFeatures/CUDA_STANDARD_17.cu new file mode 100644 index 0000000..d31b45e --- /dev/null +++ b/Tests/CompileFeatures/CUDA_STANDARD_17.cu
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_17 +# error "CUDA_STANDARD 17 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_14 +# error "CUDA_STANDARD 17 not honored" +#endif
diff --git a/Tests/CompileFeatures/CUDA_STANDARD_20.cu b/Tests/CompileFeatures/CUDA_STANDARD_20.cu new file mode 100644 index 0000000..330681a --- /dev/null +++ b/Tests/CompileFeatures/CUDA_STANDARD_20.cu
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_20 +# error "CUDA_STANDARD 20 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_17 +# error "CUDA_STANDARD 20 not honored" +#endif
diff --git a/Tests/CompileFeatures/CUDA_STANDARD_23.cu b/Tests/CompileFeatures/CUDA_STANDARD_23.cu new file mode 100644 index 0000000..3e0eb5d --- /dev/null +++ b/Tests/CompileFeatures/CUDA_STANDARD_23.cu
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_23 +# error "CUDA_STANDARD 23 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_20 +# error "CUDA_STANDARD 23 not honored" +#endif
diff --git a/Tests/CompileFeatures/CUDA_STANDARD_26.cu b/Tests/CompileFeatures/CUDA_STANDARD_26.cu new file mode 100644 index 0000000..b18a1a3 --- /dev/null +++ b/Tests/CompileFeatures/CUDA_STANDARD_26.cu
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_23 +# error "CUDA_STANDARD 26 not honored" +#endif
diff --git a/Tests/CompileFeatures/CXX_STANDARD_11.cpp b/Tests/CompileFeatures/CXX_STANDARD_11.cpp new file mode 100644 index 0000000..a1d0cd3 --- /dev/null +++ b/Tests/CompileFeatures/CXX_STANDARD_11.cpp
@@ -0,0 +1,9 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_11 && !defined(CXX_STD_MISSING) +# error "CXX_STANDARD 11 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD < CXX_STD_11 && \ + !(CXX_STD == CXX_STD_98 && \ + (defined(__IBMCPP__) && defined(_AIX) && __IBMCPP__ == 1610)) +# error "CXX_STANDARD 11 not honored" +#endif
diff --git a/Tests/CompileFeatures/CXX_STANDARD_14.cpp b/Tests/CompileFeatures/CXX_STANDARD_14.cpp new file mode 100644 index 0000000..bc3ae3f --- /dev/null +++ b/Tests/CompileFeatures/CXX_STANDARD_14.cpp
@@ -0,0 +1,12 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_14 +# error "CXX_STANDARD 14 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_11 && \ + !(CXX_STD == CXX_STD_11 && \ + ((defined(__GNUC__) && defined(__GNUC_MINOR__) && __GNUC__ == 4 && \ + __GNUC_MINOR__ <= 8 && !defined(__clang__) && \ + !defined(__INTEL_COMPILER) && !defined(__PGI)) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x5150))) +# error "CXX_STANDARD 14 not honored" +#endif
diff --git a/Tests/CompileFeatures/CXX_STANDARD_17.cpp b/Tests/CompileFeatures/CXX_STANDARD_17.cpp new file mode 100644 index 0000000..fec9e04 --- /dev/null +++ b/Tests/CompileFeatures/CXX_STANDARD_17.cpp
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_17 +# error "CXX_STANDARD 17 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_14 +# error "CXX_STANDARD 17 not honored" +#endif
diff --git a/Tests/CompileFeatures/CXX_STANDARD_20.cpp b/Tests/CompileFeatures/CXX_STANDARD_20.cpp new file mode 100644 index 0000000..5afaec6 --- /dev/null +++ b/Tests/CompileFeatures/CXX_STANDARD_20.cpp
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_20 +# error "CXX_STANDARD 20 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_17 +# error "CXX_STANDARD 20 not honored" +#endif
diff --git a/Tests/CompileFeatures/CXX_STANDARD_23.cpp b/Tests/CompileFeatures/CXX_STANDARD_23.cpp new file mode 100644 index 0000000..0f24893 --- /dev/null +++ b/Tests/CompileFeatures/CXX_STANDARD_23.cpp
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_23 && !defined(CXX_STD_LATEST) +# error "CXX_STANDARD 23 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_20 +# error "CXX_STANDARD 23 not honored" +#endif
diff --git a/Tests/CompileFeatures/CXX_STANDARD_26.cpp b/Tests/CompileFeatures/CXX_STANDARD_26.cpp new file mode 100644 index 0000000..1091c92 --- /dev/null +++ b/Tests/CompileFeatures/CXX_STANDARD_26.cpp
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_23 +# error "CXX_STANDARD 26 not honored" +#endif
diff --git a/Tests/CompileFeatures/CXX_STANDARD_98.cpp b/Tests/CompileFeatures/CXX_STANDARD_98.cpp new file mode 100644 index 0000000..14441da --- /dev/null +++ b/Tests/CompileFeatures/CXX_STANDARD_98.cpp
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD >= CXX_STD_11 && !defined(CXX_STD_MISSING) +# error "CXX_STANDARD 98 honored as higher standard" +#endif
diff --git a/Tests/CompileFeatures/C_STANDARD_11.c b/Tests/CompileFeatures/C_STANDARD_11.c new file mode 100644 index 0000000..5259ff6 --- /dev/null +++ b/Tests/CompileFeatures/C_STANDARD_11.c
@@ -0,0 +1,8 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD > C_STD_11 +# error "C_STANDARD 11 honored as higher standard" +#endif +#if defined(C_STD) && C_STD <= C_STD_99 && \ + !(defined(__SUNPRO_C) && __SUNPRO_C < 0x5140 && C_STD == C_STD_99) +# error "C_STANDARD 11 not honored" +#endif
diff --git a/Tests/CompileFeatures/C_STANDARD_17.c b/Tests/CompileFeatures/C_STANDARD_17.c new file mode 100644 index 0000000..7cd6e5b --- /dev/null +++ b/Tests/CompileFeatures/C_STANDARD_17.c
@@ -0,0 +1,7 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD > C_STD_17 +# error "C_STANDARD 17 honored as higher standard" +#endif +#if defined(C_STD) && C_STD <= C_STD_11 +# error "C_STANDARD 17 not honored" +#endif
diff --git a/Tests/CompileFeatures/C_STANDARD_23.c b/Tests/CompileFeatures/C_STANDARD_23.c new file mode 100644 index 0000000..daf596c --- /dev/null +++ b/Tests/CompileFeatures/C_STANDARD_23.c
@@ -0,0 +1,8 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD > C_STD_23 && !defined(C_STD_LATEST) +# error "C_STANDARD 23 honored as higher standard" +#endif +#if defined(C_STD) && C_STD <= C_STD_17 && \ + !(C_STD == C_STD_17 && defined(__clang_major__) && __clang_major__ < 14) +# error "C_STANDARD 23 not honored" +#endif
diff --git a/Tests/CompileFeatures/C_STANDARD_90.c b/Tests/CompileFeatures/C_STANDARD_90.c new file mode 100644 index 0000000..60669e7 --- /dev/null +++ b/Tests/CompileFeatures/C_STANDARD_90.c
@@ -0,0 +1,4 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD >= C_STD_99 && !defined(C_STD_MISSING) +# error "C_STANDARD 90 honored as higher standard" +#endif
diff --git a/Tests/CompileFeatures/C_STANDARD_99.c b/Tests/CompileFeatures/C_STANDARD_99.c new file mode 100644 index 0000000..0383bc8 --- /dev/null +++ b/Tests/CompileFeatures/C_STANDARD_99.c
@@ -0,0 +1,7 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD > C_STD_99 && !defined(C_STD_MISSING) +# error "C_STANDARD 99 honored as higher standard" +#endif +#if defined(C_STD) && C_STD < C_STD_99 +# error "C_STANDARD 99 not honored" +#endif
diff --git a/Tests/CompileFeatures/HIP_STANDARD_11.hip b/Tests/CompileFeatures/HIP_STANDARD_11.hip new file mode 100644 index 0000000..32fd87b --- /dev/null +++ b/Tests/CompileFeatures/HIP_STANDARD_11.hip
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_11 +# error "HIP_STANDARD 11 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD < CXX_STD_11 +# error "HIP_STANDARD 11 not honored" +#endif
diff --git a/Tests/CompileFeatures/HIP_STANDARD_14.hip b/Tests/CompileFeatures/HIP_STANDARD_14.hip new file mode 100644 index 0000000..2f0d7df --- /dev/null +++ b/Tests/CompileFeatures/HIP_STANDARD_14.hip
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_14 +# error "HIP_STANDARD 14 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_11 +# error "HIP_STANDARD 14 not honored" +#endif
diff --git a/Tests/CompileFeatures/HIP_STANDARD_17.hip b/Tests/CompileFeatures/HIP_STANDARD_17.hip new file mode 100644 index 0000000..831f537 --- /dev/null +++ b/Tests/CompileFeatures/HIP_STANDARD_17.hip
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_17 +# error "HIP_STANDARD 17 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_14 +# error "HIP_STANDARD 17 not honored" +#endif
diff --git a/Tests/CompileFeatures/HIP_STANDARD_20.hip b/Tests/CompileFeatures/HIP_STANDARD_20.hip new file mode 100644 index 0000000..404b763 --- /dev/null +++ b/Tests/CompileFeatures/HIP_STANDARD_20.hip
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_20 +# error "HIP_STANDARD 20 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_17 +# error "HIP_STANDARD 20 not honored" +#endif
diff --git a/Tests/CompileFeatures/HIP_STANDARD_23.hip b/Tests/CompileFeatures/HIP_STANDARD_23.hip new file mode 100644 index 0000000..556b3e9 --- /dev/null +++ b/Tests/CompileFeatures/HIP_STANDARD_23.hip
@@ -0,0 +1,7 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD > CXX_STD_23 +# error "HIP_STANDARD 23 honored as higher standard" +#endif +#if defined(CXX_STD) && CXX_STD <= CXX_STD_20 +# error "HIP_STANDARD 23 not honored" +#endif
diff --git a/Tests/CompileFeatures/HIP_STANDARD_26.hip b/Tests/CompileFeatures/HIP_STANDARD_26.hip new file mode 100644 index 0000000..7c774a8 --- /dev/null +++ b/Tests/CompileFeatures/HIP_STANDARD_26.hip
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_23 +# error "HIP_STANDARD 26 not honored" +#endif
diff --git a/Tests/CompileFeatures/c_std.h b/Tests/CompileFeatures/c_std.h index afe0308..7e6402d 100644 --- a/Tests/CompileFeatures/c_std.h +++ b/Tests/CompileFeatures/c_std.h
@@ -5,4 +5,6 @@ #ifdef __STDC_VERSION__ # define C_STD __STDC_VERSION__ +#elif defined(__POCC_STDC_VERSION__) +# define C_STD __POCC_STDC_VERSION__ #endif
diff --git a/Tests/CompileFeatures/cxx_std.h b/Tests/CompileFeatures/cxx_std.h index f17b683..c9d9ecf 100644 --- a/Tests/CompileFeatures/cxx_std.h +++ b/Tests/CompileFeatures/cxx_std.h
@@ -28,7 +28,9 @@ # define CXX_STD __cplusplus # endif #elif defined(__NVCOMPILER) -# if __cplusplus == CXX_STD_17 && defined(__cpp_aggregate_paren_init) +# if __cplusplus > CXX_STD_20 && defined(__cpp_pp_embed) +# define CXX_STD /*CXX_STD_26*/ (CXX_STD_23 + 1) +# elif __cplusplus == CXX_STD_17 && defined(__cpp_aggregate_paren_init) # define CXX_STD CXX_STD_20 # else # define CXX_STD __cplusplus
diff --git a/Tests/CompileFeatures/main.c b/Tests/CompileFeatures/main.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/CompileFeatures/main.c
@@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +}
diff --git a/Tests/Cuda/CMakeLists.txt b/Tests/Cuda/CMakeLists.txt index a355a4d..cfe47da 100644 --- a/Tests/Cuda/CMakeLists.txt +++ b/Tests/Cuda/CMakeLists.txt
@@ -32,3 +32,4 @@ add_cuda_test_macro(Cuda.WithC CudaWithC) add_cuda_test_macro(Cuda.Bin2C CudaBin2C) +add_cuda_test_macro(Cuda.Sanitizer ${CMAKE_CTEST_COMMAND} --output-on-failure)
diff --git a/Tests/Cuda/Sanitizer/CMakeLists.txt b/Tests/Cuda/Sanitizer/CMakeLists.txt new file mode 100644 index 0000000..df3b4f0 --- /dev/null +++ b/Tests/Cuda/Sanitizer/CMakeLists.txt
@@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.3) +project(Sanitizer CXX) + +enable_testing() + +# Goal for this example: +# Validate that we can use CUDAToolkit sanitizer +find_package(CUDAToolkit REQUIRED) + +if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.1) + add_executable(CudaSanitizer test_sanitizer.cpp) + target_link_libraries(CudaSanitizer PRIVATE CUDA::sanitizer CUDA::cudart_static) + + add_test(NAME CudaSanitizer COMMAND CudaSanitizer) + if(WIN32) + get_property(sanitizer_path TARGET CUDA::sanitizer PROPERTY IMPORTED_LOCATION) + get_filename_component(sanitizer_dir "${sanitizer_path}" DIRECTORY) + set_property(TEST CudaSanitizer PROPERTY ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${sanitizer_dir}") + endif() +endif()
diff --git a/Tests/Cuda/Sanitizer/test_sanitizer.cpp b/Tests/Cuda/Sanitizer/test_sanitizer.cpp new file mode 100644 index 0000000..dc3f70d --- /dev/null +++ b/Tests/Cuda/Sanitizer/test_sanitizer.cpp
@@ -0,0 +1,74 @@ +#include <iostream> + +#include <cuda_runtime.h> +#include <sanitizer.h> +#include <stddef.h> + +namespace { + +bool malloc_called = false; +bool free_called = false; + +void sanitizer_callback(void* userdata, Sanitizer_CallbackDomain domain, + Sanitizer_CallbackId cbid, void const* cbdata) +{ + if (domain == SANITIZER_CB_DOMAIN_RESOURCE) { + switch (cbid) { + case SANITIZER_CBID_RESOURCE_DEVICE_MEMORY_ALLOC: + malloc_called = true; + break; + case SANITIZER_CBID_RESOURCE_DEVICE_MEMORY_FREE: + free_called = true; + break; + default: + break; + } + } +} + +} + +#define SANITIZER_TRY(call) \ + do { \ + SanitizerResult result; \ + if ((result = (call)) != SANITIZER_SUCCESS) { \ + const char* result_str; \ + sanitizerGetResultString(result, &result_str); \ + std::cerr << __FILE__ << ":" << __LINE__ << ": " << result_str << "\n"; \ + return 1; \ + } \ + } while (0) + +#define CUDA_TRY(call) \ + do { \ + cudaError_t result; \ + if ((result = (call)) != cudaSuccess) { \ + std::cerr << __FILE__ << ":" << __LINE__ << ": " \ + << cudaGetErrorName(result) << "\n"; \ + return 1; \ + } \ + } while (0) + +int main() +{ + Sanitizer_SubscriberHandle handle; + SANITIZER_TRY(sanitizerSubscribe(&handle, sanitizer_callback, NULL)); + SANITIZER_TRY( + sanitizerEnableDomain(1, handle, SANITIZER_CB_DOMAIN_RESOURCE)); + + void* dev_ptr; + CUDA_TRY(cudaMalloc(&dev_ptr, 10)); + if (!malloc_called) { + std::cerr << "cudaMalloc() did not invoke callback\n"; + return 1; + } + + CUDA_TRY(cudaFree(dev_ptr)); + if (!free_called) { + std::cerr << "cudaFree() did not invoke callback\n"; + return 1; + } + + SANITIZER_TRY(sanitizerUnsubscribe(handle)); + return 0; +}
diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt index 2ec6ee8..396f6d3 100644 --- a/Tests/CudaOnly/CMakeLists.txt +++ b/Tests/CudaOnly/CMakeLists.txt
@@ -41,6 +41,7 @@ endif() add_cuda_test_macro(CudaOnly.DeviceLTO CudaOnlyDeviceLTO) +add_cuda_test_macro(CudaOnly.Sanitizer ${CMAKE_CTEST_COMMAND} --output-on-failure) if(MSVC) # Tests for features that only work with MSVC
diff --git a/Tests/CudaOnly/Sanitizer/CMakeLists.txt b/Tests/CudaOnly/Sanitizer/CMakeLists.txt new file mode 100644 index 0000000..10cf163 --- /dev/null +++ b/Tests/CudaOnly/Sanitizer/CMakeLists.txt
@@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.3) +project(Sanitizer CUDA) + +enable_testing() + +# Goal for this example: +# Validate that we can use CUDAToolkit sanitizer +find_package(CUDAToolkit REQUIRED) + +if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.1) + add_executable(CudaOnlySanitizer test_sanitizer.cu) + target_link_libraries(CudaOnlySanitizer PRIVATE CUDA::sanitizer) + + add_test(NAME CudaOnlySanitizer COMMAND CudaOnlySanitizer) + if(WIN32) + get_property(sanitizer_path TARGET CUDA::sanitizer PROPERTY IMPORTED_LOCATION) + get_filename_component(sanitizer_dir "${sanitizer_path}" DIRECTORY) + set_property(TEST CudaOnlySanitizer PROPERTY ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${sanitizer_dir}") + endif() +endif()
diff --git a/Tests/CudaOnly/Sanitizer/test_sanitizer.cu b/Tests/CudaOnly/Sanitizer/test_sanitizer.cu new file mode 100644 index 0000000..4fa9c25 --- /dev/null +++ b/Tests/CudaOnly/Sanitizer/test_sanitizer.cu
@@ -0,0 +1 @@ +#include "../../Cuda/Sanitizer/test_sanitizer.cpp"
diff --git a/Tests/ExportImport/InitialCache.cmake.in b/Tests/ExportImport/InitialCache.cmake.in index 55aef86..0494d40 100644 --- a/Tests/ExportImport/InitialCache.cmake.in +++ b/Tests/ExportImport/InitialCache.cmake.in
@@ -14,5 +14,5 @@ set(CMAKE_INSTALL_PREFIX "@ExportImport_BINARY_DIR@/Root" CACHE STRING "Installation Prefix") set(CMAKE_SKIP_RPATH ON CACHE BOOL "No RPATH") set(CMAKE_GNUtoMS "@ExportImport_GNUtoMS@" CACHE BOOL "CMAKE_GNUtoMS") -set(CMake_TEST_CUDA "@CMake_TEST_CUDA@" CACHE BOOL "CMake_TEST_CUDA") +set(CMake_TEST_CUDA "@CMake_TEST_CUDA@" CACHE STRING "CMake_TEST_CUDA") set(Foo_DIR "@CMAKE_CURRENT_SOURCE_DIR@/External" CACHE PATH "Foo cmake package directory")
diff --git a/Tests/ExternalProject/CMakeLists.txt b/Tests/ExternalProject/CMakeLists.txt index c1a4bae..87f6d1b 100644 --- a/Tests/ExternalProject/CMakeLists.txt +++ b/Tests/ExternalProject/CMakeLists.txt
@@ -435,9 +435,10 @@ set_property(TARGET ${proj} PROPERTY FOLDER "GIT") set(proj TS1-GIT-all-GIT_SUBMODULES-via-CMP0097-OLD) - set(CMAKE_WARN_DEPRECATED FALSE) # we are testing CMP0097 OLD behavior + cmake_diagnostic(PUSH) + cmake_diagnostic(SET CMD_DEPRECATED IGNORE) cmake_policy(SET CMP0097 OLD) - unset(CMAKE_WARN_DEPRECATED) + cmake_diagnostic(POP) ExternalProject_Add(${proj} GIT_REPOSITORY "${local_git_repo}" GIT_SUBMODULES ""
diff --git a/Tests/ExternalProjectLocal/Step5/MathFunctions/MakeTable.cxx b/Tests/ExternalProjectLocal/Step5/MathFunctions/MakeTable.cxx index cebd50f..adc02e5 100644 --- a/Tests/ExternalProjectLocal/Step5/MathFunctions/MakeTable.cxx +++ b/Tests/ExternalProjectLocal/Step5/MathFunctions/MakeTable.cxx
@@ -22,11 +22,12 @@ fprintf(fout, "double sqrtTable[] = {\n"); for (i = 0; i < 10; ++i) { result = sqrt(static_cast<double>(i)); - fprintf(fout, "%g,\n", result); + // FIXME(OrangeC#1138): OrangeC 7 no longer accepts plain '0' as a double + fprintf(fout, "%.5f,\n", result); } // close the table with a zero - fprintf(fout, "0};\n"); + fprintf(fout, "0.0};\n"); fclose(fout); return 0; }
diff --git a/Tests/FindPackageCpsTest/CMakeLists.txt b/Tests/FindPackageCpsTest/CMakeLists.txt index 3d6f01a..b31de13 100644 --- a/Tests/FindPackageCpsTest/CMakeLists.txt +++ b/Tests/FindPackageCpsTest/CMakeLists.txt
@@ -192,7 +192,7 @@ elseif(NOT TARGET Global::Target) message(SEND_ERROR "Global::Target missing !") else() - expect_global(Foo::Empty FALSE) + expect_global(Global::Target TRUE) endif() ###############################################################################
diff --git a/Tests/FindPackageModeMakefileTest/CMakeLists.txt b/Tests/FindPackageModeMakefileTest/CMakeLists.txt index 0b15be8..a9bcfc4 100644 --- a/Tests/FindPackageModeMakefileTest/CMakeLists.txt +++ b/Tests/FindPackageModeMakefileTest/CMakeLists.txt
@@ -9,9 +9,10 @@ execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} -v OUTPUT_VARIABLE makeVersionOutput ERROR_QUIET - TIMEOUT 10) + TIMEOUT 10 + RESULT_VARIABLE _make_result) string(TOUPPER "${makeVersionOutput}" MAKE_VERSION_OUTPUT) - if("${MAKE_VERSION_OUTPUT}" MATCHES "GNU MAKE") + if(_make_result EQUAL 0 AND "${MAKE_VERSION_OUTPUT}" MATCHES "GNU MAKE") # build a library which we can search during the test add_library(foo STATIC foo.cpp)
diff --git a/Tests/Fuzzing/README.rst b/Tests/Fuzzing/README.rst index dd9fe98..ee73dd0 100644 --- a/Tests/Fuzzing/README.rst +++ b/Tests/Fuzzing/README.rst
@@ -15,7 +15,7 @@ See `reproducing OSS-Fuzz bugs locally`_ for official instructions from OSS-Fuzz on reproducing using Docker. -Unofficially, to build the fuzzers locally with Clang and libFuzzer: +Unofficially, to build the fuzzers locally with Clang and libFuzzer:: mkdir build && cd build cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ @@ -31,7 +31,7 @@ ``-fsanitizer=fuzzer-no-link,memory`` or ``-fsanitizer=fuzzer-no-link,undefined``, respectively. -.. _`reproducing OSS-Fuzz bugs locally`: https://google.github.io/oss-fuzz/getting-started/reproducing-bugs-locally/ +.. _`reproducing OSS-Fuzz bugs locally`: https://google.github.io/oss-fuzz/advanced-topics/reproducing/ Seed Corpora ------------
diff --git a/Tests/Fuzzing/cmArchiveExtractFuzzer.cxx b/Tests/Fuzzing/cmArchiveExtractFuzzer.cxx index a4f66b6..4fb2b73 100644 --- a/Tests/Fuzzing/cmArchiveExtractFuzzer.cxx +++ b/Tests/Fuzzing/cmArchiveExtractFuzzer.cxx
@@ -87,7 +87,8 @@ // Extract without verbose, with timestamps bool result1 = cmSystemTools::ExtractTar( - archiveFile, files, cmSystemTools::cmTarExtractTimestamps::Yes, false); + archiveFile, files, cmSystemTools::cmTarExtractTimestamps::Yes, "UTF-8", + false); (void)result1; // Restore directory BEFORE removing (can't remove cwd) @@ -102,7 +103,8 @@ // Extract with verbose, without timestamps files.clear(); bool result2 = cmSystemTools::ExtractTar( - archiveFile, files, cmSystemTools::cmTarExtractTimestamps::No, true); + archiveFile, files, cmSystemTools::cmTarExtractTimestamps::No, "UTF-8", + true); (void)result2; // Restore directory
diff --git a/Tests/Fuzzing/cmListFileParserFuzzer.cxx b/Tests/Fuzzing/cmListFileParserFuzzer.cxx index 4908713..6632d25 100644 --- a/Tests/Fuzzing/cmListFileParserFuzzer.cxx +++ b/Tests/Fuzzing/cmListFileParserFuzzer.cxx
@@ -20,7 +20,6 @@ #include <unistd.h> #include "cmListFileCache.h" -#include "cmMessenger.h" #include "cmSystemTools.h" static constexpr size_t kMaxInputSize = 256 * 1024; @@ -59,13 +58,10 @@ fclose(fp); } - // Create a messenger for error handling - cmMessenger messenger; - // Parse the file cmListFile listFile; cmListFileBacktrace backtrace; - if (listFile.ParseFile(testFile.c_str(), &messenger, backtrace)) { + if (listFile.ParseFile(testFile.c_str(), nullptr, backtrace)) { // Successfully parsed - examine results for (auto const& func : listFile.Functions) { (void)func.LowerCaseName();
diff --git a/Tests/Fuzzing/corpus/archive/cab-huffman-uninit.tar b/Tests/Fuzzing/corpus/archive/cab-huffman-uninit.tar new file mode 100644 index 0000000..2bcd3fd --- /dev/null +++ b/Tests/Fuzzing/corpus/archive/cab-huffman-uninit.tar Binary files differ
diff --git a/Tests/Fuzzing/corpus/archive/no-pathname.tar b/Tests/Fuzzing/corpus/archive/no-pathname.tar new file mode 100644 index 0000000..d02a2c6 --- /dev/null +++ b/Tests/Fuzzing/corpus/archive/no-pathname.tar Binary files differ
diff --git a/Tests/Fuzzing/corpus/elf/oom.elf b/Tests/Fuzzing/corpus/elf/oom.elf new file mode 100755 index 0000000..6d4de24 --- /dev/null +++ b/Tests/Fuzzing/corpus/elf/oom.elf Binary files differ
diff --git a/Tests/GoogleTest/Test/CMakeLists.txt b/Tests/GoogleTest/Test/CMakeLists.txt index 0735496..024e997 100644 --- a/Tests/GoogleTest/Test/CMakeLists.txt +++ b/Tests/GoogleTest/Test/CMakeLists.txt
@@ -114,3 +114,12 @@ if(NOT TEST GoogleTest.NoKeywords) message(FATAL_ERROR "Test case GoogleTest.NoKeywords not defined") endif() + +# Check if PROPERTIES values are allowed to be lists +add_executable(test_gtest5 main5.cxx) +target_link_libraries(test_gtest5 GTest::Main) +set(environment VALX=1 VALY=2) +gtest_discover_tests(test_gtest5 + PROPERTIES + ENVIRONMENT "${environment}" +)
diff --git a/Tests/GoogleTest/Test/main1.cxx b/Tests/GoogleTest/Test/main1.cxx index c353d91..3e50398 100644 --- a/Tests/GoogleTest/Test/main1.cxx +++ b/Tests/GoogleTest/Test/main1.cxx
@@ -20,11 +20,10 @@ { ::testing::InitGoogleTest(&argc, argv); - if (argc > 1) { - if (argv[1] != std::string("--forceFail")) { - throw "Unexpected argument"; + for (int i = 0; i < argc; ++i) { + if (argv[i] == std::string("--forceFail")) { + shouldFail = true; } - shouldFail = true; } return RUN_ALL_TESTS(); }
diff --git a/Tests/GoogleTest/Test/main5.cxx b/Tests/GoogleTest/Test/main5.cxx new file mode 100644 index 0000000..28fa374 --- /dev/null +++ b/Tests/GoogleTest/Test/main5.cxx
@@ -0,0 +1,9 @@ +#include <cstdlib> + +#include <gtest/gtest.h> + +TEST(GoogleTest, Add) +{ + EXPECT_STREQ(std::getenv("VALX"), "1"); + EXPECT_STREQ(std::getenv("VALY"), "2"); +}
diff --git a/Tests/MSVCRuntimeChecks/override-common.cmake b/Tests/MSVCRuntimeChecks/override-common.cmake index 0241702..01d1322 100644 --- a/Tests/MSVCRuntimeChecks/override-common.cmake +++ b/Tests/MSVCRuntimeChecks/override-common.cmake
@@ -4,6 +4,12 @@ set(empty_StackFrameErrorCheck 1) set(empty_UninitializedVariable 1) set(empty_RTCsu 1) +elseif("${CMAKE_${lang}_COMPILER_ID};${CMAKE_${lang}_SIMULATE_ID};${lang}" STREQUAL "LLVMFlang;MSVC;Fortran") + # LLVMFlang does not actually support these, so Windows-LLVMFlang-Fortran passes no flags. + set(empty_PossibleDataLoss 1) + set(empty_StackFrameErrorCheck 1) + set(empty_UninitializedVariable 1) + set(empty_RTCsu 1) elseif("${CMAKE_${lang}_COMPILER_ID};${CMAKE_${lang}_SIMULATE_ID};${lang}" MATCHES "^Intel(LLVM)?;MSVC;Fortran$") # IntelLLVM Fortran does not actually support these, so Windows-IntelLLVM-Fortran passes no flags. set(empty_PossibleDataLoss 1)
diff --git a/Tests/MSVCRuntimeChecks/verify.h b/Tests/MSVCRuntimeChecks/verify.h index aae595b..3d26926 100644 --- a/Tests/MSVCRuntimeChecks/verify.h +++ b/Tests/MSVCRuntimeChecks/verify.h
@@ -1,6 +1,7 @@ /* Some compilers ignore the -RTC flags even if specified. */ #if (defined(_MSC_VER) && _MSC_VER <= 1310) || defined(__clang__) || \ - defined(__INTEL_LLVM_COMPILER) || defined(__INTEL_COMPILER) + defined(__flang__) || defined(__INTEL_LLVM_COMPILER) || \ + defined(__INTEL_COMPILER) # define NO__MSVC_RUNTIME_CHECKS #endif
diff --git a/Tests/Module/ExternalData/CMakeLists.txt b/Tests/Module/ExternalData/CMakeLists.txt index c09c693..bda1149 100644 --- a/Tests/Module/ExternalData/CMakeLists.txt +++ b/Tests/Module/ExternalData/CMakeLists.txt
@@ -57,3 +57,4 @@ add_subdirectory(Data4) add_subdirectory(Data5) add_subdirectory(DataNoSymlinks) +add_subdirectory(DataStateRoot)
diff --git a/Tests/Module/ExternalData/DataStateRoot/CMakeLists.txt b/Tests/Module/ExternalData/DataStateRoot/CMakeLists.txt new file mode 100644 index 0000000..eb8595f --- /dev/null +++ b/Tests/Module/ExternalData/DataStateRoot/CMakeLists.txt
@@ -0,0 +1,10 @@ +set(ExternalData_STATE_ROOT "${CMAKE_CURRENT_BINARY_DIR}/State") +ExternalData_Add_Test(DataStateRoot + NAME DataStateRootCheck + COMMAND ${CMAKE_COMMAND} + -D Data=DATA{Data.dat} + -D AdjacentHash=${ExternalData_BINARY_ROOT}/DataStateRoot/Data.dat-hash-stamp + -D StateHash=${ExternalData_STATE_ROOT}/DataStateRoot/Data.dat-hash-stamp + -P ${CMAKE_CURRENT_SOURCE_DIR}/DataStateRootCheck.cmake + ) +ExternalData_Add_Target(DataStateRoot)
diff --git a/Tests/Module/ExternalData/DataStateRoot/Data.dat.md5 b/Tests/Module/ExternalData/DataStateRoot/Data.dat.md5 new file mode 100644 index 0000000..70e39bd --- /dev/null +++ b/Tests/Module/ExternalData/DataStateRoot/Data.dat.md5
@@ -0,0 +1 @@ +8c018830e3efa5caf3c7415028335a57
diff --git a/Tests/Module/ExternalData/DataStateRoot/DataStateRootCheck.cmake b/Tests/Module/ExternalData/DataStateRoot/DataStateRootCheck.cmake new file mode 100644 index 0000000..869c877 --- /dev/null +++ b/Tests/Module/ExternalData/DataStateRoot/DataStateRootCheck.cmake
@@ -0,0 +1,9 @@ +if(NOT EXISTS "${Data}") + message(SEND_ERROR "Input file:\n ${Data}\n" "does not exist!") +endif() +if(EXISTS "${AdjacentHash}") + message(SEND_ERROR "Adjacent hash record exists but should not:\n ${AdjacentHash}") +endif() +if(NOT EXISTS "${StateHash}") + message(SEND_ERROR "State-root hash record is missing:\n ${StateHash}") +endif()
diff --git a/Tests/PellesASM/CMakeLists.txt b/Tests/PellesASM/CMakeLists.txt new file mode 100644 index 0000000..df283bc --- /dev/null +++ b/Tests/PellesASM/CMakeLists.txt
@@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 4.3) +project(PellesASM LANGUAGES ASM_POASM) + +string(TOLOWER "$ENV{LIB}" env_lib) +if(env_lib MATCHES [[[\/]+lib[\/]+win64[\/]*(;|$)]]) + set(arch "AMD64") +elseif(env_lib MATCHES [[[\/]+lib[\/]+win[\/]*(;|$)]]) + set(arch "IA32") +else() + message(FATAL_ERROR "Cannot determine target architecture.") +endif() + +add_executable(PellesASM main-${arch}.asm)
diff --git a/Tests/PellesASM/main-AMD64.asm b/Tests/PellesASM/main-AMD64.asm new file mode 100644 index 0000000..0957a6a --- /dev/null +++ b/Tests/PellesASM/main-AMD64.asm
@@ -0,0 +1,19 @@ +extern $printf:proc + +.rdata +fmt db "hello assembler world, %d arguments given", 0ah, 0 +.code +main proc + sub rsp, 40 + mov edx, ecx + lea rcx, [rip + fmt] + call $printf + mov eax, 0 + add rsp, 40 + ret +main endp + +.drectve SEGMENT + db ' -defaultlib:crt64' + db ' -defaultlib:kernel32' +end
diff --git a/Tests/PellesASM/main-IA32.asm b/Tests/PellesASM/main-IA32.asm new file mode 100644 index 0000000..535728a --- /dev/null +++ b/Tests/PellesASM/main-IA32.asm
@@ -0,0 +1,23 @@ +extern $printf:proc + +.rdata +fmt db "hello assembler world, %d arguments given", 0ah, 0 +.code +main proc + push ebp + mov ebp, esp + push dword ptr [ebp + 8] + lea eax, [fmt] + push eax + call $printf + add esp, 8 + mov eax, 0 + mov esp, ebp + pop ebp + ret +main endp + +.drectve SEGMENT + db ' -defaultlib:crt' + db ' -defaultlib:kernel32' +end
diff --git a/Tests/RunCMake/Autogen_1/NoQt-stderr.txt b/Tests/RunCMake/Autogen_1/NoQt-stderr.txt index 84319d5..8683f7e 100644 --- a/Tests/RunCMake/Autogen_1/NoQt-stderr.txt +++ b/Tests/RunCMake/Autogen_1/NoQt-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) in CMakeLists\.txt: +^CMake Warning \(author\) in CMakeLists\.txt: AUTOGEN: No valid Qt version found for target main\. AUTOMOC, AUTOUIC and AUTORCC disabled\. Consider adding: find_package\(Qt<QTVERSION> COMPONENTS Widgets\) to your CMakeLists\.txt file\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/Autogen_1/QtInFunctionNested-stderr.txt b/Tests/RunCMake/Autogen_1/QtInFunctionNested-stderr.txt index 84319d5..8683f7e 100644 --- a/Tests/RunCMake/Autogen_1/QtInFunctionNested-stderr.txt +++ b/Tests/RunCMake/Autogen_1/QtInFunctionNested-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) in CMakeLists\.txt: +^CMake Warning \(author\) in CMakeLists\.txt: AUTOGEN: No valid Qt version found for target main\. AUTOMOC, AUTOUIC and AUTORCC disabled\. Consider adding: find_package\(Qt<QTVERSION> COMPONENTS Widgets\) to your CMakeLists\.txt file\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/Autogen_2/QtAutoMocDeps-stderr.txt b/Tests/RunCMake/Autogen_2/QtAutoMocDeps-stderr.txt index 6024984..20029fb 100644 --- a/Tests/RunCMake/Autogen_2/QtAutoMocDeps-stderr.txt +++ b/Tests/RunCMake/Autogen_2/QtAutoMocDeps-stderr.txt
@@ -1,8 +1,10 @@ -^CMake Deprecation Warning at QtSubDir1/CMakeLists\.txt:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at QtSubDir1/CMakeLists\.txt:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0116 will be removed from a future version of CMake\. The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/Autogen_2/QtAutoMocVsTargets.cmake b/Tests/RunCMake/Autogen_2/QtAutoMocVsTargets.cmake new file mode 100644 index 0000000..c418b93 --- /dev/null +++ b/Tests/RunCMake/Autogen_2/QtAutoMocVsTargets.cmake
@@ -0,0 +1,21 @@ +enable_language(CXX) + +find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core) + +set(CMAKE_AUTOMOC ON) + +add_library(simple_lib STATIC simple_lib.cpp) + +# A plain AUTOMOC target gets the autogen custom command attached to itself. +add_library(plain_lib STATIC app_qt.cpp) +target_link_libraries(plain_lib PRIVATE simple_lib Qt${with_qt_version}::Core) + +# A target that depends on a GENERATED file keeps the separate autogen targets. +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated_dep.h + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/generated_dep.h) +add_library(gen_dep_lib STATIC app_qt.cpp + ${CMAKE_CURRENT_BINARY_DIR}/generated_dep.h) +target_link_libraries(gen_dep_lib PRIVATE Qt${with_qt_version}::Core) +target_include_directories(gen_dep_lib PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +set_property(TARGET gen_dep_lib PROPERTY + AUTOGEN_TARGET_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/autogen_dep.txt)
diff --git a/Tests/RunCMake/Autogen_2/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_2/RunCMakeTest.cmake index e97e896..9286d96 100644 --- a/Tests/RunCMake/Autogen_2/RunCMakeTest.cmake +++ b/Tests/RunCMake/Autogen_2/RunCMakeTest.cmake
@@ -84,4 +84,68 @@ endif() endblock() endif() + + if(RunCMake_GENERATOR MATCHES "Visual Studio" + AND QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0) + block() + macro(check_project_exists target expected) + if(EXISTS "${RunCMake_TEST_BINARY_DIR}/${target}.vcxproj") + set(actual TRUE) + else() + set(actual FALSE) + endif() + if("${actual}" STREQUAL "${expected}") + set(check_result "PASSED") + set(message_type "STATUS") + else() + set(check_result "FAILED") + set(message_type "FATAL_ERROR") + endif() + message(${message_type} + "QtAutoMocVsTargets-\"${target}.vcxproj\" exists is ${actual}, expected ${expected} - ${check_result}") + endmacro() + + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocVsTargets-build) + run_cmake_with_options(QtAutoMocVsTargets ${RunCMake_TEST_OPTIONS}) + set(RunCMake_TEST_NO_CLEAN 1) + + # A plain AUTOMOC target does not get extra projects. + check_project_exists(plain_lib TRUE) + check_project_exists(plain_lib_autogen FALSE) + check_project_exists(plain_lib_autogen_timestamp_deps FALSE) + # A target with a GENERATED dependency keeps them. + check_project_exists(gen_dep_lib_autogen TRUE) + check_project_exists(gen_dep_lib_autogen_timestamp_deps TRUE) + + # The first build must run AUTOMOC. Assert it, so that the checks below + # cannot pass just because the message is missing from the build output. + set(RunCMake_TEST_EXPECT_stdout "Automatic MOC for target plain_lib") + run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Debug) + unset(RunCMake_TEST_EXPECT_stdout) + + set(RunCMake_TEST_NOT_EXPECT_stdout "Automatic MOC for target plain_lib|\ +Automatic MOC for target gen_dep_lib") + # Assert that the depfile prevents AUTOMOC from running again. + set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC again") + run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Debug) + # Touch the source file of a library that 'plain_lib' links. This must + # not cause a rerun of AUTOMOC. + file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for a dependency change") + run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Debug) + # Files in AUTOGEN_TARGET_DEPENDS are order-only dependencies. Touching + # one must not cause a rerun of AUTOMOC either. + file(TOUCH "${RunCMake_SOURCE_DIR}/autogen_dep.txt") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for an AUTOGEN_TARGET_DEPENDS file") + run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Debug) + unset(RunCMake_TEST_NOT_EXPECT_stdout) + + # Another configuration has its own timestamp file, so it builds again. + set(RunCMake_TEST_EXPECT_stdout "Automatic MOC for target plain_lib") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-Release") + run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Release) + unset(RunCMake_TEST_EXPECT_stdout) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + endblock() + endif() endif ()
diff --git a/Tests/RunCMake/Autogen_2/autogen_dep.txt b/Tests/RunCMake/Autogen_2/autogen_dep.txt new file mode 100644 index 0000000..7415b80 --- /dev/null +++ b/Tests/RunCMake/Autogen_2/autogen_dep.txt
@@ -0,0 +1 @@ +A file listed in AUTOGEN_TARGET_DEPENDS.
diff --git a/Tests/RunCMake/Autogen_6/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_6/RunCMakeTest.cmake index 65d58a9..d18c9ef 100644 --- a/Tests/RunCMake/Autogen_6/RunCMakeTest.cmake +++ b/Tests/RunCMake/Autogen_6/RunCMakeTest.cmake
@@ -12,6 +12,7 @@ macro(set_test_variables_for_unwanted_builds) if (RunCMake_GENERATOR MATCHES "Ninja") set(RunCMake_TEST_EXPECT_stdout "ninja: no work to do.") + set(RunCMake_TEST_EXPECT_stderr "((^|\n)ninja explain:[^\n]*)*$") elseif (RunCMake_GENERATOR MATCHES "Make") set(RunCMake_TEST_NOT_EXPECT_stdout "Building CXX object multi_ui_files/CMakeFiles/example.dir/src/main.cpp.o|\ Building CXX object multi_ui_files/CMakeFiles/example.dir/src/widget.cpp.o") @@ -29,6 +30,9 @@ if (NOT RunCMake_GENERATOR MATCHES "Visual Studio") set(test_verbose_arg "--verbose") endif() + if(RunCMake_GENERATOR MATCHES "Ninja") + set(ninja_explain_arg -- -d explain) + endif() if (RunCMake_GENERATOR_IS_MULTI_CONFIG) set(config_desc "-${test_config}") set(RunCMake_TEST_VARIANT_DESCRIPTION "${config_desc}") @@ -46,7 +50,7 @@ set(RunCMake_TEST_VARIANT_DESCRIPTION "${config_desc}-No files were built on the third build") set_test_variables_for_unwanted_builds() - run_cmake_command("${test_name}-build" ${CMAKE_COMMAND} --build . ${test_verbose_arg} ${multiconfig_config_arg}) + run_cmake_command("${test_name}-build" ${CMAKE_COMMAND} --build . ${test_verbose_arg} ${multiconfig_config_arg} ${ninja_explain_arg}) endfunction() if(RunCMake_GENERATOR MATCHES "Make|Ninja|Visual Studio|Xcode")
diff --git a/Tests/RunCMake/BundleUtilities/CMP0080-COMMAND-OLD-stderr.txt b/Tests/RunCMake/BundleUtilities/CMP0080-COMMAND-OLD-stderr.txt index f4abdf0..8bd6201 100644 --- a/Tests/RunCMake/BundleUtilities/CMP0080-COMMAND-OLD-stderr.txt +++ b/Tests/RunCMake/BundleUtilities/CMP0080-COMMAND-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at [^ +^CMake Warning \(deprecated\) at [^ ]*/Tests/RunCMake/BundleUtilities/CMP0080-COMMAND\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0080 will be removed from a future version of CMake\. @@ -6,4 +6,6 @@ The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/BundleUtilities/CMP0080-OLD-stderr.txt b/Tests/RunCMake/BundleUtilities/CMP0080-OLD-stderr.txt index 7b2acba..11df59d 100644 --- a/Tests/RunCMake/BundleUtilities/CMP0080-OLD-stderr.txt +++ b/Tests/RunCMake/BundleUtilities/CMP0080-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0080-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0080-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0080 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/BundleUtilities/CMP0080-WARN-stderr.txt b/Tests/RunCMake/BundleUtilities/CMP0080-WARN-stderr.txt index c3d541e..dd4e74c 100644 --- a/Tests/RunCMake/BundleUtilities/CMP0080-WARN-stderr.txt +++ b/Tests/RunCMake/BundleUtilities/CMP0080-WARN-stderr.txt
@@ -1,10 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/BundleUtilities\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at .*/Modules/BundleUtilities\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0080 is not set: BundleUtilities cannot be included at configure time\. Run "cmake --help-policy CMP0080" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): - .*/Modules/BundleUtilities\.cmake:[0-9]+ \(_warn_cmp0080\) CMP0080-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt b/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt index 7b04900..5853068 100644 --- a/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt +++ b/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0026-IMPORTED\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0026-IMPORTED\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0111 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0068/CMP0068-OLD-stderr.txt b/Tests/RunCMake/CMP0068/CMP0068-OLD-stderr.txt index 27a6776..3e13559 100644 --- a/Tests/RunCMake/CMP0068/CMP0068-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0068/CMP0068-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0068-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0068-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0068 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0068/CMP0068-WARN-stderr.txt b/Tests/RunCMake/CMP0068/CMP0068-WARN-stderr.txt index 798e0be..00b76be 100644 --- a/Tests/RunCMake/CMP0068/CMP0068-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0068/CMP0068-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\): +CMake Warning \(policy\): Policy CMP0068 is not set: RPATH settings on macOS do not affect install_name\. Run "cmake --help-policy CMP0068" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -9,4 +9,5 @@ foo3 foo4 -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0069/CMP0069-OLD-stderr.txt b/Tests/RunCMake/CMP0069/CMP0069-OLD-stderr.txt index 55c59c3..74d9928 100644 --- a/Tests/RunCMake/CMP0069/CMP0069-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0069/CMP0069-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0069-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0069-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0069 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0069/CMP0069-WARN-stderr.txt b/Tests/RunCMake/CMP0069/CMP0069-WARN-stderr.txt index a63f4cd..549d0cf 100644 --- a/Tests/RunCMake/CMP0069/CMP0069-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0069/CMP0069-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at CMP0069-WARN\.cmake:[0-9]+ \(add_executable\): +^CMake Warning \(policy\) at CMP0069-WARN\.cmake:[0-9]+ \(add_executable\): Policy CMP0069 is not set: INTERPROCEDURAL_OPTIMIZATION is enforced when enabled\. Run "cmake --help-policy CMP0069" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -6,4 +6,5 @@ INTERPROCEDURAL_OPTIMIZATION property will be ignored for target 'foo'\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CMP0081/CMP0081-OLD-stderr.txt b/Tests/RunCMake/CMP0081/CMP0081-OLD-stderr.txt index 2709246..073bd1e 100644 --- a/Tests/RunCMake/CMP0081/CMP0081-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0081/CMP0081-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0081-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0081-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0081 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt b/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt index 2400621..2d5b65f 100644 --- a/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0081 is not set: Relative paths not allowed in LINK_DIRECTORIES target property\. Run "cmake --help-policy CMP0081" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -7,4 +7,5 @@ "\.\./lib" -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt b/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt index 5d842ad..8434e25 100644 --- a/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0102-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0102-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0102 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0102/CMP0102-WARN-stderr.txt b/Tests/RunCMake/CMP0102/CMP0102-WARN-stderr.txt index 32dd423..f7ac3ad 100644 --- a/Tests/RunCMake/CMP0102/CMP0102-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0102/CMP0102-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0102-Common\.cmake:2 \(mark_as_advanced\): +CMake Warning \(policy\) at CMP0102-Common\.cmake:2 \(mark_as_advanced\): Policy CMP0102 is not set: The variable named "CMP0102_TEST_VARIABLE" is not in the cache\. This results in an empty cache entry which is no longer created when policy CMP0102 is set to NEW\. Run "cmake --help-policy @@ -7,4 +7,5 @@ Call Stack \(most recent call first\): CMP0102-WARN\.cmake:4 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0104/CMP0104-OLD-stderr.txt b/Tests/RunCMake/CMP0104/CMP0104-OLD-stderr.txt index d9f58b2..eff2817 100644 --- a/Tests/RunCMake/CMP0104/CMP0104-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0104/CMP0104-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0104-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0104-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0104 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0104/CMP0104-WARN-stderr.txt b/Tests/RunCMake/CMP0104/CMP0104-WARN-stderr.txt index 24d451d..20928e5 100644 --- a/Tests/RunCMake/CMP0104/CMP0104-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0104/CMP0104-WARN-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0104 is not set: CMAKE_CUDA_ARCHITECTURES now detected for NVCC, empty CUDA_ARCHITECTURES not allowed\. Run "cmake --help-policy CMP0104" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. CUDA_ARCHITECTURES is empty for target "cuda"\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0106/CMP0106-OLD-stderr.txt b/Tests/RunCMake/CMP0106/CMP0106-OLD-stderr.txt index f3b7f3c..3e0bcd8 100644 --- a/Tests/RunCMake/CMP0106/CMP0106-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0106/CMP0106-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0106-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0106-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0106 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0106/CMP0106-WARN-ParaView-stderr.txt b/Tests/RunCMake/CMP0106/CMP0106-WARN-ParaView-stderr.txt index 7b3f34c..b2a19cc 100644 --- a/Tests/RunCMake/CMP0106/CMP0106-WARN-ParaView-stderr.txt +++ b/Tests/RunCMake/CMP0106/CMP0106-WARN-ParaView-stderr.txt
@@ -1,8 +1,8 @@ -CMake Warning \(dev\) at CMP0106-Common\.cmake:1 \(include\): +CMake Warning \(policy\) at CMP0106-Common\.cmake:1 \(include\): Policy CMP0106 is not set: The Documentation module is removed\. Run "cmake --help-policy CMP0106" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): subdir/CMakeLists\.txt:2 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0106/CMP0106-WARN-VTK-stderr.txt b/Tests/RunCMake/CMP0106/CMP0106-WARN-VTK-stderr.txt index 785cf54..c153071 100644 --- a/Tests/RunCMake/CMP0106/CMP0106-WARN-VTK-stderr.txt +++ b/Tests/RunCMake/CMP0106/CMP0106-WARN-VTK-stderr.txt
@@ -1,9 +1,9 @@ -CMake Warning \(dev\) at CMP0106-Common\.cmake:1 \(include\): +CMake Warning \(policy\) at CMP0106-Common\.cmake:1 \(include\): Policy CMP0106 is not set: The Documentation module is removed\. Run "cmake --help-policy CMP0106" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMP0106-WARN-VTK\.cmake:2 \(include\) CMakeLists\.txt:7 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0106/CMP0106-WARN-stderr.txt b/Tests/RunCMake/CMP0106/CMP0106-WARN-stderr.txt index 98c7eef..e8dd53d 100644 --- a/Tests/RunCMake/CMP0106/CMP0106-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0106/CMP0106-WARN-stderr.txt
@@ -1,14 +1,14 @@ -CMake Warning \(dev\) at CMP0106-Common\.cmake:1 \(include\): +CMake Warning \(policy\) at CMP0106-Common\.cmake:1 \(include\): Policy CMP0106 is not set: The Documentation module is removed\. Run "cmake --help-policy CMP0106" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMP0106-WARN\.cmake:2 \(include\) CMakeLists\.txt:7 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at .*/Modules/Documentation\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at .*/Modules/Documentation\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0106 is not set: The Documentation module is removed\. Run "cmake --help-policy CMP0106" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -20,9 +20,10 @@ CMP0106-Common\.cmake:1 \(include\) CMP0106-WARN\.cmake:2 \(include\) CMakeLists\.txt:7 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at .*/Modules/Documentation\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at .*/Modules/Documentation\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0106 is not set: The Documentation module is removed\. Run "cmake --help-policy CMP0106" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -34,4 +35,5 @@ CMP0106-Common\.cmake:10 \(include\) CMP0106-WARN\.cmake:2 \(include\) CMakeLists\.txt:7 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt b/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt index 80b1924..91c955a 100644 --- a/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0111-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0111-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0111 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt b/Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt index 9f863d1..f969f26 100644 --- a/Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^(CMake Warning \(dev\) in CMakeLists\.txt: +^(CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0111 is not set: An imported target missing its location property fails during generation\. Run "cmake --help-policy CMP0111" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -6,8 +6,9 @@ IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static)_lib"( configuration "[^"]+")?\. -This warning is for project developers\. Use -Wno-dev to suppress it\. -+)+CMake Warning \(dev\) in CMakeLists\.txt: +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. ++)+CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0111 is not set: An imported target missing its location property fails during generation\. Run "cmake --help-policy CMP0111" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -16,4 +17,5 @@ IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static)_lib"( configuration "[^"]+")?\. .*(IMPORTED_LOCATION or )?IMPORTED_IMPLIB not set for imported target.*"shared_lib".* -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt b/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt index 3472f33..6db078c 100644 --- a/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): The OLD behavior for policy CMP0115 will be removed from a future version of CMake\. @@ -6,6 +6,8 @@ policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at CMP0115\.cmake:[0-9]+ \(add_executable\): Cannot find source file:
diff --git a/Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt b/Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt index e79ca97..0b0fa6d 100644 --- a/Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at CMP0115\.cmake:[0-9]+ \(add_executable\): +^CMake Warning \(policy\) at CMP0115\.cmake:[0-9]+ \(add_executable\): Policy CMP0115 is not set: Source file extensions must be explicit\. Run "cmake --help-policy CMP0115" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -10,7 +10,8 @@ Call Stack \(most recent call first\): CMP0115-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. CMake Error at CMP0115\.cmake:[0-9]+ \(add_executable\): Cannot find source file:
diff --git a/Tests/RunCMake/CMP0116/CMP0116-Mixed-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-Mixed-stderr.txt index 930dd3c..c010395 100644 --- a/Tests/RunCMake/CMP0116/CMP0116-Mixed-stderr.txt +++ b/Tests/RunCMake/CMP0116/CMP0116-Mixed-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0116-Mixed\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0116-Mixed\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0116 will be removed from a future version of CMake\. @@ -8,21 +8,25 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at CMP0116-Mixed\.cmake:1 \(add_custom_command\): +CMake Warning \(policy\) at CMP0116-Mixed\.cmake:1 \(add_custom_command\): Policy CMP0116 is not set: Ninja generators transform DEPFILEs from add_custom_command\(\)\. Run "cmake --help-policy CMP0116" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.( +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.( -CMake Warning \(dev\) at CMP0116-Mixed\.cmake:1 \(add_custom_command\): +CMake Warning \(policy\) at CMP0116-Mixed\.cmake:1 \(add_custom_command\): Policy CMP0116 is not set: Ninja generators transform DEPFILEs from add_custom_command\(\)\. Run "cmake --help-policy CMP0116" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.)*$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.)*$
diff --git a/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN-stderr.txt index 887601c..762396d 100644 --- a/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN-stderr.txt +++ b/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN-stderr.txt
@@ -1,8 +1,10 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): The OLD behavior for policy CMP0116 will be removed from a future version of CMake\. The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN-stderr.txt index 887601c..762396d 100644 --- a/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN-stderr.txt
@@ -1,8 +1,10 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): The OLD behavior for policy CMP0116 will be removed from a future version of CMake\. The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN-stderr.txt index 843ff1c..d0c263b 100644 --- a/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN-stderr.txt +++ b/Tests/RunCMake/CMP0116/CMP0116-WARN-NOWARN-stderr.txt
@@ -1,7 +1,8 @@ -^(CMake Warning \(dev\) at Subdirectory/CMakeLists\.txt:[0-9]+ \(add_custom_command\): +^(CMake Warning \(policy\) at Subdirectory/CMakeLists\.txt:[0-9]+ \(add_custom_command\): Policy CMP0116 is not set: Ninja generators transform DEPFILEs from add_custom_command\(\)\. Run "cmake --help-policy CMP0116" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. *)+$
diff --git a/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt index e29af91..2620d40 100644 --- a/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0116/CMP0116-WARN-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^(CMake Warning \(dev\) at Common\.cmake:[0-9]+ \(add_custom_command\): +^(CMake Warning \(policy\) at Common\.cmake:[0-9]+ \(add_custom_command\): Policy CMP0116 is not set: Ninja generators transform DEPFILEs from add_custom_command\(\)\. Run "cmake --help-policy CMP0116" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -6,11 +6,13 @@ Call Stack \(most recent call first\): CMP0116-WARN-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. -+)+(CMake Warning \(dev\) at Subdirectory/CMakeLists\.txt:[0-9]+ \(add_custom_command\): +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. ++)+(CMake Warning \(policy\) at Subdirectory/CMakeLists\.txt:[0-9]+ \(add_custom_command\): Policy CMP0116 is not set: Ninja generators transform DEPFILEs from add_custom_command\(\)\. Run "cmake --help-policy CMP0116" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. *)+$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test11-stderr.txt index 05b2353..4858363 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test11-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test13-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test13-stderr.txt index 7802538..f6028c0 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test13-stderr.txt
@@ -7,18 +7,18 @@ CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\. + -(CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): +(CMake Warning \(author\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0118-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0118-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test13\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test15-stderr.txt index 3166f45..7382ecd 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test15-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt index e721876..dec36e6 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt index f358297..519d437 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt index 686ba23..37dd2a7 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt
@@ -4,13 +4,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -18,7 +18,7 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -29,7 +29,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -43,13 +43,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -57,7 +57,7 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -68,7 +68,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -96,7 +96,7 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -107,7 +107,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt index 4de6e76..2d46399 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt index b7a170b..071b88e 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt index 9285f9d..b844f57 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test1\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test1\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + prop: `0` CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(target_sources\):
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt index 9bd3d33..534ea3b 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test10\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test10\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt index 4730caf..4debc24 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test11\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test11\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test12-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test12-stderr.txt index 69a07e1..da383bb 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test12-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test12-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test12\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test12\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\.
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test13-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test13-stderr.txt index 45c1dcb..c58a594 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test13-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test13\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test13\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\.
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt index 7ade0cf..41b741c 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test14\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test14\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt index 5735539..03794ce 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test15\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test15\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-stderr.txt index 2ca118d..fcaa962 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test16\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test16\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom4' was not created in this directory\.
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-stderr.txt index ad3ea43..6fb80b5 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test1b\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test1b\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + prop: `NOTFOUND` CMake Error at CMP0118-Common-Test1b\.cmake:[0-9]+ \(target_sources\):
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2-stderr.txt index 74eb3e1..ffde85d 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test2\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test2\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,5 +8,7 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b-stderr.txt index 41114ba..640736e 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test2b\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test2b\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,5 +8,7 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c-stderr.txt index ba36763..3d40524 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test2c\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test2c\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,5 +8,7 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d-stderr.txt index 0a9a8b7..0921ab7 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test2d\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test2d\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,5 +8,7 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt index 28fe2f0..fee5dd9 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test3\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test3\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `1` Generated_with_full_path1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt index 819b8c0..6f8eb48 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test3b\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test3b\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `1` Generated_with_full_path1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt index f17c9be..22e2484 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test4\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test4\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt index 388e90e..8864e9c 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test4b\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test4b\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt index e20ffee..18b5a79 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test5\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test5\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `1` Generated_with_full_path1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt index 0cad373..5dd94f3 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test6\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test6\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt index 7f232d5..025950a 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test7\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test7\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt index dd9d2ef..4170d35 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test8\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test8\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-stderr.txt index 285051c..61f88dd 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test8b\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test8b\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt index e01f782..1df9823 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test9\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test9\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-stderr.txt index 9b2fdad..be684af 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0118-OLD-Test9b\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0118-OLD-Test9b\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt index 9395cd7..a931f13 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test13-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test13-stderr.txt index 957cc69..a6a20a7 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test13-stderr.txt
@@ -7,28 +7,28 @@ CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\. + -((CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): +((CMake Warning \(policy\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0118-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0118-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test13\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt index eab051f..881e847 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt index 6340f91..78c8ff3 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt
@@ -1,15 +1,15 @@ -^CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -17,18 +17,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -36,18 +36,18 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -55,18 +55,18 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -74,18 +74,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -93,18 +93,18 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -112,18 +112,18 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -131,18 +131,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -150,18 +150,18 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt index a213ae5..b389fbd 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt
@@ -1,15 +1,15 @@ -^CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -17,18 +17,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -36,18 +36,18 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -55,18 +55,18 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -74,18 +74,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -93,18 +93,18 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -112,18 +112,18 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -131,18 +131,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -150,18 +150,18 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt index 2f7a390..d11408e 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt
@@ -4,18 +4,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -23,7 +23,7 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -38,11 +38,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -56,18 +56,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -75,7 +75,7 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -90,11 +90,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -108,18 +108,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -127,7 +127,7 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0118-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -142,11 +142,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt index 1feef65..5fe4690 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt index cbce4ab..7e013ea 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt b/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt index 78d0def..f506eca 100644 --- a/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at GenInSubdir-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at GenInSubdir-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0118 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at GenInSubdir/CMakeLists\.txt:[0-9]+ \(target_sources\): Cannot find source file:
diff --git a/Tests/RunCMake/CMP0119/CMP0119-OLD-stderr.txt b/Tests/RunCMake/CMP0119/CMP0119-OLD-stderr.txt index 86eac41..936dad2 100644 --- a/Tests/RunCMake/CMP0119/CMP0119-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0119/CMP0119-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0119-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0119-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0119 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0119/RunCMakeTest.cmake b/Tests/RunCMake/CMP0119/RunCMakeTest.cmake index 2576a28..da93a9c 100644 --- a/Tests/RunCMake/CMP0119/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0119/RunCMakeTest.cmake
@@ -12,6 +12,8 @@ run_CMP0119(WARN) run_CMP0119(OLD) endif() -if((CMAKE_C_COMPILER_ID MATCHES "(GNU|LCC|Clang|MSVC|Borland|Embarcadero|Intel|TI|OrangeC)")) +if((CMAKE_C_COMPILER_ID MATCHES "(GNU|LCC|Clang|MSVC|Borland|Embarcadero|Intel|TI)") + # FIXME(OrangeC#1137): OrangeC 7 no longer accepts custom file extensions with -x + OR (CMAKE_C_COMPILER_ID STREQUAL "OrangeC" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 7)) run_CMP0119(NEW) endif()
diff --git a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt index 5626609..4529c8b 100644 --- a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt
@@ -1,12 +1,14 @@ -CMake Warning \(dev\) at CMP0121-ERANGE-Common\.cmake:3 \(list\): +CMake Warning \(policy\) at CMP0121-ERANGE-Common\.cmake:3 \(list\): Policy CMP0121 is not set: The list\(\) command now validates parsing of index arguments\. Run "cmake --help-policy CMP0121" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. + Invalid list index "18446744073709551616"\. Call Stack \(most recent call first\): CMP0121-ERANGE-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. .* CMake Error at CMP0121-ERANGE-Common\.cmake:3 \(list\): list index: (-2147483648|2147483647) out of range \(-5, 4\)
diff --git a/Tests/RunCMake/CMP0121/CMP0121-GET-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-GET-WARN-stderr.txt index 08c701b..06301b3 100644 --- a/Tests/RunCMake/CMP0121/CMP0121-GET-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0121/CMP0121-GET-WARN-stderr.txt
@@ -1,11 +1,13 @@ -CMake Warning \(dev\) at CMP0121-GET-Common\.cmake:3 \(list\): +CMake Warning \(policy\) at CMP0121-GET-Common\.cmake:3 \(list\): Policy CMP0121 is not set: The list\(\) command now validates parsing of index arguments\. Run "cmake --help-policy CMP0121" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. + Invalid list index "2junk"\. Call Stack \(most recent call first\): CMP0121-GET-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. GET: -->a;c<--
diff --git a/Tests/RunCMake/CMP0121/CMP0121-INSERT-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-INSERT-WARN-stderr.txt index 7adf791..8e0bfe4 100644 --- a/Tests/RunCMake/CMP0121/CMP0121-INSERT-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0121/CMP0121-INSERT-WARN-stderr.txt
@@ -1,11 +1,13 @@ -CMake Warning \(dev\) at CMP0121-INSERT-Common\.cmake:3 \(list\): +CMake Warning \(policy\) at CMP0121-INSERT-Common\.cmake:3 \(list\): Policy CMP0121 is not set: The list\(\) command now validates parsing of index arguments\. Run "cmake --help-policy CMP0121" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. + Invalid list index "junk2"\. Call Stack \(most recent call first\): CMP0121-INSERT-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. INSERT: -->new;a;b;c;d;e<--
diff --git a/Tests/RunCMake/CMP0121/CMP0121-REMOVE_AT-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-REMOVE_AT-WARN-stderr.txt index 38194e0..2bd9598 100644 --- a/Tests/RunCMake/CMP0121/CMP0121-REMOVE_AT-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0121/CMP0121-REMOVE_AT-WARN-stderr.txt
@@ -1,11 +1,13 @@ -CMake Warning \(dev\) at CMP0121-REMOVE_AT-Common\.cmake:3 \(list\): +CMake Warning \(policy\) at CMP0121-REMOVE_AT-Common\.cmake:3 \(list\): Policy CMP0121 is not set: The list\(\) command now validates parsing of index arguments\. Run "cmake --help-policy CMP0121" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. + Invalid list index "invalid"\. Call Stack \(most recent call first\): CMP0121-REMOVE_AT-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. REMOVE_AT: -->b;c;d;e<--
diff --git a/Tests/RunCMake/CMP0121/CMP0121-SUBLIST-length-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-SUBLIST-length-WARN-stderr.txt index 4d3c15e..b6d9e2b 100644 --- a/Tests/RunCMake/CMP0121/CMP0121-SUBLIST-length-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0121/CMP0121-SUBLIST-length-WARN-stderr.txt
@@ -1,11 +1,13 @@ -CMake Warning \(dev\) at CMP0121-SUBLIST-length-Common\.cmake:3 \(list\): +CMake Warning \(policy\) at CMP0121-SUBLIST-length-Common\.cmake:3 \(list\): Policy CMP0121 is not set: The list\(\) command now validates parsing of index arguments\. Run "cmake --help-policy CMP0121" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. + Invalid list index "invalid"\. Call Stack \(most recent call first\): CMP0121-SUBLIST-length-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. SUBLIST-length: --><--
diff --git a/Tests/RunCMake/CMP0121/CMP0121-SUBLIST-start-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-SUBLIST-start-WARN-stderr.txt index 7ed2aba..ba4ecb5 100644 --- a/Tests/RunCMake/CMP0121/CMP0121-SUBLIST-start-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0121/CMP0121-SUBLIST-start-WARN-stderr.txt
@@ -1,11 +1,13 @@ -CMake Warning \(dev\) at CMP0121-SUBLIST-start-Common\.cmake:3 \(list\): +CMake Warning \(policy\) at CMP0121-SUBLIST-start-Common\.cmake:3 \(list\): Policy CMP0121 is not set: The list\(\) command now validates parsing of index arguments\. Run "cmake --help-policy CMP0121" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. + Invalid list index "invalid"\. Call Stack \(most recent call first\): CMP0121-SUBLIST-start-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. SUBLIST-start: -->a;b<--
diff --git a/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt b/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt index 11f50e8..0875fc6 100644 --- a/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0126-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0126-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0126 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt b/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt index 0d88887..6caae02 100644 --- a/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt +++ b/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0126-OLD_CL\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0126-OLD_CL\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0126 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0126/CMP0126-WARN-stderr.txt b/Tests/RunCMake/CMP0126/CMP0126-WARN-stderr.txt index 23ec0c6..9d6f48f 100644 --- a/Tests/RunCMake/CMP0126/CMP0126-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0126/CMP0126-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0126-WARN\.cmake:[0-9]+ \(set\): +CMake Warning \(policy\) at CMP0126-WARN\.cmake:[0-9]+ \(set\): Policy CMP0126 is not set: set\(CACHE\) does not remove a normal variable of the same name\. Run "cmake --help-policy CMP0126" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -7,4 +7,5 @@ will be removed from the current scope\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0129/C-OLD-stderr.txt b/Tests/RunCMake/CMP0129/C-OLD-stderr.txt index 5c622d2..4c1eca2 100644 --- a/Tests/RunCMake/CMP0129/C-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0129/C-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at C-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at C-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0129 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt b/Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt index 71a1ecf..65e4c5c 100644 --- a/Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CXX-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CXX-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0129 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt b/Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt index 01260c0..e1bc43a 100644 --- a/Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Fortran-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Fortran-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0129 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0132/CMP0132-OLD-stderr.txt b/Tests/RunCMake/CMP0132/CMP0132-OLD-stderr.txt index 025665d..c2b64df 100644 --- a/Tests/RunCMake/CMP0132/CMP0132-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0132/CMP0132-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0132-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0132-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0132 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0135/CMP0135-OLD-stderr.txt b/Tests/RunCMake/CMP0135/CMP0135-OLD-stderr.txt index 59a7a58..8a3203f 100644 --- a/Tests/RunCMake/CMP0135/CMP0135-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0135/CMP0135-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0135-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0135-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0135 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt b/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt index 1632d63..4265261 100644 --- a/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is not set\. The policy's OLD behavior will be used\. When using a URL download, the timestamps of extracted files should preferably be that of @@ -9,7 +9,7 @@ DOWNLOAD_EXTRACT_TIMESTAMP option with a value of true to avoid this robustness issue\. .* -CMake Warning \(dev\) at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is not set\. The policy's OLD behavior will be used\. When using a URL download, the timestamps of extracted files should preferably be that of
diff --git a/Tests/RunCMake/CMP0139/CMP0139-OLD-stderr.txt b/Tests/RunCMake/CMP0139/CMP0139-OLD-stderr.txt index a99ab9a..c4c0cc7 100644 --- a/Tests/RunCMake/CMP0139/CMP0139-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0139/CMP0139-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0139-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0139-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0139 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at CMP0139-OLD\.cmake:[0-9]+ \(if\): if given arguments:
diff --git a/Tests/RunCMake/CMP0139/CMP0139-WARN-stderr.txt b/Tests/RunCMake/CMP0139/CMP0139-WARN-stderr.txt index 27f59b8..2512688 100644 --- a/Tests/RunCMake/CMP0139/CMP0139-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0139/CMP0139-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0139-WARN\.cmake:[0-9]+ \(if\): +CMake Warning \(policy\) at CMP0139-WARN\.cmake:[0-9]+ \(if\): Policy CMP0139 is not set: The if\(\) command supports path comparisons using PATH_EQUAL operator\. Run "cmake --help-policy CMP0139" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -7,7 +7,8 @@ NEW\. Since the policy is not set the OLD behavior will be used\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. CMake Error at CMP0139-WARN\.cmake:[0-9]+ \(if\): if given arguments:
diff --git a/Tests/RunCMake/CMP0150/CMP0150-OLD-stderr.txt b/Tests/RunCMake/CMP0150/CMP0150-OLD-stderr.txt index 41da154..57b439a 100644 --- a/Tests/RunCMake/CMP0150/CMP0150-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0150/CMP0150-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at ([^ +^CMake Warning \(deprecated\) at ([^ ]*/Tests/RunCMake/CMP0150/)?OLD-build/Top/CMakeLists\.txt:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0150 will be removed from a future version of CMake\. @@ -6,4 +6,6 @@ The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0150/CMP0150-WARN-stderr.txt b/Tests/RunCMake/CMP0150/CMP0150-WARN-stderr.txt index 74c932a..c18d628 100644 --- a/Tests/RunCMake/CMP0150/CMP0150-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0150/CMP0150-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0150 is not set: ExternalProject_Add and FetchContent_Declare commands treat relative GIT_REPOSITORY paths as being relative to the parent project's remote\. Run "cmake --help-policy CMP0150" for policy @@ -11,7 +11,7 @@ Call Stack \(most recent call first\): .*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_resolve_git_remote\) .* -CMake Warning \(dev\) at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0150 is not set: ExternalProject_Add and FetchContent_Declare commands treat relative GIT_REPOSITORY paths as being relative to the parent project's remote\. Run "cmake --help-policy CMP0150" for policy
diff --git a/Tests/RunCMake/CMP0152/CMP0152-OLD-stderr.txt b/Tests/RunCMake/CMP0152/CMP0152-OLD-stderr.txt index e69de29..71f5dad 100644 --- a/Tests/RunCMake/CMP0152/CMP0152-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0152/CMP0152-OLD-stderr.txt
@@ -0,0 +1,12 @@ +^CMake Warning \(deprecated\) at CMP0152-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0152 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0152/CMP0152-WARN-stderr.txt b/Tests/RunCMake/CMP0152/CMP0152-WARN-stderr.txt index cb65b18..92d9fef 100644 --- a/Tests/RunCMake/CMP0152/CMP0152-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0152/CMP0152-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at CMP0152-Common\.cmake:[0-9]+ \(file\): +^CMake Warning \(policy\) at CMP0152-Common\.cmake:[0-9]+ \(file\): Policy CMP0152 is not set: file\(REAL_PATH\) resolves symlinks before collapsing \.\./ components\. Run "cmake --help-policy CMP0152" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -24,4 +24,5 @@ Call Stack \(most recent call first\): CMP0152-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CMP0153/CMP0153-OLD-stderr.txt b/Tests/RunCMake/CMP0153/CMP0153-OLD-stderr.txt new file mode 100644 index 0000000..f589d29 --- /dev/null +++ b/Tests/RunCMake/CMP0153/CMP0153-OLD-stderr.txt
@@ -0,0 +1,11 @@ +^CMake Warning \(deprecated\) at [^ +]*/Tests/RunCMake/CMP0153/CMP0153-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0153 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMP0153/CMP0153-WARN-stderr.txt b/Tests/RunCMake/CMP0153/CMP0153-WARN-stderr.txt index d0b9422..5484850 100644 --- a/Tests/RunCMake/CMP0153/CMP0153-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0153/CMP0153-WARN-stderr.txt
@@ -1,8 +1,9 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(policy\) at [^ ]*/Tests/RunCMake/CMP0153/CMP0153-WARN\.cmake:[0-9]+ \(exec_program\): Policy CMP0153 is not set: The exec_program command should not be called\. Run "cmake --help-policy CMP0153" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Use execute_process\(\) instead\. -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt b/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt index 7b37985..e91b34b 100644 --- a/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0156-Common\.cmake:[0-9]+ \(add_library\): +CMake Warning \(policy\) at CMP0156-Common\.cmake:[0-9]+ \(add_library\): Policy CMP0156 is not set: De-duplicate libraries on link lines based on linker capabilities\. Run "cmake --help-policy CMP0156" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -8,9 +8,10 @@ Call Stack \(most recent call first\): CMP0156-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0156-Common\.cmake:[0-9]+ \(add_library\): +CMake Warning \(policy\) at CMP0156-Common\.cmake:[0-9]+ \(add_library\): Policy CMP0156 is not set: De-duplicate libraries on link lines based on linker capabilities\. Run "cmake --help-policy CMP0156" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -20,9 +21,10 @@ Call Stack \(most recent call first\): CMP0156-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0156-Common\.cmake:[0-9]+ \(add_executable\): +CMake Warning \(policy\) at CMP0156-Common\.cmake:[0-9]+ \(add_executable\): Policy CMP0156 is not set: De-duplicate libraries on link lines based on linker capabilities\. Run "cmake --help-policy CMP0156" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -32,4 +34,5 @@ Call Stack \(most recent call first\): CMP0156-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0160/CMP0160-WARN-stderr.txt b/Tests/RunCMake/CMP0160/CMP0160-WARN-stderr.txt index fe28f3d..7fcb354 100644 --- a/Tests/RunCMake/CMP0160/CMP0160-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0160/CMP0160-WARN-stderr.txt
@@ -38,7 +38,7 @@ CMakeLists\.txt:3 \(include\) -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -48,9 +48,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -60,9 +61,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -72,9 +74,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -84,9 +87,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -96,9 +100,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -108,9 +113,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -120,9 +126,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -132,7 +139,8 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. CMake Error at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): IMPORTED_GLOBAL property can't be set on non-imported @@ -184,7 +192,7 @@ CMakeLists\.txt:3 \(include\) -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -194,9 +202,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -206,9 +215,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -218,9 +228,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -230,9 +241,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -243,9 +255,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -255,9 +268,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -267,9 +281,10 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): +CMake Warning \(policy\) at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): Policy CMP0160 is not set: More read-only target properties now error when trying to set them\. Run "cmake --help-policy CMP0160" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -279,7 +294,8 @@ Call Stack \(most recent call first\): CMP0160-WARN\.cmake:3 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. CMake Error at READONLY_PROPERTIES\.cmake:44 \(set_target_properties\): EXPORT_NAME property can't be set on imported targets\("ReadOnlyImport"\)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11-stderr.txt index 7cdc233..870d822 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-stderr.txt index e65140a..e56a564 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-stderr.txt
@@ -7,18 +7,18 @@ CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\. + -(CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): +(CMake Warning \(author\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test13\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15-stderr.txt index 979481f..7804d8d 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-stderr.txt index 9d75891..cf0b87b 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-stderr.txt index c3f35a2..e47b4d2 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-stderr.txt index 779d708..71f301a 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-stderr.txt
@@ -4,13 +4,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -18,7 +18,7 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0163 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -29,7 +29,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -43,13 +43,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -57,7 +57,7 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0163 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -68,7 +68,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -96,7 +96,7 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0163 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -107,7 +107,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-stderr.txt index 9ab3d50..056f975 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-stderr.txt index 02d9bd3..955171c 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-NEW-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11-stderr.txt index 02ecd50..b9af9da 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11-stderr.txt
@@ -1,15 +1,15 @@ -^((CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(author\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-stderr.txt index fe1ccc3..0512034 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-stderr.txt
@@ -7,18 +7,18 @@ CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\. + -(CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): +(CMake Warning \(author\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test13\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15-stderr.txt index b37461a..d329ba3 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15-stderr.txt
@@ -1,15 +1,15 @@ -^((CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(author\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-stderr.txt index 22ae491..6ced7d4 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-stderr.txt index 23dc427..c731a6c 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-stderr.txt index ee3ee76..0ef5f8f 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-stderr.txt
@@ -4,13 +4,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -18,7 +18,7 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -29,7 +29,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -43,13 +43,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -57,7 +57,7 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -68,7 +68,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -96,7 +96,7 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -107,7 +107,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-stderr.txt index ced4fcf..cbc2637 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-stderr.txt index e855458..31937be 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-OLD-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11-stderr.txt index bd98012..dddec54 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11-stderr.txt
@@ -1,15 +1,15 @@ -^((CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(author\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-stderr.txt index 6422e36..d425253 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-stderr.txt
@@ -7,18 +7,18 @@ CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\. + -(CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): +(CMake Warning \(author\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test13\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15-stderr.txt index c34acb2..0bd9dab 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15-stderr.txt
@@ -1,15 +1,15 @@ -^((CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(author\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-stderr.txt index 0b71575..84defb6 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-stderr.txt index 0640b89..6d51c21 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-stderr.txt index f882081..94a4a86 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-stderr.txt
@@ -4,13 +4,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -18,7 +18,7 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -29,7 +29,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -43,13 +43,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -57,7 +57,7 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -68,7 +68,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -96,7 +96,7 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -107,7 +107,7 @@ Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-stderr.txt index 4b7b12e..964cb92 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-stderr.txt index f8795b9..9f63559 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0118! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0118! Call Stack \(most recent call first\): CMP0118-NEW/CMP0163-WARN-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11-stderr.txt index 182a43c..44c3c1b 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-stderr.txt index 59d81ec..300b42d 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-stderr.txt
@@ -7,18 +7,18 @@ CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\. + -(CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): +(CMake Warning \(author\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test13\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15-stderr.txt index 4c3be67..f94a703 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-stderr.txt index ffc7224..b0e73a8 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-stderr.txt index 4634ba9..a058958 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-stderr.txt
@@ -1,10 +1,10 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -12,13 +12,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -26,13 +26,13 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -40,13 +40,13 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -54,13 +54,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -68,13 +68,13 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -96,13 +96,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -110,13 +110,13 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-stderr.txt index 4b0961d..43a6370 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-stderr.txt
@@ -4,13 +4,13 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -18,7 +18,7 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0163 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -29,7 +29,7 @@ Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -43,13 +43,13 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -57,7 +57,7 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0163 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -68,7 +68,7 @@ Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -82,13 +82,13 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -96,7 +96,7 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(author\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0163 is set to NEW and the following non-boolean value given for property 'GENERATED' is therefore not allowed: @@ -107,7 +107,7 @@ Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-stderr.txt index 1e3bc32..f6cc193 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-stderr.txt index cb63bf3..9417c9f 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-stderr.txt
@@ -1,15 +1,15 @@ -^(CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): +^(CMake Warning \(author\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): Unsetting the 'GENERATED' property is not allowed under CMP0163! -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(author\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): Unsetting the 'GENERATED' property is not allowed under CMP0163! Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-NEW-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-stderr.txt index 63be2a7..52840349 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-stderr.txt index 3068eb3..7f4fdce 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-stderr.txt
@@ -7,28 +7,28 @@ CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\. + -(CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): +(CMake Warning \(policy\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test13\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-stderr.txt index 7ce1d4e..373a050 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-stderr.txt index 9098ddd..37ffce9 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-stderr.txt
@@ -1,15 +1,15 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -17,18 +17,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -36,18 +36,18 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -55,18 +55,18 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -74,18 +74,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -93,18 +93,18 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -112,18 +112,18 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -131,18 +131,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -150,18 +150,18 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-stderr.txt index 6893cc9..9fe28e0 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-stderr.txt
@@ -1,15 +1,15 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -17,18 +17,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -36,18 +36,18 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -55,18 +55,18 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -74,18 +74,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -93,18 +93,18 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -112,18 +112,18 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -131,18 +131,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -150,18 +150,18 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-stderr.txt index 7fa3ffe..701957f 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-stderr.txt
@@ -4,18 +4,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -23,7 +23,7 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -38,11 +38,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -56,18 +56,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -75,7 +75,7 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -90,11 +90,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -108,18 +108,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -127,7 +127,7 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -142,11 +142,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-stderr.txt index c4ae929..2364b78 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-stderr.txt index 7dbd729..7e6add1 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-OLD-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-stderr.txt index bffefb6..e1081de 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-stderr.txt index d9ea88e..0f9fbb6 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-stderr.txt
@@ -7,28 +7,28 @@ CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): TARGET 'custom[4-6]' was not created in this directory\. + -(CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): +(CMake Warning \(policy\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -(CMake Warning \(dev\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test13\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-stderr.txt index ac80737..f635de2 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source0\.txt: # 1a # GENERATED = `1` Generated_source0\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-stderr.txt index fbdf1da..1059f03 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-stderr.txt
@@ -1,15 +1,15 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -17,18 +17,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -36,18 +36,18 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -55,18 +55,18 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -74,18 +74,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -93,18 +93,18 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -112,18 +112,18 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -131,18 +131,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -150,18 +150,18 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-stderr.txt index caf555c..3d12a53 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-stderr.txt
@@ -1,15 +1,15 @@ -^CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +^CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_path1\.txt: # 1b # GENERATED = `0` @@ -17,18 +17,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -36,18 +36,18 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -55,18 +55,18 @@ Generated_with_full_path3\.txt: # 2b # GENERATED = `0` Generated_with_full_path3\.txt: # 3a # GENERATED = `0` Generated_with_full_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` @@ -74,18 +74,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `0` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -93,18 +93,18 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -112,18 +112,18 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` @@ -131,18 +131,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -150,18 +150,18 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test4b\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-stderr.txt index 83556e6..9589185 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-stderr.txt
@@ -4,18 +4,18 @@ Generated_with_full_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_path1\.txt: # 3a # GENERATED = `0` Generated_with_full_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_path2\.txt: # 1b # GENERATED = `0` @@ -23,7 +23,7 @@ Generated_with_full_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -38,11 +38,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` @@ -56,18 +56,18 @@ Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` @@ -75,7 +75,7 @@ Generated_with_relative_path2\.txt: # 2b # GENERATED = `0` Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -90,11 +90,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` @@ -108,18 +108,18 @@ Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` @@ -127,7 +127,7 @@ Generated_with_full_source_path2\.txt: # 2b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` -CMake Warning \(dev\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): +CMake Warning \(policy\) at CMP0163-Common-Test5\.cmake:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -142,11 +142,11 @@ instead\. This will be an error under policy CMP0118\. - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-stderr.txt index 9d8af4f..ca624f8 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `1` Generated_source1\.txt: # 1b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-stderr.txt index 4d35ef0..406f0a0 100644 --- a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-stderr.txt
@@ -1,25 +1,25 @@ -^((CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): +^((CMake Warning \(policy\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -(CMake Warning \(dev\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): +(CMake Warning \(policy\) at CMP0163-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): Policy CMP0118 is not set: GENERATED sources may be used across directories without manual marking\. Run "cmake --help-policy CMP0118" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Unsetting property 'GENERATED' will not be allowed under policy CMP0118! - Call Stack \(most recent call first\): CMP0118-WARN/CMP0163-WARN-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+)+ Generated_source1\.txt: # 1a # GENERATED = `0` Generated_source1\.txt: # 1b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0165/CMP0165-WARN-stderr.txt b/Tests/RunCMake/CMP0165/CMP0165-WARN-stderr.txt index 32a79df..e327a63 100644 --- a/Tests/RunCMake/CMP0165/CMP0165-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0165/CMP0165-WARN-stderr.txt
@@ -1,5 +1,5 @@ -CMake Warning \(dev\) at CMP0165-WARN\.cmake:[0-9]+ \(enable_language\): +CMake Warning \(author\) at CMP0165-WARN\.cmake:[0-9]+ \(enable_language\): project\(\) should be called prior to this enable_language\(\) call\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/CMP0169/CMP0169-WARN-stderr.txt b/Tests/RunCMake/CMP0169/CMP0169-WARN-stderr.txt index 7a06200..644e77a 100644 --- a/Tests/RunCMake/CMP0169/CMP0169-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0169/CMP0169-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): Calling FetchContent_Populate\(t2\) is deprecated, call FetchContent_MakeAvailable\(t2\) instead\. Policy CMP0169 can be set to OLD to allow FetchContent_Populate\(t2\) to be called directly for now, but the @@ -8,4 +8,4 @@ CMP0169\.cmake:[0-9]+ \(FetchContent_Populate\) CMP0169-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/CMP0171/CMP0171-WARN-QUIET.cmake b/Tests/RunCMake/CMP0171/CMP0171-WARN-QUIET.cmake new file mode 100644 index 0000000..928a53f --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-WARN-QUIET.cmake
@@ -0,0 +1 @@ +add_subdirectory("CMP0171-WARN-QUIET")
diff --git a/Tests/RunCMake/CMP0171/CMP0171-WARN-QUIET/CMakeLists.txt b/Tests/RunCMake/CMP0171/CMP0171-WARN-QUIET/CMakeLists.txt new file mode 100644 index 0000000..8d25157 --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-WARN-QUIET/CMakeLists.txt
@@ -0,0 +1,8 @@ +# CMake should warn the user if they have a target named codegen... unless the +# user also suppresses the diagnostic. +cmake_diagnostic(SET CMD_AUTHOR IGNORE RECURSE) +add_custom_target(codegen + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h +)
diff --git a/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt b/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt index 776aa53..1dcedb0 100644 --- a/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0171-WARN\.cmake:[0-9]+ \(add_custom_target\): +CMake Warning \(policy\) at CMP0171-WARN\.cmake:[0-9]+ \(add_custom_target\): Policy CMP0171 is not set: 'codegen' is a reserved target name\. Run "cmake --help-policy CMP0171" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -6,4 +6,5 @@ The target name "codegen" is reserved\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0171/RunCMakeTest.cmake b/Tests/RunCMake/CMP0171/RunCMakeTest.cmake index 75941d7..143e59e3 100644 --- a/Tests/RunCMake/CMP0171/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0171/RunCMakeTest.cmake
@@ -1,6 +1,7 @@ include(RunCMake) run_cmake("CMP0171-WARN") +run_cmake("CMP0171-WARN-QUIET") run_cmake_with_options(CMP0171-OLD "-DCMAKE_POLICY_DEFAULT_CMP0171=OLD")
diff --git a/Tests/RunCMake/CMP0173/CMP0173-WARN-stderr.txt b/Tests/RunCMake/CMP0173/CMP0173-WARN-stderr.txt index 3a4c130..9aa15f2 100644 --- a/Tests/RunCMake/CMP0173/CMP0173-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0173/CMP0173-WARN-stderr.txt
@@ -1,14 +1,14 @@ -CMake Warning \(dev\) at CMP0173-common\.cmake:1 \(include\): +CMake Warning \(policy\) at CMP0173-common\.cmake:1 \(include\): Policy CMP0173 is not set: The CMakeFindFrameworks module is removed\. Run "cmake --help-policy CMP0173" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMP0173-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at .*/Modules/CMakeFindFrameworks\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/CMakeFindFrameworks\.cmake:[0-9]+ \(message\): CMakeFindFrameworks\.cmake is not maintained and lacks support for more recent framework handling\. It will be removed in a future version of CMake\. Update the code to use find_library\(\) instead\. @@ -16,9 +16,9 @@ CMP0173-common\.cmake:1 \(include\) CMP0173-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at .*/Modules/CMakeFindFrameworks\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/CMakeFindFrameworks\.cmake:[0-9]+ \(message\): CMakeFindFrameworks\.cmake is not maintained and lacks support for more recent framework handling\. It will be removed in a future version of CMake\. Update the code to use find_library\(\) instead\. @@ -26,4 +26,4 @@ CMP0173-common\.cmake:12 \(include\) CMP0173-WARN\.cmake:2 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-stderr.txt b/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-stderr.txt index ac1885a..93eeab0 100644 --- a/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-stderr.txt
@@ -1,3 +1,3 @@ -^CMake Deprecation Warning at CMP0187-NEW-CMP0115-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0187-NEW-CMP0115-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0115 will be removed from a future version of CMake\.
diff --git a/Tests/RunCMake/CMP0194/CMP0194-WARN-stderr.txt b/Tests/RunCMake/CMP0194/CMP0194-WARN-stderr.txt index ae84494..0ffb1c4 100644 --- a/Tests/RunCMake/CMP0194/CMP0194-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0194/CMP0194-WARN-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/CMakeDetermineASMCompiler\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/CMakeDetermineASMCompiler\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0194 is not set: MSVC is not an assembler for language ASM\. Run "cmake --help-policy CMP0194" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -7,4 +7,5 @@ CMP0194-common\.cmake:[0-9]+ \(enable_language\) CMP0194-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/CMP0212/CMP0212-NEW-build-result.txt similarity index 100% rename from Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt rename to Tests/RunCMake/CMP0212/CMP0212-NEW-build-result.txt
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-stderr.txt b/Tests/RunCMake/CMP0212/CMP0212-NEW-build-stdout.txt similarity index 100% rename from Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-stderr.txt rename to Tests/RunCMake/CMP0212/CMP0212-NEW-build-stdout.txt
diff --git a/Tests/RunCMake/CMP0212/CMP0212-common-exe.cmake b/Tests/RunCMake/CMP0212/CMP0212-common-exe.cmake new file mode 100644 index 0000000..05eda9f --- /dev/null +++ b/Tests/RunCMake/CMP0212/CMP0212-common-exe.cmake
@@ -0,0 +1,2 @@ +add_executable(foo main.c) +set_target_properties(foo PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin)
diff --git a/Tests/RunCMake/CMP0212/CMakeLists.txt b/Tests/RunCMake/CMP0212/CMakeLists.txt new file mode 100644 index 0000000..b18faf4 --- /dev/null +++ b/Tests/RunCMake/CMP0212/CMakeLists.txt
@@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 4.3) +project(${RunCMake_TEST} C) +include(CMP0212-common-exe.cmake) +add_subdirectory(subdir)
diff --git a/Tests/RunCMake/CMP0212/RunCMakeTest.cmake b/Tests/RunCMake/CMP0212/RunCMakeTest.cmake new file mode 100644 index 0000000..d4a0747 --- /dev/null +++ b/Tests/RunCMake/CMP0212/RunCMakeTest.cmake
@@ -0,0 +1,12 @@ +include(RunCMake) + +function(run_cmake_case_cmp0212 case) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) + run_cmake(${case}) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --target ${ARGN}) +endfunction() + +run_cmake_case_cmp0212(CMP0212-OLD tgt1) +run_cmake_case_cmp0212(CMP0212-NEW tgt1 bar)
diff --git a/Tests/RunCMake/CMP0212/main.c b/Tests/RunCMake/CMP0212/main.c new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/CMP0212/main.c
@@ -0,0 +1,4 @@ +int main() +{ + return 0; +}
diff --git a/Tests/RunCMake/CMP0212/subdir/CMP0212-NEW.cmake b/Tests/RunCMake/CMP0212/subdir/CMP0212-NEW.cmake new file mode 100644 index 0000000..9568a4a --- /dev/null +++ b/Tests/RunCMake/CMP0212/subdir/CMP0212-NEW.cmake
@@ -0,0 +1,11 @@ +cmake_policy(SET CMP0212 NEW) +include(CMP0212-common-custom.cmake) + +# Define an executable with a different target name so that the above custom +# command finds the file-level dependency instead of the target-level +# dependency, and thus fails the build by compiling a bad C file. +add_executable(bar bad.c) +set_target_properties(bar PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "$<1:${CMAKE_CURRENT_BINARY_DIR}>" + RUNTIME_OUTPUT_NAME foo +)
diff --git a/Tests/RunCMake/CMP0212/subdir/CMP0212-OLD.cmake b/Tests/RunCMake/CMP0212/subdir/CMP0212-OLD.cmake new file mode 100644 index 0000000..05b4849 --- /dev/null +++ b/Tests/RunCMake/CMP0212/subdir/CMP0212-OLD.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0212 OLD) +include(CMP0212-common-custom.cmake)
diff --git a/Tests/RunCMake/CMP0212/subdir/CMP0212-common-custom.cmake b/Tests/RunCMake/CMP0212/subdir/CMP0212-common-custom.cmake new file mode 100644 index 0000000..d5f2c9e --- /dev/null +++ b/Tests/RunCMake/CMP0212/subdir/CMP0212-common-custom.cmake
@@ -0,0 +1,6 @@ +add_custom_command( + OUTPUT cmd1 + COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_CURRENT_BINARY_DIR}/cmd1" + DEPENDS foo.exe +) +add_custom_target(tgt1 DEPENDS cmd1)
diff --git a/Tests/RunCMake/CMP0212/subdir/CMakeLists.txt b/Tests/RunCMake/CMP0212/subdir/CMakeLists.txt new file mode 100644 index 0000000..a7cff2b --- /dev/null +++ b/Tests/RunCMake/CMP0212/subdir/CMakeLists.txt
@@ -0,0 +1 @@ +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0212/subdir/bad.c b/Tests/RunCMake/CMP0212/subdir/bad.c new file mode 100644 index 0000000..a364cfd --- /dev/null +++ b/Tests/RunCMake/CMP0212/subdir/bad.c
@@ -0,0 +1 @@ +#error "This is an invalid C file."
diff --git a/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake b/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake new file mode 100644 index 0000000..881d941 --- /dev/null +++ b/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake
@@ -0,0 +1,10 @@ + +cmake_policy(SET CMP0217 NEW) + +macro(FOO) +endmacro() + +get_property(macros DIRECTORY PROPERTY MACROS) +if (macros) + set(RunCMake_TEST_FAILED "unexpected content for MACROS directory property.") +endif()
diff --git a/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake b/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake new file mode 100644 index 0000000..dee0cc3 --- /dev/null +++ b/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake
@@ -0,0 +1,10 @@ + +cmake_policy(SET CMP0217 OLD) + +macro(FOO) +endmacro() + +get_property(macros DIRECTORY PROPERTY MACROS) +if (NOT "FOO" IN_LIST macros) + set(RunCMake_TEST_FAILED "unexpected content for MACROS directory property.") +endif()
diff --git a/Tests/RunCMake/CMP0217/CMakeLists.txt b/Tests/RunCMake/CMP0217/CMakeLists.txt new file mode 100644 index 0000000..955802c --- /dev/null +++ b/Tests/RunCMake/CMP0217/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.0) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0217/RunCMakeTest.cmake b/Tests/RunCMake/CMP0217/RunCMakeTest.cmake new file mode 100644 index 0000000..8b6cffa --- /dev/null +++ b/Tests/RunCMake/CMP0217/RunCMakeTest.cmake
@@ -0,0 +1,4 @@ +include(RunCMake) + +run_cmake(CMP0217-OLD) +run_cmake(CMP0217-NEW)
diff --git a/Tests/RunCMake/CMP0218/CMP0218-NEW.cmake b/Tests/RunCMake/CMP0218/CMP0218-NEW.cmake new file mode 100644 index 0000000..035be35 --- /dev/null +++ b/Tests/RunCMake/CMP0218/CMP0218-NEW.cmake
@@ -0,0 +1,72 @@ +cmake_policy(SET CMP0218 NEW) + +function(expect NAME ACTUAL EXPECTED) + if(NOT "${ACTUAL}" STREQUAL "${EXPECTED}") + message(SEND_ERROR "${NAME} is '${ACTUAL}'; should be '${EXPECTED}'") + endif() +endfunction() + +if(DEFINED CMAKE_WARN_DEPRECATED) + message(SEND_ERROR "CMAKE_WARN_DEPRECATED should not be defined") +endif() + +if(DEFINED CMAKE_ERROR_DEPRECATED) + message(SEND_ERROR "CMAKE_ERROR_DEPRECATED should not be defined") +endif() + +cmake_diagnostic(GET CMD_DEPRECATED action) +if(NOT "${action}" STREQUAL "WARN") + message(SEND_ERROR "CMD_DEPRECATED is ${action}; should be WARN") +endif() + +# ----------------------------------------------------------------------------- +# Setting CMAKE_WARN_DEPRECATED should have no effect on CMD_DEPRECATED +block() + set(CMAKE_WARN_DEPRECATED OFF) + + cmake_diagnostic(GET CMD_DEPRECATED action) + if(NOT "${action}" STREQUAL "WARN") + message(SEND_ERROR "CMD_DEPRECATED is ${action}; should be WARN") + endif() +endblock() + +# ----------------------------------------------------------------------------- +# Setting CMAKE_ERROR_DEPRECATED should have no effect on CMD_DEPRECATED +block() + set(CMAKE_ERROR_DEPRECATED ON) + + cmake_diagnostic(GET CMD_DEPRECATED action) + if(NOT "${action}" STREQUAL "WARN") + message(SEND_ERROR "CMD_DEPRECATED is ${action}; should be WARN") + endif() +endblock() + +# ----------------------------------------------------------------------------- +# Changing CMD_DEPRECATED should have no effect on variables +block() + cmake_diagnostic(SET CMD_DEPRECATED IGNORE) + + if(DEFINED CMAKE_WARN_DEPRECATED) + message(SEND_ERROR "CMAKE_WARN_DEPRECATED should not be defined") + endif() + + if(DEFINED CMAKE_ERROR_DEPRECATED) + message(SEND_ERROR "CMAKE_ERROR_DEPRECATED should not be defined") + endif() + + expect(CMAKE_WARN_DEPRECATED "${CMAKE_WARN_DEPRECATED}" "") + expect(CMAKE_ERROR_DEPRECATED "${CMAKE_ERROR_DEPRECATED}" "") + + cmake_diagnostic(SET CMD_DEPRECATED SEND_ERROR) + + if(DEFINED CMAKE_WARN_DEPRECATED) + message(SEND_ERROR "CMAKE_WARN_DEPRECATED should not be defined") + endif() + + if(DEFINED CMAKE_ERROR_DEPRECATED) + message(SEND_ERROR "CMAKE_ERROR_DEPRECATED should not be defined") + endif() + + expect(CMAKE_WARN_DEPRECATED "${CMAKE_WARN_DEPRECATED}" "") + expect(CMAKE_ERROR_DEPRECATED "${CMAKE_ERROR_DEPRECATED}" "") +endblock()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMP0218/CMP0218-OLD-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMP0218/CMP0218-OLD-result.txt
diff --git a/Tests/RunCMake/CMP0218/CMP0218-OLD-stderr.txt b/Tests/RunCMake/CMP0218/CMP0218-OLD-stderr.txt new file mode 100644 index 0000000..fed4f0f --- /dev/null +++ b/Tests/RunCMake/CMP0218/CMP0218-OLD-stderr.txt
@@ -0,0 +1,17 @@ +CMake Warning \(deprecated\) at [^ +]+/Tests/RunCMake/CMP0218/CMP0218-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0153 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. ++ +CMake Error \(deprecated\) at [^ +]+/Tests/RunCMake/CMP0218/CMP0218-OLD\.cmake:[0-9]+ \(message\): + Test +This error is for project developers\. Use -Wno-error=author or +-Wno-error=deprecated to suppress it\.
diff --git a/Tests/RunCMake/CMP0218/CMP0218-OLD.cmake b/Tests/RunCMake/CMP0218/CMP0218-OLD.cmake new file mode 100644 index 0000000..83043f3 --- /dev/null +++ b/Tests/RunCMake/CMP0218/CMP0218-OLD.cmake
@@ -0,0 +1,15 @@ +cmake_diagnostic(SET CMD_DEPRECATED IGNORE) + +# Note: We're using the deprecation warning from cmMakefile::SetPolicy as a +# test, which requires use of policies within the deprecation window (as of +# CMake 4.4, CMP0066...CMP0155). These will likely need to be rotated to newer +# policies when that window changes. + +cmake_policy(SET CMP0218 NEW) +cmake_policy(SET CMP0152 OLD) # should be silent + +cmake_policy(SET CMP0218 OLD) +cmake_policy(SET CMP0153 OLD) # should NOT be silent + +set(CMAKE_ERROR_DEPRECATED ON) +message(DEPRECATION "Test") # should be error
diff --git a/Tests/RunCMake/CMP0218/CMP0218-WARN-stderr.txt b/Tests/RunCMake/CMP0218/CMP0218-WARN-stderr.txt new file mode 100644 index 0000000..763f0f6 --- /dev/null +++ b/Tests/RunCMake/CMP0218/CMP0218-WARN-stderr.txt
@@ -0,0 +1,35 @@ +CMake Warning \(policy\) at [^ +]+/Tests/RunCMake/CMP0218/CMP0218-WARN\.cmake:[0-9]+ \(set\): + Policy CMP0218 is not set: The CMAKE_WARN_DEPRECATED and + CMAKE_ERROR_DEPRECATED variables are ignored\. Run "cmake --help-policy + CMP0218" for policy details\. Use the cmake_policy command to set the + policy and suppress this warning\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + +CMake Warning \(policy\) at [^ +]+/Tests/RunCMake/CMP0218/CMP0218-WARN\.cmake:[0-9]+ \(set\): + Policy CMP0218 is not set: The CMAKE_WARN_DEPRECATED and + CMAKE_ERROR_DEPRECATED variables are ignored\. Run "cmake --help-policy + CMP0218" for policy details\. Use the cmake_policy command to set the + policy and suppress this warning\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + +CMake Warning \(policy\) at [^ +]+/Tests/RunCMake/CMP0218/CMP0218-WARN\.cmake:[0-9]+ \(set\): + Policy CMP0218 is not set: The CMAKE_WARN_DEPRECATED and + CMAKE_ERROR_DEPRECATED variables are ignored\. Run "cmake --help-policy + CMP0218" for policy details\. Use the cmake_policy command to set the + policy and suppress this warning\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + +CMake Warning \(policy\) at [^ +]+/Tests/RunCMake/CMP0218/CMP0218-WARN\.cmake:[0-9]+ \(set\): + Policy CMP0218 is not set: The CMAKE_WARN_DEPRECATED and + CMAKE_ERROR_DEPRECATED variables are ignored\. Run "cmake --help-policy + CMP0218" for policy details\. Use the cmake_policy command to set the + policy and suppress this warning\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0218/CMP0218-WARN.cmake b/Tests/RunCMake/CMP0218/CMP0218-WARN.cmake new file mode 100644 index 0000000..5e57da3 --- /dev/null +++ b/Tests/RunCMake/CMP0218/CMP0218-WARN.cmake
@@ -0,0 +1,8 @@ +# Test warning when setting deprecated variables. +set(CMAKE_WARN_DEPRECATED OFF) +message(DEPRECATION "Test") +set(CMAKE_ERROR_DEPRECATED ON) + +# Test warning when reading deprecated variables. +set(warn "${CMAKE_WARN_DEPRECATED}") +set(error "${CMAKE_ERROR_DEPRECATED}")
diff --git a/Tests/RunCMake/CMP0218/CMakeLists.txt b/Tests/RunCMake/CMP0218/CMakeLists.txt new file mode 100644 index 0000000..ad13db5 --- /dev/null +++ b/Tests/RunCMake/CMP0218/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) # Must be < 4.4 to test WARN +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0218/ExpectError-stderr.txt b/Tests/RunCMake/CMP0218/ExpectError-stderr.txt new file mode 100644 index 0000000..0c122de --- /dev/null +++ b/Tests/RunCMake/CMP0218/ExpectError-stderr.txt
@@ -0,0 +1 @@ +^The CMAKE_ERROR_DEPRECATED variable is deprecated\. Use -W\[no-\]error=deprecated instead\.$
diff --git a/Tests/RunCMake/CMP0218/ExpectError.cmake b/Tests/RunCMake/CMP0218/ExpectError.cmake new file mode 100644 index 0000000..a867217 --- /dev/null +++ b/Tests/RunCMake/CMP0218/ExpectError.cmake
@@ -0,0 +1,4 @@ +cmake_diagnostic(GET CMD_DEPRECATED action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "CMD_DEPRECATED is ${action}; should be SEND_ERROR") +endif()
diff --git a/Tests/RunCMake/CMP0218/ExpectIgnore-stderr.txt b/Tests/RunCMake/CMP0218/ExpectIgnore-stderr.txt new file mode 100644 index 0000000..9eaed90 --- /dev/null +++ b/Tests/RunCMake/CMP0218/ExpectIgnore-stderr.txt
@@ -0,0 +1 @@ +^The CMAKE_WARN_DEPRECATED variable is deprecated\. Use -W\[no-\]deprecated instead\.$
diff --git a/Tests/RunCMake/CMP0218/ExpectIgnore.cmake b/Tests/RunCMake/CMP0218/ExpectIgnore.cmake new file mode 100644 index 0000000..c84b0f9 --- /dev/null +++ b/Tests/RunCMake/CMP0218/ExpectIgnore.cmake
@@ -0,0 +1,4 @@ +cmake_diagnostic(GET CMD_DEPRECATED action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "CMD_DEPRECATED is ${action}; should be IGNORE") +endif()
diff --git a/Tests/RunCMake/CMP0218/RunCMakeTest.cmake b/Tests/RunCMake/CMP0218/RunCMakeTest.cmake new file mode 100644 index 0000000..5953075 --- /dev/null +++ b/Tests/RunCMake/CMP0218/RunCMakeTest.cmake
@@ -0,0 +1,8 @@ +include(RunCMake) + +run_cmake_script(CMP0218-WARN) +run_cmake_script(CMP0218-OLD) +run_cmake_script(CMP0218-NEW) + +run_cmake_with_options(ExpectIgnore -DCMAKE_WARN_DEPRECATED=OFF) +run_cmake_with_options(ExpectError -DCMAKE_ERROR_DEPRECATED=ON)
diff --git a/Tests/RunCMake/CMP0219/AllNEW-Forward.cmake b/Tests/RunCMake/CMP0219/AllNEW-Forward.cmake new file mode 100644 index 0000000..4a2f5ef --- /dev/null +++ b/Tests/RunCMake/CMP0219/AllNEW-Forward.cmake
@@ -0,0 +1,17 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_inner) + set(cmp0219_inner_argn "${ARGN}") +endmacro() + +macro(cmp0219_middle) + cmp0219_inner(${ARGN}) +endmacro() + +macro(cmp0219_outer) + cmp0219_middle(${ARGN}) +endmacro() + +cmp0219_outer(HINTS "${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_inner_argn}" "HINTS;${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CMP0219/Basic-NEW.cmake b/Tests/RunCMake/CMP0219/Basic-NEW.cmake new file mode 100644 index 0000000..bf17b54 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Basic-NEW.cmake
@@ -0,0 +1,18 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_capture package_name first_named) + set(cmp0219_named "${first_named}") + set(cmp0219_argn "${ARGN}") + set(cmp0219_argv "${ARGV}") + set(cmp0219_argv0 "${ARGV0}") + set(cmp0219_argv1 "${ARGV1}") +endmacro() + +cmp0219_capture(pybind11 HINTS "${cmp0219_path_native}") + +cmp0219_assert_equal("${cmp0219_named}" "HINTS") +cmp0219_assert_equal("${cmp0219_argn}" "${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_argv}" "pybind11;HINTS;${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_argv0}" "pybind11") +cmp0219_assert_equal("${cmp0219_argv1}" "HINTS")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMP0219/Basic-OLD-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMP0219/Basic-OLD-result.txt
diff --git a/Tests/RunCMake/CMP0219/Basic-OLD-stderr.txt b/Tests/RunCMake/CMP0219/Basic-OLD-stderr.txt new file mode 100644 index 0000000..4116c67 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Basic-OLD-stderr.txt
@@ -0,0 +1 @@ +Invalid character escape '\\b'\.
diff --git a/Tests/RunCMake/CMP0219/Basic-OLD.cmake b/Tests/RunCMake/CMP0219/Basic-OLD.cmake new file mode 100644 index 0000000..6ea405b --- /dev/null +++ b/Tests/RunCMake/CMP0219/Basic-OLD.cmake
@@ -0,0 +1,8 @@ +cmake_policy(SET CMP0219 OLD) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_capture_old) + set(cmp0219_old_argn "${ARGN}") +endmacro() + +cmp0219_capture_old(HINTS "${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CMP0219/CMP0219-helpers.cmake b/Tests/RunCMake/CMP0219/CMP0219-helpers.cmake new file mode 100644 index 0000000..eb88100 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CMP0219-helpers.cmake
@@ -0,0 +1,20 @@ +function(cmp0219_assert_equal actual expected) + if(NOT "${actual}" STREQUAL "${expected}") + message(FATAL_ERROR + "Assertion failed:\n" + " expected=[${expected}]\n" + " actual=[${actual}]") + endif() +endfunction() + +function(cmp0219_assert_undefined variable_name) + if(DEFINED ${variable_name}) + message(FATAL_ERROR + "Assertion failed:\n" + " ${variable_name} should be undefined\n" + " actual=[${${variable_name}}]") + endif() +endfunction() + +set(cmp0219_path_fwd "C:/build_bot/new/temp/vendor") +string(REPLACE "/" "\\" cmp0219_path_native "${cmp0219_path_fwd}")
diff --git a/Tests/RunCMake/CMP0219/CMakeLists.txt b/Tests/RunCMake/CMP0219/CMakeLists.txt new file mode 100644 index 0000000..30c0273 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.10) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/CMP0219/CheckCSourceCompiles-NEW.cmake b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-NEW.cmake new file mode 100644 index 0000000..53f1944 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-NEW.cmake
@@ -0,0 +1,8 @@ +cmake_policy(SET CMP0219 NEW) + +enable_language(C) +include(CheckCSourceCompiles) +check_c_source_compiles("int main() { return '\\0'; }" RESULT) +if(NOT RESULT) + message(FATAL_ERROR "check_c_source_compiles failed for escaped NUL character source") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-result.txt
diff --git a/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-stderr.txt b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-stderr.txt new file mode 100644 index 0000000..ea54247 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD-stderr.txt
@@ -0,0 +1 @@ +Invalid character escape '\\0'\.
diff --git a/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD.cmake b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD.cmake new file mode 100644 index 0000000..27715d1 --- /dev/null +++ b/Tests/RunCMake/CMP0219/CheckCSourceCompiles-OLD.cmake
@@ -0,0 +1,5 @@ +cmake_policy(SET CMP0219 OLD) + +enable_language(C) +include(CheckCSourceCompiles) +check_c_source_compiles("int main() { return '\\0'; }" RESULT)
diff --git a/Tests/RunCMake/CMP0219/Defer-EndPolicy-NEW.cmake b/Tests/RunCMake/CMP0219/Defer-EndPolicy-NEW.cmake new file mode 100644 index 0000000..420f2f0 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Defer-EndPolicy-NEW.cmake
@@ -0,0 +1,30 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_defer_capture var_name) + set(${var_name} "${ARGN}") +endmacro() + +# Deferred call is scheduled with OLD at the call site, +# but executed with NEW at end-of-file. +cmake_policy(SET CMP0219 OLD) +cmake_language( + DEFER CALL + cmp0219_defer_capture cmp0219_defer_old_callsite + HINTS "${cmp0219_path_native}") + +# Deferred call is scheduled with NEW at the call site, +# and executed with NEW at end-of-file. +cmake_policy(SET CMP0219 NEW) +cmake_language( + DEFER CALL + cmp0219_defer_capture cmp0219_defer_new_callsite + HINTS "${cmp0219_path_native}") + +cmake_language( + DEFER CALL + cmp0219_assert_equal "${cmp0219_defer_old_callsite}" + "HINTS;${cmp0219_path_native}") +cmake_language( + DEFER CALL + cmp0219_assert_equal "${cmp0219_defer_new_callsite}" + "HINTS;${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-result.txt
diff --git a/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-stderr.txt b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-stderr.txt new file mode 100644 index 0000000..4116c67 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD-stderr.txt
@@ -0,0 +1 @@ +Invalid character escape '\\b'\.
diff --git a/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD.cmake b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD.cmake new file mode 100644 index 0000000..5901ca1 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Defer-EndPolicy-OLD.cmake
@@ -0,0 +1,15 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_defer_capture var_name) + set(${var_name} "${ARGN}") +endmacro() + +# Deferred call is scheduled with NEW at the call site, +# but executed with OLD at end-of-file. +cmake_policy(SET CMP0219 NEW) +cmake_language( + DEFER CALL + cmp0219_defer_capture cmp0219_defer_new_callsite + HINTS "${cmp0219_path_native}") + +cmake_policy(SET CMP0219 OLD)
diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-NEW.cmake b/Tests/RunCMake/CMP0219/DependencyProviderMacro-NEW.cmake new file mode 100644 index 0000000..c0e410f --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-NEW.cmake
@@ -0,0 +1,7 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +find_package(pybind11 HINTS "${cmp0219_path_native}") + +cmp0219_assert_equal("${cmp0219_provider_method}" "FIND_PACKAGE") +cmp0219_assert_equal("${cmp0219_provider_package}" "pybind11") +cmp0219_assert_equal("${cmp0219_provider_argn}" "HINTS;${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-result.txt
diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-stderr.txt b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-stderr.txt new file mode 100644 index 0000000..4116c67 --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD-stderr.txt
@@ -0,0 +1 @@ +Invalid character escape '\\b'\.
diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD.cmake b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD.cmake new file mode 100644 index 0000000..1baa8c3 --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-OLD.cmake
@@ -0,0 +1,3 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +find_package(pybind11 HINTS "${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CMP0219/DependencyProviderMacro-TopInclude.cmake b/Tests/RunCMake/CMP0219/DependencyProviderMacro-TopInclude.cmake new file mode 100644 index 0000000..6ba459f --- /dev/null +++ b/Tests/RunCMake/CMP0219/DependencyProviderMacro-TopInclude.cmake
@@ -0,0 +1,11 @@ +macro(cmp0219_provider method package_name) + set(cmp0219_provider_method "${method}") + set(cmp0219_provider_package "${package_name}") + set(cmp0219_provider_argn "${ARGN}") + set(${package_name}_FOUND TRUE) +endmacro() + +cmake_language( + SET_DEPENDENCY_PROVIDER cmp0219_provider + SUPPORTED_METHODS FIND_PACKAGE +)
diff --git a/Tests/RunCMake/CMP0219/DynamicDispatch-NEW.cmake b/Tests/RunCMake/CMP0219/DynamicDispatch-NEW.cmake new file mode 100644 index 0000000..0cbd969 --- /dev/null +++ b/Tests/RunCMake/CMP0219/DynamicDispatch-NEW.cmake
@@ -0,0 +1,9 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_dispatch) + set(cmp0219_dispatch_argn "${ARGN}") +endmacro() + +cmake_language(CALL cmp0219_dispatch HINTS "${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_dispatch_argn}" "HINTS;${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CMP0219/Escape2-NEW.cmake b/Tests/RunCMake/CMP0219/Escape2-NEW.cmake new file mode 100644 index 0000000..a8ed1bf --- /dev/null +++ b/Tests/RunCMake/CMP0219/Escape2-NEW.cmake
@@ -0,0 +1,8 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +macro(cmp0219_escape str) + cmp0219_assert_equal("${str}" "\\") +endmacro() + +cmp0219_escape("\\")
diff --git a/Tests/RunCMake/CMP0219/MixedChain-OLD-NEWMiddle.cmake b/Tests/RunCMake/CMP0219/MixedChain-OLD-NEWMiddle.cmake new file mode 100644 index 0000000..1a9cef1 --- /dev/null +++ b/Tests/RunCMake/CMP0219/MixedChain-OLD-NEWMiddle.cmake
@@ -0,0 +1,48 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +# Build progressively escaped variants to model callers that added extra +# backslashes to make an OLD-only macro chain work. +string(REPLACE "\\" "\\\\" cmp0219_path_2 "${cmp0219_path_native}") +string(REPLACE "\\" "\\\\" cmp0219_path_4 "${cmp0219_path_2}") +string(REPLACE "\\" "\\\\" cmp0219_path_8 "${cmp0219_path_4}") + +cmake_policy(SET CMP0219 OLD) + +macro(cmp0219_leaf var_name) + set(${var_name} "${ARGN}") +endmacro() + +macro(cmp0219_middle_old) + cmp0219_leaf(cmp0219_old_chain_capture ${ARGN}) +endmacro() + +macro(cmp0219_middle_new) + # Simulate a dependency update opting this middle forwarding layer into NEW. + cmake_policy(PUSH) + cmake_policy(SET CMP0219 NEW) + cmp0219_leaf(cmp0219_mixed_chain_capture ${ARGN}) + cmake_policy(POP) +endmacro() + +macro(cmp0219_outer_to_old) + cmp0219_middle_old(${ARGN}) +endmacro() + +macro(cmp0219_outer_to_new_middle) + cmp0219_middle_new(${ARGN}) +endmacro() + +# OLD->OLD->OLD requires heavily escaped input and produces a native path. +cmp0219_outer_to_old(HINTS "${cmp0219_path_8}") +cmp0219_assert_equal( + "${cmp0219_old_chain_capture}" "HINTS;${cmp0219_path_native}") + +# OLD->NEW->OLD with the same input preserves one extra layer. +cmp0219_outer_to_new_middle(HINTS "${cmp0219_path_8}") +cmp0219_assert_equal( + "${cmp0219_mixed_chain_capture}" "HINTS;${cmp0219_path_2}") + +# OLD->NEW->OLD needs fewer escape layers to reach a native path. +cmp0219_outer_to_new_middle(HINTS "${cmp0219_path_4}") +cmp0219_assert_equal( + "${cmp0219_mixed_chain_capture}" "HINTS;${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CMP0219/NEW-calls-OLD.cmake b/Tests/RunCMake/CMP0219/NEW-calls-OLD.cmake new file mode 100644 index 0000000..ee5457a --- /dev/null +++ b/Tests/RunCMake/CMP0219/NEW-calls-OLD.cmake
@@ -0,0 +1,12 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0219 OLD) +macro(cmp0219_old_capture) + set(cmp0219_old_argn "${ARGN}") +endmacro() +cmake_policy(POP) + +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +cmp0219_old_capture(HINTS "${cmp0219_path_native}") +cmp0219_assert_equal("${cmp0219_old_argn}" "HINTS;${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMP0219/OLD-calls-NEW-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMP0219/OLD-calls-NEW-result.txt
diff --git a/Tests/RunCMake/CMP0219/OLD-calls-NEW-stderr.txt b/Tests/RunCMake/CMP0219/OLD-calls-NEW-stderr.txt new file mode 100644 index 0000000..4116c67 --- /dev/null +++ b/Tests/RunCMake/CMP0219/OLD-calls-NEW-stderr.txt
@@ -0,0 +1 @@ +Invalid character escape '\\b'\.
diff --git a/Tests/RunCMake/CMP0219/OLD-calls-NEW.cmake b/Tests/RunCMake/CMP0219/OLD-calls-NEW.cmake new file mode 100644 index 0000000..b3fe04e --- /dev/null +++ b/Tests/RunCMake/CMP0219/OLD-calls-NEW.cmake
@@ -0,0 +1,11 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0219 NEW) +macro(cmp0219_new_capture) + set(cmp0219_new_argn "${ARGN}") +endmacro() +cmake_policy(POP) + +cmake_policy(SET CMP0219 OLD) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +cmp0219_new_capture(HINTS "${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CMP0219/ParseArguments-Semicolon.cmake b/Tests/RunCMake/CMP0219/ParseArguments-Semicolon.cmake new file mode 100644 index 0000000..cd0fb6f --- /dev/null +++ b/Tests/RunCMake/CMP0219/ParseArguments-Semicolon.cmake
@@ -0,0 +1,46 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") +cmake_policy(SET CMP0219 NEW) + +macro(cmp0219_parse_corner_case) + set(_options) + set(_one_value_args FOO) + set(_multi_value_args) + cmake_parse_arguments(cmp0219_q + "${_options}" + "${_one_value_args}" + "${_multi_value_args}" + "${ARGN}") + cmake_parse_arguments(cmp0219_u + "${_options}" + "${_one_value_args}" + "${_multi_value_args}" + ${ARGN}) +endmacro() + +cmp0219_parse_corner_case(FOO "foo\\;bar") + +cmp0219_assert_equal("${cmp0219_q_FOO}" "foo;bar") +cmp0219_assert_undefined(cmp0219_q_UNPARSED_ARGUMENTS) +cmp0219_assert_equal("${cmp0219_u_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") + +cmp0219_parse_corner_case(FOO "foo\;bar") + +cmp0219_assert_equal("${cmp0219_q_FOO}" "foo;bar") +cmp0219_assert_undefined(cmp0219_q_UNPARSED_ARGUMENTS) +cmp0219_assert_equal("${cmp0219_u_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") + +cmp0219_parse_corner_case(FOO "foo;bar") + +cmp0219_assert_equal("${cmp0219_q_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") +cmp0219_assert_equal("${cmp0219_u_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") + +cmp0219_parse_corner_case(FOO foo;bar) + +cmp0219_assert_equal("${cmp0219_q_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar") +cmp0219_assert_equal("${cmp0219_u_FOO}" "foo") +cmp0219_assert_equal("${cmp0219_u_UNPARSED_ARGUMENTS}" "bar")
diff --git a/Tests/RunCMake/CMP0219/ReturnAndPolicyPropagation.cmake b/Tests/RunCMake/CMP0219/ReturnAndPolicyPropagation.cmake new file mode 100644 index 0000000..a95a477 --- /dev/null +++ b/Tests/RunCMake/CMP0219/ReturnAndPolicyPropagation.cmake
@@ -0,0 +1,32 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +cmake_policy(PUSH) +cmake_policy(SET CMP0219 OLD) +macro(cmp0219_old_return) + set(cmp0219_old_return_argn "${ARGN}" PARENT_SCOPE) + return() +endmacro() + +macro(cmp0219_old_set_policy) + set(cmp0219_old_set_policy_argn "${ARGN}") + cmake_policy(SET CMP0210 OLD) +endmacro() +cmake_policy(POP) + +function(cmp0219_test_return) + set(cmp0219_return_state "before" PARENT_SCOPE) + cmp0219_old_return(HINTS "${cmp0219_path_native}") + set(cmp0219_return_state "after" PARENT_SCOPE) +endfunction() + +cmp0219_test_return() +cmp0219_assert_equal("${cmp0219_return_state}" "before") +cmp0219_assert_equal( + "${cmp0219_old_return_argn}" "HINTS;${cmp0219_path_native}") + +cmp0219_old_set_policy(HINTS "${cmp0219_path_native}") +cmake_policy(GET CMP0210 cmp0219_cmp0210_status) +cmp0219_assert_equal("${cmp0219_cmp0210_status}" "OLD") +cmp0219_assert_equal( + "${cmp0219_old_set_policy_argn}" "HINTS;${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CMP0219/RunCMakeTest.cmake b/Tests/RunCMake/CMP0219/RunCMakeTest.cmake new file mode 100644 index 0000000..07d63ca --- /dev/null +++ b/Tests/RunCMake/CMP0219/RunCMakeTest.cmake
@@ -0,0 +1,30 @@ +include(RunCMake) + +run_cmake_script(Basic-NEW) +run_cmake_script(Basic-OLD) +run_cmake_script(Escape2-NEW) +run_cmake_script(AllNEW-Forward) +run_cmake_script(NEW-calls-OLD) +run_cmake_script(OLD-calls-NEW) +run_cmake_script(MixedChain-OLD-NEWMiddle) +run_cmake_script(DynamicDispatch-NEW) +run_cmake_script(ParseArguments-Semicolon) +run_cmake_script(SemicolonEscape-QuotedUnquoted) +run_cmake_script(ReturnAndPolicyPropagation) +run_cmake_script(Warn-Unset-Macro) +run_cmake_script(Warn-Unset-Macro-Multi) +run_cmake_script(Warn-Unset-VariableWatch) +run_cmake_script(VariableWatch-OLD) +run_cmake_script(VariableWatch-NEW) +run_cmake(CheckCSourceCompiles-OLD) +run_cmake(CheckCSourceCompiles-NEW) +run_cmake_with_options(DependencyProviderMacro-OLD + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=${RunCMake_SOURCE_DIR}/DependencyProviderMacro-TopInclude.cmake" + -D "CMAKE_POLICY_DEFAULT_CMP0219=OLD" +) +run_cmake_with_options(DependencyProviderMacro-NEW + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=${RunCMake_SOURCE_DIR}/DependencyProviderMacro-TopInclude.cmake" + -D "CMAKE_POLICY_DEFAULT_CMP0219=NEW" +) +run_cmake(Defer-EndPolicy-NEW) +run_cmake(Defer-EndPolicy-OLD)
diff --git a/Tests/RunCMake/CMP0219/SemicolonEscape-QuotedUnquoted.cmake b/Tests/RunCMake/CMP0219/SemicolonEscape-QuotedUnquoted.cmake new file mode 100644 index 0000000..da46a71 --- /dev/null +++ b/Tests/RunCMake/CMP0219/SemicolonEscape-QuotedUnquoted.cmake
@@ -0,0 +1,62 @@ +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +function(cmp0219_assert_encoded actual expected) + string(REPLACE "\\" "<BS>" cmp0219_encoded "${actual}") + string(REPLACE ";" "<SC>" cmp0219_encoded "${cmp0219_encoded}") + cmp0219_assert_equal("${cmp0219_encoded}" "${expected}") +endfunction() + +function(cmp0219_sink out_prefix) + math(EXPR cmp0219_sink_argc "${ARGC} - 1") + set(${out_prefix}_argc "${cmp0219_sink_argc}" PARENT_SCOPE) + if(cmp0219_sink_argc GREATER 0) + set(${out_prefix}_arg0 "${ARGV1}" PARENT_SCOPE) + endif() + if(cmp0219_sink_argc GREATER 1) + set(${out_prefix}_arg1 "${ARGV2}" PARENT_SCOPE) + endif() +endfunction() + +macro(cmp0219_semicolon_probe prefix value) + set(${prefix}_named_q "${value}") + set(${prefix}_named_u ${value}) + set(${prefix}_argv1_q "${ARGV1}") + set(${prefix}_argv1_u ${ARGV1}) + set(${prefix}_argn_q "${ARGN}") + set(${prefix}_argn_u ${ARGN}) + + cmp0219_sink("${prefix}_sink_named_q" "${value}") + cmp0219_sink("${prefix}_sink_named_u" ${value}) + cmp0219_sink("${prefix}_sink_argn_q" "${ARGN}") + cmp0219_sink("${prefix}_sink_argn_u" ${ARGN}) +endmacro() + +function(cmp0219_run_semicolon_escape_mode mode) + cmake_policy(SET CMP0219 "${mode}") + + # Value passed with a quoted argument keeps '\;' in textual substitutions + # until an unquoted use decodes it. + cmp0219_semicolon_probe("${mode}_quoted" "foo\\;bar" "foo\\;bar") + cmp0219_assert_encoded("${${mode}_quoted_named_q}" "foo<BS><SC>bar") + cmp0219_assert_encoded("${${mode}_quoted_named_u}" "foo<SC>bar") + cmp0219_assert_equal("${${mode}_quoted_sink_named_q_argc}" "1") + cmp0219_assert_equal("${${mode}_quoted_sink_named_u_argc}" "1") + cmp0219_assert_equal("${${mode}_quoted_sink_argn_q_argc}" "1") + cmp0219_assert_equal("${${mode}_quoted_sink_argn_u_argc}" "1") + + # Value passed with an unquoted argument uses '\;' to keep one argument at + # the call site, but unquoted forwarding splits at ';' in the macro body. + cmp0219_semicolon_probe("${mode}_unquoted" foo\;bar foo\;bar) + cmp0219_assert_encoded("${${mode}_unquoted_named_q}" "foo<SC>bar") + cmp0219_assert_equal("${${mode}_unquoted_sink_named_q_argc}" "1") + cmp0219_assert_equal("${${mode}_unquoted_sink_named_u_argc}" "2") + cmp0219_assert_encoded("${${mode}_unquoted_sink_named_u_arg0}" "foo") + cmp0219_assert_encoded("${${mode}_unquoted_sink_named_u_arg1}" "bar") + cmp0219_assert_equal("${${mode}_unquoted_sink_argn_q_argc}" "1") + cmp0219_assert_equal("${${mode}_unquoted_sink_argn_u_argc}" "2") + cmp0219_assert_encoded("${${mode}_unquoted_sink_argn_u_arg0}" "foo") + cmp0219_assert_encoded("${${mode}_unquoted_sink_argn_u_arg1}" "bar") +endfunction() + +cmp0219_run_semicolon_escape_mode(OLD) +cmp0219_run_semicolon_escape_mode(NEW)
diff --git a/Tests/RunCMake/CMP0219/VariableWatch-NEW.cmake b/Tests/RunCMake/CMP0219/VariableWatch-NEW.cmake new file mode 100644 index 0000000..2874ab7 --- /dev/null +++ b/Tests/RunCMake/CMP0219/VariableWatch-NEW.cmake
@@ -0,0 +1,11 @@ +cmake_policy(SET CMP0219 NEW) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +function(cmp0219_watch_callback variable access value current_list_file stack) + set(cmp0219_watch_value "${value}" PARENT_SCOPE) +endfunction() + +variable_watch(cmp0219_watched cmp0219_watch_callback) +set(cmp0219_watched "${cmp0219_path_native}") + +cmp0219_assert_equal("${cmp0219_watch_value}" "${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMP0219/VariableWatch-OLD-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMP0219/VariableWatch-OLD-result.txt
diff --git a/Tests/RunCMake/CMP0219/VariableWatch-OLD-stderr.txt b/Tests/RunCMake/CMP0219/VariableWatch-OLD-stderr.txt new file mode 100644 index 0000000..4116c67 --- /dev/null +++ b/Tests/RunCMake/CMP0219/VariableWatch-OLD-stderr.txt
@@ -0,0 +1 @@ +Invalid character escape '\\b'\.
diff --git a/Tests/RunCMake/CMP0219/VariableWatch-OLD.cmake b/Tests/RunCMake/CMP0219/VariableWatch-OLD.cmake new file mode 100644 index 0000000..794c0dd --- /dev/null +++ b/Tests/RunCMake/CMP0219/VariableWatch-OLD.cmake
@@ -0,0 +1,9 @@ +cmake_policy(SET CMP0219 OLD) +include("${CMAKE_CURRENT_LIST_DIR}/CMP0219-helpers.cmake") + +function(cmp0219_watch_callback variable access value current_list_file stack) + set(cmp0219_watch_value "${value}" PARENT_SCOPE) +endfunction() + +variable_watch(cmp0219_watched cmp0219_watch_callback) +set(cmp0219_watched "${cmp0219_path_native}")
diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi-stderr.txt b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi-stderr.txt new file mode 100644 index 0000000..f473566 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi-stderr.txt
@@ -0,0 +1,22 @@ +CMake Warning \(policy\) at .*/Warn-Unset-Macro-Multi\.cmake:[0-9]+ \(cmp0219_warn_macro\): + Policy CMP0219 is not set: Macro invocations preserve backslashes in + arguments\. Run "cmake --help-policy CMP0219" for policy details\. Use the + cmake_policy command to set the policy and suppress this warning\. + + Command "cmp0219_warn_macro" called with arguments containing backslashes\. + + Since the policy is not set, backslashes in the arguments: + + "prefix\\tsuffix" + "left\\;right" + + will be interpreted as escape sequences for compatibility\. + + Set the policy to NEW to instead pass + + "prefix\\\\tsuffix" + "left\\\\;right" + + so that argument parsing will preserve the original values\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi.cmake b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi.cmake new file mode 100644 index 0000000..1fae871 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-Multi.cmake
@@ -0,0 +1,9 @@ +set(cmp0219_warn_value1 "prefix\\tsuffix") +set(cmp0219_warn_value2 "left\\;right") + +macro(cmp0219_warn_macro arg1 arg2) + set(cmp0219_warn_seen1 "${arg1}") + set(cmp0219_warn_seen2 "${arg2}") +endmacro() + +cmp0219_warn_macro("${cmp0219_warn_value1}" "${cmp0219_warn_value2}")
diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-Macro-stderr.txt b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-stderr.txt new file mode 100644 index 0000000..ac462cf --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-Macro-stderr.txt
@@ -0,0 +1,20 @@ +CMake Warning \(policy\) at .*/Warn-Unset-Macro\.cmake:[0-9]+ \(cmp0219_warn_macro\): + Policy CMP0219 is not set: Macro invocations preserve backslashes in + arguments\. Run "cmake --help-policy CMP0219" for policy details\. Use the + cmake_policy command to set the policy and suppress this warning\. + + Command "cmp0219_warn_macro" called with arguments containing backslashes\. + + Since the policy is not set, backslashes in the arguments: + + "prefix\\tsuffix" + + will be interpreted as escape sequences for compatibility\. + + Set the policy to NEW to instead pass + + "prefix\\\\tsuffix" + + so that argument parsing will preserve the original values\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-Macro.cmake b/Tests/RunCMake/CMP0219/Warn-Unset-Macro.cmake new file mode 100644 index 0000000..ec7d7a1 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-Macro.cmake
@@ -0,0 +1,8 @@ +set(cmp0219_warn_value "prefix\\tsuffix") + +macro(cmp0219_warn_macro value) + set(cmp0219_warn_seen "${value}") +endmacro() + +cmp0219_warn_macro("${cmp0219_warn_value}") +cmp0219_warn_macro("${cmp0219_warn_value}")
diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch-stderr.txt b/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch-stderr.txt new file mode 100644 index 0000000..1c1c396 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch-stderr.txt
@@ -0,0 +1,20 @@ +CMake Warning \(policy\) at .*/Warn-Unset-VariableWatch\.cmake:[0-9]+ \(set\): + Policy CMP0219 is not set: Macro invocations preserve backslashes in + arguments\. Run "cmake --help-policy CMP0219" for policy details\. Use the + cmake_policy command to set the policy and suppress this warning\. + + Command "cmp0219_warn_watch" called with arguments containing backslashes\. + + Since the policy is not set, backslashes in the arguments: + + "prefix\\tsuffix" + + will be interpreted as escape sequences for compatibility\. + + Set the policy to NEW to instead pass + + "prefix\\\\tsuffix" + + so that argument parsing will preserve the original values\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch.cmake b/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch.cmake new file mode 100644 index 0000000..f5898e8 --- /dev/null +++ b/Tests/RunCMake/CMP0219/Warn-Unset-VariableWatch.cmake
@@ -0,0 +1,6 @@ +function(cmp0219_warn_watch variable access value current_list_file stack) +endfunction() + +variable_watch(cmp0219_warned cmp0219_warn_watch) +set(cmp0219_warned "prefix\\tsuffix") +set(cmp0219_warned "prefix\\tsuffix")
diff --git a/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stderr.txt b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stderr.txt index dce713c..ec2a3e8 100644 --- a/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stderr.txt +++ b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stderr.txt
@@ -1,9 +1,10 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/CMakeDependentOption\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/CMakeDependentOption\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0127 is not set: cmake_dependent_option\(\) supports full Condition Syntax\. Run "cmake --help-policy CMP0127" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): [^ ]*/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN\.cmake:[0-9]+ \(cmake_dependent_option\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt index 1953091..1a68066 100644 --- a/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt +++ b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at [^ +^CMake Warning \(deprecated\) at [^ ]*/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0127 will be removed from a future version of CMake\. @@ -6,4 +6,6 @@ The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 4620d69..e6f0148 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt
@@ -139,7 +139,8 @@ add_RunCMake_test(CMP0116) endif() add_RunCMake_test(CMP0118) -add_RunCMake_test(CMP0119 -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}) +add_RunCMake_test(CMP0119 -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) add_RunCMake_test(CMP0121) if (CMAKE_SYSTEM_NAME MATCHES "(Linux|Darwin)") add_RunCMake_test(CMP0125 -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX} @@ -180,11 +181,18 @@ add_RunCMake_test(CMP0171) add_RunCMake_test(CMP0173) add_RunCMake_test(CMP0187) +if(WIN32) + add_RunCMake_test(CMP0212) +endif() +add_RunCMake_test(CMP0217) +add_RunCMake_test(CMP0219) if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") add_RunCMake_test(CMP0194 -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) endif() +add_RunCMake_test(CMP0218) + # The test for Policy 65 requires the use of the # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode # generators ignore. The policy will have no effect on those generators. @@ -262,9 +270,17 @@ if(DEFINED CMake_TEST_CUDA) list(APPEND NinjaMultiConfig_ARGS -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) endif() - add_RunCMake_test(NinjaMultiConfig) - set_property(TEST RunCMake.NinjaMultiConfig APPEND - PROPERTY LABELS "CUDA") + function(add_RunCMake_NinjaMultiConfig_test variant) + add_RunCMake_test(NinjaMultiConfig${variant} + TEST_DIR NinjaMultiConfig + ${NinjaMultiConfig_ARGS} + -DCMake_TEST_NinjaMultiConfig_VARIANT=${variant}) + set_property(TEST RunCMake.NinjaMultiConfig${variant} APPEND + PROPERTY LABELS "CUDA" "NinjaMultiConfig") + endfunction() + add_RunCMake_NinjaMultiConfig_test(Simple) + add_RunCMake_NinjaMultiConfig_test(CustomCommands) + add_RunCMake_NinjaMultiConfig_test("") add_RunCMake_test(NinjaPrivateDeps -DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION} -DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig}) @@ -408,7 +424,9 @@ add_RunCMake_test(CpsExportImportBuild) add_RunCMake_test(CpsExportImportInstall) add_RunCMake_test(CpsMapConfig) +add_RunCMake_test(create_test_sourcelist) add_RunCMake_test(DisallowedCommands) +add_RunCMake_test(discover_tests) if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja|FASTBuild") add_RunCMake_test(ExportCompileCommands) endif() @@ -429,8 +447,22 @@ -DCMAKE_CXX_SIMULATE_ID=${CMAKE_CXX_SIMULATE_ID} -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}) if(CMAKE_GENERATOR MATCHES "Make|Ninja|FASTBuild") - add_RunCMake_test(Instrumentation -DPython_EXECUTABLE=${Python_EXECUTABLE} - -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}) + function(add_RunCMake_Instrumentation_test variant) + add_RunCMake_test(Instrumentation${variant} + TEST_DIR Instrumentation + -DPython_EXECUTABLE=${Python_EXECUTABLE} + -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION} + -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA} + -DCMake_TEST_INSTRUMENTATION_VARIANT=${variant} + ) + set_property(TEST RunCMake.Instrumentation${variant} APPEND PROPERTY + LABELS "Instrumentation" + ) + endfunction() + add_RunCMake_Instrumentation_test("") + add_RunCMake_Instrumentation_test(MakeProgram) endif() add_RunCMake_test(ConfigDir) if(CMake_TEST_FindPython2) @@ -536,6 +568,11 @@ add_RunCMake_test(GenEx-FILE_SET_PROPERTY) add_RunCMake_test(GenEx-TARGET_PROPERTY) add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS) +list(LENGTH CMAKE_OSX_ARCHITECTURES _num_archs) +if(NOT (XCODE AND _num_archs GREATER 1)) + add_RunCMake_test(GenEx-TARGET_OBJECTS -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) + set_property(TEST RunCMake.GenEx-TARGET_OBJECTS APPEND PROPERTY LABELS "CUDA") +endif() add_RunCMake_test(GenEx-STRING) add_RunCMake_test(GenEx-PATH) add_RunCMake_test(GenEx-PATH_EQUAL) @@ -607,11 +644,21 @@ add_RunCMake_test(StandardLinkDirectories) add_RunCMake_test(Swift -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DCMake_TEST_Swift=${CMake_TEST_Swift} + -DCMAKE_Swift_COMPILER_VERSION=${CMAKE_Swift_COMPILER_VERSION} -DXCODE_VERSION=${XCODE_VERSION}) add_RunCMake_test(TargetArtifacts -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) add_RunCMake_test(TargetObjects) add_RunCMake_test(TargetProperties) +add_RunCMake_test(FileSetProperties) +add_RunCMake_test(FileSet-SOURCES -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION} + -DMSVC_VERSION=${MSVC_VERSION}) add_RunCMake_test(ToolchainFile) +add_RunCMake_test(ToolchainSkipsChecks + -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID} + -DCMAKE_Fortran_COMPILER_ID=${CMAKE_Fortran_COMPILER_ID} +) if(NOT CMake_TEST_EXTERNAL_CMAKE) set(Tutorial_ARGS TEST_DIR Tutorial -DTutorial_SOURCE_DIR=${CMake_SOURCE_DIR}/Help/guide/tutorial) @@ -637,6 +684,8 @@ add_RunCMake_test(CompileFeatures -DCMake_NO_C_STANDARD=${CMake_NO_C_STANDARD} -DCMake_NO_CXX_STANDARD=${CMake_NO_CXX_STANDARD}) add_RunCMake_test(Policy) add_RunCMake_test(PolicyScope) +add_RunCMake_test(Diagnostics) +add_RunCMake_test(DiagnosticScope) add_RunCMake_test(WriteBasicConfigVersionFile) add_RunCMake_test(WriteCompilerDetectionHeader) add_RunCMake_test(SourceProperties) @@ -688,6 +737,9 @@ if(DEFINED CMake_TEST_LOCALE_CHARSET) list(APPEND cmake_host_system_information_ARGS -DCMake_TEST_LOCALE_CHARSET=${CMake_TEST_LOCALE_CHARSET}) endif() +if(DEFINED CMake_TEST_LOCALE_C_UTF8) + list(APPEND cmake_host_system_information_ARGS -DCMake_TEST_LOCALE_C_UTF8=${CMake_TEST_LOCALE_C_UTF8}) +endif() if(CMake_TEST_MSYSTEM_PREFIX) list(APPEND cmake_host_system_information_ARGS -DCMake_TEST_MSYSTEM_PREFIX=${CMake_TEST_MSYSTEM_PREFIX}) endif() @@ -749,7 +801,8 @@ add_RunCMake_test(file-STRINGS) add_RunCMake_test(find_file -DMINGW=${MINGW}) add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS} -DMSVC=${MSVC}) -add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS}) +add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS} + -DCMake_TEST_FILESYSTEM_CASE=${CMake_TEST_FILESYSTEM_CASE}) add_RunCMake_test(find_package-CPS) add_RunCMake_test(find_path -DMINGW=${MINGW}) add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) @@ -764,11 +817,14 @@ add_RunCMake_test(INSTALL_OBJECT_NAME_STRATEGY) add_RunCMake_test(INSTALL_OBJECT_ONLY_USE_DESTINATION) add_RunCMake_test(if -DMSYS=${MSYS}) -add_RunCMake_test(include) +add_RunCMake_test(include -DCMake_TEST_FILESYSTEM_CASE=${CMake_TEST_FILESYSTEM_CASE}) add_RunCMake_test(include_directories) add_RunCMake_test(include_guard) add_RunCMake_test(list) add_RunCMake_test(load_cache) +if(DEFINED CMake_TEST_MATH_OVERFLOW) + list(APPEND math_ARGS -DCMake_TEST_MATH_OVERFLOW=${CMake_TEST_MATH_OVERFLOW}) +endif() add_RunCMake_test(math) add_RunCMake_test(message) add_RunCMake_test(option) @@ -1008,7 +1064,11 @@ add_RunCMake_test(LinkLibrariesProcessing) add_RunCMake_test(LinkLibrariesStrategy) add_RunCMake_test(LinkFlags -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}) -add_RunCMake_test(File_Archive -DPython_EXECUTABLE=${Python_EXECUTABLE}) +if(DEFINED CMake_TEST_LOCALE_CHARSET) + list(APPEND File_Archive_ARGS -DCMake_TEST_LOCALE_CHARSET=${CMake_TEST_LOCALE_CHARSET}) +endif() +list(APPEND File_Archive_ARGS -DPython_EXECUTABLE=${Python_EXECUTABLE}) +add_RunCMake_test(File_Archive) add_RunCMake_test(File_Configure) add_RunCMake_test(File_Generate) add_RunCMake_test(ExportWithoutLanguage) @@ -1105,6 +1165,9 @@ if(CMake_TEST_LibArchive_VERSION) list(APPEND CommandLineTar_ARGS -DCMake_TEST_LibArchive_VERSION=${CMake_TEST_LibArchive_VERSION}) endif() +if(DEFINED CMake_TEST_LOCALE_CHARSET) + list(APPEND CommandLineTar_ARGS -DCMake_TEST_LOCALE_CHARSET=${CMake_TEST_LOCALE_CHARSET}) +endif() list(APPEND CommandLineTar_ARGS -DPython_EXECUTABLE=${Python_EXECUTABLE}) add_RunCMake_test(CommandLineTar) @@ -1149,6 +1212,9 @@ endif() add_RunCMake_test(FetchContent) add_RunCMake_test(FetchContent_find_package) +if(CMake_TEST_CLANG_COVERAGE) + add_RunCMake_test(CTestCoverage -DCMake_TEST_CLANG_COVERAGE=${CMake_TEST_CLANG_COVERAGE}) +endif() set(CTestCommandLine_ARGS -DPython_EXECUTABLE=${Python_EXECUTABLE} -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA} @@ -1159,6 +1225,9 @@ add_executable(print_stdin print_stdin.c) add_RunCMake_test(CTestCommandLine -DTEST_PRINT_STDIN=$<TARGET_FILE:print_stdin>) add_RunCMake_test(CacheNewline) +add_RunCMake_test(CacheVarHelpFallback) +add_RunCMake_test(CacheVarHelpCoverage + -DCMake_SOURCE_DIR=${CMake_SOURCE_DIR}) # Only run this test on unix platforms that support # symbolic links if(UNIX) @@ -1209,8 +1278,9 @@ if(UNIX AND NOT CYGWIN) execute_process(COMMAND ldd --help OUTPUT_VARIABLE LDD_HELP - ERROR_VARIABLE LDD_ERR) - if("${LDD_HELP}" MATCHES + ERROR_VARIABLE LDD_ERR + RESULT_VARIABLE LDD_RESULT) + if(LDD_RESULT EQUAL 0 AND "${LDD_HELP}" MATCHES "(-r, --function-relocs.*process data and function relocations.*-u, --unused.*print unused direct dependencies)") add_RunCMake_test(LinkWhatYouUse) endif() @@ -1332,7 +1402,9 @@ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "e2k" AND NOT DEFINED CMake_TEST_E2K_BROKEN_LIBC) # Exclude tests that fail due to a broken libc version on Elbrus. find_program(DPKG_QUERY "dpkg-query" ) - execute_process(COMMAND "${DPKG_QUERY}" "-f" "\${Version}" "-W" "glibc" OUTPUT_VARIABLE LIBC_VERSION) + execute_process(COMMAND "${DPKG_QUERY}" "-f" "\${Version}" "-W" "glibc" + OUTPUT_VARIABLE LIBC_VERSION + RESULT_VARIABLE _result_libc) if(LIBC_VERSION MATCHES "2.29-25.*") list(REMOVE_ITEM cpack_tests DEB.DEFAULT_PERMISSIONS @@ -1505,7 +1577,7 @@ -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA} ) -add_RunCMake_test(VerifyHeaderSets) +add_RunCMake_test(VerifyHeaderSets -DCMake_TEST_OBJC=${CMake_TEST_OBJC}) add_RunCMake_test(set_tests_properties) add_RunCMake_test(CMakeListFileName)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-result.txt
diff --git a/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-stderr.txt new file mode 100644 index 0000000..e8d349d --- /dev/null +++ b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey: +Invalid build preset: "EmptyEnvKey": Empty environment variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey.json.in b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey.json.in new file mode 100644 index 0000000..7e9932b --- /dev/null +++ b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey.json.in
@@ -0,0 +1,11 @@ +{ + "version": 2, + "buildPresets": [ + { + "name": "EmptyEnvKey", + "environment": { + "": "value" + } + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/BuildInvalidInheritance-result.txt
diff --git a/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-stderr.txt new file mode 100644 index 0000000..e9ba8c2 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/BuildInvalidInheritance: +Invalid build preset: "InvalidInheritance": Could not find inherited preset "NoExist"$
diff --git a/Tests/RunCMake/CMakePresets/BuildInvalidInheritance.json.in b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance.json.in new file mode 100644 index 0000000..2dd5d81 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance.json.in
@@ -0,0 +1,11 @@ +{ + "version": 2, + "buildPresets": [ + { + "name": "InvalidInheritance", + "inherits": [ + "NoExist" + ] + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/CMakeLists.txt.in b/Tests/RunCMake/CMakePresets/CMakeLists.txt.in index 67c2d48..039c8e2 100644 --- a/Tests/RunCMake/CMakePresets/CMakeLists.txt.in +++ b/Tests/RunCMake/CMakePresets/CMakeLists.txt.in
@@ -1,4 +1,4 @@ cmake_minimum_required(VERSION 3.18) project(${RunCMake_TEST} NONE) set(RunCMake_SOURCE_DIR [==[@RunCMake_SOURCE_DIR@]==]) -include("${RunCMake_SOURCE_DIR}/${RunCMake_TEST}.cmake") +include("${RunCMake_SOURCE_DIR}/${RunCMake_TEST}.cmake" OPTIONAL)
diff --git a/Tests/RunCMake/CMakePresets/CMakePresets.json.in b/Tests/RunCMake/CMakePresets/CMakePresets.json.in index c6f943c..70bdd58 100644 --- a/Tests/RunCMake/CMakePresets/CMakePresets.json.in +++ b/Tests/RunCMake/CMakePresets/CMakePresets.json.in
@@ -126,6 +126,22 @@ "binaryDir": "build" }, { + "name": "GoodSourceSlash", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "build", + "cacheVariables": { + "TEST_SOURCE_DIR": { + "type": "PATH", + "value": "${sourceDir}" + }, + "TEST_SOURCE_PARENT_DIR": { + "type": "PATH", + "value": "${sourceParentDir}" + }, + "TEST_SOURCE_DIR_NAME": "${sourceDirName}" + } + }, + { "name": "Good Spaces", "generator": "@RunCMake_GENERATOR@", "binaryDir": "${sourceDir}/build",
diff --git a/Tests/RunCMake/CMakePresets/CommentValid.cmake b/Tests/RunCMake/CMakePresets/CommentValid.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/CommentValid.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/CommentValidFull.cmake b/Tests/RunCMake/CMakePresets/CommentValidFull.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/CommentValidFull.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/CommentValidOldSchema.cmake b/Tests/RunCMake/CMakePresets/CommentValidOldSchema.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/CommentValidOldSchema.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt index 2d5b477..d7366f7 100644 --- a/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt +++ b/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/CyclicInheritance0: -Cyclic preset inheritance for preset "CyclicInheritance0"$ +Cyclic preset inheritance for configure preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt index 596fcf8..e2d9d11 100644 --- a/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt +++ b/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/CyclicInheritance1: -Cyclic preset inheritance for preset "CyclicInheritance0"$ +Cyclic preset inheritance for configure preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt index a6b83c8..99fe0bd 100644 --- a/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt +++ b/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/CyclicInheritance2: -Cyclic preset inheritance for preset "CyclicInheritance0"$ +Cyclic preset inheritance for configure preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/Debug.json.in b/Tests/RunCMake/CMakePresets/Debug.json.in index 500700e..5b201f0 100644 --- a/Tests/RunCMake/CMakePresets/Debug.json.in +++ b/Tests/RunCMake/CMakePresets/Debug.json.in
@@ -14,6 +14,19 @@ "find": true, "tryCompile": true } + }, + { + "name": "DebugInherit", + "inherits": "Debug" + }, + { + "name": "DebugInheritOverride", + "inherits": "Debug", + "debug": { + "output": false, + "find": false, + "tryCompile": false + } } ] }
diff --git a/Tests/RunCMake/CMakePresets/DebugInherit-stderr.txt b/Tests/RunCMake/CMakePresets/DebugInherit-stderr.txt new file mode 100644 index 0000000..d30dc58 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/DebugInherit-stderr.txt
@@ -0,0 +1,2 @@ + find_package considered the following locations for + ThisPackageHopefullyDoesNotExist's Config module:
diff --git a/Tests/RunCMake/CMakePresets/DebugInherit-stdout.txt b/Tests/RunCMake/CMakePresets/DebugInherit-stdout.txt new file mode 100644 index 0000000..6544f95 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/DebugInherit-stdout.txt
@@ -0,0 +1,4 @@ +-- Generating [^ +]*/Tests/RunCMake/CMakePresets/DebugInherit/build + Called from: \[1\][^ +]*/Tests/RunCMake/CMakePresets/DebugInherit/CMakeLists\.txt
diff --git a/Tests/RunCMake/CMakePresets/DebugInherit.cmake b/Tests/RunCMake/CMakePresets/DebugInherit.cmake new file mode 100644 index 0000000..507eeb9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/DebugInherit.cmake
@@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Debug.cmake)
diff --git a/Tests/RunCMake/CMakePresets/DebugInheritOverride-stdout.txt b/Tests/RunCMake/CMakePresets/DebugInheritOverride-stdout.txt new file mode 100644 index 0000000..f32056a --- /dev/null +++ b/Tests/RunCMake/CMakePresets/DebugInheritOverride-stdout.txt
@@ -0,0 +1,2 @@ +-- Configuring done \([0-9]+\.[0-9]s\) +-- Generating done \([0-9]+\.[0-9]s\)
diff --git a/Tests/RunCMake/CMakePresets/DebugInheritOverride.cmake b/Tests/RunCMake/CMakePresets/DebugInheritOverride.cmake new file mode 100644 index 0000000..575e44f --- /dev/null +++ b/Tests/RunCMake/CMakePresets/DebugInheritOverride.cmake
@@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/NoDebug.cmake)
diff --git a/Tests/RunCMake/CMakePresets/DiagnosticOrder.cmake b/Tests/RunCMake/CMakePresets/DiagnosticOrder.cmake new file mode 100644 index 0000000..463549e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/DiagnosticOrder.cmake
@@ -0,0 +1,6 @@ +cmake_diagnostic(GET CMD_DEPRECATED action) +if(NOT "${action}" STREQUAL IGNORE) + message(SEND_ERROR + "wrong action for diagnostic CMD_DEPRECATED" + " (expected 'IGNORE', actual '${action}')") +endif()
diff --git a/Tests/RunCMake/CMakePresets/DocumentationExampleListAllPresets-stdout.txt b/Tests/RunCMake/CMakePresets/DocumentationExampleListAllPresets-stdout.txt index 57b714d..b3cda37 100644 --- a/Tests/RunCMake/CMakePresets/DocumentationExampleListAllPresets-stdout.txt +++ b/Tests/RunCMake/CMakePresets/DocumentationExampleListAllPresets-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "default" ?- Default Config "ninja-multi" ?- Ninja Multi-Config(
diff --git a/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt index cb9e545..d6f402f 100644 --- a/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt +++ b/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/EmptyCacheKey: -Invalid preset: "EmptyCacheKey"$ +Invalid configure preset: "EmptyCacheKey": Empty cache variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt index bc0f866..a357267 100644 --- a/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt +++ b/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/EmptyEnvKey: -Invalid preset: "EmptyEnvKey"$ +Invalid configure preset: "EmptyEnvKey": Empty environment variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt b/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt index 8e19c69..eb658ce 100644 --- a/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt +++ b/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt
@@ -1,4 +1,4 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/EnvCycle: -Invalid preset: "EnvCycle" +Invalid configure preset: "EnvCycle": Invalid macro expansion Invalid macro expansion in "EnvCycle"$
diff --git a/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt new file mode 100644 index 0000000..6d9315e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt
@@ -0,0 +1,5 @@ +CMake Warning \(unused-cli\): + Manually-specified variables were not used by the project: + + RunCMake_GENERATOR + UNUSED_VARIABLE$
diff --git a/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake new file mode 100644 index 0000000..d6fe2ff --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake
@@ -0,0 +1,6 @@ +cmake_diagnostic(GET CMD_INSTALL_ABSOLUTE_DESTINATION action) +if(NOT "${action}" STREQUAL SEND_ERROR) + message(SEND_ERROR + "wrong action for diagnostic CMD_INSTALL_ABSOLUTE_DESTINATION" + " (expected 'SEND_ERROR', actual '${action}')") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported-result.txt
diff --git a/Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported-stderr.txt new file mode 100644 index 0000000..b75a5d6 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported: +File version must be 12 or higher for errors\.author support$
diff --git a/Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported.json.in b/Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported.json.in new file mode 100644 index 0000000..11b36bc --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorAuthorNotSupported.json.in
@@ -0,0 +1,13 @@ +{ + "version": 11, + "configurePresets": [ + { + "name": "ErrorAuthorNotSupported", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "errors": { + "author": true + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/ErrorDeprecated-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorDeprecated-stderr.txt index 964a504..2b8c98c 100644 --- a/Tests/RunCMake/CMakePresets/ErrorDeprecated-stderr.txt +++ b/Tests/RunCMake/CMakePresets/ErrorDeprecated-stderr.txt
@@ -1,7 +1,9 @@ -^CMake Deprecation Error at [^ +^CMake Error \(deprecated\) at [^ ]*/Tests/RunCMake/CMakePresets/WarningsBase\.cmake:[0-9]+ \(message\): Deprecation warning Call Stack \(most recent call first\): [^ ]*/Tests/RunCMake/CMakePresets/ErrorDeprecated\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=deprecated to suppress it\.$
diff --git a/Tests/RunCMake/CMakePresets/ErrorDev-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorDev-stderr.txt index f76478c..c55b91e 100644 --- a/Tests/RunCMake/CMakePresets/ErrorDev-stderr.txt +++ b/Tests/RunCMake/CMakePresets/ErrorDev-stderr.txt
@@ -1,8 +1,18 @@ -^CMake Error \(dev\) at [^ +^CMake Error \(author\) at [^ ]*/Tests/RunCMake/CMakePresets/WarningsBase\.cmake:[0-9]+ \(message\): Dev warning Call Stack \(most recent call first\): [^ ]*/Tests/RunCMake/CMakePresets/ErrorDev\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This error is for project developers\. Use -Wno-error=dev to suppress it\.$ +This error is for project developers\. Use -Wno-error=author to suppress it\. ++ +CMake Error \(deprecated\) at [^ +]*/Tests/RunCMake/CMakePresets/WarningsBase\.cmake:[0-9]+ \(message\): + Deprecation warning +Call Stack \(most recent call first\): + [^ +]*/Tests/RunCMake/CMakePresets/ErrorDev\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=deprecated to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/ErrorDevNotSupported-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/ErrorDevNotSupported-result.txt
diff --git a/Tests/RunCMake/CMakePresets/ErrorDevNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorDevNotSupported-stderr.txt new file mode 100644 index 0000000..7111cb7 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorDevNotSupported-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/ErrorDevNotSupported: +File version must be 11 or lower for errors\.dev support$
diff --git a/Tests/RunCMake/CMakePresets/ErrorDevNotSupported.json.in b/Tests/RunCMake/CMakePresets/ErrorDevNotSupported.json.in new file mode 100644 index 0000000..b2a6df2 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorDevNotSupported.json.in
@@ -0,0 +1,13 @@ +{ + "version": 12, + "configurePresets": [ + { + "name": "ErrorDevNotSupported", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "errors": { + "dev": true + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt index 5c02d5b..dd47dc7 100644 --- a/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt +++ b/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated: -Invalid preset: "ErrorNoWarningDeprecated"$ +Invalid configure preset: "ErrorNoWarningDeprecated"$
diff --git a/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt index 4640b1e..ca087b1 100644 --- a/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt +++ b/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/ErrorNoWarningDev: -Invalid preset: "ErrorNoWarningDev"$ +Invalid configure preset: "ErrorNoWarningDev"$
diff --git a/Tests/RunCMake/CMakePresets/ErrorUninitialized-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorUninitialized-stderr.txt new file mode 100644 index 0000000..6d9315e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorUninitialized-stderr.txt
@@ -0,0 +1,5 @@ +CMake Warning \(unused-cli\): + Manually-specified variables were not used by the project: + + RunCMake_GENERATOR + UNUSED_VARIABLE$
diff --git a/Tests/RunCMake/CMakePresets/ErrorUninitialized.cmake b/Tests/RunCMake/CMakePresets/ErrorUninitialized.cmake new file mode 100644 index 0000000..12a28a5 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorUninitialized.cmake
@@ -0,0 +1,6 @@ +cmake_diagnostic(GET CMD_UNINITIALIZED action) +if(NOT "${action}" STREQUAL SEND_ERROR) + message(SEND_ERROR + "wrong action for diagnostic CMD_UNINITIALIZED" + " (expected 'SEND_ERROR', actual '${action}')") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported-result.txt
diff --git a/Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported-stderr.txt new file mode 100644 index 0000000..9646315 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported: +File version must be 12 or higher for errors\.uninitialized support$
diff --git a/Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported.json.in b/Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported.json.in new file mode 100644 index 0000000..0f4ce21 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorUninitializedNotSupported.json.in
@@ -0,0 +1,13 @@ +{ + "version": 11, + "configurePresets": [ + { + "name": "ErrorUninitializedNotSupported", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "errors": { + "uninitialized": true + } + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/ErrorUnusedCli-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/ErrorUnusedCli-result.txt
diff --git a/Tests/RunCMake/CMakePresets/ErrorUnusedCli-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorUnusedCli-stderr.txt new file mode 100644 index 0000000..676a111 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorUnusedCli-stderr.txt
@@ -0,0 +1,7 @@ +CMake Error \(unused-cli\): + Manually-specified variables were not used by the project: + + RunCMake_GENERATOR + UNUSED_VARIABLE ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMakePresets/ErrorUnusedCli.cmake b/Tests/RunCMake/CMakePresets/ErrorUnusedCli.cmake new file mode 100644 index 0000000..a4dd3ba --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorUnusedCli.cmake
@@ -0,0 +1,6 @@ +cmake_diagnostic(GET CMD_UNUSED_CLI action) +if(NOT "${action}" STREQUAL SEND_ERROR) + message(SEND_ERROR + "wrong action for diagnostic CMD_UNUSED_CLI" + " (expected 'SEND_ERROR', actual '${action}')") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported-result.txt
diff --git a/Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported-stderr.txt new file mode 100644 index 0000000..07d3a13 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported: +File version must be 12 or higher for errors\.unusedCli support$
diff --git a/Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported.json.in b/Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported.json.in new file mode 100644 index 0000000..83c2964 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorUnusedCliNotSupported.json.in
@@ -0,0 +1,13 @@ +{ + "version": 11, + "configurePresets": [ + { + "name": "ErrorUnusedCliNotSupported", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "errors": { + "unusedCli": true + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/ExplicitNoTrace.cmake b/Tests/RunCMake/CMakePresets/ExplicitNoTrace.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/ExplicitNoTrace.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/FileDir.cmake b/Tests/RunCMake/CMakePresets/FileDir.cmake deleted file mode 100644 index c7298cd..0000000 --- a/Tests/RunCMake/CMakePresets/FileDir.cmake +++ /dev/null
@@ -1,3 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake) - -test_variable(TEST_FILE_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir")
diff --git a/Tests/RunCMake/CMakePresets/FileDir.json.in b/Tests/RunCMake/CMakePresets/FileDir.json.in index 899e5f3..b741853 100644 --- a/Tests/RunCMake/CMakePresets/FileDir.json.in +++ b/Tests/RunCMake/CMakePresets/FileDir.json.in
@@ -1,6 +1,15 @@ { "version": 4, "include": [ - "subdir/FileDir.json" + "@FileDir_include_subdir@" + ], + "configurePresets": [ + { + "name": "FileDirExt", + "inherits": "FileDir", + "cacheVariables": { + "PRESET": "FileDirExt" + } + } ] }
diff --git a/Tests/RunCMake/CMakePresets/FileDirv12.cmake b/Tests/RunCMake/CMakePresets/FileDirv12.cmake new file mode 100644 index 0000000..04fb6e9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/FileDirv12.cmake
@@ -0,0 +1,6 @@ +include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake) + +if(NOT "${PRESET}" STREQUAL "FileDirInclude") + test_variable(CMAKE_BINARY_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir/build") + test_variable(TEST_FILE_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir") +endif()
diff --git a/Tests/RunCMake/CMakePresets/FileDirv4.cmake b/Tests/RunCMake/CMakePresets/FileDirv4.cmake new file mode 100644 index 0000000..32f516c --- /dev/null +++ b/Tests/RunCMake/CMakePresets/FileDirv4.cmake
@@ -0,0 +1,9 @@ +include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake) + +if (PRESET STREQUAL "FileDir") + test_variable(CMAKE_BINARY_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir/build") + test_variable(TEST_FILE_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir") +elseif (PRESET STREQUAL "FileDirExt") + test_variable(CMAKE_BINARY_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/build") + test_variable(TEST_FILE_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}") +endif()
diff --git a/Tests/RunCMake/CMakePresets/GoodBOM.cmake b/Tests/RunCMake/CMakePresets/GoodBOM.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GoodBOM.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GoodNoArgs.cmake b/Tests/RunCMake/CMakePresets/GoodNoArgs.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GoodNoArgs.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GoodSourceSlash.cmake b/Tests/RunCMake/CMakePresets/GoodSourceSlash.cmake new file mode 100644 index 0000000..c9af430 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/GoodSourceSlash.cmake
@@ -0,0 +1,6 @@ +include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake) + +get_filename_component(_parent_dir "${CMAKE_SOURCE_DIR}" DIRECTORY) +test_variable(TEST_SOURCE_DIR "PATH" "${CMAKE_SOURCE_DIR}") +test_variable(TEST_SOURCE_PARENT_DIR "PATH" "${_parent_dir}") +test_variable(TEST_SOURCE_DIR_NAME "UNINITIALIZED" "GoodSourceSlash")
diff --git a/Tests/RunCMake/CMakePresets/GoodSpaces.cmake b/Tests/RunCMake/CMakePresets/GoodSpaces.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GoodSpaces.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GoodSpacesEq.cmake b/Tests/RunCMake/CMakePresets/GoodSpacesEq.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GoodSpacesEq.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GoodUserFromMain.cmake b/Tests/RunCMake/CMakePresets/GoodUserFromMain.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GoodUserFromMain.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GoodUserFromUser.cmake b/Tests/RunCMake/CMakePresets/GoodUserFromUser.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GoodUserFromUser.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GoodUserOnly.cmake b/Tests/RunCMake/CMakePresets/GoodUserOnly.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GoodUserOnly.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GoodWindowsBackslash.cmake b/Tests/RunCMake/CMakePresets/GoodWindowsBackslash.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GoodWindowsBackslash.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid.cmake b/Tests/RunCMake/CMakePresets/GraphvizValid.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GraphvizValid.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt b/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt index ba962b4..4a9f6e4 100644 --- a/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt +++ b/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt
@@ -1,5 +1,5 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/HighVersion: -CMakePresets\.json:2: Unrecognized "version" 1000: must be >=1 and <=11 +CMakePresets\.json:2: Unrecognized "version" 1000: must be >=1 and <=12 "version": 1000, \^$
diff --git a/Tests/RunCMake/CMakePresets/Include-stdout.txt b/Tests/RunCMake/CMakePresets/Include-stdout.txt index 6ba1170..e256057 100644 --- a/Tests/RunCMake/CMakePresets/Include-stdout.txt +++ b/Tests/RunCMake/CMakePresets/Include-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "IncludeUser" "IncludeUserCommon"
diff --git a/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt index f7e3461..357050f 100644 --- a/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt +++ b/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt
@@ -1,4 +1,4 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/IncludeCycle: -Cyclic include among preset files: [^ +Cyclic include among presets files: [^ ]*/CMakeUserPresets\.json$
diff --git a/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt index 85ddab1..c106f29 100644 --- a/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt +++ b/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt
@@ -1,4 +1,4 @@ CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/IncludeCycle3Files: -Cyclic include among preset files: [^ +Cyclic include among presets files: [^ ]*/CMakePresets\.json
diff --git a/Tests/RunCMake/CMakePresets/IncludeExpansion-stdout.txt b/Tests/RunCMake/CMakePresets/IncludeExpansion-stdout.txt index d3f1afc..facf2b6 100644 --- a/Tests/RunCMake/CMakePresets/IncludeExpansion-stdout.txt +++ b/Tests/RunCMake/CMakePresets/IncludeExpansion-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "Include" "IncludeCommon"$
diff --git a/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros-stdout.txt b/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros-stdout.txt index d3f1afc..facf2b6 100644 --- a/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros-stdout.txt +++ b/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "Include" "IncludeCommon"$
diff --git a/Tests/RunCMake/CMakePresets/IncludeOutsideProject.cmake b/Tests/RunCMake/CMakePresets/IncludeOutsideProject.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/IncludeOutsideProject.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/IncludeUserOutsideProject.cmake b/Tests/RunCMake/CMakePresets/IncludeUserOutsideProject.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/IncludeUserOutsideProject.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt index 216f308..a8eb597 100644 --- a/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt +++ b/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/InvalidInheritance: -Invalid preset: "InvalidInheritance"$ +Invalid configure preset: "InvalidInheritance": Could not find inherited preset "NoExist"$
diff --git a/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt index 86cd861..b948676 100644 --- a/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt +++ b/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt
@@ -1,4 +1,4 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/InvalidRegex: -Invalid preset: "InvalidRegex" +Invalid configure preset: "InvalidRegex": Invalid condition Invalid macro expansion in "InvalidRegex"$
diff --git a/Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild-stdout.txt b/Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild-stdout.txt index 5d978bf..96346d5 100644 --- a/Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "default"
diff --git a/Tests/RunCMake/CMakePresets/ListAllPresetsNoTest-stdout.txt b/Tests/RunCMake/CMakePresets/ListAllPresetsNoTest-stdout.txt index 71ce24a..2bf40c5 100644 --- a/Tests/RunCMake/CMakePresets/ListAllPresetsNoTest-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListAllPresetsNoTest-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "default"
diff --git a/Tests/RunCMake/CMakePresets/ListConfigurePresetsWorkingDir-stdout.txt b/Tests/RunCMake/CMakePresets/ListConfigurePresetsWorkingDir-stdout.txt index 97eedae..ae4188a 100644 --- a/Tests/RunCMake/CMakePresets/ListConfigurePresetsWorkingDir-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListConfigurePresetsWorkingDir-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming
diff --git a/Tests/RunCMake/CMakePresets/ListPresets-stdout.txt b/Tests/RunCMake/CMakePresets/ListPresets-stdout.txt index 97eedae..ae4188a 100644 --- a/Tests/RunCMake/CMakePresets/ListPresets-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListPresets-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming
diff --git a/Tests/RunCMake/CMakePresets/ListPresetsHidden-stderr.txt b/Tests/RunCMake/CMakePresets/ListPresetsHidden-stderr.txt index 1403814..5ccf749 100644 --- a/Tests/RunCMake/CMakePresets/ListPresetsHidden-stderr.txt +++ b/Tests/RunCMake/CMakePresets/ListPresetsHidden-stderr.txt
@@ -1,2 +1,2 @@ -^CMake Error: Cannot use hidden preset in [^ +^CMake Error: Cannot use hidden configure preset in [^ ]*/Tests/RunCMake/CMakePresets/ListPresetsHidden: "ListPresetsHidden"$
diff --git a/Tests/RunCMake/CMakePresets/ListPresetsHidden-stdout.txt b/Tests/RunCMake/CMakePresets/ListPresetsHidden-stdout.txt index 97eedae..ae4188a 100644 --- a/Tests/RunCMake/CMakePresets/ListPresetsHidden-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListPresetsHidden-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming
diff --git a/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stderr.txt b/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stderr.txt index eea1b99..1c9c30a 100644 --- a/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stderr.txt +++ b/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stderr.txt
@@ -1,2 +1,2 @@ -^CMake Error: No such preset in [^ +^CMake Error: No such configure preset in [^ ]*/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset: "ListPresetsNoSuchPreset"$
diff --git a/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stdout.txt b/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stdout.txt index 97eedae..ae4188a 100644 --- a/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming
diff --git a/Tests/RunCMake/CMakePresets/ListPresetsWorkingDir-stdout.txt b/Tests/RunCMake/CMakePresets/ListPresetsWorkingDir-stdout.txt index 97eedae..ae4188a 100644 --- a/Tests/RunCMake/CMakePresets/ListPresetsWorkingDir-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListPresetsWorkingDir-stdout.txt
@@ -1,5 +1,4 @@ -^Not searching for unused variables given on the command line\. -Available configure presets: +^Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming
diff --git a/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt b/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt index 5ed2857..880058f 100644 --- a/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt +++ b/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt
@@ -1,5 +1,5 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/LowVersion: -CMakePresets\.json:2: Unrecognized "version" 0: must be >=1 and <=11 +CMakePresets\.json:2: Unrecognized "version" 0: must be >=1 and <=12 "version": 0, \^
diff --git a/Tests/RunCMake/CMakePresets/MinimumRequiredEmpty.cmake b/Tests/RunCMake/CMakePresets/MinimumRequiredEmpty.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/MinimumRequiredEmpty.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/NoCommentValid.cmake b/Tests/RunCMake/CMakePresets/NoCommentValid.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/NoCommentValid.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake b/Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt index bae9794..ccd3d4c 100644 --- a/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt +++ b/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt
@@ -1,4 +1,4 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/NoPresetBinaryDir: Preset "NoPresetBinaryDir" missing field "binaryDir" -Invalid preset: "NoPresetBinaryDir"$ +Invalid configure preset: "NoPresetBinaryDir"$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/NoPresetFileArgument-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/NoPresetFileArgument-result.txt
diff --git a/Tests/RunCMake/CMakePresets/NoPresetFileArgument-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetFileArgument-stderr.txt new file mode 100644 index 0000000..29d37ad --- /dev/null +++ b/Tests/RunCMake/CMakePresets/NoPresetFileArgument-stderr.txt
@@ -0,0 +1,2 @@ +^CMake Error: No file specified for --presets-file +CMake Error: Run 'cmake --help' for all supported options\.$
diff --git a/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt index c7e5b5e..51558cd 100644 --- a/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt +++ b/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt
@@ -1,4 +1,4 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/NoPresetGenerator: Preset "NoPresetGenerator" missing field "generator" -Invalid preset: "NoPresetGenerator"$ +Invalid configure preset: "NoPresetGenerator"$
diff --git a/Tests/RunCMake/CMakePresets/NoPresets-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresets-stderr.txt index 5ff3d33..0f38dca 100644 --- a/Tests/RunCMake/CMakePresets/NoPresets-stderr.txt +++ b/Tests/RunCMake/CMakePresets/NoPresets-stderr.txt
@@ -1,2 +1,2 @@ -^CMake Error: No such preset in [^ +^CMake Error: No such configure preset in [^ ]*/Tests/RunCMake/CMakePresets/NoPresets: "NoPresets"$
diff --git a/Tests/RunCMake/CMakePresets/NoPresets-stdout.txt b/Tests/RunCMake/CMakePresets/NoPresets-stdout.txt index cb01a02..10f3293 100644 --- a/Tests/RunCMake/CMakePresets/NoPresets-stdout.txt +++ b/Tests/RunCMake/CMakePresets/NoPresets-stdout.txt
@@ -1 +1 @@ -^Not searching for unused variables given on the command line\.$ +^$
diff --git a/Tests/RunCMake/CMakePresets/NoSuchPreset-stderr.txt b/Tests/RunCMake/CMakePresets/NoSuchPreset-stderr.txt index 9a2d0d5..ce95657 100644 --- a/Tests/RunCMake/CMakePresets/NoSuchPreset-stderr.txt +++ b/Tests/RunCMake/CMakePresets/NoSuchPreset-stderr.txt
@@ -1,2 +1,2 @@ -^CMake Error: No such preset in [^ +^CMake Error: No such configure preset in [^ ]*/Tests/RunCMake/CMakePresets/NoSuchPreset: "NoSuchPreset"$
diff --git a/Tests/RunCMake/CMakePresets/NoTrace.cmake b/Tests/RunCMake/CMakePresets/NoTrace.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/NoTrace.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/NoWarningFlags-stderr.txt b/Tests/RunCMake/CMakePresets/NoWarningFlags-stderr.txt index a16d362..d18e9ec 100644 --- a/Tests/RunCMake/CMakePresets/NoWarningFlags-stderr.txt +++ b/Tests/RunCMake/CMakePresets/NoWarningFlags-stderr.txt
@@ -1,22 +1,23 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(author\) at [^ ]*/Tests/RunCMake/CMakePresets/WarningsBase\.cmake:[0-9]+ \(message\): Dev warning Call Stack \(most recent call first\): [^ ]*/Tests/RunCMake/CMakePresets/NoWarningFlags\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. - -CMake Deprecation Warning at [^ +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(deprecated\) at [^ ]*/Tests/RunCMake/CMakePresets/WarningsBase\.cmake:[0-9]+ \(message\): Deprecation warning Call Stack \(most recent call first\): [^ ]*/Tests/RunCMake/CMakePresets/NoWarningFlags\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) - - -CMake Warning: +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. ++ +CMake Warning \(unused-cli\): Manually-specified variables were not used by the project: RunCMake_GENERATOR
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.cmake new file mode 100644 index 0000000..d15bd8d --- /dev/null +++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.cmake
@@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake) + +test_variable(PRESETS_FILE "UNINITIALIZED" "OtherCMakePresetsFile.json")
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.json.in new file mode 100644 index 0000000..af68ee8 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.json.in
@@ -0,0 +1,54 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "OtherCMakePresetsFile", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "PRESETS_FILE": "OtherCMakePresetsFile.json" + } + } + ], + "buildPresets": [ + { + "name": "OtherCMakePresetsFile", + "configurePreset": "OtherCMakePresetsFile" + } + ], + "testPresets": [ + { + "name": "OtherCMakePresetsFile", + "configurePreset": "OtherCMakePresetsFile" + } + ], + "packagePresets": [ + { + "name": "OtherCMakePresetsFile", + "configurePreset": "OtherCMakePresetsFile" + } + ], + "workflowPresets": [ + { + "name": "OtherCMakePresetsFile", + "steps": [ + { + "type": "configure", + "name": "OtherCMakePresetsFile" + }, + { + "type": "build", + "name": "OtherCMakePresetsFile" + }, + { + "type": "test", + "name": "OtherCMakePresetsFile" + }, + { + "type": "package", + "name": "OtherCMakePresetsFile" + } + ] + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakePresets.json.in b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakePresets.json.in new file mode 100644 index 0000000..4b7648d --- /dev/null +++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakePresets.json.in
@@ -0,0 +1,13 @@ +{ + "version": 1, + "configurePresets": [ + { + "name": "OtherCMakePresetsFile", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "PRESETS_FILE": "CMakePresets.json" + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakeUserPresets.json.in b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakeUserPresets.json.in new file mode 100644 index 0000000..185f46d --- /dev/null +++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakeUserPresets.json.in
@@ -0,0 +1,13 @@ +{ + "version": 1, + "configurePresets": [ + { + "name": "OtherCMakePresetsFile", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "PRESETS_FILE": "CMakeUserPresets.json" + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileList-stdout.txt b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileList-stdout.txt new file mode 100644 index 0000000..7ec3a37 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileList-stdout.txt
@@ -0,0 +1,19 @@ +^Available configure presets: + + "OtherCMakePresetsFile" + +Available build presets: + + "OtherCMakePresetsFile" + +Available test presets: + + "OtherCMakePresetsFile" + +Available package presets: + + "OtherCMakePresetsFile" + +Available workflow presets: + + "OtherCMakePresetsFile"$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-result.txt
diff --git a/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-stderr.txt new file mode 100644 index 0000000..6d18c7a --- /dev/null +++ b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey: +Invalid package preset: "EmptyEnvKey": Empty environment variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey.json.in b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey.json.in new file mode 100644 index 0000000..f181e45 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey.json.in
@@ -0,0 +1,11 @@ +{ + "version": 6, + "packagePresets": [ + { + "name": "EmptyEnvKey", + "environment": { + "": "value" + } + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/PackageInvalidInheritance-result.txt
diff --git a/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-stderr.txt new file mode 100644 index 0000000..ad32da4 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/PackageInvalidInheritance: +Invalid package preset: "InvalidInheritance": Could not find inherited preset "NoExist"$
diff --git a/Tests/RunCMake/CMakePresets/PackageInvalidInheritance.json.in b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance.json.in new file mode 100644 index 0000000..7076ecc --- /dev/null +++ b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance.json.in
@@ -0,0 +1,11 @@ +{ + "version": 6, + "packagePresets": [ + { + "name": "InvalidInheritance", + "inherits": [ + "NoExist" + ] + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-result.txt
diff --git a/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-stderr.txt new file mode 100644 index 0000000..b29e953 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset: +Invalid package preset: "noConfigurePreset": Package presets must either be hidden or have an associated configure preset$
diff --git a/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset.json.in b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset.json.in new file mode 100644 index 0000000..03b09ed --- /dev/null +++ b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset.json.in
@@ -0,0 +1,8 @@ +{ + "version": 6, + "packagePresets": [ + { + "name": "noConfigurePreset" + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/PresetScriptMode-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/PresetScriptMode-result.txt
diff --git a/Tests/RunCMake/CMakePresets/PresetScriptMode-stderr.txt b/Tests/RunCMake/CMakePresets/PresetScriptMode-stderr.txt new file mode 100644 index 0000000..ea606e4 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/PresetScriptMode-stderr.txt
@@ -0,0 +1,2 @@ +^CMake Error: + Presets are not supported in CMake script mode\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-result.txt
diff --git a/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-stderr.txt b/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-stderr.txt new file mode 100644 index 0000000..668f949 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist: +File not found: [^ +]*/FileDoesNotExist\.json$
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake index 6ffd7e5..6660eaa 100644 --- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -30,6 +30,33 @@ endif() configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" "${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt" @ONLY) + set(_presets_file) + if(CMakePresets_FILE_ARG) + cmake_path(GET RunCMake_TEST_SOURCE_DIR PARENT_PATH CMakePresets_FILE_ARG_DIRECTORY) + cmake_path(APPEND CMakePresets_FILE_ARG_DIRECTORY "_OtherCMakePresetsFiles") + if(NOT RunCMake_TEST_SOURCE_DIR_NO_CLEAN) + file(REMOVE_RECURSE "${CMakePresets_FILE_ARG_DIRECTORY}") + file(MAKE_DIRECTORY "${CMakePresets_FILE_ARG_DIRECTORY}") + endif() + if(CMakePresets_FILE_ARG_RELATIVE) + cmake_path(RELATIVE_PATH + CMakePresets_FILE_ARG_DIRECTORY + BASE_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}" + OUTPUT_VARIABLE CMakePresets_FILE_ARG_RELATIVE_PATH + ) + set(_presets_file "--presets-file=${CMakePresets_FILE_ARG_RELATIVE_PATH}/${CMakePresets_FILE_ARG}") + else() + set(_presets_file "--presets-file=${CMakePresets_FILE_ARG_DIRECTORY}/${CMakePresets_FILE_ARG}") + endif() + foreach(_presets_file_arg IN ITEMS ${CMakePresets_FILE_ARG} ${CMakePresets_FILE_ARG_EXTRA_FILES}) + configure_file( + "${RunCMake_SOURCE_DIR}/${_presets_file_arg}.in" + "${CMakePresets_FILE_ARG_DIRECTORY}/${_presets_file_arg}" + @ONLY + ) + endforeach() + endif() + if(NOT CMakePresets_FILE) set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/${name}.json.in") endif() @@ -66,7 +93,7 @@ if(CMakePresets_NO_SOURCE_ARGS) set(_source_args) endif() - set(_unused_cli --no-warn-unused-cli) + set(_unused_cli -Wno-unused-cli) if(CMakePresets_WARN_UNUSED_CLI) set(_unused_cli) endif() @@ -93,6 +120,7 @@ ${_make_program} ${_unused_cli} ${_preset} + ${_presets_file} ${_log_level} ${ARGN} ) @@ -153,6 +181,10 @@ run_cmake_presets(CyclicInheritance1) run_cmake_presets(CyclicInheritance2) run_cmake_presets(InvalidInheritance) +run_cmake_presets(BuildInvalidInheritance) +run_cmake_presets(PackageInvalidInheritance) +run_cmake_presets(TestInvalidInheritance) +run_cmake_presets(PackageNoConfigurePreset) run_cmake_presets(ErrorNoWarningDev) run_cmake_presets(ErrorNoWarningDeprecated) set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) @@ -162,6 +194,9 @@ run_cmake_presets(UnknownToolsetStrategy) run_cmake_presets(EmptyCacheKey) run_cmake_presets(EmptyEnvKey) +run_cmake_presets(BuildEmptyEnvKey) +run_cmake_presets(PackageEmptyEnvKey) +run_cmake_presets(TestEmptyEnvKey) set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) run_cmake_presets(UnclosedMacro) run_cmake_presets(NoSuchMacro) @@ -174,6 +209,13 @@ run_cmake_presets(ConditionFuture) run_cmake_presets(SubConditionNull) run_cmake_presets(TraceNotSupported) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) +run_cmake_presets(WarningDevNotSupported) +run_cmake_presets(WarningAuthorNotSupported) +run_cmake_presets(ErrorDevNotSupported) +run_cmake_presets(ErrorAuthorNotSupported) +run_cmake_presets(ErrorUninitializedNotSupported) +run_cmake_presets(ErrorUnusedCliNotSupported) set(CMakePresets_NO_PRESET 1) set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) @@ -208,6 +250,8 @@ run_cmake_presets(GoodBinaryUp) set(CMakePresets_SOURCE_ARG "../GoodBinaryRelative") run_cmake_presets(GoodBinaryRelative) +set(CMakePresets_SOURCE_ARG "${RunCMake_BINARY_DIR}/GoodSourceSlash/") +run_cmake_presets(GoodSourceSlash) unset(CMakePresets_SOURCE_ARG) run_cmake_presets(GoodSpaces "--preset" "Good Spaces") run_cmake_presets(GoodSpacesEq "--preset=Good Spaces") @@ -304,6 +348,8 @@ run_cmake_presets(NoPresetArgument --preset) run_cmake_presets(NoPresetArgumentEq --preset= -DA=B) run_cmake_presets(UseHiddenPreset) +run_cmake_presets(NoPresetFileArgument --preset GoodNoArgs --presets-file) +run_cmake_presets(PresetScriptMode -P PresetScriptMode.cmake --preset GoodNoArgs) # Test CMakeUserPresets.json unset(CMakePresets_FILE) @@ -317,6 +363,30 @@ run_cmake_presets(UserDuplicateCross) run_cmake_presets(UserInheritance) +# Test --presets-file=<file> +# <file> should be preferred over CMakePresets.json and CMakeUserPresets.json +set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/OtherCMakePresetsFileCMakePresets.json.in") +set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/OtherCMakePresetsFileCMakeUserPresets.json.in") +# <file> must exist +run_cmake_presets(PresetsFileArgNoExist --presets-file=FileDoesNotExist.json) +set(CMakePresets_FILE_ARG OtherCMakePresetsFile.json) +run_cmake_presets(OtherCMakePresetsFile) +# --list-presets should only list the presets defined in <file> +run_cmake_presets(OtherCMakePresetsFileList --list-presets=all) +# <file> can be specified via relative path on the command line +set(CMakePresets_FILE_ARG_RELATIVE 1) +run_cmake_presets(OtherCMakePresetsFileRelPath --preset OtherCMakePresetsFile) +set(CMakePresets_FILE_ARG "subdir/OtherCMakePresetsFileSubdir.json") +set(CMakePresets_FILE_ARG_EXTRA_FILES "subdir/IncludeRelativeToSubdir.json") +# Files included by <file> should have paths evaluated relative to <file> +run_cmake_presets(OtherCMakePresetsFileSubdir) +run_cmake_presets(OtherCMakePresetsFileSubdir --preset=IncludeRelativeToSubdir) +unset(CMakePresets_FILE) +unset(CMakeUserPresets_FILE) +unset(CMakePresets_FILE_ARG) +unset(CMakePresets_FILE_ARG_RELATIVE) +unset(CMakePresets_FILE_ARG_EXTRA_FILES) + # Test listing presets set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/ListPresets.json.in") run_cmake_presets(ListPresets --list-presets) @@ -343,19 +413,32 @@ run_cmake_presets(ListAllPresetsNoTest --list-presets=all) # Test warning and error flags -set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Warnings.json.in") set(CMakePresets_WARN_UNUSED_CLI 1) +set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Warnings.json.in") run_cmake_presets(NoWarningFlags) run_cmake_presets(WarningFlags) run_cmake_presets(DisableWarningFlags) run_cmake_presets(ErrorDev) run_cmake_presets(ErrorDeprecated) + +set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Warnings12.json.in") +run_cmake_presets(NoWarningFlags) +run_cmake_presets(WarningFlags) +run_cmake_presets(WarningAbsInstallPath) +run_cmake_presets(DisableWarningFlags) +run_cmake_presets(ErrorAbsInstallPath) +run_cmake_presets(ErrorDev) +run_cmake_presets(ErrorUninitialized) +run_cmake_presets(ErrorUnusedCli) +run_cmake_presets(DiagnosticOrder) unset(CMakePresets_WARN_UNUSED_CLI) # Test debug set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Debug.json.in") run_cmake_presets(NoDebug) run_cmake_presets(Debug) +run_cmake_presets(DebugInherit) +run_cmake_presets(DebugInheritOverride) # Test trace set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Trace.json.in") @@ -368,6 +451,11 @@ run_cmake_presets(TraceSource) run_cmake_presets(TraceRedirect) run_cmake_presets(TraceAll) +run_cmake_presets(TraceInherit) +run_cmake_presets(TraceInheritOverride) +run_cmake_presets(TraceFormatInherit) +run_cmake_presets(TraceSourceInherit) +run_cmake_presets(TraceRedirectInherit) # Test ${hostSystemName} macro set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/HostSystemName.json.in") @@ -377,10 +465,20 @@ # Test ${fileDir} macro set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/FileDir.json.in") +set(FileDir_include_subdir "subdir/FileDirv4.json") set(CMakePresets_EXTRA_FILES - "${RunCMake_SOURCE_DIR}/subdir/FileDir.json.in" - ) -run_cmake_presets(FileDir) + "${RunCMake_SOURCE_DIR}/${FileDir_include_subdir}.in" +) +run_cmake_presets(FileDirv4 --preset FileDir) +run_cmake_presets(FileDirv4 --preset FileDirExt) +set(FileDir_include_subdir "subdir/FileDirv12.json") +set(CMakePresets_EXTRA_FILES + "${RunCMake_SOURCE_DIR}/${FileDir_include_subdir}.in" + "${RunCMake_SOURCE_DIR}/subdir/FileDirInclude.json.in" +) +run_cmake_presets(FileDirv12 --preset FileDir) +run_cmake_presets(FileDirv12 --preset FileDirExt) +run_cmake_presets(FileDirv12 --preset FileDirInclude) unset(CMakePresets_EXTRA_FILES) set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/FileDirFuture.json.in") run_cmake_presets(FileDirFuture)
diff --git a/Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt b/Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt index 6a9a7de..ea53102 100644 --- a/Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt +++ b/Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt
@@ -1 +1,2 @@ -^CMake Error: Could not use disabled preset "SimpleFalse"$ +^CMake Error: Cannot use disabled configure preset in [^ +]*/Tests/RunCMake/CMakePresets/SimpleFalse: "SimpleFalse"$
diff --git a/Tests/RunCMake/CMakePresets/SimpleTrue.cmake b/Tests/RunCMake/CMakePresets/SimpleTrue.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/SimpleTrue.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/TestEmptyEnvKey-result.txt
diff --git a/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-stderr.txt new file mode 100644 index 0000000..df00af9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/TestEmptyEnvKey: +Invalid test preset: "EmptyEnvKey": Empty environment variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/TestEmptyEnvKey.json.in b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey.json.in new file mode 100644 index 0000000..50741d1 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey.json.in
@@ -0,0 +1,11 @@ +{ + "version": 2, + "testPresets": [ + { + "name": "EmptyEnvKey", + "environment": { + "": "value" + } + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/TestInvalidInheritance-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/TestInvalidInheritance-result.txt
diff --git a/Tests/RunCMake/CMakePresets/TestInvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/TestInvalidInheritance-stderr.txt new file mode 100644 index 0000000..a72e498 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/TestInvalidInheritance-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/TestInvalidInheritance: +Invalid test preset: "InvalidInheritance": Could not find inherited preset "NoExist"$
diff --git a/Tests/RunCMake/CMakePresets/TestInvalidInheritance.json.in b/Tests/RunCMake/CMakePresets/TestInvalidInheritance.json.in new file mode 100644 index 0000000..e137f52 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/TestInvalidInheritance.json.in
@@ -0,0 +1,11 @@ +{ + "version": 2, + "testPresets": [ + { + "name": "InvalidInheritance", + "inherits": [ + "NoExist" + ] + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/Trace.cmake b/Tests/RunCMake/CMakePresets/Trace.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/Trace.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/Trace.json.in b/Tests/RunCMake/CMakePresets/Trace.json.in index f50a6f2..135f0e3 100644 --- a/Tests/RunCMake/CMakePresets/Trace.json.in +++ b/Tests/RunCMake/CMakePresets/Trace.json.in
@@ -64,6 +64,29 @@ "source": "TraceSourceFile.txt", "redirect": "TraceRedirectFile.json" } + }, + { + "name": "TraceInherit", + "inherits": "Trace" + }, + { + "name": "TraceInheritOverride", + "inherits": "Trace", + "trace": { + "mode": "off" + } + }, + { + "name": "TraceFormatInherit", + "inherits": "TraceFormatJSON" + }, + { + "name": "TraceSourceInherit", + "inherits": "TraceSource" + }, + { + "name": "TraceRedirectInherit", + "inherits": "TraceRedirect" } ] }
diff --git a/Tests/RunCMake/CMakePresets/TraceAll.cmake b/Tests/RunCMake/CMakePresets/TraceAll.cmake deleted file mode 100644 index 9896ec0..0000000 --- a/Tests/RunCMake/CMakePresets/TraceAll.cmake +++ /dev/null
@@ -1,4 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/TraceExpand.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/TraceFormatJSON.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/TraceSource.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/TraceRedirect.cmake)
diff --git a/Tests/RunCMake/CMakePresets/TraceExpand.cmake b/Tests/RunCMake/CMakePresets/TraceExpand.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/TraceExpand.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/TraceFormatHuman.cmake b/Tests/RunCMake/CMakePresets/TraceFormatHuman.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/TraceFormatHuman.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/TraceFormatInherit-stderr.txt b/Tests/RunCMake/CMakePresets/TraceFormatInherit-stderr.txt new file mode 100644 index 0000000..01370ab --- /dev/null +++ b/Tests/RunCMake/CMakePresets/TraceFormatInherit-stderr.txt
@@ -0,0 +1,3 @@ +^{"version":{"major":1,"minor":2}} +{"args":\["VERSION","3\.18"\],"cmd":"cmake_minimum_required","file":"[^"]*/Tests/RunCMake/CMakePresets/TraceFormatInherit/CMakeLists\.txt","frame":1,"global_frame":1,"line":1,"time":[0-9\.]+} +{"args":\["\${RunCMake_TEST}","NONE"\],"cmd":"project","file":"[^"]*/Tests/RunCMake/CMakePresets/TraceFormatInherit/CMakeLists\.txt","frame":1,"global_frame":1,"line":2,"time":[0-9\.]+}
diff --git a/Tests/RunCMake/CMakePresets/TraceFormatJSON.cmake b/Tests/RunCMake/CMakePresets/TraceFormatJSON.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/TraceFormatJSON.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/TraceInherit-stderr.txt b/Tests/RunCMake/CMakePresets/TraceInherit-stderr.txt new file mode 100644 index 0000000..66cdcaa --- /dev/null +++ b/Tests/RunCMake/CMakePresets/TraceInherit-stderr.txt
@@ -0,0 +1,4 @@ +^[^ +]*/Tests/RunCMake/CMakePresets/TraceInherit/CMakeLists\.txt\(1\): cmake_minimum_required\(VERSION 3\.18 \) +[^ +]*/Tests/RunCMake/CMakePresets/TraceInherit/CMakeLists\.txt\(2\): project\(\${RunCMake_TEST} NONE \)
diff --git a/Tests/RunCMake/CMakePresets/TraceRedirect.cmake b/Tests/RunCMake/CMakePresets/TraceRedirect.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/TraceRedirect.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/TraceSource.cmake b/Tests/RunCMake/CMakePresets/TraceSource.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/TraceSource.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/UseHiddenPreset-stderr.txt b/Tests/RunCMake/CMakePresets/UseHiddenPreset-stderr.txt index 45b4cd4..9ccad06 100644 --- a/Tests/RunCMake/CMakePresets/UseHiddenPreset-stderr.txt +++ b/Tests/RunCMake/CMakePresets/UseHiddenPreset-stderr.txt
@@ -1,2 +1,2 @@ -^CMake Error: Cannot use hidden preset in [^ +^CMake Error: Cannot use hidden configure preset in [^ ]*/Tests/RunCMake/CMakePresets/UseHiddenPreset: "UseHiddenPreset"$
diff --git a/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt index 2ce1316..65d1a5f 100644 --- a/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt +++ b/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt
@@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/UserInheritance: -Inherited preset "UserInheritance" is unreachable from preset's file$ +Inherited configure preset "UserInheritance" is unreachable from preset's file$
diff --git a/Tests/RunCMake/CMakePresets/V2InheritV3Optional.cmake b/Tests/RunCMake/CMakePresets/V2InheritV3Optional.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresets/V2InheritV3Optional.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresets/VendorMacro-stderr.txt b/Tests/RunCMake/CMakePresets/VendorMacro-stderr.txt index 2e98019..3d9c60e 100644 --- a/Tests/RunCMake/CMakePresets/VendorMacro-stderr.txt +++ b/Tests/RunCMake/CMakePresets/VendorMacro-stderr.txt
@@ -1 +1 @@ -^CMake Error: Could not evaluate preset "VendorMacro": Invalid macro expansion$ +^CMake Error: Could not evaluate configure preset "VendorMacro": Invalid macro expansion$
diff --git a/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt new file mode 100644 index 0000000..6d9315e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt
@@ -0,0 +1,5 @@ +CMake Warning \(unused-cli\): + Manually-specified variables were not used by the project: + + RunCMake_GENERATOR + UNUSED_VARIABLE$
diff --git a/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake new file mode 100644 index 0000000..fc59b1c --- /dev/null +++ b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake
@@ -0,0 +1,6 @@ +cmake_diagnostic(GET CMD_INSTALL_ABSOLUTE_DESTINATION action) +if(NOT "${action}" STREQUAL WARN) + message(SEND_ERROR + "wrong action for diagnostic CMD_INSTALL_ABSOLUTE_DESTINATION" + " (expected 'WARN', actual '${action}')") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/WarningAuthorNotSupported-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/WarningAuthorNotSupported-result.txt
diff --git a/Tests/RunCMake/CMakePresets/WarningAuthorNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/WarningAuthorNotSupported-stderr.txt new file mode 100644 index 0000000..4dc87b4 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/WarningAuthorNotSupported-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/WarningAuthorNotSupported: +File version must be 12 or higher for warnings\.author support$
diff --git a/Tests/RunCMake/CMakePresets/WarningAuthorNotSupported.json.in b/Tests/RunCMake/CMakePresets/WarningAuthorNotSupported.json.in new file mode 100644 index 0000000..03a3656 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/WarningAuthorNotSupported.json.in
@@ -0,0 +1,13 @@ +{ + "version": 11, + "configurePresets": [ + { + "name": "WarningAuthorNotSupported", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "warnings": { + "author": true + } + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresets/WarningDevNotSupported-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresets/WarningDevNotSupported-result.txt
diff --git a/Tests/RunCMake/CMakePresets/WarningDevNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/WarningDevNotSupported-stderr.txt new file mode 100644 index 0000000..75f3003 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/WarningDevNotSupported-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/WarningDevNotSupported: +File version must be 11 or lower for warnings\.dev support$
diff --git a/Tests/RunCMake/CMakePresets/WarningDevNotSupported.json.in b/Tests/RunCMake/CMakePresets/WarningDevNotSupported.json.in new file mode 100644 index 0000000..32c6198 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/WarningDevNotSupported.json.in
@@ -0,0 +1,13 @@ +{ + "version": 12, + "configurePresets": [ + { + "name": "WarningDevNotSupported", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "warnings": { + "dev": true + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/WarningFlags-stderr.txt b/Tests/RunCMake/CMakePresets/WarningFlags-stderr.txt index 6e488a9..3ab49de 100644 --- a/Tests/RunCMake/CMakePresets/WarningFlags-stderr.txt +++ b/Tests/RunCMake/CMakePresets/WarningFlags-stderr.txt
@@ -1,34 +1,35 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(uninitialized\) at [^ ]*/Modules/[^/ ]*:[0-9]+ \([a-zA-Z_][a-zA-Z0-9_]*\): uninitialized variable '[^ ]*' Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\..* -CMake Warning \(dev\) at [^ + CMakeLists\.txt:[0-9]+ \(project\)( +This warning is for project developers\. Use -Wno-author to suppress it\.)?.* +CMake Warning \(author\) at [^ ]*/Tests/RunCMake/CMakePresets/WarningsBase\.cmake:[0-9]+ \(message\): Dev warning Call Stack \(most recent call first\): [^ ]*/Tests/RunCMake/CMakePresets/WarningFlags\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. - -CMake Deprecation Warning at [^ +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(deprecated\) at [^ ]*/Tests/RunCMake/CMakePresets/WarningsBase\.cmake:[0-9]+ \(message\): Deprecation warning Call Stack \(most recent call first\): [^ ]*/Tests/RunCMake/CMakePresets/WarningFlags\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) - - -CMake Warning \(dev\) at [^ +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. ++ +CMake Warning \(uninitialized\) at [^ ]*/Tests/RunCMake/CMakePresets/WarningsBase\.cmake:[0-9]+ \(set\): uninitialized variable 'UNINITIALIZED_VARIABLE' Call Stack \(most recent call first\): [^ ]*/Tests/RunCMake/CMakePresets/WarningFlags\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ + CMakeLists\.txt:[0-9]+ \(include\)( +This warning is for project developers\. Use -Wno-author to suppress it\.)?$
diff --git a/Tests/RunCMake/CMakePresets/Warnings12.json.in b/Tests/RunCMake/CMakePresets/Warnings12.json.in new file mode 100644 index 0000000..701e5f4 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/Warnings12.json.in
@@ -0,0 +1,95 @@ +{ + "version": 12, + "configurePresets": [ + { + "name": "NoWarningFlags", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "UNUSED_VARIABLE": "Unused" + } + }, + { + "name": "WarningFlags", + "inherits": "NoWarningFlags", + "warnings": { + "author": true, + "deprecated": true, + "uninitialized": true, + "unusedCli": false, + "systemVars": true + } + }, + { + "name": "DisableWarningFlags", + "inherits": "NoWarningFlags", + "warnings": { + "author": false, + "deprecated": false, + "unusedCli": false + } + }, + { + "name": "WarningAbsInstallPath", + "inherits": "NoWarningFlags", + "warnings": { + "installAbsoluteDestination": true + } + }, + { + "name": "ErrorAbsInstallPath", + "inherits": "NoWarningFlags", + "errors": { + "installAbsoluteDestination": true + } + }, + { + "name": "ErrorDev", + "inherits": "NoWarningFlags", + "errors": { + "author": true + } + }, + { + "name": "ErrorDeprecated", + "inherits": "NoWarningFlags", + "warnings": { + "author": false + }, + "errors": { + "deprecated": true + } + }, + { + "name": "ErrorUninitialized", + "inherits": "NoWarningFlags", + "warnings": { + "author": false + }, + "errors": { + "uninitialized": true + } + }, + { + "name": "ErrorUnusedCli", + "inherits": "NoWarningFlags", + "warnings": { + "author": false + }, + "errors": { + "unusedCli": true + } + }, + { + "name": "DiagnosticOrder", + "inherits": "NoWarningFlags", + "warnings": { + "deprecated": false, + "unusedCli": false + }, + "errors": { + "author": true + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/subdir/FileDir.json.in b/Tests/RunCMake/CMakePresets/subdir/FileDir.json.in deleted file mode 100644 index 00282a7..0000000 --- a/Tests/RunCMake/CMakePresets/subdir/FileDir.json.in +++ /dev/null
@@ -1,13 +0,0 @@ -{ - "version": 4, - "configurePresets": [ - { - "name": "FileDir", - "generator": "@RunCMake_GENERATOR@", - "binaryDir": "${sourceDir}/build", - "cacheVariables": { - "TEST_FILE_DIR": "${fileDir}" - } - } - ] -}
diff --git a/Tests/RunCMake/CMakePresets/subdir/FileDirInclude.json.in b/Tests/RunCMake/CMakePresets/subdir/FileDirInclude.json.in new file mode 100644 index 0000000..f7dd597 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/subdir/FileDirInclude.json.in
@@ -0,0 +1,13 @@ +{ + "version": 9, + "configurePresets": [ + { + "name": "FileDirInclude", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${fileDir}/build", + "cacheVariables": { + "PRESET": "FileDirInclude" + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/subdir/FileDirv12.json.in b/Tests/RunCMake/CMakePresets/subdir/FileDirv12.json.in new file mode 100644 index 0000000..ed20764 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/subdir/FileDirv12.json.in
@@ -0,0 +1,17 @@ +{ + "version": 12, + "include": [ + "${fileDir}/FileDirInclude.json" + ], + "configurePresets": [ + { + "name": "FileDir", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${fileDir}/build", + "cacheVariables": { + "TEST_FILE_DIR": "${fileDir}", + "PRESET": "FileDir" + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/subdir/FileDirv4.json.in b/Tests/RunCMake/CMakePresets/subdir/FileDirv4.json.in new file mode 100644 index 0000000..e055c4e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/subdir/FileDirv4.json.in
@@ -0,0 +1,14 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "FileDir", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${fileDir}/build", + "cacheVariables": { + "TEST_FILE_DIR": "${fileDir}", + "PRESET": "FileDir" + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/subdir/IncludeRelativeToSubdir.json.in b/Tests/RunCMake/CMakePresets/subdir/IncludeRelativeToSubdir.json.in new file mode 100644 index 0000000..56db1ad --- /dev/null +++ b/Tests/RunCMake/CMakePresets/subdir/IncludeRelativeToSubdir.json.in
@@ -0,0 +1,10 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "IncludeRelativeToSubdir", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/subdir/OtherCMakePresetsFileSubdir.json.in b/Tests/RunCMake/CMakePresets/subdir/OtherCMakePresetsFileSubdir.json.in new file mode 100644 index 0000000..e8a5f7f --- /dev/null +++ b/Tests/RunCMake/CMakePresets/subdir/OtherCMakePresetsFileSubdir.json.in
@@ -0,0 +1,13 @@ +{ + "version": 4, + "include": [ + "IncludeRelativeToSubdir.json" + ], + "configurePresets": [ + { + "name": "OtherCMakePresetsFileSubdir", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ] +}
diff --git a/Tests/RunCMake/CMakePresetsBuild/BuildDirectoryOverride-build-override-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/BuildDirectoryOverride-build-override-stderr.txt index fbd9416..86204c2 100644 --- a/Tests/RunCMake/CMakePresetsBuild/BuildDirectoryOverride-build-override-stderr.txt +++ b/Tests/RunCMake/CMakePresetsBuild/BuildDirectoryOverride-build-override-stderr.txt
@@ -1,2 +1,4 @@ -^Error: [^ -]*/Tests/RunCMake/build2 is not a directory$ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/build2: +File not found: [^ +]*/Tests/RunCMake/build2/CMakePresets.json$
diff --git a/Tests/RunCMake/CMakePresetsBuild/CMakeLists.txt.in b/Tests/RunCMake/CMakePresetsBuild/CMakeLists.txt.in index 129184a..2f5cf8e 100644 --- a/Tests/RunCMake/CMakePresetsBuild/CMakeLists.txt.in +++ b/Tests/RunCMake/CMakePresetsBuild/CMakeLists.txt.in
@@ -1,3 +1,3 @@ cmake_minimum_required(VERSION 3.19) project("@CASE_NAME@" NONE) -include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake") +include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake" OPTIONAL)
diff --git a/Tests/RunCMake/CMakePresetsBuild/Condition.cmake b/Tests/RunCMake/CMakePresetsBuild/Condition.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsBuild/Condition.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid.cmake b/Tests/RunCMake/CMakePresetsBuild/Invalid.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsBuild/Invalid.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset.cmake b/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsBuild/JobsNegative.cmake b/Tests/RunCMake/CMakePresetsBuild/JobsNegative.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsBuild/JobsNegative.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsBuild/ListPresets.cmake b/Tests/RunCMake/CMakePresetsBuild/ListPresets.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsBuild/ListPresets.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt index 9ce6ea5..4749340 100644 --- a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt +++ b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt
@@ -1,3 +1,3 @@ CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset: -Invalid preset: "noConfigurePreset"$ +Invalid build preset: "noConfigurePreset": Build presets must either be hidden or have an associated configure preset$
diff --git a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.cmake b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.json.in new file mode 100644 index 0000000..474c669 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.json.in
@@ -0,0 +1,16 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ], + "buildPresets": [ + { + "name": "default-from-other-file", + "configurePreset": "default" + } + ] +}
diff --git a/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFileListPresets-build-x-stdout.txt b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFileListPresets-build-x-stdout.txt new file mode 100644 index 0000000..ee586df --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFileListPresets-build-x-stdout.txt
@@ -0,0 +1,3 @@ +^Available build presets: + + "default-from-other-file"$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-result.txt
diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-stderr.txt new file mode 100644 index 0000000..50d4e2b --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No file specified for --presets-file
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsNoArg-build-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsBuild/PresetsNoArg-build-result.txt
diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsNoArg-build-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsNoArg-build-stderr.txt new file mode 100644 index 0000000..aa348ab --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/PresetsNoArg-build-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No preset specified for --preset
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsNoArgEq-build-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsBuild/PresetsNoArgEq-build-result.txt
diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsNoArgEq-build-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsNoArgEq-build-stderr.txt new file mode 100644 index 0000000..aa348ab --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/PresetsNoArgEq-build-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No preset specified for --preset
diff --git a/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake index 63dc4f3..50a4e8e 100644 --- a/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake
@@ -26,6 +26,12 @@ configure_file("${CMakePresetsBuild_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" @ONLY) endif() + set(_presets_file) + if(CMakePresets_FILE_ARG) + set(_presets_file "--presets-file=${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}") + configure_file("${RunCMake_SOURCE_DIR}/${CMakePresets_FILE_ARG}.in" "${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}" @ONLY) + endif() + if(NOT CMakeUserPresets_FILE) set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/${name}User.json.in") endif() @@ -50,11 +56,11 @@ if(eq) run_cmake_command(${name}-build-${BUILD_PRESET} - ${CMAKE_COMMAND} "--build" ${CMakePresetsBuild_BUILD_DIR_OVERRIDE} "--preset=${BUILD_PRESET}" ${ARGN}) + ${CMAKE_COMMAND} "--build" ${CMakePresetsBuild_BUILD_DIR_OVERRIDE} "--preset=${BUILD_PRESET}" ${_presets_file} ${ARGN}) set(eq 0) else() run_cmake_command(${name}-build-${BUILD_PRESET} - ${CMAKE_COMMAND} "--build" ${CMakePresetsBuild_BUILD_DIR_OVERRIDE} "--preset" "${BUILD_PRESET}" ${ARGN}) + ${CMAKE_COMMAND} "--build" ${CMakePresetsBuild_BUILD_DIR_OVERRIDE} "--preset" "${BUILD_PRESET}" ${_presets_file} ${ARGN}) set(eq 1) endif() endforeach() @@ -74,7 +80,17 @@ run_cmake_build_presets(InvalidConfigurePreset "default" "badConfigurePreset" "") run_cmake_build_presets(Condition "default" "enabled;disabled" "") +set(CMakePresets_FILE_ARG "OtherCMakePresetsFile.json") +run_cmake_build_presets(OtherCMakePresetsFile "default" "default-from-other-file" "") set(CMakePresetsBuild_BUILD_ONLY 1) +run_cmake_build_presets(OtherCMakePresetsFileListPresets "" "x" "--list-presets" "") +unset(CMakePresetsBuild_BUILD_ONLY) +unset(CMakePresets_FILE_ARG) + +set(CMakePresetsBuild_BUILD_ONLY 1) +run_cmake_command(PresetsNoArg-build ${CMAKE_COMMAND} "--build" "--preset") +run_cmake_command(PresetsNoArgEq-build ${CMAKE_COMMAND} "--build" "--preset=") +run_cmake_Command(PresetsFileNoArg-build ${CMAKE_COMMAND} "--build" "--presets-file") run_cmake_build_presets(ListPresets "x" "x" "--list-presets" "") run_cmake_build_presets(NoConfigurePreset "x" "noConfigurePreset" "") run_cmake_build_presets(Invalid "x" "hidden;vendorMacro" "") @@ -88,3 +104,34 @@ run_cmake_build_presets(ConfigurePresetUnreachable "x" "x" "") set(CMakePresetsBuild_BUILD_ONLY 0) + +# Verify that `cmake --build <dir> --preset <pre>` works when +# the current working directory is not the source directory. +block() + set(name "BuildPresetFromBinaryDir") + set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/${name}") + set(RunCMake_TEST_BINARY_DIR "${RunCMake_TEST_SOURCE_DIR}/build/default") + set(RunCMake_TEST_NO_CLEAN TRUE) + + file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + + set(CASE_NAME "${name}") + set(CASE_SOURCE_DIR "${RunCMake_SOURCE_DIR}") + configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt" @ONLY) + configure_file("${RunCMake_SOURCE_DIR}/ListPresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" @ONLY) + + # Configure using the "default" preset. + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}") + run_cmake_command("${name}-configure" + "${CMAKE_COMMAND}" "--preset" "default") + + # Verify calling `cmake --build <dir> --preset <pre>` from the + # binary directory. + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + run_cmake_command("${name}-build" + "${CMAKE_COMMAND}" "--build" "${RunCMake_TEST_BINARY_DIR}" + "--preset" "build-default") +endblock()
diff --git a/Tests/RunCMake/CMakePresetsPackage/CMakeLists.txt.in b/Tests/RunCMake/CMakePresetsPackage/CMakeLists.txt.in index 129184a..2f5cf8e 100644 --- a/Tests/RunCMake/CMakePresetsPackage/CMakeLists.txt.in +++ b/Tests/RunCMake/CMakePresetsPackage/CMakeLists.txt.in
@@ -1,3 +1,3 @@ cmake_minimum_required(VERSION 3.19) project("@CASE_NAME@" NONE) -include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake") +include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake" OPTIONAL)
diff --git a/Tests/RunCMake/CMakePresetsPackage/ListPresets.cmake b/Tests/RunCMake/CMakePresetsPackage/ListPresets.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsPackage/ListPresets.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.cmake new file mode 100644 index 0000000..a9068c7 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.cmake
@@ -0,0 +1,4 @@ +set(CPACK_PACKAGE_NAME OtherCMakePresetsFile) +set(CPACK_GENERATOR "TGZ;TXZ") + +include(CPack)
diff --git a/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.json.in new file mode 100644 index 0000000..a015307 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.json.in
@@ -0,0 +1,22 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default" + } + ], + "packagePresets": [ + { + "name": "default-from-other-file", + "configurePreset": "default" + } + ] +}
diff --git a/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFileListPresets-package-x-stdout.txt b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFileListPresets-package-x-stdout.txt new file mode 100644 index 0000000..7e03528 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFileListPresets-package-x-stdout.txt
@@ -0,0 +1,3 @@ +^Available package presets: + + "default-from-other-file"$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-result.txt
diff --git a/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-stderr.txt b/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-stderr.txt new file mode 100644 index 0000000..c532031 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No file specified for --presets-file$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsPackage/PresetsNoArg-package-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsPackage/PresetsNoArg-package-result.txt
diff --git a/Tests/RunCMake/CMakePresetsPackage/PresetsNoArg-package-stderr.txt b/Tests/RunCMake/CMakePresetsPackage/PresetsNoArg-package-stderr.txt new file mode 100644 index 0000000..aef30d2 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsPackage/PresetsNoArg-package-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No preset specified for --preset$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsPackage/PresetsNoArgEq-package-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsPackage/PresetsNoArgEq-package-result.txt
diff --git a/Tests/RunCMake/CMakePresetsPackage/PresetsNoArgEq-package-stderr.txt b/Tests/RunCMake/CMakePresetsPackage/PresetsNoArgEq-package-stderr.txt new file mode 100644 index 0000000..aef30d2 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsPackage/PresetsNoArgEq-package-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No preset specified for --preset$
diff --git a/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake index 269fb6e..b8c1eb3 100644 --- a/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake
@@ -33,6 +33,12 @@ configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY) endif() + set(_presets_file) + if(CMakePresets_FILE_ARG) + set(_presets_file "--presets-file=${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}") + configure_file("${RunCMake_SOURCE_DIR}/${CMakePresets_FILE_ARG}.in" "${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}" @ONLY) + endif() + foreach(ASSET ${CMakePresetsPackage_ASSETS}) configure_file("${RunCMake_SOURCE_DIR}/${ASSET}" "${RunCMake_TEST_SOURCE_DIR}" COPYONLY) endforeach() @@ -63,11 +69,11 @@ if(eq) run_cmake_command(${name}-package-${PACKAGE_PRESET} - ${CMAKE_CPACK_COMMAND} "--preset=${PACKAGE_PRESET}" ${ARGN}) + ${CMAKE_CPACK_COMMAND} "--preset=${PACKAGE_PRESET}" ${_presets_file} ${ARGN}) set(eq 0) else() run_cmake_command(${name}-package-${PACKAGE_PRESET} - ${CMAKE_CPACK_COMMAND} "--preset" "${PACKAGE_PRESET}" ${ARGN}) + ${CMAKE_CPACK_COMMAND} "--preset" "${PACKAGE_PRESET}" ${_presets_file} ${ARGN}) set(eq 1) endif() endforeach() @@ -100,3 +106,12 @@ run_cmake_package_presets(UnsupportedVersion "x" "" "") run_cmake_package_presets(Good "default" "build-default-debug" "no-environment;with-environment;generators;configurations;variables;config-file;debug;verbose;package-name;package-version;package-directory;vendor-name") run_cmake_package_presets(ListPresets "default" "" "x" "--list-presets") + +set(CMakePresets_FILE_ARG "OtherCMakePresetsFile.json") +run_cmake_package_presets(OtherCMakePresetsFile "default" "default" "default-from-other-file" "") +run_cmake_package_presets(OtherCMakePresetsFileListPresets "" "" "x" "--list-presets") +unset(CMakePresets_FILE_ARG) + +run_cmake_command(PresetsNoArg-package ${CMAKE_CPACK_COMMAND} "--preset") +run_cmake_command(PresetsNoArgEq-package ${CMAKE_CPACK_COMMAND} "--preset=") +run_cmake_command(PresetsFileNoArg-package ${CMAKE_CPACK_COMMAND} "--presets-file")
diff --git a/Tests/RunCMake/CMakePresetsTest/CMakeLists.txt.in b/Tests/RunCMake/CMakePresetsTest/CMakeLists.txt.in index 129184a..2f5cf8e 100644 --- a/Tests/RunCMake/CMakePresetsTest/CMakeLists.txt.in +++ b/Tests/RunCMake/CMakePresetsTest/CMakeLists.txt.in
@@ -1,3 +1,3 @@ cmake_minimum_required(VERSION 3.19) project("@CASE_NAME@" NONE) -include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake") +include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake" OPTIONAL)
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-config-debug-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-config-debug-stdout.txt index c281c81..04da40e 100644 --- a/Tests/RunCMake/CMakePresetsTest/Good-test-config-debug-stdout.txt +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-config-debug-stdout.txt
@@ -2,4 +2,4 @@ ]*/Tests/RunCMake/CMakePresetsTest/Good/build/default Start 6: debug-only .* -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-config-release-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-config-release-stdout.txt index 66bfd22..e18be0d 100644 --- a/Tests/RunCMake/CMakePresetsTest/Good-test-config-release-stdout.txt +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-config-release-stdout.txt
@@ -2,4 +2,4 @@ ]*/Tests/RunCMake/CMakePresetsTest/Good/build/default Start 6: release-only .* -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-exclude-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-exclude-stdout.txt index 5e990de..35cf4d6 100644 --- a/Tests/RunCMake/CMakePresetsTest/Good-test-exclude-stdout.txt +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-exclude-stdout.txt
@@ -4,4 +4,4 @@ .* Start 4: testd .* -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-index-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-index-stdout.txt index 2df6fcb..44de61c 100644 --- a/Tests/RunCMake/CMakePresetsTest/Good-test-index-stdout.txt +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-index-stdout.txt
@@ -4,4 +4,4 @@ .* Start 3: testc .* -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-indexFile-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-indexFile-stdout.txt index 1366876..71a36e2 100644 --- a/Tests/RunCMake/CMakePresetsTest/Good-test-indexFile-stdout.txt +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-indexFile-stdout.txt
@@ -4,4 +4,4 @@ .* Start 4: testd .* -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-jobsProc-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-jobsProc-stdout.txt index c59dcd3..cb578ed 100644 --- a/Tests/RunCMake/CMakePresetsTest/Good-test-jobsProc-stdout.txt +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-jobsProc-stdout.txt
@@ -1,4 +1,4 @@ Test project [^ ]*/Tests/RunCMake/CMakePresetsTest/Good/build/default .* -100% tests passed, 0 tests failed out of 5 +100% tests passed out of 5
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-noEnvironment-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-noEnvironment-stdout.txt index 8b4845e..da768cd 100644 --- a/Tests/RunCMake/CMakePresetsTest/Good-test-noEnvironment-stdout.txt +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-noEnvironment-stdout.txt
@@ -5,4 +5,4 @@ .* 5: TEST_ENV_REF=xx .* -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset.cmake b/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsTest/InvalidJobs.cmake b/Tests/RunCMake/CMakePresetsTest/InvalidJobs.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsTest/InvalidJobs.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsTest/ListPresets.cmake b/Tests/RunCMake/CMakePresetsTest/ListPresets.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsTest/ListPresets.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt index a70497f..d414ed8 100644 --- a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt +++ b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt
@@ -1,3 +1,3 @@ CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset: -Invalid preset: "noConfigurePreset"$ +Invalid test preset: "noConfigurePreset": Test presets must either be hidden or have an associated configure preset$
diff --git a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.cmake b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsTest/NoTestsAction.cmake b/Tests/RunCMake/CMakePresetsTest/NoTestsAction.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CMakePresetsTest/NoTestsAction.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.cmake new file mode 100644 index 0000000..3b3b9f9 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.cmake
@@ -0,0 +1,5 @@ +enable_testing() +add_test(testa ${CMAKE_COMMAND} -E echo testa) +add_test(testb ${CMAKE_COMMAND} -E echo testb) +add_test(testc ${CMAKE_COMMAND} -E echo testc) +add_test(testd ${CMAKE_COMMAND} -E echo testd)
diff --git a/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.json.in new file mode 100644 index 0000000..7fb2df5 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.json.in
@@ -0,0 +1,16 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ], + "testPresets": [ + { + "name": "default-from-other-file", + "configurePreset": "default" + } + ] +}
diff --git a/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFileListPresets-test-x-stdout.txt b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFileListPresets-test-x-stdout.txt new file mode 100644 index 0000000..7623488 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFileListPresets-test-x-stdout.txt
@@ -0,0 +1,3 @@ +^Available test presets: + + "default-from-other-file"$
diff --git a/Tests/RunCMake/CMakePresetsTest/Passthrough-test-basic-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Passthrough-test-basic-stdout.txt new file mode 100644 index 0000000..e600e4c --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Passthrough-test-basic-stdout.txt
@@ -0,0 +1,3 @@ +1: Test command: [^ +]* "-E" "echo" "base_output" "--extra-flag" +.*
diff --git a/Tests/RunCMake/CMakePresetsTest/Passthrough-test-execOnly-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Passthrough-test-execOnly-stdout.txt new file mode 100644 index 0000000..93e76d0 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Passthrough-test-execOnly-stdout.txt
@@ -0,0 +1,3 @@ +1: Test command: [^ +]* "-E" "echo" "base_output" "--only-arg" +.*
diff --git a/Tests/RunCMake/CMakePresetsTest/Passthrough-test-inherit-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Passthrough-test-inherit-stdout.txt new file mode 100644 index 0000000..5788920 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Passthrough-test-inherit-stdout.txt
@@ -0,0 +1,3 @@ +1: Test command: [^ +]* "-E" "echo" "base_output" "--parent-arg" +.*
diff --git a/Tests/RunCMake/CMakePresetsTest/Passthrough.cmake b/Tests/RunCMake/CMakePresetsTest/Passthrough.cmake new file mode 100644 index 0000000..a398f9e --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Passthrough.cmake
@@ -0,0 +1,2 @@ +enable_testing() +add_test(echo_test ${CMAKE_COMMAND} -E echo base_output)
diff --git a/Tests/RunCMake/CMakePresetsTest/Passthrough.json.in b/Tests/RunCMake/CMakePresetsTest/Passthrough.json.in new file mode 100644 index 0000000..6fe7ac2 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Passthrough.json.in
@@ -0,0 +1,47 @@ +{ + "version": 12, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}" + } + ], + "testPresets": [ + { + "name": "basic", + "configurePreset": "default", + "output": { + "verbosity": "verbose" + }, + "execution": { + "testPassthroughArguments": ["--extra-flag"] + } + }, + { + "name": "parent", + "hidden": true, + "execution": { + "testPassthroughArguments": ["--parent-arg"] + } + }, + { + "name": "inherit", + "inherits": "parent", + "configurePreset": "default", + "output": { + "verbosity": "verbose" + } + }, + { + "name": "execOnly", + "configurePreset": "default", + "output": { + "verbosity": "verbose" + }, + "execution": { + "testPassthroughArguments": ["--only-arg"] + } + } + ] +}
diff --git a/Tests/RunCMake/CMakePresetsTest/PassthroughCombined-test-combined-stdout.txt b/Tests/RunCMake/CMakePresetsTest/PassthroughCombined-test-combined-stdout.txt new file mode 100644 index 0000000..e572da9 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PassthroughCombined-test-combined-stdout.txt
@@ -0,0 +1,3 @@ +1: Test command: [^ +]* "-E" "echo" "base_output" "--preset-arg" "--cli-arg" +.*
diff --git a/Tests/RunCMake/CMakePresetsTest/PassthroughCombined.cmake b/Tests/RunCMake/CMakePresetsTest/PassthroughCombined.cmake new file mode 100644 index 0000000..a398f9e --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PassthroughCombined.cmake
@@ -0,0 +1,2 @@ +enable_testing() +add_test(echo_test ${CMAKE_COMMAND} -E echo base_output)
diff --git a/Tests/RunCMake/CMakePresetsTest/PassthroughCombined.json.in b/Tests/RunCMake/CMakePresetsTest/PassthroughCombined.json.in new file mode 100644 index 0000000..37beede --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PassthroughCombined.json.in
@@ -0,0 +1,22 @@ +{ + "version": 12, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}" + } + ], + "testPresets": [ + { + "name": "combined", + "configurePreset": "default", + "output": { + "verbosity": "verbose" + }, + "execution": { + "testPassthroughArguments": ["--preset-arg"] + } + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported-test-x-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported-test-x-result.txt
diff --git a/Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported-test-x-stderr.txt new file mode 100644 index 0000000..a3aaf4f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported-test-x-stderr.txt
@@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported: +File version must be 12 or higher for testPassthroughArguments preset support$
diff --git a/Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported.json.in b/Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported.json.in new file mode 100644 index 0000000..c6edbcc --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PassthroughUnsupported.json.in
@@ -0,0 +1,19 @@ +{ + "version": 11, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ], + "testPresets": [ + { + "name": "default", + "configurePreset": "default", + "execution": { + "testPassthroughArguments": ["--extra"] + } + } + ] +}
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-result.txt
diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-stderr.txt b/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-stderr.txt new file mode 100644 index 0000000..5235262 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: '--presets-file' requires an argument$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsTest/PresetsNoArg-test-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsTest/PresetsNoArg-test-result.txt
diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsNoArg-test-stderr.txt b/Tests/RunCMake/CMakePresetsTest/PresetsNoArg-test-stderr.txt new file mode 100644 index 0000000..c477131 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PresetsNoArg-test-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: '--preset' requires an argument$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsTest/PresetsNoArgEq-test-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsTest/PresetsNoArgEq-test-result.txt
diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsNoArgEq-test-stderr.txt b/Tests/RunCMake/CMakePresetsTest/PresetsNoArgEq-test-stderr.txt new file mode 100644 index 0000000..c477131 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PresetsNoArgEq-test-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: '--preset' requires an argument$
diff --git a/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake index d7fcb48..2b320f5 100644 --- a/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
@@ -37,6 +37,12 @@ configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY) endif() + set(_presets_file) + if(CMakePresets_FILE_ARG) + set(_presets_file "--presets-file=${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}") + configure_file("${RunCMake_SOURCE_DIR}/${CMakePresets_FILE_ARG}.in" "${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}" @ONLY) + endif() + foreach(ASSET ${CMakePresetsTest_ASSETS}) configure_file("${RunCMake_SOURCE_DIR}/${ASSET}" "${RunCMake_TEST_SOURCE_DIR}" COPYONLY) endforeach() @@ -65,11 +71,11 @@ if(eq) run_cmake_command(${name}-test-${TEST_PRESET} - ${CMAKE_CTEST_COMMAND} "--preset=${TEST_PRESET}" ${ARGN}) + ${CMAKE_CTEST_COMMAND} "--preset=${TEST_PRESET}" ${_presets_file} ${ARGN}) set(eq 0) else() run_cmake_command(${name}-test-${TEST_PRESET} - ${CMAKE_CTEST_COMMAND} "--preset" "${TEST_PRESET}" ${ARGN}) + ${CMAKE_CTEST_COMMAND} "--preset" "${TEST_PRESET}" ${_presets_file} ${ARGN}) set(eq 1) endif() endforeach() @@ -95,6 +101,10 @@ set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/Good.json.in") run_cmake_test_presets(ListPresets "" "" "x" "--list-presets") +run_cmake_command(PresetsNoArg-test ${CMAKE_CTEST_COMMAND} "--preset") +run_cmake_command(PresetsNoArgEq-test ${CMAKE_CTEST_COMMAND} "--preset=") +run_cmake_command(PresetsFileNoArg-test ${CMAKE_CTEST_COMMAND} "--presets-file") + set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/Condition.json.in") run_cmake_test_presets(ConditionListPresets "" "" "x" "--list-presets") unset(CMakePresetsTest_NO_CONFIGURE) @@ -113,8 +123,14 @@ run_cmake_test_presets(OutputJUnitUnsupported "" "" "x") run_cmake_test_presets(InvalidJobs "" "" "x") run_cmake_test_presets(JobsProcUnsupported "" "" "x") +run_cmake_test_presets(PassthroughUnsupported "" "" "x") set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) run_cmake_test_presets(ConfigurePresetUnreachable "" "" "x") set(CMakePresetsTest_NO_CONFIGURE 0) -set(CMakePresetsTest_NO_BUILD 0) +run_cmake_test_presets(Passthrough "default" "" "basic;inherit;execOnly") +run_cmake_test_presets(PassthroughCombined "default" "" "combined" "--" "--cli-arg") + +set(CMakePresets_FILE_ARG OtherCMakePresetsFile.json) +run_cmake_test_presets(OtherCMakePresetsFile "default" "" "default-from-other-file") +run_cmake_test_presets(OtherCMakePresetsFileListPresets "" "" "x" "--list-presets")
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/CMakeLists.txt.in b/Tests/RunCMake/CMakePresetsWorkflow/CMakeLists.txt.in index 129184a..2f5cf8e 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/CMakeLists.txt.in +++ b/Tests/RunCMake/CMakePresetsWorkflow/CMakeLists.txt.in
@@ -1,3 +1,3 @@ cmake_minimum_required(VERSION 3.19) project("@CASE_NAME@" NONE) -include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake") +include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake" OPTIONAL)
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt index 8e23c9b..f5644e7 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt
@@ -1,6 +1,7 @@ ^Unknown argument -DINVALID_OPTION Usage: cmake --workflow <options> Options: - --preset <preset> = Workflow preset to execute\. - --list-presets = List available workflow presets\. - --fresh = Configure a fresh build tree, removing any existing cache file\.$ + --preset <preset> = Workflow preset to execute\. + --presets-file <file> = Path to a presets file\. + --list-presets = List available workflow presets\. + --fresh = Configure a fresh build tree, removing any existing cache file\.$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.cmake new file mode 100644 index 0000000..31ce7ff --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.cmake
@@ -0,0 +1,8 @@ +message(STATUS "Testing the configure step at ${CMAKE_BINARY_DIR}") + +add_custom_target(echo_test ALL COMMAND ${CMAKE_COMMAND} -E echo "Testing the build step at ${CMAKE_BINARY_DIR}") + +enable_testing() +add_test(NAME EchoTest COMMAND ${CMAKE_COMMAND} -E echo "Testing the test step at ${CMAKE_BINARY_DIR}") + +include(CPack)
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.json.in new file mode 100644 index 0000000..4b3f7c3 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.json.in
@@ -0,0 +1,57 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "default", + "binaryDir": "${sourceDir}/build", + "generator": "@RunCMake_GENERATOR@" + } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default", + "configuration": "Debug" + } + ], + "testPresets": [ + { + "name": "default", + "configurePreset": "default", + "configuration": "Debug" + } + ], + "packagePresets": [ + { + "name": "default", + "configurePreset": "default", + "generators": [ + "External" + ], + "configurations": ["Debug"] + } + ], + "workflowPresets": [ + { + "name": "OtherCMakePresetsFile", + "steps": [ + { + "type": "configure", + "name": "default" + }, + { + "type": "build", + "name": "default" + }, + { + "type": "test", + "name": "default" + }, + { + "type": "package", + "name": "default" + } + ] + } + ] +}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFileListPresets-stdout.txt b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFileListPresets-stdout.txt new file mode 100644 index 0000000..194fbfd --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFileListPresets-stdout.txt
@@ -0,0 +1,3 @@ +^Available workflow presets: + + "OtherCMakePresetsFile"$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-result.txt
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-stderr.txt new file mode 100644 index 0000000..50d4e2b --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No file specified for --presets-file
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArg-workflow-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArg-workflow-result.txt
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArg-workflow-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArg-workflow-stderr.txt new file mode 100644 index 0000000..aa348ab --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArg-workflow-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No preset specified for --preset
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArgEq-workflow-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArgEq-workflow-result.txt
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArgEq-workflow-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArgEq-workflow-stderr.txt new file mode 100644 index 0000000..aa348ab --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/PresetsNoArgEq-workflow-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: No preset specified for --preset
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake index 8c1002d..304d6ca 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake
@@ -35,6 +35,12 @@ configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY) endif() + set(_presets_file) + if(CMakePresets_FILE_ARG) + set(_presets_file "--presets-file=${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}") + configure_file("${RunCMake_SOURCE_DIR}/${CMakePresets_FILE_ARG}.in" "${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}" @ONLY) + endif() + foreach(ASSET ${CMakePresets_ASSETS}) configure_file("${RunCMake_SOURCE_DIR}/${ASSET}.in" "${RunCMake_TEST_SOURCE_DIR}/${ASSET}" @ONLY) endforeach() @@ -54,7 +60,7 @@ set(eq 1 PARENT_SCOPE) set(preset_arg "--preset" "${name}") endif() - run_cmake_command("${name}" "${CMAKE_COMMAND}" "--workflow" ${preset_arg} ${ARGN}) + run_cmake_command("${name}" "${CMAKE_COMMAND}" "--workflow" ${preset_arg} ${_presets_file} ${ARGN}) endfunction() set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) @@ -80,7 +86,17 @@ unset(CMakeUserPresets_FILE) unset(CMakePresets_ASSETS) +set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/DoesNotExist.json.in") +set(CMakePresets_FILE_ARG "OtherCMakePresetsFile.json") +run_cmake_workflow_presets(OtherCMakePresetsFile) +run_cmake_workflow_presets(OtherCMakePresetsFileListPresets --list-presets) +unset(CMakePresets_FILE) +unset(CMakePresets_FILE_ARG) + run_cmake_workflow_presets(ListPresets --list-presets) +run_cmake_command(PresetsNoArg-workflow ${CMAKE_COMMAND} "--workflow" "--preset") +run_cmake_command(PresetsNoArgEq-workflow ${CMAKE_COMMAND} "--workflow" "--preset=") +run_cmake_command(PresetsFileNoArg-workflow ${CMAKE_COMMAND} "--workflow" "--presets-file") run_cmake_workflow_presets(InvalidOption -DINVALID_OPTION) set(RunCMake_TEST_NO_CLEAN TRUE)
diff --git a/Tests/RunCMake/CPack/DEB/Helpers.cmake b/Tests/RunCMake/CPack/DEB/Helpers.cmake index 7471446..3e9cc9d 100644 --- a/Tests/RunCMake/CPack/DEB/Helpers.cmake +++ b/Tests/RunCMake/CPack/DEB/Helpers.cmake
@@ -145,13 +145,14 @@ " Content: '${content_}'") endif() - execute_process(COMMAND ls -l "${CMAKE_CURRENT_BINARY_DIR}/control_${PREFIX}/${FILE_}" - OUTPUT_VARIABLE package_permissions_ - ERROR_VARIABLE package_permissions_error_ - OUTPUT_STRIP_TRAILING_WHITESPACE) - + execute_process( + COMMAND stat --format=%A "${CMAKE_CURRENT_BINARY_DIR}/control_${PREFIX}/${FILE_}" + OUTPUT_VARIABLE package_permissions_ + ERROR_VARIABLE package_permissions_error_ + OUTPUT_STRIP_TRAILING_WHITESPACE + ) if(NOT package_permissions_error_) - if(NOT package_permissions_ MATCHES "${${PREFIX}_${FILE_}_permissions_regex}") + if(NOT package_permissions_ STREQUAL ${PREFIX}_${FILE_}_permissions) message(FATAL_ERROR "Unexpected file permissions for ${PREFIX}_${FILE_}: '${package_permissions_}'!") endif() else()
diff --git a/Tests/RunCMake/CPack/TAR/packaging_COMPONENT_default.cmake b/Tests/RunCMake/CPack/TAR/packaging_COMPONENT_default.cmake index 81a5035..a6801f3 100644 --- a/Tests/RunCMake/CPack/TAR/packaging_COMPONENT_default.cmake +++ b/Tests/RunCMake/CPack/TAR/packaging_COMPONENT_default.cmake
@@ -1 +1,2 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL "ON") +set(CPACK_ARCHIVE_ENCODING "OEM")
diff --git a/Tests/RunCMake/CPack/ZIP/packaging_COMPONENT_default.cmake b/Tests/RunCMake/CPack/ZIP/packaging_COMPONENT_default.cmake index 81a5035..a6801f3 100644 --- a/Tests/RunCMake/CPack/ZIP/packaging_COMPONENT_default.cmake +++ b/Tests/RunCMake/CPack/ZIP/packaging_COMPONENT_default.cmake
@@ -1 +1,2 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL "ON") +set(CPACK_ARCHIVE_ENCODING "OEM")
diff --git a/Tests/RunCMake/CPack/ZIP_DEFLATE/packaging_COMPONENT_default.cmake b/Tests/RunCMake/CPack/ZIP_DEFLATE/packaging_COMPONENT_default.cmake index 2a57279..e1f8e70 100644 --- a/Tests/RunCMake/CPack/ZIP_DEFLATE/packaging_COMPONENT_default.cmake +++ b/Tests/RunCMake/CPack/ZIP_DEFLATE/packaging_COMPONENT_default.cmake
@@ -1,2 +1,3 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL "ON") set(CPACK_ARCHIVE_COMPRESSION_LEVEL 4) +set(CPACK_ARCHIVE_ENCODING "UTF-8")
diff --git a/Tests/RunCMake/CPack/tests/CUSTOM_BINARY_SPEC_FILE/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/CUSTOM_BINARY_SPEC_FILE/VerifyResult.cmake new file mode 100644 index 0000000..20b20c8 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/CUSTOM_BINARY_SPEC_FILE/VerifyResult.cmake
@@ -0,0 +1,27 @@ +cmake_path(APPEND bin_dir _CPack_Packages/Linux/RPM/SPECS OUTPUT_VARIABLE SPECS) + +file(GLOB specs "${SPECS}/*.spec") +if(NOT specs) + return() +endif() + +set(_pfx multilene_defines_test) +set( + expected_defines + "%define ${_pfx}_satu 1" + "%define ${_pfx}_dua 2" + "%define ${_pfx}_tiga 3" + ) + +foreach(spec IN LISTS specs) + file(STRINGS "${spec}" defines REGEX "%define multilene_defines_test_*.") + if(NOT defines STREQUAL expected_defines) + message( + FATAL_ERROR + "Multiline definitions mismatch:\n" + " file: `${spec}`\n" + " expected: `${expected_defines}`\n" + " actual: `${defines}`\n" + ) + endif() +endforeach()
diff --git a/Tests/RunCMake/CPack/tests/CUSTOM_BINARY_SPEC_FILE/test.cmake b/Tests/RunCMake/CPack/tests/CUSTOM_BINARY_SPEC_FILE/test.cmake index a604863..da5f6fc 100644 --- a/Tests/RunCMake/CPack/tests/CUSTOM_BINARY_SPEC_FILE/test.cmake +++ b/Tests/RunCMake/CPack/tests/CUSTOM_BINARY_SPEC_FILE/test.cmake
@@ -1,5 +1,10 @@ install(FILES CMakeLists.txt DESTINATION foo COMPONENT test) +set(_pfx multilene_defines_test) +set(CPACK_RPM_SPEC_MORE_DEFINE "%define ${_pfx}_satu 1") +list(APPEND CPACK_RPM_SPEC_MORE_DEFINE "%define ${_pfx}_dua 2") +list(APPEND CPACK_RPM_SPEC_MORE_DEFINE "%define ${_pfx}_tiga 3") + if(PACKAGING_TYPE STREQUAL "MONOLITHIC") set(CPACK_RPM_USER_BINARY_SPECFILE "${CMAKE_CURRENT_LIST_DIR}/custom.spec.in") elseif(PACKAGING_TYPE STREQUAL "COMPONENT")
diff --git a/Tests/RunCMake/CPack/tests/DEBUGINFO/RPM-COMPONENT-stderr.txt b/Tests/RunCMake/CPack/tests/DEBUGINFO/RPM-COMPONENT-stderr.txt index 5d0da27..07ad074 100644 --- a/Tests/RunCMake/CPack/tests/DEBUGINFO/RPM-COMPONENT-stderr.txt +++ b/Tests/RunCMake/CPack/tests/DEBUGINFO/RPM-COMPONENT-stderr.txt
@@ -1,11 +1,11 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(author\) at [^ ]*/Modules/Internal/CPack/CPackRPM\.cmake:[0-9]+ \(message\): CPackRPM:Warning: debuginfo package was requested but will not be generated as no source files were found! Component: 'appheaders'\. Call Stack \(most recent call first\): [^ ]*/Modules/Internal/CPack/CPackRPM\.cmake:[0-9]+ \(cpack_rpm_generate_package\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CPackRPM: Will use GENERATED spec file: [^ ]*/Tests/RunCMake/RPM\.DEBUGINFO/CPack/DEBUGINFO-build/_CPack_Packages/Linux/RPM/SPECS/Debuginfo-appheaders\.spec
diff --git a/Tests/RunCMake/CPack/tests/DEBUGINFO/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/DEBUGINFO/VerifyResult.cmake new file mode 100644 index 0000000..5ef05a3 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/DEBUGINFO/VerifyResult.cmake
@@ -0,0 +1,19 @@ +if(NOT GENERATOR_TYPE STREQUAL "DEB") + return() +endif() + +set(whitespaces_ "[\t\n\r ]*") +set(hashsyms_ "[a-f0-9]+") + +set(dbgsym_md5sums_md5sums + "^(${hashsyms_} usr/lib/debug/\\.build-id/[^/\r\n]+/[^/\r\n]+\\.debug${whitespaces_})+$") +set(dbgsym_md5sums_md5sums_permissions "-rw-r--r--") + +if(PACKAGING_TYPE STREQUAL "COMPONENT") + verifyDebControl("${FOUND_FILE_4}" "dbgsym_md5sums" "md5sums") + verifyDebControl("${FOUND_FILE_5}" "dbgsym_md5sums" "md5sums") + +elseif(PACKAGING_TYPE STREQUAL "MONOLITHIC") + verifyDebControl("${FOUND_FILE_2}" "dbgsym_md5sums" "md5sums") + +endif()
diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt index 5544885..003c396 100644 --- a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt +++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt
@@ -1 +1 @@ -CPack Error: ERROR The data in files with the same filename is different.* +CPack Error: ERROR The data in files with the same filename is different: files/1\.txt
diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt index 5544885..ac23e71 100644 --- a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt +++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt
@@ -1 +1 @@ -CPack Error: ERROR The data in files with the same filename is different.* +CPack Error: ERROR The data in files with the same filename is different: files/symlink2
diff --git a/Tests/RunCMake/CPack/tests/EXTRA/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/EXTRA/VerifyResult.cmake index 85e137f..1dd1a75 100644 --- a/Tests/RunCMake/CPack/tests/EXTRA/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/tests/EXTRA/VerifyResult.cmake
@@ -1,18 +1,18 @@ set(foo_preinst "^echo default_preinst$") # NOTE: optional dot at the end of permissions regex is for SELinux enabled systems -set(foo_preinst_permissions_regex "-rwxr-xr-x\.? .*") +set(foo_preinst_permissions "-rwxr-xr-x") set(foo_prerm "^echo default_prerm$") -set(foo_prerm_permissions_regex "-rwxr-xr-x\.? .*") +set(foo_prerm_permissions "-rwxr-xr-x") verifyDebControl("${FOUND_FILE_1}" "foo" "preinst;prerm") set(bar_preinst "^echo bar_preinst$") -set(bar_preinst_permissions_regex "-rwx------\.? .*") +set(bar_preinst_permissions "-rwx------") set(bar_prerm "^echo bar_prerm$") -set(bar_prerm_permissions_regex "-rwx------\.? .*") +set(bar_prerm_permissions "-rwx------") verifyDebControl("${FOUND_FILE_2}" "bar" "preinst;prerm") set(bas_preinst "^echo default_preinst$") -set(bas_preinst_permissions_regex "-rwxr-xr-x\.? .*") +set(bas_preinst_permissions "-rwxr-xr-x") set(bas_prerm "^echo default_prerm$") -set(bas_prerm_permissions_regex "-rwxr-xr-x\.? .*") +set(bas_prerm_permissions "-rwxr-xr-x") verifyDebControl("${FOUND_FILE_3}" "bas" "preinst;prerm")
diff --git a/Tests/RunCMake/CPack/tests/GENERATE_SHLIBS/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/GENERATE_SHLIBS/VerifyResult.cmake index 8cefeea..894b27c 100644 --- a/Tests/RunCMake/CPack/tests/GENERATE_SHLIBS/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/tests/GENERATE_SHLIBS/VerifyResult.cmake
@@ -5,5 +5,5 @@ endif() # optional dot at the end of permissions regex is for SELinux enabled systems -set(shlibs_shlibs_permissions_regex "-rw-r--r--\.? .*") +set(shlibs_shlibs_permissions "-rw-r--r--") verifyDebControl("${FOUND_FILE_1}" "shlibs" "shlibs")
diff --git a/Tests/RunCMake/CPack/tests/GENERATE_SHLIBS_LDCONFIG/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/GENERATE_SHLIBS_LDCONFIG/VerifyResult.cmake index 422cfbe..edb1846 100644 --- a/Tests/RunCMake/CPack/tests/GENERATE_SHLIBS_LDCONFIG/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/tests/GENERATE_SHLIBS_LDCONFIG/VerifyResult.cmake
@@ -1,8 +1,37 @@ +find_program(DPKG_EXECUTABLE dpkg) +set(_dpkg_is_old FALSE) +if(DPKG_EXECUTABLE) + execute_process( + COMMAND "${DPKG_EXECUTABLE}" --version + OUTPUT_VARIABLE DPKG_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(_dpkg_version_re "Debian 'dpkg' package management program version ([0-9]+\.[0-9]+\.[0-9]+).*") + if(DPKG_VERSION MATCHES "${_dpkg_version_re}") + set(DPKG_VERSION "${CMAKE_MATCH_1}") + if(DPKG_VERSION VERSION_LESS 1.18.3) + set(_dpkg_is_old TRUE) + endif() + else() + set(_dpkg_is_old TRUE) + endif() +else() + set(_dpkg_is_old TRUE) +endif() + +set(expected_files shlibs) set(shlibs_shlibs "^libtest_lib 0\\.8 generate_shlibs_ldconfig \\(>\\= 0\\.1\\.1\\)\n$") -# NOTE: optional dot at the end of permissions regex is for SELinux enabled systems -set(shlibs_shlibs_permissions_regex "-rw-r--r--\.? .*") -set(shlibs_postinst ".*ldconfig.*") -set(shlibs_postinst_permissions_regex "-rwxr-xr-x\.? .*") -set(shlibs_postrm ".*ldconfig.*") -set(shlibs_postrm_permissions_regex "-rwxr-xr-x\.? .*") -verifyDebControl("${FOUND_FILE_1}" "shlibs" "shlibs;postinst;postrm") +set(shlibs_shlibs_permissions "-rw-r--r--") +if(_dpkg_is_old) + list(APPEND expected_files postinst postrm) + set(shlibs_postinst ".*ldconfig.*") + set(shlibs_postinst_permissions "-rwxr-xr-x") + + set(shlibs_postrm ".*ldconfig.*") + set(shlibs_postrm_permissions "-rwxr-xr-x") +else() + list(APPEND expected_files triggers) + set(shlibs_triggers ".*activate-noawait ldconfig.*") + set(shlibs_triggers_permissions "-rw-r--r--") +endif() +verifyDebControl("${FOUND_FILE_1}" "shlibs" "${expected_files}")
diff --git a/Tests/RunCMake/CPack/tests/MD5SUMS/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/MD5SUMS/VerifyResult.cmake index 13af097..8b12e57 100644 --- a/Tests/RunCMake/CPack/tests/MD5SUMS/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/tests/MD5SUMS/VerifyResult.cmake
@@ -1,4 +1,5 @@ set(whitespaces_ "[\t\n\r ]*") set(hashsyms_ "[a-f0-9]+") set(md5sums_md5sums "^${hashsyms_} usr/bar/CMakeLists\.txt${whitespaces_}${hashsyms_} usr/baz/CMakeLists\.txt${whitespaces_}${hashsyms_} usr/foo/CMakeLists\.txt${whitespaces_}$") +set(md5sums_md5sums_permissions "-rw-r--r--") verifyDebControl("${FOUND_FILE_1}" "md5sums" "md5sums")
diff --git a/Tests/RunCMake/CPack/tests/MULTIARCH/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/MULTIARCH/VerifyResult.cmake index f4e8050..1dfed5c 100644 --- a/Tests/RunCMake/CPack/tests/MULTIARCH/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/tests/MULTIARCH/VerifyResult.cmake
@@ -1,4 +1,5 @@ if(NOT ${RunCMake_SUBTEST_SUFFIX} STREQUAL "fail") set(MULTIARCH_control "Multi-Arch: ${RunCMake_SUBTEST_SUFFIX}") + set(MULTIARCH_control_permissions "-rw-r--r--") verifyDebControl("${FOUND_FILE_1}" "MULTIARCH" "control") endif ()
diff --git a/Tests/RunCMake/CPack/tests/SYMLINKS/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/SYMLINKS/VerifyResult.cmake index 0632319..9e57365 100644 --- a/Tests/RunCMake/CPack/tests/SYMLINKS/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/tests/SYMLINKS/VerifyResult.cmake
@@ -6,21 +6,18 @@ file(GLOB_RECURSE symlink_files RELATIVE "${bin_dir}" "${bin_dir}/*/symlink_*") foreach(check_symlink IN LISTS symlink_files) - get_filename_component(symlink_name "${check_symlink}" NAME) - execute_process(COMMAND ls -la "${check_symlink}" - WORKING_DIRECTORY "${bin_dir}" - OUTPUT_VARIABLE SYMLINK_POINT_ - OUTPUT_STRIP_TRAILING_WHITESPACE) + cmake_path(GET check_symlink FILENAME symlink_name) + file(READ_SYMLINK "${check_symlink}" symlink_dst) if("${symlink_name}" STREQUAL "symlink_to_empty_dir") - string(REGEX MATCH "^.*${whitespaces}->${whitespaces}empty_dir$" check_symlink "${SYMLINK_POINT_}") + string(REGEX MATCH "^empty_dir$" check_symlink "${symlink_dst}") elseif("${symlink_name}" STREQUAL "symlink_to_non_empty_dir") - string(REGEX MATCH "^.*${whitespaces}->${whitespaces}non_empty_dir$" check_symlink "${SYMLINK_POINT_}") + string(REGEX MATCH "^non_empty_dir$" check_symlink "${symlink_dst}") else() message(FATAL_ERROR "error: unexpected rpm symbolic link '${check_symlink}'") endif() if(NOT check_symlink) - message(FATAL_ERROR "symlink points to unexpected location '${SYMLINK_POINT_}'") + message(FATAL_ERROR "symlink points to unexpected location '${symlink_dst}'") endif() endforeach()
diff --git a/Tests/RunCMake/CPackConfig/CMP0133-WARN-stderr.txt b/Tests/RunCMake/CPackConfig/CMP0133-WARN-stderr.txt index e1626ae..e759821 100644 --- a/Tests/RunCMake/CPackConfig/CMP0133-WARN-stderr.txt +++ b/Tests/RunCMake/CPackConfig/CMP0133-WARN-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/CPack\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/CPack\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0133 is not set: The CPack module disables SLA by default in the CPack DragNDrop Generator\. Run "cmake --help-policy CMP0133" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -9,4 +9,5 @@ Generator\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CPackConfig/CMP0161-WARN-stderr-darwin.txt b/Tests/RunCMake/CPackConfig/CMP0161-WARN-stderr-darwin.txt index 5841ec7..321330d 100644 --- a/Tests/RunCMake/CPackConfig/CMP0161-WARN-stderr-darwin.txt +++ b/Tests/RunCMake/CPackConfig/CMP0161-WARN-stderr-darwin.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/CPack\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/CPack\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0161 is not set: CPACK_PRODUCTBUILD_DOMAINS defaults to true\. Run "cmake --help-policy CMP0161" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -9,4 +9,5 @@ NEW will prevent this warning\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CPackConfig/CMP0172-WARN-stderr.txt b/Tests/RunCMake/CPackConfig/CMP0172-WARN-stderr.txt index 28025df..63ab3f5 100644 --- a/Tests/RunCMake/CPackConfig/CMP0172-WARN-stderr.txt +++ b/Tests/RunCMake/CPackConfig/CMP0172-WARN-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/CPack\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/CPack\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0172 is not set: The CPack module enables per-machine installation by default in the CPack WIX Generator\. Run "cmake --help-policy CMP0172" for policy details\. Use the cmake_policy command to @@ -9,4 +9,5 @@ default in the CPack WIX Generator\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CPackConfig/CMP0206-WARN-stderr.txt b/Tests/RunCMake/CPackConfig/CMP0206-WARN-stderr.txt index ce6f443..a6930a1 100644 --- a/Tests/RunCMake/CPackConfig/CMP0206-WARN-stderr.txt +++ b/Tests/RunCMake/CPackConfig/CMP0206-WARN-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/CPack\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/CPack\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0206 is not set: The CPack Archive Generator defaults to UID 0 and GID 0\. Run "cmake --help-policy CMP0206" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -7,4 +7,5 @@ For compatibility, CMake will set archive UID/GID to -1/-1\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-OLD-stderr.txt b/Tests/RunCMake/CTest/CMP0145-Dart-OLD-stderr.txt index 3624549..6fa95e5 100644 --- a/Tests/RunCMake/CTest/CMP0145-Dart-OLD-stderr.txt +++ b/Tests/RunCMake/CTest/CMP0145-Dart-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0145-Dart-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0145-Dart-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0145 will be removed from a future version of CMake\. @@ -8,8 +8,10 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:5 \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Deprecation Warning at [^ +CMake Warning \(deprecated\) at [^ ]*/Modules/Dart\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0145 will be removed from a future version of CMake\. @@ -20,4 +22,6 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMP0145-Dart-OLD\.cmake:3 \(include\) - CMakeLists\.txt:5 \(include\)$ + CMakeLists\.txt:5 \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt b/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt index e8ec493..af5d60b 100644 --- a/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt +++ b/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt
@@ -1,24 +1,25 @@ -^CMake Warning \(dev\) at CMP0145-Dart-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0145-Dart-WARN\.cmake:[0-9]+ \(include\): Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at [^ -]*/Modules/Dart\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at [^ +]*/Modules/Dart\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): CMP0145-Dart-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Deprecation Warning at [^ -]*/Modules/Dart\.cmake:[0-9]+6 \(cmake_policy\): +CMake Warning \(deprecated\) at [^ +]*/Modules/Dart\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0145 will be removed from a future version of CMake\. @@ -28,4 +29,6 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMP0145-Dart-WARN\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-OLD-stderr.txt b/Tests/RunCMake/CTest/CMP0145-FindDart-OLD-stderr.txt index e11ffc6..ad82835 100644 --- a/Tests/RunCMake/CTest/CMP0145-FindDart-OLD-stderr.txt +++ b/Tests/RunCMake/CTest/CMP0145-FindDart-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0145-FindDart-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0145-FindDart-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0145 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt index d076235..e56d0b0 100644 --- a/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt +++ b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0145-FindDart-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0145-FindDart-WARN\.cmake:[0-9]+ \(include\): Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/CTest/SingleConfig-test-stdout.txt b/Tests/RunCMake/CTest/SingleConfig-test-stdout.txt index 5771618..7898434 100644 --- a/Tests/RunCMake/CTest/SingleConfig-test-stdout.txt +++ b/Tests/RunCMake/CTest/SingleConfig-test-stdout.txt
@@ -3,6 +3,6 @@ Start 1: SingleConfig 1/1 Test #1: SingleConfig .+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt index b1851b9..24495d0 100644 --- a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt +++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt
@@ -2,6 +2,6 @@ .* +Start 1: CommandExpandList 1/1 Test #1: CommandExpandList +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/CTestCommandLine/BadCMakePresets.json.in b/Tests/RunCMake/CTestCommandLine/BadCMakePresets.json.in new file mode 100644 index 0000000..dcf3148 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/BadCMakePresets.json.in
@@ -0,0 +1 @@ +{ this is not valid json
diff --git a/Tests/RunCMake/CTestCommandLine/CMakePresets.json.in b/Tests/RunCMake/CTestCommandLine/CMakePresets.json.in new file mode 100644 index 0000000..4bd4d58 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/CMakePresets.json.in
@@ -0,0 +1,21 @@ +{ + "version": 1, + "cmakeMinimumRequired": { + "major": 3, + "minor": 18, + "patch": 0 + }, + "configurePresets": [ + { + "name": "my-preset", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "MY_CUSTOM_VAR": { + "type": "STRING", + "value": "this-gets-set" + } + } + } + ] +}
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersist-check.cmake b/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersist-check.cmake new file mode 100644 index 0000000..1b9f1e0 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersist-check.cmake
@@ -0,0 +1,13 @@ +set(dart_file "${RunCMake_TEST_BINARY_DIR}/DartConfiguration.tcl") +if(NOT EXISTS "${dart_file}") + set(RunCMake_TEST_FAILED "DartConfiguration.tcl not found in build dir") + return() +endif() +file(READ "${dart_file}" dart_content) +if(NOT dart_content MATCHES "BuildName: cli-build-name") + set(RunCMake_TEST_FAILED + "DartConfiguration.tcl does not contain BuildName: cli-build-name") +elseif(NOT dart_content MATCHES "Site: cli-site") + set(RunCMake_TEST_FAILED + "DartConfiguration.tcl does not contain Site: cli-site") +endif()
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersistSourceDir-check.cmake b/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersistSourceDir-check.cmake new file mode 100644 index 0000000..ffe4e6c --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersistSourceDir-check.cmake
@@ -0,0 +1,14 @@ +# The preset's binaryDir is "${sourceDir}/build". +set(dart_file "${RunCMake_TEST_SOURCE_DIR}/build/DartConfiguration.tcl") +if(NOT EXISTS "${dart_file}") + set(RunCMake_TEST_FAILED "DartConfiguration.tcl not found in preset binaryDir") + return() +endif() +file(READ "${dart_file}" dart_content) +if(NOT dart_content MATCHES "BuildName: cli-build-name") + set(RunCMake_TEST_FAILED + "DartConfiguration.tcl does not contain BuildName: cli-build-name") +elseif(NOT dart_content MATCHES "Site: cli-site") + set(RunCMake_TEST_FAILED + "DartConfiguration.tcl does not contain Site: cli-site") +endif()
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersistSourceDir-stderr.txt b/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersistSourceDir-stderr.txt new file mode 100644 index 0000000..233b8c4 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigureCLIVarDartPersistSourceDir-stderr.txt
@@ -0,0 +1 @@ +^Cannot find file: .*/DartConfiguration\.tcl$
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVar-check.cmake b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVar-check.cmake new file mode 100644 index 0000000..0cd22a1 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVar-check.cmake
@@ -0,0 +1,25 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif()
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarBadPresets-result.txt similarity index 100% copy from Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt copy to Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarBadPresets-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarBadPresets-stderr.txt b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarBadPresets-stderr.txt new file mode 100644 index 0000000..21e2532 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarBadPresets-stderr.txt
@@ -0,0 +1 @@ +Could not read presets from ".*"
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarBuildDirOverride-check.cmake b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarBuildDirOverride-check.cmake new file mode 100644 index 0000000..0e3b955 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarBuildDirOverride-check.cmake
@@ -0,0 +1,13 @@ +# --build-dir should take precedence over the preset's binaryDir. +# Configure.xml must be in the explicit build dir, not in ${sourceDir}/build. +file(GLOB configure_xml_file + "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(NOT configure_xml_file) + set(RunCMake_TEST_FAILED + "Configure.xml not found in explicit --build-dir (${RunCMake_TEST_BINARY_DIR})") +endif() + +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "Preset binaryDir (${RunCMake_TEST_SOURCE_DIR}/build) was created") +endif()
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarSourceDir-check.cmake b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarSourceDir-check.cmake new file mode 100644 index 0000000..5a7aedc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarSourceDir-check.cmake
@@ -0,0 +1,23 @@ +# The preset's binaryDir is "${sourceDir}/build", so Testing/ should be +# written there, not in the working directory used to invoke ctest. +file(GLOB configure_xml_file + "${RunCMake_TEST_SOURCE_DIR}/build/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found in preset binaryDir") +endif() + +set(cmakecache_file "${RunCMake_TEST_SOURCE_DIR}/build/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found in preset binaryDir") +endif()
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarSourceDir-stderr.txt b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarSourceDir-stderr.txt new file mode 100644 index 0000000..233b8c4 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarSourceDir-stderr.txt
@@ -0,0 +1 @@ +^Cannot find file: .*/DartConfiguration\.tcl$
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarUnknownPreset-result.txt similarity index 100% copy from Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt copy to Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarUnknownPreset-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarUnknownPreset-stderr.txt b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarUnknownPreset-stderr.txt new file mode 100644 index 0000000..aa387b1 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ConfigurePresetCLIVarUnknownPreset-stderr.txt
@@ -0,0 +1,2 @@ +^Cannot find file: .*/DartConfiguration\.tcl +No such configure preset in .*: "nonexistent-preset"$
diff --git a/Tests/RunCMake/CTestCommandLine/CoverageTool-T-Test-stdout.txt b/Tests/RunCMake/CTestCommandLine/CoverageTool-T-Test-stdout.txt new file mode 100644 index 0000000..6371dbb --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/CoverageTool-T-Test-stdout.txt
@@ -0,0 +1,2 @@ +Using environment variable LLVM_PROFILE_FILE=[^ +]*/Tests/RunCMake/CTestCommandLine/CoverageTool/test1_%p\.profraw
diff --git a/Tests/RunCMake/CTestCommandLine/CoverageTool-stdout.txt b/Tests/RunCMake/CTestCommandLine/CoverageTool-stdout.txt new file mode 100644 index 0000000..6371dbb --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/CoverageTool-stdout.txt
@@ -0,0 +1,2 @@ +Using environment variable LLVM_PROFILE_FILE=[^ +]*/Tests/RunCMake/CTestCommandLine/CoverageTool/test1_%p\.profraw
diff --git a/Tests/RunCMake/CTestCommandLine/CustomPrePost-show-only-stdout.txt b/Tests/RunCMake/CTestCommandLine/CustomPrePost-show-only-stdout.txt new file mode 100644 index 0000000..971bc9a --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/CustomPrePost-show-only-stdout.txt
@@ -0,0 +1,5 @@ +^Test project [^ +]+/Tests/RunCMake/CTestCommandLine/CustomPrePost + Test #1: Echo + +Total Tests: 1$
diff --git a/Tests/RunCMake/CTestCommandLine/CustomPrePost-stdout.txt b/Tests/RunCMake/CTestCommandLine/CustomPrePost-stdout.txt new file mode 100644 index 0000000..d65ee95 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/CustomPrePost-stdout.txt
@@ -0,0 +1,10 @@ +^Test project [^ +]+/Tests/RunCMake/CTestCommandLine/CustomPrePost +Custom Pre-Test + Start 1: Echo +1/1 Test #1: Echo \.+ Passed +[0-9\.]+ sec + +100% tests passed out of 1 + +Total Test time \(real\) = +[0-9\.]+ sec +Custom Post-Test$
diff --git a/Tests/RunCMake/CTestCommandLine/DashD-DashboardVar-stdout.txt b/Tests/RunCMake/CTestCommandLine/DashD-DashboardVar-stdout.txt new file mode 100644 index 0000000..b7f7e6c --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/DashD-DashboardVar-stdout.txt
@@ -0,0 +1 @@ +.*Build name: cli-build-name.*
diff --git a/Tests/RunCMake/CTestCommandLine/DashD-ScriptVars-stderr.txt b/Tests/RunCMake/CTestCommandLine/DashD-ScriptVars-stderr.txt new file mode 100644 index 0000000..f82de5f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/DashD-ScriptVars-stderr.txt
@@ -0,0 +1,7 @@ +.*CTEST_BUILD_NAME=cli-build-name.* +.*CTEST_SITE=cli-site.* +.*CTEST_BUILD_FLAGS=-O2 -Wall.* +.*CTEST_BUILD_TARGET=my-target.* +.*CTEST_CMAKE_GENERATOR=Ninja.* +.*CTEST_EXTRA_COVERAGE_GLOB=\*\*/\*.gcov.* +.*CTEST_TIME_LIMIT=3600.*
diff --git a/Tests/RunCMake/CTestCommandLine/DashD-ScriptVars.cmake b/Tests/RunCMake/CTestCommandLine/DashD-ScriptVars.cmake new file mode 100644 index 0000000..ebb881b --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/DashD-ScriptVars.cmake
@@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +# Verify that CTEST_* variables defined via -D on the ctest command line +# are available in the script. +message("CTEST_BUILD_NAME=${CTEST_BUILD_NAME}") +message("CTEST_SITE=${CTEST_SITE}") +message("CTEST_BUILD_FLAGS=${CTEST_BUILD_FLAGS}") +message("CTEST_BUILD_TARGET=${CTEST_BUILD_TARGET}") +message("CTEST_CMAKE_GENERATOR=${CTEST_CMAKE_GENERATOR}") +message("CTEST_EXTRA_COVERAGE_GLOB=${CTEST_EXTRA_COVERAGE_GLOB}") +message("CTEST_TIME_LIMIT=${CTEST_TIME_LIMIT}")
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/LabelCount-stdout.txt b/Tests/RunCMake/CTestCommandLine/LabelCount-stdout.txt index 0e9f937..e3586a3 100644 --- a/Tests/RunCMake/CTestCommandLine/LabelCount-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/LabelCount-stdout.txt
@@ -1,4 +1,4 @@ -100% tests passed, 0 tests failed out of 4 +100% tests passed out of 4 + +Label Time Summary: +'bar' = +[0-9\.]+ sec\*proc \(3 tests\)
diff --git a/Tests/RunCMake/CTestCommandLine/Passthrough-basic-stdout.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-basic-stdout.txt new file mode 100644 index 0000000..7888f80 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/Passthrough-basic-stdout.txt
@@ -0,0 +1,6 @@ +1: Test command: [^ +]* "-E" "echo" "base_output" "--extra-flag" +.* +2: Test command: [^ +]* "-E" "echo" "second_test" "--extra-flag" +.*
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-empty-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-empty-error-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-empty-error-stderr.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-empty-error-stderr.txt new file mode 100644 index 0000000..3648159 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-empty-error-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: The -- option cannot be used with --build-and-test\. Use --build-options or --test-command to forward arguments\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-error-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-error-stderr.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-error-stderr.txt new file mode 100644 index 0000000..3648159 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/Passthrough-build-and-test-error-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: The -- option cannot be used with --build-and-test\. Use --build-options or --test-command to forward arguments\.$
diff --git a/Tests/RunCMake/CTestCommandLine/Passthrough-ctest-flags-stdout.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-ctest-flags-stdout.txt new file mode 100644 index 0000000..40864f2 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/Passthrough-ctest-flags-stdout.txt
@@ -0,0 +1,6 @@ +1: Test command: [^ +]* "-E" "echo" "base_output" "-R" "--verbose" +.* +2: Test command: [^ +]* "-E" "echo" "second_test" "-R" "--verbose" +.*
diff --git a/Tests/RunCMake/CTestCommandLine/Passthrough-empty-stdout.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-empty-stdout.txt new file mode 100644 index 0000000..a835400 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/Passthrough-empty-stdout.txt
@@ -0,0 +1,6 @@ +1: Test command: [^ +]* "-E" "echo" "base_output" +.* +2: Test command: [^ +]* "-E" "echo" "second_test" +.*
diff --git a/Tests/RunCMake/CTestCommandLine/Passthrough-multiple-stdout.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-multiple-stdout.txt new file mode 100644 index 0000000..70b0d13 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/Passthrough-multiple-stdout.txt
@@ -0,0 +1,6 @@ +1: Test command: [^ +]* "-E" "echo" "base_output" "--flag1" "--flag2" "value" +.* +2: Test command: [^ +]* "-E" "echo" "second_test" "--flag1" "--flag2" "value" +.*
diff --git a/Tests/RunCMake/CTestCommandLine/Passthrough-with-filter-stdout.txt b/Tests/RunCMake/CTestCommandLine/Passthrough-with-filter-stdout.txt new file mode 100644 index 0000000..084b51d --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/Passthrough-with-filter-stdout.txt
@@ -0,0 +1,8 @@ +Test project [^ +]*/Tests/RunCMake/CTestCommandLine/Passthrough-with-filter( +| +[^0-9][^ +]*)+ +2: Test command: [^ +]* "-E" "echo" "second_test" "--extra" +.*
diff --git a/Tests/RunCMake/CTestCommandLine/ReuseBuildNameFromPresetBinaryDir-configure-stderr.txt b/Tests/RunCMake/CTestCommandLine/ReuseBuildNameFromPresetBinaryDir-configure-stderr.txt new file mode 100644 index 0000000..233b8c4 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ReuseBuildNameFromPresetBinaryDir-configure-stderr.txt
@@ -0,0 +1 @@ +^Cannot find file: .*/DartConfiguration\.tcl$
diff --git a/Tests/RunCMake/CTestCommandLine/ReuseBuildNameFromPresetBinaryDir-update-check.cmake b/Tests/RunCMake/CTestCommandLine/ReuseBuildNameFromPresetBinaryDir-update-check.cmake new file mode 100644 index 0000000..e76f3ac --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/ReuseBuildNameFromPresetBinaryDir-update-check.cmake
@@ -0,0 +1,18 @@ +# Check that Site and BuildName in Update.xml match what was passed via -D +# for the Configure step. +file(GLOB update_xml_files + "${RunCMake_TEST_BINARY_DIR}/Testing/*/Update.xml") +if(NOT update_xml_files) + set(RunCMake_TEST_FAILED + "Update.xml not found in ${RunCMake_TEST_BINARY_DIR}/Testing/") + return() +endif() +list(GET update_xml_files 0 update_xml) +file(READ "${update_xml}" content) +if(NOT content MATCHES "<Site>my-site</Site>") + set(RunCMake_TEST_FAILED + "Update.xml does not contain <Site>my-site</Site>\nActual content:\n${content}") +elseif(NOT content MATCHES "<BuildName>my-build-name</BuildName>") + set(RunCMake_TEST_FAILED + "Update.xml does not contain <BuildName>my-build-name</BuildName>\nActual content:\n${content}") +endif()
diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake index ede949a..36d4fe4 100644 --- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
@@ -424,6 +424,8 @@ endfunction() run_stop_on_failure() +run_cmake_command(usage ${CMAKE_CTEST_COMMAND}) + function(run_TestAffinity) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TestAffinity) set(RunCMake_TEST_NO_CLEAN 1) @@ -490,6 +492,22 @@ return(PROPAGATE RunCMake_TEST_FAILED) endfunction() +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CustomPrePost) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestCustom.cmake" " +set(CTEST_CUSTOM_PRE_TEST \"\\\"${CMAKE_COMMAND}\\\" -E echo \\\"Custom Pre-Test\\\"\") +set(CTEST_CUSTOM_POST_TEST \"\\\"${CMAKE_COMMAND}\\\" -E echo \\\"Custom Post-Test\\\"\") +") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" " + add_test(Echo \"${CMAKE_COMMAND}\" -E echo) +") + run_cmake_command(CustomPrePost ${CMAKE_CTEST_COMMAND}) + run_cmake_command(CustomPrePost-show-only ${CMAKE_CTEST_COMMAND} --show-only=human) +endblock() + function(run_ShowOnly) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ShowOnly) set(RunCMake_TEST_NO_CLEAN 1) @@ -638,6 +656,219 @@ run_testDir(testDir 0) run_testDir(testDir-preset 1) +# Test --source-dir and --build-dir +run_cmake_command(source-dir-invalid-arg ${CMAKE_CTEST_COMMAND} --source-dir) +run_cmake_command(build-dir-invalid-arg ${CMAKE_CTEST_COMMAND} --build-dir) + +# Build the -D arguments needed to pass the generator to ctest -T Configure. +macro(ctest_source_dir_generator_args var) + set(${var} -D "CTEST_CMAKE_GENERATOR=${RunCMake_GENERATOR}") + if(RunCMake_GENERATOR_PLATFORM) + list(APPEND ${var} + -D "CTEST_CMAKE_GENERATOR_PLATFORM=${RunCMake_GENERATOR_PLATFORM}") + endif() +endmacro() + +# Verify that ctest -T Configure works with an empty binary directory +# when --source-dir is provided. +function(run_configure_empty_bindir) + set(src "${RunCMake_BINARY_DIR}/configure-empty-bindir-src") + set(bin "${RunCMake_BINARY_DIR}/configure-empty-bindir-bin") + file(REMOVE_RECURSE "${src}" "${bin}") + file(MAKE_DIRECTORY "${src}") + file(WRITE "${src}/CMakeLists.txt" + "cmake_minimum_required(VERSION 3.10)\nproject(Minimal LANGUAGES NONE)\n") + # Note that run_cmake_command always pre-creates RunCMake_TEST_BINARY_DIR + # before invoking the command, so this test exercises configure from an already- + # created but otherwise empty binary directory. The MakeDirectory call in CTest + # itself is what would handle the truly-absent case in production use. + set(RunCMake_TEST_BINARY_DIR "${bin}") + set(RunCMake_TEST_NO_CLEAN 1) + ctest_source_dir_generator_args(generator_args) + run_cmake_command(configure-empty-bindir + ${CMAKE_CTEST_COMMAND} + --source-dir "${src}" + --build-dir "${bin}" + ${generator_args} + -T Configure) +endfunction() +run_configure_empty_bindir() + +# Verify that ctest_configure() does not trigger a spurious unused-cli +# warning about BUILDNAME/SITE when CTEST_SITE/CTEST_BUILD_NAME are set +# but the project does not include(CTest). See issue #27953. +function(run_configure_site_buildname_no_unused_cli) + set(src "${RunCMake_BINARY_DIR}/configure-site-buildname-no-unused-cli-src") + set(bin "${RunCMake_BINARY_DIR}/configure-site-buildname-no-unused-cli-bin") + file(REMOVE_RECURSE "${src}" "${bin}") + file(MAKE_DIRECTORY "${src}") + file(WRITE "${src}/CMakeLists.txt" + "cmake_minimum_required(VERSION 3.10)\nproject(Minimal LANGUAGES NONE)\n") + set(RunCMake_TEST_BINARY_DIR "${bin}") + set(RunCMake_TEST_NO_CLEAN 1) + ctest_source_dir_generator_args(generator_args) + set(RunCMake_TEST_NOT_EXPECT_stdout "CMake Warning \\(unused-cli\\)") + run_cmake_command(configure-site-buildname-no-unused-cli + ${CMAKE_CTEST_COMMAND} + --source-dir "${src}" + --build-dir "${bin}" + ${generator_args} + -D "CTEST_SITE=Test Site" + -D "CTEST_BUILD_NAME=Test Build" + -T Configure + -V) + unset(RunCMake_TEST_NOT_EXPECT_stdout) +endfunction() +run_configure_site_buildname_no_unused_cli() + +# Verify expected error condition when --source-dir does not contain +# a CMakeLists.txt file. +function(run_configure_no_cmakelists) + set(src "${RunCMake_BINARY_DIR}/configure-no-cmakelists-src") + set(bin "${RunCMake_BINARY_DIR}/configure-no-cmakelists-bin") + file(REMOVE_RECURSE "${src}" "${bin}") + # Source dir exists but contains no CMakeLists.txt + file(MAKE_DIRECTORY "${src}") + set(RunCMake_TEST_BINARY_DIR "${bin}") + set(RunCMake_TEST_NO_CLEAN 1) + ctest_source_dir_generator_args(generator_args) + run_cmake_command(configure-no-cmakelists + ${CMAKE_CTEST_COMMAND} + --source-dir "${src}" + --build-dir "${bin}" + ${generator_args} + -T Configure) +endfunction() +run_configure_no_cmakelists() + +# Helper for tests that invoke ctest -M/-T Configure with -D CTEST_PRESET. +# Options: +# SOURCE_DIR -- pass --source-dir to ctest +# BUILD_DIR -- create a separate <CASE_NAME>-build dir with +# DartConfiguration.tcl and pass --build-dir to ctest +# DART_VARS -- seed DartConfiguration.tcl with BuildName/Site originals +# and pass -D CTEST_BUILD_NAME/CTEST_SITE overrides to ctest +function(run_ctest_configure_cli_preset CASE_NAME) + cmake_parse_arguments(PARSE_ARGV 1 ARG "SOURCE_DIR;BUILD_DIR;BAD_PRESETS;DART_VARS" "PRESET_NAME" "") + set(src "${RunCMake_BINARY_DIR}/${CASE_NAME}") + set(bin "${src}") + if(ARG_BUILD_DIR) + set(bin "${RunCMake_BINARY_DIR}/${CASE_NAME}-build") + endif() + set(preset_name "my-preset") + if(ARG_PRESET_NAME) + set(preset_name "${ARG_PRESET_NAME}") + endif() + configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" + "${src}/CMakeLists.txt" @ONLY) + if(ARG_BAD_PRESETS) + configure_file("${RunCMake_SOURCE_DIR}/BadCMakePresets.json.in" + "${src}/CMakePresets.json" @ONLY) + else() + configure_file("${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${src}/CMakePresets.json" @ONLY) + endif() + file(REMOVE_RECURSE "${src}/build") + if(ARG_BUILD_DIR) + file(REMOVE_RECURSE "${bin}") + file(MAKE_DIRECTORY "${bin}") + set(dart_extra "") + if(ARG_DART_VARS) + set(dart_extra "BuildName: original-build-name\nSite: original-site\n") + endif() + file(WRITE "${bin}/DartConfiguration.tcl" + "BuildDirectory: ${bin}\n" + "SourceDirectory: ${src}\n" + "${dart_extra}" + "ConfigureCommand: \"${CMAKE_COMMAND}\" -S\"${src}\" -B\"${bin}\"\n") + endif() + set(extra_args "") + if(ARG_SOURCE_DIR) + list(APPEND extra_args --source-dir "${src}") + endif() + if(ARG_BUILD_DIR) + list(APPEND extra_args --build-dir "${bin}") + endif() + if(ARG_DART_VARS) + list(APPEND extra_args + -D "CTEST_BUILD_NAME=cli-build-name" + -D "CTEST_SITE=cli-site") + endif() + set(RunCMake_TEST_SOURCE_DIR "${src}") + set(RunCMake_TEST_BINARY_DIR "${bin}") + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${CASE_NAME} + ${CMAKE_CTEST_COMMAND} + ${extra_args} + -M Experimental + -D "CTEST_PRESET=${preset_name}" + -T Configure + -V) +endfunction() + +# CTEST_PRESET via -D reaches ctest_configure() when run with -M/-T. +run_ctest_configure_cli_preset(ConfigurePresetCLIVar BUILD_DIR) + +# --source-dir + -D CTEST_PRESET picks up the preset's binaryDir when no +# DartConfiguration.tcl / explicit --build-dir is provided. +run_ctest_configure_cli_preset(ConfigurePresetCLIVarSourceDir SOURCE_DIR) + +# An explicit --build-dir takes precedence over the preset's binaryDir. +run_ctest_configure_cli_preset(ConfigurePresetCLIVarBuildDirOverride SOURCE_DIR BUILD_DIR) + +# A malformed presets file is an error. +run_ctest_configure_cli_preset(ConfigurePresetCLIVarBadPresets SOURCE_DIR BAD_PRESETS) + +# Referencing a preset that does not exist is an error. +run_ctest_configure_cli_preset(ConfigurePresetCLIVarUnknownPreset SOURCE_DIR + PRESET_NAME nonexistent-preset) + +# -D CTEST_BUILD_NAME and -D CTEST_SITE are recorded in DartConfiguration.tcl, +# overriding pre-existing values (pre-existing binary dir). +run_ctest_configure_cli_preset(ConfigureCLIVarDartPersist BUILD_DIR DART_VARS) + +# -D CTEST_BUILD_NAME and -D CTEST_SITE are recorded in DartConfiguration.tcl +# when cmake creates it fresh (empty binary dir via --source-dir). +run_ctest_configure_cli_preset(ConfigureCLIVarDartPersistSourceDir SOURCE_DIR DART_VARS) + +# The following end-to-end test demonstrates that: +# 1) ctest -T Configure writes user-specified Site and BuildName in +# preset.binaryDir/DartConfiguration.tcl. +# 2) ctest -T Update correctly loads this data. +block() + set(src "${RunCMake_BINARY_DIR}/ReuseBuildNameFromPresetBinaryDir") + set(bin "${src}/build") + configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" + "${src}/CMakeLists.txt" @ONLY) + configure_file("${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${src}/CMakePresets.json" @ONLY) + file(REMOVE_RECURSE "${bin}") + ctest_source_dir_generator_args(generator_args) + set(RunCMake_TEST_SOURCE_DIR "${src}") + set(RunCMake_TEST_BINARY_DIR "${bin}") + set(RunCMake_TEST_NO_CLEAN 1) + # Step 1: ctest -T Configure sets Site/BuildName in DartConfiguration.tcl. + run_cmake_command(ReuseBuildNameFromPresetBinaryDir-configure + ${CMAKE_CTEST_COMMAND} + --source-dir "${src}" + ${generator_args} + -M Experimental + -T Configure + -D "CTEST_PRESET=my-preset" + -D "CTEST_SITE=my-site" + -D "CTEST_BUILD_NAME=my-build-name") + # Step 2: ctest -T Update reuses Site/BuildName from above. + run_cmake_command(ReuseBuildNameFromPresetBinaryDir-update + ${CMAKE_CTEST_COMMAND} + --source-dir "${src}" + -M Experimental + -T Update + -D "CTEST_PRESET=my-preset" + -D "CTEST_UPDATE_COMMAND=${CMAKE_COMMAND}" + -D "CTEST_UPDATE_VERSION_ONLY=1" + -V) +endblock() + # Test --output-junit function(run_output_junit) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/output-junit) @@ -657,6 +888,21 @@ endfunction() run_output_junit() +function(run_output_junit_invalid) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/output-junit-invalid) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" " +add_test(test1 \"${CMAKE_COMMAND}\" -E true) +") + set(output_dir "${RunCMake_TEST_BINARY_DIR}/not-a-dir") + file(WRITE "${output_dir}" "") + set(output_file "${output_dir}/junit.xml") + run_cmake_command(output-junit-invalid ${CMAKE_CTEST_COMMAND} --output-junit "${output_file}") +endfunction() +run_output_junit_invalid() + run_cmake_command(invalid-ctest-argument ${CMAKE_CTEST_COMMAND} --not-a-valid-ctest-argument) block() @@ -716,3 +962,123 @@ run_cmake_command(ScheduleRandomSeed1 ${CMAKE_CTEST_COMMAND} --schedule-random --schedule-random-seed 42) run_cmake_command(ScheduleRandomSeed2 ${CMAKE_CTEST_COMMAND} --schedule-random --schedule-random-seed 42) endblock() + +function(run_PassthroughTest case) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Passthrough-${case}) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" " +add_test(echo_test \"${CMAKE_COMMAND}\" -E echo \"base_output\") +add_test(echo_test2 \"${CMAKE_COMMAND}\" -E echo \"second_test\") +") + run_cmake_command(Passthrough-${case} ${CMAKE_CTEST_COMMAND} ${ARGN}) +endfunction() + +# Basic passthrough +run_PassthroughTest(basic -V -- --extra-flag) +# Multiple passthrough args +run_PassthroughTest(multiple -V -- --flag1 --flag2 value) +# Passthrough with -R filter (only echo_test2 should run) +run_PassthroughTest(with-filter -V -R echo_test2 -- --extra) +# Empty passthrough (bare --) +run_PassthroughTest(empty -V --) +# Passthrough args that look like ctest flags +run_PassthroughTest(ctest-flags -V -- -R --verbose) +# -- rejected with --build-and-test +run_cmake_command(Passthrough-build-and-test-error ${CMAKE_CTEST_COMMAND} + --build-and-test + ${RunCMake_BINARY_DIR}/Passthrough-build-and-test-error/does-not-exist + ${RunCMake_BINARY_DIR}/Passthrough-build-and-test-error/does-not-exist + --build-generator "None" -- --extra-flag) +# bare -- rejected with --build-and-test +run_cmake_command(Passthrough-build-and-test-empty-error ${CMAKE_CTEST_COMMAND} + --build-and-test + ${RunCMake_BINARY_DIR}/Passthrough-build-and-test-empty-error/does-not-exist + ${RunCMake_BINARY_DIR}/Passthrough-build-and-test-empty-error/does-not-exist + --build-generator "None" --) + +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CoverageTool) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/DartConfiguration.tcl" " +BuildDirectory: ${RunCMake_TEST_BINARY_DIR} +CTestTestCoverageTool: LLVM-COV +") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" " +add_test(test1 \"${CMAKE_COMMAND}\" -E true) +") + run_cmake_command(CoverageTool ${CMAKE_CTEST_COMMAND} -V) + run_cmake_command(CoverageTool-T-Test ${CMAKE_CTEST_COMMAND} -V -T Test) +endblock() + +# Verify that CTEST_* variables defined via the command-line are visible +# from the CTest script. +run_cmake_command(DashD-ScriptVars + ${CMAKE_CTEST_COMMAND} + "-S" "${RunCMake_SOURCE_DIR}/DashD-ScriptVars.cmake" + "-D" "CTEST_BUILD_NAME=cli-build-name" + "-D" "CTEST_SITE=cli-site" + "-D" "CTEST_BUILD_FLAGS=-O2 -Wall" + "-D" "CTEST_BUILD_TARGET=my-target" + "-D" "CTEST_CMAKE_GENERATOR=Ninja" + "-D" "CTEST_EXTRA_COVERAGE_GLOB=**/*.gcov" + "-D" "CTEST_TIME_LIMIT=3600" + ) + +# Verify that CTEST_* variables defined via the command-line override +# settings from the dashboard configuration file. +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DashD-DashboardVar-build) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/DartConfiguration.tcl" + "BuildName: original-build-name\nSite: original-site\nSourceDirectory: ${RunCMake_TEST_BINARY_DIR}\n") + run_cmake_command(DashD-DashboardVar + ${CMAKE_CTEST_COMMAND} -M Experimental -T Start + -D CTEST_BUILD_NAME=cli-build-name + ) +endblock() + +# Test CTEST_SUBMIT_PARTS: a valid part name reaches the network submission step. +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SubmitParts-valid-build) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/DartConfiguration.tcl" + "SourceDirectory: ${RunCMake_TEST_BINARY_DIR}\n" + "BuildDirectory: ${RunCMake_TEST_BINARY_DIR}\n" + "DropMethod: https\n" + "DropSite: badhostname.invalid\n" + "DropLocation: /submit.php?project=Test\n" + "CTestSubmitRetryCount: 0\n" + ) + run_cmake_command(SubmitParts-valid-ctest + ${CMAKE_CTEST_COMMAND} -M Experimental -T Start -T Submit -VV + -D CTEST_SUBMIT_PARTS=Done + ) +endblock() + +# Test CTEST_SUBMIT_PARTS: an invalid part name produces a validation error. +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SubmitParts-badpart-build) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/DartConfiguration.tcl" + "SourceDirectory: ${RunCMake_TEST_BINARY_DIR}\n" + "BuildDirectory: ${RunCMake_TEST_BINARY_DIR}\n" + "DropMethod: https\n" + "DropSite: badhostname.invalid\n" + "DropLocation: /submit.php?project=Test\n" + "CTestSubmitRetryCount: 0\n" + ) + run_cmake_command(SubmitParts-badpart-ctest + ${CMAKE_CTEST_COMMAND} -M Experimental -T Start -T Submit + -D CTEST_SUBMIT_PARTS=BadPart + ) +endblock()
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/CTestCommandLine/SubmitParts-badpart-ctest-result.txt similarity index 100% copy from Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt copy to Tests/RunCMake/CTestCommandLine/SubmitParts-badpart-ctest-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/SubmitParts-badpart-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/SubmitParts-badpart-ctest-stderr.txt new file mode 100644 index 0000000..2868f85 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/SubmitParts-badpart-ctest-stderr.txt
@@ -0,0 +1 @@ +Part name "BadPart" is invalid\.
diff --git a/Tests/RunCMake/CTestCommandLine/SubmitParts-valid-ctest-check.cmake b/Tests/RunCMake/CTestCommandLine/SubmitParts-valid-ctest-check.cmake new file mode 100644 index 0000000..340196c --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/SubmitParts-valid-ctest-check.cmake
@@ -0,0 +1,14 @@ +# Verify that CTEST_SUBMIT_PARTS=Done caused only Done.xml to be submitted. +# The upload attempt appears in stderr as "Error when uploading file: .../Done.xml". +if(NOT actual_stderr MATCHES "Error when uploading file:[^\n]*/Done\\.xml") + set(RunCMake_TEST_FAILED + "Expected stderr to contain an upload attempt for Done.xml.\nActual stderr:\n${actual_stderr}") +endif() + +# No other XML part files should have been uploaded. +foreach(unexpected IN ITEMS Configure Build Test Update Coverage MemCheck Notes Upload) + if(actual_stderr MATCHES "Error when uploading file:[^\n]*/${unexpected}\\.xml") + string(APPEND RunCMake_TEST_FAILED + "\nUnexpected upload attempt for ${unexpected}.xml found in stderr.") + endif() +endforeach()
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/CTestCommandLine/SubmitParts-valid-ctest-result.txt similarity index 100% copy from Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt copy to Tests/RunCMake/CTestCommandLine/SubmitParts-valid-ctest-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/SubmitParts-valid-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/SubmitParts-valid-ctest-stderr.txt new file mode 100644 index 0000000..085e90a --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/SubmitParts-valid-ctest-stderr.txt
@@ -0,0 +1,2 @@ +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* + Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-empty-stdout.txt b/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-empty-stdout.txt index d42dde9..487dd8b 100644 --- a/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-empty-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-empty-stdout.txt
@@ -7,4 +7,4 @@ +Start 3: Test11 3/3 Test #3: Test11 \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 3 +100% tests passed out of 3
diff --git a/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-stdout.txt b/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-stdout.txt index afc683f..3d9179e 100644 --- a/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-stdout.txt
@@ -5,5 +5,5 @@ +Start 3: Test11 2/2 Test #3: Test11 \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2 +
diff --git a/Tests/RunCMake/CTestCommandLine/TestsFromFile-include-stdout.txt b/Tests/RunCMake/CTestCommandLine/TestsFromFile-include-stdout.txt index 6231f02..06b3b0c 100644 --- a/Tests/RunCMake/CTestCommandLine/TestsFromFile-include-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/TestsFromFile-include-stdout.txt
@@ -3,5 +3,5 @@ +Start 1: Test1 1/1 Test #1: Test1 \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 +
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CTestCommandLine/build-dir-invalid-arg-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CTestCommandLine/build-dir-invalid-arg-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/build-dir-invalid-arg-stderr.txt b/Tests/RunCMake/CTestCommandLine/build-dir-invalid-arg-stderr.txt new file mode 100644 index 0000000..3006bc5 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/build-dir-invalid-arg-stderr.txt
@@ -0,0 +1 @@ +CMake Error: '--build-dir' requires an argument
diff --git a/Tests/RunCMake/CTestCommandLine/configure-empty-bindir-check.cmake b/Tests/RunCMake/CTestCommandLine/configure-empty-bindir-check.cmake new file mode 100644 index 0000000..de5fec6 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/configure-empty-bindir-check.cmake
@@ -0,0 +1,5 @@ +set(cache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(NOT EXISTS "${cache_file}") + set(RunCMake_TEST_FAILED + "CMakeCache.txt not found after configure:\n ${cache_file}") +endif()
diff --git a/Tests/RunCMake/CTestCommandLine/configure-empty-bindir-stderr.txt b/Tests/RunCMake/CTestCommandLine/configure-empty-bindir-stderr.txt new file mode 100644 index 0000000..f8b065d --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/configure-empty-bindir-stderr.txt
@@ -0,0 +1 @@ +Cannot find file: .*/DartConfiguration\.tcl
diff --git a/Tests/RunCMake/CTestCommandLine/configure-no-cmakelists-result.txt b/Tests/RunCMake/CTestCommandLine/configure-no-cmakelists-result.txt new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/configure-no-cmakelists-result.txt
@@ -0,0 +1 @@ +2
diff --git a/Tests/RunCMake/CTestCommandLine/configure-no-cmakelists-stderr.txt b/Tests/RunCMake/CTestCommandLine/configure-no-cmakelists-stderr.txt new file mode 100644 index 0000000..eb333ad --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/configure-no-cmakelists-stderr.txt
@@ -0,0 +1 @@ +ctest_configure called with invalid source directory
diff --git a/Tests/RunCMake/CTestCommandLine/configure-site-buildname-no-unused-cli-stderr.txt b/Tests/RunCMake/CTestCommandLine/configure-site-buildname-no-unused-cli-stderr.txt new file mode 100644 index 0000000..302556f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/configure-site-buildname-no-unused-cli-stderr.txt
@@ -0,0 +1,2 @@ +^Cannot find file: [^ +]*/DartConfiguration\.tcl$
diff --git a/Tests/RunCMake/CTestCommandLine/output-junit-invalid-check.cmake b/Tests/RunCMake/CTestCommandLine/output-junit-invalid-check.cmake new file mode 100644 index 0000000..96f6e49 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/output-junit-invalid-check.cmake
@@ -0,0 +1,6 @@ +# Verify that CTest did not create our badly formed output file. +if(EXISTS "${RunCMake_TEST_BINARY_DIR}/not-a-dir/junit.xml" OR IS_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/not-a-dir") + set(RunCMake_TEST_FAILED + "Found output junit ${RunCMake_TEST_BINARY_DIR}/not-a-dir/junit.xml" + ) +endif()
diff --git a/Tests/RunCMake/CTestCommandLine/output-junit-invalid-result.txt b/Tests/RunCMake/CTestCommandLine/output-junit-invalid-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/output-junit-invalid-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/CTestCommandLine/output-junit-invalid-stderr.txt b/Tests/RunCMake/CTestCommandLine/output-junit-invalid-stderr.txt new file mode 100644 index 0000000..2783510 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/output-junit-invalid-stderr.txt
@@ -0,0 +1,10 @@ +^CMake Error: Cannot open file for write: [^ +]*/Tests/RunCMake/CTestCommandLine/output-junit-invalid/not-a-dir/junit\.xml\.tmp[^ +]* +CMake Error: : System Error: (Not a directory|No such file or directory) +Problem opening file: [^ +]*/Tests/RunCMake/CTestCommandLine/output-junit-invalid/not-a-dir/junit\.xml +Errors while running CTest +Output from these tests are in: [^ +]*/Tests/RunCMake/CTestCommandLine/output-junit-invalid/Testing/Temporary/LastTest\.log +Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.$
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-ctest-stdout.txt index 4c21763..13bd601 100644 --- a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-ctest-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-ctest-stdout.txt
@@ -10,6 +10,6 @@ Start 4: goodbye 4/4 Test #4: goodbye \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of 4 +100% tests passed out of 4 Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-until-pass-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/repeat-until-pass-ctest-stdout.txt index adc5e1f..5fd974e 100644 --- a/Tests/RunCMake/CTestCommandLine/repeat-until-pass-ctest-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/repeat-until-pass-ctest-stdout.txt
@@ -10,6 +10,6 @@ Start 4: goodbye 4/4 Test #4: goodbye \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of 4 +100% tests passed out of 4 Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CTestCommandLine/source-dir-invalid-arg-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CTestCommandLine/source-dir-invalid-arg-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/source-dir-invalid-arg-stderr.txt b/Tests/RunCMake/CTestCommandLine/source-dir-invalid-arg-stderr.txt new file mode 100644 index 0000000..ddc3d04 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/source-dir-invalid-arg-stderr.txt
@@ -0,0 +1 @@ +CMake Error: '--source-dir' requires an argument
diff --git a/Tests/RunCMake/CTestCommandLine/test-load-invalid-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-invalid-stdout.txt index c5809a7..0853b1d 100644 --- a/Tests/RunCMake/CTestCommandLine/test-load-invalid-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/test-load-invalid-stdout.txt
@@ -17,4 +17,4 @@ ]*)* 2/2 Test #[1-2]: TestLoad[1-2] \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2
diff --git a/Tests/RunCMake/CTestCommandLine/test-load-pass-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-pass-stdout.txt index c5809a7..0853b1d 100644 --- a/Tests/RunCMake/CTestCommandLine/test-load-pass-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/test-load-pass-stdout.txt
@@ -17,4 +17,4 @@ ]*)* 2/2 Test #[1-2]: TestLoad[1-2] \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2
diff --git a/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt index ef0bdd5..70c5715 100644 --- a/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt
@@ -18,4 +18,4 @@ ]*)* 2/2 Test #[1-2]: TestLoad[1-2] \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2
diff --git a/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt index 9e90b34..da5ec70 100644 --- a/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt
@@ -18,4 +18,4 @@ ]*)* 2/2 Test #[1-2]: TestLoad[1-2] \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CTestCommandLine/usage-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CTestCommandLine/usage-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/usage-stderr.txt b/Tests/RunCMake/CTestCommandLine/usage-stderr.txt new file mode 100644 index 0000000..dd65757 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/usage-stderr.txt
@@ -0,0 +1,3 @@ +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +No test configuration file found! +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
diff --git a/Tests/RunCMake/CTestCommandLine/usage-stdout.txt b/Tests/RunCMake/CTestCommandLine/usage-stdout.txt new file mode 100644 index 0000000..db2cedc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/usage-stdout.txt
@@ -0,0 +1,3 @@ +Usage + + ctest \[options\]
diff --git a/Tests/RunCMake/CTestCoverage/CMakeLists.txt b/Tests/RunCMake/CTestCoverage/CMakeLists.txt new file mode 100644 index 0000000..f54f746 --- /dev/null +++ b/Tests/RunCMake/CTestCoverage/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CTestCoverage/ClangCoverage-coverage-stdout.txt b/Tests/RunCMake/CTestCoverage/ClangCoverage-coverage-stdout.txt new file mode 100644 index 0000000..e1187c8 --- /dev/null +++ b/Tests/RunCMake/CTestCoverage/ClangCoverage-coverage-stdout.txt
@@ -0,0 +1,2 @@ +.Process file: [^ +]*CTestCoverage/ClangCoverage.c
diff --git a/Tests/RunCMake/CTestCoverage/ClangCoverage-ctest-check.cmake b/Tests/RunCMake/CTestCoverage/ClangCoverage-ctest-check.cmake new file mode 100644 index 0000000..ae1d326 --- /dev/null +++ b/Tests/RunCMake/CTestCoverage/ClangCoverage-ctest-check.cmake
@@ -0,0 +1,3 @@ +if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/ClangCoverage.profdata") + string(APPEND RunCMake_TEST_FAILED "ClangCoverage.profdata not found\n") +endif()
diff --git a/Tests/RunCMake/CTestCoverage/ClangCoverage-ctest-stdout.txt b/Tests/RunCMake/CTestCoverage/ClangCoverage-ctest-stdout.txt new file mode 100644 index 0000000..c8ab6ed --- /dev/null +++ b/Tests/RunCMake/CTestCoverage/ClangCoverage-ctest-stdout.txt
@@ -0,0 +1,2 @@ +Using environment variable LLVM_PROFILE_FILE=[^ +]*/Tests/RunCMake/CTestCoverage/ClangCoverage-build/ClangCoverage_%p\.profraw
diff --git a/Tests/RunCMake/CTestCoverage/ClangCoverage.c b/Tests/RunCMake/CTestCoverage/ClangCoverage.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/CTestCoverage/ClangCoverage.c
@@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +}
diff --git a/Tests/RunCMake/CTestCoverage/ClangCoverage.cmake b/Tests/RunCMake/CTestCoverage/ClangCoverage.cmake new file mode 100644 index 0000000..26da358 --- /dev/null +++ b/Tests/RunCMake/CTestCoverage/ClangCoverage.cmake
@@ -0,0 +1,12 @@ +enable_language(C) + +if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") + message(FATAL_ERROR "This test requires a Clang C compiler.") +endif() + +string(APPEND CMAKE_C_FLAGS " -fprofile-instr-generate -fcoverage-mapping") +set(CTEST_TEST_COVERAGE_TOOL "LLVM-COV") +include(CTest) + +add_executable(ClangCoverage ClangCoverage.c) +add_test(NAME ClangCoverage COMMAND ClangCoverage)
diff --git a/Tests/RunCMake/CTestCoverage/RunCMakeTest.cmake b/Tests/RunCMake/CTestCoverage/RunCMakeTest.cmake new file mode 100644 index 0000000..aec085c --- /dev/null +++ b/Tests/RunCMake/CTestCoverage/RunCMakeTest.cmake
@@ -0,0 +1,15 @@ +include(RunCMake) + +if(CMake_TEST_CLANG_COVERAGE) + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ClangCoverage-build) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + run_cmake(ClangCoverage) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(ClangCoverage-build ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(ClangCoverage-ctest ${CMAKE_CTEST_COMMAND} -C Debug -VV) + run_cmake_command(ClangCoverage-coverage ${CMAKE_CTEST_COMMAND} -D ExperimentalCoverage -C Debug -VV) + endblock() +endif()
diff --git a/Tests/RunCMake/CTestResourceAllocation/process_count-ctest-s-res-check.cmake b/Tests/RunCMake/CTestResourceAllocation/process_count-ctest-s-res-check.cmake index 585c36b..727d2f4 100644 --- a/Tests/RunCMake/CTestResourceAllocation/process_count-ctest-s-res-check.cmake +++ b/Tests/RunCMake/CTestResourceAllocation/process_count-ctest-s-res-check.cmake
@@ -1,9 +1,9 @@ verify_ctest_resources() read_testing_file("Test.xml" _test_contents) -if(NOT _test_contents MATCHES "\nCTEST_RESOURCE_GROUP_0=widgets") +if(NOT _test_contents MATCHES "(>|\n)CTEST_RESOURCE_GROUP_0=widgets") string(APPEND RunCMake_TEST_FAILED "Could not find variable CTEST_RESOURCE_GROUP_0 in test measurements\n") endif() -if(NOT _test_contents MATCHES "\nCTEST_RESOURCE_GROUP_0_WIDGETS=id:") +if(NOT _test_contents MATCHES "(>|\n)CTEST_RESOURCE_GROUP_0_WIDGETS=id:") string(APPEND RunCMake_TEST_FAILED "Could not find variable CTEST_RESOURCE_GROUP_0_WIDGETS in test measurements\n") endif()
diff --git a/Tests/RunCMake/CTestTimeout/FlagOverridesVar-stdout.txt b/Tests/RunCMake/CTestTimeout/FlagOverridesVar-stdout.txt index 4e30bf6..7936164 100644 --- a/Tests/RunCMake/CTestTimeout/FlagOverridesVar-stdout.txt +++ b/Tests/RunCMake/CTestTimeout/FlagOverridesVar-stdout.txt
@@ -3,4 +3,4 @@ Start 1: TestTimeout 1/1 Test #1: TestTimeout \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[1-9][0-9\.]* sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/CTestTimeout/PropertyOverridesVar-stdout.txt b/Tests/RunCMake/CTestTimeout/PropertyOverridesVar-stdout.txt index 2d06734..888f25b 100644 --- a/Tests/RunCMake/CTestTimeout/PropertyOverridesVar-stdout.txt +++ b/Tests/RunCMake/CTestTimeout/PropertyOverridesVar-stdout.txt
@@ -3,4 +3,4 @@ Start 1: TestTimeout 1/1 Test #1: TestTimeout \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[1-9][0-9\.]* sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/CTestTimeout/ZeroOverridesFlag-stdout.txt b/Tests/RunCMake/CTestTimeout/ZeroOverridesFlag-stdout.txt index 6f33fee..70753cc 100644 --- a/Tests/RunCMake/CTestTimeout/ZeroOverridesFlag-stdout.txt +++ b/Tests/RunCMake/CTestTimeout/ZeroOverridesFlag-stdout.txt
@@ -3,4 +3,4 @@ Start 1: TestTimeout 1/1 Test #1: TestTimeout \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[1-9][0-9\.]* sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/CTestTimeout/ZeroOverridesVar-stdout.txt b/Tests/RunCMake/CTestTimeout/ZeroOverridesVar-stdout.txt index c66aa01..602cbda 100644 --- a/Tests/RunCMake/CTestTimeout/ZeroOverridesVar-stdout.txt +++ b/Tests/RunCMake/CTestTimeout/ZeroOverridesVar-stdout.txt
@@ -3,4 +3,4 @@ Start 1: TestTimeout 1/1 Test #1: TestTimeout \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[1-9][0-9\.]* sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/CTestTimeoutAfterMatch/ShouldPass-stdout.txt b/Tests/RunCMake/CTestTimeoutAfterMatch/ShouldPass-stdout.txt index 941a0fd..1d2ce21 100644 --- a/Tests/RunCMake/CTestTimeoutAfterMatch/ShouldPass-stdout.txt +++ b/Tests/RunCMake/CTestTimeoutAfterMatch/ShouldPass-stdout.txt
@@ -1,6 +1,6 @@ Start 1: SleepFor1Second 1/1 Test #1: SleepFor1Second \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake b/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake index c754930..d68775a 100644 --- a/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake +++ b/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake
@@ -1,6 +1,3 @@ -# Block making C++ `import std` targets. -add_library(__CMAKE::CXX23 IMPORTED INTERFACE) - enable_language(CXX) unset(CMAKE_CXX_SCANDEP_SOURCE)
diff --git a/Tests/RunCMake/CXXModules/CMP0155-OLD-stderr.txt b/Tests/RunCMake/CXXModules/CMP0155-OLD-stderr.txt new file mode 100644 index 0000000..988218f --- /dev/null +++ b/Tests/RunCMake/CXXModules/CMP0155-OLD-stderr.txt
@@ -0,0 +1,12 @@ +^CMake Warning \(deprecated\) at CMP0155-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0155 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake b/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake index a994266..201598e 100644 --- a/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake +++ b/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake
@@ -1,6 +1,3 @@ -# Block making C++ `import std` targets. -add_library(__CMAKE::CXX23 IMPORTED INTERFACE) - enable_language(CXX) unset(CMAKE_CXX_SCANDEP_SOURCE)
diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModulesConfig-check.cmake b/Tests/RunCMake/CXXModules/ExportBuildCxxModulesConfig-check.cmake new file mode 100644 index 0000000..545711f --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportBuildCxxModulesConfig-check.cmake
@@ -0,0 +1,53 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/export-modules-targets.cmake" export_script) + +if (NOT export_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules/cxx-modules-exp\.cmake"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property script inclusion") +endif () + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + if (NOT export_script MATCHES [[\$<\$<CONFIG:Debug>:.*sources/debug]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find config-specific Debug genex in export file") + endif () + if (NOT export_script MATCHES [[\$<\$<CONFIG:Release>:.*sources/release]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find config-specific Release genex in export file") + endif () +endif () + +file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules-exp.cmake" trampoline_script) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + if (NOT trampoline_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-exp-[^.]*\.cmake" OPTIONAL\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property per-config script inclusion(s)") + endif () +else () + if (NOT trampoline_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-exp-[^.]*\.cmake"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property per-config script inclusion(s)") + endif () +endif () + +set(any_exists 0) +foreach (config IN ITEMS noconfig Debug Release RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules-exp-${config}.cmake") + continue () + endif () + set(any_exists 1) + + file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules-exp-${config}.cmake" config_script) + + if (NOT config_script MATCHES "include\\(\"\\\${CMAKE_CURRENT_LIST_DIR}/target-export-name-${config}\\.cmake\"\\)") + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module per-target property script inclusion") + endif () +endforeach () + +if (NOT any_exists) + list(APPEND RunCMake_TEST_FAILED + "No per-configuration target files exist.") +endif () + +string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModulesConfig.cmake b/Tests/RunCMake/CXXModules/ExportBuildCxxModulesConfig.cmake new file mode 100644 index 0000000..aa554c7 --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportBuildCxxModulesConfig.cmake
@@ -0,0 +1,24 @@ +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "") + +add_library(export-modules) +target_sources(export-modules + PUBLIC + FILE_SET fs TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,sources/debug,sources/release>" + FILES + "${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,sources/debug,sources/release>/module.cxx") +target_compile_features(export-modules + PRIVATE + cxx_std_20) +set_property(TARGET export-modules + PROPERTY EXPORT_NAME export-name) + +install(TARGETS export-modules + EXPORT exp + FILE_SET fs DESTINATION "include/cxx/export-modules") + +export(EXPORT exp + FILE "${CMAKE_BINARY_DIR}/lib/cmake/export-modules/export-modules-targets.cmake" + CXX_MODULES_DIRECTORY "cxx-modules")
diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModulesConfig-check.cmake b/Tests/RunCMake/CXXModules/ExportInstallCxxModulesConfig-check.cmake new file mode 100644 index 0000000..62cbcd6 --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportInstallCxxModulesConfig-check.cmake
@@ -0,0 +1,49 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/exp.cmake" export_script) + +# Check for C++ module trampoline include. +if (NOT export_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules/cxx-modules-exp\.cmake"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property script inclusion") +endif () + +# Verify config-specific generator expressions for multi-config generators. +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + if (NOT export_script MATCHES [[\$<\$<CONFIG:Debug>:.*cxx/debug]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find config-specific Debug genex in export file") + endif () + if (NOT export_script MATCHES [[\$<\$<CONFIG:Release>:.*cxx/release]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find config-specific Release genex in export file") + endif () +endif () + +# Verify per-config C++ module script inclusion exists. +file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules-exp.cmake" trampoline_script) + +if (NOT trampoline_script MATCHES [[file\(GLOB _cmake_cxx_module_includes "\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-exp-\*\.cmake"\)]]) + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module property per-config script inclusion(s)") +endif () + +set(any_exists 0) +foreach (config IN ITEMS noconfig Debug Release RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules-exp-${config}.cmake") + continue () + endif () + set(any_exists 1) + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules-exp-${config}.cmake" config_script) + + if (NOT config_script MATCHES "include\\(\"\\\${CMAKE_CURRENT_LIST_DIR}/target-export-name-${config}\\.cmake\"\\)") + list(APPEND RunCMake_TEST_FAILED + "Could not find C++ module per-target property script inclusion") + endif () +endforeach () + +if (NOT any_exists) + list(APPEND RunCMake_TEST_FAILED + "No per-configuration target files exist.") +endif () + +string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModulesConfig.cmake b/Tests/RunCMake/CXXModules/ExportInstallCxxModulesConfig.cmake new file mode 100644 index 0000000..8b2e3ef --- /dev/null +++ b/Tests/RunCMake/CXXModules/ExportInstallCxxModulesConfig.cmake
@@ -0,0 +1,26 @@ +set(CMAKE_INTERMEDIATE_DIR_STRATEGY FULL CACHE STRING "" FORCE) + +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "") + +add_library(export-modules) +target_sources(export-modules + PUBLIC + FILE_SET fs TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,sources/debug,sources/release>" + FILES + "${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,sources/debug,sources/release>/module.cxx") +target_compile_features(export-modules + PRIVATE + cxx_std_20) +set_property(TARGET export-modules + PROPERTY EXPORT_NAME export-name) + +install(TARGETS export-modules + EXPORT exp + FILE_SET fs DESTINATION "include/cxx/$<IF:$<CONFIG:Debug>,debug,release>") + +install(EXPORT exp + DESTINATION "lib/cmake/export-modules" + CXX_MODULES_DIRECTORY "cxx-modules")
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnInterface-stderr.txt index 70d00ee..607d638 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnInterface-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnInterface-stderr.txt
@@ -1,5 +1,4 @@ CMake Error at FileSetModulesInterfaceOnInterface\.cmake:[0-9]+ \(target_sources\): - target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" - visibility + target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" scope Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnStatic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnStatic-stderr.txt index 9e1613c..606c679 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnStatic-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnStatic-stderr.txt
@@ -1,5 +1,4 @@ CMake Error at FileSetModulesInterfaceOnStatic\.cmake:[0-9]+ \(target_sources\): - target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" - visibility + target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" scope Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivateOnInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPrivateOnInterface-stderr.txt index f949df9..1d9edea 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesPrivateOnInterface-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesPrivateOnInterface-stderr.txt
@@ -1,5 +1,5 @@ ^CMake Error at FileSetModulesPrivateOnInterface\.cmake:[0-9]+ \(target_sources\): target_sources File set TYPE "CXX_MODULES" may not have "PUBLIC" or - "PRIVATE" visibility on INTERFACE libraries\. + "PRIVATE" scope on INTERFACE libraries\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublicOnInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPublicOnInterface-stderr.txt index 7d732eb..925ea98 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesPublicOnInterface-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesPublicOnInterface-stderr.txt
@@ -1,5 +1,5 @@ ^CMake Error at FileSetModulesPublicOnInterface\.cmake:[0-9]+ \(target_sources\): target_sources File set TYPE "CXX_MODULES" may not have "PUBLIC" or - "PRIVATE" visibility on INTERFACE libraries\. + "PRIVATE" scope on INTERFACE libraries\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake b/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake index 64d69f3..cac1777 100644 --- a/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake +++ b/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake
@@ -1,9 +1,6 @@ # Enable scanning by default for targets that explicitly use C++ 20. cmake_policy(SET CMP0155 NEW) -# Block making C++ `import std` targets. -add_library(__CMAKE::CXX23 IMPORTED INTERFACE) - # Force CMAKE_CXX_STANDARD_DEFAULT to be C++ 20. set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} ${CMAKE_CXX20_STANDARD_COMPILE_OPTION}") enable_language(CXX)
diff --git a/Tests/RunCMake/CXXModules/Inspect.cmake b/Tests/RunCMake/CXXModules/Inspect.cmake index 702e349..2ebb892 100644 --- a/Tests/RunCMake/CXXModules/Inspect.cmake +++ b/Tests/RunCMake/CXXModules/Inspect.cmake
@@ -15,22 +15,6 @@ set(forced_cxx_standard 1) endif () -macro (cxx_check_import_std version) - set(have_cxx${version}_import_std 0) - if ("${version}" IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD) - set(have_cxx${version}_import_std 1) - endif () - - if (TARGET "__CMAKE:CXX${version}" AND NOT have_cxx${version}_import_std) - message(FATAL_ERROR - "The toolchain's C++${version} target exists, but the user variable does " - "not indicate it.") - endif () -endmacro () - -cxx_check_import_std(23) -cxx_check_import_std(26) - # Forward information about the C++ compile features. string(APPEND info "\ set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CXXModules/InstallBMIAbsDir-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CXXModules/InstallBMIAbsDir-result.txt
diff --git a/Tests/RunCMake/CXXModules/InstallBMIAbsDir-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIAbsDir-stderr.txt new file mode 100644 index 0000000..4a5a1f0 --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMIAbsDir-stderr.txt
@@ -0,0 +1,10 @@ +CMake Error \(install-absolute-destination\) at InstallBMIAbsDir\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /lib/bmi +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=install-absolute-destination to suppress it\. + +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CXXModules/InstallBMIAbsDir.cmake b/Tests/RunCMake/CXXModules/InstallBMIAbsDir.cmake new file mode 100644 index 0000000..7cc1868 --- /dev/null +++ b/Tests/RunCMake/CXXModules/InstallBMIAbsDir.cmake
@@ -0,0 +1,32 @@ +set(CMAKE_INTERMEDIATE_DIR_STRATEGY FULL CACHE STRING "" FORCE) + +enable_language(CXX) + +add_library(install-bmi SHARED) +target_sources(install-bmi + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + sources/importable.cxx) +target_compile_features(install-bmi PUBLIC cxx_std_20) + +cmake_diagnostic(SET CMD_INSTALL_ABSOLUTE_DESTINATION FATAL_ERROR) + +install(TARGETS install-bmi + CXX_MODULES_BMI + DESTINATION "/lib/bmi" + COMPONENT "bmi") + +install(TARGETS install-bmi + CXX_MODULES_BMI + DESTINATION "/lib/bmi" + EXCLUDE_FROM_ALL + COMPONENT "bmi-optional") + +install(TARGETS install-bmi + CXX_MODULES_BMI + DESTINATION "/lib/bmi" + CONFIGURATIONS Debug + COMPONENT "bmi-only-debug")
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-stderr.txt index a001af7..93f5525 100644 --- a/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-stderr.txt +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-stderr.txt
@@ -1,4 +1,5 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(experimental\) in CMakeLists\.txt: CMake's support for exporting build databases is experimental\. It is meant only for experimentation and feedback to CMake developers\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase.cmake index c481f59..be92dae 100644 --- a/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase.cmake +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase.cmake
@@ -4,7 +4,7 @@ # here. set(CMAKE_CXX_SCANDEP_SOURCE "") -set(CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE "73194a1d-c0b5-41b9-9190-a4512925e192") +set(CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE "70ef007e-b743-492d-9407-e35eeac03a40") enable_language(CXX)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20.cmake b/Tests/RunCMake/CXXModules/NoCXX20.cmake index 9710728..b7372e8 100644 --- a/Tests/RunCMake/CXXModules/NoCXX20.cmake +++ b/Tests/RunCMake/CXXModules/NoCXX20.cmake
@@ -1,6 +1,3 @@ -# Block making C++ `import std` targets. -add_library(__CMAKE::CXX23 IMPORTED INTERFACE) - enable_language(CXX) add_library(nocxx20)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CXXModules/NoScanningFileSetProperty-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CXXModules/NoScanningFileSetProperty-result.txt
diff --git a/Tests/RunCMake/CXXModules/NoScanningFileSetProperty-stderr.txt b/Tests/RunCMake/CXXModules/NoScanningFileSetProperty-stderr.txt new file mode 100644 index 0000000..c8f4eec --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoScanningFileSetProperty-stderr.txt
@@ -0,0 +1,17 @@ +(CMake Error in CMakeLists\.txt: + The target named "noscanning-fs-property" has C\+\+ sources that may use + modules, but the compiler does not provide a way to discover the import + graph dependencies\. See the cmake-cxxmodules\(7\) manual for details\. Use + the CMAKE_CXX_SCAN_FOR_MODULES variable to enable or disable scanning\. +|CMake Error in CMakeLists\.txt: + The target named "noscanning-fs-property" has C\+\+ sources that may use + modules, but modules are not supported by this generator: + + [^ +]+ + + Modules are supported only by Ninja, Ninja Multi-Config, and Visual Studio + generators for VS 17\.4 and newer\. See the cmake-cxxmodules\(7\) manual for + details\. Use the CMAKE_CXX_SCAN_FOR_MODULES variable to enable or disable + scanning\. +)
diff --git a/Tests/RunCMake/CXXModules/NoScanningFileSetProperty.cmake b/Tests/RunCMake/CXXModules/NoScanningFileSetProperty.cmake new file mode 100644 index 0000000..a2d6fb3 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoScanningFileSetProperty.cmake
@@ -0,0 +1,16 @@ +enable_language(CXX) +unset(CMAKE_CXX_SCANDEP_SOURCE) + +add_executable(noscanning-fs-property) +target_sources(noscanning-fs-property PRIVATE + FILE_SET SOURCES FILES sources/module-use.cxx) +set_target_properties(noscanning-fs-property + PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_SCAN_FOR_MODULES 0) +set_source_files_properties(sources/module-use.cxx + PROPERTIES + CXX_SCAN_FOR_MODULES 0) +set_property(FILE_SET SOURCES TARGET noscanning-fs-property + PROPERTY CXX_SCAN_FOR_MODULES 1)
diff --git a/Tests/RunCMake/CXXModules/NoScanningVariable.cmake b/Tests/RunCMake/CXXModules/NoScanningVariable.cmake index 950f1f3..4bb6a70 100644 --- a/Tests/RunCMake/CXXModules/NoScanningVariable.cmake +++ b/Tests/RunCMake/CXXModules/NoScanningVariable.cmake
@@ -1,9 +1,6 @@ # Enable scanning by default for targets that explicitly use C++ 20. cmake_policy(SET CMP0155 NEW) -# Block making C++ `import std` targets. -add_library(__CMAKE::CXX23 IMPORTED INTERFACE) - enable_language(CXX) # Hide any real scanning rule that may be available.
diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake index 31c5e3e..4412283 100644 --- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
@@ -22,6 +22,7 @@ endif () run_cmake(NoScanningSourceFileProperty) + run_cmake(NoScanningFileSetProperty) run_cmake(NoScanningTargetProperty) run_cmake(NoScanningVariable) run_cmake(CMP0155-OLD) @@ -116,10 +117,15 @@ run_cmake(InstallBMI) run_cmake(InstallBMIGenericArgs) run_cmake(InstallBMIIgnore) +if(NOT RunCMake_GENERATOR MATCHES "^Visual Studio ") + run_cmake(InstallBMIAbsDir) +endif() run_cmake(ExportBuildCxxModules) +run_cmake(ExportBuildCxxModulesConfig) run_cmake(ExportBuildCxxModulesTargets) run_cmake(ExportInstallCxxModules) +run_cmake(ExportInstallCxxModulesConfig) # Generator-specific tests. if (RunCMake_GENERATOR MATCHES "Ninja")
diff --git a/Tests/RunCMake/CXXModules/VisualStudioNoSyntheticTargets-stderr.txt b/Tests/RunCMake/CXXModules/VisualStudioNoSyntheticTargets-stderr.txt index 6052b24..5ba9bc4 100644 --- a/Tests/RunCMake/CXXModules/VisualStudioNoSyntheticTargets-stderr.txt +++ b/Tests/RunCMake/CXXModules/VisualStudioNoSyntheticTargets-stderr.txt
@@ -1,6 +1,6 @@ ^(CMake Error in CMakeLists\.txt: - Target "imported-cxx-modules@synth_[0-9a-f]+" contains C\+\+ modules - intended for BMI-only compilation\. This is not yet supported by the Visual - Studio generator\. + Target "imported-cxx-modules@synth_[0-9]+" contains C\+\+ modules intended for + BMI-only compilation\. This is not yet supported by the Visual Studio + generator\. * )+CMake Generate step failed\. Build files cannot be regenerated correctly\.
diff --git a/Tests/RunCMake/CXXModules/sources/debug/module.cxx b/Tests/RunCMake/CXXModules/sources/debug/module.cxx new file mode 100644 index 0000000..37eedeb --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/debug/module.cxx
@@ -0,0 +1,5 @@ +export module M; +export import M:part; +import M:internal_part; + +int f();
diff --git a/Tests/RunCMake/CXXModules/sources/importable.cxx b/Tests/RunCMake/CXXModules/sources/importable.cxx new file mode 100644 index 0000000..607680a --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/importable.cxx
@@ -0,0 +1,6 @@ +export module importable; + +export int from_import() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModules/sources/release/module.cxx b/Tests/RunCMake/CXXModules/sources/release/module.cxx new file mode 100644 index 0000000..37eedeb --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/release/module.cxx
@@ -0,0 +1,5 @@ +export module M; +export import M:part; +import M:internal_part; + +int f();
diff --git a/Tests/RunCMake/CXXModulesCompile/RunCMakeTest.cmake b/Tests/RunCMake/CXXModulesCompile/RunCMakeTest.cmake index 695cbf7..becb546 100644 --- a/Tests/RunCMake/CXXModulesCompile/RunCMakeTest.cmake +++ b/Tests/RunCMake/CXXModulesCompile/RunCMakeTest.cmake
@@ -109,7 +109,11 @@ set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test_name}-build") if (RunCMake_GENERATOR_IS_MULTI_CONFIG) - set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) + if (RunCMake_CXXModules_CONFIGS) + set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=${RunCMake_CXXModules_CONFIGS}") + else () + set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) + endif () else () set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) endif () @@ -193,6 +197,8 @@ run_cxx_module_test(file-sets-with-dot) run_cxx_module_test(vs-without-flags) run_cxx_module_test(library library-static -DBUILD_SHARED_LIBS=OFF) + run_cxx_module_test(library library-static-file-set-SOURCES -DBUILD_SHARED_LIBS=OFF + -DWITH_FILE_SET_SOURCES=ON) run_cxx_module_test(unity-build) run_cxx_module_test(object-library) run_cxx_module_test(generated) @@ -201,6 +207,9 @@ run_cxx_module_test(non-triv-coll-rando) run_cxx_module_test(duplicate) set(RunCMake_CXXModules_NO_TEST 1) + if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(private-cxx-modules) + endif () run_cxx_module_test(imp-from-object) run_cxx_module_test(circular) run_cxx_module_test(try-compile) @@ -210,12 +219,15 @@ run_cxx_module_test(scan_props) run_cxx_module_test(target-objects) - # mixed-bmi-compatibility requires a generator that implements per-importer - # BMI generation + # Requires a generator that implements per-importer BMI generation if ("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND RunCMake_GENERATOR MATCHES "Ninja") - # FIXME(#25539,#27600,#27597): Restore per-importer BMI and enable this test. - # run_cxx_module_test(mixed-bmi-compatibility) + run_cxx_module_test(mixed-bmi-compatibility) + if (RunCMake_GENERATOR_IS_MULTI_CONFIG AND + "collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND + "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(multi-config-synth) + endif() endif() if ("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND @@ -271,6 +283,10 @@ set(RunCMake_CXXModules_NO_TEST 1) run_cxx_module_test(exp-builddb) + # Skip for multi-config generators. + if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + run_cxx_module_test(exp-builddb-emptyconfig) + endif () unset(RunCMake_CXXModules_NO_TEST) unset(RunCMake_CXXModules_TARGETS) @@ -319,6 +335,7 @@ # Tests which install BMIs if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(exp-nested-dirs-build) run_cxx_module_test(exp-iface-no-props-build) run_cxx_module_test(exp-iface-build) run_cxx_module_test(exp-incdirs-build) @@ -330,6 +347,7 @@ run_cxx_module_test(exp-trans-mods1-build) run_cxx_module_test(exp-trans-mods-build exp-trans-mods-build "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/exp-trans-mods1-build-build") run_cxx_module_test(exp-with-headers-build) + run_cxx_module_test(exp-iface-config-build) if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION) @@ -341,6 +359,7 @@ run_cxx_module_import_test(build exp-trans-targets-build -DTRANSITIVE_TARGETS=1) run_cxx_module_import_test(build exp-trans-mods-build -DTRANSITIVE_MODULES=1) run_cxx_module_import_test(build exp-with-headers-build -DWITH_HEADERS=1) + run_cxx_module_import_test(build exp-iface-config-build -DCONFIG_MODULES=1) if ("build_database" IN_LIST CMake_TEST_MODULE_COMPILATION) setup_export_build_database_targets() @@ -372,6 +391,11 @@ run_cxx_module_test(exp-trans-mods1-install) run_cxx_module_test(exp-trans-mods-install exp-trans-mods-install "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/exp-trans-mods1-install-install" "-Dexport_transitive_modules1_cps_DIR=${RunCMake_BINARY_DIR}/exp-trans-mods1-install-install/lib/cmake/export_transitive_modules1_cps") run_cxx_module_test(exp-with-headers-install) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_CXXModules_CONFIGS "Debug\\;Release") + endif() + run_cxx_module_test(exp-iface-config-install) + unset(RunCMake_CXXModules_CONFIGS) if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION) @@ -383,6 +407,7 @@ run_cxx_module_import_test(install exp-trans-targets-install -DTRANSITIVE_TARGETS=1) run_cxx_module_import_test(install exp-trans-mods-install -DTRANSITIVE_MODULES=1) run_cxx_module_import_test(install exp-with-headers-install -DWITH_HEADERS=1) + run_cxx_module_import_test(install exp-iface-config-install -DCONFIG_MODULES=1) endif () endif () endif ()
diff --git a/Tests/RunCMake/CXXModulesCompile/depchain-mods-json-file-rebuild-check.cmake b/Tests/RunCMake/CXXModulesCompile/depchain-mods-json-file-rebuild-check.cmake index 94e833a..cc22816 100644 --- a/Tests/RunCMake/CXXModulesCompile/depchain-mods-json-file-rebuild-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/depchain-mods-json-file-rebuild-check.cmake
@@ -1,12 +1,8 @@ -if (RunCMake_GENERATOR_IS_MULTI_CONFIG) - set(dep_modules_json_path "CMakeFiles/depchain_modules_json_file.dir/Debug/CXX.dd") - set(modules_json_path "CMakeFiles/depchain_with_modules_json_file.dir/Debug/CXXModules.json") -else () - set(dep_modules_json_path "CMakeFiles/depchain_modules_json_file.dir/CXX.dd") - set(modules_json_path "CMakeFiles/depchain_with_modules_json_file.dir/CXXModules.json") -endif () +file(GLOB synth_dirs + "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/depchain_with_modules_json_file@synth_*.dir") -if ("${RunCMake_TEST_BINARY_DIR}/${modules_json_path}" IS_NEWER_THAN "${RunCMake_TEST_BINARY_DIR}/${dep_modules_json_path}") +list(LENGTH synth_dirs synth_dirs_len) +if (NOT synth_dirs_len EQUAL 0) list(APPEND RunCMake_TEST_FAILED - "Object '${dep_modules_json_path}' should have recompiled if '${modules_json_path}' changed.") + "Expected no synthetic targets for consuming 'depchain_with_modules_json_file' but found ${synth_dirs_len}: ${synth_dirs}") endif ()
diff --git a/Tests/RunCMake/CXXModulesCompile/depchain-mods-json-file/pre-rebuild.cmake b/Tests/RunCMake/CXXModulesCompile/depchain-mods-json-file/pre-rebuild.cmake index 9c3f2ff..2b2ce92 100644 --- a/Tests/RunCMake/CXXModulesCompile/depchain-mods-json-file/pre-rebuild.cmake +++ b/Tests/RunCMake/CXXModulesCompile/depchain-mods-json-file/pre-rebuild.cmake
@@ -1,7 +1,17 @@ +file(GLOB synth_dirs + "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/depchain_with_modules_json_file@synth_*.dir") + +list(LENGTH synth_dirs synth_dirs_len) +if (NOT synth_dirs_len EQUAL 1) + return() +endif() + +list(GET synth_dirs 0 synth_dir) + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) - set(modules_json_path "CMakeFiles/depchain_with_modules_json_file.dir/Debug/CXXModules.json") + set(modules_json_path "${synth_dir}/Debug/CXXModules.json") else () - set(modules_json_path "CMakeFiles/depchain_with_modules_json_file.dir/CXXModules.json") + set(modules_json_path "${synth_dir}/CXXModules.json") endif () -file(TOUCH_NOCREATE "${RunCMake_TEST_BINARY_DIR}/${modules_json_path}") +file(TOUCH_NOCREATE "${modules_json_path}")
diff --git a/Tests/RunCMake/CXXModulesCompile/duplicate-sources-stderr.txt b/Tests/RunCMake/CXXModulesCompile/duplicate-sources-stderr.txt deleted file mode 100644 index 1dfe3d3..0000000 --- a/Tests/RunCMake/CXXModulesCompile/duplicate-sources-stderr.txt +++ /dev/null
@@ -1,8 +0,0 @@ -CMake Warning \(dev\) in CMakeLists\.txt: - Target "duplicate_sources" has source file - - [^ -]*/Tests/RunCMake/CXXModulesCompile/duplicate-sources/duplicate\.cxx - - in a "FILE_SET TYPE CXX_MODULES" multiple times\. -This warning is for project developers\. Use -Wno-dev to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-check.cmake index efa43d2..bff3a51 100644 --- a/Tests/RunCMake/CXXModulesCompile/exp-builddb-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-check.cmake
@@ -1,19 +1,19 @@ include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") -check_build_database("export-build-database" "build_database.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) +check_build_database("exp-builddb" "build_database.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_CXX.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX_Debug.json" NO_EXIST) -check_build_database("export-build-database" "build_database_Debug.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_CXX_Debug.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_Debug.json" NO_EXIST) if (RunCMake_GENERATOR_IS_MULTI_CONFIG) - check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" NO_EXIST) + check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" NO_EXIST) - check_build_database("export-build-database" "build_database_CXX_Release.json" NO_EXIST) - check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) - check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) + check_build_database("exp-builddb" "build_database_CXX_Release.json" NO_EXIST) + check_build_database("exp-builddb" "build_database_Release.json" NO_EXIST) + check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) else () - check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/CXX_build_database.json" NO_EXIST) + check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/CXX_build_database.json" NO_EXIST) endif () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-build-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-build-check.cmake new file mode 100644 index 0000000..d87c4f5 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-build-check.cmake
@@ -0,0 +1,11 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") + +check_build_database("exp-builddb-emptyconfig" "build_database.json" NO_EXIST) +check_build_database("exp-builddb-emptyconfig" "build_database_CXX.json" NO_EXIST) + +check_build_database("exp-builddb-emptyconfig" "build_database_CXX_Debug.json" NO_EXIST) +check_build_database("exp-builddb-emptyconfig" "build_database_Debug.json" NO_EXIST) + +check_build_database("exp-builddb-emptyconfig" "CMakeFiles/export_build_database.dir/CXX_build_database.json" JUST_TARGET) + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-check.cmake new file mode 100644 index 0000000..57ed805 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-check.cmake
@@ -0,0 +1,11 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") + +check_build_database("exp-builddb-emptyconfig" "build_database.json" NO_EXIST) +check_build_database("exp-builddb-emptyconfig" "build_database_CXX.json" NO_EXIST) + +check_build_database("exp-builddb-emptyconfig" "build_database_CXX_Debug.json" NO_EXIST) +check_build_database("exp-builddb-emptyconfig" "build_database_Debug.json" NO_EXIST) + +check_build_database("exp-builddb-emptyconfig" "CMakeFiles/export_build_database.dir/CXX_build_database.json" NO_EXIST) + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-stderr.txt b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-stderr.txt new file mode 100644 index 0000000..93f5525 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-stderr.txt
@@ -0,0 +1,5 @@ +CMake Warning \(experimental\) in CMakeLists\.txt: + CMake's support for exporting build databases is experimental\. It is meant + only for experimentation and feedback to CMake developers\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-target-cmake_build_database-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-target-cmake_build_database-check.cmake new file mode 100644 index 0000000..ffa1897 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-target-cmake_build_database-check.cmake
@@ -0,0 +1,8 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") + +check_build_database("exp-builddb-emptyconfig" "build_database.json" ALL) +check_build_database("exp-builddb-emptyconfig" "build_database_CXX.json" JUST_CXX) + +check_build_database("exp-builddb-emptyconfig" "CMakeFiles/export_build_database.dir/CXX_build_database.json" JUST_TARGET) + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-target-cmake_build_database/CXX-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-target-cmake_build_database/CXX-check.cmake new file mode 100644 index 0000000..0c08a3e --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig-target-cmake_build_database/CXX-check.cmake
@@ -0,0 +1,8 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") + +check_build_database("exp-builddb-emptyconfig" "build_database.json" NO_EXIST) +check_build_database("exp-builddb-emptyconfig" "build_database_CXX.json" JUST_CXX) + +check_build_database("exp-builddb-emptyconfig" "CMakeFiles/export_build_database.dir/CXX_build_database.json" JUST_TARGET) + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/CMakeLists.txt new file mode 100644 index 0000000..8997c29 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/CMakeLists.txt
@@ -0,0 +1,99 @@ +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_export_build_database CXX) + +# Force empty configuration. +set(CMAKE_BUILD_TYPE "") + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +include("${CMAKE_SOURCE_DIR}/../export-build-database-setup.cmake") + +# _MBCS default append for MSVC ABI workaround: see CMP0204 +# So, force add the default value for all platforms to be consistent. +cmake_policy(SET CMP0204 NEW) +add_compile_definitions(_MBCS) + +add_compile_options(-Dfrom_compile_options) +add_compile_definitions(-Dfrom_compile_definitions) +include_directories(from_include_directories) + +add_library(provide_flags INTERFACE) +target_compile_options(provide_flags + INTERFACE + -Ddep_interface_option) +target_compile_definitions(provide_flags + INTERFACE + dep_interface_define) +target_include_directories(provide_flags + INTERFACE + dep_interface_include) + +add_library(export_build_database) +target_sources(export_build_database + PRIVATE + lib.cxx + PUBLIC + FILE_SET modules + TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(export_build_database PUBLIC cxx_std_20) +target_link_libraries(export_build_database + PRIVATE + provide_flags) + +set_property(FILE_SET modules TARGET export_build_database PROPERTY COMPILE_OPTIONS "-Dfileset_private_option") +set_property(FILE_SET modules TARGET export_build_database PROPERTY COMPILE_DEFINITIONS "fileset_private_define") +set_property(FILE_SET modules TARGET export_build_database PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/fileset_private_include") + +set_property(SOURCE importable.cxx APPEND + PROPERTY COMPILE_FLAGS "-Dfrom_source_flag") +set_property(SOURCE importable.cxx APPEND + PROPERTY COMPILE_OPTIONS "-Dfrom_source_option") +set_property(SOURCE importable.cxx APPEND + PROPERTY COMPILE_DEFINITIONS "from_source_define") +set_property(SOURCE importable.cxx APPEND + PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/from_source_include") + +set_property(TARGET export_build_database APPEND + PROPERTY COMPILE_FLAGS "-Dfrom_compile_flags") +target_compile_options(export_build_database + PRIVATE + -Dtarget_private_option + INTERFACE + -Dtarget_interface_option + PUBLIC + -Dtarget_public_option) +target_compile_definitions(export_build_database + PRIVATE + target_private_define + INTERFACE + target_interface_define + PUBLIC + target_public_define) +target_include_directories(export_build_database + PRIVATE + target_private_include + INTERFACE + target_interface_include + PUBLIC + target_public_include) + +install(TARGETS export_build_database + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +export(EXPORT CXXModules + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_build_database-targets.cmake") +install(TARGETS provide_flags + EXPORT CXXModulesDeps) +export(EXPORT CXXModulesDeps + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_build_database-dep-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_build_database-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_build_database-dep-targets.cmake\") +include(\"\${CMAKE_CURRENT_LIST_DIR}/export_build_database-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +")
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/dep_interface_include/anchor similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/dep_interface_include/anchor
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/importable.cxx b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/importable.cxx new file mode 100644 index 0000000..607680a --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/importable.cxx
@@ -0,0 +1,6 @@ +export module importable; + +export int from_import() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/lib.cxx b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/lib.cxx new file mode 100644 index 0000000..b144b27 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/lib.cxx
@@ -0,0 +1,6 @@ +import importable; + +int f() +{ + return from_import(); +}
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/target_interface_include/anchor similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/target_interface_include/anchor
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/target_public_include/anchor similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/CXXModulesCompile/exp-builddb-emptyconfig/target_public_include/anchor
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-stderr.txt b/Tests/RunCMake/CXXModulesCompile/exp-builddb-stderr.txt index a001af7..93f5525 100644 --- a/Tests/RunCMake/CXXModulesCompile/exp-builddb-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-stderr.txt
@@ -1,4 +1,5 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(experimental\) in CMakeLists\.txt: CMake's support for exporting build databases is experimental\. It is meant only for experimentation and feedback to CMake developers\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX-check.cmake index cf712ba..a0069ed 100644 --- a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX-check.cmake
@@ -1,21 +1,21 @@ include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") -check_build_database("export-build-database" "build_database.json" NO_EXIST) +check_build_database("exp-builddb" "build_database.json" NO_EXIST) if (RunCMake_GENERATOR_IS_MULTI_CONFIG) - check_build_database("export-build-database" "build_database_CXX.json" JUST_CXX_MULTI) + check_build_database("exp-builddb" "build_database_CXX.json" JUST_CXX_MULTI) - check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) - check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) - check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + check_build_database("exp-builddb" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("exp-builddb" "build_database_Debug.json" JUST_DEBUG) + check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) - check_build_database("export-build-database" "build_database_CXX_Release.json" CXX_AND_RELEASE) - check_build_database("export-build-database" "build_database_Release.json" JUST_RELEASE) - check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) + check_build_database("exp-builddb" "build_database_CXX_Release.json" CXX_AND_RELEASE) + check_build_database("exp-builddb" "build_database_Release.json" JUST_RELEASE) + check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) else () - check_build_database("export-build-database" "build_database_CXX.json" JUST_CXX) + check_build_database("exp-builddb" "build_database_CXX.json" JUST_CXX) - check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/CXX_build_database.json" JUST_TARGET) + check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/CXX_build_database.json" JUST_TARGET) endif () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX/Debug-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX/Debug-check.cmake index 6f60675..0e7316a 100644 --- a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX/Debug-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX/Debug-check.cmake
@@ -1,14 +1,14 @@ include("${CMAKE_CURRENT_LIST_DIR}/../../build-database-check.cmake") -check_build_database("export-build-database" "build_database.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) +check_build_database("exp-builddb" "build_database.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_CXX.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) -check_build_database("export-build-database" "build_database_Debug.json" NO_EXIST) -check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) +check_build_database("exp-builddb" "build_database_CXX_Debug.json" CXX_AND_DEBUG) +check_build_database("exp-builddb" "build_database_Debug.json" NO_EXIST) +check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) -check_build_database("export-build-database" "build_database_CXX_Release.json" NO_EXIST) -check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) -check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_CXX_Release.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_Release.json" NO_EXIST) +check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX/Release-Release-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX/Release-Release-check.cmake index dcb36c9..49b01a5 100644 --- a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX/Release-Release-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/CXX/Release-Release-check.cmake
@@ -1,14 +1,14 @@ include("${CMAKE_CURRENT_LIST_DIR}/../../build-database-check.cmake") -check_build_database("export-build-database" "build_database.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) +check_build_database("exp-builddb" "build_database.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_CXX.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) -check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) -check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) +check_build_database("exp-builddb" "build_database_CXX_Debug.json" CXX_AND_DEBUG) +check_build_database("exp-builddb" "build_database_Debug.json" JUST_DEBUG) +check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) -check_build_database("export-build-database" "build_database_CXX_Release.json" CXX_AND_RELEASE) -check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) -check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) +check_build_database("exp-builddb" "build_database_CXX_Release.json" CXX_AND_RELEASE) +check_build_database("exp-builddb" "build_database_Release.json" NO_EXIST) +check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/Debug-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/Debug-check.cmake index 6868302..a3ff2b0 100644 --- a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/Debug-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/Debug-check.cmake
@@ -1,14 +1,14 @@ include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") -check_build_database("export-build-database" "build_database.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) +check_build_database("exp-builddb" "build_database.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_CXX.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) -check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) -check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) +check_build_database("exp-builddb" "build_database_CXX_Debug.json" CXX_AND_DEBUG) +check_build_database("exp-builddb" "build_database_Debug.json" JUST_DEBUG) +check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) -check_build_database("export-build-database" "build_database_CXX_Release.json" NO_EXIST) -check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) -check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_CXX_Release.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_Release.json" NO_EXIST) +check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/Release-Release-check.cmake b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/Release-Release-check.cmake index 31da4be..325467c 100644 --- a/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/Release-Release-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb-target-cmake_build_database/Release-Release-check.cmake
@@ -1,14 +1,14 @@ include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") -check_build_database("export-build-database" "build_database.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) +check_build_database("exp-builddb" "build_database.json" NO_EXIST) +check_build_database("exp-builddb" "build_database_CXX.json" NO_EXIST) -check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) -check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) -check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) +check_build_database("exp-builddb" "build_database_CXX_Debug.json" CXX_AND_DEBUG) +check_build_database("exp-builddb" "build_database_Debug.json" JUST_DEBUG) +check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) -check_build_database("export-build-database" "build_database_CXX_Release.json" CXX_AND_RELEASE) -check_build_database("export-build-database" "build_database_Release.json" JUST_RELEASE) -check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) +check_build_database("exp-builddb" "build_database_CXX_Release.json" CXX_AND_RELEASE) +check_build_database("exp-builddb" "build_database_Release.json" JUST_RELEASE) +check_build_database("exp-builddb" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-builddb/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/exp-builddb/CMakeLists.txt index 0ce2072..4f09faa 100644 --- a/Tests/RunCMake/CXXModulesCompile/exp-builddb/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/exp-builddb/CMakeLists.txt
@@ -41,12 +41,18 @@ PRIVATE provide_flags) +set_property(FILE_SET modules TARGET export_build_database PROPERTY COMPILE_OPTIONS "-Dfileset_private_option") +set_property(FILE_SET modules TARGET export_build_database PROPERTY COMPILE_DEFINITIONS "fileset_private_define") +set_property(FILE_SET modules TARGET export_build_database PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/fileset_private_include") + set_property(SOURCE importable.cxx APPEND PROPERTY COMPILE_FLAGS "-Dfrom_source_flag") set_property(SOURCE importable.cxx APPEND PROPERTY COMPILE_OPTIONS "-Dfrom_source_option") set_property(SOURCE importable.cxx APPEND PROPERTY COMPILE_DEFINITIONS "from_source_define") +set_property(SOURCE importable.cxx APPEND + PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/from_source_include") set_property(TARGET export_build_database APPEND PROPERTY COMPILE_FLAGS "-Dfrom_compile_flags")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/CMakeLists.txt new file mode 100644 index 0000000..296198c --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/CMakeLists.txt
@@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 4.3) +project(cxx_modules_export_interface_config CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_interface_config STATIC) +target_sources(export_interface_config + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,debug,release>" + FILES + "${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,debug,release>/module.cxx" + ) +target_compile_features(export_interface_config PUBLIC cxx_std_20) + +install(TARGETS export_interface_config + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu" + ) + +export(EXPORT CXXModules + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_interface_config-targets.cmake" + CXX_MODULES_DIRECTORY "export_interface_config-cxx-modules" + ) +export(PACKAGE_INFO export_interface_config_cps + EXPORT CXXModules + CXX_MODULES_DIRECTORY "export_interface_config-cxx-modules-cps" + ) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_interface_config-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_interface_config-targets.cmake\")\n" + "set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)\n" + ) + +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_interface_config_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_source_dir=${CMAKE_CURRENT_SOURCE_DIR}" + "-Dexpected_binary_dir=${CMAKE_CURRENT_BINARY_DIR}" + "-DCMAKE_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test" + )
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/debug/module.cxx b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/debug/module.cxx new file mode 100644 index 0000000..ad83965 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/debug/module.cxx
@@ -0,0 +1,5 @@ +export module importable; +export int from_import() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/release/module.cxx b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/release/module.cxx new file mode 100644 index 0000000..573bd99 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/release/module.cxx
@@ -0,0 +1,5 @@ +export module importable; +export int from_import() +{ + return 1; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/test/CMakeLists.txt new file mode 100644 index 0000000..f972cd9 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-build/test/CMakeLists.txt
@@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 4.3) +project(cxx_modules_config_import NONE) + +find_package(export_interface_config REQUIRED) +find_package(export_interface_config_cps CONFIG REQUIRED) + +function(test_target target set_name) + set(check_genex "${ARGV2}") + if (NOT TARGET ${target}) + message(FATAL_ERROR "Missing imported target") + endif () + + get_property(file_sets TARGET ${target} + PROPERTY INTERFACE_CXX_MODULE_SETS) + if (NOT file_sets STREQUAL set_name) + message(FATAL_ERROR + "Incorrect exported file sets in ${target}:\n ${file_sets}") + endif () + + get_property(file_set_files TARGET ${target} + PROPERTY CXX_MODULE_SET_${set_name}) + + get_property(_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + + # Verify the Debug module file is present in the file set. + if (NOT file_set_files MATCHES "debug/module\.cxx") + message(FATAL_ERROR + "Expected debug module in file set:\n ${file_set_files}") + endif () + + # For CMakeConfig, verify genex wrapping for both configs. + if (check_genex AND _multi_config) + if (NOT file_set_files MATCHES [[\$<\$<CONFIG:Debug>:.*debug/module\.cxx]]) + message(FATAL_ERROR + "Missing Debug genex entry in file set:\n ${file_set_files}") + endif () + if (NOT file_set_files MATCHES [[\$<\$<CONFIG:Release>:.*release/module\.cxx]]) + message(FATAL_ERROR + "Missing Release genex entry in file set:\n ${file_set_files}") + endif () + endif () + + # Verify IMPORTED_CXX_MODULES_DEBUG points to the Debug module. + get_property(imported_modules_debug TARGET ${target} + PROPERTY IMPORTED_CXX_MODULES_DEBUG) + if (NOT imported_modules_debug MATCHES "importable=.*debug/module\.cxx") + message(FATAL_ERROR + "Incorrect Debug module entry in IMPORTED_CXX_MODULES_DEBUG:\n ${imported_modules_debug}") + endif () +endfunction() + +test_target(CXXModules::export_interface_config "modules" FALSE) +test_target(export_interface_config_cps::export_interface_config "CXX_MODULES" FALSE)
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/CMakeLists.txt new file mode 100644 index 0000000..5af7d6f --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/CMakeLists.txt
@@ -0,0 +1,63 @@ +cmake_minimum_required(VERSION 4.3) +project(cxx_modules_export_interface_config CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_interface_config STATIC) +target_sources(export_interface_config + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,debug,release>" + FILES + "${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,debug,release>/module.cxx" + ) +target_compile_features(export_interface_config PUBLIC cxx_std_20) + +install(TARGETS export_interface_config + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu" + ) + +install(EXPORT CXXModules + NAMESPACE CXXModules:: + DESTINATION "lib/cmake/export_interface_config" + FILE "export_interface_config-targets.cmake" + CXX_MODULES_DIRECTORY "export_interface_config-cxx-modules" + ) +install(PACKAGE_INFO "export_interface_config_cps" + EXPORT CXXModules + DESTINATION "lib/cmake/export_interface_config_cps" + CXX_MODULES_DIRECTORY "export_interface_config-cxx-modules-cps" + ) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_interface_config-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_interface_config-targets.cmake\")\n" + "set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)\n" + ) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_interface_config-config.cmake" + DESTINATION "lib/cmake/export_interface_config" + ) + +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_interface_config_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_source_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu" + "-Dexpected_binary_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/bmi" + "-Dexport_interface_config_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_interface_config" + "-Dexport_interface_config_cps_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_interface_config_cps" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test" + )
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/debug/module.cxx b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/debug/module.cxx new file mode 100644 index 0000000..ad83965 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/debug/module.cxx
@@ -0,0 +1,5 @@ +export module importable; +export int from_import() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/release/module.cxx b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/release/module.cxx new file mode 100644 index 0000000..573bd99 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/release/module.cxx
@@ -0,0 +1,5 @@ +export module importable; +export int from_import() +{ + return 1; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/test/CMakeLists.txt new file mode 100644 index 0000000..f1619f9 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-iface-config-install/test/CMakeLists.txt
@@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 4.3) +project(cxx_modules_config_import NONE) + +find_package(export_interface_config REQUIRED) +find_package(export_interface_config_cps CONFIG REQUIRED) + +get_property(_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + +function(test_target target set_name) + set(check_genex "${ARGV2}") + if (NOT TARGET ${target}) + message(FATAL_ERROR "Missing imported target") + endif () + + get_property(file_sets TARGET ${target} + PROPERTY INTERFACE_CXX_MODULE_SETS) + if (NOT file_sets STREQUAL set_name) + message(FATAL_ERROR + "Incorrect exported file sets in ${target}:\n ${file_sets}") + endif () + + get_property(file_set_files TARGET ${target} + PROPERTY CXX_MODULE_SET_${set_name}) + + # Verify module.cxx is present in the file set. + if (NOT file_set_files MATCHES "module\.cxx") + message(FATAL_ERROR + "Expected module.cxx in file set:\n ${file_set_files}") + endif () + + # For CMakeConfig, verify genex wrapping for both configs. + if (check_genex AND _multi_config) + if (NOT file_set_files MATCHES [[\$<\$<CONFIG:Debug>:.*module\.cxx]]) + message(FATAL_ERROR + "Missing Debug genex entry in file set:\n ${file_set_files}") + endif () + if (NOT file_set_files MATCHES [[\$<\$<CONFIG:Release>:.*module\.cxx]]) + message(FATAL_ERROR + "Missing Release genex entry in file set:\n ${file_set_files}") + endif () + endif () + + # Verify IMPORTED_CXX_MODULES_DEBUG points to the module. + get_property(imported_modules_debug TARGET ${target} + PROPERTY IMPORTED_CXX_MODULES_DEBUG) + if (NOT imported_modules_debug MATCHES "importable=.*module\.cxx") + message(FATAL_ERROR + "Incorrect Debug module entry in IMPORTED_CXX_MODULES_DEBUG:\n ${imported_modules_debug}") + endif () +endfunction() + +test_target(CXXModules::export_interface_config "modules" TRUE) +test_target(export_interface_config_cps::export_interface_config "CXX_MODULES" FALSE)
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/CMakeLists.txt new file mode 100644 index 0000000..12d1474 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/CMakeLists.txt
@@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.24...3.28) +project(cxx_modules_nested_dirs CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_nested_dirs STATIC) +target_sources(export_nested_dirs + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + nested/alpha.cxx + nested/sub/beta.cxx + ) +target_compile_features(export_nested_dirs PUBLIC cxx_std_20) + +install(TARGETS export_nested_dirs + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +export(EXPORT CXXModules + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_nested_dirs-targets.cmake" + CXX_MODULES_DIRECTORY "export_nested_dirs-cxx-modules") +export(PACKAGE_INFO export_nested_dirs_cps + EXPORT CXXModules + CXX_MODULES_DIRECTORY "export_nested_dirs-cxx-modules-cps") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_nested_dirs-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_nested_dirs-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_nested_dirs_build + COMMAND + "${CMAKE_COMMAND}" + "-DCMAKE_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/nested/alpha.cxx b/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/nested/alpha.cxx new file mode 100644 index 0000000..d5c20c0 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/nested/alpha.cxx
@@ -0,0 +1,6 @@ +export module nested_alpha; + +export int from_alpha() +{ + return 1; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/nested/sub/beta.cxx b/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/nested/sub/beta.cxx new file mode 100644 index 0000000..fea3c24 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/nested/sub/beta.cxx
@@ -0,0 +1,6 @@ +export module nested_sub_beta; + +export int from_beta() +{ + return 2; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/test/CMakeLists.txt new file mode 100644 index 0000000..47d693e --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/exp-nested-dirs-build/test/CMakeLists.txt
@@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.28) +project(cxx_modules_library NONE) + +find_package(export_nested_dirs REQUIRED) +find_package(export_nested_dirs_cps REQUIRED)
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-all-multi.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-all-multi.json index 1cb79bb..4937728 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-all-multi.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-all-multi.json
@@ -84,6 +84,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -98,6 +102,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "-c", @@ -128,6 +133,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -141,7 +150,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "private": false, @@ -237,6 +247,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -251,6 +265,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_OTHER_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_OTHER_DIR>/importable.cxx<OBJEXT>", "-c", @@ -281,6 +296,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -294,7 +313,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_OTHER_DIR>/importable.cxx<OBJEXT>", "private": false,
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-all.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-all.json index 9b13bd0..60c2cdb 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-all.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-all.json
@@ -84,6 +84,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -98,6 +102,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "-c", @@ -128,6 +133,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -141,7 +150,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "private": false,
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-config.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-config.json index 0253351..df5b9af 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-config.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-config.json
@@ -84,6 +84,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -98,6 +102,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "-c", @@ -128,6 +133,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -141,7 +150,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "private": false,
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx-config.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx-config.json index 0253351..df5b9af 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx-config.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx-config.json
@@ -84,6 +84,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -98,6 +102,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "-c", @@ -128,6 +133,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -141,7 +150,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "private": false,
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx-multi.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx-multi.json index 78cde14..e4b33eb 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx-multi.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx-multi.json
@@ -84,6 +84,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -98,6 +102,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "-c", @@ -128,6 +133,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -141,7 +150,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "private": false, @@ -237,6 +247,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -251,6 +265,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_OTHER_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_OTHER_DIR>/importable.cxx<OBJEXT>", "-c", @@ -281,6 +296,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -294,7 +313,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_OTHER_DIR>/importable.cxx<OBJEXT>", "private": false,
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx.json index 0253351..df5b9af 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-cxx.json
@@ -84,6 +84,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -98,6 +102,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "-c", @@ -128,6 +133,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -141,7 +150,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "private": false,
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-all.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-all.json new file mode 100644 index 0000000..4eb0990 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-all.json
@@ -0,0 +1,163 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/lib.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/lib.cxx", + "work-directory": "<BINARY_DIR>" + }, + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_source_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "-Dfileset_private_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/importable.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_source_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "-Dfileset_private_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>", + "private": false, + "provides": { + "importable": "<BINARY_DIR>/CMakeFiles/export_build_database.dir/importable<BMIEXT>" + }, + "requires": [], + "source": "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/importable.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [] + } + ] +}
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-cxx.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-cxx.json new file mode 100644 index 0000000..9bf9097 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-cxx.json
@@ -0,0 +1,163 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/lib.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/lib.cxx", + "work-directory": "<BINARY_DIR>" + }, + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_source_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "-Dfileset_private_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/importable.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_source_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "-Dfileset_private_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>", + "private": false, + "provides": { + "importable": "PATH:<BINARY_DIR>/CMakeFiles/export_build_database.dir/importable<BMIEXT>" + }, + "requires": [], + "source": "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/importable.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [] + } + ] +}
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-target.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-target.json new file mode 100644 index 0000000..4eb0990 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-emptyconfig-target.json
@@ -0,0 +1,163 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/lib.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/lib.cxx", + "work-directory": "<BINARY_DIR>" + }, + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_source_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "-Dfileset_private_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/importable.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_source_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/from_include_directories", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/target_public_include", + "PATH:-I<SOURCE_DIR>/exp-builddb-emptyconfig/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "-Dfileset_private_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx<OBJEXT>", + "private": false, + "provides": { + "importable": "<BINARY_DIR>/CMakeFiles/export_build_database.dir/importable<BMIEXT>" + }, + "requires": [], + "source": "PATH:<SOURCE_DIR>/exp-builddb-emptyconfig/importable.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [] + } + ] +}
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all-multi.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all-multi.json index eee9105..e835f6a 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all-multi.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all-multi.json
@@ -51,12 +51,16 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] }, { "family-name": "use_import_interfaces", @@ -107,16 +111,152 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG_OTHER>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG_OTHER>" + ] + }, + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] + }, + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG_OTHER>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_OTHER_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_OTHER_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_OTHER_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG_OTHER>" + ] }, { "family-name": "REGEX:CXXModules::export_build_database@<HEX>", - "name": "REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>", + "name": "CXXModules__export_build_database@synth_0@<CONFIG>", "translation-units": [ { "arguments": [ @@ -138,11 +278,11 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", "-Dtarget_interface_option", - "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/.d\"", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/.d\"", "REGEX:<BMI_ONLY_FLAG>", - "REGEX:PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/", + "PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/", "REGEX:-[cv]", "PATH:<SOURCE_DIR>/exp-builddb/importable.cxx" ], @@ -164,8 +304,8 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], "local-arguments": [ "-D_MBCS", @@ -185,10 +325,9 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], - "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": false, "provides": { "importable": "<IGNORE>" @@ -202,7 +341,7 @@ }, { "family-name": "REGEX:CXXModules::export_build_database@<HEX>", - "name": "REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG_OTHER>", + "name": "CXXModules__export_build_database@synth_0@<CONFIG_OTHER>", "translation-units": [ { "arguments": [ @@ -224,11 +363,11 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", "-Dtarget_interface_option", - "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_OTHER_DIR>/.d\"", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_OTHER_DIR>/.d\"", "REGEX:<BMI_ONLY_FLAG>", - "REGEX:PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_OTHER_DIR>/", + "PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_OTHER_DIR>/", "REGEX:-[cv]", "PATH:<SOURCE_DIR>/exp-builddb/importable.cxx" ], @@ -250,8 +389,8 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], "local-arguments": [ "-D_MBCS", @@ -271,10 +410,9 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], - "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>", "private": false, "provides": { "importable": "<IGNORE>"
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all.json index 4d1a3df..1f50bfa 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all.json
@@ -51,16 +51,86 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] + }, + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] }, { "family-name": "REGEX:CXXModules::export_build_database@<HEX>", - "name": "REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>", + "name": "CXXModules__export_build_database@synth_0@<CONFIG>", "translation-units": [ { "arguments": [ @@ -82,11 +152,11 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", "-Dtarget_interface_option", - "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/.d\"", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/.d\"", "REGEX:<BMI_ONLY_FLAG>", - "REGEX:PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/", + "PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/", "REGEX:-[cv]", "PATH:<SOURCE_DIR>/exp-builddb/importable.cxx" ], @@ -108,8 +178,8 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], "local-arguments": [ "-D_MBCS", @@ -129,10 +199,9 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], - "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": false, "provides": { "importable": "<IGNORE>"
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-config.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-config.json index 4d1a3df..1f50bfa 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-config.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-config.json
@@ -51,16 +51,86 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] + }, + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] }, { "family-name": "REGEX:CXXModules::export_build_database@<HEX>", - "name": "REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>", + "name": "CXXModules__export_build_database@synth_0@<CONFIG>", "translation-units": [ { "arguments": [ @@ -82,11 +152,11 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", "-Dtarget_interface_option", - "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/.d\"", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/.d\"", "REGEX:<BMI_ONLY_FLAG>", - "REGEX:PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/", + "PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/", "REGEX:-[cv]", "PATH:<SOURCE_DIR>/exp-builddb/importable.cxx" ], @@ -108,8 +178,8 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], "local-arguments": [ "-D_MBCS", @@ -129,10 +199,9 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], - "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": false, "provides": { "importable": "<IGNORE>"
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-config.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-config.json index 4d1a3df..1f50bfa 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-config.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-config.json
@@ -51,16 +51,86 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] + }, + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] }, { "family-name": "REGEX:CXXModules::export_build_database@<HEX>", - "name": "REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>", + "name": "CXXModules__export_build_database@synth_0@<CONFIG>", "translation-units": [ { "arguments": [ @@ -82,11 +152,11 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", "-Dtarget_interface_option", - "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/.d\"", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/.d\"", "REGEX:<BMI_ONLY_FLAG>", - "REGEX:PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/", + "PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/", "REGEX:-[cv]", "PATH:<SOURCE_DIR>/exp-builddb/importable.cxx" ], @@ -108,8 +178,8 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], "local-arguments": [ "-D_MBCS", @@ -129,10 +199,9 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], - "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": false, "provides": { "importable": "<IGNORE>"
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-multi.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-multi.json index eee9105..e835f6a 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-multi.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-multi.json
@@ -51,12 +51,16 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] }, { "family-name": "use_import_interfaces", @@ -107,16 +111,152 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG_OTHER>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG_OTHER>" + ] + }, + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] + }, + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG_OTHER>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_OTHER_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_OTHER_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_OTHER_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG_OTHER>" + ] }, { "family-name": "REGEX:CXXModules::export_build_database@<HEX>", - "name": "REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>", + "name": "CXXModules__export_build_database@synth_0@<CONFIG>", "translation-units": [ { "arguments": [ @@ -138,11 +278,11 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", "-Dtarget_interface_option", - "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/.d\"", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/.d\"", "REGEX:<BMI_ONLY_FLAG>", - "REGEX:PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/", + "PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/", "REGEX:-[cv]", "PATH:<SOURCE_DIR>/exp-builddb/importable.cxx" ], @@ -164,8 +304,8 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], "local-arguments": [ "-D_MBCS", @@ -185,10 +325,9 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], - "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": false, "provides": { "importable": "<IGNORE>" @@ -202,7 +341,7 @@ }, { "family-name": "REGEX:CXXModules::export_build_database@<HEX>", - "name": "REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG_OTHER>", + "name": "CXXModules__export_build_database@synth_0@<CONFIG_OTHER>", "translation-units": [ { "arguments": [ @@ -224,11 +363,11 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", "-Dtarget_interface_option", - "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_OTHER_DIR>/.d\"", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_OTHER_DIR>/.d\"", "REGEX:<BMI_ONLY_FLAG>", - "REGEX:PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_OTHER_DIR>/", + "PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_OTHER_DIR>/", "REGEX:-[cv]", "PATH:<SOURCE_DIR>/exp-builddb/importable.cxx" ], @@ -250,8 +389,8 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], "local-arguments": [ "-D_MBCS", @@ -271,10 +410,9 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], - "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_OTHER_DIR>/use.cxx<OBJEXT>", "private": false, "provides": { "importable": "<IGNORE>"
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx.json index 4d1a3df..1f50bfa 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx.json
@@ -51,16 +51,86 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] + }, + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] }, { "family-name": "REGEX:CXXModules::export_build_database@<HEX>", - "name": "REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>", + "name": "CXXModules__export_build_database@synth_0@<CONFIG>", "translation-units": [ { "arguments": [ @@ -82,11 +152,11 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", "-Dtarget_interface_option", - "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/.d\"", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/.d\"", "REGEX:<BMI_ONLY_FLAG>", - "REGEX:PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_<HEX>.dir<CONFIG_DIR>/", + "PATH:<OUTPUT_FLAG>CMakeFiles/CXXModules__export_build_database@synth_0.dir<CONFIG_DIR>/", "REGEX:-[cv]", "PATH:<SOURCE_DIR>/exp-builddb/importable.cxx" ], @@ -108,8 +178,8 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], "local-arguments": [ "-D_MBCS", @@ -129,10 +199,9 @@ "-Dfrom_compile_options", "-Dtarget_private_option", "-Dtarget_public_option", - "-Ddep_interface_option", - "-Dtarget_interface_option" + "-Dtarget_interface_option", + "-Ddep_interface_option" ], - "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": false, "provides": { "importable": "<IGNORE>"
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-matching-target.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-matching-target.json new file mode 100644 index 0000000..e6d2629 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-matching-target.json
@@ -0,0 +1,72 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "use_import_interfaces_matching", + "name": "use_import_interfaces_matching@<CONFIG>", + "translation-units": [ + { + "arguments": [ + "<IGNORE>", + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>.d\"", + "PATH:<OUTPUT_FLAG>CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "-c", + "PATH:<SOURCE_DIR>/imp-mods/use.cxx" + ], + "baseline-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-D_MBCS", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_interface_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx_<CONFIG_LOWER>_flags", + "<CXX20_OPTION>", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces_matching.dir<CONFIG_DIR>/use.cxx<OBJEXT>", + "private": true, + "provides": {}, + "requires": [ + "importable" + ], + "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", + "work-directory": "<BINARY_DIR>" + } + ], + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] + } + ] +}
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-target.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-target.json index 1263ef0..193c549 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-target.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-target.json
@@ -51,12 +51,16 @@ "object": "PATH:CMakeFiles/use_import_interfaces.dir<CONFIG_DIR>/use.cxx<OBJEXT>", "private": true, "provides": {}, - "requires": ["importable"], + "requires": [ + "importable" + ], "source": "PATH:<SOURCE_DIR>/imp-mods/use.cxx", "work-directory": "<BINARY_DIR>" } ], - "visible-sets": ["REGEX:CXXModules__export_build_database@synth_<HEX>@<CONFIG>"] + "visible-sets": [ + "CXXModules__export_build_database@synth_0@<CONFIG>" + ] } ] }
diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-target.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-target.json index 9b13bd0..60c2cdb 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-target.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-target.json
@@ -84,6 +84,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -98,6 +102,7 @@ "-Ddep_interface_option", "-Dfrom_source_flag", "-Dfrom_source_option", + "-Dfileset_private_option", "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>.d\"", "PATH:<OUTPUT_FLAG>CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "-c", @@ -128,6 +133,10 @@ "-Dfrom_compile_definitions", "-Dtarget_private_define", "-Dtarget_public_define", + "-Dfrom_source_define", + "-Dfileset_private_define", + "PATH:-I<SOURCE_DIR>/exp-builddb/fileset_private_include", + "PATH:-I<SOURCE_DIR>/exp-builddb/from_source_include", "PATH:-I<SOURCE_DIR>/exp-builddb/from_include_directories", "PATH:-I<SOURCE_DIR>/exp-builddb/target_private_include", "PATH:-I<SOURCE_DIR>/exp-builddb/target_public_include", @@ -141,7 +150,8 @@ "-Dtarget_public_option", "-Ddep_interface_option", "-Dfrom_source_flag", - "-Dfrom_source_option" + "-Dfrom_source_option", + "-Dfileset_private_option" ], "object": "PATH:CMakeFiles/export_build_database.dir<CONFIG_DIR>/importable.cxx<OBJEXT>", "private": false,
diff --git a/Tests/RunCMake/CXXModulesCompile/export-build-database-setup.cmake b/Tests/RunCMake/CXXModulesCompile/export-build-database-setup.cmake index 4b85cfa..5a853b5 100644 --- a/Tests/RunCMake/CXXModulesCompile/export-build-database-setup.cmake +++ b/Tests/RunCMake/CXXModulesCompile/export-build-database-setup.cmake
@@ -1,4 +1,4 @@ -set(CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE "73194a1d-c0b5-41b9-9190-a4512925e192") +set(CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE "70ef007e-b743-492d-9407-e35eeac03a40") # _MBCS default append for MSVC ABI workaround: see CMP0204 # So, force add the default value for all platforms to be consistent.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-dummy-std-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-dummy-std-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-dummy-std-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-dummy-std-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-dummy-std/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-dummy-std/CMakeLists.txt index 051b6ab..8a0bdb5 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-dummy-std/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-dummy-std/CMakeLists.txt
@@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") set(CMAKE_CXX_STDLIB_MODULES_JSON "${CMAKE_CURRENT_LIST_DIR}/../dummy-std/manifest/dummy-std.modules.json" )
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-stderr.txt index a001af7..93f5525 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-stderr.txt
@@ -1,4 +1,5 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(experimental\) in CMakeLists\.txt: CMake's support for exporting build databases is experimental\. It is meant only for experimentation and feedback to CMake developers\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database-check.cmake b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database-check.cmake index 518c20b..5154881 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database-check.cmake
@@ -8,15 +8,18 @@ check_build_database("exp-builddb-imped" "build_database_CXX_Debug.json" CXX_AND_DEBUG) check_build_database("exp-builddb-imped" "build_database_Debug.json" JUST_DEBUG) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) check_build_database("exp-builddb-imped" "build_database_CXX_Release.json" CXX_AND_RELEASE) check_build_database("exp-builddb-imped" "build_database_Release.json" JUST_RELEASE) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) else () check_build_database("exp-builddb-imped" "build_database.json" ALL) check_build_database("exp-builddb-imped" "build_database_CXX.json" JUST_CXX) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/CXX_build_database.json" JUST_TARGET) endif () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX-check.cmake b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX-check.cmake index 4cfe667..5c84841 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX-check.cmake
@@ -9,14 +9,17 @@ check_build_database("exp-builddb-imped" "build_database_CXX_Debug.json" CXX_AND_DEBUG) check_build_database("exp-builddb-imped" "build_database_Debug.json" JUST_DEBUG) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) check_build_database("exp-builddb-imped" "build_database_CXX_Release.json" CXX_AND_RELEASE) check_build_database("exp-builddb-imped" "build_database_Release.json" JUST_RELEASE) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) else () check_build_database("exp-builddb-imped" "build_database_CXX.json" JUST_CXX) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/CXX_build_database.json" JUST_TARGET) endif () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX/Debug-check.cmake b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX/Debug-check.cmake index 05e542f..b020210 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX/Debug-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX/Debug-check.cmake
@@ -8,12 +8,15 @@ check_build_database("exp-builddb-imped" "build_database_CXX_Debug.json" CXX_AND_DEBUG) check_build_database("exp-builddb-imped" "build_database_Debug.json" NO_EXIST) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) check_build_database("exp-builddb-imped" "build_database_CXX_Release.json" NO_EXIST) check_build_database("exp-builddb-imped" "build_database_Release.json" NO_EXIST) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" NO_EXIST) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Release/CXX_build_database.json" NO_EXIST) else () check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/CXX_build_database.json" JUST_TARGET) endif () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX/Release-Release-check.cmake b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX/Release-Release-check.cmake index 5ff723c..df684d5 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX/Release-Release-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/CXX/Release-Release-check.cmake
@@ -8,12 +8,15 @@ check_build_database("exp-builddb-imped" "build_database_CXX_Debug.json" CXX_AND_DEBUG) check_build_database("exp-builddb-imped" "build_database_Debug.json" JUST_DEBUG) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) check_build_database("exp-builddb-imped" "build_database_CXX_Release.json" CXX_AND_RELEASE) check_build_database("exp-builddb-imped" "build_database_Release.json" NO_EXIST) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) else () check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/CXX_build_database.json" JUST_TARGET) endif () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/Debug-check.cmake b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/Debug-check.cmake index 7a7598e..0e86d72 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/Debug-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/Debug-check.cmake
@@ -8,12 +8,15 @@ check_build_database("exp-builddb-imped" "build_database_CXX_Debug.json" CXX_AND_DEBUG) check_build_database("exp-builddb-imped" "build_database_Debug.json" JUST_DEBUG) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) check_build_database("exp-builddb-imped" "build_database_CXX_Release.json" NO_EXIST) check_build_database("exp-builddb-imped" "build_database_Release.json" NO_EXIST) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" NO_EXIST) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Release/CXX_build_database.json" NO_EXIST) else () check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/CXX_build_database.json" JUST_TARGET) endif () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/Release-check.cmake b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/Release-check.cmake index 1ea6a23..0e9958f 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/Release-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/imp-mods-exp-builddb-target-cmake_build_database/Release-check.cmake
@@ -8,12 +8,15 @@ check_build_database("exp-builddb-imped" "build_database_CXX_Debug.json" CXX_AND_DEBUG) check_build_database("exp-builddb-imped" "build_database_Debug.json" JUST_DEBUG) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) check_build_database("exp-builddb-imped" "build_database_CXX_Release.json" CXX_AND_RELEASE) check_build_database("exp-builddb-imped" "build_database_Release.json" JUST_RELEASE) check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) else () check_build_database("exp-builddb-imped" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) + check_build_database("exp-builddb-imped-matching" "CMakeFiles/use_import_interfaces_matching.dir/CXX_build_database.json" JUST_TARGET) endif () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-mods/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-mods/CMakeLists.txt index a12db0a..16a6297 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-mods/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-mods/CMakeLists.txt
@@ -19,6 +19,8 @@ set(package_name "export_transitive_modules") elseif (WITH_HEADERS) set(package_name "export_with_headers") +elseif (CONFIG_MODULES) + set(package_name "export_interface_config") elseif (BUILD_DATABASE) include("${CMAKE_SOURCE_DIR}/../export-build-database-setup.cmake") set(package_name "export_build_database") @@ -56,3 +58,23 @@ endif () add_test(NAME use_import_interfaces COMMAND use_import_interfaces) + +if (BUILD_DATABASE) + add_executable(use_import_interfaces_matching) + target_sources(use_import_interfaces_matching + PRIVATE + use.cxx) + target_compile_features(use_import_interfaces_matching PRIVATE cxx_std_20) + target_compile_options(use_import_interfaces_matching PRIVATE + -Dfrom_compile_options + -Dtarget_private_option + -Dtarget_public_option) + target_link_libraries(use_import_interfaces_matching PRIVATE "${target_name}") + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND + CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + target_link_libraries(use_import_interfaces_matching PRIVATE libcmt) + endif () + + add_test(NAME use_import_interfaces_matching COMMAND use_import_interfaces_matching) +endif ()
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build/CMakeLists.txt index 7e1f998..361fc44 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build/CMakeLists.txt
@@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") cmake_minimum_required(VERSION 3.29) project(cxx_modules_import_std_export_no_std CXX)
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build/test/CMakeLists.txt index e37f174..7483b3f 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build/test/CMakeLists.txt
@@ -39,7 +39,7 @@ # Strip quotes. string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") -if ("__CMAKE::CXX23" IN_LIST usage_dependent_targets) +if ("@cmake_cxx_std" IN_LIST usage_dependent_targets) message(SEND_ERROR - "The main export requires the '__CMAKE::CXX23' target") + "The main export requires the '@cmake_cxx_std' target") endif ()
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install/CMakeLists.txt index b63c814..c249276 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install/CMakeLists.txt
@@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") cmake_minimum_required(VERSION 3.29) project(cxx_modules_import_std_export_no_std CXX)
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install/test/CMakeLists.txt index e37f174..7483b3f 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install/test/CMakeLists.txt
@@ -39,7 +39,7 @@ # Strip quotes. string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") -if ("__CMAKE::CXX23" IN_LIST usage_dependent_targets) +if ("@cmake_cxx_std" IN_LIST usage_dependent_targets) message(SEND_ERROR - "The main export requires the '__CMAKE::CXX23' target") + "The main export requires the '@cmake_cxx_std' target") endif ()
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop/CMakeLists.txt index 12dc294..982c7d6 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop/CMakeLists.txt
@@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") cmake_minimum_required(VERSION 3.29) project(cxx_modules_import_std_no_std_property CXX)
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build/CMakeLists.txt index 2298cf6..4f77dca 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build/CMakeLists.txt
@@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") cmake_minimum_required(VERSION 3.29) project(cxx_modules_import_std_not_in_export CXX)
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build/test/CMakeLists.txt index 4e994d2..a3cf78f 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build/test/CMakeLists.txt
@@ -39,7 +39,7 @@ # Strip quotes. string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") -if ("__CMAKE::CXX23" IN_LIST usage_dependent_targets) +if ("@cmake_cxx_std" IN_LIST usage_dependent_targets) message(SEND_ERROR - "The main export requires the '__CMAKE::CXX23' target") + "The main export requires the '@cmake_cxx_std' target") endif ()
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install/CMakeLists.txt index b0587c9..0e2e181 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install/CMakeLists.txt
@@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") cmake_minimum_required(VERSION 3.29) project(cxx_modules_import_std_not_in_export CXX)
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install/test/CMakeLists.txt index 4e994d2..a3cf78f 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install/test/CMakeLists.txt
@@ -39,7 +39,7 @@ # Strip quotes. string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") -if ("__CMAKE::CXX23" IN_LIST usage_dependent_targets) +if ("@cmake_cxx_std" IN_LIST usage_dependent_targets) message(SEND_ERROR - "The main export requires the '__CMAKE::CXX23' target") + "The main export requires the '@cmake_cxx_std' target") endif ()
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-build-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-build-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-build-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-build-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-install-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-install-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-install-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-install-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-build-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-build-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-build-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-build-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-install-stderr.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-install-stderr.txt index f7d2858..5b4728e 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-install-stderr.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-install-stderr.txt
@@ -1,8 +1,9 @@ -CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): +CMake Warning \(experimental\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\) .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\) CMakeLists\.txt:[0-9]* \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std-trans/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std-trans/CMakeLists.txt index aa37dcc..8c6dccb 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std-trans/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std-trans/CMakeLists.txt
@@ -1,14 +1,8 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") cmake_minimum_required(VERSION 3.29) -if (EXPORT_NO_STD) - # Block making C++ `import std` targets. - add_library(__CMAKE::CXX23 IMPORTED INTERFACE) - add_library(__CMAKE::CXX26 IMPORTED INTERFACE) -endif () - project(cxx_modules_import_std_transitive CXX) include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModulesCompile/imp-std/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/imp-std/CMakeLists.txt index 5488596..3a81e06 100644 --- a/Tests/RunCMake/CXXModulesCompile/imp-std/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/imp-std/CMakeLists.txt
@@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") cmake_minimum_required(VERSION 3.29) project(cxx_modules_import_std CXX)
diff --git a/Tests/RunCMake/CXXModulesCompile/library/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/library/CMakeLists.txt index d611029..ce05a4a 100644 --- a/Tests/RunCMake/CXXModulesCompile/library/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/library/CMakeLists.txt
@@ -23,6 +23,13 @@ importable.cxx) target_compile_features(library PUBLIC cxx_std_20) +if(WITH_FILE_SET_SOURCES) + target_sources(library + PUBLIC + FILE_SET SOURCES + FILES src.cpp) +endif() + add_executable(exe) target_link_libraries(exe PRIVATE library) target_sources(exe
diff --git a/Tests/RunCMake/CXXModulesCompile/library/src.cpp b/Tests/RunCMake/CXXModulesCompile/library/src.cpp new file mode 100644 index 0000000..16f5d59 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/library/src.cpp
@@ -0,0 +1,3 @@ +void src() +{ +}
diff --git a/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility-build-check.cmake b/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility-build-check.cmake index a53b3ca..8728c7a 100644 --- a/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility-build-check.cmake +++ b/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility-build-check.cmake
@@ -1,7 +1,8 @@ -# Verify the build system generated a synthetic target/BMI for each unique importer -set(expected_consumers consumer20 consumer23) -set(linked_dir_keys "") -set(linked_dir_names "") +# Verify the build system reused the owning target BMI when compatible, +# generated a shared synthetic target BMI for the incompatible importers, +# and ignored preprocessor-only compile options in the BMI compatibility hash. +set(expected_consumers consumer20 consumer23 consumer23flag) +set(shared_synth_target_name "") if (DEFINED RunCMake_TEST_CONFIG) set(config_dir "${RunCMake_TEST_CONFIG}") @@ -31,9 +32,24 @@ string(JSON linked_dirs_len LENGTH "${depend_info_json}" "linked-target-dirs") string(JSON linked_dirs GET "${depend_info_json}" "linked-target-dirs") + if (consumer STREQUAL "consumer20") + set(expected_linked_tgt_regex "^importable\.dir$") + set(expected_linked_tgt_desc "owning target dir for 'importable'") + set(expected_linked_tgt_has_bmi 0) + elseif (consumer STREQUAL "consumer23" OR + consumer STREQUAL "consumer23flag") + set(expected_linked_tgt_regex "^importable@synth_[A-Za-z0-9_]+\.dir$") + set(expected_linked_tgt_desc "shared synthetic target dir for 'importable'") + set(expected_linked_tgt_has_bmi 1) + else () + list(APPEND RunCMake_TEST_FAILED + "No linked target expectation defined for consumer '${consumer}'") + continue() + endif () + if (NOT linked_dirs_len GREATER 0) list(APPEND RunCMake_TEST_FAILED - "Consumer '${consumer}' has no linked-target-dirs but expected synthetic target for 'importable'") + "Consumer '${consumer}' has no linked-target-dirs but expected ${expected_linked_tgt_desc}") continue() endif () @@ -54,10 +70,9 @@ cmake_path(GET linked_tgt_root FILENAME linked_tgt_name) - # Verify it is a synthetic target for the 'importable' library - if (NOT linked_tgt_name MATCHES "^importable@synth_[A-Za-z0-9_]+\.dir$") + if (NOT linked_tgt_name MATCHES "${expected_linked_tgt_regex}") list(APPEND RunCMake_TEST_FAILED - "Consumer '${consumer}' should link to synthetic target dir for 'importable' but found ${linked_dir}") + "Consumer '${consumer}' should link to ${expected_linked_tgt_desc} but found ${linked_dir}") continue() endif () @@ -67,45 +82,31 @@ set(linked_output_dir "${linked_dir}") endif () - # Verify the synthetic target dir exists and contains a BMI + # Verify the linked target dir exists and contains a BMI if (NOT EXISTS "${linked_dir}") list(APPEND RunCMake_TEST_FAILED - "Consumer '${consumer}' links to synthetic target directory that does not exist: ${linked_dir}") + "Consumer '${consumer}' links to target directory that does not exist: ${linked_dir}") continue() endif () - file(GLOB_RECURSE bmi_files "${linked_dir}/*.bmi") - if (NOT bmi_files) - list(APPEND RunCMake_TEST_FAILED - "No BMI files found in synthetic target directory: ${linked_dir}") - continue() + if (expected_linked_tgt_has_bmi) + file(GLOB_RECURSE bmi_files "${linked_dir}/*.bmi") + if (NOT bmi_files) + list(APPEND RunCMake_TEST_FAILED + "No BMI files found in target directory: ${linked_dir}") + continue() + endif () endif () - # Record the consumers of each linked dir to verify uniqueness - string(REGEX REPLACE "[^A-Za-z0-9_]" "_" linked_dir_key "${linked_tgt_name}") - set(linked_dir_consumers_var "linked_dir_consumers_${linked_dir_key}") - if (NOT DEFINED ${linked_dir_consumers_var}) - list(APPEND linked_dir_keys "${linked_dir_key}") - list(APPEND linked_dir_names "${linked_tgt_name}") + if (consumer STREQUAL "consumer23" OR consumer STREQUAL "consumer23flag") + if (shared_synth_target_name STREQUAL "") + set(shared_synth_target_name "${linked_tgt_name}") + elseif (NOT linked_tgt_name STREQUAL shared_synth_target_name) + list(APPEND RunCMake_TEST_FAILED + "Expected consumer23 and consumer23flag to share one synthetic target dir, but found '${shared_synth_target_name}' and '${linked_tgt_name}'") + endif () endif () - list(APPEND ${linked_dir_consumers_var} ${consumer}) - -endforeach () - -# Verify each synthetic target is consumed exactly once -foreach (linked_dir_key IN LISTS linked_dir_keys) - set(consumers "${linked_dir_consumers_${linked_dir_key}}") - list(LENGTH consumers linked_dir_consumer_count) - - if (linked_dir_consumer_count GREATER 1) - list(FIND linked_dir_keys ${linked_dir_key} linked_dir_index) - list(GET linked_dir_names ${linked_dir_index} linked_tgt_name) - - string(JOIN ", " linked_dir_consumers_joined ${consumers}) - list(APPEND RunCMake_TEST_FAILED - "Expected per-importer BMI generation, but '${linked_tgt_name}' is linked to by multiple targets: ${linked_dir_consumers_joined}") - endif () endforeach () string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility/CMakeLists.txt index 4af50c8..794c0de 100644 --- a/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility/CMakeLists.txt
@@ -8,6 +8,7 @@ PUBLIC FILE_SET CXX_MODULES FILES importable.cxx) target_compile_features(importable PUBLIC cxx_std_20) +target_compile_options(importable PRIVATE "-DIMPORTABLE_PRIVATE_DEFINE=1") add_executable(consumer20 consumer20.cxx) target_compile_features(consumer20 PRIVATE cxx_std_20) @@ -17,5 +18,11 @@ target_compile_features(consumer23 PRIVATE cxx_std_23) target_link_libraries(consumer23 PRIVATE importable) +add_executable(consumer23flag consumer23.cxx) +target_compile_features(consumer23flag PRIVATE cxx_std_23) +target_compile_options(consumer23flag PRIVATE "-DCONSUMER23FLAG_ONLY=1") +target_link_libraries(consumer23flag PRIVATE importable) + add_test(NAME consumer20 COMMAND consumer20) add_test(NAME consumer23 COMMAND consumer23) +add_test(NAME consumer23flag COMMAND consumer23flag)
diff --git a/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility/importable.cxx b/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility/importable.cxx index 607680a..f2a7908 100644 --- a/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility/importable.cxx +++ b/Tests/RunCMake/CXXModulesCompile/mixed-bmi-compatibility/importable.cxx
@@ -1,5 +1,9 @@ export module importable; +#ifndef IMPORTABLE_PRIVATE_DEFINE +# error "missing private compile option required for BMI generation" +#endif + export int from_import() { return 0;
diff --git a/Tests/RunCMake/CXXModulesCompile/multi-config-synth/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/multi-config-synth/CMakeLists.txt new file mode 100644 index 0000000..fb343a0 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/multi-config-synth/CMakeLists.txt
@@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.24...3.28) +project(cxx_modules_multi_config_synth CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(importable) +target_sources(importable + PUBLIC FILE_SET CXX_MODULES FILES importable.cxx) +target_compile_features(importable PUBLIC cxx_std_20) + +export(TARGETS importable + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/importable-targets.cmake" + CXX_MODULES_DIRECTORY "importable-cxx-modules" + ) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/importable-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/importable-targets.cmake\")\n" + ) + +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 multi_config_synth_consumer + COMMAND + "${CMAKE_COMMAND}" + "-DCMAKE_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/consumer" + -B "${CMAKE_CURRENT_BINARY_DIR}/consumer" + ) +add_test(NAME multi_config_synth_build_debug + COMMAND + "${CMAKE_COMMAND}" --build "${CMAKE_CURRENT_BINARY_DIR}/consumer" --config Debug + ) +add_test(NAME multi_config_synth_build_release + COMMAND + "${CMAKE_COMMAND}" --build "${CMAKE_CURRENT_BINARY_DIR}/consumer" --config Release + ) +set_tests_properties( + multi_config_synth_build_debug + multi_config_synth_build_release + PROPERTIES DEPENDS multi_config_synth_consumer) +add_test(NAME multi_config_synth_run + COMMAND "${CMAKE_CTEST_COMMAND}" -C Debug --test-dir "${CMAKE_CURRENT_BINARY_DIR}/consumer" + ) +set_tests_properties(multi_config_synth_run + PROPERTIES DEPENDS multi_config_synth_build_debug)
diff --git a/Tests/RunCMake/CXXModulesCompile/multi-config-synth/consumer/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/multi-config-synth/consumer/CMakeLists.txt new file mode 100644 index 0000000..141b739 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/multi-config-synth/consumer/CMakeLists.txt
@@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.24...3.28) +project(cxx_modules_multi_config_consumer CXX) + +find_package(importable REQUIRED) + +add_executable(consumer main.cxx) +target_link_libraries(consumer PRIVATE CXXModules::importable) +target_compile_features(consumer PRIVATE cxx_std_20) + +add_test(NAME consumer COMMAND consumer)
diff --git a/Tests/RunCMake/CXXModulesCompile/multi-config-synth/consumer/main.cxx b/Tests/RunCMake/CXXModulesCompile/multi-config-synth/consumer/main.cxx new file mode 100644 index 0000000..5ff7dc2 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/multi-config-synth/consumer/main.cxx
@@ -0,0 +1,5 @@ +import importable; +int main() +{ + return from_import(); +}
diff --git a/Tests/RunCMake/CXXModulesCompile/multi-config-synth/importable.cxx b/Tests/RunCMake/CXXModulesCompile/multi-config-synth/importable.cxx new file mode 100644 index 0000000..ad83965 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/multi-config-synth/importable.cxx
@@ -0,0 +1,5 @@ +export module importable; +export int from_import() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/ninja-cmp0154/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/ninja-cmp0154/CMakeLists.txt index 1aa36c1..07dc327 100644 --- a/Tests/RunCMake/CXXModulesCompile/ninja-cmp0154/CMakeLists.txt +++ b/Tests/RunCMake/CXXModulesCompile/ninja-cmp0154/CMakeLists.txt
@@ -27,5 +27,7 @@ set_property(SOURCE unrelated.cxx PROPERTY CXX_SCAN_FOR_MODULES 0) +# ensure property INDEPENDENT_FILES=OFF is ignored +set_property(FILE_SET CXX_MODULES TARGET ninja_cmp0154 PROPERTY INDEPENDENT_FILES OFF) add_test(NAME ninja_cmp0154 COMMAND ninja_cmp0154)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules-build-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CXXModulesCompile/private-cxx-modules-build-result.txt
diff --git a/Tests/RunCMake/CXXModulesCompile/private-cxx-modules-build-stdout.txt b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules-build-stdout.txt new file mode 100644 index 0000000..14cd00f --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules-build-stdout.txt
@@ -0,0 +1 @@ +CMake Error: Unable to use module 'importable' as it is 'PRIVATE' and therefore not accessible outside of its owning target\.
diff --git a/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/CMakeLists.txt b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/CMakeLists.txt new file mode 100644 index 0000000..9a4bab4 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/CMakeLists.txt
@@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.24...3.28) +project(cxx_modules_library CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +include(GenerateExportHeader) + +add_library(library) +generate_export_header(library) +target_sources(library + PUBLIC + FILE_SET HEADERS + BASE_DIRS + "${CMAKE_CURRENT_BINARY_DIR}" + FILES + "${CMAKE_CURRENT_BINARY_DIR}/library_export.h" + PRIVATE + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.ixx + PRIVATE + importable.cxx) +target_compile_features(library PUBLIC cxx_std_20) + +add_executable(exe) +target_link_libraries(exe PRIVATE library) +target_sources(exe + PRIVATE + main.cxx)
diff --git a/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/importable.cxx b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/importable.cxx new file mode 100644 index 0000000..859fa3d --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/importable.cxx
@@ -0,0 +1,6 @@ +module importable; + +int from_import() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/importable.ixx b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/importable.ixx new file mode 100644 index 0000000..d3fb7f6 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/importable.ixx
@@ -0,0 +1,5 @@ +export module importable; + +#include "library_export.h" + +export LIBRARY_EXPORT int from_import();
diff --git a/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/main.cxx b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/main.cxx new file mode 100644 index 0000000..feb38d2 --- /dev/null +++ b/Tests/RunCMake/CXXModulesCompile/private-cxx-modules/main.cxx
@@ -0,0 +1,6 @@ +import importable; + +int main(int argc, char* argv[]) +{ + return from_import(); +}
diff --git a/Tests/RunCMake/CacheNewline/CacheNewline-check.cmake b/Tests/RunCMake/CacheNewline/CacheNewline-check.cmake deleted file mode 100644 index 6534f63..0000000 --- a/Tests/RunCMake/CacheNewline/CacheNewline-check.cmake +++ /dev/null
@@ -1,16 +0,0 @@ -set(CACHE_EXPECTED_FILE "${RunCMake_TEST_SOURCE_DIR}/cache-regex.txt") -set(CACHE_ACTUAL_FILE "${RunCMake_BINARY_DIR}/CacheNewline-build/CMakeCache.txt") - -file(READ ${CACHE_EXPECTED_FILE} CACHE_EXPECTED) -string(REGEX REPLACE "\r\n" "\n" CACHE_EXPECTED "${CACHE_EXPECTED}") -string(REGEX REPLACE "\n+$" "" CACHE_EXPECTED "${CACHE_EXPECTED}") -file(READ ${CACHE_ACTUAL_FILE} CACHE_ACTUAL) -string(REGEX REPLACE "\r\n" "\n" CACHE_ACTUAL "${CACHE_ACTUAL}") -string(REGEX REPLACE "\n+$" "" CACHE_ACTUAL "${CACHE_ACTUAL}") - -if(NOT "${CACHE_ACTUAL}" MATCHES "${CACHE_EXPECTED}") - set(RunCMake_TEST_FAILED "${CACHE_ACTUAL_FILE} does not match ${CACHE_EXPECTED_FILE}: - -CMakeCache.txt contents = [\n${CACHE_ACTUAL}\n] -Expected = [\n${CACHE_EXPECTED}\n]") -endif()
diff --git a/Tests/RunCMake/CacheNewline/cache-regex.txt b/Tests/RunCMake/CacheNewline/CacheNewlines-CMakeCacheRegex.txt similarity index 100% rename from Tests/RunCMake/CacheNewline/cache-regex.txt rename to Tests/RunCMake/CacheNewline/CacheNewlines-CMakeCacheRegex.txt
diff --git a/Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt b/Tests/RunCMake/CacheNewline/CacheNewlines-stderr.txt similarity index 100% copy from Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt copy to Tests/RunCMake/CacheNewline/CacheNewlines-stderr.txt
diff --git a/Tests/RunCMake/CacheNewline/CacheNewline.cmake b/Tests/RunCMake/CacheNewline/CacheNewlines.cmake similarity index 100% rename from Tests/RunCMake/CacheNewline/CacheNewline.cmake rename to Tests/RunCMake/CacheNewline/CacheNewlines.cmake
diff --git a/Tests/RunCMake/CacheNewline/CacheSingleNewline-CMakeCacheRegex.txt b/Tests/RunCMake/CacheNewline/CacheSingleNewline-CMakeCacheRegex.txt new file mode 100644 index 0000000..92bd7b8 --- /dev/null +++ b/Tests/RunCMake/CacheNewline/CacheSingleNewline-CMakeCacheRegex.txt
@@ -0,0 +1,5 @@ +//Offending entry +NEWLINE_VARIABLE:STRING= +# WARNING: Value of NEWLINE_VARIABLE contained a newline and was +# truncated\. Original value: +# \\n
diff --git a/Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt b/Tests/RunCMake/CacheNewline/CacheSingleNewline-stderr.txt similarity index 100% rename from Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt rename to Tests/RunCMake/CacheNewline/CacheSingleNewline-stderr.txt
diff --git a/Tests/RunCMake/CacheNewline/CacheSingleNewline.cmake b/Tests/RunCMake/CacheNewline/CacheSingleNewline.cmake new file mode 100644 index 0000000..d4b2d6e --- /dev/null +++ b/Tests/RunCMake/CacheNewline/CacheSingleNewline.cmake
@@ -0,0 +1 @@ +set(NEWLINE_VARIABLE "\n" CACHE STRING "Offending entry")
diff --git a/Tests/RunCMake/CacheNewline/CacheStartsWithNewline-CMakeCacheRegex.txt b/Tests/RunCMake/CacheNewline/CacheStartsWithNewline-CMakeCacheRegex.txt new file mode 100644 index 0000000..9b4fe32 --- /dev/null +++ b/Tests/RunCMake/CacheNewline/CacheStartsWithNewline-CMakeCacheRegex.txt
@@ -0,0 +1,6 @@ +//Offending entry +NEWLINE_VARIABLE:STRING= +# WARNING: Value of NEWLINE_VARIABLE contained a newline and was +# truncated\. Original value: +# \\na +# \\nb
diff --git a/Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt b/Tests/RunCMake/CacheNewline/CacheStartsWithNewline-stderr.txt similarity index 100% copy from Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt copy to Tests/RunCMake/CacheNewline/CacheStartsWithNewline-stderr.txt
diff --git a/Tests/RunCMake/CacheNewline/CacheStartsWithNewline.cmake b/Tests/RunCMake/CacheNewline/CacheStartsWithNewline.cmake new file mode 100644 index 0000000..4b90ffa --- /dev/null +++ b/Tests/RunCMake/CacheNewline/CacheStartsWithNewline.cmake
@@ -0,0 +1 @@ +set(NEWLINE_VARIABLE "\na\nb" CACHE STRING "Offending entry")
diff --git a/Tests/RunCMake/Configure/CacheVarHelpString.cmake b/Tests/RunCMake/CacheNewline/CacheVarHelpString.cmake similarity index 100% rename from Tests/RunCMake/Configure/CacheVarHelpString.cmake rename to Tests/RunCMake/CacheNewline/CacheVarHelpString.cmake
diff --git a/Tests/RunCMake/CacheNewline/CheckCache.cmake b/Tests/RunCMake/CacheNewline/CheckCache.cmake new file mode 100644 index 0000000..96024b4 --- /dev/null +++ b/Tests/RunCMake/CacheNewline/CheckCache.cmake
@@ -0,0 +1,20 @@ +set(CACHE_EXPECTED_FILE "${RunCMake_TEST_SOURCE_DIR}/${case}-CMakeCacheRegex.txt") +set(CACHE_ACTUAL_FILE "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") + +if(NOT EXISTS ${CACHE_EXPECTED_FILE}) + message(FATAL_ERROR "Could not find ${RunCMake_TEST_SOURCE_DIR}/${case}-CMakeCacheRegex.txt") +endif() + +file(READ ${CACHE_EXPECTED_FILE} CACHE_EXPECTED) +string(REGEX REPLACE "\r\n" "\n" CACHE_EXPECTED "${CACHE_EXPECTED}") +string(REGEX REPLACE "\n+$" "" CACHE_EXPECTED "${CACHE_EXPECTED}") +file(READ ${CACHE_ACTUAL_FILE} CACHE_ACTUAL) +string(REGEX REPLACE "\r\n" "\n" CACHE_ACTUAL "${CACHE_ACTUAL}") +string(REGEX REPLACE "\n+$" "" CACHE_ACTUAL "${CACHE_ACTUAL}") + +if(NOT "${CACHE_ACTUAL}" MATCHES "${CACHE_EXPECTED}") + set(RunCMake_TEST_FAILED "${CACHE_ACTUAL_FILE} does not match ${CACHE_EXPECTED_FILE}: + +CMakeCache.txt contents = [\n${CACHE_ACTUAL}\n] +Expected = [\n${CACHE_EXPECTED}\n]") +endif()
diff --git a/Tests/RunCMake/CacheNewline/RunCMakeTest.cmake b/Tests/RunCMake/CacheNewline/RunCMakeTest.cmake index 5e3d2d4..f907c1b 100644 --- a/Tests/RunCMake/CacheNewline/RunCMakeTest.cmake +++ b/Tests/RunCMake/CacheNewline/RunCMakeTest.cmake
@@ -1,3 +1,22 @@ include(RunCMake) -run_cmake(CacheNewline) +function(run_cmake_cache_newline_test case) + set(RunCMake-check-file CheckCache.cmake) + run_cmake(${case}) + + # Sanity check that the CMakeCache.txt is valid by trying to list variables. + unset(RunCMake-check-file) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) + run_cmake_command(${case}-List ${CMAKE_COMMAND} -L ${RunCMake_TEST_BINARY_DIR}) +endfunction() + +run_cmake_cache_newline_test(CacheNewlines) +run_cmake_cache_newline_test(CacheStartsWithNewline) +run_cmake_cache_newline_test(CacheSingleNewline) + +block() + run_cmake(CacheVarHelpString) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake(CacheVarHelpString) +endblock()
diff --git a/Tests/RunCMake/CacheVarHelpCoverage/RunCMakeTest.cmake b/Tests/RunCMake/CacheVarHelpCoverage/RunCMakeTest.cmake new file mode 100644 index 0000000..3466b3f --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpCoverage/RunCMakeTest.cmake
@@ -0,0 +1,134 @@ +# Built-in cache documentation structural-parity lint. +# +# The table ``Source/cmCacheDocumentationTable.cxx`` is hand-maintained; +# its set of entries is the curated list of CMake cache variables for +# which ``cmake-gui`` and ``ccmake`` surface a built-in tooltip when the +# on-disk ``HELPSTRING`` is empty (the common case for variables a user +# sets via ``-D`` without an accompanying docstring). +# +# The sibling table ``Source/cmCachePatternTable.cxx`` carries the +# placeholder-shaped fallbacks (``CMAKE_<LANG>_FLAGS``, +# ``CMAKE_POLICY_DEFAULT_CMP<NNNN>``, ``<PROJECT-NAME>_VERSION``, ...). +# Patterns store their placeholder names with angle brackets; the +# corresponding ``Help/variable/<NAME>.rst`` filenames carry the same +# placeholder tokens with the brackets stripped. +# +# Drift between an entry's wording and its source ``Help/variable/*.rst`` +# manual is handled by reviewer discipline : when an ``.rst`` is reworded, +# the matching table entry is reviewed in the same change. This test +# therefore enforces only the one piece of drift that a human reviewer +# would not naturally catch: that every entry NAME in both tables still +# resolves to an existing ``Help/variable/<NAME>.rst`` file (so the +# tooltip is never silently divorced from a real manual via a typo, a +# rename, or a deletion). + +if(NOT DEFINED CMake_SOURCE_DIR OR NOT IS_DIRECTORY "${CMake_SOURCE_DIR}") + message(FATAL_ERROR + "CacheVarHelpCoverage: CMake_SOURCE_DIR was not provided.") +endif() + +set(_help_dir "${CMake_SOURCE_DIR}/Help/variable") +set(_table_file "${CMake_SOURCE_DIR}/Source/cmCacheDocumentationTable.cxx") +set(_pattern_file "${CMake_SOURCE_DIR}/Source/cmCachePatternTable.cxx") + +if(NOT IS_DIRECTORY "${_help_dir}") + message(FATAL_ERROR + "CacheVarHelpCoverage: ${_help_dir} does not exist.") +endif() +if(NOT EXISTS "${_table_file}") + message(FATAL_ERROR + "CacheVarHelpCoverage: ${_table_file} does not exist.") +endif() +if(NOT EXISTS "${_pattern_file}") + message(FATAL_ERROR + "CacheVarHelpCoverage: ${_pattern_file} does not exist.") +endif() + +# Extract entry names from the C++ exact-match table. Each entry begins +# with the fixed pattern ``\n { "NAME",``; see the header comment in +# cmCacheDocumentationTable.cxx for the layout convention. +file(READ "${_table_file}" _table_text) +string(REGEX MATCHALL "\n \\{ \"[^\"]+\"" _hits "${_table_text}") +set(_table_names) +foreach(_hit IN LISTS _hits) + string(REGEX REPLACE "^\n \\{ \"" "" _name "${_hit}") + string(REGEX REPLACE "\"$" "" _name "${_name}") + list(APPEND _table_names "${_name}") +endforeach() +list(SORT _table_names) + +# Extract entry patterns from the C++ pattern table. Same row shape as +# above; the pattern strings carry angle-bracketed placeholders like +# ``CMAKE_<LANG>_FLAGS`` or ``<PROJECT-NAME>_VERSION``. Strip the +# angle brackets to derive the matching ``.rst`` basename +# (``CMAKE_LANG_FLAGS.rst`` etc.). +file(READ "${_pattern_file}" _pattern_text) +string(REGEX MATCHALL "\n \\{ \"[^\"]+\"" _phits "${_pattern_text}") +set(_pattern_names) +foreach(_hit IN LISTS _phits) + string(REGEX REPLACE "^\n \\{ \"" "" _name "${_hit}") + string(REGEX REPLACE "\"$" "" _name "${_name}") + list(APPEND _pattern_names "${_name}") +endforeach() +list(SORT _pattern_names) + +# Table entries whose NAME does not match an existing +# Help/variable/<NAME>.rst manual. +set(_missing_exact) +foreach(_name IN LISTS _table_names) + if(NOT EXISTS "${_help_dir}/${_name}.rst") + list(APPEND _missing_exact "${_name}") + endif() +endforeach() + +# Pattern entries whose NAME (with ``<`` and ``>`` stripped) does not +# match an existing Help/variable/<NAME>.rst manual. +set(_missing_pattern) +foreach(_name IN LISTS _pattern_names) + string(REPLACE "<" "" _basename "${_name}") + string(REPLACE ">" "" _basename "${_basename}") + if(NOT EXISTS "${_help_dir}/${_basename}.rst") + list(APPEND _missing_pattern "${_name}") + endif() +endforeach() + +if(_missing_exact OR _missing_pattern) + set(_msg "Built-in cache documentation coverage check failed.") + if(_missing_exact) + list(LENGTH _missing_exact _n) + string(APPEND _msg + "\n\n${_n} entry/entries in Source/cmCacheDocumentationTable.cxx" + "\nhave no matching Help/variable/<NAME>.rst manual:") + foreach(_name IN LISTS _missing_exact) + string(APPEND _msg "\n ${_name}") + endforeach() + endif() + if(_missing_pattern) + list(LENGTH _missing_pattern _n) + string(APPEND _msg + "\n\n${_n} entry/entries in Source/cmCachePatternTable.cxx" + "\nhave no matching Help/variable/<NAME>.rst manual (the " + "placeholder tokens carry through verbatim with ``<``/``>`` " + "stripped):") + foreach(_name IN LISTS _missing_pattern) + string(REPLACE "<" "" _basename "${_name}") + string(REPLACE ">" "" _basename "${_basename}") + string(APPEND _msg "\n ${_name} -> ${_basename}.rst") + endforeach() + endif() + string(APPEND _msg + "\n\nLikely causes and remedies:" + "\n * The entry NAME / Pattern is misspelled -- fix the spelling." + "\n * Help/variable/<NAME>.rst was renamed -- update the entry to" + "\n match the new filename." + "\n * Help/variable/<NAME>.rst was deleted intentionally -- remove" + "\n the corresponding entry from the table.") + message(FATAL_ERROR "${_msg}") +endif() + +list(LENGTH _table_names _n_tab) +list(LENGTH _pattern_names _n_pat) +message(STATUS + "Built-in cache documentation coverage OK: " + "${_n_tab} exact entry/entries + ${_n_pat} pattern entry/entries, " + "all matched to Help/variable/*.rst.")
diff --git a/Tests/RunCMake/CacheVarHelpFallback/CMakeLists.txt b/Tests/RunCMake/CacheVarHelpFallback/CMakeLists.txt new file mode 100644 index 0000000..bf2ef15 --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpFallback/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.10) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CacheVarHelpFallback/CheckCache.cmake b/Tests/RunCMake/CacheVarHelpFallback/CheckCache.cmake new file mode 100644 index 0000000..d06b8f6 --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpFallback/CheckCache.cmake
@@ -0,0 +1,136 @@ +# Inspects ${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt and verifies that +# ``cmake::ProcessCacheArg`` installed the expected HELPSTRING (from the +# built-in documentation table) and preserved the cache entry's type +# and value verbatim. +# +# Each entry's HELPSTRING is serialized as zero or more ``//``-prefixed +# lines immediately preceding the ``KEY:TYPE=VALUE`` line. + +set(_cache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(NOT EXISTS "${_cache_file}") + set(RunCMake_TEST_FAILED "Missing cache file: ${_cache_file}") + return() +endif() + +file(READ "${_cache_file}" _cache_text) +string(REGEX REPLACE "\r\n" "\n" _cache_text "${_cache_text}") +string(REPLACE ";" "\\;" _cache_text "${_cache_text}") +string(REPLACE "\n" ";" _cache_lines "${_cache_text}") + +# Collect the contiguous run of ``//`` comment lines that immediately +# precedes ``${entry_key}:`` and join their bodies (everything after the +# leading ``//``) into a single string, one body per line. An entry +# preceded by no ``//`` lines yields the empty string. +function(_collect_help entry_key out_var) + set(_collected "") + set(_run "") + foreach(_line IN LISTS _cache_lines) + if(_line MATCHES "^//(.*)$") + string(APPEND _run "${CMAKE_MATCH_1}\n") + elseif(_line MATCHES "^${entry_key}:[A-Za-z]+=") + set(_collected "${_run}") + break() + else() + set(_run "") + endif() + endforeach() + set(${out_var} "${_collected}" PARENT_SCOPE) +endfunction() + +# Find the ``${entry_key}:TYPE=VALUE`` line itself (the full type+value +# record, without the leading ``//`` help block). Sets ``out_var`` to +# the empty string if the entry is absent. +function(_find_entry_line entry_key out_var) + set(_found "") + foreach(_line IN LISTS _cache_lines) + if(_line MATCHES "^${entry_key}:[A-Za-z]+=") + set(_found "${_line}") + break() + endif() + endforeach() + set(${out_var} "${_found}" PARENT_SCOPE) +endfunction() + +if(test STREQUAL "DefineSetsTypeAndHelp") + # Bare ``-D CMAKE_COMPILE_WARNING_AS_ERROR=ON`` -- ProcessCacheArg + # must install the documentation-table summary as HELPSTRING and + # leave the entry typed UNINITIALIZED (the user did not pin a type). + _find_entry_line("CMAKE_COMPILE_WARNING_AS_ERROR" _line) + _collect_help("CMAKE_COMPILE_WARNING_AS_ERROR" _help) + if(NOT _line MATCHES "^CMAKE_COMPILE_WARNING_AS_ERROR:UNINITIALIZED=ON$") + set(RunCMake_TEST_FAILED + "ProcessCacheArg unexpectedly altered the bare -D entry's type " + "or value.\nRecovered entry line:\n[${_line}]") + elseif(NOT _help MATCHES "treat warnings on compile as errors") + set(RunCMake_TEST_FAILED + "ProcessCacheArg did not install HELPSTRING from documentation " + "table.\nRecovered help block:\n[${_help}]") + endif() +elseif(test STREQUAL "DefineTypePreserved") + # ``-D CMAKE_COMPILE_WARNING_AS_ERROR:STRING=ON`` -- the user's + # explicit ``:STRING`` annotation is honored; HELPSTRING is still + # populated from the table because the user supplied none. + _find_entry_line("CMAKE_COMPILE_WARNING_AS_ERROR" _line) + _collect_help("CMAKE_COMPILE_WARNING_AS_ERROR" _help) + if(NOT _line MATCHES "^CMAKE_COMPILE_WARNING_AS_ERROR:STRING=ON$") + set(RunCMake_TEST_FAILED + "ProcessCacheArg overwrote user's explicit :STRING type pin.\n" + "Recovered entry line:\n[${_line}]") + elseif(NOT _help MATCHES "treat warnings on compile as errors") + set(RunCMake_TEST_FAILED + "ProcessCacheArg did not install HELPSTRING when user pinned a " + "type.\nRecovered help block:\n[${_help}]") + endif() +elseif(test STREQUAL "DefineUnknownVarUninitialized") + # ``-D MY_CUSTOM_VAR_XYZZY=x`` -- a project-specific name absent from + # the documentation table. Type stays UNINITIALIZED and HELPSTRING + # falls back to the legacy sentinel. + _find_entry_line("MY_CUSTOM_VAR_XYZZY" _line) + _collect_help("MY_CUSTOM_VAR_XYZZY" _help) + if(NOT _line MATCHES "^MY_CUSTOM_VAR_XYZZY:UNINITIALIZED=x$") + set(RunCMake_TEST_FAILED + "ProcessCacheArg altered the unknown-variable entry.\n" + "Recovered entry line:\n[${_line}]") + elseif(NOT _help MATCHES "No help, variable specified on the command line") + set(RunCMake_TEST_FAILED + "ProcessCacheArg did not preserve sentinel HELPSTRING for unknown " + "variable.\nRecovered help block:\n[${_help}]") + endif() +elseif(test STREQUAL "PatternCxxClangTidy") + # ``-D CMAKE_CXX_CLANG_TIDY=clang-tidy`` -- no exact-match entry in + # ``cmCacheDocumentationTable``; the pattern table's + # ``CMAKE_<LANG>_CLANG_TIDY`` entry matches. ProcessCacheArg installs + # the pattern's Summary as HELPSTRING; type stays UNINITIALIZED. + _find_entry_line("CMAKE_CXX_CLANG_TIDY" _line) + _collect_help("CMAKE_CXX_CLANG_TIDY" _help) + if(NOT _line MATCHES "^CMAKE_CXX_CLANG_TIDY:UNINITIALIZED=clang-tidy$") + set(RunCMake_TEST_FAILED + "ProcessCacheArg unexpectedly altered the pattern-matched entry's " + "type or value.\nRecovered entry line:\n[${_line}]") + elseif(NOT _help MATCHES "clang-tidy command") + set(RunCMake_TEST_FAILED + "Pattern-table lookup did not install HELPSTRING from the " + "CMAKE_<LANG>_CLANG_TIDY pattern.\n" + "Recovered help block:\n[${_help}]") + endif() +elseif(test STREQUAL "PatternHipCppcheck") + # ``-D CMAKE_HIP_CPPCHECK=anything`` -- the matcher is intentionally + # permissive about ``<LANG>`` even though the cppcheck pattern's + # Summary names a C/CXX restriction. Assert the value is preserved + # verbatim, the type stays UNINITIALIZED, and the Summary's + # restriction phrase appears in the HELPSTRING. + _find_entry_line("CMAKE_HIP_CPPCHECK" _line) + _collect_help("CMAKE_HIP_CPPCHECK" _help) + if(NOT _line MATCHES "^CMAKE_HIP_CPPCHECK:UNINITIALIZED=anything$") + set(RunCMake_TEST_FAILED + "ProcessCacheArg unexpectedly altered the pattern-matched entry's " + "type or value.\nRecovered entry line:\n[${_line}]") + elseif(NOT _help MATCHES "C or CXX") + set(RunCMake_TEST_FAILED + "Pattern-table lookup did not surface the C/CXX restriction in " + "the HELPSTRING for CMAKE_HIP_CPPCHECK.\n" + "Recovered help block:\n[${_help}]") + endif() +else() + set(RunCMake_TEST_FAILED "Unknown test case: ${test}") +endif()
diff --git a/Tests/RunCMake/CacheVarHelpFallback/DefineSetsTypeAndHelp.cmake b/Tests/RunCMake/CacheVarHelpFallback/DefineSetsTypeAndHelp.cmake new file mode 100644 index 0000000..3d2d122 --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpFallback/DefineSetsTypeAndHelp.cmake
@@ -0,0 +1,9 @@ +# Defines a built-in cache variable on the command line with no +# ``:TYPE=`` annotation. ``cmake::ProcessCacheArg`` must consult the +# built-in documentation table and install the table summary as the +# entry's HELPSTRING. Type and value are preserved verbatim, so the +# entry remains ``:UNINITIALIZED=`` (consistent with the project-side +# behavior, which would re-type later in ``cmMakefile::AddCacheDefinition`` +# only if a ``set(... CACHE BOOL ...)`` call appeared in a listfile). +# The project listfile itself does not ``set(...)`` the variable; the +# cache entry materializes solely through ``-D``.
diff --git a/Tests/RunCMake/CacheVarHelpFallback/DefineTypePreserved.cmake b/Tests/RunCMake/CacheVarHelpFallback/DefineTypePreserved.cmake new file mode 100644 index 0000000..ea2dfd6 --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpFallback/DefineTypePreserved.cmake
@@ -0,0 +1,5 @@ +# Defines a built-in cache variable on the command line with an explicit +# ``:STRING`` annotation. ``ProcessCacheArg`` honors the explicit type +# (the entry must be persisted as ``:STRING=``) and still installs the +# HELPSTRING from the documentation table because the user supplied +# none.
diff --git a/Tests/RunCMake/CacheVarHelpFallback/DefineUnknownVarUninitialized.cmake b/Tests/RunCMake/CacheVarHelpFallback/DefineUnknownVarUninitialized.cmake new file mode 100644 index 0000000..34144ea --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpFallback/DefineUnknownVarUninitialized.cmake
@@ -0,0 +1,4 @@ +# Defines a project-specific cache variable on the command line with no +# ``:TYPE=`` annotation. The documentation table cannot match this +# name, so the entry must remain ``:UNINITIALIZED=`` and receive the +# generic sentinel help string preserved for backwards compatibility.
diff --git a/Tests/RunCMake/CacheVarHelpFallback/PatternCxxClangTidy.cmake b/Tests/RunCMake/CacheVarHelpFallback/PatternCxxClangTidy.cmake new file mode 100644 index 0000000..95520a9 --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpFallback/PatternCxxClangTidy.cmake
@@ -0,0 +1,9 @@ +# Defines a placeholder-shaped built-in cache variable on the command +# line with no ``:TYPE=`` annotation. No exact-match entry exists for +# ``CMAKE_CXX_CLANG_TIDY`` in ``cmCacheDocumentationTable``; +# ``cmake::ProcessCacheArg`` therefore consults the pattern table +# (``cmCachePatternTable``), matches the ``CMAKE_<LANG>_CLANG_TIDY`` +# pattern, and installs the pattern's Summary as the entry's HELPSTRING. +# Type and value are preserved verbatim. The project listfile itself +# does not ``set(...)`` the variable; the cache entry materializes +# solely through ``-D``.
diff --git a/Tests/RunCMake/CacheVarHelpFallback/PatternHipCppcheck.cmake b/Tests/RunCMake/CacheVarHelpFallback/PatternHipCppcheck.cmake new file mode 100644 index 0000000..7bc4e87 --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpFallback/PatternHipCppcheck.cmake
@@ -0,0 +1,9 @@ +# Defines a placeholder-shaped built-in cache variable on the command +# line for a language combination the underlying ``.rst`` manual +# explicitly excludes (``CMAKE_<LANG>_CPPCHECK`` applies only when +# ``<LANG>`` is C or CXX; this test passes ``HIP``). The matcher is +# intentionally permissive: it accepts the name and returns the +# pattern's Summary, which is installed as the entry's HELPSTRING; +# the per-language restriction is documented in the Summary prose, +# not enforced by the matcher. This case validates the "permissive +# matching with restriction-in-prose" design end-to-end.
diff --git a/Tests/RunCMake/CacheVarHelpFallback/RunCMakeTest.cmake b/Tests/RunCMake/CacheVarHelpFallback/RunCMakeTest.cmake new file mode 100644 index 0000000..d2b99b8 --- /dev/null +++ b/Tests/RunCMake/CacheVarHelpFallback/RunCMakeTest.cmake
@@ -0,0 +1,49 @@ +include(RunCMake) + +# All cases below exercise ``cmake::ProcessCacheArg``: when a built-in +# cache variable is set via ``-D <var>=<value>`` with no ``:TYPE=`` and +# no help text, ``ProcessCacheArg`` consults the built-in documentation +# table (``cmCacheDocumentationTable`` and ``cmCachePatternTable``, +# seeded from ``Help/variable/*.rst`` and the per-language pattern +# manuals) and installs the table's Summary as the entry's HELPSTRING. +# The cache entry's type and value are left untouched. +function(run_define_case case) + set(RunCMake-check-file CheckCache.cmake) + run_cmake(${case}) +endfunction() + +# Bare ``-D <var>=<value>`` of a documented built-in: HELPSTRING is +# populated from the table's Summary; type stays UNINITIALIZED because +# the user did not pin one. +set(RunCMake_TEST_OPTIONS "-DCMAKE_COMPILE_WARNING_AS_ERROR=ON") +run_define_case(DefineSetsTypeAndHelp) +unset(RunCMake_TEST_OPTIONS) + +# ``-D <var>:STRING=<value>`` of a documented built-in: user's explicit +# type pin is honored; HELPSTRING is still populated from the table. +set(RunCMake_TEST_OPTIONS "-DCMAKE_COMPILE_WARNING_AS_ERROR:STRING=ON") +run_define_case(DefineTypePreserved) +unset(RunCMake_TEST_OPTIONS) + +# Bare ``-D <var>=<value>`` of a project-specific name: no table match, +# so the entry stays ``:UNINITIALIZED=`` with the legacy sentinel +# HELPSTRING preserved for backwards compatibility. +set(RunCMake_TEST_OPTIONS "-DMY_CUSTOM_VAR_XYZZY=x") +run_define_case(DefineUnknownVarUninitialized) +unset(RunCMake_TEST_OPTIONS) + +# A built-in variable name absent from the exact-match table but present +# in the pattern table (``CMAKE_<LANG>_CLANG_TIDY``): HELPSTRING comes +# from the pattern's Summary; type stays UNINITIALIZED. +set(RunCMake_TEST_OPTIONS "-DCMAKE_CXX_CLANG_TIDY=clang-tidy") +run_define_case(PatternCxxClangTidy) +unset(RunCMake_TEST_OPTIONS) + +# A built-in variable name matching a pattern whose manual documents a +# per-language restriction CMake does not enforce at match time +# (cppcheck applies only to C/CXX, but here we set HIP). The matcher +# is intentionally permissive; the restriction text travels in the +# Summary prose. +set(RunCMake_TEST_OPTIONS "-DCMAKE_HIP_CPPCHECK=anything") +run_define_case(PatternHipCppcheck) +unset(RunCMake_TEST_OPTIONS)
diff --git a/Tests/RunCMake/CheckIPOSupported/CMP0138-OLD-stderr.txt b/Tests/RunCMake/CheckIPOSupported/CMP0138-OLD-stderr.txt index 375ab1d..e14eee3 100644 --- a/Tests/RunCMake/CheckIPOSupported/CMP0138-OLD-stderr.txt +++ b/Tests/RunCMake/CheckIPOSupported/CMP0138-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0138-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0138-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0138 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CheckIPOSupported/cmp0069-is-old-stderr.txt b/Tests/RunCMake/CheckIPOSupported/cmp0069-is-old-stderr.txt index a61729f..67e244b 100644 --- a/Tests/RunCMake/CheckIPOSupported/cmp0069-is-old-stderr.txt +++ b/Tests/RunCMake/CheckIPOSupported/cmp0069-is-old-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at cmp0069-is-old\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at cmp0069-is-old\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0069 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(message\): Policy CMP0069 set to OLD
diff --git a/Tests/RunCMake/CheckModules/CMP0075-stderr.txt b/Tests/RunCMake/CheckModules/CMP0075-stderr.txt index 833ccaf..de84f4c 100644 --- a/Tests/RunCMake/CheckModules/CMP0075-stderr.txt +++ b/Tests/RunCMake/CheckModules/CMP0075-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/CheckIncludeFile\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/CheckIncludeFile\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0075 is not set: Include file check macros honor CMAKE_REQUIRED_LIBRARIES\. Run "cmake --help-policy CMP0075" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -13,10 +13,11 @@ Call Stack \(most recent call first\): CMP0075\.cmake:11 \(check_include_file\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at [^ -]*/Modules/CheckIncludeFileCXX\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at [^ +]*/Modules/CheckIncludeFileCXX\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0075 is not set: Include file check macros honor CMAKE_REQUIRED_LIBRARIES\. Run "cmake --help-policy CMP0075" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -30,10 +31,11 @@ Call Stack \(most recent call first\): CMP0075\.cmake:26 \(check_include_file_cxx\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at [^ -]*/Modules/CheckIncludeFiles\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at [^ +]*/Modules/CheckIncludeFiles\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0075 is not set: Include file check macros honor CMAKE_REQUIRED_LIBRARIES\. Run "cmake --help-policy CMP0075" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -47,9 +49,10 @@ Call Stack \(most recent call first\): CMP0075\.cmake:41 \(check_include_files\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Deprecation Warning at CMP0075\.cmake:[0-9]+ \(cmake_policy\): +CMake Warning \(deprecated\) at CMP0075\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0075 will be removed from a future version of CMake\. @@ -58,4 +61,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CheckModules/CheckRequiredFlags.cmake b/Tests/RunCMake/CheckModules/CheckRequiredFlags.cmake new file mode 100644 index 0000000..76f1f99 --- /dev/null +++ b/Tests/RunCMake/CheckModules/CheckRequiredFlags.cmake
@@ -0,0 +1,56 @@ +enable_language(C) +enable_language(CXX) + +# Warning options after the first entry are ignored for compatibility. +set(CMAKE_REQUIRED_FLAGS + "-DCHECK_REQUIRED_FLAGS -Drequired_flag_function=memcpy -Drequired_flag_library=memcpy -Drequired_flag_type=int" + "-Wseen-by-cmake") +set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_LIST_DIR}") + +include(CheckFunctionExists) +include(CheckIncludeFile) +include(CheckIncludeFileCXX) +include(CheckIncludeFiles) +include(CheckLibraryExists) +include(CheckPrototypeDefinition) +include(CheckTypeSize) + +function(run_tests) + if(${ARGC} GREATER 1) + set(${ARGN}) + endif() + + check_function_exists(required_flag_function CHECK_FUNCTION_EXISTS_RESULT) + check_include_file(CheckRequiredFlags.h CHECK_INCLUDE_FILE_RESULT) + check_include_file_cxx(CheckRequiredFlags.h CHECK_INCLUDE_FILE_CXX_RESULT) + check_include_files("stddef.h;CheckRequiredFlags.h" CHECK_INCLUDE_FILES_RESULT) + check_library_exists("" required_flag_library "" CHECK_LIBRARY_EXISTS_RESULT) + check_prototype_definition( + required_flag_prototype + "int required_flag_prototype(int value)" + "value" + "CheckRequiredFlags.h" + CHECK_PROTOTYPE_DEFINITION_RESULT + ) + check_type_size(required_flag_type CHECK_TYPE_SIZE_RESULT) + + foreach(result IN ITEMS + CHECK_FUNCTION_EXISTS_RESULT + CHECK_INCLUDE_FILE_RESULT + CHECK_INCLUDE_FILE_CXX_RESULT + CHECK_INCLUDE_FILES_RESULT + CHECK_LIBRARY_EXISTS_RESULT + CHECK_PROTOTYPE_DEFINITION_RESULT + CHECK_TYPE_SIZE_RESULT + ) + if(NOT ${result}) + message(SEND_ERROR "${result} did not honor CMAKE_REQUIRED_FLAGS") + endif() + unset(CACHE{${result}}) + endforeach() + unset(CACHE{HAVE_CHECK_TYPE_SIZE_RESULT}) +endfunction() + +run_tests() +run_tests(CMAKE_REQUIRED_LINK_OPTIONS REQUIRED-NOTFOUND) +run_tests(CMAKE_REQUIRED_LIBRARIES REQUIRED-NOTFOUND)
diff --git a/Tests/RunCMake/CheckModules/CheckRequiredFlags.h b/Tests/RunCMake/CheckModules/CheckRequiredFlags.h new file mode 100644 index 0000000..b4ec37c --- /dev/null +++ b/Tests/RunCMake/CheckModules/CheckRequiredFlags.h
@@ -0,0 +1,5 @@ +#ifndef CHECK_REQUIRED_FLAGS +# error "CMAKE_REQUIRED_FLAGS was not honored" +#endif + +int required_flag_prototype(int);
diff --git a/Tests/RunCMake/CheckModules/RunCMakeTest.cmake b/Tests/RunCMake/CheckModules/RunCMakeTest.cmake index 75176b0..46ab830 100644 --- a/Tests/RunCMake/CheckModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CheckModules/RunCMakeTest.cmake
@@ -34,6 +34,7 @@ run_cmake(CheckIncludeFilesMissingLanguage) run_cmake(CheckIncludeFilesUnknownArgument) run_cmake(CheckIncludeFilesUnknownLanguage) +run_cmake(CheckRequiredFlags) block() # Set common variables
diff --git a/Tests/RunCMake/CheckSourceCompiles/CMakeLists.txt b/Tests/RunCMake/CheckSourceCompiles/CMakeLists.txt index 0421e28..87023e0 100644 --- a/Tests/RunCMake/CheckSourceCompiles/CMakeLists.txt +++ b/Tests/RunCMake/CheckSourceCompiles/CMakeLists.txt
@@ -1,5 +1,9 @@ cmake_minimum_required(VERSION 3.13) +if(POLICY CMP0219) + cmake_policy(SET CMP0219 OLD) +endif() + project(${RunCMake_TEST} LANGUAGES NONE) include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CheckSourceCompiles/Extra.cmake b/Tests/RunCMake/CheckSourceCompiles/Extra.cmake new file mode 100644 index 0000000..edabced --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/Extra.cmake
@@ -0,0 +1 @@ +message(WARNING "Hello from Extra.cmake")
diff --git a/Tests/RunCMake/CheckSourceCompiles/Issue27893.cmake b/Tests/RunCMake/CheckSourceCompiles/Issue27893.cmake new file mode 100644 index 0000000..ec3dfe8 --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/Issue27893.cmake
@@ -0,0 +1,9 @@ +enable_language (C) +include(CheckSourceCompiles) + +# If CMAKE_REQUIRED_FLAGS is a list, all items but the first are passed as +# arguments to 'cmake'. This was never supported, but arguments that start with +# '-W' were silently ignored by CMake < 4.4. Starting with 4.4, CMake complains +# about unknown warning flags. Therefore, for compatibility, we strip them. +set(CMAKE_REQUIRED_FLAGS -DFOO -Wseen-by-cmake) +check_source_compiles(C "int main() {return 0;}" SHOULD_BUILD)
diff --git a/Tests/RunCMake/CheckSourceCompiles/MultipleLanguages.cmake b/Tests/RunCMake/CheckSourceCompiles/MultipleLanguages.cmake new file mode 100644 index 0000000..5596040 --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/MultipleLanguages.cmake
@@ -0,0 +1,11 @@ +enable_language (C CXX) +include(CheckSourceCompiles) + +add_library(cxx_iface INTERFACE IMPORTED) +target_compile_features(cxx_iface INTERFACE cxx_std_11) +set(CMAKE_REQUIRED_LIBRARIES cxx_iface) + +check_source_compiles(C "int main() {return 0;}" SHOULD_BUILD) +if(NOT SHOULD_BUILD) + message(SEND_ERROR "Test fail for valid C source.") +endif()
diff --git a/Tests/RunCMake/CheckSourceCompiles/RequiredFlags-stderr.txt b/Tests/RunCMake/CheckSourceCompiles/RequiredFlags-stderr.txt new file mode 100644 index 0000000..c7e0153 --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/RequiredFlags-stderr.txt
@@ -0,0 +1,7 @@ +CMake Warning at [^ +]*/Tests/RunCMake/CheckSourceCompiles/Extra\.cmake:[0-9]+ \(message\): + Hello from Extra\.cmake +Call Stack \(most recent call first\): + [^ +]*/Tests/RunCMake/CheckSourceCompiles/RequiredFlags-build/CMakeFiles/CMakeScratch/TryCompile-[^ +]*/CMakeLists\.txt:[0-9]+ \(project\)
diff --git a/Tests/RunCMake/CheckSourceCompiles/RequiredFlags.cmake b/Tests/RunCMake/CheckSourceCompiles/RequiredFlags.cmake new file mode 100644 index 0000000..3d10f5d --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/RequiredFlags.cmake
@@ -0,0 +1,17 @@ +enable_language (C) +include(CheckSourceCompiles) + +# If CMAKE_REQUIRED_FLAGS is a list, all items but the first are passed as +# arguments to 'cmake'. For backwards compatibility, test that this works. +# +# It would be nice if we could also test that a literal semicolon makes it +# through. Unfortunately, that needs different levels of escaping depending on +# the generator, because the build rule may or may not be subject to shell +# evaluation. +set(CMAKE_REQUIRED_FLAGS + "-DFUNC=main" + "-DCMAKE_PROJECT_INCLUDE=${CMAKE_CURRENT_LIST_DIR}/Extra.cmake") +check_source_compiles(C "int FUNC() {return 0;}" SHOULD_BUILD) +if(NOT SHOULD_BUILD) + message(SEND_ERROR "Test fail for valid C source.") +endif()
diff --git a/Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake b/Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake index 2ed3e36..6bfafdf 100644 --- a/Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake +++ b/Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake
@@ -3,6 +3,10 @@ run_cmake(NotEnabledLanguage) run_cmake(NonExistentLanguage) run_cmake(UnknownArgument) +run_cmake(MultipleLanguages) + +run_cmake(RequiredFlags) +run_cmake(Issue27893) run_cmake(CheckCSourceCompiles) run_cmake(CheckCXXSourceCompiles)
diff --git a/Tests/RunCMake/CheckSourceRuns/Extra.cmake b/Tests/RunCMake/CheckSourceRuns/Extra.cmake new file mode 100644 index 0000000..edabced --- /dev/null +++ b/Tests/RunCMake/CheckSourceRuns/Extra.cmake
@@ -0,0 +1 @@ +message(WARNING "Hello from Extra.cmake")
diff --git a/Tests/RunCMake/CheckSourceRuns/Issue27893.cmake b/Tests/RunCMake/CheckSourceRuns/Issue27893.cmake new file mode 100644 index 0000000..227d38d --- /dev/null +++ b/Tests/RunCMake/CheckSourceRuns/Issue27893.cmake
@@ -0,0 +1,9 @@ +enable_language (C) +include(CheckSourceRuns) + +# If CMAKE_REQUIRED_FLAGS is a list, all items but the first are passed as +# arguments to 'cmake'. This was never supported, but arguments that start with +# '-W' were silently ignored by CMake < 4.4. Starting with 4.4, CMake complains +# about unknown warning flags. Therefore, for compatibility, we strip them. +set(CMAKE_REQUIRED_FLAGS -DFOO -Wseen-by-cmake) +check_source_runs(C "int main() {return 0;}" SHOULD_RUN)
diff --git a/Tests/RunCMake/CheckSourceRuns/RequiredFlags-stderr.txt b/Tests/RunCMake/CheckSourceRuns/RequiredFlags-stderr.txt new file mode 100644 index 0000000..341d244 --- /dev/null +++ b/Tests/RunCMake/CheckSourceRuns/RequiredFlags-stderr.txt
@@ -0,0 +1,7 @@ +CMake Warning at [^ +]*/Tests/RunCMake/CheckSourceRuns/Extra\.cmake:[0-9]+ \(message\): + Hello from Extra\.cmake +Call Stack \(most recent call first\): + [^ +]*/Tests/RunCMake/CheckSourceRuns/RequiredFlags-build/CMakeFiles/CMakeScratch/TryCompile-[^ +]*/CMakeLists\.txt:[0-9]+ \(project\)
diff --git a/Tests/RunCMake/CheckSourceRuns/RequiredFlags.cmake b/Tests/RunCMake/CheckSourceRuns/RequiredFlags.cmake new file mode 100644 index 0000000..c54b70d --- /dev/null +++ b/Tests/RunCMake/CheckSourceRuns/RequiredFlags.cmake
@@ -0,0 +1,17 @@ +enable_language (C) +include(CheckSourceRuns) + +# If CMAKE_REQUIRED_FLAGS is a list, all items but the first are passed as +# arguments to 'cmake'. For backwards compatibility, test that this works. +# +# It would be nice if we could also test that a literal semicolon makes it +# through. Unfortunately, that needs different levels of escaping depending on +# the generator, because the build rule may or may not be subject to shell +# evaluation. +set(CMAKE_REQUIRED_FLAGS + "-DFUNC=main" + "-DCMAKE_PROJECT_INCLUDE=${CMAKE_CURRENT_LIST_DIR}/Extra.cmake") +check_source_runs(C "int FUNC() {return 0;}" SHOULD_RUN) +if(NOT SHOULD_RUN) + message(SEND_ERROR "C check_source_runs failed for valid C executable.") +endif()
diff --git a/Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake b/Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake index 64cecfc..9c51a6b 100644 --- a/Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake +++ b/Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake
@@ -4,6 +4,9 @@ run_cmake(NonExistentLanguage) run_cmake(UnknownArgument) +run_cmake(RequiredFlags) +run_cmake(Issue27893) + run_cmake(CheckCSourceRuns) run_cmake(CheckCXXSourceRuns) run_cmake(CheckSourceRunsC)
diff --git a/Tests/RunCMake/CheckSourceRuns/UnknownArgument-stderr.txt b/Tests/RunCMake/CheckSourceRuns/UnknownArgument-stderr.txt index a87fddc..79572c7 100644 --- a/Tests/RunCMake/CheckSourceRuns/UnknownArgument-stderr.txt +++ b/Tests/RunCMake/CheckSourceRuns/UnknownArgument-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(author\) at [^ ]*/Modules/Internal/CheckSourceRuns\.cmake:[0-9]+ \(message\): Unknown argument: @@ -9,7 +9,7 @@ ]*/Modules/CheckCSourceRuns\.cmake:[0-9]+ \(cmake_check_source_runs\) UnknownArgument\.cmake:[0-9]+ \(check_c_source_runs\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Error at [^ ]*/Modules/Internal/CheckSourceRuns\.cmake:[0-9]+ \(message\):
diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt index 2b4587f..d90b345 100644 --- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt +++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
@@ -1 +1 @@ -^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":10}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":1}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$ +^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":11}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":1}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
diff --git a/Tests/RunCMake/CommandLine/E_cat-stdin.cmake b/Tests/RunCMake/CommandLine/E_cat-stdin.cmake index e83e619..e168bb9 100644 --- a/Tests/RunCMake/CommandLine/E_cat-stdin.cmake +++ b/Tests/RunCMake/CommandLine/E_cat-stdin.cmake
@@ -1,10 +1,14 @@ -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ell.txt" "ell") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ll.txt" "ll") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/rld.txt" "rld") execute_process( COMMAND ${CMAKE_COMMAND} -E echo_append "H" - COMMAND ${CMAKE_COMMAND} -E cat - + COMMAND ${CMAKE_COMMAND} -E cat - # Read stdin from - arg + ) +execute_process( + COMMAND ${CMAKE_COMMAND} -E echo_append "e" + COMMAND ${CMAKE_COMMAND} -E cat # Read stdin when no args ) execute_process( COMMAND ${CMAKE_COMMAND} -E echo_append "o wo" - COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/ell.txt" - "${CMAKE_CURRENT_BINARY_DIR}/rld.txt" + COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/ll.txt" - "${CMAKE_CURRENT_BINARY_DIR}/rld.txt" )
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLine/E_copy_directory-t-argument-no-source-dirs-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLine/E_copy_directory-t-argument-no-source-dirs-result.txt
diff --git a/Tests/RunCMake/CommandLine/E_copy_directory-t-argument-no-source-dirs-stderr.txt b/Tests/RunCMake/CommandLine/E_copy_directory-t-argument-no-source-dirs-stderr.txt new file mode 100644 index 0000000..c3c3d6a --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_copy_directory-t-argument-no-source-dirs-stderr.txt
@@ -0,0 +1 @@ +^Error: No source directories specified \(for copy_directory command\)\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLine/E_copy_directory_if_different-t-argument-no-source-dirs-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLine/E_copy_directory_if_different-t-argument-no-source-dirs-result.txt
diff --git a/Tests/RunCMake/CommandLine/E_copy_directory_if_different-t-argument-no-source-dirs-stderr.txt b/Tests/RunCMake/CommandLine/E_copy_directory_if_different-t-argument-no-source-dirs-stderr.txt new file mode 100644 index 0000000..371acbf --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_copy_directory_if_different-t-argument-no-source-dirs-stderr.txt
@@ -0,0 +1 @@ +^Error: No source directories specified \(for copy_directory_if_different command\)\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLine/E_copy_directory_if_newer-t-argument-no-source-dirs-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLine/E_copy_directory_if_newer-t-argument-no-source-dirs-result.txt
diff --git a/Tests/RunCMake/CommandLine/E_copy_directory_if_newer-t-argument-no-source-dirs-stderr.txt b/Tests/RunCMake/CommandLine/E_copy_directory_if_newer-t-argument-no-source-dirs-stderr.txt new file mode 100644 index 0000000..9af85a8 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_copy_directory_if_newer-t-argument-no-source-dirs-stderr.txt
@@ -0,0 +1 @@ +^Error: No source directories specified \(for copy_directory_if_newer command\)\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLine/E_copy_if_different-t-argument-target-is-file-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLine/E_copy_if_different-t-argument-target-is-file-result.txt
diff --git a/Tests/RunCMake/CommandLine/E_copy_if_different-t-argument-target-is-file-stderr.txt b/Tests/RunCMake/CommandLine/E_copy_if_different-t-argument-target-is-file-stderr.txt new file mode 100644 index 0000000..138ce81 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_copy_if_different-t-argument-target-is-file-stderr.txt
@@ -0,0 +1 @@ +^Error: Target \(for copy_if_different command\).* is not a directory\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLine/E_copy_if_newer-t-argument-target-is-file-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLine/E_copy_if_newer-t-argument-target-is-file-result.txt
diff --git a/Tests/RunCMake/CommandLine/E_copy_if_newer-t-argument-target-is-file-stderr.txt b/Tests/RunCMake/CommandLine/E_copy_if_newer-t-argument-target-is-file-stderr.txt new file mode 100644 index 0000000..08eab4c --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_copy_if_newer-t-argument-target-is-file-stderr.txt
@@ -0,0 +1 @@ +^Error: Target \(for copy_if_newer command\).* is not a directory\.$
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index 584f38b..fada1ee 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -153,6 +153,15 @@ run_cmake_command(install-options-to-vars ${CMAKE_COMMAND} --install ${RunCMake_SOURCE_DIR}/dir-install-options-to-vars --strip --prefix /var/test --config sample --component pack) +run_cmake_command(install-no-component-value + ${CMAKE_COMMAND} --install ${RunCMake_SOURCE_DIR}/dir-install-options-to-vars + --component) +run_cmake_command(install-multi-component-1 + ${CMAKE_COMMAND} --install ${RunCMake_SOURCE_DIR}/dir-install-options-to-vars + --component comp1 comp2) +run_cmake_command(install-multi-component-2 + ${CMAKE_COMMAND} --install ${RunCMake_SOURCE_DIR}/dir-install-options-to-vars + --component comp1 --component comp2) run_cmake_command(install-default-dir-permissions-all ${CMAKE_COMMAND} --install ${RunCMake_SOURCE_DIR}/dir-permissions-install-options-to-vars --default-directory-permissions u=rwx,g=rx,o=rx) @@ -659,6 +668,12 @@ ${CMAKE_COMMAND} -E copy ${in}/f1.txt -t ${out}/f1.txt ${in}/f3.txt) run_cmake_command(E_copy-t-argument-no-source-files ${CMAKE_COMMAND} -E copy -t ${out}) +run_cmake_command(E_copy_if_different-t-argument + ${CMAKE_COMMAND} -E copy_if_different ${in}/f1.txt -t ${out} ${in}/f3.txt) +run_cmake_command(E_copy_if_different-t-argument-target-is-file + ${CMAKE_COMMAND} -E copy_if_different ${in}/f1.txt -t ${out}/f1.txt ${in}/f3.txt) +run_cmake_command(E_copy_if_different-t-argument-no-source-files + ${CMAKE_COMMAND} -E copy_if_different -t ${out}) run_cmake_command(E_copy_if_different-one-source-directory-target-is-directory ${CMAKE_COMMAND} -E copy_if_different ${in}/f1.txt ${out}) run_cmake_command(E_copy_if_different-three-source-files-target-is-directory @@ -667,6 +682,12 @@ ${CMAKE_COMMAND} -E copy_if_different ${in}/f1.txt ${in}/f2.txt ${in}/f3.txt ${out}/f1.txt) run_cmake_command(E_copy_if_different-nonexistent-source ${CMAKE_COMMAND} -E copy_if_different ${in}/nonexistent.txt ${out}) +run_cmake_command(E_copy_if_newer-t-argument + ${CMAKE_COMMAND} -E copy_if_newer ${in}/f1.txt -t ${out} ${in}/f3.txt) +run_cmake_command(E_copy_if_newer-t-argument-target-is-file + ${CMAKE_COMMAND} -E copy_if_newer ${in}/f1.txt -t ${out}/f1.txt ${in}/f3.txt) +run_cmake_command(E_copy_if_newer-t-argument-no-source-files + ${CMAKE_COMMAND} -E copy_if_newer -t ${out}) run_cmake_command(E_copy_if_newer-one-source-directory-target-is-directory ${CMAKE_COMMAND} -E copy_if_newer ${in}/f1.txt ${out}) run_cmake_command(E_copy_if_newer-three-source-files-target-is-directory @@ -684,8 +705,16 @@ file(MAKE_DIRECTORY ${out}) run_cmake_command(E_copy_directory_if_different ${CMAKE_COMMAND} -E copy_directory_if_different ${in} ${out}) +run_cmake_command(E_copy_directory_if_different-t-argument + ${CMAKE_COMMAND} -E copy_directory_if_different -t ${out} ${in}) +run_cmake_command(E_copy_directory_if_different-t-argument-no-source-dirs + ${CMAKE_COMMAND} -E copy_directory_if_different -t ${out}) run_cmake_command(E_copy_directory_if_newer ${CMAKE_COMMAND} -E copy_directory_if_newer ${in} ${out}) +run_cmake_command(E_copy_directory_if_newer-t-argument + ${CMAKE_COMMAND} -E copy_directory_if_newer -t ${out} ${in}) +run_cmake_command(E_copy_directory_if_newer-t-argument-no-source-dirs + ${CMAKE_COMMAND} -E copy_directory_if_newer -t ${out}) run_cmake_command(E_copy_directory_if_newer-nonexistent-source ${CMAKE_COMMAND} -E copy_directory_if_newer ${in}/nonexistent ${out}/target) unset(in) @@ -703,6 +732,10 @@ ${CMAKE_COMMAND} -E copy_directory ${in}/d1 ${in}/d2 ${in}/d3 ${outfile}) run_cmake_command(E_copy_directory-three-source-files-target-is-not-exist ${CMAKE_COMMAND} -E copy_directory ${in}/d1 ${in}/d2 ${in}/d3 ${out}/not_existing_directory) +run_cmake_command(E_copy_directory-t-argument + ${CMAKE_COMMAND} -E copy_directory -t ${out} ${in}/d1 ${in}/d2 ${in}/d3) +run_cmake_command(E_copy_directory-t-argument-no-source-dirs + ${CMAKE_COMMAND} -E copy_directory -t ${out}) unset(in) unset(out) unset(outfile) @@ -1013,37 +1046,39 @@ "-DFOO:STRING=-DBAR:BOOL=BAZ") run_cmake(D_typed_nested_cache) -set(RunCMake_TEST_OPTIONS -Wno-dev) -run_cmake(Wno-dev) -unset(RunCMake_TEST_OPTIONS) - +# -Wdev is a deprecated synonym for -Wauthor set(RunCMake_TEST_OPTIONS -Wdev) run_cmake(Wdev) unset(RunCMake_TEST_OPTIONS) -set(RunCMake_TEST_OPTIONS -Werror=dev) -run_cmake(Werror_dev) +set(RunCMake_TEST_OPTIONS -Wno-author) +run_cmake(Wno-author) unset(RunCMake_TEST_OPTIONS) -set(RunCMake_TEST_OPTIONS -Wno-error=dev) -run_cmake(Wno-error_deprecated) +set(RunCMake_TEST_OPTIONS -Wauthor) +run_cmake(Wauthor) unset(RunCMake_TEST_OPTIONS) -# -Wdev should not override deprecated options if specified -set(RunCMake_TEST_OPTIONS -Wdev -Wno-deprecated) -run_cmake(Wno-deprecated) +set(RunCMake_TEST_OPTIONS -Werror=author) +run_cmake(Werror_author) unset(RunCMake_TEST_OPTIONS) -set(RunCMake_TEST_OPTIONS -Wno-deprecated -Wdev) + +set(RunCMake_TEST_OPTIONS -Wno-error=author) +run_cmake(Wno-error_author) +unset(RunCMake_TEST_OPTIONS) + +# -Wauthor should not override deprecated options if specified +set(RunCMake_TEST_OPTIONS -Wauthor -Wno-deprecated) run_cmake(Wno-deprecated) unset(RunCMake_TEST_OPTIONS) -# -Wdev should enable deprecated warnings as well -set(RunCMake_TEST_OPTIONS -Wdev) +# -Wauthor should enable deprecated warnings as well +set(RunCMake_TEST_OPTIONS -Wauthor) run_cmake(Wdeprecated) unset(RunCMake_TEST_OPTIONS) -# -Werror=dev should enable deprecated errors as well -set(RunCMake_TEST_OPTIONS -Werror=dev) +# -Werror=author should enable deprecated errors as well +set(RunCMake_TEST_OPTIONS -Werror=author) run_cmake(Werror_deprecated) unset(RunCMake_TEST_OPTIONS) @@ -1067,23 +1102,28 @@ run_cmake(Wno-error_deprecated) unset(RunCMake_TEST_OPTIONS) -# Dev warnings should be on by default -run_cmake(Wdev) +# Author warnings should be on by default +run_cmake(Wauthor) # Deprecated warnings should be on by default run_cmake(Wdeprecated) # Conflicting -W options should honor the last value -set(RunCMake_TEST_OPTIONS -Wno-dev -Wdev) -run_cmake(Wdev) +set(RunCMake_TEST_OPTIONS -Wno-author -Wauthor) +run_cmake(Wauthor) unset(RunCMake_TEST_OPTIONS) -set(RunCMake_TEST_OPTIONS -Wdev -Wno-dev) -run_cmake(Wno-dev) +set(RunCMake_TEST_OPTIONS -Wauthor -Wno-author) +run_cmake(Wno-author) +unset(RunCMake_TEST_OPTIONS) + +set(RunCMake_TEST_OPTIONS -Wno-deprecated -Wuninitialized) +run_cmake(Wuninitialized) unset(RunCMake_TEST_OPTIONS) run_cmake_command(W_bad-arg1 ${CMAKE_COMMAND} -B DummyBuildDir -W) run_cmake_command(W_bad-arg2 ${CMAKE_COMMAND} -B DummyBuildDir -Wno-) run_cmake_command(W_bad-arg3 ${CMAKE_COMMAND} -B DummyBuildDir -Werror=) +run_cmake_command(W_bad-arg4 ${CMAKE_COMMAND} -B DummyBuildDir -Wimaginary) set(RunCMake_TEST_OPTIONS --debug-output) run_cmake(debug-output) @@ -1098,7 +1138,7 @@ run_cmake(trace-expand) unset(RunCMake_TEST_OPTIONS) -set(RunCMake_TEST_OPTIONS --trace-expand --warn-uninitialized) +set(RunCMake_TEST_OPTIONS --trace-expand -Wuninitialized) run_cmake(trace-expand-warn-uninitialized) unset(RunCMake_TEST_OPTIONS) @@ -1119,10 +1159,6 @@ run_cmake(trace-json-v1-expand) unset(RunCMake_TEST_OPTIONS) -set(RunCMake_TEST_OPTIONS -Wno-deprecated --warn-uninitialized) -run_cmake(warn-uninitialized) -unset(RunCMake_TEST_OPTIONS) - set(RunCMake_TEST_OPTIONS --trace-source=trace-only-this-file.cmake) run_cmake(trace-source) unset(RunCMake_TEST_OPTIONS) @@ -1230,6 +1266,7 @@ unset(RunCMake_TEST_OPTIONS) run_cmake_with_options(help-arbitrary "--help" "CMAKE_CXX_IGNORE_EXTENSIONS") +run_cmake_with_options(help-variable-lang "--help-variable" "CMAKE_CXX_PVS_STUDIO") if (WIN32 OR DEFINED ENV{HOME}) set(config_dir_test print-config-dir)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLine/W_bad-arg4-result.txt similarity index 100% rename from Tests/RunCMake/CommandLine/Werror_dev-result.txt rename to Tests/RunCMake/CommandLine/W_bad-arg4-result.txt
diff --git a/Tests/RunCMake/CommandLine/W_bad-arg4-stderr.txt b/Tests/RunCMake/CommandLine/W_bad-arg4-stderr.txt new file mode 100644 index 0000000..e79fc0d --- /dev/null +++ b/Tests/RunCMake/CommandLine/W_bad-arg4-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error: The warning category "imaginary" is not known. +CMake Error: Run 'cmake --help' for all supported options\.
diff --git a/Tests/RunCMake/CommandLine/Wauthor-stderr.txt b/Tests/RunCMake/CommandLine/Wauthor-stderr.txt new file mode 100644 index 0000000..49c9927 --- /dev/null +++ b/Tests/RunCMake/CommandLine/Wauthor-stderr.txt
@@ -0,0 +1,11 @@ +^CMake Warning \(author\) at Wauthor\.cmake:1 \(message\): + Some author warning +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\. + +CMake Warning \(author\) at Wauthor\.cmake:6 \(include\): + include\(\) given empty file name \(ignored\)\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Wauthor.cmake b/Tests/RunCMake/CommandLine/Wauthor.cmake new file mode 100644 index 0000000..29beff1 --- /dev/null +++ b/Tests/RunCMake/CommandLine/Wauthor.cmake
@@ -0,0 +1,6 @@ +message(AUTHOR_WARNING "Some author warning") + +# with -Wauthor this will also cause an AUTHOR_WARNING message, checks that +# messages issued outside of the message command, by other CMake commands, also +# are affected by -Wauthor +include("")
diff --git a/Tests/RunCMake/CommandLine/Wdeprecated-stderr.txt b/Tests/RunCMake/CommandLine/Wdeprecated-stderr.txt index 6ce172d..c58437b 100644 --- a/Tests/RunCMake/CommandLine/Wdeprecated-stderr.txt +++ b/Tests/RunCMake/CommandLine/Wdeprecated-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Wdeprecated\.cmake:1 \(message\): +^CMake Warning \(deprecated\) at Wdeprecated\.cmake:1 \(message\): Some deprecated warning Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CommandLine/Wdev-stderr.txt b/Tests/RunCMake/CommandLine/Wdev-stderr.txt index 31ed21b..6b0a7b7 100644 --- a/Tests/RunCMake/CommandLine/Wdev-stderr.txt +++ b/Tests/RunCMake/CommandLine/Wdev-stderr.txt
@@ -1,11 +1,12 @@ -^CMake Warning \(dev\) at Wdev\.cmake:1 \(message\): +^The dev option is deprecated\. Use -Wauthor instead\. +CMake Warning \(author\) at Wdev\.cmake:1 \(message\): Some author warning Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at Wdev\.cmake:6 \(include\): +CMake Warning \(author\) at Wdev\.cmake:6 \(include\): include\(\) given empty file name \(ignored\)\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLine/Werror_author-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLine/Werror_author-result.txt
diff --git a/Tests/RunCMake/CommandLine/Werror_author-stderr.txt b/Tests/RunCMake/CommandLine/Werror_author-stderr.txt new file mode 100644 index 0000000..29e0dbf --- /dev/null +++ b/Tests/RunCMake/CommandLine/Werror_author-stderr.txt
@@ -0,0 +1,11 @@ +^CMake Error \(author\) at Werror_author\.cmake:4 \(include\): + include\(\) given empty file name \(ignored\)\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author to suppress it\. + +CMake Error \(author\) at Werror_author\.cmake:7 \(message\): + Some author warning +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_author.cmake b/Tests/RunCMake/CommandLine/Werror_author.cmake new file mode 100644 index 0000000..4b83606 --- /dev/null +++ b/Tests/RunCMake/CommandLine/Werror_author.cmake
@@ -0,0 +1,7 @@ +# with -Werror=author this will also cause an (upgraded) AUTHOR_ERROR message, +# checks that messages issued outside of the message command, by other CMake +# commands, also are affected by -Werror=author +include("") + +# message command sets fatal occurred flag, so run it last +message(AUTHOR_WARNING "Some author warning")
diff --git a/Tests/RunCMake/CommandLine/Werror_deprecated-stderr.txt b/Tests/RunCMake/CommandLine/Werror_deprecated-stderr.txt index e60b549..3bbac47 100644 --- a/Tests/RunCMake/CommandLine/Werror_deprecated-stderr.txt +++ b/Tests/RunCMake/CommandLine/Werror_deprecated-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Error at Werror_deprecated\.cmake:1 \(message\): +^CMake Error \(deprecated\) at Werror_deprecated\.cmake:1 \(message\): Some deprecated warning Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-stderr.txt b/Tests/RunCMake/CommandLine/Werror_dev-stderr.txt deleted file mode 100644 index 7a36b00..0000000 --- a/Tests/RunCMake/CommandLine/Werror_dev-stderr.txt +++ /dev/null
@@ -1,11 +0,0 @@ -^CMake Error \(dev\) at Werror_dev\.cmake:4 \(include\): - include\(\) given empty file name \(ignored\)\. -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) -This error is for project developers\. Use -Wno-error=dev to suppress it\. - -CMake Error \(dev\) at Werror_dev\.cmake:7 \(message\): - Some author warning -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) -This error is for project developers\. Use -Wno-error=dev to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev.cmake b/Tests/RunCMake/CommandLine/Werror_dev.cmake deleted file mode 100644 index 05f333a..0000000 --- a/Tests/RunCMake/CommandLine/Werror_dev.cmake +++ /dev/null
@@ -1,7 +0,0 @@ -# with -Werror=dev this will also cause an (upgraded) AUTHOR_ERROR message, -# checks that messages issued outside of the message command, by other CMake -# commands, also are affected by -Werror=dev -include("") - -# message command sets fatal occurred flag, so run it last -message(AUTHOR_WARNING "Some author warning")
diff --git a/Tests/RunCMake/CommandLine/Wno-author.cmake b/Tests/RunCMake/CommandLine/Wno-author.cmake new file mode 100644 index 0000000..67a7bde --- /dev/null +++ b/Tests/RunCMake/CommandLine/Wno-author.cmake
@@ -0,0 +1,6 @@ +message(AUTHOR_WARNING "Some author warning") + +# without -Wno-author this will also cause an AUTHOR_WARNING message, checks +# that messages issued outside of the message command, by other CMake commands, +# also are affected by -Wno-author +include("")
diff --git a/Tests/RunCMake/CommandLine/Wno-dev.cmake b/Tests/RunCMake/CommandLine/Wno-dev.cmake deleted file mode 100644 index 802b435..0000000 --- a/Tests/RunCMake/CommandLine/Wno-dev.cmake +++ /dev/null
@@ -1,6 +0,0 @@ -message(AUTHOR_WARNING "Some author warning") - -# without -Wno-dev this will also cause an AUTHOR_WARNING message, checks that -# messages issued outside of the message command, by other CMake commands, also -# are affected by -Wno-dev -include("")
diff --git a/Tests/RunCMake/CommandLine/Wno-error_author-stderr.txt b/Tests/RunCMake/CommandLine/Wno-error_author-stderr.txt new file mode 100644 index 0000000..0cef7a0 --- /dev/null +++ b/Tests/RunCMake/CommandLine/Wno-error_author-stderr.txt
@@ -0,0 +1,11 @@ +^CMake Warning \(author\) at Wno-error_author\.cmake:2 \(message\): + Some author warning +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\. + +CMake Warning \(author\) at Wno-error_author\.cmake:7 \(include\): + include\(\) given empty file name \(ignored\)\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Wno-error_author.cmake b/Tests/RunCMake/CommandLine/Wno-error_author.cmake new file mode 100644 index 0000000..7e10fcb --- /dev/null +++ b/Tests/RunCMake/CommandLine/Wno-error_author.cmake
@@ -0,0 +1,7 @@ +# This should still produce a warning when -Wno-error=author is specified +message(AUTHOR_WARNING "Some author warning") + +# with -Wno-error=author this will also cause an AUTHOR_WARNING message, checks +# that messages issued outside of the message command, by other CMake commands, +# also are affected by -Wno-error=author +include("")
diff --git a/Tests/RunCMake/CommandLine/Wno-error_deprecated-stderr.txt b/Tests/RunCMake/CommandLine/Wno-error_deprecated-stderr.txt index 91dedd5..e985690 100644 --- a/Tests/RunCMake/CommandLine/Wno-error_deprecated-stderr.txt +++ b/Tests/RunCMake/CommandLine/Wno-error_deprecated-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Wno-error_deprecated\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at Wno-error_deprecated\.cmake:2 \(message\): Some deprecated warning Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CommandLine/Wno-error_dev-stderr.txt b/Tests/RunCMake/CommandLine/Wno-error_dev-stderr.txt deleted file mode 100644 index 1beaaad..0000000 --- a/Tests/RunCMake/CommandLine/Wno-error_dev-stderr.txt +++ /dev/null
@@ -1,11 +0,0 @@ -^CMake Warning \(dev\) at Wno-error_dev\.cmake:2 \(message\): - Some author warning -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. - -CMake Warning \(dev\) at Wno-error_dev\.cmake:6 \(include\): - include\(\) given empty file name \(ignored\)\. -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Wno-error_dev.cmake b/Tests/RunCMake/CommandLine/Wno-error_dev.cmake deleted file mode 100644 index b700c19..0000000 --- a/Tests/RunCMake/CommandLine/Wno-error_dev.cmake +++ /dev/null
@@ -1,7 +0,0 @@ -# This should still produce a warning when -Wno-error=dev is specified -message(AUTHOR_WARNING "Some author warning") - -# with -Wno-error=dev this will also cause an AUTHOR_WARNING message, checks -# that messages issued outside of the message command, by other CMake commands, -# also are affected by -Wno-error=dev -include("")
diff --git a/Tests/RunCMake/CommandLine/Wuninitialized-stderr.txt b/Tests/RunCMake/CommandLine/Wuninitialized-stderr.txt new file mode 100644 index 0000000..9490c81 --- /dev/null +++ b/Tests/RunCMake/CommandLine/Wuninitialized-stderr.txt
@@ -0,0 +1,23 @@ +CMake Warning \(uninitialized\) at Wuninitialized\.cmake:1 \(set\): + uninitialized variable 'NEW_WARN_FROM_NORMAL_CMAKE_FILE_INSIDE_BRACES' +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\) +(This warning is for project developers\. Use -Wno-author to suppress it\.)? + +CMake Warning \(uninitialized\) at Wuninitialized\.cmake:2 \(string\): + uninitialized variable 'NEW_WARN_FROM_STRING_CONFIGURE_INSIDE_BRACES' +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\) +(This warning is for project developers\. Use -Wno-author to suppress it\.)? + +CMake Warning \(uninitialized\) at Wuninitialized\.cmake:4 \(configure_file\): + uninitialized variable 'NEW_WARN_FROM_CONFIGURE_FILE_INSIDE_AT' +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\) +(This warning is for project developers\. Use -Wno-author to suppress it\.)? + +CMake Warning \(uninitialized\) at Wuninitialized\.cmake:5 \(string\): + uninitialized variable 'NEW_WARN_FROM_STRING_CONFIGURE_INSIDE_AT' +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)( +This warning is for project developers\. Use -Wno-author to suppress it\.)?$
diff --git a/Tests/RunCMake/CommandLine/warn-uninitialized.cmake b/Tests/RunCMake/CommandLine/Wuninitialized.cmake similarity index 100% rename from Tests/RunCMake/CommandLine/warn-uninitialized.cmake rename to Tests/RunCMake/CommandLine/Wuninitialized.cmake
diff --git a/Tests/RunCMake/CommandLine/help-variable-lang-stdout.txt b/Tests/RunCMake/CommandLine/help-variable-lang-stdout.txt new file mode 100644 index 0000000..17d02fe --- /dev/null +++ b/Tests/RunCMake/CommandLine/help-variable-lang-stdout.txt
@@ -0,0 +1,13 @@ +CMAKE_<LANG>_PVS_STUDIO +----------------------- + +.. versionadded:: 4.3 + +Default value for ``<LANG>_PVS_STUDIO`` target property +when ``<LANG>`` is ``C`` or ``CXX``. + +This variable is used to initialize the property on each target as it is +created. For example: + + set\(CMAKE_CXX_PVS_STUDIO pvs-studio-analyzer analyze -a "GA\\;OP"\) + add_executable\(foo foo\.cxx\)
diff --git a/Tests/RunCMake/CommandLine/install-multi-component-1-stderr.txt b/Tests/RunCMake/CommandLine/install-multi-component-1-stderr.txt new file mode 100644 index 0000000..4abd24e --- /dev/null +++ b/Tests/RunCMake/CommandLine/install-multi-component-1-stderr.txt
@@ -0,0 +1,2 @@ +CMAKE_INSTALL_COMPONENT is comp1 +CMAKE_INSTALL_COMPONENT is comp2
diff --git a/Tests/RunCMake/CommandLine/install-multi-component-2-stderr.txt b/Tests/RunCMake/CommandLine/install-multi-component-2-stderr.txt new file mode 100644 index 0000000..4abd24e --- /dev/null +++ b/Tests/RunCMake/CommandLine/install-multi-component-2-stderr.txt
@@ -0,0 +1,2 @@ +CMAKE_INSTALL_COMPONENT is comp1 +CMAKE_INSTALL_COMPONENT is comp2
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLine/install-no-component-value-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLine/install-no-component-value-result.txt
diff --git a/Tests/RunCMake/CommandLine/install-no-component-value-stderr.txt b/Tests/RunCMake/CommandLine/install-no-component-value-stderr.txt new file mode 100644 index 0000000..a8eff7c --- /dev/null +++ b/Tests/RunCMake/CommandLine/install-no-component-value-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: Invalid value used with --component
diff --git a/Tests/RunCMake/CommandLine/warn-uninitialized-stderr.txt b/Tests/RunCMake/CommandLine/warn-uninitialized-stderr.txt deleted file mode 100644 index 31ff067..0000000 --- a/Tests/RunCMake/CommandLine/warn-uninitialized-stderr.txt +++ /dev/null
@@ -1,23 +0,0 @@ -CMake Warning \(dev\) at warn-uninitialized\.cmake:1 \(set\): - uninitialized variable 'NEW_WARN_FROM_NORMAL_CMAKE_FILE_INSIDE_BRACES' -Call Stack \(most recent call first\): - CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. - -CMake Warning \(dev\) at warn-uninitialized\.cmake:2 \(string\): - uninitialized variable 'NEW_WARN_FROM_STRING_CONFIGURE_INSIDE_BRACES' -Call Stack \(most recent call first\): - CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. - -CMake Warning \(dev\) at warn-uninitialized\.cmake:4 \(configure_file\): - uninitialized variable 'NEW_WARN_FROM_CONFIGURE_FILE_INSIDE_AT' -Call Stack \(most recent call first\): - CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. - -CMake Warning \(dev\) at warn-uninitialized\.cmake:5 \(string\): - uninitialized variable 'NEW_WARN_FROM_STRING_CONFIGURE_INSIDE_AT' -Call Stack \(most recent call first\): - CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake b/Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake index e2b0ca7..01c2f7e 100644 --- a/Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake
@@ -119,6 +119,34 @@ run_cmake(compression-method-zstd) run_cmake(compression-method-ppmd) +# Encoding tests. These rely on UTF-8 encoding of our test sources. +if(NOT DEFINED CMake_TEST_LOCALE_CHARSET) + cmake_host_system_information(RESULT CMake_TEST_LOCALE_CHARSET QUERY LOCALE_CHARSET) + message(STATUS "Detected LOCALE_CHARSET '${CMake_TEST_LOCALE_CHARSET}'") +endif() +if(CMake_TEST_LOCALE_CHARSET STREQUAL "UTF-8") + if(CMAKE_HOST_WIN32) + # The OEM encoding cannot represent our UTF-8 test paths. + run_cmake(encoding-oem-gnutar-fails) + run_cmake(encoding-oem-zip-fails) + else() + run_cmake(encoding-oem-gnutar) + run_cmake(encoding-oem-zip) + endif() + + run_cmake(encoding-oem-7zip) + run_cmake(encoding-oem-pax) + run_cmake(encoding-oem-paxr) + + run_cmake(encoding-utf-8-7zip) + run_cmake(encoding-utf-8-gnutar) + run_cmake(encoding-utf-8-pax) + run_cmake(encoding-utf-8-paxr) + run_cmake(encoding-utf-8-zip) +else() + message(STATUS "encoding-* - SKIPPED due to non-UTF-8 locale") +endif() + # Extracting only selected files or directories run_cmake(zip-filtered)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-7zip.cmake b/Tests/RunCMake/CommandLineTar/encoding-oem-7zip.cmake new file mode 100644 index 0000000..722ca8e --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-7zip.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.7z") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=7zip --cmake-tar-encoding=OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=OEM) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("377abcaf271c" LIMIT 6 HEX)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar-fails-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLineTar/encoding-oem-gnutar-fails-result.txt
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar-fails-stderr.txt b/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar-fails-stderr.txt new file mode 100644 index 0000000..ba1bc13 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar-fails-stderr.txt
@@ -0,0 +1,6 @@ +^CMake Error: archive_write_header: Can't translate [Pp]athname( '[^']*\.txt')? to [^ +]* +CMake Error: Problem creating tar: +[^ +]*/bad\.tar +CMake Error at roundtrip\.cmake:[0-9]+ \(message\):
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar-fails.cmake b/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar-fails.cmake new file mode 100644 index 0000000..10ae837 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar-fails.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "bad.tar") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=gnutar --cmake-tar-encoding=OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=OEM) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172202000" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar.cmake b/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar.cmake new file mode 100644 index 0000000..93bcd1c --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-gnutar.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.tar") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=gnutar --cmake-tar-encoding=OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=OEM) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172202000" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-pax.cmake b/Tests/RunCMake/CommandLineTar/encoding-oem-pax.cmake new file mode 100644 index 0000000..603ef66 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-pax.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.tar") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=pax --cmake-tar-encoding=OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=OEM) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172003030" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-paxr.cmake b/Tests/RunCMake/CommandLineTar/encoding-oem-paxr.cmake new file mode 100644 index 0000000..12b6197 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-paxr.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.tar") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=paxr --cmake-tar-encoding=OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=OEM) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172003030" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/CommandLineTar/encoding-oem-zip-fails-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/CommandLineTar/encoding-oem-zip-fails-result.txt
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-zip-fails-stderr.txt b/Tests/RunCMake/CommandLineTar/encoding-oem-zip-fails-stderr.txt new file mode 100644 index 0000000..5f0acc3 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-zip-fails-stderr.txt
@@ -0,0 +1,6 @@ +^CMake Error: archive_write_header: Can't translate [Pp]athname( '[^']*\.txt')? to [^ +]* +CMake Error: Problem creating tar: +[^ +]*/bad\.zip +CMake Error at roundtrip\.cmake:[0-9]+ \(message\):
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-zip-fails.cmake b/Tests/RunCMake/CommandLineTar/encoding-oem-zip-fails.cmake new file mode 100644 index 0000000..17e3da3 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-zip-fails.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "bad.zip") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=zip --cmake-tar-encoding=OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=OEM) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-oem-zip.cmake b/Tests/RunCMake/CommandLineTar/encoding-oem-zip.cmake new file mode 100644 index 0000000..5112770 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-oem-zip.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.zip") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=zip --cmake-tar-encoding=OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=OEM) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-utf-8-7zip.cmake b/Tests/RunCMake/CommandLineTar/encoding-utf-8-7zip.cmake new file mode 100644 index 0000000..cdecb90 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-utf-8-7zip.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.7z") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=7zip --cmake-tar-encoding=UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=UTF-8) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("377abcaf271c" LIMIT 6 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-utf-8-gnutar.cmake b/Tests/RunCMake/CommandLineTar/encoding-utf-8-gnutar.cmake new file mode 100644 index 0000000..e74f87f --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-utf-8-gnutar.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.tar") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=gnutar --cmake-tar-encoding=UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=UTF-8) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172202000" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-utf-8-pax.cmake b/Tests/RunCMake/CommandLineTar/encoding-utf-8-pax.cmake new file mode 100644 index 0000000..10b2342 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-utf-8-pax.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.tar") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=pax --cmake-tar-encoding=UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=UTF-8) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172003030" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-utf-8-paxr.cmake b/Tests/RunCMake/CommandLineTar/encoding-utf-8-paxr.cmake new file mode 100644 index 0000000..5adc9dc --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-utf-8-paxr.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.tar") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=paxr --cmake-tar-encoding=UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=UTF-8) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172003030" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/encoding-utf-8-zip.cmake b/Tests/RunCMake/CommandLineTar/encoding-utf-8-zip.cmake new file mode 100644 index 0000000..fded11f --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/encoding-utf-8-zip.cmake
@@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.zip") + +set(COMPRESSION_FLAGS cvf) +set(COMPRESSION_OPTIONS --format=zip --cmake-tar-encoding=UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +set(DECOMPRESSION_FLAGS xvf) +set(DECOMPRESSION_OPTIONS --cmake-tar-encoding=UTF-8) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/CommandLineTar/roundtrip.cmake b/Tests/RunCMake/CommandLineTar/roundtrip.cmake index 53bbbc6..6533aaa 100644 --- a/Tests/RunCMake/CommandLineTar/roundtrip.cmake +++ b/Tests/RunCMake/CommandLineTar/roundtrip.cmake
@@ -11,7 +11,7 @@ ) if(NOT result STREQUAL "0") - message(FATAL_ERROR "tar failed with arguments [${ARGN}] result [${result}]") + message(FATAL_ERROR "tar failed with arguments:\n ${ARGN}\nresult:\n ${result}") endif() endfunction() @@ -31,6 +31,7 @@ "d_4/f1.txt" "d-4/f1.txt" "My Special Directory/f1.txt" + ${COMPRESSION_ADDITIONAL_FILE_NAMES} ) foreach(file ${CHECK_FILES}) @@ -76,18 +77,25 @@ set(output ${FULL_DECOMPRESS_DIR}/${COMPRESS_DIR}/${file}) if(NOT EXISTS ${input}) - message(SEND_ERROR "Cannot find input file ${output}") + message(SEND_ERROR "Cannot find input file:\n \"${output}\"") endif() if(NOT EXISTS ${output}) - message(SEND_ERROR "Cannot find output file ${output}") + message(SEND_ERROR "Cannot find output file:\n \"${output}\"") endif() file(MD5 ${input} input_md5) file(MD5 ${output} output_md5) if(NOT input_md5 STREQUAL output_md5) - message(SEND_ERROR "Files \"${input}\" and \"${output}\" are different") + message(SEND_ERROR + "Files:\n" + " \"${input}\"\n" + "and\n" + " \"${output}\"\n" + "are different" + ) + endif() endforeach() @@ -95,7 +103,7 @@ set(output ${FULL_DECOMPRESS_DIR}/${COMPRESS_DIR}/${file}) if(EXISTS ${output}) - message(SEND_ERROR "File ${output} exists but it shouldn't") + message(SEND_ERROR "File exists but it shouldn't:\n \"${output}\"") endif() endforeach()
diff --git a/Tests/RunCMake/CommandLineTar/utf-8-paths.cmake b/Tests/RunCMake/CommandLineTar/utf-8-paths.cmake new file mode 100644 index 0000000..af85ffa --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/utf-8-paths.cmake
@@ -0,0 +1,10 @@ +list(APPEND COMPRESSION_ADDITIONAL_FILE_NAMES + "arabic-نَسِيج.txt" + "armenian-Բարեւ ձեզ.txt" + "chinese-质地.txt" + "cyrillic-привет.txt" + "greek-Γεια.txt" + "hungarian-hűtő.txt" + "japanese-テクスチャ.txt" + "korean-조직.txt" +)
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt index d34e514..cb3ca0f 100644 --- a/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt +++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0128WarnMatch-(C|CXX)\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0128WarnMatch-(C|CXX)\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0128 will be removed from a future version of CMake\. @@ -8,12 +8,15 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0128 is not set: Selection of language standard and extension flags improved\. Run "cmake --help-policy CMP0128" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. For compatibility with older versions of CMake, unnecessary flags for language standard or compiler extensions may be added\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt index 37a0767..ccc2329 100644 --- a/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt +++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0128WarnUnset-(C|CXX)\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0128WarnUnset-(C|CXX)\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0128 will be removed from a future version of CMake\. @@ -8,12 +8,15 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0128 is not set: Selection of language standard and extension flags improved\. Run "cmake --help-policy CMP0128" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. For compatibility with older versions of CMake, compiler extensions won't be @opposite@\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/target_compile_features/cxx_not_enabled-result.txt b/Tests/RunCMake/CompileFeatures/NotEnabled-result.txt similarity index 100% rename from Tests/RunCMake/target_compile_features/cxx_not_enabled-result.txt rename to Tests/RunCMake/CompileFeatures/NotEnabled-result.txt
diff --git a/Tests/RunCMake/CompileFeatures/NotEnabled-stderr.txt b/Tests/RunCMake/CompileFeatures/NotEnabled-stderr.txt new file mode 100644 index 0000000..999778d --- /dev/null +++ b/Tests/RunCMake/CompileFeatures/NotEnabled-stderr.txt
@@ -0,0 +1,8 @@ +^CMake Error at NotEnabled.cmake:[0-9]+ \(target_compile_definitions\): + Error evaluating generator expression: + + \$<COMPILE_FEATURES:c_std_99> + + cannot use features from non-enabled language C +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CompileFeatures/NotEnabled.cmake b/Tests/RunCMake/CompileFeatures/NotEnabled.cmake new file mode 100644 index 0000000..6b8a362 --- /dev/null +++ b/Tests/RunCMake/CompileFeatures/NotEnabled.cmake
@@ -0,0 +1,2 @@ +add_library(empty empty.cpp) +target_compile_definitions(empty PRIVATE FOO=$<COMPILE_FEATURES:c_std_99>)
diff --git a/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake b/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake index 508662e..b990a51 100644 --- a/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake +++ b/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake
@@ -7,6 +7,7 @@ run_cmake(NotAFeature_OriginDebugGenex) run_cmake(NotAFeature_OriginDebugTransitive) run_cmake(NotAFeature_OriginDebugCommand) +run_cmake(NotEnabled) run_cmake(compiler_introspection) include("${RunCMake_BINARY_DIR}/compiler_introspection-build/info.cmake")
diff --git a/Tests/RunCMake/CompilerLauncher/C-launch-Build-check.cmake b/Tests/RunCMake/CompilerLauncher/C-launch-Build-check.cmake new file mode 100644 index 0000000..ac76a86 --- /dev/null +++ b/Tests/RunCMake/CompilerLauncher/C-launch-Build-check.cmake
@@ -0,0 +1,21 @@ +if(RunCMake_GENERATOR STREQUAL "Ninja") + set(rules_ninja "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/rules.ninja") + set(build_ninja "${RunCMake_TEST_BINARY_DIR}/build.ninja") + + file(READ "${rules_ninja}" ninja_content) + file(READ "${build_ninja}" build_ninja_content) + + if(NOT ninja_content MATCHES "command = \"[^\"]*ctest(\.exe)?\" --launch") + set(RunCMake_TEST_FAILED + "Expected compile command to use ctest --launch: ${ninja_content}" + ) + return() + endif() + + if(NOT build_ninja_content MATCHES "LAUNCHER = [^\n]*USED_LAUNCHER=1") + set(RunCMake_TEST_FAILED + "Expected compile command to include the compiler launcher" + ) + return() + endif() +endif()
diff --git a/Tests/RunCMake/Configure/RunCMakeTest.cmake b/Tests/RunCMake/Configure/RunCMakeTest.cmake index b29640a..0b65e8b 100644 --- a/Tests/RunCMake/Configure/RunCMakeTest.cmake +++ b/Tests/RunCMake/Configure/RunCMakeTest.cmake
@@ -73,12 +73,6 @@ run_cmake(NoCMAKE_DEFAULT_CONFIGS) endif() -block() - run_cmake(CacheVarHelpString) - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake(CacheVarHelpString) -endblock() - if(NOT CMAKE_HOST_WIN32) block() # Test a non-writable build directory.
diff --git a/Tests/RunCMake/DiagnosticScope/Block.cmake b/Tests/RunCMake/DiagnosticScope/Block.cmake new file mode 100644 index 0000000..a69324d --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/Block.cmake
@@ -0,0 +1,22 @@ +cmake_diagnostic(SET CMD_AUTHOR IGNORE) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "failed to set diagnostic state") +endif() + +block(SCOPE_FOR DIAGNOSTICS) + +cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "failed to set diagnostic state") +endif() + +endblock() + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "failed to restore diagnostic state") +endif()
diff --git a/Tests/RunCMake/DiagnosticScope/CMakeLists.txt b/Tests/RunCMake/DiagnosticScope/CMakeLists.txt new file mode 100644 index 0000000..d383479 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake NO_DIAGNOSTIC_SCOPE)
diff --git a/Tests/RunCMake/DiagnosticScope/DiagnosticInclude.cmake b/Tests/RunCMake/DiagnosticScope/DiagnosticInclude.cmake new file mode 100644 index 0000000..1d9d748 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/DiagnosticInclude.cmake
@@ -0,0 +1,6 @@ +cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "failed to set diagnostic state") +endif()
diff --git a/Tests/RunCMake/DiagnosticScope/IncludeWithScope.cmake b/Tests/RunCMake/DiagnosticScope/IncludeWithScope.cmake new file mode 100644 index 0000000..2de0db5 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/IncludeWithScope.cmake
@@ -0,0 +1,7 @@ +cmake_diagnostic(SET CMD_AUTHOR WARN) +include(DiagnosticInclude.cmake) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "WARN") + message(SEND_ERROR "include unexpectedly leaked diagnostic state") +endif()
diff --git a/Tests/RunCMake/DiagnosticScope/IncludeWithoutScope.cmake b/Tests/RunCMake/DiagnosticScope/IncludeWithoutScope.cmake new file mode 100644 index 0000000..fa71e3f --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/IncludeWithoutScope.cmake
@@ -0,0 +1,7 @@ +cmake_diagnostic(SET CMD_AUTHOR WARN) +include(DiagnosticInclude.cmake NO_DIAGNOSTIC_SCOPE) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "include unexpectedly encapsulated diagnostic state") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/DiagnosticScope/NotClosed-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/DiagnosticScope/NotClosed-result.txt
diff --git a/Tests/RunCMake/DiagnosticScope/NotClosed-stderr.txt b/Tests/RunCMake/DiagnosticScope/NotClosed-stderr.txt new file mode 100644 index 0000000..27dc157 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotClosed-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error in NotClosed\.cmake: + cmake_diagnostic PUSH without matching POP +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/DiagnosticScope/NotClosed.cmake b/Tests/RunCMake/DiagnosticScope/NotClosed.cmake new file mode 100644 index 0000000..17d62bc --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotClosed.cmake
@@ -0,0 +1 @@ +cmake_diagnostic(PUSH)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/DiagnosticScope/NotOpened-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/DiagnosticScope/NotOpened-result.txt
diff --git a/Tests/RunCMake/DiagnosticScope/NotOpened-stderr.txt b/Tests/RunCMake/DiagnosticScope/NotOpened-stderr.txt new file mode 100644 index 0000000..bdd1003 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotOpened-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at NotOpened\.cmake:[0-9]+ \(cmake_diagnostic\): + cmake_diagnostic POP without matching PUSH +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/DiagnosticScope/NotOpened.cmake b/Tests/RunCMake/DiagnosticScope/NotOpened.cmake new file mode 100644 index 0000000..be5e5f8 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotOpened.cmake
@@ -0,0 +1 @@ +cmake_diagnostic(POP)
diff --git a/Tests/RunCMake/DiagnosticScope/PushPop.cmake b/Tests/RunCMake/DiagnosticScope/PushPop.cmake new file mode 100644 index 0000000..055506b --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/PushPop.cmake
@@ -0,0 +1,22 @@ +cmake_diagnostic(SET CMD_AUTHOR IGNORE) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "failed to set diagnostic state") +endif() + +cmake_diagnostic(PUSH) + +cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "failed to set diagnostic state") +endif() + +cmake_diagnostic(POP) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "failed to restore diagnostic state") +endif()
diff --git a/Tests/RunCMake/DiagnosticScope/RunCMakeTest.cmake b/Tests/RunCMake/DiagnosticScope/RunCMakeTest.cmake new file mode 100644 index 0000000..ed6a4a0 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/RunCMakeTest.cmake
@@ -0,0 +1,8 @@ +include(RunCMake) + +run_cmake(PushPop) +run_cmake(Block) +run_cmake(IncludeWithScope) +run_cmake(IncludeWithoutScope) +run_cmake(NotClosed) +run_cmake(NotOpened)
diff --git a/Tests/RunCMake/Diagnostics/Actions.cmake b/Tests/RunCMake/Diagnostics/Actions.cmake new file mode 100644 index 0000000..06a1e5a --- /dev/null +++ b/Tests/RunCMake/Diagnostics/Actions.cmake
@@ -0,0 +1,74 @@ +function(run_test ACTION INIT TARGET EXPECTED) + cmake_diagnostic(SET CMD_DEPRECATED ${INIT}) + + cmake_diagnostic(GET CMD_DEPRECATED action) + if(NOT "${action}" STREQUAL "${INIT}") + message(SEND_ERROR "failed to set diagnostic state") + endif() + + cmake_diagnostic(${ACTION} CMD_AUTHOR ${TARGET} RECURSE) + + cmake_diagnostic(GET CMD_DEPRECATED action) + if(NOT "${action}" STREQUAL "${EXPECTED}") + message(SEND_ERROR + "failed to change diagnostic state" + " (expected '${EXPECTED}', actual '${action}')" + ) + endif() +endfunction() + +# Run tests for altering a diagnostic from a known state +run_test(SET IGNORE WARN WARN) +run_test(SET IGNORE SEND_ERROR SEND_ERROR) +run_test(SET IGNORE FATAL_ERROR FATAL_ERROR) + +run_test(PROMOTE IGNORE IGNORE IGNORE) +run_test(PROMOTE IGNORE WARN WARN) +run_test(PROMOTE IGNORE SEND_ERROR SEND_ERROR) +run_test(PROMOTE IGNORE FATAL_ERROR FATAL_ERROR) + +run_test(PROMOTE WARN IGNORE WARN) +run_test(PROMOTE WARN WARN WARN) +run_test(PROMOTE WARN SEND_ERROR SEND_ERROR) +run_test(PROMOTE WARN FATAL_ERROR FATAL_ERROR) + +run_test(PROMOTE SEND_ERROR IGNORE SEND_ERROR) +run_test(PROMOTE SEND_ERROR WARN SEND_ERROR) +run_test(PROMOTE SEND_ERROR SEND_ERROR SEND_ERROR) +run_test(PROMOTE SEND_ERROR FATAL_ERROR FATAL_ERROR) + +run_test(PROMOTE FATAL_ERROR IGNORE FATAL_ERROR) +run_test(PROMOTE FATAL_ERROR WARN FATAL_ERROR) +run_test(PROMOTE FATAL_ERROR SEND_ERROR FATAL_ERROR) +run_test(PROMOTE FATAL_ERROR FATAL_ERROR FATAL_ERROR) + +run_test(DEMOTE IGNORE IGNORE IGNORE) +run_test(DEMOTE IGNORE WARN IGNORE) +run_test(DEMOTE IGNORE SEND_ERROR IGNORE) +run_test(DEMOTE IGNORE FATAL_ERROR IGNORE) + +run_test(DEMOTE WARN IGNORE IGNORE) +run_test(DEMOTE WARN WARN WARN) +run_test(DEMOTE WARN SEND_ERROR WARN) +run_test(DEMOTE WARN FATAL_ERROR WARN) + +run_test(DEMOTE SEND_ERROR IGNORE IGNORE) +run_test(DEMOTE SEND_ERROR WARN WARN) +run_test(DEMOTE SEND_ERROR SEND_ERROR SEND_ERROR) +run_test(DEMOTE SEND_ERROR FATAL_ERROR SEND_ERROR) + +run_test(DEMOTE FATAL_ERROR IGNORE IGNORE) +run_test(DEMOTE FATAL_ERROR WARN WARN) +run_test(DEMOTE FATAL_ERROR SEND_ERROR SEND_ERROR) +run_test(DEMOTE FATAL_ERROR FATAL_ERROR FATAL_ERROR) + +# Ensure that altering a diagnostic that is still in the default state +# uses the default state as the basis for alteration +cmake_diagnostic(DEMOTE CMD_UNINITIALIZED WARN) + +cmake_diagnostic(GET CMD_UNINITIALIZED action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR + "CMD_UNINITIALIZED has unexpected state '${action}' (expected 'IGNORE')" + ) +endif()
diff --git a/Tests/RunCMake/Diagnostics/Assertions.cmake b/Tests/RunCMake/Diagnostics/Assertions.cmake new file mode 100644 index 0000000..c771b5b --- /dev/null +++ b/Tests/RunCMake/Diagnostics/Assertions.cmake
@@ -0,0 +1,19 @@ +function(expect CATEGORY EXPECTED) + cmake_diagnostic(GET ${CATEGORY} actual) + if(NOT "${actual}" STREQUAL "${EXPECTED}") + message(SEND_ERROR + "wrong action for diagnostic ${CATEGORY}" + " (expected '${EXPECTED}', actual '${actual}')") + endif() +endfunction() + +function(expect_cached CATEGORY EXPECTED) + expect(${CATEGORY} ${EXPECTED}) + + if(NOT "${CATEGORY}=${EXPECTED}" IN_LIST CMAKE_DIAGNOSTIC_INIT) + list(JOIN CMAKE_DIAGNOSTIC_INIT ", " pretty_state) + message(SEND_ERROR + "Cached state for ${CATEGORY} missing or incorrect" + " (expected '${EXPECTED}' in ${pretty_state})") + endif() +endfunction()
diff --git a/Tests/RunCMake/Diagnostics/CMakeLists.txt b/Tests/RunCMake/Diagnostics/CMakeLists.txt new file mode 100644 index 0000000..d383479 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake NO_DIAGNOSTIC_SCOPE)
diff --git a/Tests/RunCMake/Diagnostics/CacheInit.cmake b/Tests/RunCMake/Diagnostics/CacheInit.cmake new file mode 100644 index 0000000..24baac2 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/CacheInit.cmake
@@ -0,0 +1,5 @@ +include(Assertions.cmake) + +expect(CMD_AUTHOR IGNORE) +expect(CMD_INSTALL_ABSOLUTE_DESTINATION IGNORE) +expect(CMD_DEPRECATED SEND_ERROR)
diff --git a/Tests/RunCMake/Diagnostics/CommandLine1.cmake b/Tests/RunCMake/Diagnostics/CommandLine1.cmake new file mode 100644 index 0000000..72380c3 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/CommandLine1.cmake
@@ -0,0 +1,4 @@ +include(Assertions.cmake) + +expect_cached(CMD_AUTHOR SEND_ERROR) +expect_cached(CMD_DEPRECATED SEND_ERROR)
diff --git a/Tests/RunCMake/Diagnostics/CommandLine2.cmake b/Tests/RunCMake/Diagnostics/CommandLine2.cmake new file mode 100644 index 0000000..90e4741 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/CommandLine2.cmake
@@ -0,0 +1,4 @@ +include(Assertions.cmake) + +expect_cached(CMD_AUTHOR SEND_ERROR) +expect_cached(CMD_DEPRECATED IGNORE)
diff --git a/Tests/RunCMake/Diagnostics/CommandLine3.cmake b/Tests/RunCMake/Diagnostics/CommandLine3.cmake new file mode 100644 index 0000000..96f8e1a --- /dev/null +++ b/Tests/RunCMake/Diagnostics/CommandLine3.cmake
@@ -0,0 +1,5 @@ +include(Assertions.cmake) + +expect_cached(CMD_UNINITIALIZED IGNORE) + +expect_cached(CMD_INSTALL_ABSOLUTE_DESTINATION WARN)
diff --git a/Tests/RunCMake/Diagnostics/ExistenceCheck.cmake b/Tests/RunCMake/Diagnostics/ExistenceCheck.cmake new file mode 100644 index 0000000..b2bbff8 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/ExistenceCheck.cmake
@@ -0,0 +1,7 @@ +if(NOT DIAGNOSTIC CMD_AUTHOR) + message(SEND_ERROR "existence check for CMD_AUTHOR failed?!") +endif() + +if(DIAGNOSTIC BOGUS) + message(SEND_ERROR "existence check for bogus category succeeded?!") +endif()
diff --git a/Tests/RunCMake/Diagnostics/Persist1.cmake b/Tests/RunCMake/Diagnostics/Persist1.cmake new file mode 100644 index 0000000..46c7215 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/Persist1.cmake
@@ -0,0 +1,4 @@ +include(Assertions.cmake) + +expect_cached(CMD_AUTHOR IGNORE) +expect_cached(CMD_DEPRECATED IGNORE)
diff --git a/Tests/RunCMake/Diagnostics/Persist2.cmake b/Tests/RunCMake/Diagnostics/Persist2.cmake new file mode 100644 index 0000000..161d5ed --- /dev/null +++ b/Tests/RunCMake/Diagnostics/Persist2.cmake
@@ -0,0 +1,4 @@ +include(Assertions.cmake) + +expect_cached(CMD_AUTHOR WARN) +expect_cached(CMD_DEPRECATED WARN)
diff --git a/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake b/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake new file mode 100644 index 0000000..630d182 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake
@@ -0,0 +1,31 @@ +include(RunCMake) + +run_cmake(Actions) +run_cmake(ExistenceCheck) + +run_cmake_with_options( + CacheInit + "-DCMAKE_DIAGNOSTIC_INIT=CMD_AUTHOR=IGNORE\;CMD_DEPRECATED=SEND_ERROR" +) + +run_cmake_with_options(CommandLine1 -Wno-author -Werror=author) +run_cmake_with_options(CommandLine1 -Werror=author) +run_cmake_with_options(CommandLine1 -Werror=author -Wdeprecated) +run_cmake_with_options(CommandLine1 -Wno-deprecated -Werror=author) +run_cmake_with_options(CommandLine2 -Werror=author -Wno-deprecated) +run_cmake_with_options(CommandLine3 -Wno-error=uninitialized -Winstall-absolute-destination) + +function(run_persist_test NAME) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-first") + run_cmake_with_options(${NAME} ${ARGN}) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-second") + run_cmake(${NAME}) +endfunction() + +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Persist-build) + run_persist_test(Persist1 -Wno-author) + set(RunCMake_TEST_NO_CLEAN 1) + run_persist_test(Persist2 -Wauthor) +endblock()
diff --git a/Tests/RunCMake/EnvSbom/PartialCoverage-install-check.cmake b/Tests/RunCMake/EnvSbom/PartialCoverage-install-check.cmake new file mode 100644 index 0000000..f33fa32 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/PartialCoverage-install-check.cmake
@@ -0,0 +1,24 @@ +set(failures "") + +foreach(f explicit_root_sbom explicit_subdir_sbom) + if(NOT EXISTS "${RunCMake_TEST_INSTALL_DIR}/${f}.spdx.json") + list(APPEND failures "expected explicit SBOM ${f}.spdx.json to exist") + endif() +endforeach() + +foreach(f implicit_root implicit_subdir) + if(NOT EXISTS "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/test_project/${f}.spdx.json") + list(APPEND failures "expected autogen SBOM ${f}.spdx.json to exist") + endif() +endforeach() + +foreach(f explicit_root explicit_subdir) + if(EXISTS "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/test_project/${f}.spdx.json") + list(APPEND failures "autogen wrongly produced ${f}.spdx.json (should be suppressed by explicit install(SBOM))") + endif() +endforeach() + +if(failures) + string(REPLACE ";" "\n " msg "${failures}") + set(RunCMake_TEST_FAILED "${msg}") +endif()
diff --git a/Tests/RunCMake/EnvSbom/PartialCoverage.cmake b/Tests/RunCMake/EnvSbom/PartialCoverage.cmake new file mode 100644 index 0000000..cc49d14 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/PartialCoverage.cmake
@@ -0,0 +1,12 @@ +project(test_project) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Setup.cmake) + +add_library(libA INTERFACE) +add_library(libB INTERFACE) + +install(TARGETS libA EXPORT explicit_root DESTINATION .) +install(TARGETS libB EXPORT implicit_root DESTINATION .) + +install(SBOM explicit_root_sbom EXPORTS explicit_root DESTINATION .) + +add_subdirectory(PartialCoverage_subdir)
diff --git a/Tests/RunCMake/EnvSbom/PartialCoverage_subdir/CMakeLists.txt b/Tests/RunCMake/EnvSbom/PartialCoverage_subdir/CMakeLists.txt new file mode 100644 index 0000000..aa5cb6e --- /dev/null +++ b/Tests/RunCMake/EnvSbom/PartialCoverage_subdir/CMakeLists.txt
@@ -0,0 +1,7 @@ +add_library(libC INTERFACE) +add_library(libD INTERFACE) + +install(TARGETS libC EXPORT explicit_subdir DESTINATION .) +install(TARGETS libD EXPORT implicit_subdir DESTINATION .) + +install(SBOM explicit_subdir_sbom EXPORTS explicit_subdir DESTINATION .)
diff --git a/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-install-check.cmake b/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-install-check.cmake deleted file mode 100644 index d00e3c0..0000000 --- a/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-install-check.cmake +++ /dev/null
@@ -1,2 +0,0 @@ -file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/test_project/dog.spdx.json" content) -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget-install-check.cmake)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-result.txt
diff --git a/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-stderr.txt b/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-stderr.txt new file mode 100644 index 0000000..3dfbdb6 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-stderr.txt
@@ -0,0 +1,6 @@ +CMake Error in CMakeLists\.txt: + Target "canine" references target "mammal" which has no + install\(EXPORT\)/export\(EXPORT\) namespace and is not covered by any SBOM\. + An SBOM cannot attribute this dependency\. Give "mammal" an + install\(EXPORT\)/export\(EXPORT\) with a NAMESPACE, or include it in an + install\(SBOM\)/export\(SBOM\)\.
diff --git a/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake b/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake index f72942d..a188fcc 100644 --- a/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake +++ b/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake
@@ -1,12 +1,21 @@ include(RunCMake) set(common_test_options - -Wno-dev - "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=ca494ed3-b261-4205-a01f-603c95e4cae0" + -Wno-author + "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317" "-DCMAKE_INSTALL_SBOM_FORMATS:STRING=JSON" "-DCMAKE_INSTALL_LIBDIR=lib" ) +function(run_cmake_error test) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_OPTIONS ${common_test_options}) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=DEBUG) + endif() + run_cmake(${test}) +endfunction() + function(run_cmake_install test) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) @@ -19,7 +28,9 @@ run_cmake(${test}) set(RunCMake_TEST_NO_CLEAN TRUE) + set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Debug) + unset(RunCMake_TEST_OUTPUT_MERGE) run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug) endfunction() @@ -28,5 +39,7 @@ run_cmake_install(SharedTarget) run_cmake_install(MissingPackageNamespace) -run_cmake_install(ReferencesNonExportedTarget) run_cmake_install(ProjectMetadata) +run_cmake_install(PartialCoverage) + +run_cmake_error(ReferencesNonExportedTarget)
diff --git a/Tests/RunCMake/ExportPackageInfo/PrivateLinkDependency-check.cmake b/Tests/RunCMake/ExportPackageInfo/PrivateLinkDependency-check.cmake new file mode 100644 index 0000000..9b6ef52 --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/PrivateLinkDependency-check.cmake
@@ -0,0 +1,15 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/PrivateLinkDependency-build") + +file(READ "${out_dir}/cps/mylib/mylib.cps" content) + +# Private link dependency must appear in package requires +expect_array("${content}" 1 "requires" "libdep" "components") +expect_value("${content}" "libdep" "requires" "libdep" "components" 0) + +# And in per-config dyld_requires +expect_array("${content}" 1 + "components" "mylib" "configurations" "generic" "dyld_requires") +expect_value("${content}" "libdep:libdep" + "components" "mylib" "configurations" "generic" "dyld_requires" 0)
diff --git a/Tests/RunCMake/ExportPackageInfo/PrivateLinkDependency.cmake b/Tests/RunCMake/ExportPackageInfo/PrivateLinkDependency.cmake new file mode 100644 index 0000000..cb81e84 --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/PrivateLinkDependency.cmake
@@ -0,0 +1,20 @@ +project(PrivateLinkDependency CXX) + +get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(_isMultiConfig) + set(CMAKE_CONFIGURATION_TYPES "generic" CACHE STRING "" FORCE) +else() + set(CMAKE_BUILD_TYPE "generic" CACHE STRING "" FORCE) +endif() + +find_package( + libdep REQUIRED CONFIG + NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_LIST_DIR} +) + +add_library(mylib SHARED foo.cxx) +target_link_libraries(mylib PRIVATE libdep::libdep) + +install(TARGETS mylib EXPORT mylib DESTINATION .) +export(PACKAGE_INFO mylib EXPORT mylib)
diff --git a/Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake b/Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake index e2582cf..16242ca 100644 --- a/Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake
@@ -29,6 +29,7 @@ run_cmake(LowerCaseFile) run_cmake(Requirements) run_cmake(LinkDependentLibraries) +run_cmake(PrivateLinkDependency) run_cmake(ExportSymbolicComponent) run_cmake(TargetTypes) run_cmake(DependsMultiple)
diff --git a/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning-stderr.txt b/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning-stderr.txt index 0e833c2..d4702fe 100644 --- a/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning-stderr.txt +++ b/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning-stderr.txt
@@ -1,3 +1,5 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(author\) at VersionCheckWarning\.cmake:[0-9]+ \(export\): Package "foo" uses unrecognized version schema "unrecognized"\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/ExportPackageInfo/cps/libdep/libdep.cps b/Tests/RunCMake/ExportPackageInfo/cps/libdep/libdep.cps new file mode 100644 index 0000000..7e9feac --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/cps/libdep/libdep.cps
@@ -0,0 +1,12 @@ +{ + "components" : + { + "libdep" : + { + "type" : "dylib" + } + }, + "cps_path" : "@prefix@/cps/libdep", + "cps_version" : "0.14.1", + "name" : "libdep" +}
diff --git a/Tests/RunCMake/ExportPackageInfo/cps/libdep/libdep@generic.cps b/Tests/RunCMake/ExportPackageInfo/cps/libdep/libdep@generic.cps new file mode 100644 index 0000000..5e529ee --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/cps/libdep/libdep@generic.cps
@@ -0,0 +1,12 @@ +{ + "components" : + { + "libdep" : + { + "link_location" : "@prefix@/libdep.lib", + "location" : "@prefix@/libdep.dll" + } + }, + "configuration" : "generic", + "name" : "libdep" +}
diff --git a/Tests/RunCMake/ExportSbom/ApplicationTarget.cmake b/Tests/RunCMake/ExportSbom/ApplicationTarget.cmake index 39b0c29..fafc73c 100644 --- a/Tests/RunCMake/ExportSbom/ApplicationTarget.cmake +++ b/Tests/RunCMake/ExportSbom/ApplicationTarget.cmake
@@ -2,6 +2,6 @@ export( SBOM application_targets - EXPORT application_targets + EXPORTS application_targets FORMAT "spdx-3.0+json" )
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/ExportSbom/DuplicateSbom-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/ExportSbom/DuplicateSbom-result.txt
diff --git a/Tests/RunCMake/ExportSbom/DuplicateSbom-stderr.txt b/Tests/RunCMake/ExportSbom/DuplicateSbom-stderr.txt new file mode 100644 index 0000000..c0d4b12 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/DuplicateSbom-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at DuplicateSbom\.cmake:[0-9]+ \(export\): + export SBOM command already specified for the file mySbom\.spdx\.json\.
diff --git a/Tests/RunCMake/ExportSbom/DuplicateSbom.cmake b/Tests/RunCMake/ExportSbom/DuplicateSbom.cmake new file mode 100644 index 0000000..ab51460 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/DuplicateSbom.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DuplicateSbom.cmake) + +export(EXPORT setA FILE setA.cmake) +export(SBOM mySbom EXPORTS setA) +export(SBOM mySbom EXPORTS setA)
diff --git a/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback-install-check.cmake b/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback-install-check.cmake new file mode 100644 index 0000000..ce8270e --- /dev/null +++ b/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback-install-check.cmake
@@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/mySbom/mySbom.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/EmptyNamespaceFallback-install-check.cmake)
diff --git a/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback-stderr.txt b/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback-stderr.txt new file mode 100644 index 0000000..c6d0d96 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback-stderr.txt
@@ -0,0 +1,4 @@ +CMake Warning \(author\) in CMakeLists\.txt: + Target "libb" references target "liba", whose export does not use the + standard namespace separator\. The dependency will be recorded by its bare + target name without provenance\.
diff --git a/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback.cmake b/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback.cmake new file mode 100644 index 0000000..99ade04 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/EmptyNamespaceFallback.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/EmptyNamespaceFallback.cmake) + +export(EXPORT setA FILE setA.cmake) +export(EXPORT setB NAMESPACE B:: FILE setB.cmake) +export(SBOM mySbom EXPORTS setB)
diff --git a/Tests/RunCMake/ExportSbom/InstallExportPlusSbomSameSet.cmake b/Tests/RunCMake/ExportSbom/InstallExportPlusSbomSameSet.cmake new file mode 100644 index 0000000..5bf947f --- /dev/null +++ b/Tests/RunCMake/ExportSbom/InstallExportPlusSbomSameSet.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/InstallExportPlusSbomSameSet.cmake) + +export(EXPORT export_set_a NAMESPACE A:: FILE export_set_a.cmake) +export(SBOM mySbom EXPORTS export_set_a) +export(EXPORT export_set_b FILE export_set_b.cmake)
diff --git a/Tests/RunCMake/ExportSbom/InterfaceTarget.cmake b/Tests/RunCMake/ExportSbom/InterfaceTarget.cmake index 5bf0678..470b672 100644 --- a/Tests/RunCMake/ExportSbom/InterfaceTarget.cmake +++ b/Tests/RunCMake/ExportSbom/InterfaceTarget.cmake
@@ -2,5 +2,5 @@ export( SBOM interface_targets - EXPORT interface_targets + EXPORTS interface_targets )
diff --git a/Tests/RunCMake/ExportSbom/MissingPackageNamespace-install-check.cmake b/Tests/RunCMake/ExportSbom/MissingPackageNamespace-install-check.cmake index 842e77e..2583d26 100644 --- a/Tests/RunCMake/ExportSbom/MissingPackageNamespace-install-check.cmake +++ b/Tests/RunCMake/ExportSbom/MissingPackageNamespace-install-check.cmake
@@ -1,2 +1,2 @@ -file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/interface_targets/interface_targets.spdx.json" content) +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/test_targets/test_targets.spdx.json" content) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MissingPackageNamespace-install-check.cmake)
diff --git a/Tests/RunCMake/ExportSbom/MissingPackageNamespace.cmake b/Tests/RunCMake/ExportSbom/MissingPackageNamespace.cmake index 235f3a7..d478765 100644 --- a/Tests/RunCMake/ExportSbom/MissingPackageNamespace.cmake +++ b/Tests/RunCMake/ExportSbom/MissingPackageNamespace.cmake
@@ -1,7 +1,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MissingPackageNamespace.cmake) export( - SBOM interface_targets - EXPORT test_targets + SBOM test_targets + EXPORTS test_targets VERSION 1.0.2 )
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/ExportSbom/MultiNamespaceAmbiguity-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/ExportSbom/MultiNamespaceAmbiguity-result.txt
diff --git a/Tests/RunCMake/ExportSbom/MultiNamespaceAmbiguity-stderr.txt b/Tests/RunCMake/ExportSbom/MultiNamespaceAmbiguity-stderr.txt new file mode 100644 index 0000000..9be0fed --- /dev/null +++ b/Tests/RunCMake/ExportSbom/MultiNamespaceAmbiguity-stderr.txt
@@ -0,0 +1,7 @@ +CMake Error in CMakeLists\.txt: + Target "libb" references target "liba" that is in an export set which is + exported multiple times with different namespaces:[^,]+setA-A\.cmake,[^,]+setA-OldA\.cmake\. + + An SBOM cannot attribute a dependency exported in more than one export set + or with more than one namespace\. Consider consolidating the exports of the + "liba" target to a single export\.
diff --git a/Tests/RunCMake/ExportSbom/MultiNamespaceAmbiguity.cmake b/Tests/RunCMake/ExportSbom/MultiNamespaceAmbiguity.cmake new file mode 100644 index 0000000..0715424 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/MultiNamespaceAmbiguity.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiNamespaceAmbiguity.cmake) + +export(EXPORT setA NAMESPACE A:: FILE setA-A.cmake) +export(EXPORT setA NAMESPACE OldA:: FILE setA-OldA.cmake) +export(SBOM mySbom EXPORTS setB)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/ExportSbom/MultiSetAmbiguity-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/ExportSbom/MultiSetAmbiguity-result.txt
diff --git a/Tests/RunCMake/ExportSbom/MultiSetAmbiguity-stderr.txt b/Tests/RunCMake/ExportSbom/MultiSetAmbiguity-stderr.txt new file mode 100644 index 0000000..69837c2 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/MultiSetAmbiguity-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error in CMakeLists\.txt: + Target "libb" references target "liba" that is in multiple export sets: + .*/setA1\.cmake, + .*/setA2\.cmake\. + + An SBOM cannot attribute a dependency exported in more than one export set + or with more than one namespace\. Consider consolidating the exports of the + "liba" target to a single export\.
diff --git a/Tests/RunCMake/ExportSbom/MultiSetAmbiguity.cmake b/Tests/RunCMake/ExportSbom/MultiSetAmbiguity.cmake new file mode 100644 index 0000000..f247f3b --- /dev/null +++ b/Tests/RunCMake/ExportSbom/MultiSetAmbiguity.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetAmbiguity.cmake) + +export(EXPORT setA1 NAMESPACE A1:: FILE setA1.cmake) +export(EXPORT setA2 NAMESPACE A2:: FILE setA2.cmake) +export(SBOM mySbom EXPORTS setB)
diff --git a/Tests/RunCMake/ExportSbom/MultiSetSingleSbom-install-check.cmake b/Tests/RunCMake/ExportSbom/MultiSetSingleSbom-install-check.cmake new file mode 100644 index 0000000..791c4cb --- /dev/null +++ b/Tests/RunCMake/ExportSbom/MultiSetSingleSbom-install-check.cmake
@@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/mySbom/mySbom.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetSingleSbom-install-check.cmake)
diff --git a/Tests/RunCMake/ExportSbom/MultiSetSingleSbom.cmake b/Tests/RunCMake/ExportSbom/MultiSetSingleSbom.cmake new file mode 100644 index 0000000..65eccc0 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/MultiSetSingleSbom.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetSingleSbom.cmake) + +export(EXPORT setA NAMESPACE A:: FILE setA.cmake) +export(EXPORT setB NAMESPACE B:: FILE setB.cmake) +export(SBOM mySbom EXPORTS setA setB)
diff --git a/Tests/RunCMake/ExportSbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/ExportSbom/ProjectMetadata-install-check.cmake index 34e57af..502cd43 100644 --- a/Tests/RunCMake/ExportSbom/ProjectMetadata-install-check.cmake +++ b/Tests/RunCMake/ExportSbom/ProjectMetadata-install-check.cmake
@@ -1,2 +1,3 @@ file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/test_targets/test_targets.spdx.json" content) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadataExplicitAssertions.cmake)
diff --git a/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake b/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake index ed96780..02b5ce3 100644 --- a/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake +++ b/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake
@@ -2,9 +2,10 @@ export( SBOM test_targets - EXPORT test_targets + EXPORTS test_targets DESCRIPTION "An eloquent description" LICENSE "BSD-3" HOMEPAGE_URL "www.example.com" + PACKAGE_URL "https://example.com/test_targets.tar.gz" VERSION "1.3.4" )
diff --git a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-install-check.cmake b/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-install-check.cmake deleted file mode 100644 index 465fcf0..0000000 --- a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-install-check.cmake +++ /dev/null
@@ -1,2 +0,0 @@ -file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/dog/dog.spdx.json" content) -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget-install-check.cmake)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-result.txt
diff --git a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-stderr.txt b/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-stderr.txt new file mode 100644 index 0000000..3dfbdb6 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-stderr.txt
@@ -0,0 +1,6 @@ +CMake Error in CMakeLists\.txt: + Target "canine" references target "mammal" which has no + install\(EXPORT\)/export\(EXPORT\) namespace and is not covered by any SBOM\. + An SBOM cannot attribute this dependency\. Give "mammal" an + install\(EXPORT\)/export\(EXPORT\) with a NAMESPACE, or include it in an + install\(SBOM\)/export\(SBOM\)\.
diff --git a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget.cmake b/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget.cmake index dba5762..9c06ac8 100644 --- a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget.cmake +++ b/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget.cmake
@@ -1,3 +1,3 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget.cmake) -export(SBOM dog EXPORT dog) +export(SBOM dog EXPORTS dog)
diff --git a/Tests/RunCMake/ExportSbom/Requirements-install-check.cmake b/Tests/RunCMake/ExportSbom/Requirements-install-check.cmake index 5cae65a..e0de7e7 100644 --- a/Tests/RunCMake/ExportSbom/Requirements-install-check.cmake +++ b/Tests/RunCMake/ExportSbom/Requirements-install-check.cmake
@@ -1,3 +1,3 @@ -file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/bar/bar.spdx.json" BAR_CONTENT) -file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo.spdx.json" FOO_CONTENT) +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/bar_sbom/bar_sbom.spdx.json" BAR_CONTENT) +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo_sbom/foo_sbom.spdx.json" FOO_CONTENT) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements-install-check.cmake)
diff --git a/Tests/RunCMake/ExportSbom/Requirements.cmake b/Tests/RunCMake/ExportSbom/Requirements.cmake index 41b38a7..756e691 100644 --- a/Tests/RunCMake/ExportSbom/Requirements.cmake +++ b/Tests/RunCMake/ExportSbom/Requirements.cmake
@@ -1,4 +1,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements.cmake) -export(SBOM foo EXPORT foo) -export(SBOM bar EXPORT bar) +export(EXPORT foo NAMESPACE foo:: FILE foo.cmake) +export(SBOM foo_sbom EXPORTS foo) +export(SBOM bar_sbom EXPORTS bar)
diff --git a/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake b/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake index 5e09f91..244b349 100644 --- a/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake
@@ -1,10 +1,19 @@ include(RunCMake) set(common_test_options - -Wno-dev - "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=ca494ed3-b261-4205-a01f-603c95e4cae0" + -Wno-author + "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317" ) +function(run_cmake_error test) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_OPTIONS ${common_test_options}) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=DEBUG) + endif() + run_cmake(${test}) +endfunction() + function(run_cmake_install test) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) @@ -17,14 +26,27 @@ run_cmake(${test}) set(RunCMake_TEST_NO_CLEAN TRUE) + set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Debug) + unset(RunCMake_TEST_OUTPUT_MERGE) run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug) endfunction() run_cmake_install(ApplicationTarget) +run_cmake_install(InstallExportPlusSbomSameSet) run_cmake_install(InterfaceTarget) run_cmake_install(SharedTarget) run_cmake_install(Requirements) +run_cmake_install(SbomNamespaceFallback) +run_cmake_install(MultiSetSingleSbom) +run_cmake_install(TargetInMultipleSets) +run_cmake_install(EmptyNamespaceFallback -Wauthor) +run_cmake_install(SbomNamespaceAmbiguity -Wauthor) run_cmake_install(MissingPackageNamespace) -run_cmake_install(ReferencesNonExportedTarget) +run_cmake_install(ProjectMetadata) + +run_cmake_error(ReferencesNonExportedTarget) +run_cmake_error(MultiNamespaceAmbiguity) +run_cmake_error(MultiSetAmbiguity) +run_cmake_error(DuplicateSbom)
diff --git a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-install-check.cmake b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-install-check.cmake new file mode 100644 index 0000000..31aa1bc --- /dev/null +++ b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-install-check.cmake
@@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/bar_sbom/bar_sbom.spdx.json" BAR_CONTENT) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity-install-check.cmake)
diff --git a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-stderr.txt b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-stderr.txt new file mode 100644 index 0000000..ea4234f --- /dev/null +++ b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-stderr.txt
@@ -0,0 +1,4 @@ +CMake Warning \(author\) in CMakeLists\.txt: + Target "libc" references target "libb" which has no + install\(EXPORT\)/export\(EXPORT\) namespace and is covered by multiple SBOMs: + foo_sbom, foo_sbom2\. Attributing to "foo_sbom" \(first alphabetically\)\.
diff --git a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity.cmake b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity.cmake new file mode 100644 index 0000000..a667aca --- /dev/null +++ b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity.cmake) + +export(SBOM foo_sbom EXPORTS foo) +export(SBOM foo_sbom2 EXPORTS foo) +export(SBOM bar_sbom EXPORTS bar)
diff --git a/Tests/RunCMake/ExportSbom/SbomNamespaceFallback-install-check.cmake b/Tests/RunCMake/ExportSbom/SbomNamespaceFallback-install-check.cmake new file mode 100644 index 0000000..df0183c --- /dev/null +++ b/Tests/RunCMake/ExportSbom/SbomNamespaceFallback-install-check.cmake
@@ -0,0 +1,3 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/bar/bar.spdx.json" BAR_CONTENT) +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo.spdx.json" FOO_CONTENT) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceFallback-install-check.cmake)
diff --git a/Tests/RunCMake/ExportSbom/SbomNamespaceFallback.cmake b/Tests/RunCMake/ExportSbom/SbomNamespaceFallback.cmake new file mode 100644 index 0000000..5cdb2f9 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/SbomNamespaceFallback.cmake
@@ -0,0 +1,4 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceFallback.cmake) + +export(SBOM foo EXPORTS foo) +export(SBOM bar EXPORTS bar)
diff --git a/Tests/RunCMake/ExportSbom/SharedTarget.cmake b/Tests/RunCMake/ExportSbom/SharedTarget.cmake index 02143cf..c11cff7 100644 --- a/Tests/RunCMake/ExportSbom/SharedTarget.cmake +++ b/Tests/RunCMake/ExportSbom/SharedTarget.cmake
@@ -2,5 +2,5 @@ export( SBOM shared_targets - EXPORT shared_targets + EXPORTS shared_targets )
diff --git a/Tests/RunCMake/ExportSbom/TargetInMultipleSets-install-check.cmake b/Tests/RunCMake/ExportSbom/TargetInMultipleSets-install-check.cmake new file mode 100644 index 0000000..6a0d77f --- /dev/null +++ b/Tests/RunCMake/ExportSbom/TargetInMultipleSets-install-check.cmake
@@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/mySbom/mySbom.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake)
diff --git a/Tests/RunCMake/ExportSbom/TargetInMultipleSets.cmake b/Tests/RunCMake/ExportSbom/TargetInMultipleSets.cmake new file mode 100644 index 0000000..244519e --- /dev/null +++ b/Tests/RunCMake/ExportSbom/TargetInMultipleSets.cmake
@@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets.cmake) + +export(SBOM mySbom EXPORTS setA1 setA2)
diff --git a/Tests/RunCMake/ExternalData/BadArguments-stderr.txt b/Tests/RunCMake/ExternalData/BadArguments-stderr.txt index 9067305..d37702b 100644 --- a/Tests/RunCMake/ExternalData/BadArguments-stderr.txt +++ b/Tests/RunCMake/ExternalData/BadArguments-stderr.txt
@@ -1,7 +1,7 @@ -CMake Warning \(dev\) at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): Ignoring unrecognized arguments passed to ExternalData_add_target: `UNKNOWN_ARGUMENT` Call Stack \(most recent call first\): BadArguments\.cmake:[0-9]+ \(ExternalData_Add_Target\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/ExternalData/BadLinkMode-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/ExternalData/BadLinkMode-result.txt
diff --git a/Tests/RunCMake/ExternalData/BadLinkMode-stderr.txt b/Tests/RunCMake/ExternalData/BadLinkMode-stderr.txt new file mode 100644 index 0000000..29c02a2 --- /dev/null +++ b/Tests/RunCMake/ExternalData/BadLinkMode-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): + ExternalData_LINK_MODE must be `auto` or list: hardlink, symlink, copy
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/ExternalData/BadLinkModeNoSymlinks-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/ExternalData/BadLinkModeNoSymlinks-result.txt
diff --git a/Tests/RunCMake/ExternalData/BadLinkModeNoSymlinks-stderr.txt b/Tests/RunCMake/ExternalData/BadLinkModeNoSymlinks-stderr.txt new file mode 100644 index 0000000..51f3634 --- /dev/null +++ b/Tests/RunCMake/ExternalData/BadLinkModeNoSymlinks-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): + ExternalData_LINK_MODE with `symlink` conflicts with NO_SYMLINKS
diff --git a/Tests/RunCMake/ExternalData/Directory3-stderr.txt b/Tests/RunCMake/ExternalData/Directory3-stderr.txt index 75e8ba9..98f4b6c 100644 --- a/Tests/RunCMake/ExternalData/Directory3-stderr.txt +++ b/Tests/RunCMake/ExternalData/Directory3-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): Data file referenced by argument DATA{Directory3/\*} @@ -12,4 +12,4 @@ .* Directory3\.cmake:[0-9]+ \(ExternalData_Add_Test\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/ExternalData/GoodLinkModeList-result.txt b/Tests/RunCMake/ExternalData/GoodLinkModeList-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/ExternalData/GoodLinkModeList-result.txt
@@ -0,0 +1 @@ +0
diff --git a/Tests/RunCMake/ExternalData/MissingData-stderr.txt b/Tests/RunCMake/ExternalData/MissingData-stderr.txt index c3945c5..d305873 100644 --- a/Tests/RunCMake/ExternalData/MissingData-stderr.txt +++ b/Tests/RunCMake/ExternalData/MissingData-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): Data file referenced by argument DATA{MissingData\.txt} @@ -12,4 +12,4 @@ .* MissingData\.cmake:[0-9]+ \(ExternalData_Expand_Arguments\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/ExternalData/MissingDataWithAssociated-stderr.txt b/Tests/RunCMake/ExternalData/MissingDataWithAssociated-stderr.txt index e5c0262..9fb74f3 100644 --- a/Tests/RunCMake/ExternalData/MissingDataWithAssociated-stderr.txt +++ b/Tests/RunCMake/ExternalData/MissingDataWithAssociated-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/ExternalData\.cmake:[0-9]+ \(message\): Data file referenced by argument DATA{MissingData\.txt,Data\.txt} @@ -12,4 +12,4 @@ .* MissingDataWithAssociated\.cmake:[0-9]+ \(ExternalData_Expand_Arguments\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/ExternalData/RunCMakeTest.cmake b/Tests/RunCMake/ExternalData/RunCMakeTest.cmake index b4cc95e..53c7152 100644 --- a/Tests/RunCMake/ExternalData/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExternalData/RunCMakeTest.cmake
@@ -1,5 +1,25 @@ include(RunCMake) +function(run_externaldata_local test) + run_cmake_command(${test} + ${CMAKE_COMMAND} + -DExternalData_ACTION=local + -DExternalData_OBJECT_STORES=${RunCMake_BINARY_DIR}/${test}-store + ${ARGN} + -Dfile=${RunCMake_BINARY_DIR}/${test}-output.txt + -Dname=${RunCMake_SOURCE_DIR}/CMakeLists.txt + -P ${RunCMake_SOURCE_DIR}/../../../Modules/ExternalData.cmake + ) +endfunction() + +function(run_externaldata_local_link_mode test link_mode) + string(REPLACE ";" "\\\\;" link_mode_arg "${link_mode}") + run_externaldata_local(${test} + "-DExternalData_LINK_MODE=${link_mode_arg}" + ${ARGN} + ) +endfunction() + run_cmake(BadAlgoMap1) run_cmake(BadAlgoMap2) run_cmake(BadArguments) @@ -8,6 +28,13 @@ run_cmake(BadCustom3) run_cmake(BadCustom4) run_cmake(BadHashAlgo1) +run_externaldata_local_link_mode(BadLinkMode "invalid;copy" + ) +run_externaldata_local_link_mode(BadLinkModeNoSymlinks "symlink;copy" + -DExternalData_NO_SYMLINKS=1 + ) +run_externaldata_local_link_mode(GoodLinkModeList "copy;hardlink" + ) run_cmake(BadOption1) run_cmake(BadOption2) run_cmake(BadRecurse1)
diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt b/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt index c2b1db3..5a6eebb 100644 --- a/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt +++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt
@@ -1,4 +1,4 @@ -^(CMake Deprecation Warning at Add_StepDependencies\.cmake:[0-9]+ \(cmake_policy\): +^(CMake Warning \(deprecated\) at Add_StepDependencies\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0114 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\))?$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.)?$
diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt index 0bc4671..b4f7f18 100644 --- a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt +++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt
@@ -1,4 +1,4 @@ -^(CMake Deprecation Warning at Add_StepDependencies_no_target\.cmake:[0-9]+ \(cmake_policy\): +^(CMake Warning \(deprecated\) at Add_StepDependencies_no_target\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0114 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\))?$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.)?$
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/ExternalProject/GitCloneRetry-build-result.txt similarity index 100% copy from Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt copy to Tests/RunCMake/ExternalProject/GitCloneRetry-build-result.txt
diff --git a/Tests/RunCMake/ExternalProject/GitCloneRetry-build-stdout.txt b/Tests/RunCMake/ExternalProject/GitCloneRetry-build-stdout.txt new file mode 100644 index 0000000..be2ff68 --- /dev/null +++ b/Tests/RunCMake/ExternalProject/GitCloneRetry-build-stdout.txt
@@ -0,0 +1,9 @@ + *-- Retry #1, waiting 1 seconds before next attempt\.\.\..* + *-- Retry #2, waiting 1 seconds before next attempt\.\.\..* + *-- Retry #3, waiting 1 seconds before next attempt\.\.\..* + *Had to git clone more than once: 4 times\. + *CMake Error at [^ +]*TestGitRetry-prefix/tmp/TestGitRetry-gitclone.cmake:[0-9]+ \(message\): + +Failed to clone repository:[ + ]* + +'[^']*/Tests/RunCMake/ExternalProject/GitCloneRetry-build/TestGitRetry.git'
diff --git a/Tests/RunCMake/ExternalProject/GitCloneRetry.cmake b/Tests/RunCMake/ExternalProject/GitCloneRetry.cmake new file mode 100644 index 0000000..16c9dab --- /dev/null +++ b/Tests/RunCMake/ExternalProject/GitCloneRetry.cmake
@@ -0,0 +1,13 @@ +include(ExternalProject) + +set(CMAKE_EP_GIT_CLONE_RETRY_COUNT 3) +set(CMAKE_EP_GIT_CLONE_RETRY_DELAY 1) + +# The GIT_REPOSITORY path does not exist, so git clone will always fail, +# which is what triggers the retries. +ExternalProject_Add(TestGitRetry + GIT_REPOSITORY "${CMAKE_CURRENT_BINARY_DIR}/TestGitRetry.git" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" +)
diff --git a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt index 1e79f39..9dca2da 100644 --- a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt +++ b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at NO_DEPENDS-CMP0114-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at NO_DEPENDS-CMP0114-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0114 will be removed from a future version of CMake\. @@ -8,8 +8,10 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "configure" step might break parallel builds Call Stack \(most recent call first\): @@ -24,9 +26,9 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-OLD\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "build" step might break parallel builds Call Stack \(most recent call first\): @@ -41,9 +43,9 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-OLD\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "install" step might break parallel builds Call Stack \(most recent call first\): @@ -58,9 +60,9 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-OLD\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "test" step might break parallel builds Call Stack \(most recent call first\): @@ -69,4 +71,4 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add_StepTargets\) NO_DEPENDS-CMP0114-OLD\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-WARN-stderr.txt b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-WARN-stderr.txt index 4accabd..7479f1c 100644 --- a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-WARN-stderr.txt +++ b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-WARN-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/ExternalProject\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0114 is not set: ExternalProject step targets fully adopt their steps\. Run "cmake --help-policy CMP0114" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -17,9 +17,10 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "configure" step might break parallel builds Call Stack \(most recent call first\): @@ -34,9 +35,9 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "build" step might break parallel builds Call Stack \(most recent call first\): @@ -51,10 +52,10 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -CMake Warning \(dev\) at [^ -]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at [^ +]*/Modules/ExternalProject\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0114 is not set: ExternalProject step targets fully adopt their steps\. Run "cmake --help-policy CMP0114" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -72,9 +73,10 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "install" step might break parallel builds Call Stack \(most recent call first\): @@ -89,10 +91,10 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add\) NO_DEPENDS-CMP0114-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -CMake Warning \(dev\) at [^ -]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at [^ +]*/Modules/ExternalProject\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0114 is not set: ExternalProject step targets fully adopt their steps\. Run "cmake --help-policy CMP0114" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -105,9 +107,10 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add_StepTargets\) NO_DEPENDS-CMP0114-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "test" step might break parallel builds Call Stack \(most recent call first\): @@ -116,4 +119,4 @@ NO_DEPENDS-CMP0114-Common\.cmake:[0-9]+ \(ExternalProject_Add_StepTargets\) NO_DEPENDS-CMP0114-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake index 3848d77..3e87efb 100644 --- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
@@ -258,6 +258,14 @@ run_cmake(TLSVersionBadArg) run_cmake(TLSVersionBadVar) run_cmake(TLSVersionBadEnv) + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GitCloneRetry-build) + run_cmake(GitCloneRetry) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE TRUE) + run_cmake_command(GitCloneRetry-build ${CMAKE_COMMAND} --build . --target TestGitRetry) + endblock() endif() set(RunCMake_TEST_OUTPUT_MERGE 1)
diff --git a/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt b/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt index 98f24a9..4c111f2 100644 --- a/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt +++ b/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Steps-CMP0114-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Steps-CMP0114-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0114 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/ExternalProject/Xcode-CMP0114-stderr.txt b/Tests/RunCMake/ExternalProject/Xcode-CMP0114-stderr.txt index 450b5c4..54345cd 100644 --- a/Tests/RunCMake/ExternalProject/Xcode-CMP0114-stderr.txt +++ b/Tests/RunCMake/ExternalProject/Xcode-CMP0114-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at .*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): +^CMake Warning \(author\) at .*/Modules/ExternalProject\.cmake:[0-9]+ \(message\): Policy CMP0114 is not set to NEW\. In order to support the Xcode "new build system", this project must be updated to set policy CMP0114 to NEW\. @@ -8,4 +8,4 @@ Call Stack \(most recent call first\): Xcode-CMP0114\.cmake:[0-9]+ \(ExternalProject_Add\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt b/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt index e327a9f..4621971 100644 --- a/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt +++ b/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning: +^CMake Warning: Support for "Extra Generators" like [^
diff --git a/Tests/RunCMake/FASTBuild/RunCMakeTest.cmake b/Tests/RunCMake/FASTBuild/RunCMakeTest.cmake index 33d70b1..4142faa 100644 --- a/Tests/RunCMake/FASTBuild/RunCMakeTest.cmake +++ b/Tests/RunCMake/FASTBuild/RunCMakeTest.cmake
@@ -6,6 +6,9 @@ run_cmake(UnityBatchSize) run_cmake(UnityGroup) run_cmake(UnityIsolate) +run_cmake(UnityIsolateFileSet) +run_cmake(UnitySourceProperties) +run_cmake(UnityFileSetProperties) run_cmake(UnitySubdirs) run_cmake(UnitySubdirsIsolate) run_cmake(DisableCaching)
diff --git a/Tests/RunCMake/FASTBuild/UnityFileSetProperties-check.cmake b/Tests/RunCMake/FASTBuild/UnityFileSetProperties-check.cmake new file mode 100644 index 0000000..c9b7ccc --- /dev/null +++ b/Tests/RunCMake/FASTBuild/UnityFileSetProperties-check.cmake
@@ -0,0 +1,35 @@ +set(fbuild_bff "${RunCMake_TEST_BINARY_DIR}/fbuild.bff") + +if(NOT EXISTS "${fbuild_bff}") + set(RunCMake_TEST_FAILED "Generator output file is missing:\n ${fbuild_bff}") + return() +endif() + +file(STRINGS "${fbuild_bff}" fbuild_bff_lines + REGEX "\\.UnityInputExcludedFiles =|unity_.*\\.cpp'|^ \\}") + +set(in_excluded_files 0) +set(excluded_sources) +foreach(line IN LISTS fbuild_bff_lines) + if(line MATCHES "\\.UnityInputExcludedFiles =") + set(in_excluded_files 1) + elseif(in_excluded_files AND line MATCHES "^ \\}") + set(in_excluded_files 0) + elseif(in_excluded_files AND line MATCHES "unity_([^']+)\\.cpp'") + list(APPEND excluded_sources "${CMAKE_MATCH_1}") + endif() +endforeach() + +foreach(prop IN ITEMS + compile_options + compile_definitions + include_directories) + foreach(index IN ITEMS 1 2) + set(expected_source "${prop}_${index}") + if(NOT expected_source IN_LIST excluded_sources) + set(RunCMake_TEST_FAILED + "Source unity_${prop}_${index}.cpp was not excluded from generated FASTBuild unity files in ${fbuild_bff}") + return() + endif() + endforeach() +endforeach()
diff --git a/Tests/RunCMake/FASTBuild/UnityFileSetProperties.cmake b/Tests/RunCMake/FASTBuild/UnityFileSetProperties.cmake new file mode 100644 index 0000000..0a942e8 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/UnityFileSetProperties.cmake
@@ -0,0 +1,16 @@ +set(CMAKE_UNITY_BUILD ON) + +add_executable(main main.cpp) + +target_sources(main PRIVATE FILE_SET s1 TYPE SOURCES FILES unity_compile_options_1.cpp + unity_compile_options_2.cpp) +set_property(FILE_SET s1 TARGET main PROPERTY COMPILE_OPTIONS -DUNITY_COMPILE_OPTIONS) + +target_sources(main PRIVATE FILE_SET s2 TYPE SOURCES FILES unity_compile_definitions_1.cpp + unity_compile_definitions_2.cpp) +set_property(FILE_SET s2 TARGET main PROPERTY COMPILE_DEFINITIONS UNITY_COMPILE_DEFINITIONS) + + +target_sources(main PRIVATE FILE_SET s3 TYPE SOURCES FILES unity_include_directories_1.cpp + unity_include_directories_2.cpp) +set_property(FILE_SET s3 TARGET main PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}")
diff --git a/Tests/RunCMake/FASTBuild/UnityIsolate-check.cmake b/Tests/RunCMake/FASTBuild/UnityIsolate-check.cmake index f42bb1b..94ead02 100644 --- a/Tests/RunCMake/FASTBuild/UnityIsolate-check.cmake +++ b/Tests/RunCMake/FASTBuild/UnityIsolate-check.cmake
@@ -10,7 +10,7 @@ '.*some_source_file_3.cpp', '.*some_source_file_4.cpp' } - .UnityInputIsolatedFiles = + .UnityInputExcludedFiles = { '.*some_source_file_1.cpp', '.*some_source_file_4.cpp'
diff --git a/Tests/RunCMake/FASTBuild/UnityIsolateFileSet-check.cmake b/Tests/RunCMake/FASTBuild/UnityIsolateFileSet-check.cmake new file mode 100644 index 0000000..d60ae15 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/UnityIsolateFileSet-check.cmake
@@ -0,0 +1,27 @@ +set(REGEX_TO_MATCH " + Unity\\('main_Unity_1'\\) + { +.* + .UnityInputFiles = + { + '.*main.cpp', + '.*some_source_file_1.cpp', + '.*some_source_file_4.cpp', + '.*some_source_file_2.cpp', + '.*some_source_file_3.cpp' + } + .UnityInputExcludedFiles = + { + '.*some_source_file_1.cpp', + '.*some_source_file_4.cpp' + } + } +.*ObjectList.* +.* + .CompilerInputUnity = + { + 'main_Unity_1' + } +") + +include(${RunCMake_SOURCE_DIR}/check.cmake)
diff --git a/Tests/RunCMake/FASTBuild/UnityIsolateFileSet.cmake b/Tests/RunCMake/FASTBuild/UnityIsolateFileSet.cmake new file mode 100644 index 0000000..2a8e00f --- /dev/null +++ b/Tests/RunCMake/FASTBuild/UnityIsolateFileSet.cmake
@@ -0,0 +1,10 @@ +set(CMAKE_UNITY_BUILD ON) +add_executable(main + main.cpp) + +target_sources(main PRIVATE FILE_SET s1 TYPE SOURCES FILES some_source_file_1.cpp + some_source_file_4.cpp) +set_property(FILE_SET s1 TARGET main PROPERTY SKIP_UNITY_BUILD_INCLUSION ON) + +target_sources(main PRIVATE FILE_SET s2 TYPE SOURCES FILES some_source_file_2.cpp + some_source_file_3.cpp)
diff --git a/Tests/RunCMake/FASTBuild/UnitySourceProperties-check.cmake b/Tests/RunCMake/FASTBuild/UnitySourceProperties-check.cmake new file mode 100644 index 0000000..91c4c24 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/UnitySourceProperties-check.cmake
@@ -0,0 +1,36 @@ +set(fbuild_bff "${RunCMake_TEST_BINARY_DIR}/fbuild.bff") + +if(NOT EXISTS "${fbuild_bff}") + set(RunCMake_TEST_FAILED "Generator output file is missing:\n ${fbuild_bff}") + return() +endif() + +file(STRINGS "${fbuild_bff}" fbuild_bff_lines + REGEX "\\.UnityInputExcludedFiles =|unity_.*\\.cpp'|^ \\}") + +set(in_excluded_files 0) +set(excluded_sources) +foreach(line IN LISTS fbuild_bff_lines) + if(line MATCHES "\\.UnityInputExcludedFiles =") + set(in_excluded_files 1) + elseif(in_excluded_files AND line MATCHES "^ \\}") + set(in_excluded_files 0) + elseif(in_excluded_files AND line MATCHES "unity_([^']+)\\.cpp'") + list(APPEND excluded_sources "${CMAKE_MATCH_1}") + endif() +endforeach() + +foreach(prop IN ITEMS + compile_options + compile_definitions + compile_flags + include_directories) + foreach(index IN ITEMS 1 2) + set(expected_source "${prop}_${index}") + if(NOT expected_source IN_LIST excluded_sources) + set(RunCMake_TEST_FAILED + "Source unity_${prop}_${index}.cpp was not excluded from generated FASTBuild unity files in ${fbuild_bff}") + return() + endif() + endforeach() +endforeach()
diff --git a/Tests/RunCMake/FASTBuild/UnitySourceProperties.cmake b/Tests/RunCMake/FASTBuild/UnitySourceProperties.cmake new file mode 100644 index 0000000..1fc1a37 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/UnitySourceProperties.cmake
@@ -0,0 +1,49 @@ +set(CMAKE_UNITY_BUILD ON) + +add_executable(main + main.cpp + unity_compile_options_1.cpp + unity_compile_options_2.cpp + unity_compile_definitions_1.cpp + unity_compile_definitions_2.cpp + unity_compile_flags_1.cpp + unity_compile_flags_2.cpp + unity_include_directories_1.cpp + unity_include_directories_2.cpp +) + +set_source_files_properties( + unity_compile_options_1.cpp + unity_compile_options_2.cpp + TARGET_DIRECTORY + main + PROPERTIES + COMPILE_OPTIONS -DUNITY_COMPILE_OPTIONS +) + +set_source_files_properties( + unity_compile_definitions_1.cpp + unity_compile_definitions_2.cpp + TARGET_DIRECTORY + main + PROPERTIES + COMPILE_DEFINITIONS UNITY_COMPILE_DEFINITIONS +) + +set_source_files_properties( + unity_compile_flags_1.cpp + unity_compile_flags_2.cpp + TARGET_DIRECTORY + main + PROPERTIES + COMPILE_FLAGS -DUNITY_COMPILE_FLAGS +) + +set_source_files_properties( + unity_include_directories_1.cpp + unity_include_directories_2.cpp + TARGET_DIRECTORY + main + PROPERTIES + INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" +)
diff --git a/Tests/RunCMake/FASTBuild/unity_compile_definitions_1.cpp b/Tests/RunCMake/FASTBuild/unity_compile_definitions_1.cpp new file mode 100644 index 0000000..914cfd5 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/unity_compile_definitions_1.cpp
@@ -0,0 +1,4 @@ +int unity_compile_definitions_1() +{ + return 0; +}
diff --git a/Tests/RunCMake/FASTBuild/unity_compile_definitions_2.cpp b/Tests/RunCMake/FASTBuild/unity_compile_definitions_2.cpp new file mode 100644 index 0000000..9f42928 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/unity_compile_definitions_2.cpp
@@ -0,0 +1,4 @@ +int unity_compile_definitions_2() +{ + return 0; +}
diff --git a/Tests/RunCMake/FASTBuild/unity_compile_flags_1.cpp b/Tests/RunCMake/FASTBuild/unity_compile_flags_1.cpp new file mode 100644 index 0000000..aa1a14b --- /dev/null +++ b/Tests/RunCMake/FASTBuild/unity_compile_flags_1.cpp
@@ -0,0 +1,4 @@ +int unity_compile_flags_1() +{ + return 0; +}
diff --git a/Tests/RunCMake/FASTBuild/unity_compile_flags_2.cpp b/Tests/RunCMake/FASTBuild/unity_compile_flags_2.cpp new file mode 100644 index 0000000..915f7d2 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/unity_compile_flags_2.cpp
@@ -0,0 +1,4 @@ +int unity_compile_flags_2() +{ + return 0; +}
diff --git a/Tests/RunCMake/FASTBuild/unity_compile_options_1.cpp b/Tests/RunCMake/FASTBuild/unity_compile_options_1.cpp new file mode 100644 index 0000000..3b13073 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/unity_compile_options_1.cpp
@@ -0,0 +1,4 @@ +int unity_compile_options_1() +{ + return 0; +}
diff --git a/Tests/RunCMake/FASTBuild/unity_compile_options_2.cpp b/Tests/RunCMake/FASTBuild/unity_compile_options_2.cpp new file mode 100644 index 0000000..3689628 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/unity_compile_options_2.cpp
@@ -0,0 +1,4 @@ +int unity_compile_options_2() +{ + return 0; +}
diff --git a/Tests/RunCMake/FASTBuild/unity_include_directories_1.cpp b/Tests/RunCMake/FASTBuild/unity_include_directories_1.cpp new file mode 100644 index 0000000..777af1e --- /dev/null +++ b/Tests/RunCMake/FASTBuild/unity_include_directories_1.cpp
@@ -0,0 +1,4 @@ +int unity_include_directories_1() +{ + return 0; +}
diff --git a/Tests/RunCMake/FASTBuild/unity_include_directories_2.cpp b/Tests/RunCMake/FASTBuild/unity_include_directories_2.cpp new file mode 100644 index 0000000..3a5df21 --- /dev/null +++ b/Tests/RunCMake/FASTBuild/unity_include_directories_2.cpp
@@ -0,0 +1,4 @@ +int unity_include_directories_2() +{ + return 0; +}
diff --git a/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt b/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt index a0669da..02176fc 100644 --- a/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt +++ b/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at .*/Modules/FindPackageHandleStandardArgs\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/FindPackageHandleStandardArgs\.cmake:[0-9]+ \(message\): The package name passed to find_package_handle_standard_args\(\) \(NAMEMISMATCH\) does not match the name of the calling package \(NameMismatch\)\. This can lead to problems in calling code that expects @@ -8,9 +8,9 @@ FindNameMismatch\.cmake:[0-9]+ \(find_package_handle_standard_args\) NameMismatch\.cmake:[0-9]+ \(find_package\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at .*/Modules/FindPackageHandleStandardArgs\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .*/Modules/FindPackageHandleStandardArgs\.cmake:[0-9]+ \(message\): The package name passed to find_package_handle_standard_args\(\) \(NAMEMISMATCH\) does not match the name of the calling package \(NameMismatchOld\)\. This can lead to problems in calling code that expects @@ -20,4 +20,4 @@ FindNameMismatchOld\.cmake:[0-9]+ \(find_package_handle_standard_args\) NameMismatch\.cmake:[0-9]+ \(find_package\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/FPHSA/range_ignored-stderr.txt b/Tests/RunCMake/FPHSA/range_ignored-stderr.txt index a28bef6..9a90885 100644 --- a/Tests/RunCMake/FPHSA/range_ignored-stderr.txt +++ b/Tests/RunCMake/FPHSA/range_ignored-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at .+FindPackageHandleStandardArgs\.cmake:[0-9]+ \(message\): +CMake Warning \(author\) at .+FindPackageHandleStandardArgs\.cmake:[0-9]+ \(message\): find_package\(\) specify a version range but the module Pseudo does not support this capability\. Only the lower endpoint of the range will be used\.
diff --git a/Tests/RunCMake/FeatureSummary/FeatureSummaryParentheses-CMP0183-WARN-stderr.txt b/Tests/RunCMake/FeatureSummary/FeatureSummaryParentheses-CMP0183-WARN-stderr.txt index 7918f21..ca25ca2 100644 --- a/Tests/RunCMake/FeatureSummary/FeatureSummaryParentheses-CMP0183-WARN-stderr.txt +++ b/Tests/RunCMake/FeatureSummary/FeatureSummaryParentheses-CMP0183-WARN-stderr.txt
@@ -1,9 +1,10 @@ -^CMake Warning \(dev\) at [^ -]*/Modules/FeatureSummary\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at [^ +]*/Modules/FeatureSummary\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0183 is not set: add_feature_info\(\) supports full Condition Syntax\. Run "cmake --help-policy CMP0183" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): FeatureSummaryParentheses-CMP0183-WARN\.cmake:[0-9]+ \(add_feature_info\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/FetchContent/VarPassthroughs.cmake b/Tests/RunCMake/FetchContent/VarPassthroughs.cmake index 284f5b8..f3bc7d0 100644 --- a/Tests/RunCMake/FetchContent/VarPassthroughs.cmake +++ b/Tests/RunCMake/FetchContent/VarPassthroughs.cmake
@@ -8,6 +8,8 @@ set(CMAKE_NETRC DDDD) set(CMAKE_NETRC_FILE EEEE) set(CMAKE_TLS_VERSION FFFF) +set(CMAKE_EP_GIT_CLONE_RETRY_COUNT GGGG) +set(CMAKE_EP_GIT_CLONE_RETRY_DELAY HHHH) FetchContent_Declare(PassThrough DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed" @@ -43,3 +45,11 @@ if(NOT contents MATCHES "CMAKE_NETRC_FILE \\[==\\[EEEE\\]==\\]") message(FATAL_ERROR "Missing CMAKE_NETRC_FILE") endif() + +if(NOT contents MATCHES "CMAKE_EP_GIT_CLONE_RETRY_COUNT \\[==\\[GGGG\\]==\\]") + message(FATAL_ERROR "Missing CMAKE_EP_GIT_CLONE_RETRY_COUNT") +endif() + +if(NOT contents MATCHES "CMAKE_EP_GIT_CLONE_RETRY_DELAY \\[==\\[HHHH\\]==\\]") + message(FATAL_ERROR "Missing CMAKE_EP_GIT_CLONE_RETRY_DELAY") +endif()
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py index 34d0e86..d5fafdc 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -13,7 +13,7 @@ assert is_list(o) assert len(o) == 1 major = 2 - minor = 10 + minor = 11 check_index_object(o[0], "codemodel", major, minor, check_object_codemodel(g, major, minor)) def check_backtrace(t, b, backtrace): @@ -355,9 +355,20 @@ assert is_dict(actual) expected_keys = ["path"] - if expected["fileSetName"] is not None: + if expected["fileSetNames"] is not None: expected_keys.append("fileSetIndex") - assert is_string(obj["fileSets"][actual["fileSetIndex"]]["name"], expected["fileSetName"]) + expected_keys.append("fileSetIndexes") + + assert is_list(expected["fileSetIndexes"]) + assert is_list(expected["fileSetNames"]) + + assert is_list(actual["fileSetIndexes"]) + assert is_int(actual["fileSetIndex"], actual["fileSetIndexes"][-1]) + assert is_string(obj["fileSets"][actual["fileSetIndex"]]["name"], expected["fileSetNames"][-1]) + + assert is_int(len(actual["fileSetIndexes"]), len(expected["fileSetNames"])) + for (actual_idx, expected_name) in zip(actual["fileSetIndexes"], expected["fileSetNames"]): + assert is_string(obj["fileSets"][actual_idx]["name"], expected_name) if expected["compileGroupLanguage"] is not None: expected_keys.append("compileGroupIndex") @@ -371,9 +382,12 @@ expected_keys.append("isGenerated") assert is_bool(actual["isGenerated"], expected["isGenerated"]) - if expected["backtrace"] is not None: + if expected["backtraces"] is not None: expected_keys.append("backtrace") - check_backtrace(obj, actual["backtrace"], expected["backtrace"]) + expected_keys.append("backtraces") + assert is_list(actual["backtraces"]) + check_backtrace(obj, actual["backtrace"], expected["backtraces"][0]) + check_backtraces(obj, actual["backtraces"], expected["backtraces"]) assert sorted(actual.keys()) == sorted(expected_keys) @@ -1077,6 +1091,8 @@ read_codemodel_json_data("targets/c_headers_1.json"), read_codemodel_json_data("targets/c_headers_2.json"), + read_codemodel_json_data("targets/c_sources_1.json"), + read_codemodel_json_data("targets/c_sources_2.json"), ] if sys.platform == "darwin": @@ -1162,16 +1178,18 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/([0-9a-f]+/)?generate\\.stamp\\.rule$", "isGenerated": True, - "fileSetName": None, + "fileSetNames": None, "sourceGroupName": "CMake Rules", "compileGroupLanguage": None, - "backtrace": [ - { - "file": "^CMakeLists\\.txt$", - "line": None, - "command": None, - "hasParent": False, - }, + "backtraces": [ + [ + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ] ], }, ]
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json index ea7a079..1c9fd19 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json
@@ -5,7 +5,9 @@ "childSources": null, "targetIds": [ "^c_headers_1::@6b8db101d64c125f29fe$", - "^c_headers_2::@6b8db101d64c125f29fe$" + "^c_headers_2::@6b8db101d64c125f29fe$", + "^c_sources_1::@6b8db101d64c125f29fe$", + "^c_sources_2::@6b8db101d64c125f29fe$" ], "abstractTargetIds": null, "projectName": "codemodel-v2", @@ -132,7 +134,8 @@ "type": "fileSet", "destination": "include", "paths": [ - "^fileset/h3\\.h$" + "^fileset/h3\\.h$", + "^fileset/h5\\.h$" ], "isExcludeFromAll": null, "isForAllComponents": null,
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json index ad73de9..658d77b 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json
@@ -32,7 +32,9 @@ "^c_static_exe::@6890427a1f51a3e7e1df$", "^c_subdir::@6890427a1f51a3e7e1df$", "^c_headers_1::@6b8db101d64c125f29fe$", - "^c_headers_2::@6b8db101d64c125f29fe$" + "^c_headers_2::@6b8db101d64c125f29fe$", + "^c_sources_1::@6b8db101d64c125f29fe$", + "^c_sources_2::@6b8db101d64c125f29fe$" ], "abstractTargetIds": [ "^interface_lib::@6890427a1f51a3e7e1df$"
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_alias.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_alias.json index 2e1b90e..a1279ee 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_alias.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_alias.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^alias/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^alias/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_custom.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_custom.json index 78e98bb..94b3f30 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_custom.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_custom.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^custom/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^custom/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json index 9dc94a2..f393572 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_direct.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_direct.json index 0e157ce..37727e1 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_direct.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_direct.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/direct/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/direct/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_external.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_external.json index 997b053..81d6dc6 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_external.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_external.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_framework.json index a984b84..2012cf8 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_framework.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_framework.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/framework/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^framework/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^framework/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/framework/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^framework/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^framework/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_imported.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_imported.json index 54a0e71..7173fa9 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_imported.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_imported.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_interface.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_interface.json index a8c94b7..1401cae 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_interface.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_interface.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/interface/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^interface/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^interface/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/interface/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^interface/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^interface/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_object.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_object.json index 31a679d..b2ccf93 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_object.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_object.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json index 1cad522..80dcfc2 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ], @@ -206,8 +210,8 @@ "backtrace": null }, { - "id": "^link_imported_interface_symbolic_exe::@ba7eb709d0b48779c6c8$", - "backtrace": null + "id": "^link_imported_interface_symbolic_exe::@ba7eb709d0b48779c6c8$", + "backtrace": null }, { "id": "^iface_srcs::@25b7fa8ea00134654b85$", @@ -230,6 +234,14 @@ "backtrace": null }, { + "id": "^c_sources_1::@6b8db101d64c125f29fe$", + "backtrace": null + }, + { + "id": "^c_sources_2::@6b8db101d64c125f29fe$", + "backtrace": null + }, + { "id": "^static_framework::@217a96c3a62328a73ef4$", "backtrace": null }, @@ -421,6 +433,14 @@ "backtrace": null }, { + "id": "^c_sources_1::@6b8db101d64c125f29fe$", + "backtrace": null + }, + { + "id": "^c_sources_2::@6b8db101d64c125f29fe$", + "backtrace": null + }, + { "id": "^static_framework::@217a96c3a62328a73ef4$", "backtrace": 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 dd49b39..48fb353 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
@@ -1,6 +1,5 @@ { - "compileGroups": - [ + "compileGroups": [ { "language": "CXX", "sourcePaths": [ @@ -8,8 +7,7 @@ ], "includes": null, "defines": null, - "frameworks": - [ + "frameworks": [ { "isSystem": null, "path": "^.*/framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?static_framework.framework",
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_alias_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_alias_exe.json index 8235b2d..40f357e 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_alias_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_alias_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^alias/CMakeLists\\.txt$", - "line": 5, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^alias/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^alias/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_exe.json index 247c8b9..f1000f8 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_exe.json
@@ -14,34 +14,36 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^codemodel-v2\\.cmake$", - "line": 13, - "command": "add_executable", - "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 - } + "backtraces": [ + [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 13, + "command": "add_executable", + "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 + } + ] ] } ],
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 099ff3d..38c78cd 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
@@ -47,127 +47,182 @@ { "path": "^fileset/empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^fileset/CMakeLists\\.txt$", - "line": 1, - "command": "add_library", - "hasParent": true - }, - { - "file": "^fileset/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 1, + "command": "add_library", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^fileset/error\\.c$", "isGenerated": null, - "fileSetName": "HEADERS", + "fileSetNames": ["HEADERS"], + "fileSetIndexes": [0], "sourceGroupName": "Header Files", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^fileset/CMakeLists\\.txt$", - "line": 3, - "command": "target_sources", - "hasParent": true - }, - { - "file": "^fileset/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 3, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^fileset/other\\.c$", "isGenerated": null, - "fileSetName": "HEADERS", + "fileSetNames": ["HEADERS"], + "fileSetIndexes": [0], "sourceGroupName": "Source Files", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^fileset/CMakeLists\\.txt$", - "line": 3, - "command": "target_sources", - "hasParent": true - }, - { - "file": "^fileset/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 3, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^fileset/h1\\.h$", "isGenerated": null, - "fileSetName": "a", + "fileSetNames": ["a"], + "fileSetIndexes": [1], "sourceGroupName": "Header Files", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^fileset/CMakeLists\\.txt$", - "line": 3, - "command": "target_sources", - "hasParent": true - }, - { - "file": "^fileset/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 3, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^fileset/dir/h4\\.h$", "isGenerated": null, - "fileSetName": "a", + "fileSetNames": ["a"], + "fileSetIndexes": [1], "sourceGroupName": "Header Files", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^fileset/CMakeLists\\.txt$", - "line": 3, - "command": "target_sources", - "hasParent": true - }, - { - "file": "^fileset/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 3, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + ] + }, + { + "path": "^fileset/h5\\.h$", + "isGenerated": null, + "fileSetNames": ["a", "c"], + "fileSetIndexes": [1, 3], + "sourceGroupName": "Header Files", + "compileGroupLanguage": null, + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 3, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 10, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^fileset/dir/h2\\.h$", "isGenerated": null, - "fileSetName": "b", + "fileSetNames": ["b"], + "fileSetIndexes": [2], "sourceGroupName": "Header Files", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^fileset/CMakeLists\\.txt$", - "line": 7, - "command": "target_sources", - "hasParent": true - }, - { - "file": "^fileset/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 7, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ], @@ -175,25 +230,36 @@ { "path": "^fileset/error\\.c$", "isGenerated": null, - "fileSetName": "HEADERS", + "fileSetNames": ["HEADERS"], + "fileSetIndexes": [0], "sourceGroupName": "Header Files" }, { "path": "^fileset/other\\.c$", "isGenerated": null, - "fileSetName": "HEADERS", + "fileSetNames": ["HEADERS"], + "fileSetIndexes": [0], "sourceGroupName": "Source Files" }, { "path": "^fileset/h3\\.h$", "isGenerated": null, - "fileSetName": "c", + "fileSetNames": ["c"], + "fileSetIndexes": [3], + "sourceGroupName": "Header Files" + }, + { + "path": "^fileset/h5\\.h$", + "isGenerated": null, + "fileSetNames": ["c"], + "fileSetIndexes": [1, 3], "sourceGroupName": "Header Files" }, { "path": "^fileset/dir/h2\\.h$", "isGenerated": null, - "fileSetName": "b", + "fileSetNames": ["b"], + "fileSetIndexes": [2], "sourceGroupName": "Header Files" } ], @@ -213,13 +279,15 @@ "sourcePaths": [ "^fileset/error\\.c$", "^fileset/h1\\.h$", + "^fileset/h5\\.h$", "^fileset/dir/h2\\.h$", "^fileset/dir/h4\\.h$" ], "interfaceSourcePaths": [ "^fileset/error\\.c$", - "^fileset/dir/h2\\.h$", - "^fileset/h3\\.h$" + "^fileset/h3\\.h$", + "^fileset/h5\\.h$", + "^fileset/dir/h2\\.h$" ] } ],
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 11c84e7..996ef72 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
@@ -23,22 +23,24 @@ { "path": "^fileset/empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^fileset/CMakeLists\\.txt$", - "line": 15, - "command": "add_library", - "hasParent": true - }, - { - "file": "^fileset/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 15, + "command": "add_library", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ], @@ -46,7 +48,7 @@ { "path": "^fileset/h1\\.h$", "isGenerated": null, - "fileSetName": "HEADERS", + "fileSetNames": ["HEADERS"], "sourceGroupName": "Header Files" } ],
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 7188530..b5422f5 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json
@@ -14,34 +14,36 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^codemodel-v2\\.cmake$", - "line": 12, - "command": "add_library", - "hasParent": true - }, - { - "file": "^codemodel-v2\\.cmake$", - "line": null, - "command": null, - "hasParent": true - }, - { - "file": "^CMakeLists\\.txt$", - "line": 3, - "command": "include", - "hasParent": true - }, - { - "file": "^CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 12, + "command": "add_library", + "hasParent": true + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": null, + "command": null, + "hasParent": true + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": true + }, + { + "file": "^CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_exe.json index 7c0c27a..d279fb6 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_exe.json
@@ -14,43 +14,47 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": 9, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(object|build/c_object_lib\\.build)/.*/empty(\\.c)?\\.o(bj)?$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Object Libraries", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": 10, - "command": "target_link_libraries", - "hasParent": true - }, - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 10, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_lib.json index 86800e1..512bd42 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_lib.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": 8, - "command": "add_library", - "hasParent": true - }, - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 8, + "command": "add_library", + "hasParent": true + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_exe.json index 6401b6f..65d294a 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_exe.json
@@ -14,34 +14,36 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^codemodel-v2\\.cmake$", - "line": 18, - "command": "add_executable", - "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 - } + "backtraces": [ + [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 18, + "command": "add_executable", + "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 + } + ] ] } ],
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 70a0a83..eef8899 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
@@ -14,34 +14,36 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^codemodel-v2\\.cmake$", - "line": 17, - "command": "add_library", - "hasParent": true - }, - { - "file": "^codemodel-v2\\.cmake$", - "line": null, - "command": null, - "hasParent": true - }, - { - "file": "^CMakeLists\\.txt$", - "line": 3, - "command": "include", - "hasParent": true - }, - { - "file": "^CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 17, + "command": "add_library", + "hasParent": true + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": null, + "command": null, + "hasParent": true + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": true + }, + { + "file": "^CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_1.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_1.json new file mode 100644 index 0000000..160babc --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_1.json
@@ -0,0 +1,194 @@ +{ + "name": "c_sources_1", + "id": "^c_sources_1::@6b8db101d64c125f29fe$", + "directorySource": "^fileset$", + "projectName": "codemodel-v2", + "type": "STATIC_LIBRARY", + "imported": null, + "local": null, + "abstract": null, + "symbolic": null, + "isGeneratorProvided": null, + "fileSets": [ + { + "name": "SOURCES", + "type": "SOURCES", + "visibility": "PRIVATE", + "baseDirectories": [ + "^fileset$" + ] + }, + { + "name": "public_srcs", + "type": "SOURCES", + "visibility": "PUBLIC", + "baseDirectories": [ + "^fileset$" + ] + } + ], + "sources": [ + { + "path": "^fileset/empty\\.c$", + "isGenerated": null, + "fileSetNames": ["public_srcs"], + "fileSetIndexes": [0], + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 40, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + ] + }, + { + "path": "^fileset/other\\.c$", + "isGenerated": null, + "fileSetNames": ["SOURCES"], + "fileSetIndexes": [0], + "sourceGroupName": "Source Files", + "compileGroupLanguage": null, + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 35, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + ] + } + ], + "interfaceSources": [ + { + "path": "^fileset/empty\\.c$", + "isGenerated": null, + "fileSetNames": ["SOURCES"], + "sourceGroupName": "Source Files" + } + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^fileset/empty\\.c$", + "^fileset/other\\.c$" + ], + "interfaceSourcePaths": [ + "^fileset/empty\\.c$" + ] + } + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^fileset/empty\\.c$" + ], + "includes": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/fileset/dir$", + "isSystem": null, + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 46, + "command": "set_property", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + } + ], + "frameworks": null, + "defines": [ + { + "define": "DEF2", + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 43, + "command": "set_property", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, + { + "define": "_MBCS", + "backtrace": null + } + ], + "compileCommandFragments": null + } + ], + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 33, + "command": "add_library", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "folder": null, + "nameOnDisk": "^(lib)?c_sources_1\\.(a|lib|l)$", + "artifacts": [ + { + "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_sources_1\\.(a|lib|l)$", + "_dllExtra": false + } + ], + "build": "^fileset$", + "source": "^fileset$", + "install": null, + "link": null, + "archive": { + "lto": null + }, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": null + } + ], + "linkLibraries": null, + "interfaceLinkLibraries": null, + "compileDependencies": null, + "interfaceCompileDependencies": null, + "objectDependencies": null, + "orderDependencies": null +}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_2.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_2.json new file mode 100644 index 0000000..b92da4b --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_2.json
@@ -0,0 +1,221 @@ +{ + "name": "c_sources_2", + "id": "^c_sources_2::@6b8db101d64c125f29fe$", + "directorySource": "^fileset$", + "projectName": "codemodel-v2", + "type": "STATIC_LIBRARY", + "imported": null, + "local": null, + "abstract": null, + "symbolic": null, + "isGeneratorProvided": null, + "fileSets": null, + "sources": [ + { + "path": "^fileset/empty\\.c$", + "isGenerated": null, + "fileSetNames": null, + "fileSetIndexes": null, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtraces": [ + [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 50, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + ] + } + ], + "interfaceSources": null, + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^fileset/empty\\.c$" + ], + "interfaceSourcePaths": null + } + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^fileset/empty\\.c$" + ], + "includes": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/fileset/dir$", + "isSystem": null, + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 47, + "command": "set_property", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + } + ], + "frameworks": null, + "defines": [ + { + "define": "DEF3", + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 44, + "command": "set_property", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, + { + "define": "_MBCS", + "backtrace": null + } + ], + "compileCommandFragments": null + } + ], + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 49, + "command": "add_library", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "folder": null, + "nameOnDisk": "^(lib)?c_sources_2\\.(a|lib|l)$", + "artifacts": [ + { + "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_sources_2\\.(a|lib|l)$", + "_dllExtra": false + } + ], + "build": "^fileset$", + "source": "^fileset$", + "install": null, + "link": null, + "archive": { + "lto": null + }, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": null + }, + { + "id": "^c_sources_1::@6b8db101d64c125f29fe$", + "fragment": null, + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 50, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "fromDependency": null + } + ], + "linkLibraries": [ + { + "id": "^c_sources_1::@6b8db101d64c125f29fe$", + "fragment": null, + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 50, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "fromDependency": null + } + ], + "interfaceLinkLibraries": [ + { + "id": "^c_sources_1::@6b8db101d64c125f29fe$", + "fragment": null, + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 50, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + } + ], + "compileDependencies": [ + { + "id": "^c_sources_1::@6b8db101d64c125f29fe$", + "fragment": null, + "backtrace": [ + { + "file": "^fileset/CMakeLists\\.txt$", + "line": 50, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^fileset/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "fromDependency": null + } + ], + "interfaceCompileDependencies": null, + "objectDependencies": null, + "orderDependencies": null +}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_exe.json index 97dddf0..b11671c 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_exe.json
@@ -14,34 +14,36 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^codemodel-v2\\.cmake$", - "line": 22, - "command": "add_executable", - "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 - } + "backtraces": [ + [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 22, + "command": "add_executable", + "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 + } + ] ] } ],
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 0a2dea0..8ccc381 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
@@ -14,34 +14,36 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^codemodel-v2\\.cmake$", - "line": 21, - "command": "add_library", - "hasParent": true - }, - { - "file": "^codemodel-v2\\.cmake$", - "line": null, - "command": null, - "hasParent": true - }, - { - "file": "^CMakeLists\\.txt$", - "line": 3, - "command": "include", - "hasParent": true - }, - { - "file": "^CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 21, + "command": "add_library", + "hasParent": true + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": null, + "command": null, + "hasParent": true + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": true + }, + { + "file": "^CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
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 f54216a..4354dfb 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json
@@ -14,22 +14,24 @@ { "path": "^subdir/empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^subdir/CMakeLists\\.txt$", - "line": 4, - "command": "target_sources", - "hasParent": true - }, - { - "file": "^subdir/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^subdir/CMakeLists\\.txt$", + "line": 4, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^subdir/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/compile_usage_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/compile_usage_exe.json index 104f30e..a42cee3 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/compile_usage_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/compile_usage_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": 38, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 38, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ], @@ -97,6 +99,23 @@ "archive": null, "dependencies": [ { + "id": "^usage_lib::@79b6367c9e05c77f6a80$", + "backtrace": [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 40, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, + { "id": "^ZERO_CHECK::@79b6367c9e05c77f6a80$", "backtrace": null }
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/custom_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/custom_exe.json index fc087f9..2c2dfd3 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/custom_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/custom_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^custom/CMakeLists\\.txt$", - "line": 4, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^custom/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": 4, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^custom/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/custom_tgt.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/custom_tgt.json index 6b474e2..f17ed1d 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/custom_tgt.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/custom_tgt.json
@@ -14,37 +14,41 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/custom_tgt(-(Debug|Release|RelWithDebInfo|MinSizeRel))?$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^custom/CMakeLists\\.txt$", - "line": 3, - "command": "add_custom_target", - "hasParent": true - }, - { - "file": "^custom/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": 3, + "command": "add_custom_target", + "hasParent": true + }, + { + "file": "^custom/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(custom/)?CMakeFiles/([0-9a-f]+/)?custom_tgt(-\\(CONFIG\\))?\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^custom/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_alias_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_alias_exe.json index 342d455..c5a8d94 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_alias_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_alias_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^alias/CMakeLists\\.txt$", - "line": 9, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^alias/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^alias/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json index 849535b..cc1e36c 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 5, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json index ba2ab2a..08e70c3 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/cross/CMakeLists\\.txt$", - "line": 3, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/cross/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 3, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json index ebb6f17..cf51be8 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/cross/CMakeLists\\.txt$", - "line": 6, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/cross/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 6, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader.json index 60e687d..b3b141c 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader.json
@@ -109,52 +109,58 @@ { "path": ".*cmake_pch(_[^.]+)?(\\.hxx)?\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 5, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": ".*/cmake_pch(_[^.]+)?\\.hxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Precompile Header File", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader_2arch.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader_2arch.json index 1c063f5..a4085b0 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader_2arch.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader_2arch.json
@@ -161,82 +161,92 @@ { "path": ".*cmake_pch(_[^.]+)?(\\.hxx)?\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": ".*cmake_pch(_[^.]+)?(\\.hxx)?\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 5, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": ".*/cmake_pch(_[^.]+)?\\.hxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Precompile Header File", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": ".*/cmake_pch(_[^.]+)?\\.hxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Precompile Header File", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader_multigen.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader_multigen.json index acabada..8be7349 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader_multigen.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_precompileheader_multigen.json
@@ -109,97 +109,109 @@ { "path": ".*cmake_pch(_[^.]+)?(\\.hxx)?\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 5, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": ".*/Debug/cmake_pch(_[^.]+)?\\.hxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Precompile Header File", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": ".*/Release/cmake_pch(_[^.]+)?\\.hxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Precompile Header File", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": ".*/MinSizeRel/cmake_pch(_[^.]+)?\\.hxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Precompile Header File", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": ".*/RelWithDebInfo/cmake_pch(_[^.]+)?\\.hxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Precompile Header File", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json index 4bf5c1e..4ef1420 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 51, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 51, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json index 66a735f..f9fe5cb 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/cross/CMakeLists\\.txt$", - "line": 9, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/cross/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": 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 fbd6fd8..0d72b45 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 4, - "command": "add_library", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 4, + "command": "add_library", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_exe.json index 283c7ef..05f579b 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_exe.json
@@ -14,43 +14,47 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": 13, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 13, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(object|build/cxx_object_lib\\.build)/.*/empty(\\.cxx)?\\.o(bj)?$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Object Libraries", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": 14, - "command": "target_link_libraries", - "hasParent": true - }, - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 14, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_lib.json index 02bc1a3..d517fb3 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_lib.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": 12, - "command": "add_library", - "hasParent": true - }, - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 12, + "command": "add_library", + "hasParent": true + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_exe.json index 643bab7..9aed1e1 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 12, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 12, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ], @@ -140,7 +142,7 @@ } ], "interfaceLinkLibraries": null, - "compileDependencies": [ + "compileDependencies": [ { "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$", "backtrace": [
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 fbfe9d1..8c9306e 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
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 11, - "command": "add_library", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 11, + "command": "add_library", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe.json index 4d1ee2d..ed064df 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 28, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 28, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_exe.json index 35ef56b..dede306 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 25, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 25, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_exe.json index a1547c2..c225eae 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 16, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 16, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
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 a4a1450..1022c6b 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
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": 15, - "command": "add_library", - "hasParent": true - }, - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 15, + "command": "add_library", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/exe_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/exe_framework.json index 5f56563..d5ad792 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/exe_framework.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/exe_framework.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^framework/CMakeLists\\.txt$", - "line": 16, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^framework/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^framework/CMakeLists\\.txt$", + "line": 16, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^framework/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/generated_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/generated_exe.json index 30fecb8..e3a8c6b 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/generated_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/generated_exe.json
@@ -14,43 +14,47 @@ { "path": "^.*/Tests/RunCMake/FileAPIExternalSource/empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", - "line": 5, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/generated\\.cxx$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Generated Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", - "line": 6, - "command": "target_sources", - "hasParent": true - }, - { - "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 6, + "command": "target_sources", + "hasParent": true + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/iface_srcs.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/iface_srcs.json index 8530a4c..05ed1f4 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/iface_srcs.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/iface_srcs.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^interface/CMakeLists\\.txt$", - "line": 3, - "command": "add_library", - "hasParent": true - }, - { - "file": "^interface/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^interface/CMakeLists\\.txt$", + "line": 3, + "command": "add_library", + "hasParent": true + }, + { + "file": "^interface/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/inject_direct_lib_impl.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/inject_direct_lib_impl.json index e14f1ba..9d44c4e 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/inject_direct_lib_impl.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/inject_direct_lib_impl.json
@@ -14,22 +14,24 @@ { "path": "^direct/other\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": 6, - "command": "add_library", - "hasParent": true - }, - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 6, + "command": "add_library", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": 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 1c04f48..b7ea263 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json
@@ -14,46 +14,48 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^include_test\\.cmake$", - "line": 3, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^include_test\\.cmake$", - "line": null, - "command": null, - "hasParent": true - }, - { - "file": "^codemodel-v2\\.cmake$", - "line": 10, - "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 - } + "backtraces": [ + [ + { + "file": "^include_test\\.cmake$", + "line": 3, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^include_test\\.cmake$", + "line": null, + "command": null, + "hasParent": true + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": 10, + "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 + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_exe.json index 4603edc..153cf41 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": 9, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_interface_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_interface_exe.json index 640c523..351ed3f 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_interface_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_interface_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": 32, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 32, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_interface_symbolic_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_interface_symbolic_exe.json index b0ed28f..a27040f 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_interface_symbolic_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_interface_symbolic_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": 36, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 36, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_object_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_object_exe.json index 71f62dd..54e8979 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_object_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_object_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": 27, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 27, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_shared_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_shared_exe.json index 011b0c5..d0ca024 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_shared_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_shared_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": 17, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 17, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_static_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_static_exe.json index a0376c1..d839aa2 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_static_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_imported_static_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": 22, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 22, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_transitive_direct_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_transitive_direct_exe.json index c273a69..73313d6 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_transitive_direct_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_transitive_direct_exe.json
@@ -14,43 +14,47 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": 3, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 3, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(direct|build/inject_direct_lib_impl\\.build)/.*/other(\\.c)?\\.o(bj)?$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Object Libraries", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": 13, - "command": "set_property", - "hasParent": true - }, - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 13, + "command": "set_property", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_usage_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_usage_exe.json index 2cc10d2..8931a17 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_usage_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/link_usage_exe.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": 37, - "command": "add_executable", - "hasParent": true - }, - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 37, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
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 5785a12..bb40a6c 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^framework/CMakeLists\\.txt$", - "line": 7, - "command": "add_library", - "hasParent": true - }, - { - "file": "^framework/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^framework/CMakeLists\\.txt$", + "line": 7, + "command": "add_library", + "hasParent": true + }, + { + "file": "^framework/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
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 a347aa2..cac84c9 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.cxx$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "CXX", - "backtrace": [ - { - "file": "^framework/CMakeLists\\.txt$", - "line": 4, - "command": "add_library", - "hasParent": true - }, - { - "file": "^framework/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^framework/CMakeLists\\.txt$", + "line": 4, + "command": "add_library", + "hasParent": true + }, + { + "file": "^framework/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/transitive_direct_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/transitive_direct_lib.json index 7dc75d4..0833748 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/transitive_direct_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/transitive_direct_lib.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": 4, - "command": "add_library", - "hasParent": true - }, - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 4, + "command": "add_library", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/usage_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/usage_lib.json index 2f363bc..29c00eb 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/usage_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/usage_lib.json
@@ -14,22 +14,24 @@ { "path": "^empty\\.c$", "isGenerated": null, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "Source Files", "compileGroupLanguage": "C", - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": 36, - "command": "add_library", - "hasParent": true - }, - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": 36, + "command": "add_library", + "hasParent": true + }, + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_alias.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_alias.json index 9536691..fbb915e 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_alias.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_alias.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^alias/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^alias/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_custom.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_custom.json index 2fcc163..d0e21a1 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_custom.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_custom.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^custom/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^custom/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_cxx.json index a6406fd..2b84b06 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_cxx.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_cxx.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^cxx/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_direct.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_direct.json index ef207b9..20d79ba 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_direct.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_direct.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/direct/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/direct/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^direct/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^direct/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_external.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_external.json index 5852efa..a8bd755 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_external.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_external.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_framework.json index d3e0ca6..342679d 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_framework.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_framework.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/framework/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^framework/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^framework/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/framework/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^framework/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^framework/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_imported.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_imported.json index 81aa8ca..80c5a76 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_imported.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_imported.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^imported/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_interface.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_interface.json index 91c6a25..d1a022a 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_interface.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_interface.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/interface/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^interface/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^interface/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/interface/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^interface/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^interface/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_object.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_object.json index 14314bb..0fd65be 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_object.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_object.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^object/CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^object/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_top.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_top.json index 5f15cb0..08f057c 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_top.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/zero_check_top.json
@@ -14,31 +14,35 @@ { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] }, { "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK\\.rule$", "isGenerated": true, - "fileSetName": null, + "fileSetNames": null, "sourceGroupName": "CMake Rules", "compileGroupLanguage": null, - "backtrace": [ - { - "file": "^CMakeLists\\.txt$", - "line": null, - "command": null, - "hasParent": false - } + "backtraces": [ + [ + { + "file": "^CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] ] } ],
diff --git a/Tests/RunCMake/FileAPI/fileset/CMakeLists.txt b/Tests/RunCMake/FileAPI/fileset/CMakeLists.txt index c318b21..6b7fd0e 100644 --- a/Tests/RunCMake/FileAPI/fileset/CMakeLists.txt +++ b/Tests/RunCMake/FileAPI/fileset/CMakeLists.txt
@@ -2,13 +2,13 @@ target_sources(c_headers_1 PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES error.c other.c - PRIVATE FILE_SET a TYPE HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES h1.h dir/h4.h + PRIVATE FILE_SET a TYPE HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES h1.h dir/h4.h h5.h ) target_sources(c_headers_1 PUBLIC FILE_SET b TYPE HEADERS BASE_DIRS "$<1:${CMAKE_CURRENT_SOURCE_DIR}/dir>" FILES "$<1:${CMAKE_CURRENT_SOURCE_DIR}/dir/h2.h>" ) target_sources(c_headers_1 - INTERFACE FILE_SET c TYPE HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES h3.h + INTERFACE FILE_SET c TYPE HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES h3.h "$<1:h5.h>" ) source_group("Source Files" FILES "${CMAKE_CURRENT_SOURCE_DIR}/other.c") @@ -26,3 +26,25 @@ FILE_SET d ) install(TARGETS c_headers_2) + +# +# SOURCES file set type +# +add_library(c_sources_1 STATIC) + +target_sources(c_sources_1 + PRIVATE FILE_SET SOURCES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES other.c + ) +set_property(FILE_SET SOURCES TARGET c_sources_1 PROPERTY COMPILE_DEFINITIONS DEF1) + +target_sources(c_sources_1 + PUBLIC FILE_SET public_srcs TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES empty.c + ) +set_property(FILE_SET public_srcs TARGET c_sources_1 PROPERTY COMPILE_DEFINITIONS DEF2) +set_property(FILE_SET public_srcs TARGET c_sources_1 PROPERTY INTERFACE_COMPILE_DEFINITIONS DEF3) + +set_property(FILE_SET public_srcs TARGET c_sources_1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/dir") +set_property(FILE_SET public_srcs TARGET c_sources_1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/dir") + +add_library(c_sources_2 STATIC) +target_link_libraries(c_sources_2 PRIVATE c_sources_1)
diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid.cmake b/Tests/RunCMake/FileAPI/fileset/h5.h similarity index 100% rename from Tests/RunCMake/CMakePresetsTest/Invalid.cmake rename to Tests/RunCMake/FileAPI/fileset/h5.h
diff --git a/Tests/RunCMake/FileSet-SOURCES/CMakeLists.txt b/Tests/RunCMake/FileSet-SOURCES/CMakeLists.txt new file mode 100644 index 0000000..7652e18 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.0) +project(${RunCMake_TEST} LANGUAGES NONE) +include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/FileSet-SOURCES/CompileOptionsOrder-build-check.cmake b/Tests/RunCMake/FileSet-SOURCES/CompileOptionsOrder-build-check.cmake new file mode 100644 index 0000000..2d73158 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/CompileOptionsOrder-build-check.cmake
@@ -0,0 +1,9 @@ +if(RunCMake_GENERATOR MATCHES "Xcode") + if (NOT actual_stdout MATCHES "-DOPT_SRC +-DOPT_FS") + set (RunCMake_TEST_FAILED "Wrong compile options order.") + endif() +else() + if (NOT actual_stdout MATCHES "(/|-)D *OPT_TGT.+(/|-)D *OPT_SRC.+(/|-)D *OPT_FS") + set (RunCMake_TEST_FAILED "Wrong compile options order.") + endif() +endif()
diff --git a/Tests/RunCMake/FileSet-SOURCES/CompileOptionsOrder.cmake b/Tests/RunCMake/FileSet-SOURCES/CompileOptionsOrder.cmake new file mode 100644 index 0000000..66fe7ef --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/CompileOptionsOrder.cmake
@@ -0,0 +1,15 @@ +enable_language(C) + +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS OFF) +# ensure no temp file will be used +string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}") +string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}") + +add_library(lib1 STATIC) +target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib1.c) + +set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_OPTIONS -DOPT_FS) + +set_property(TARGET lib1 PROPERTY COMPILE_OPTIONS -DOPT_TGT) +set_property(SOURCE lib1.c PROPERTY COMPILE_OPTIONS -DOPT_SRC)
diff --git a/Tests/RunCMake/FileSet-SOURCES/CustomCommandInput.cmake b/Tests/RunCMake/FileSet-SOURCES/CustomCommandInput.cmake new file mode 100644 index 0000000..2ab3b26 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/CustomCommandInput.cmake
@@ -0,0 +1,10 @@ + +enable_language(C) + +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/custom.c" + COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/lib1.c" + "${CMAKE_CURRENT_BINARY_DIR}/custom.c") + +add_library(lib1 STATIC) +target_sources(lib1 PRIVATE FILE_SET SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES "${CMAKE_CURRENT_BINARY_DIR}/custom.c")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/FileSet-SOURCES/FileNoExist-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/FileSet-SOURCES/FileNoExist-result.txt
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileNoExist-stderr.txt b/Tests/RunCMake/FileSet-SOURCES/FileNoExist-stderr.txt new file mode 100644 index 0000000..4e566b3 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileNoExist-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error at FileNoExist\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + + .+/Tests/RunCMake/FileSet-SOURCES/noexist\.c
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileNoExist.cmake b/Tests/RunCMake/FileSet-SOURCES/FileNoExist.cmake new file mode 100644 index 0000000..c25cba2 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileNoExist.cmake
@@ -0,0 +1,4 @@ +enable_language(C) + +add_library(lib1 STATIC lib1.c) +target_sources(lib1 PRIVATE FILE_SET SOURCES BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES noexist.c)
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetExport.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetExport.cmake new file mode 100644 index 0000000..9f50867 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetExport.cmake
@@ -0,0 +1,40 @@ +enable_language(C) + +add_library(lib1 STATIC) +target_sources(lib1 PRIVATE lib1.c) +target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib2.c) + +set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_DEFINITIONS LIB1_A) +set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_OPTIONS -DOPT_LIB1_A) +set_property(FILE_SET a TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/subdir1>" + "$<INSTALL_INTERFACE:include/subdir1>") + +set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_COMPILE_DEFINITIONS INTERFACE_LIB1_A) +set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_COMPILE_OPTIONS -DOPT_INTERFACE_LIB1_A) +set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/subdir2>" + "$<INSTALL_INTERFACE:include/subdir2>") + +target_sources(lib1 PUBLIC FILE_SET SOURCES FILES lib5.c) + +set_property(FILE_SET SOURCES TARGET lib1 PROPERTY INTERFACE_COMPILE_DEFINITIONS INTERFACE_LIB1_SRCS) +set_property(FILE_SET SOURCES TARGET lib1 PROPERTY INTERFACE_COMPILE_OPTIONS -DOPT_INTERFACE_LIB1_SRCS) +set_property(FILE_SET SOURCES TARGET lib1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/srcs>" + "$<INSTALL_INTERFACE:include/srcs>") + + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib1) +target_compile_definitions(main PRIVATE CONSUMER) + +if(CMAKE_GENERATOR MATCHES "Xcode") + install(TARGETS lib1 EXPORT export FILE_SET a DESTINATION sources + FILE_SET SOURCES DESTINATION sources/srcs) +else() + install(TARGETS lib1 EXPORT export FILE_SET a DESTINATION sources + FILE_SET SOURCES DESTINATION sources/$<IF:$<CONFIG:Debug>,debug,release>) +endif() +install(FILES subdir1/h1.h subdir1/h3.h DESTINATION include/subdir1) +install(FILES subdir2/h2.h subdir2/h3.h DESTINATION include/subdir2) + +install(EXPORT export FILE export.cmake NAMESPACE install:: DESTINATION lib/cmake) +export(EXPORT export FILE export.cmake NAMESPACE export::)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-result.txt
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-stderr.txt b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-stderr.txt new file mode 100644 index 0000000..5c54059 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-stderr.txt
@@ -0,0 +1,3 @@ +CMake Error in CMakeLists\.txt: + The file set "HEADERS", of type "HEADERS", is incompatible with the + "FRAMEWORK" target "lib1"\.
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1.cmake new file mode 100644 index 0000000..7c166a0 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1.cmake
@@ -0,0 +1,8 @@ +enable_language(C) + +add_library(lib1 SHARED) +target_sources(lib1 + PUBLIC FILE_SET SOURCES FILES lib1.c + FILE_SET HEADERS FILES lib1.h + ) +set_property(TARGET lib1 PROPERTY FRAMEWORK ON)
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetFramework2.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework2.cmake new file mode 100644 index 0000000..f577381 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework2.cmake
@@ -0,0 +1,7 @@ +enable_language(C) + +add_library(lib1 SHARED) +target_sources(lib1 + PUBLIC FILE_SET SOURCES FILES lib1.c + ) +set_property(TARGET lib1 PROPERTY FRAMEWORK ON)
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetImport.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetImport.cmake new file mode 100644 index 0000000..53264cf --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetImport.cmake
@@ -0,0 +1,85 @@ +enable_language(C) + +get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + +function(assert_target_prop_eq tgt prop value) + unset(actual_value) + get_property(actual_value TARGET ${tgt} PROPERTY ${prop}) + if(NOT actual_value STREQUAL value) + message(SEND_ERROR "Expected value of target ${prop}:\n ${value}\nActual value:\n ${actual_value}") + endif() +endfunction() + +function(assert_fileset_prop_eq tgt fs prop value) + unset(actual_value) + get_property(actual_value FILE_SET ${fs} TARGET ${tgt} PROPERTY ${prop}) + if(NOT actual_value STREQUAL value) + message(SEND_ERROR "Expected value of file set ${prop}:\n ${value}\nActual value:\n ${actual_value}") + endif() +endfunction() + +cmake_path(GET CMAKE_BINARY_DIR PARENT_PATH export_build_dir) +cmake_path(APPEND export_build_dir "FileSetExport-build") + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_EQUAL 4.3 + AND NOT CMAKE_PATCH_VERSION VERSION_LESS 20000000) + # development version for future 4.4: Force version 4.4 + set(CMAKE_VERSION_BACKUP "${CMAKE_VERSION}") + set(CMAKE_VERSION 4.4) +endif() +include("${export_build_dir}/export.cmake") +include("${export_build_dir}/install/lib/cmake/export.cmake") +if(CMAKE_VERSION VERSION_GREATER 4.3 AND CMAKE_VERSION VERSION_LESS 4.4 + AND NOT CMake_VERSION_PATCH VERSION_LESS 20000000) + # development version for future 4.4: Force version 4.4 + set(CMAKE_VERSION "${CMAKE_VERSION_BACKUP}") +endif() + + +assert_target_prop_eq(export::lib1 SOURCE_SETS "") +assert_target_prop_eq(export::lib1 INTERFACE_SOURCE_SETS "a;SOURCES") +assert_target_prop_eq(export::lib1 SOURCE_SET_a "${CMAKE_CURRENT_SOURCE_DIR}/lib2.c") +assert_target_prop_eq(export::lib1 SOURCE_SET "${CMAKE_CURRENT_SOURCE_DIR}/lib5.c") +assert_target_prop_eq(export::lib1 SOURCE_SET_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/lib5.c") + +assert_fileset_prop_eq(export::lib1 a INTERFACE_COMPILE_DEFINITIONS "INTERFACE_LIB1_A") +assert_fileset_prop_eq(export::lib1 a INTERFACE_COMPILE_OPTIONS "-DOPT_INTERFACE_LIB1_A") +assert_fileset_prop_eq(export::lib1 a INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir2") + +assert_fileset_prop_eq(export::lib1 SOURCES INTERFACE_COMPILE_DEFINITIONS "INTERFACE_LIB1_SRCS") +assert_fileset_prop_eq(export::lib1 SOURCES INTERFACE_COMPILE_OPTIONS "-DOPT_INTERFACE_LIB1_SRCS") +assert_fileset_prop_eq(export::lib1 SOURCES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/srcs") + + +assert_target_prop_eq(install::lib1 SOURCE_SETS "") +assert_target_prop_eq(install::lib1 INTERFACE_SOURCE_SETS "a;SOURCES") +assert_target_prop_eq(install::lib1 SOURCE_SET_a "${export_build_dir}/install/sources/lib2.c") +if(multi_config) + if(CMAKE_GENERATOR MATCHES "Xcode") + assert_target_prop_eq(install::lib1 SOURCE_SET "${export_build_dir}/install/sources/srcs/lib5.c") + assert_target_prop_eq(install::lib1 SOURCE_SET_SOURCES "${export_build_dir}/install/sources/srcs/lib5.c") + else() + assert_target_prop_eq(install::lib1 SOURCE_SET "$<$<CONFIG:Debug>:${export_build_dir}/install/sources/debug/lib5.c>;$<$<CONFIG:Release>:${export_build_dir}/install/sources/release/lib5.c>") + assert_target_prop_eq(install::lib1 SOURCE_SET_SOURCES "$<$<CONFIG:Debug>:${export_build_dir}/install/sources/debug/lib5.c>;$<$<CONFIG:Release>:${export_build_dir}/install/sources/release/lib5.c>") + endif() +else() + assert_target_prop_eq(install::lib1 SOURCE_SET "${export_build_dir}/install/sources/debug/lib5.c") + assert_target_prop_eq(install::lib1 SOURCE_SET_SOURCES "${export_build_dir}/install/sources/debug/lib5.c") +endif() + +assert_fileset_prop_eq(install::lib1 a INTERFACE_COMPILE_DEFINITIONS "INTERFACE_LIB1_A") +assert_fileset_prop_eq(install::lib1 a INTERFACE_COMPILE_OPTIONS "-DOPT_INTERFACE_LIB1_A") +assert_fileset_prop_eq(install::lib1 a INTERFACE_INCLUDE_DIRECTORIES "${export_build_dir}/install/include/subdir2") + +assert_fileset_prop_eq(install::lib1 SOURCES INTERFACE_COMPILE_DEFINITIONS "INTERFACE_LIB1_SRCS") +assert_fileset_prop_eq(install::lib1 SOURCES INTERFACE_COMPILE_OPTIONS "-DOPT_INTERFACE_LIB1_SRCS") +assert_fileset_prop_eq(install::lib1 SOURCES INTERFACE_INCLUDE_DIRECTORIES "${export_build_dir}/install/include/srcs") + + +add_executable(main_export main.c) +target_link_libraries(main_export PRIVATE export::lib1) +target_compile_definitions(main_export PRIVATE CONSUMER) + +add_executable(main_install main.c) +target_link_libraries(main_install PRIVATE install::lib1) +target_compile_definitions(main_install PRIVATE CONSUMER)
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetProperties.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetProperties.cmake new file mode 100644 index 0000000..b463914 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetProperties.cmake
@@ -0,0 +1,19 @@ +enable_language(C) + +add_library(lib1 STATIC) +target_sources(lib1 PRIVATE lib1.c) +target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib2.c) + +set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_DEFINITIONS LIB1_A) +set_property(FILE_SET a TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir1") + +set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_COMPILE_DEFINITIONS INTERFACE_LIB1_A) +set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir2") + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib1) +target_compile_definitions(main PRIVATE CONSUMER) + +add_library(lib2 SHARED) +target_link_libraries(lib2 PRIVATE lib1) +target_compile_definitions(lib2 PRIVATE CONSUMER)
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2-lib1-check.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2-lib1-check.cmake new file mode 100644 index 0000000..2705cba --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2-lib1-check.cmake
@@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)D *OPT_FS" OR actual_stdout MATCHES "(/|-)D *OPT_INTERFACE_FS") + set (RunCMake_TEST_FAILED "Wrong compile options specified.") +endif()
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2-main-check.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2-main-check.cmake new file mode 100644 index 0000000..1637d27 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2-main-check.cmake
@@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)D *OPT_INTERFACE_FS" OR actual_stdout MATCHES "(/|-)D *OPT_FS") + set (RunCMake_TEST_FAILED "Wrong compile options specified.") +endif()
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2.cmake new file mode 100644 index 0000000..79cfe58 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetProperties2.cmake
@@ -0,0 +1,17 @@ +enable_language(C) + +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS OFF) +# ensure no temp file will be used +string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}") +string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}") + +add_library(lib1 STATIC) +target_sources(lib1 PRIVATE lib1.c) +target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib4.c) + +set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_OPTIONS -DOPT_FS) +set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_COMPILE_OPTIONS -DOPT_INTERFACE_FS) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib1)
diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetTransitivity.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetTransitivity.cmake new file mode 100644 index 0000000..0a8f724 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetTransitivity.cmake
@@ -0,0 +1,17 @@ +enable_language(C) + +add_library(lib1 STATIC) +target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib2.c) + +set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_DEFINITIONS LIB1_A) +set_property(FILE_SET a TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir1") + +set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_COMPILE_DEFINITIONS INTERFACE_LIB1_A) +set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir2") + +add_library(lib2 STATIC lib1.c) +target_link_libraries(lib2 INTERFACE lib1) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib2) +target_compile_definitions(main PRIVATE CONSUMER)
diff --git a/Tests/RunCMake/FileSet-SOURCES/IncludeDirectoriesOrder.cmake b/Tests/RunCMake/FileSet-SOURCES/IncludeDirectoriesOrder.cmake new file mode 100644 index 0000000..80a2767 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/IncludeDirectoriesOrder.cmake
@@ -0,0 +1,11 @@ +enable_language(C) + +add_library(lib1 STATIC) +target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib3.c) + +set_property(FILE_SET a TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/dir") + +if(NOT CMAKE_GENERATOR MATCHES "Xcode") + set_property(TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir1") +endif() +set_property(SOURCE lib3.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir2")
diff --git a/Tests/RunCMake/FileSet-SOURCES/IndependentFiles-check.cmake b/Tests/RunCMake/FileSet-SOURCES/IndependentFiles-check.cmake new file mode 100644 index 0000000..748665a --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/IndependentFiles-check.cmake
@@ -0,0 +1,4 @@ +if (EXISTS "${RunCMake_TEST_BINARY_DIR}/gen.c") + list(APPEND RunCMake_TEST_FAILED + "The `gen.c` file should not be generated to compile `unrelated`'s object") +endif ()
diff --git a/Tests/RunCMake/FileSet-SOURCES/IndependentFiles.cmake b/Tests/RunCMake/FileSet-SOURCES/IndependentFiles.cmake new file mode 100644 index 0000000..821270d --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/IndependentFiles.cmake
@@ -0,0 +1,16 @@ + +enable_language(C) + +set(CACHE{CMAKE_INTERMEDIATE_DIR_STRATEGY} TYPE STRING FORCE VALUE FULL) + +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/gen.c" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/gen.c.in" + "${CMAKE_CURRENT_BINARY_DIR}/gen.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/gen.c.in") + +add_library(lib1 STATIC) +target_sources(lib1 PRIVATE independent.c + PRIVATE FILE_SET SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES "${CMAKE_CURRENT_BINARY_DIR}/gen.c") + +set_property(FILE_SET SOURCES TARGET lib1 PROPERTY INDEPENDENT_FILES ON)
diff --git a/Tests/RunCMake/FileSet-SOURCES/IndependentFilesWarning-stderr.txt b/Tests/RunCMake/FileSet-SOURCES/IndependentFilesWarning-stderr.txt new file mode 100644 index 0000000..014ffdb --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/IndependentFilesWarning-stderr.txt
@@ -0,0 +1,5 @@ +CMake Warning \(author\) in CMakeLists\.txt: + the "INDEPENDENT_FILES" property of the file set "HEADERS" of the target + "lib1" will be ignored because it is incompatible with the file set type + "HEADERS"\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/FileSet-SOURCES/IndependentFilesWarning.cmake b/Tests/RunCMake/FileSet-SOURCES/IndependentFilesWarning.cmake new file mode 100644 index 0000000..acfed11 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/IndependentFilesWarning.cmake
@@ -0,0 +1,13 @@ + +enable_language(C) + +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/gen.h" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/gen.c.in" + "${CMAKE_CURRENT_BINARY_DIR}/gen.h" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/gen.c.in") + +add_library(lib1 STATIC lib1.c) +target_sources(lib1 PRIVATE FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES "${CMAKE_CURRENT_BINARY_DIR}/gen.h") + +set_property(FILE_SET HEADERS TARGET lib1 PROPERTY INDEPENDENT_FILES ON)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterface-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterface-result.txt
diff --git a/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterface-stderr.txt b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterface-stderr.txt new file mode 100644 index 0000000..62bef3e --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterface-stderr.txt
@@ -0,0 +1,5 @@ +CMake Error at InstallMissingSetsInterface\.cmake:[0-9]+ \(install\): + install TARGETS target lib1 is exported but not all of its interface file + sets are installed +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterface.cmake b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterface.cmake new file mode 100644 index 0000000..9567291 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterface.cmake
@@ -0,0 +1,5 @@ +enable_language(C) + +add_library(lib1 STATIC) +target_sources(lib1 INTERFACE FILE_SET a TYPE SOURCES BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES lib1.c) +install(TARGETS lib1 EXPORT a)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterfacePostInstall-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterfacePostInstall-result.txt
diff --git a/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterfacePostInstall-stderr.txt b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterfacePostInstall-stderr.txt new file mode 100644 index 0000000..948dbf7 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterfacePostInstall-stderr.txt
@@ -0,0 +1,6 @@ +CMake Error in CMakeLists\.txt: + File set "a" is listed in interface file sets of lib1 but has not been + exported + + +CMake Generate step failed\. Build files cannot be regenerated correctly\.
diff --git a/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterfacePostInstall.cmake b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterfacePostInstall.cmake new file mode 100644 index 0000000..b3c2476 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/InstallMissingSetsInterfacePostInstall.cmake
@@ -0,0 +1,6 @@ +enable_language(C) + +add_library(lib1 STATIC) +install(TARGETS lib1 EXPORT a) +target_sources(lib1 INTERFACE FILE_SET a TYPE SOURCES BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES lib1.c) +install(EXPORT a DESTINATION lib/cmake/test)
diff --git a/Tests/RunCMake/FileSet-SOURCES/RunCMakeTest.cmake b/Tests/RunCMake/FileSet-SOURCES/RunCMakeTest.cmake new file mode 100644 index 0000000..2e2c265 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/RunCMakeTest.cmake
@@ -0,0 +1,107 @@ + +include(RunCMake) + +function(run_and_build test) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(config_options "-DCMAKE_CONFIGURATION_TYPES=Debug") + else() + set(config_options -DCMAKE_BUILD_TYPE=Debug) + endif() + set(RunCMake_TEST_OPTIONS ${config_options}) + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test}-build") + run_cmake_with_options(${test}) + set(RunCMake_TEST_NO_CLEAN 1) + if(ARGN) + foreach(target IN LISTS ARGN) + run_cmake_command(${test}-${target} ${CMAKE_COMMAND} --build . --config Debug --target ${target} --verbose) + endforeach() + else() + run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Debug --verbose) + endif() +endfunction() + +function(run_and_check test) + set(RunCMake_TEST_OUTPUT_MERGE TRUE) + run_and_build(${test} ${ARGN}) +endfunction() + + +run_cmake(FileNoExist) +run_cmake(TargetProperties) + +run_and_build(FileSetProperties) +run_and_build(CustomCommandInput) +run_and_build(IncludeDirectoriesOrder) +run_and_build(FileSetTransitivity) +run_and_check(CompileOptionsOrder) +if(APPLE) + run_cmake(FileSetFramework1) + run_and_build(FileSetFramework2) +endif() +if (RunCMake_GENERATOR MATCHES "Ninja") + run_cmake(IndependentFilesWarning) + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(IndependentFiles_target "CMakeFiles/lib1.dir/Debug/independent.c${CMAKE_C_OUTPUT_EXTENSION}") + else () + set(IndependentFiles_target "CMakeFiles/lib1.dir/independent.c${CMAKE_C_OUTPUT_EXTENSION}") + endif () + set(RunCMake-check-file IndependentFiles-check.cmake) + run_and_build(IndependentFiles ${IndependentFiles_target}) +endif() + +# Some environments are excluded because they are not able to honor verbose mode +if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode" + OR (RunCMake_GENERATOR MATCHES "Visual Studio" AND MSVC_VERSION GREATER_EQUAL "1600")) + AND NOT CMAKE_C_COMPILER_ID STREQUAL "Intel") + run_and_check(FileSetProperties2 lib1 main) +endif() + +function(run_export_import name) + if(ARGV1) + set(importname ${ARGV1}) + else() + set(importname ${name}) + endif() + + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_options "-DCMAKE_CONFIGURATION_TYPES=Debug\\\\;Release") + else() + set(_config_options -DCMAKE_BUILD_TYPE=Debug) + endif() + + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${name}Export-build") + set(RunCMake_TEST_OPTIONS "--install-prefix=${RunCMake_TEST_BINARY_DIR}/install" ${_config_options}) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + run_cmake(${name}Export) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + run_cmake_command(${name}Export-build-Debug ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(${name}Export-install-Debug ${CMAKE_COMMAND} --install . --config Debug) + run_cmake_command(${name}Export-build-Release ${CMAKE_COMMAND} --build . --config Release) + run_cmake_command(${name}Export-install-Release ${CMAKE_COMMAND} --install . --config Release) + else() + run_cmake_command(${name}Export-build ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(${name}Export-install ${CMAKE_COMMAND} --install . --config Debug) + endif() + unset(RunCMake_TEST_OPTIONS) + + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${importname}Import-build") + unset(RunCMake_TEST_OPTIONS) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + run_cmake(${importname}Import) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + run_cmake_command(${importname}Import-build-Debug ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(${importname}Import-build-Release ${CMAKE_COMMAND} --build . --config Release) + else() + run_cmake_command(${importname}Import-build ${CMAKE_COMMAND} --build . --config Debug) + endif() + + unset(RunCMake_TEST_BINARY_DIR) + unset(RunCMake_TEST_NO_CLEAN) +endfunction() + +run_cmake(InstallMissingSetsInterface) +run_cmake(InstallMissingSetsInterfacePostInstall) +run_export_import(FileSet)
diff --git a/Tests/RunCMake/FileSet-SOURCES/TargetProperties.cmake b/Tests/RunCMake/FileSet-SOURCES/TargetProperties.cmake new file mode 100644 index 0000000..2b8d180 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/TargetProperties.cmake
@@ -0,0 +1,61 @@ +enable_language(C) + +function(assert_prop_undef tgt prop) + unset(actual_value) + get_property(actual_value TARGET ${tgt} PROPERTY ${prop}) + if(DEFINED actual_value) + message(SEND_ERROR "${prop} should be undefined, actual value:\n ${actual_value}") + endif() +endfunction() + +function(assert_prop_eq tgt prop value) + unset(actual_value) + get_property(actual_value TARGET ${tgt} PROPERTY ${prop}) + if(NOT actual_value STREQUAL value) + message(SEND_ERROR "Expected value of ${prop}:\n ${value}\nActual value:\n ${actual_value}") + endif() +endfunction() + +add_library(lib1 STATIC) +assert_prop_eq(lib1 SOURCE_SETS "") +assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "") + +target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES BASE_DIRS "." FILES lib1.c lib2.c) +assert_prop_eq(lib1 SOURCE_SETS "a") +assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a") +assert_prop_eq(lib1 SOURCE_DIRS_a "${CMAKE_CURRENT_SOURCE_DIR}/.") +assert_prop_eq(lib1 SOURCE_SET_a "${CMAKE_CURRENT_SOURCE_DIR}/lib1.c;${CMAKE_CURRENT_SOURCE_DIR}/lib2.c") + +target_sources(lib1 PUBLIC FILE_SET a FILES lib3.c) +assert_prop_eq(lib1 SOURCE_SETS "a") +assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a") +assert_prop_eq(lib1 SOURCE_DIRS_a "${CMAKE_CURRENT_SOURCE_DIR}/.") +assert_prop_eq(lib1 SOURCE_SET_a "${CMAKE_CURRENT_SOURCE_DIR}/lib1.c;${CMAKE_CURRENT_SOURCE_DIR}/lib2.c;${CMAKE_CURRENT_SOURCE_DIR}/lib3.c") + +target_sources(lib1 PRIVATE FILE_SET b TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/dir" FILES dir/dir.c) +assert_prop_eq(lib1 SOURCE_SETS "a;b") +assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a") +assert_prop_eq(lib1 SOURCE_DIRS_b "${CMAKE_CURRENT_SOURCE_DIR}/dir") +assert_prop_eq(lib1 SOURCE_SET_b "${CMAKE_CURRENT_SOURCE_DIR}/dir/dir.c") + +target_sources(lib1 INTERFACE FILE_SET c TYPE SOURCES FILE_SET d TYPE SOURCES) +assert_prop_eq(lib1 SOURCE_SETS "a;b") +assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a;c;d") +assert_prop_eq(lib1 SOURCE_DIRS_c "${CMAKE_CURRENT_SOURCE_DIR}") +assert_prop_eq(lib1 SOURCE_SET_c "") +assert_prop_eq(lib1 SOURCE_DIRS_d "${CMAKE_CURRENT_SOURCE_DIR}") +assert_prop_eq(lib1 SOURCE_SET_d "") + +target_sources(lib1 PUBLIC FILE_SET SOURCES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES lib4.c) +assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a;c;d;SOURCES") +assert_prop_eq(lib1 SOURCE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") +assert_prop_eq(lib1 SOURCE_SET "${CMAKE_CURRENT_SOURCE_DIR}/lib4.c") +assert_prop_eq(lib1 SOURCE_DIRS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}") +assert_prop_eq(lib1 SOURCE_SET_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/lib4.c") + +target_sources(lib1 PUBLIC FILE_SET SOURCES FILES lib5.c) +assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a;c;d;SOURCES") +assert_prop_eq(lib1 SOURCE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") +assert_prop_eq(lib1 SOURCE_SET "${CMAKE_CURRENT_SOURCE_DIR}/lib4.c;${CMAKE_CURRENT_SOURCE_DIR}/lib5.c") +assert_prop_eq(lib1 SOURCE_DIRS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}") +assert_prop_eq(lib1 SOURCE_SET_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/lib4.c;${CMAKE_CURRENT_SOURCE_DIR}/lib5.c")
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSet-SOURCES/dir/dir.c similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSet-SOURCES/dir/dir.c
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSet-SOURCES/dir/h3.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSet-SOURCES/dir/h3.h
diff --git a/Tests/RunCMake/FileSet-SOURCES/gen.c.in b/Tests/RunCMake/FileSet-SOURCES/gen.c.in new file mode 100644 index 0000000..caf53fe --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/gen.c.in
@@ -0,0 +1,4 @@ + +void gen(void) +{ +}
diff --git a/Tests/RunCMake/FileSet-SOURCES/independent.c b/Tests/RunCMake/FileSet-SOURCES/independent.c new file mode 100644 index 0000000..f061210 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/independent.c
@@ -0,0 +1,4 @@ + +void independent(void) +{ +}
diff --git a/Tests/RunCMake/FileSet-SOURCES/lib1.c b/Tests/RunCMake/FileSet-SOURCES/lib1.c new file mode 100644 index 0000000..950c340 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/lib1.c
@@ -0,0 +1,14 @@ + +#if defined(INTERFACE_LIB1_A) +# error "INTERFACE_LIB1_A defined" +#endif +#if defined(LIB1_A) +# error "LIB1_A defined" +#endif +#if defined(CONSUMER) +# error "CONSUMER defined" +#endif + +void f1(void) +{ +}
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSet-SOURCES/lib1.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSet-SOURCES/lib1.h
diff --git a/Tests/RunCMake/FileSet-SOURCES/lib2.c b/Tests/RunCMake/FileSet-SOURCES/lib2.c new file mode 100644 index 0000000..f216e2a --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/lib2.c
@@ -0,0 +1,24 @@ + +#if defined(CONSUMER) +# if defined(LIB1_A) +# error "LIB1_A defined" +# endif +# if !defined(INTERFACE_LIB1_A) +# error "INTERFACE_LIB1_A not defined" +# endif + +# include "h2.h" +#else +# if !defined(LIB1_A) +# error "LIB1_A not defined" +# endif +# if defined(INTERFACE_LIB1_A) +# error "INTERFACE_LIB1_A defined" +# endif + +# include "h1.h" +#endif + +void f2(void) +{ +}
diff --git a/Tests/RunCMake/FileSet-SOURCES/lib3.c b/Tests/RunCMake/FileSet-SOURCES/lib3.c new file mode 100644 index 0000000..edb0abb --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/lib3.c
@@ -0,0 +1,7 @@ + + +#include "h3.h" + +void f3(void) +{ +}
diff --git a/Tests/RunCMake/FileSet-SOURCES/lib4.c b/Tests/RunCMake/FileSet-SOURCES/lib4.c new file mode 100644 index 0000000..bbac37d --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/lib4.c
@@ -0,0 +1,4 @@ + +void f2(void) +{ +}
diff --git a/Tests/RunCMake/FileSet-SOURCES/lib5.c b/Tests/RunCMake/FileSet-SOURCES/lib5.c new file mode 100644 index 0000000..431805c --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/lib5.c
@@ -0,0 +1,4 @@ + +void f5(void) +{ +}
diff --git a/Tests/RunCMake/FileSet-SOURCES/main.c b/Tests/RunCMake/FileSet-SOURCES/main.c new file mode 100644 index 0000000..6666e93 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/main.c
@@ -0,0 +1,9 @@ + +extern void f2(void); + +int main(void) +{ + f2(); + + return 0; +}
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSet-SOURCES/subdir1/h1.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSet-SOURCES/subdir1/h1.h
diff --git a/Tests/RunCMake/FileSet-SOURCES/subdir1/h3.h b/Tests/RunCMake/FileSet-SOURCES/subdir1/h3.h new file mode 100644 index 0000000..5ef3114 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/subdir1/h3.h
@@ -0,0 +1,2 @@ + +#error "TARGET INCLUDE_DIRECTORIES"
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSet-SOURCES/subdir2/h2.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSet-SOURCES/subdir2/h2.h
diff --git a/Tests/RunCMake/FileSet-SOURCES/subdir2/h3.h b/Tests/RunCMake/FileSet-SOURCES/subdir2/h3.h new file mode 100644 index 0000000..40a6150 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/subdir2/h3.h
@@ -0,0 +1,2 @@ + +#error "SOURCE INCLUDE_DIRECTORIES"
diff --git a/Tests/RunCMake/FileSetProperties/BASE_DIRS.cmake b/Tests/RunCMake/FileSetProperties/BASE_DIRS.cmake new file mode 100644 index 0000000..4ca91c6 --- /dev/null +++ b/Tests/RunCMake/FileSetProperties/BASE_DIRS.cmake
@@ -0,0 +1,21 @@ + +enable_language(C) + +add_library(foo STATIC foo.c) + + +target_sources(foo PUBLIC FILE_SET HEADERS FILES foo.h) + +get_property(dirs FILE_SET HEADERS TARGET foo PROPERTY BASE_DIRS) +if(NOT dirs STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + message(SEND_ERROR "wrong base dirs: '${dirs}' instead of '${CMAKE_CURRENT_SOURCE_DIR}'") +endif() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/foo.h" "") +set_property(FILE_SET HEADERS TARGET foo PROPERTY BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}") +set_property(FILE_SET HEADERS TARGET foo PROPERTY SOURCES "${CMAKE_CURRENT_BINARY_DIR}/foo.h") + +get_property(dirs FILE_SET HEADERS TARGET foo PROPERTY BASE_DIRS) +if(NOT dirs STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") + message(SEND_ERROR "wrong base dirs: '${dirs}' instead of '${CMAKE_CURRENT_BINARY_DIR}'") +endif()
diff --git a/Tests/RunCMake/FileSetProperties/CMakeLists.txt b/Tests/RunCMake/FileSetProperties/CMakeLists.txt new file mode 100644 index 0000000..6e44ef5 --- /dev/null +++ b/Tests/RunCMake/FileSetProperties/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +project(${RunCMake_TEST}) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/FileSetProperties/ReadOnly-Properties-result.txt similarity index 100% copy from Tests/RunCMake/target_sources/FileSetFramework-result.txt copy to Tests/RunCMake/FileSetProperties/ReadOnly-Properties-result.txt
diff --git a/Tests/RunCMake/FileSetProperties/ReadOnly-Properties-stderr.txt b/Tests/RunCMake/FileSetProperties/ReadOnly-Properties-stderr.txt new file mode 100644 index 0000000..f57079f --- /dev/null +++ b/Tests/RunCMake/FileSetProperties/ReadOnly-Properties-stderr.txt
@@ -0,0 +1,12 @@ +CMake Error at ReadOnly-Properties\.cmake:[0-9]+ \(set_property\): + TYPE property is read-only for the file set "HEADERS of the target "foo" + +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at ReadOnly-Properties\.cmake:[0-9]+ \(set_property\): + SCOPE property is read-only for the file set "HEADERS of the target "foo" + +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/FileSetProperties/ReadOnly-Properties.cmake b/Tests/RunCMake/FileSetProperties/ReadOnly-Properties.cmake new file mode 100644 index 0000000..3113824 --- /dev/null +++ b/Tests/RunCMake/FileSetProperties/ReadOnly-Properties.cmake
@@ -0,0 +1,7 @@ + +add_library(foo STATIC) + +target_sources(foo PUBLIC FILE_SET HEADERS) + +set_property(FILE_SET HEADERS TARGET foo PROPERTY TYPE CXX_MODULES) +set_property(FILE_SET HEADERS TARGET foo PROPERTY SCOPE PRIVATE)
diff --git a/Tests/RunCMake/FileSetProperties/RunCMakeTest.cmake b/Tests/RunCMake/FileSetProperties/RunCMakeTest.cmake new file mode 100644 index 0000000..e52ce14 --- /dev/null +++ b/Tests/RunCMake/FileSetProperties/RunCMakeTest.cmake
@@ -0,0 +1,8 @@ +include(RunCMake) + +run_cmake(ReadOnly-Properties) + +run_cmake(TYPE) +run_cmake(SCOPE) +run_cmake(SOURCES) +run_cmake(BASE_DIRS)
diff --git a/Tests/RunCMake/FileSetProperties/SCOPE.cmake b/Tests/RunCMake/FileSetProperties/SCOPE.cmake new file mode 100644 index 0000000..cc58aa7 --- /dev/null +++ b/Tests/RunCMake/FileSetProperties/SCOPE.cmake
@@ -0,0 +1,10 @@ + +enable_language(C) + +add_library(foo STATIC foo.c) +target_sources(foo PUBLIC FILE_SET HEADERS FILES foo.h) + +get_property(scope FILE_SET HEADERS TARGET foo PROPERTY SCOPE) +if(NOT scope STREQUAL "PUBLIC") + message(SEND_ERROR "wrong scope: ${scope} instead of PUBLIC") +endif()
diff --git a/Tests/RunCMake/FileSetProperties/SOURCES.cmake b/Tests/RunCMake/FileSetProperties/SOURCES.cmake new file mode 100644 index 0000000..c6b36cf --- /dev/null +++ b/Tests/RunCMake/FileSetProperties/SOURCES.cmake
@@ -0,0 +1,112 @@ + +enable_language(C) + +add_library(foo STATIC foo.c) + + +target_sources(foo PUBLIC FILE_SET HEADERS FILES foo.h) + +get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY SOURCES) +if(NOT srcs MATCHES ".*/foo.h$") + message(SEND_ERROR "wrong sources: '${srcs}' instead of 'foo.h'") +endif() +get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY INTERFACE_SOURCES) +if(NOT srcs MATCHES ".*/foo.h$") + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'foo.h'") +endif() + + +set_property(FILE_SET HEADERS TARGET foo PROPERTY SOURCES bar.h) + +get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY SOURCES) +if(NOT srcs MATCHES "[^;]*bar.h$") + message(SEND_ERROR "wrong sources: '${srcs}' instead of 'bar.h'") +endif() +get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY INTERFACE_SOURCES) +if(NOT srcs MATCHES "[^;]*bar.h$") + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'bar.h'") +endif() + + +set_property(FILE_SET HEADERS TARGET foo APPEND PROPERTY SOURCES foo.h) + +get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY SOURCES) +if(NOT srcs MATCHES "[^;]*bar.h;[^;]*foo.h$") + message(SEND_ERROR "wrong sources: '${srcs}' instead of 'foo.h;bar.h'") +endif() +get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY INTERFACE_SOURCES) +if(NOT srcs MATCHES "[^;]*bar.h;[^;]*foo.h$") + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'foo.h;bar.h'") +endif() + + +target_sources(foo PRIVATE FILE_SET foo TYPE HEADERS FILES h1.h) + +get_property(srcs FILE_SET foo TARGET foo PROPERTY SOURCES) +if(NOT srcs MATCHES ".*/h1.h$") + message(SEND_ERROR "wrong sources: '${srcs}' instead of 'h1.h'") +endif() +get_property(srcs FILE_SET foo TARGET foo PROPERTY INTERFACE_SOURCES) +if(srcs) + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of empty list") +endif() + + +set_property(FILE_SET foo TARGET foo PROPERTY SOURCES h2.h) + +get_property(srcs FILE_SET foo TARGET foo PROPERTY SOURCES) +if(NOT srcs MATCHES "[^;]*h2.h$") + message(SEND_ERROR "wrong sources: '${srcs}' instead of 'h2.h'") +endif() +get_property(srcs FILE_SET foo TARGET foo PROPERTY INTERFACE_SOURCES) +if(srcs) + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of empty list") +endif() + + +set_property(FILE_SET foo TARGET foo PROPERTY INTERFACE_SOURCES h2.h) + +get_property(srcs FILE_SET foo TARGET foo PROPERTY SOURCES) +if(NOT srcs MATCHES "[^;]*h2.h$") + message(SEND_ERROR "wrong sources: '${srcs}' instead of 'h2.h'") +endif() +get_property(srcs FILE_SET foo TARGET foo PROPERTY INTERFACE_SOURCES) +if(srcs) + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of empty list") +endif() + + +target_sources(foo INTERFACE FILE_SET bar TYPE HEADERS FILES foo.h) + +get_property(srcs FILE_SET bar TARGET foo PROPERTY SOURCES) +if(srcs) + message(SEND_ERROR "wrong sources: '${srcs}' instead of empty list") +endif() +get_property(srcs FILE_SET bar TARGET foo PROPERTY INTERFACE_SOURCES) +if(NOT srcs MATCHES "[^;]*/foo.h$") + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'foo.h'") +endif() + + +set_property(FILE_SET bar TARGET foo PROPERTY INTERFACE_SOURCES bar.h) + +get_property(srcs FILE_SET bar TARGET foo PROPERTY SOURCES) +if(srcs) + message(SEND_ERROR "wrong sources: '${srcs}' instead of empty list") +endif() +get_property(srcs FILE_SET bar TARGET foo PROPERTY INTERFACE_SOURCES) +if(NOT srcs MATCHES "[^;]*bar.h$") + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'bar.h'") +endif() + + +set_property(FILE_SET bar TARGET foo PROPERTY SOURCES foo.h) + +get_property(srcs FILE_SET bar TARGET foo PROPERTY SOURCES) +if(srcs) + message(SEND_ERROR "wrong sources: '${srcs}' instead of empty list") +endif() +get_property(srcs FILE_SET bar TARGET foo PROPERTY INTERFACE_SOURCES) +if(NOT srcs MATCHES "[^;]*bar.h$") + message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'bar.h'") +endif()
diff --git a/Tests/RunCMake/FileSetProperties/TYPE.cmake b/Tests/RunCMake/FileSetProperties/TYPE.cmake new file mode 100644 index 0000000..c3026f8 --- /dev/null +++ b/Tests/RunCMake/FileSetProperties/TYPE.cmake
@@ -0,0 +1,10 @@ + +enable_language(C) + +add_library(foo STATIC foo.c) +target_sources(foo PUBLIC FILE_SET HEADERS FILES foo.h) + +get_property(type FILE_SET HEADERS TARGET foo PROPERTY TYPE) +if(NOT type STREQUAL "HEADERS") + message(SEND_ERROR "wrong type: ${type} instead of HEADERS") +endif()
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSetProperties/bar.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSetProperties/bar.h
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSetProperties/foo.c similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSetProperties/foo.c
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSetProperties/foo.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSetProperties/foo.h
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSetProperties/h1.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSetProperties/h1.h
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/FileSetProperties/h2.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/FileSetProperties/h2.h
diff --git a/Tests/RunCMake/File_Archive/CMP0213-NEW.cmake b/Tests/RunCMake/File_Archive/CMP0213-NEW.cmake new file mode 100644 index 0000000..bf126fe --- /dev/null +++ b/Tests/RunCMake/File_Archive/CMP0213-NEW.cmake
@@ -0,0 +1,10 @@ +set(OUTPUT_NAME "test.zip") + +cmake_policy(SET CMP0213 NEW) + +set(ARCHIVE_FORMAT zip) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/File_Archive/CMP0213-OLD-fails-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/File_Archive/CMP0213-OLD-fails-result.txt
diff --git a/Tests/RunCMake/File_Archive/CMP0213-OLD-fails-stderr.txt b/Tests/RunCMake/File_Archive/CMP0213-OLD-fails-stderr.txt new file mode 100644 index 0000000..1976196 --- /dev/null +++ b/Tests/RunCMake/File_Archive/CMP0213-OLD-fails-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error: archive_write_header: Can't translate [Pp]athname( '[^']*\.txt')? to [^ +]* +CMake Error at roundtrip\.cmake:[0-9]+ \(file\): + file failed to compress:
diff --git a/Tests/RunCMake/File_Archive/CMP0213-OLD-fails.cmake b/Tests/RunCMake/File_Archive/CMP0213-OLD-fails.cmake new file mode 100644 index 0000000..bf5dafa --- /dev/null +++ b/Tests/RunCMake/File_Archive/CMP0213-OLD-fails.cmake
@@ -0,0 +1,11 @@ +set(OUTPUT_NAME "bad.zip") + +cmake_policy(SET CMP0213 OLD) + +set(ARCHIVE_FORMAT zip) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/File_Archive/CMP0213-OLD.cmake b/Tests/RunCMake/File_Archive/CMP0213-OLD.cmake new file mode 100644 index 0000000..6829cf1 --- /dev/null +++ b/Tests/RunCMake/File_Archive/CMP0213-OLD.cmake
@@ -0,0 +1,10 @@ +set(OUTPUT_NAME "test.zip") + +cmake_policy(SET CMP0213 OLD) + +set(ARCHIVE_FORMAT zip) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/File_Archive/CMP0213-WARN-fails-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/File_Archive/CMP0213-WARN-fails-result.txt
diff --git a/Tests/RunCMake/File_Archive/CMP0213-WARN-fails-stderr.txt b/Tests/RunCMake/File_Archive/CMP0213-WARN-fails-stderr.txt new file mode 100644 index 0000000..1976196 --- /dev/null +++ b/Tests/RunCMake/File_Archive/CMP0213-WARN-fails-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error: archive_write_header: Can't translate [Pp]athname( '[^']*\.txt')? to [^ +]* +CMake Error at roundtrip\.cmake:[0-9]+ \(file\): + file failed to compress:
diff --git a/Tests/RunCMake/File_Archive/CMP0213-WARN-fails.cmake b/Tests/RunCMake/File_Archive/CMP0213-WARN-fails.cmake new file mode 100644 index 0000000..30b2260 --- /dev/null +++ b/Tests/RunCMake/File_Archive/CMP0213-WARN-fails.cmake
@@ -0,0 +1,11 @@ +set(OUTPUT_NAME "bad.zip") + +# No need to set CMP0213 - WARN is default + +set(ARCHIVE_FORMAT zip) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/File_Archive/CMP0213-WARN.cmake b/Tests/RunCMake/File_Archive/CMP0213-WARN.cmake new file mode 100644 index 0000000..795c980 --- /dev/null +++ b/Tests/RunCMake/File_Archive/CMP0213-WARN.cmake
@@ -0,0 +1,10 @@ +set(OUTPUT_NAME "test.zip") + +# No need to set CMP0213 - WARN is default + +set(ARCHIVE_FORMAT zip) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake index a49c7f0..ba860dc 100644 --- a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake +++ b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake
@@ -30,11 +30,47 @@ run_cmake(threads-xz) run_cmake(threads-zstd) +# Encoding tests. These rely on UTF-8 encoding of our test sources. +if(NOT DEFINED CMake_TEST_LOCALE_CHARSET) + cmake_host_system_information(RESULT CMake_TEST_LOCALE_CHARSET QUERY LOCALE_CHARSET) + message(STATUS "Detected LOCALE_CHARSET '${CMake_TEST_LOCALE_CHARSET}'") +endif() + +if(CMake_TEST_LOCALE_CHARSET STREQUAL "UTF-8") + if(CMAKE_HOST_WIN32) + # The OEM encoding cannot represent our UTF-8 test paths. + run_cmake(encoding-oem-gnutar-fails) + run_cmake(encoding-oem-zip-fails) + run_cmake(CMP0213-OLD-fails) + run_cmake(CMP0213-WARN-fails) + else() + run_cmake(encoding-oem-gnutar) + run_cmake(encoding-oem-zip) + run_cmake(CMP0213-OLD) + run_cmake(CMP0213-WARN) + endif() + + run_cmake(CMP0213-NEW) + + run_cmake(encoding-oem-7zip) + run_cmake(encoding-oem-pax) + run_cmake(encoding-oem-paxr) + + run_cmake(encoding-utf-8-7zip) + run_cmake(encoding-utf-8-gnutar) + run_cmake(encoding-utf-8-pax) + run_cmake(encoding-utf-8-paxr) + run_cmake(encoding-utf-8-zip) +else() + message(STATUS "encoding-* - SKIPPED due to non-UTF-8 locale") +endif() + # Extracting only selected files or directories run_cmake(zip-filtered) run_cmake(create-missing-args) run_cmake(extract-missing-args) +run_cmake(extract-missing-pattern) run_cmake(unsupported-format) run_cmake(zip-with-bad-compression)
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-7zip.cmake b/Tests/RunCMake/File_Archive/encoding-oem-7zip.cmake new file mode 100644 index 0000000..f0e73d2 --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-7zip.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.7z") + +set(ARCHIVE_FORMAT 7zip) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("377abcaf271c" LIMIT 6 HEX)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/File_Archive/encoding-oem-gnutar-fails-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/File_Archive/encoding-oem-gnutar-fails-result.txt
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-gnutar-fails-stderr.txt b/Tests/RunCMake/File_Archive/encoding-oem-gnutar-fails-stderr.txt new file mode 100644 index 0000000..1976196 --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-gnutar-fails-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error: archive_write_header: Can't translate [Pp]athname( '[^']*\.txt')? to [^ +]* +CMake Error at roundtrip\.cmake:[0-9]+ \(file\): + file failed to compress:
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-gnutar-fails.cmake b/Tests/RunCMake/File_Archive/encoding-oem-gnutar-fails.cmake new file mode 100644 index 0000000..63508be --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-gnutar-fails.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "bad.tar") + +set(ARCHIVE_FORMAT gnutar) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172202000" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-gnutar.cmake b/Tests/RunCMake/File_Archive/encoding-oem-gnutar.cmake new file mode 100644 index 0000000..3f67fef --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-gnutar.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.tar") + +set(ARCHIVE_FORMAT gnutar) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172202000" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-pax.cmake b/Tests/RunCMake/File_Archive/encoding-oem-pax.cmake new file mode 100644 index 0000000..ade4a86 --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-pax.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.tar") + +set(ARCHIVE_FORMAT pax) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172003030" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-paxr.cmake b/Tests/RunCMake/File_Archive/encoding-oem-paxr.cmake new file mode 100644 index 0000000..010e10f --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-paxr.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.tar") + +set(ARCHIVE_FORMAT paxr) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172003030" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/File_Archive/encoding-oem-zip-fails-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/File_Archive/encoding-oem-zip-fails-result.txt
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-zip-fails-stderr.txt b/Tests/RunCMake/File_Archive/encoding-oem-zip-fails-stderr.txt new file mode 100644 index 0000000..1976196 --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-zip-fails-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error: archive_write_header: Can't translate [Pp]athname( '[^']*\.txt')? to [^ +]* +CMake Error at roundtrip\.cmake:[0-9]+ \(file\): + file failed to compress:
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-zip-fails.cmake b/Tests/RunCMake/File_Archive/encoding-oem-zip-fails.cmake new file mode 100644 index 0000000..fd48639 --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-zip-fails.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "bad.zip") + +set(ARCHIVE_FORMAT zip) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-oem-zip.cmake b/Tests/RunCMake/File_Archive/encoding-oem-zip.cmake new file mode 100644 index 0000000..3e1bc3d --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-oem-zip.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.zip") + +set(ARCHIVE_FORMAT zip) +set(ENCODING OEM) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-utf-8-7zip.cmake b/Tests/RunCMake/File_Archive/encoding-utf-8-7zip.cmake new file mode 100644 index 0000000..8fba09f --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-utf-8-7zip.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.7z") + +set(ARCHIVE_FORMAT 7zip) +set(ENCODING UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("377abcaf271c" LIMIT 6 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-utf-8-gnutar.cmake b/Tests/RunCMake/File_Archive/encoding-utf-8-gnutar.cmake new file mode 100644 index 0000000..2a25ffc --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-utf-8-gnutar.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.tar") + +set(ARCHIVE_FORMAT gnutar) +set(ENCODING UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172202000" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-utf-8-pax.cmake b/Tests/RunCMake/File_Archive/encoding-utf-8-pax.cmake new file mode 100644 index 0000000..7c813e1 --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-utf-8-pax.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.tar") + +set(ARCHIVE_FORMAT pax) +set(ENCODING UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172003030" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-utf-8-paxr.cmake b/Tests/RunCMake/File_Archive/encoding-utf-8-paxr.cmake new file mode 100644 index 0000000..26ff3cd --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-utf-8-paxr.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.tar") + +set(ARCHIVE_FORMAT paxr) +set(ENCODING UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("7573746172003030" OFFSET 257 LIMIT 8 HEX)
diff --git a/Tests/RunCMake/File_Archive/encoding-utf-8-zip.cmake b/Tests/RunCMake/File_Archive/encoding-utf-8-zip.cmake new file mode 100644 index 0000000..b71cc41 --- /dev/null +++ b/Tests/RunCMake/File_Archive/encoding-utf-8-zip.cmake
@@ -0,0 +1,9 @@ +set(OUTPUT_NAME "test.zip") + +set(ARCHIVE_FORMAT zip) +set(ENCODING UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/utf-8-paths.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/File_Archive/extract-missing-pattern-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/File_Archive/extract-missing-pattern-result.txt
diff --git a/Tests/RunCMake/File_Archive/extract-missing-pattern-stderr.txt b/Tests/RunCMake/File_Archive/extract-missing-pattern-stderr.txt new file mode 100644 index 0000000..ecb6b2b --- /dev/null +++ b/Tests/RunCMake/File_Archive/extract-missing-pattern-stderr.txt
@@ -0,0 +1 @@ +^CMake Error: tar: no_such_entry: Not found in archive
diff --git a/Tests/RunCMake/File_Archive/extract-missing-pattern.cmake b/Tests/RunCMake/File_Archive/extract-missing-pattern.cmake new file mode 100644 index 0000000..bd48c81 --- /dev/null +++ b/Tests/RunCMake/File_Archive/extract-missing-pattern.cmake
@@ -0,0 +1,26 @@ +# A PATTERNS entry that matches no entry in the archive must fail with a clear +# "Not found in archive" diagnostic. This exercises the most user-reachable +# error path in extract_tar() (the one that previously leaked all three +# libarchive handles), so it guards that the error path still behaves correctly +# after the RAII cleanup refactor. + +set(archive "${CMAKE_CURRENT_BINARY_DIR}/test.zip") +set(src "${CMAKE_CURRENT_BINARY_DIR}/src") +set(dest "${CMAKE_CURRENT_BINARY_DIR}/dest") + +file(REMOVE "${archive}") +file(REMOVE_RECURSE "${src}" "${dest}") +file(MAKE_DIRECTORY "${src}") +file(WRITE "${src}/f1.txt" "content\n") +file(MAKE_DIRECTORY "${dest}") + +file(ARCHIVE_CREATE + OUTPUT "${archive}" + FORMAT zip + PATHS "${src}") + +# "no_such_entry" matches nothing in the archive, so extraction must fail. +file(ARCHIVE_EXTRACT + INPUT "${archive}" + DESTINATION "${dest}" + PATTERNS no_such_entry)
diff --git a/Tests/RunCMake/File_Archive/roundtrip.cmake b/Tests/RunCMake/File_Archive/roundtrip.cmake index 78a138d..bdbc600 100644 --- a/Tests/RunCMake/File_Archive/roundtrip.cmake +++ b/Tests/RunCMake/File_Archive/roundtrip.cmake
@@ -20,6 +20,7 @@ "d_4/f1.txt" "d-4/f1.txt" "My Special Directory/f1.txt" + ${COMPRESSION_ADDITIONAL_FILE_NAMES} ) foreach(file ${CHECK_FILES}) @@ -53,16 +54,23 @@ file(MAKE_DIRECTORY ${FULL_DECOMPRESS_DIR}) endif() +set(ENCODING_OPTIONS "") +if (ENCODING) + set(ENCODING_OPTIONS ENCODING "${ENCODING}") +endif() + file(ARCHIVE_CREATE OUTPUT ${FULL_OUTPUT_NAME} FORMAT "${ARCHIVE_FORMAT}" COMPRESSION "${COMPRESSION_TYPE}" WORKING_DIRECTORY "${WORKING_DIRECTORY}" + ${ENCODING_OPTIONS} VERBOSE PATHS ${FULL_COMPRESS_DIR}) file(ARCHIVE_EXTRACT INPUT ${FULL_OUTPUT_NAME} + ${ENCODING_OPTIONS} ${DECOMPRESSION_OPTIONS} DESTINATION ${FULL_DECOMPRESS_DIR} VERBOSE) @@ -76,18 +84,25 @@ set(output ${FULL_DECOMPRESS_DIR}/${CUSTOM_OUTPUT_DIRECTORY}/${COMPRESS_DIR}/${file}) if(NOT EXISTS ${input}) - message(SEND_ERROR "Cannot find input file ${input}") + message(SEND_ERROR "Cannot find input file:\n \"${output}\"") endif() if(NOT EXISTS ${output}) - message(SEND_ERROR "Cannot find output file ${output}") + message(SEND_ERROR "Cannot find output file:\n \"${output}\"") endif() file(MD5 ${input} input_md5) file(MD5 ${output} output_md5) if(NOT input_md5 STREQUAL output_md5) - message(SEND_ERROR "Files \"${input}\" and \"${output}\" are different") + message(SEND_ERROR + "Files:\n" + " \"${input}\"\n" + "and\n" + " \"${output}\"\n" + "are different" + ) + endif() endforeach() @@ -95,7 +110,7 @@ set(output ${FULL_DECOMPRESS_DIR}/${COMPRESS_DIR}/${file}) if(EXISTS ${output}) - message(SEND_ERROR "File ${output} exists but it shouldn't") + message(SEND_ERROR "File exists but it shouldn't:\n \"${output}\"") endif() endforeach()
diff --git a/Tests/RunCMake/File_Archive/utf-8-paths.cmake b/Tests/RunCMake/File_Archive/utf-8-paths.cmake new file mode 100644 index 0000000..af85ffa --- /dev/null +++ b/Tests/RunCMake/File_Archive/utf-8-paths.cmake
@@ -0,0 +1,10 @@ +list(APPEND COMPRESSION_ADDITIONAL_FILE_NAMES + "arabic-نَسِيج.txt" + "armenian-Բարեւ ձեզ.txt" + "chinese-质地.txt" + "cyrillic-привет.txt" + "greek-Γεια.txt" + "hungarian-hűtő.txt" + "japanese-テクスチャ.txt" + "korean-조직.txt" +)
diff --git a/Tests/RunCMake/File_Generate/CMP0070-OLD-stderr.txt b/Tests/RunCMake/File_Generate/CMP0070-OLD-stderr.txt index 0231741..e6aa1cd 100644 --- a/Tests/RunCMake/File_Generate/CMP0070-OLD-stderr.txt +++ b/Tests/RunCMake/File_Generate/CMP0070-OLD-stderr.txt
@@ -1,12 +1,14 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_policy\): Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument <min> value\. Or, use the <min>\.\.\.<max> syntax to tell CMake that the project requires at least <min> but has been updated to work with policies introduced by <max> or earlier\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Deprecation Warning at CMP0070-OLD\.cmake:[0-9]+ \(cmake_policy\): +CMake Warning \(deprecated\) at CMP0070-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0070 will be removed from a future version of CMake\. @@ -15,4 +17,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt b/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt index 4a0c117..0d708b0 100644 --- a/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt +++ b/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt
@@ -1,12 +1,14 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_policy\): Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument <min> value\. Or, use the <min>\.\.\.<max> syntax to tell CMake that the project requires at least <min> but has been updated to work with policies introduced by <max> or earlier\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -(CMake Warning \(dev\) in CMakeLists\.txt: +(CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0070 is not set: Define file\(GENERATE\) behavior for relative paths\. Run "cmake --help-policy CMP0070" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -18,9 +20,10 @@ This is not defined behavior unless CMP0070 is set to NEW\. For compatibility with older versions of CMake, the previous undefined behavior will be used\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. +)+ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0070 is not set: Define file\(GENERATE\) behavior for relative paths\. Run "cmake --help-policy CMP0070" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -32,9 +35,10 @@ This is not defined behavior unless CMP0070 is set to NEW\. For compatibility with older versions of CMake, the previous undefined behavior will be used\. -This warning is for project developers\. Use -Wno-dev to suppress it\.( +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.( + -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0070 is not set: Define file\(GENERATE\) behavior for relative paths\. Run "cmake --help-policy CMP0070" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -46,4 +50,5 @@ This is not defined behavior unless CMP0070 is set to NEW\. For compatibility with older versions of CMake, the previous undefined behavior will be used\. -This warning is for project developers\. Use -Wno-dev to suppress it\.)+$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.)+$
diff --git a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt index b0ec351..d487f16 100644 --- a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt +++ b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt
@@ -1,12 +1,14 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_policy\): Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument <min> value\. Or, use the <min>\.\.\.<max> syntax to tell CMake that the project requires at least <min> but has been updated to work with policies introduced by <max> or earlier\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Deprecation Warning at SourceProperty-CMP0070-OLD\.cmake:[0-9]+ \(cmake_policy\): +CMake Warning \(deprecated\) at SourceProperty-CMP0070-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0070 will be removed from a future version of CMake\. @@ -16,6 +18,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at SourceProperty-CMP0070-OLD\.cmake:[0-9]+ \(target_sources\): Cannot find source file:
diff --git a/Tests/RunCMake/File_Generate/SourceProperty-stderr.txt b/Tests/RunCMake/File_Generate/SourceProperty-stderr.txt index c55e3b7..e25b783 100644 --- a/Tests/RunCMake/File_Generate/SourceProperty-stderr.txt +++ b/Tests/RunCMake/File_Generate/SourceProperty-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at SourceProperty\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at SourceProperty\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0070 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/FindBoost/CMP0093-OLD-stderr.txt b/Tests/RunCMake/FindBoost/CMP0093-OLD-stderr.txt index 4ecd961..6b62ee0 100644 --- a/Tests/RunCMake/FindBoost/CMP0093-OLD-stderr.txt +++ b/Tests/RunCMake/FindBoost/CMP0093-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0093-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0093-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0093 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/FindOpenGL/CMP0072-OLD-stderr.txt b/Tests/RunCMake/FindOpenGL/CMP0072-OLD-stderr.txt index 4e482a3..d1a478b 100644 --- a/Tests/RunCMake/FindOpenGL/CMP0072-OLD-stderr.txt +++ b/Tests/RunCMake/FindOpenGL/CMP0072-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0072-OLD\.cmake:1 \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0072-OLD\.cmake:1 \(cmake_policy\): The OLD behavior for policy CMP0072 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:3 \(include\)$ + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/FindOpenGL/CMP0072-WARN-stderr.txt b/Tests/RunCMake/FindOpenGL/CMP0072-WARN-stderr.txt index 8b5e2ca..49e555f 100644 --- a/Tests/RunCMake/FindOpenGL/CMP0072-WARN-stderr.txt +++ b/Tests/RunCMake/FindOpenGL/CMP0072-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at .*/Modules/FindOpenGL\.cmake:[0-9]+ \(message\): +^CMake Warning \(policy\) at .*/Modules/FindOpenGL\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0072 is not set: FindOpenGL prefers GLVND by default when available\. Run "cmake --help-policy CMP0072" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -18,4 +18,5 @@ CMP0072-common\.cmake:[0-9]+ \(find_package\) CMP0072-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/FindPython/RequiredArtifacts.cmake b/Tests/RunCMake/FindPython/RequiredArtifacts.cmake index 51895ef..385c210 100644 --- a/Tests/RunCMake/FindPython/RequiredArtifacts.cmake +++ b/Tests/RunCMake/FindPython/RequiredArtifacts.cmake
@@ -70,6 +70,19 @@ unset(_Python3_SABI_LIBRARY_RELEASE CACHE) endblock() +block(SCOPE_FOR VARIABLES) + set(components Interpreter) + if (CMake_TEST_FindPython3_SABIModule) + list (APPEND components Development.SABIModule) + endif() + find_package(Python3 REQUIRED COMPONENTS ${components}) + if (NOT Python3_FOUND) + message (FATAL_ERROR "Failed to find Python 3") + endif() + unset(_Python3_EXECUTABLE CACHE) + unset(_Python3_SABI_LIBRARY_RELEASE CACHE) +endblock() + set(components Interpreter Development) if (CMake_TEST_FindPython3_SABIModule)
diff --git a/Tests/RunCMake/FindPython/RunCMakeTest.cmake b/Tests/RunCMake/FindPython/RunCMakeTest.cmake index a4d667d..1719069 100644 --- a/Tests/RunCMake/FindPython/RunCMakeTest.cmake +++ b/Tests/RunCMake/FindPython/RunCMakeTest.cmake
@@ -322,7 +322,9 @@ run_python(ArtifactsInteractive VARIANT "OFF" OPTIONS -DCMake_TEST_FindPython3_NumPy=${CMake_TEST_FindPython3_NumPy} -DPython3_ARTIFACTS_INTERACTIVE=OFF) - run_python(Android OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/android_toolchain.cmake") + if(NOT WIN32) + run_python(Android OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/android_toolchain.cmake") + endif() endif() if(CMake_TEST_FindPython2 OR CMake_TEST_FindPython3)
diff --git a/Tests/RunCMake/Framework/InstallBeforeFramework-stderr.txt b/Tests/RunCMake/Framework/InstallBeforeFramework-stderr.txt index 083e6d6..27757d5 100644 --- a/Tests/RunCMake/Framework/InstallBeforeFramework-stderr.txt +++ b/Tests/RunCMake/Framework/InstallBeforeFramework-stderr.txt
@@ -1,7 +1,7 @@ -^CMake Warning \(dev\) at InstallBeforeFramework\.cmake:4 \(install\): +^CMake Warning \(author\) at InstallBeforeFramework\.cmake:4 \(install\): Target 'foo' was changed to a FRAMEWORK sometime after install\(\)\. This may result in the wrong install DESTINATION\. Set the FRAMEWORK property earlier\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/GNUInstallDirs/GetAbs-stderr.txt b/Tests/RunCMake/GNUInstallDirs/GetAbs-stderr.txt index 78fcd87..5ee4217 100644 --- a/Tests/RunCMake/GNUInstallDirs/GetAbs-stderr.txt +++ b/Tests/RunCMake/GNUInstallDirs/GetAbs-stderr.txt
@@ -1,5 +1,5 @@ ^PROJ1_FULL_BINDIR='/usr/bin' -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/GNUInstallDirs\.cmake:[0-9]+ \(message\): GNUInstallDirs_get_absolute_install_dir called without third argument\. Using \${dir} from the caller's scope for compatibility with CMake 3\.19 and @@ -7,6 +7,6 @@ Call Stack \(most recent call first\): GetAbs\.cmake:10 \(GNUInstallDirs_get_absolute_install_dir\) CMakeLists\.txt:5 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + PROJ2_FULL_BINDIR='/usr/bin'$
diff --git a/Tests/RunCMake/GNUInstallDirs/NoSystem-stderr.txt b/Tests/RunCMake/GNUInstallDirs/NoSystem-stderr.txt index fe9bbed..a0039c6 100644 --- a/Tests/RunCMake/GNUInstallDirs/NoSystem-stderr.txt +++ b/Tests/RunCMake/GNUInstallDirs/NoSystem-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at .*/Modules/GNUInstallDirs\.cmake:[0-9]+ \(message\): +^CMake Warning \(author\) at .*/Modules/GNUInstallDirs\.cmake:[0-9]+ \(message\): Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known\. Please enable at least one language before including GNUInstallDirs\. @@ -8,4 +8,4 @@ .*GNUInstallDirs\.cmake:[0-9]+ \(_GNUInstallDirs_cache_path\) NoSystem\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/GenEx-LINK_GROUP/library-ignored-stderr.txt b/Tests/RunCMake/GenEx-LINK_GROUP/library-ignored-stderr.txt index 67605f8..580b949 100644 --- a/Tests/RunCMake/GenEx-LINK_GROUP/library-ignored-stderr.txt +++ b/Tests/RunCMake/GenEx-LINK_GROUP/library-ignored-stderr.txt
@@ -1,13 +1,13 @@ -CMake Warning \(dev\) at library-ignored\.cmake:[0-9]+ \(add_library\): +CMake Warning \(author\) at library-ignored\.cmake:[0-9]+ \(add_library\): The feature 'feat', specified as part of a generator-expression '\$<LINK_GROUP:feat>', will not be applied to the INTERFACE library 'front'\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at library-ignored\.cmake:[0-9]+ \(add_library\): +CMake Warning \(author\) at library-ignored\.cmake:[0-9]+ \(add_library\): The feature 'feat', specified as part of a generator-expression '\$<LINK_GROUP:feat>', will not be applied to the OBJECT library 'dep'\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt index f3f7198..0883909 100644 --- a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
@@ -1,21 +1,21 @@ -CMake Warning \(dev\) at library-ignored\.cmake:[0-9]+ \(add_library\): +CMake Warning \(author\) at library-ignored\.cmake:[0-9]+ \(add_library\): The feature 'feat', specified as part of a generator-expression '\$<LINK_LIBRARY:feat>', will not be applied to the INTERFACE_LIBRARY 'front'\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at library-ignored\.cmake:[0-9]+ \(add_library\): +CMake Warning \(author\) at library-ignored\.cmake:[0-9]+ \(add_library\): The feature 'feat', specified as part of a generator-expression '\$<LINK_LIBRARY:feat>', will not be applied to the OBJECT_LIBRARY 'dep'\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at library-ignored\.cmake:[0-9]+ \(add_library\): +CMake Warning \(author\) at library-ignored\.cmake:[0-9]+ \(add_library\): The feature 'feat', specified as part of a generator-expression '\$<LINK_LIBRARY:feat>', will not be applied to the SHARED_LIBRARY 'lib'\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type-stderr.txt index 4120139..41bd1ea 100644 --- a/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type-stderr.txt +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type-stderr.txt
@@ -1,6 +1,6 @@ -CMake Warning \(dev\) at unsupported-library_type\.cmake:[0-9]+ \(add_library\): +CMake Warning \(author\) at unsupported-library_type\.cmake:[0-9]+ \(add_library\): The feature 'feature', specified as part of a generator-expression '\$<LINK_LIBRARY:feature>', will not be applied to the SHARED_LIBRARY 'dep'\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/GenEx-SOURCE_PROPERTY/Local.cmake b/Tests/RunCMake/GenEx-SOURCE_PROPERTY/Local.cmake index 6ff5a74..91e379f 100644 --- a/Tests/RunCMake/GenEx-SOURCE_PROPERTY/Local.cmake +++ b/Tests/RunCMake/GenEx-SOURCE_PROPERTY/Local.cmake
@@ -42,5 +42,23 @@ "check_value (\"<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,VOID>\" \"$<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,VOID>\" \"${reference}\")\n") +set_property(SOURCE foo.c PROPERTY INCLUDE_DIRECTORIES "$<$<BOOL:ON>:${CMAKE_CURRENT_SOURCE_DIR}>") +string (APPEND GENERATE_CONTENT + "check_value (\"<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,INCLUDE_DIRECTORIES>\" \"$<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,INCLUDE_DIRECTORIES>\" \"${CMAKE_CURRENT_SOURCE_DIR}\")\n") + +set_property(SOURCE foo.c PROPERTY COMPILE_DEFINITIONS "$<$<BOOL:ON>:SRC_DEF>") +string (APPEND GENERATE_CONTENT + "check_value (\"<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,COMPILE_DEFINITIONS>\" \"$<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,COMPILE_DEFINITIONS>\" \"SRC_DEF\")\n") + +set_property(SOURCE foo.c PROPERTY COMPILE_OPTIONS "$<$<BOOL:ON>:SRC_OPT>") +string (APPEND GENERATE_CONTENT + "check_value (\"<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,COMPILE_OPTIONS>\" \"$<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,COMPILE_OPTIONS>\" \"SRC_OPT\")\n") + +if(NOT CMAKE_GENERATOR MATCHES "Xcode|FASTBuild") + set_property(SOURCE foo.c PROPERTY OBJECT_NAME "$<$<BOOL:ON>:bar>") + string (APPEND GENERATE_CONTENT + "check_value (\"<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,OBJECT_NAME>\" \"$<SOURCE_PROPERTY:foo.c,TARGET_DIRECTORY:foo,OBJECT_NAME>\" \"bar\")\n") +endif() + file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Local-generated.cmake" CONTENT "${GENERATE_CONTENT}")
diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/CMP0202-WARN-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GenEx-TARGET_FILE/CMP0202-WARN-TARGET_PDB_FILE_BASE_NAME-stderr.txt index b1326c1..7a7d271 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/CMP0202-WARN-TARGET_PDB_FILE_BASE_NAME-stderr.txt +++ b/Tests/RunCMake/GenEx-TARGET_FILE/CMP0202-WARN-TARGET_PDB_FILE_BASE_NAME-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at CMP0202-WARN-TARGET_PDB_FILE_BASE_NAME\.cmake:[0-9]+ \(file\): +^CMake Warning \(policy\) at CMP0202-WARN-TARGET_PDB_FILE_BASE_NAME\.cmake:[0-9]+ \(file\): Policy CMP0202 is not set: PDB file names always include their target's per-config POSTFIX\. Run "cmake --help-policy CMP0202" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -7,4 +7,5 @@ the policy is not set, the OLD behavior will be used\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/CMakeLists.txt new file mode 100644 index 0000000..f54f746 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/RunCMakeTest.cmake new file mode 100644 index 0000000..9b65309 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/RunCMakeTest.cmake
@@ -0,0 +1,18 @@ +include(RunCMake) + +function(run_cmake_and_build name) + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${name}-build") + run_cmake_with_options(${name} -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --verbose) +endfunction() + +run_cmake_and_build(all) +run_cmake_and_build(SOURCE_FILES) + +run_cmake(SOURCE_FILES-noexist) +run_cmake(SOURCE_FILES-not-in-target) +run_cmake(SOURCE_FILES-imported) +run_cmake(SOURCE_FILES-relative) + +run_cmake(invalid)
diff --git a/Tests/RunCMake/target_compile_features/cxx_not_enabled-result.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-imported-result.txt similarity index 100% copy from Tests/RunCMake/target_compile_features/cxx_not_enabled-result.txt copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-imported-result.txt
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-imported-stderr.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-imported-stderr.txt new file mode 100644 index 0000000..2060f39 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-imported-stderr.txt
@@ -0,0 +1,7 @@ +CMake Error at SOURCE_FILES-imported\.cmake:[0-9]+ \(add_custom_target\): + Error evaluating generator expression: + + \$<TARGET_OBJECTS:c_objs,SOURCE_FILES:[^ +]*/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty4\.c> + + Cannot use SOURCE_FILES argument on imported target "c_objs"
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-imported.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-imported.cmake new file mode 100644 index 0000000..de82c0b --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-imported.cmake
@@ -0,0 +1,8 @@ +enable_language(C) + +add_library(c_objs IMPORTED OBJECT) + +add_custom_target(obj ALL + COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_OBJECTS:c_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty4.c> + VERBATIM + )
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-noexist-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-noexist-result.txt
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-noexist-stderr.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-noexist-stderr.txt new file mode 100644 index 0000000..30faeee --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-noexist-stderr.txt
@@ -0,0 +1,12 @@ +CMake Error at SOURCE_FILES-noexist\.cmake:[0-9]+ \(add_custom_target\): + Error evaluating generator expression: + + \$<TARGET_OBJECTS:c_objs,SOURCE_FILES:[^ +]*/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty4\.c> + + Source file: + + [^ +]*/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty4\.c + + does not exist for target "c_objs"
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-noexist.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-noexist.cmake new file mode 100644 index 0000000..9459d7c --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-noexist.cmake
@@ -0,0 +1,8 @@ +enable_language(C) + +add_library(c_objs OBJECT empty1.c empty2.c empty3.c) + +add_custom_target(obj ALL + COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_OBJECTS:c_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty4.c> + VERBATIM + )
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-not-in-target-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-not-in-target-result.txt
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-not-in-target-stderr.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-not-in-target-stderr.txt new file mode 100644 index 0000000..5709c82 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-not-in-target-stderr.txt
@@ -0,0 +1,12 @@ +CMake Error at SOURCE_FILES-not-in-target\.cmake:[0-9]+ \(add_custom_target\): + Error evaluating generator expression: + + \$<TARGET_OBJECTS:c_objs,SOURCE_FILES:[^ +]*/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty3\.c> + + Source file: + + [^ +]*/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty3\.c + + does not exist for target "c_objs"
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-not-in-target.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-not-in-target.cmake new file mode 100644 index 0000000..206f9f8 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-not-in-target.cmake
@@ -0,0 +1,9 @@ +enable_language(C) + +add_library(c_objs OBJECT empty1.c empty2.c) +add_library(other OBJECT empty3.c) + +add_custom_target(obj ALL + COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_OBJECTS:c_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty3.c> + VERBATIM + )
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-relative-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-relative-result.txt
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-relative-stderr.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-relative-stderr.txt new file mode 100644 index 0000000..cdb97af --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-relative-stderr.txt
@@ -0,0 +1,10 @@ +CMake Error at SOURCE_FILES-relative\.cmake:[0-9]+ \(add_custom_target\): + Error evaluating generator expression: + + \$<TARGET_OBJECTS:c_objs,SOURCE_FILES:empty1\.c> + + Source file: + + empty1\.c + + is not an absolute path
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-relative.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-relative.cmake new file mode 100644 index 0000000..df1127c --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-relative.cmake
@@ -0,0 +1,8 @@ +enable_language(C) + +add_library(c_objs OBJECT empty1.c empty2.c empty3.c) + +add_custom_target(obj ALL + COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_OBJECTS:c_objs,SOURCE_FILES:empty1.c> + VERBATIM + )
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES.cmake new file mode 100644 index 0000000..1727847 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES.cmake
@@ -0,0 +1,53 @@ +enable_language(C) + +include(common.cmake) + +add_library(c_objs OBJECT EXCLUDE_FROM_ALL empty1.c empty2.c empty3.c) + +check_target_objects(single_absolute + "$<TARGET_OBJECTS:c_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty2.c>" + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty2(\.c)?\.(o|obj)$]] + ) +check_target_objects(multi + "$<TARGET_OBJECTS:c_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty1.c;${CMAKE_CURRENT_SOURCE_DIR}/empty2.c>" + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty1(\.c)?\.(o|obj)$]] + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty2(\.c)?\.(o|obj)$]] + ) +check_target_objects(multi_reordered + "$<TARGET_OBJECTS:c_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty3.c;${CMAKE_CURRENT_SOURCE_DIR}/empty2.c>" + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty2(\.c)?\.(o|obj)$]] + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty3(\.c)?\.(o|obj)$]] + ) +check_target_objects(multi_duplicate + "$<TARGET_OBJECTS:c_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty1.c;${CMAKE_CURRENT_SOURCE_DIR}/empty3.c;${CMAKE_CURRENT_SOURCE_DIR}/empty1.c>" + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty1(\.c)?\.(o|obj)$]] + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty3(\.c)?\.(o|obj)$]] + ) + +add_subdirectory(SOURCE_FILES_subdir) + +if(CMake_TEST_CUDA STREQUAL "NVIDIA") + enable_language(CUDA) + + include(CheckIPOSupported) + check_ipo_supported(RESULT ipo_supported LANGUAGES CUDA) + + if(ipo_supported) + add_library(fatbin_objs OBJECT EXCLUDE_FROM_ALL empty1.cu empty2.cu) + set_target_properties(fatbin_objs PROPERTIES + CUDA_SEPARABLE_COMPILATION ON + CUDA_FATBIN_COMPILATION ON + POSITION_INDEPENDENT_CODE ON + INTERPROCEDURAL_OPTIMIZATION ON + ) + + check_target_objects(fatbin_1 + "$<TARGET_OBJECTS:fatbin_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty1.cu>" + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/fatbin_objs\.dir(/(\.|[a-zA-Z]+))?|/build/fatbin_objs\.build/.*)/empty1(\.cu\.o)?\.fatbin$]] + ) + check_target_objects(fatbin_2 + "$<TARGET_OBJECTS:fatbin_objs,SOURCE_FILES:${CMAKE_CURRENT_SOURCE_DIR}/empty2.cu>" + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/fatbin_objs\.dir(/(\.|[a-zA-Z]+))?|/build/fatbin_objs\.build/.*)/empty2(\.cu\.o)?\.fatbin$]] + ) + endif() +endif()
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES_subdir/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES_subdir/CMakeLists.txt new file mode 100644 index 0000000..208977b --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES_subdir/CMakeLists.txt
@@ -0,0 +1,4 @@ +check_target_objects(single_subdir_absolute + "$<TARGET_OBJECTS:c_objs,SOURCE_FILES:${CMAKE_SOURCE_DIR}/empty2.c>" + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/SOURCE_FILES-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty2(\.c)?\.(o|obj)$]] + )
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/all.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/all.cmake new file mode 100644 index 0000000..15991c3 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/all.cmake
@@ -0,0 +1,12 @@ +enable_language(C) + +include(common.cmake) + +add_library(c_objs OBJECT EXCLUDE_FROM_ALL empty1.c empty2.c empty3.c) + +check_target_objects(all + $<TARGET_OBJECTS:c_objs> + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/all-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty1(\.c)?\.(o|obj)$]] + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/all-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty2(\.c)?\.(o|obj)$]] + [[/Tests/RunCMake/GenEx-TARGET_OBJECTS/all-build((/CMakeFiles)?/c_objs\.dir(/(\.|[a-zA-Z]+))?|/build/c_objs\.build/.*)/empty3(\.c)?\.(o|obj)$]] + )
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/check_target_objects.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/check_target_objects.cmake new file mode 100644 index 0000000..62e68c7 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/check_target_objects.cmake
@@ -0,0 +1,13 @@ +include("${VARS_FILE}") + +list(LENGTH ACTUAL al) +list(LENGTH EXPECTED el) +if(NOT al EQUAL el) + message(FATAL_ERROR "Got the wrong number of objects") +endif() + +foreach(a e IN ZIP_LISTS ACTUAL EXPECTED) + if(NOT a MATCHES "${e}") + message(SEND_ERROR "Object \"${a}\" does not match expected regex \"${e}\"") + endif() +endforeach()
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/common.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/common.cmake new file mode 100644 index 0000000..61898b3 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/common.cmake
@@ -0,0 +1,12 @@ +function(check_target_objects name genex) + file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}_vars_$<CONFIG>.cmake" CONTENT " +set(EXPECTED [==[${ARGN}]==]) +set(ACTUAL [==[${genex}]==]) +") + add_custom_target(${name}_check ALL + COMMAND ${CMAKE_COMMAND} + "-DVARS_FILE=${CMAKE_CURRENT_BINARY_DIR}/${name}_vars_$<CONFIG>.cmake" + -P "${CMAKE_SOURCE_DIR}/check_target_objects.cmake" + VERBATIM + ) +endfunction()
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty1.c similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/empty1.c
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty1.cu similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/empty1.cu
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty2.c similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/empty2.c
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty2.cu similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/empty2.cu
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/empty3.c similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/empty3.c
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/invalid-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/GenEx-TARGET_OBJECTS/invalid-result.txt
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/invalid-stderr.txt b/Tests/RunCMake/GenEx-TARGET_OBJECTS/invalid-stderr.txt new file mode 100644 index 0000000..95cebcb --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/invalid-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error at invalid\.cmake:[0-9]+ \(add_custom_target\): + Error evaluating generator expression: + + \$<TARGET_OBJECTS:c_objs,INVALID_ARGUMENT_1,INVALID_ARGUMENT_2> + + Unrecognized argument: + + INVALID_ARGUMENT_1
diff --git a/Tests/RunCMake/GenEx-TARGET_OBJECTS/invalid.cmake b/Tests/RunCMake/GenEx-TARGET_OBJECTS/invalid.cmake new file mode 100644 index 0000000..e5f1b90 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_OBJECTS/invalid.cmake
@@ -0,0 +1,8 @@ +enable_language(C) + +add_library(c_objs OBJECT empty1.c empty2.c empty3.c) + +add_custom_target(obj ALL + COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_OBJECTS:c_objs,INVALID_ARGUMENT_1,INVALID_ARGUMENT_2> + VERBATIM + )
diff --git a/Tests/RunCMake/GenerateExportHeader/GEH.cmake b/Tests/RunCMake/GenerateExportHeader/GEH.cmake index 948a799..6158c5d 100644 --- a/Tests/RunCMake/GenerateExportHeader/GEH.cmake +++ b/Tests/RunCMake/GenerateExportHeader/GEH.cmake
@@ -1,5 +1,6 @@ # Test add_compiler_export_flags without deprecation warning. -set(CMAKE_WARN_DEPRECATED OFF) +cmake_policy(SET CMP0218 NEW) +cmake_diagnostic(SET CMD_DEPRECATED IGNORE) project(GenerateExportHeader)
diff --git a/Tests/RunCMake/GeneratorExpression/CMP0085-OLD-stderr.txt b/Tests/RunCMake/GeneratorExpression/CMP0085-OLD-stderr.txt index 31f2f3d..2b623c1 100644 --- a/Tests/RunCMake/GeneratorExpression/CMP0085-OLD-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/CMP0085-OLD-stderr.txt
@@ -1,8 +1,10 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): The OLD behavior for policy CMP0085 will be removed from a future version of CMake\. The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/GeneratorExpression/CMP0085-WARN-stderr.txt b/Tests/RunCMake/GeneratorExpression/CMP0085-WARN-stderr.txt index 81bd450..b5ce713 100644 --- a/Tests/RunCMake/GeneratorExpression/CMP0085-WARN-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/CMP0085-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0085-WARN\.cmake:[0-9]+ \(file\): +CMake Warning \(policy\) at CMP0085-WARN\.cmake:[0-9]+ \(file\): Policy CMP0085 is not set: \$<IN_LIST:\.\.\.> handles empty list items\. Run "cmake --help-policy CMP0085" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -13,9 +13,10 @@ Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0085-WARN\.cmake:[0-9]+ \(file\): +CMake Warning \(policy\) at CMP0085-WARN\.cmake:[0-9]+ \(file\): Policy CMP0085 is not set: \$<IN_LIST:\.\.\.> handles empty list items\. Run "cmake --help-policy CMP0085" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -30,4 +31,5 @@ Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/GeneratorExpression/CMP0199-WARN-stderr.txt b/Tests/RunCMake/GeneratorExpression/CMP0199-WARN-stderr.txt index 0d3af19..ea86883 100644 --- a/Tests/RunCMake/GeneratorExpression/CMP0199-WARN-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/CMP0199-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0199-WARN\.cmake:[0-9]+ \(target_link_libraries\): +CMake Warning \(policy\) at CMP0199-WARN\.cmake:[0-9]+ \(target_link_libraries\): Policy CMP0199 is not set: \$<CONFIG:cfgs> does not match mapped configurations that are not selected\. Run "cmake --help-policy CMP0199" for policy details\. Use the cmake_policy command to set the policy and @@ -6,7 +6,7 @@ Evaluation of \$<CONFIG> for imported target "lib_test", used by "exe_test", may match multiple configurations\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/GeneratorExpression/CMP0200-WARN-stderr.txt b/Tests/RunCMake/GeneratorExpression/CMP0200-WARN-stderr.txt index a44af11..d60f614 100644 --- a/Tests/RunCMake/GeneratorExpression/CMP0200-WARN-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/CMP0200-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0200 is not set: Location and configuration selection for imported targets is more consistent\. Run "cmake --help-policy CMP0200" for policy details\. Use the cmake_policy command to set the policy and @@ -7,10 +7,10 @@ Configuration selection for imported target "lib_test1" selected configuration "CAT", but would select configuration "DOG" under the NEW policy\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -This warning is for project developers\. Use -Wno-dev to suppress it\. - -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0200 is not set: Location and configuration selection for imported targets is more consistent\. Run "cmake --help-policy CMP0200" for policy details\. Use the cmake_policy command to set the policy and @@ -19,5 +19,5 @@ Configuration selection for imported target "lib_test2" selected configuration "\(none\)", but would select configuration "[^"]*" under the NEW policy\. - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps-consumer.c b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps-consumer.c new file mode 100644 index 0000000..2a8d167 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps-consumer.c
@@ -0,0 +1,4 @@ +#include "generated.h" +void consume(void) +{ +}
diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps.cmake b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps.cmake new file mode 100644 index 0000000..3c31171 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-custom-target-deps.cmake
@@ -0,0 +1,21 @@ +enable_language(C) + +# A custom command produces a header that does not exist at configure time. +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated.h" + COMMAND "${CMAKE_COMMAND}" -E touch "${CMAKE_CURRENT_BINARY_DIR}/generated.h" +) + +# An INTERFACE library generates the header. +add_library(header_provider INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/generated.h) +target_include_directories(header_provider INTERFACE "${CMAKE_CURRENT_BINARY_DIR}") +# Not in ALL so it is only built when something depends on it. +set_property(TARGET header_provider PROPERTY EXCLUDE_FROM_ALL 1) + +# A consumer uses $<COMPILE_ONLY:header_provider> so it gets the include +# path but does not link against header_provider. The build-order +# dependency on header_provider must be propagated through the +# COMPILE_ONLY expression so that generated.h exists when consumer.c +# is compiled. +add_library(consumer SHARED COMPILE_ONLY-custom-target-deps-consumer.c) +target_link_libraries(consumer PRIVATE "$<COMPILE_ONLY:header_provider>")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/GeneratorExpression/Empty-IMPORTED_CONFIGURATIONS-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/GeneratorExpression/Empty-IMPORTED_CONFIGURATIONS-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/Empty-IMPORTED_CONFIGURATIONS-stderr.txt b/Tests/RunCMake/GeneratorExpression/Empty-IMPORTED_CONFIGURATIONS-stderr.txt new file mode 100644 index 0000000..c5ac1d2 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/Empty-IMPORTED_CONFIGURATIONS-stderr.txt
@@ -0,0 +1,6 @@ +(CMake Error in CMakeLists.txt: + IMPORTED_LOCATION not set for imported target "imported"( configuration + "[A-Za-z]+")?\. + +)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.
diff --git a/Tests/RunCMake/GeneratorExpression/Empty-IMPORTED_CONFIGURATIONS.cmake b/Tests/RunCMake/GeneratorExpression/Empty-IMPORTED_CONFIGURATIONS.cmake new file mode 100644 index 0000000..918a4b3 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/Empty-IMPORTED_CONFIGURATIONS.cmake
@@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 4.4) +project(empty_imported_configuration NONE) + +add_library(imported UNKNOWN IMPORTED) +set_property(TARGET imported PROPERTY IMPORTED_CONFIGURATIONS "") + +file(GENERATE OUTPUT out.txt CONTENT "$<TARGET_FILE:imported>")
diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index e75b53b..86ef540 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
@@ -75,6 +75,22 @@ run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release) endfunction() +function(run_cmake_build_target test target) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Release) + else() + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release) + endif() + block() + unset(RunCMake-check-file) + run_cmake(${test}) + endblock() + set(RunCMake_TEST_NO_CLEAN TRUE) + run_cmake_command(${test}-build + ${CMAKE_COMMAND} --build . --target ${target} --config Release) +endfunction() + function(run_linker_genex test lang type) set(options_args CHECK_RESULT EXECUTE) cmake_parse_arguments(PARSE_ARGV 3 RLG "${options_args}" "" "") @@ -132,6 +148,8 @@ run_cmake(CONFIG-empty-entries) unset(RunCMake_TEST_OPTIONS) +run_cmake_build_target(COMPILE_ONLY-custom-target-deps consumer) + run_cmake(CMP0199-WARN) run_cmake_build(CMP0199-OLD) run_cmake_build(CMP0199-NEW) @@ -142,6 +160,8 @@ run_cmake_build(CMP0199-NEW+CMP0200-NEW) +run_cmake(Empty-IMPORTED_CONFIGURATIONS) + set(RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0085:STRING=OLD) run_cmake(CMP0085-OLD) unset(RunCMake_TEST_OPTIONS)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-flush-script-check-list.cmake.in b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-flush-script-check-list.cmake.in index b334d2b..b573645 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-flush-script-check-list.cmake.in +++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-flush-script-check-list.cmake.in
@@ -15,9 +15,9 @@ # uncovered by checking the name buffer flushing above. # PRE_TEST can have a config-specific tests file, POST_BUILD never does -set(tests_file "@CMAKE_CURRENT_BINARY_DIR@/flush_script_test[1]_tests-Debug.cmake") +set(tests_file "@CMAKE_CURRENT_BINARY_DIR@/flush_script_test_e3b0c442_Debug_tests.cmake") if(NOT EXISTS "${tests_file}") - set(tests_file "@CMAKE_CURRENT_BINARY_DIR@/flush_script_test[1]_tests.cmake") + set(tests_file "@CMAKE_CURRENT_BINARY_DIR@/flush_script_test_e3b0c442_tests.cmake") endif() if(NOT EXISTS "${tests_file}") message(FATAL_ERROR "Tests file is missing")
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-skip-test-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-skip-test-stdout.txt index 3ecda70..041378e 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-skip-test-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-skip-test-stdout.txt
@@ -4,7 +4,7 @@ *Start +[0-9]+: skip_test\.test1 *[0-9]+/[0-9]+ +Test +#[0-9]+: skip_test\.test1 .+\*\*\*Skipped +[0-9\.]+ sec -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2 Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test1-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test1-stdout.txt index 6ea0c8b..dd7ef7a 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-test1-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-test1-stdout.txt
@@ -126,7 +126,7 @@ *Start +[0-9]+: TEST:prefix/both\.case/test<TYPE>!1 *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/test<TYPE>!1 .+ +Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of [0-9]+ +100% tests passed out of [0-9]+ Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test2-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test2-stdout.txt index fd5971b..7dcac80 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-test2-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-test2-stdout.txt
@@ -126,7 +126,7 @@ *Start +[0-9]+: TEST:prefix/both\.case/test<TYPE>!2 *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/test<TYPE>!2 .+ +Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of [0-9]+ +100% tests passed out of [0-9]+ Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt index 3b3d654..4e99477 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt
@@ -16,7 +16,7 @@ *Start +[0-9]+: TEST:ns\.basic\.DISABLEDnot_really_case!3 *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.basic\.DISABLEDnot_really_case!3 .+ +Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of [0-9]+ +100% tests passed out of [0-9]+ Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt index c2dadc3..08df7aa 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt
@@ -40,6 +40,6 @@ *Start +[0-9]+: TEST:prefix/both\.case/test<TYPE>!4 *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/test<TYPE>!4 .+ +Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of [0-9]+ +100% tests passed out of [0-9]+ Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test5-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test5-stdout.txt index ddfc5e3..8b8864b 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-test5-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-test5-stdout.txt
@@ -82,6 +82,6 @@ *Start +[0-9]+: TEST:prefix/both\.case/test<TYPE>!5 *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/test<TYPE>!5 .+ +Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of [0-9]+ +100% tests passed out of [0-9]+ Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test6-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test6-stdout.txt index 0a99775..ed7eec3 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-test6-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-test6-stdout.txt
@@ -40,6 +40,6 @@ *Start +[0-9]+: TEST:prefix/both\.case/test<suite>!6 *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/test<suite>!6 .+ +Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of [0-9]+ +100% tests passed out of [0-9]+ Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test7-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test7-stdout.txt index 9d46f95..7a4bf07 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-test7-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-test7-stdout.txt
@@ -16,6 +16,6 @@ *Start +[0-9]+: TEST:prefix/both\.case/test<suite>!7 *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/test<suite>!7 .+ +Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of [0-9]+ +100% tests passed out of [0-9]+ Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest.cmake b/Tests/RunCMake/GoogleTest/GoogleTest.cmake index 792f5f7..8c33cf6 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest.cmake +++ b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
@@ -6,10 +6,11 @@ include(xcode_sign_adhoc.cmake) add_executable(fake_gtest fake_gtest.cpp) +add_executable(fake::gtest ALIAS fake_gtest) xcode_sign_adhoc(fake_gtest) gtest_discover_tests( - fake_gtest + fake::gtest TEST_PREFIX TEST: TEST_SUFFIX !1 EXTRA_ARGS how now "\"brown\" cow"
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryDuplicate-stderr.txt b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryDuplicate-stderr.txt new file mode 100644 index 0000000..c947f77 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryDuplicate-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(author\) at [^ +]*/Modules/GoogleTest\.cmake:[0-9]+ \(message\): + gtest_discover_tests\(\) invoked multiple times with the same arguments for + the same target 'fake_gtest'\. This invocation will be ignored\. +Call Stack \(most recent call first\): + GoogleTestDiscoveryDuplicate\.cmake:[0-9]+ \(gtest_discover_tests\) + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryDuplicate.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryDuplicate.cmake new file mode 100644 index 0000000..664a950 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryDuplicate.cmake
@@ -0,0 +1,20 @@ +enable_language(CXX) +include(GoogleTest) + +enable_testing() + +include(xcode_sign_adhoc.cmake) + +add_executable(fake_gtest fake_gtest.cpp) +xcode_sign_adhoc(fake_gtest) + +gtest_discover_tests(fake_gtest + DISCOVERY_MODE POST_BUILD + TEST_PREFIX DUPLICATE: + PROPERTIES LABELS DUPLICATE +) +gtest_discover_tests(fake_gtest + DISCOVERY_MODE POST_BUILD + TEST_PREFIX DUPLICATE: + PROPERTIES LABELS DUPLICATE +)
diff --git a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-stderr.txt b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-stderr.txt index 00b4939..258d0ef 100644 --- a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-stderr.txt +++ b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-stderr.txt
@@ -1,5 +1,5 @@ -CMake Warning \(dev\) at [^ -]*/Modules/GoogleTest\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at [^ +]*/Modules/GoogleTest\.cmake:[0-9]+ \(cmake_policy\): The 'launcher_test' target's TEST_LAUNCHER or CROSSCOMPILING_EMULATOR test properties contain one or more empty values\. Those empty values are being silently discarded to preserve backward compatibility\. @@ -11,10 +11,11 @@ Launcher\.cmake:[0-9]+ \(gtest_discover_tests\) Launcher-CMP0178-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at [^ -]*/Modules/GoogleTest\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at [^ +]*/Modules/GoogleTest\.cmake:[0-9]+ \(cmake_policy\): The EXTRA_ARGS value contains one or more empty values\. Those empty values are being silently discarded to preserve backward compatibility\. @@ -25,9 +26,10 @@ Launcher\.cmake:[0-9]+ \(gtest_discover_tests\) Launcher-CMP0178-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: The TEST_LAUNCHER property of target 'launcher_test' contains empty list items\. Those empty items are being silently discarded to preserve backward compatibility\. @@ -35,4 +37,5 @@ Policy CMP0178 is not set: Test command lines preserve empty arguments\. Run "cmake --help-policy CMP0178" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/GoogleTest/PropertiesOdd-stderr.txt b/Tests/RunCMake/GoogleTest/PropertiesOdd-stderr.txt new file mode 100644 index 0000000..f3ec8bd --- /dev/null +++ b/Tests/RunCMake/GoogleTest/PropertiesOdd-stderr.txt
@@ -0,0 +1,7 @@ +^CMake Warning \(author\) at [^ +]*/Modules/GoogleTest.cmake:[0-9]+ \(message\): + PROPERTIES should be key-value pairs. Ignoring unpaired key 'PROP2'. +Call Stack \(most recent call first\): + PropertiesOdd.cmake:[0-9]+ \(gtest_discover_tests\) + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/GoogleTest/PropertiesOdd.cmake b/Tests/RunCMake/GoogleTest/PropertiesOdd.cmake new file mode 100644 index 0000000..bbe9d72 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/PropertiesOdd.cmake
@@ -0,0 +1,8 @@ +add_executable(example IMPORTED) +set_property(TARGET example PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/example) +include(GoogleTest) +gtest_discover_tests(example DISCOVERY_MODE PRE_TEST + PROPERTIES + PROP1 VAL1 + PROP2 # no VAL2 +)
diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake index 8ebcfb8..6bb8eb7 100644 --- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
@@ -11,6 +11,8 @@ set(fs_delay 1.125) endif() +run_cmake(PropertiesOdd) + function(run_GoogleTest DISCOVERY_MODE) # Use a single build tree for a few tests without cleaning. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-build) @@ -487,6 +489,26 @@ ) endfunction() +function(run_GoogleTest_discovery_duplicate) + # Use a single build tree for a few tests without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-duplicate-build) + set(RunCMake_TEST_NO_CLEAN 1) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + + run_cmake(GoogleTestDiscoveryDuplicate) + + run_cmake_command(GoogleTest-discovery-duplicate-build + ${CMAKE_COMMAND} + --build . + --config Debug + --target fake_gtest + ) +endfunction() + foreach(DISCOVERY_MODE POST_BUILD PRE_TEST) message(STATUS "Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...") run_GoogleTest(${DISCOVERY_MODE}) @@ -542,3 +564,5 @@ if (NOT RunCMake_GENERATOR MATCHES "(Borland|NMake|Watcom)") run_GoogleTest_discovery_post_build_race() endif() + +run_GoogleTest_discovery_duplicate()
diff --git a/Tests/RunCMake/GoogleTest/WorkDirWithSpaces-test-stdout.txt b/Tests/RunCMake/GoogleTest/WorkDirWithSpaces-test-stdout.txt index 8849fd0..0d5ee49 100644 --- a/Tests/RunCMake/GoogleTest/WorkDirWithSpaces-test-stdout.txt +++ b/Tests/RunCMake/GoogleTest/WorkDirWithSpaces-test-stdout.txt
@@ -2,4 +2,4 @@ .* Start 2: WorkDirWithSpaces\.test2_discovered .* Start 3: WorkDirWithSpaces\.test1 .* Start 4: WorkDirWithSpaces\.test2 -.*0 tests failed out of 4 +.*100% tests passed out of 4
diff --git a/Tests/RunCMake/InstallExportsAsPackageInfo/ExperimentalWarning-stderr.txt b/Tests/RunCMake/InstallExportsAsPackageInfo/ExperimentalWarning-stderr.txt index 1b2c7d8..13a4927 100644 --- a/Tests/RunCMake/InstallExportsAsPackageInfo/ExperimentalWarning-stderr.txt +++ b/Tests/RunCMake/InstallExportsAsPackageInfo/ExperimentalWarning-stderr.txt
@@ -1,7 +1,8 @@ -CMake Warning \(dev\) at ExperimentalWarning\.cmake:[0-9]+ \(install\): +CMake Warning \(experimental\) at ExperimentalWarning\.cmake:[0-9]+ \(install\): CMake's support for generating package information in the Common Package Specification format from CMake script exports is experimental\. It is meant only for experimentation and feedback to CMake developers\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.
diff --git a/Tests/RunCMake/InstallExportsAsPackageInfo/RunCMakeTest.cmake b/Tests/RunCMake/InstallExportsAsPackageInfo/RunCMakeTest.cmake index 54f3e37..973d814 100644 --- a/Tests/RunCMake/InstallExportsAsPackageInfo/RunCMakeTest.cmake +++ b/Tests/RunCMake/InstallExportsAsPackageInfo/RunCMakeTest.cmake
@@ -6,7 +6,7 @@ # Enable experimental feature and suppress warnings set(RunCMake_TEST_OPTIONS - -Wno-dev + -Wno-author "-DCMAKE_EXPERIMENTAL_MAPPED_PACKAGE_INFO:STRING=ababa1b5-7099-495f-a9cd-e22d38f274f2" )
diff --git a/Tests/RunCMake/InstallPackageInfo/PrivateLinkDependency-check.cmake b/Tests/RunCMake/InstallPackageInfo/PrivateLinkDependency-check.cmake new file mode 100644 index 0000000..a074f0a --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/PrivateLinkDependency-check.cmake
@@ -0,0 +1,14 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/PrivateLinkDependency-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/mylib.cps" content) + +# Private link dependency must appear in package requires +expect_array("${content}" 1 "requires" "libdep" "components") +expect_value("${content}" "libdep" "requires" "libdep" "components" 0) + +# And in per-config dyld_requires +file(READ "${out_dir}/mylib@generic.cps" content) +expect_array("${content}" 1 "components" "mylib" "dyld_requires") +expect_value("${content}" "libdep:libdep" "components" "mylib" "dyld_requires" 0)
diff --git a/Tests/RunCMake/InstallPackageInfo/PrivateLinkDependency.cmake b/Tests/RunCMake/InstallPackageInfo/PrivateLinkDependency.cmake new file mode 100644 index 0000000..c8626ef --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/PrivateLinkDependency.cmake
@@ -0,0 +1,20 @@ +project(PrivateLinkDependency CXX) + +get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(_isMultiConfig) + set(CMAKE_CONFIGURATION_TYPES "generic" CACHE STRING "" FORCE) +else() + set(CMAKE_BUILD_TYPE "generic" CACHE STRING "" FORCE) +endif() + +find_package( + libdep REQUIRED CONFIG + NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_LIST_DIR} +) + +add_library(mylib SHARED foo.cxx) +target_link_libraries(mylib PRIVATE libdep::libdep) + +install(TARGETS mylib EXPORT mylib DESTINATION .) +install(PACKAGE_INFO mylib DESTINATION cps EXPORT mylib)
diff --git a/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake b/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake index 56ee883..c25c629 100644 --- a/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake +++ b/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake
@@ -38,6 +38,7 @@ run_cmake(MinimalVersion) run_cmake(LowerCaseFile) run_cmake(Requirements) +run_cmake(PrivateLinkDependency) run_cmake(TargetTypes) run_cmake(DependsMultiple) run_cmake(DependsMultipleNotInstalled)
diff --git a/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning-stderr.txt b/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning-stderr.txt index 0e833c2..c2f4382 100644 --- a/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning-stderr.txt +++ b/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning-stderr.txt
@@ -1,3 +1,5 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(author\) at VersionCheckWarning\.cmake:[0-9]+ \(install\): Package "foo" uses unrecognized version schema "unrecognized"\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/InstallPackageInfo/cps/libdep/libdep.cps b/Tests/RunCMake/InstallPackageInfo/cps/libdep/libdep.cps new file mode 100644 index 0000000..7e9feac --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/cps/libdep/libdep.cps
@@ -0,0 +1,12 @@ +{ + "components" : + { + "libdep" : + { + "type" : "dylib" + } + }, + "cps_path" : "@prefix@/cps/libdep", + "cps_version" : "0.14.1", + "name" : "libdep" +}
diff --git a/Tests/RunCMake/InstallPackageInfo/cps/libdep/libdep@generic.cps b/Tests/RunCMake/InstallPackageInfo/cps/libdep/libdep@generic.cps new file mode 100644 index 0000000..5e529ee --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/cps/libdep/libdep@generic.cps
@@ -0,0 +1,12 @@ +{ + "components" : + { + "libdep" : + { + "link_location" : "@prefix@/libdep.lib", + "location" : "@prefix@/libdep.dll" + } + }, + "configuration" : "generic", + "name" : "libdep" +}
diff --git a/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake b/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake index b7a059b..d491b66 100644 --- a/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake +++ b/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake
@@ -48,6 +48,7 @@ endfunction() install_test(parallel PARALLEL ARGS "-j 4") +install_test(parallel-no-space PARALLEL ARGS "-j4") install_test(no-parallel ARGS "-j 4") install_test(out-of-date-json TOUCH_CACHE PARALLEL ARGS "-j 4") install_test(component PARALLEL ARGS "-j 4" COMPONENT "ALPHANUMERIC123")
diff --git a/Tests/RunCMake/InstallParallel/parallel-no-space-install-stdout.txt b/Tests/RunCMake/InstallParallel/parallel-no-space-install-stdout.txt new file mode 100644 index 0000000..9c059b0 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/parallel-no-space-install-stdout.txt
@@ -0,0 +1,15 @@ +\[1\/5\] .* +\-\- Installing:[^ +]* +\[2\/5\] .* +\-\- Installing:[^ +]* +\[3\/5\] .* +\-\- Installing:[^ +]* +\[4\/5\] .* +\-\- Installing:[^ +]* +\[5\/5\] .* +\-\- Installing:[^ +]*
diff --git a/Tests/RunCMake/InstallSbom/ApplicationTarget.cmake b/Tests/RunCMake/InstallSbom/ApplicationTarget.cmake index e08b927..60c73ac 100644 --- a/Tests/RunCMake/InstallSbom/ApplicationTarget.cmake +++ b/Tests/RunCMake/InstallSbom/ApplicationTarget.cmake
@@ -1,7 +1,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ApplicationTarget.cmake) install(SBOM application_targets - EXPORT application_targets + EXPORTS application_targets FORMAT "spdx-3.0+json" DESTINATION . )
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/InstallSbom/DuplicateSbom-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/InstallSbom/DuplicateSbom-result.txt
diff --git a/Tests/RunCMake/InstallSbom/DuplicateSbom-stderr.txt b/Tests/RunCMake/InstallSbom/DuplicateSbom-stderr.txt new file mode 100644 index 0000000..97a197b --- /dev/null +++ b/Tests/RunCMake/InstallSbom/DuplicateSbom-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at DuplicateSbom\.cmake:[0-9]+ \(install\): + install SBOM command already specified for the file mySbom\.spdx\.json\.
diff --git a/Tests/RunCMake/InstallSbom/DuplicateSbom.cmake b/Tests/RunCMake/InstallSbom/DuplicateSbom.cmake new file mode 100644 index 0000000..2959dd5 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/DuplicateSbom.cmake
@@ -0,0 +1,4 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DuplicateSbom.cmake) + +install(SBOM mySbom EXPORTS setA DESTINATION .) +install(SBOM mySbom EXPORTS setA DESTINATION .)
diff --git a/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback-install-check.cmake b/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback-install-check.cmake new file mode 100644 index 0000000..3f61b5b --- /dev/null +++ b/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback-install-check.cmake
@@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/EmptyNamespaceFallback-install-check.cmake)
diff --git a/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback-stderr.txt b/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback-stderr.txt new file mode 100644 index 0000000..c6d0d96 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback-stderr.txt
@@ -0,0 +1,4 @@ +CMake Warning \(author\) in CMakeLists\.txt: + Target "libb" references target "liba", whose export does not use the + standard namespace separator\. The dependency will be recorded by its bare + target name without provenance\.
diff --git a/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback.cmake b/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback.cmake new file mode 100644 index 0000000..d6d5d57 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/EmptyNamespaceFallback.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/EmptyNamespaceFallback.cmake) + +install(EXPORT setA DESTINATION lib/cmake/A) +install(EXPORT setB NAMESPACE B:: DESTINATION lib/cmake/B) +install(SBOM mySbom EXPORTS setB DESTINATION .)
diff --git a/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs.cmake b/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs.cmake index eed2908..1f5a640 100644 --- a/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs.cmake +++ b/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs.cmake
@@ -14,6 +14,6 @@ ) install(SBOM interface_targets - EXPORT interface_targets + EXPORTS interface_targets DESTINATION . )
diff --git a/Tests/RunCMake/InstallSbom/InstallExportPlusSbomSameSet.cmake b/Tests/RunCMake/InstallSbom/InstallExportPlusSbomSameSet.cmake new file mode 100644 index 0000000..d07a369 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/InstallExportPlusSbomSameSet.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/InstallExportPlusSbomSameSet.cmake) + +install(EXPORT export_set_a NAMESPACE A:: DESTINATION lib/cmake/setA) +install(SBOM mySbom EXPORTS export_set_a DESTINATION .) +install(EXPORT export_set_b DESTINATION lib/cmake/setB)
diff --git a/Tests/RunCMake/InstallSbom/InterfaceTarget.cmake b/Tests/RunCMake/InstallSbom/InterfaceTarget.cmake index 9d88f16..f31ba77 100644 --- a/Tests/RunCMake/InstallSbom/InterfaceTarget.cmake +++ b/Tests/RunCMake/InstallSbom/InterfaceTarget.cmake
@@ -1,6 +1,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/InterfaceTarget.cmake) install(SBOM interface_targets - EXPORT interface_targets + EXPORTS interface_targets DESTINATION . )
diff --git a/Tests/RunCMake/InstallSbom/MissingPackageNamespace.cmake b/Tests/RunCMake/InstallSbom/MissingPackageNamespace.cmake index 79c1812..2e3cf4e 100644 --- a/Tests/RunCMake/InstallSbom/MissingPackageNamespace.cmake +++ b/Tests/RunCMake/InstallSbom/MissingPackageNamespace.cmake
@@ -2,6 +2,6 @@ install(SBOM test_targets VERSION "1.0.2" - EXPORT test_targets + EXPORTS test_targets DESTINATION . )
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/InstallSbom/MultiNamespaceAmbiguity-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/InstallSbom/MultiNamespaceAmbiguity-result.txt
diff --git a/Tests/RunCMake/InstallSbom/MultiNamespaceAmbiguity-stderr.txt b/Tests/RunCMake/InstallSbom/MultiNamespaceAmbiguity-stderr.txt new file mode 100644 index 0000000..739ec83 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/MultiNamespaceAmbiguity-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error in CMakeLists\.txt: + Target "libb" references target "liba" that is in an export set which is + exported multiple times with different namespaces: lib/cmake/A/setA\.cmake, + lib/cmake/OldA/setA\.cmake\. + + An SBOM cannot attribute a dependency exported in more than one export set + or with more than one namespace\. Consider consolidating the exports of the + "liba" target to a single export\.
diff --git a/Tests/RunCMake/InstallSbom/MultiNamespaceAmbiguity.cmake b/Tests/RunCMake/InstallSbom/MultiNamespaceAmbiguity.cmake new file mode 100644 index 0000000..b634a66 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/MultiNamespaceAmbiguity.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiNamespaceAmbiguity.cmake) + +install(EXPORT setA NAMESPACE A:: DESTINATION lib/cmake/A) +install(EXPORT setA NAMESPACE OldA:: DESTINATION lib/cmake/OldA) +install(SBOM mySbom EXPORTS setB DESTINATION .)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/InstallSbom/MultiSetAmbiguity-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/InstallSbom/MultiSetAmbiguity-result.txt
diff --git a/Tests/RunCMake/InstallSbom/MultiSetAmbiguity-stderr.txt b/Tests/RunCMake/InstallSbom/MultiSetAmbiguity-stderr.txt new file mode 100644 index 0000000..45acf12 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/MultiSetAmbiguity-stderr.txt
@@ -0,0 +1,7 @@ +CMake Error in CMakeLists\.txt: + Target "libb" references target "liba" that is in multiple export sets: + lib/cmake/A1/setA1\.cmake, lib/cmake/A2/setA2\.cmake\. + + An SBOM cannot attribute a dependency exported in more than one export set + or with more than one namespace\. Consider consolidating the exports of the + "liba" target to a single export\.
diff --git a/Tests/RunCMake/InstallSbom/MultiSetAmbiguity.cmake b/Tests/RunCMake/InstallSbom/MultiSetAmbiguity.cmake new file mode 100644 index 0000000..a434374 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/MultiSetAmbiguity.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetAmbiguity.cmake) + +install(EXPORT setA1 NAMESPACE A1:: DESTINATION lib/cmake/A1) +install(EXPORT setA2 NAMESPACE A2:: DESTINATION lib/cmake/A2) +install(SBOM mySbom EXPORTS setB DESTINATION .)
diff --git a/Tests/RunCMake/InstallSbom/MultiSetSingleSbom-install-check.cmake b/Tests/RunCMake/InstallSbom/MultiSetSingleSbom-install-check.cmake new file mode 100644 index 0000000..e1e8aa1 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/MultiSetSingleSbom-install-check.cmake
@@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetSingleSbom-install-check.cmake)
diff --git a/Tests/RunCMake/InstallSbom/MultiSetSingleSbom.cmake b/Tests/RunCMake/InstallSbom/MultiSetSingleSbom.cmake new file mode 100644 index 0000000..ac55d2f --- /dev/null +++ b/Tests/RunCMake/InstallSbom/MultiSetSingleSbom.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetSingleSbom.cmake) + +install(EXPORT setA NAMESPACE A:: DESTINATION lib/cmake/A) +install(EXPORT setB NAMESPACE B:: DESTINATION lib/cmake/B) +install(SBOM mySbom EXPORTS setA setB DESTINATION .)
diff --git a/Tests/RunCMake/InstallSbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/InstallSbom/ProjectMetadata-install-check.cmake index 355e80d..75adf4e 100644 --- a/Tests/RunCMake/InstallSbom/ProjectMetadata-install-check.cmake +++ b/Tests/RunCMake/InstallSbom/ProjectMetadata-install-check.cmake
@@ -1,2 +1,3 @@ file(READ "${RunCMake_TEST_INSTALL_DIR}/test_targets.spdx.json" content) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadataExplicitAssertions.cmake)
diff --git a/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake b/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake index a743217..daaf3b4 100644 --- a/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake +++ b/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake
@@ -5,7 +5,8 @@ DESCRIPTION "An eloquent description" LICENSE "BSD-3" HOMEPAGE_URL "www.example.com" + PACKAGE_URL "https://example.com/test_targets.tar.gz" VERSION "1.3.4" - EXPORT test_targets + EXPORTS test_targets DESTINATION . )
diff --git a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-install-check.cmake b/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-install-check.cmake deleted file mode 100644 index 6dc7558..0000000 --- a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-install-check.cmake +++ /dev/null
@@ -1,2 +0,0 @@ -file(READ "${RunCMake_TEST_INSTALL_DIR}/dog.spdx.json" content) -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget-install-check.cmake)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-result.txt
diff --git a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-stderr.txt b/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-stderr.txt new file mode 100644 index 0000000..3dfbdb6 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-stderr.txt
@@ -0,0 +1,6 @@ +CMake Error in CMakeLists\.txt: + Target "canine" references target "mammal" which has no + install\(EXPORT\)/export\(EXPORT\) namespace and is not covered by any SBOM\. + An SBOM cannot attribute this dependency\. Give "mammal" an + install\(EXPORT\)/export\(EXPORT\) with a NAMESPACE, or include it in an + install\(SBOM\)/export\(SBOM\)\.
diff --git a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget.cmake b/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget.cmake index 0fcedfa..561529c 100644 --- a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget.cmake +++ b/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget.cmake
@@ -1,3 +1,3 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget.cmake) -install(SBOM dog EXPORT dog DESTINATION .) +install(SBOM dog EXPORTS dog DESTINATION .)
diff --git a/Tests/RunCMake/InstallSbom/Requirements-install-check.cmake b/Tests/RunCMake/InstallSbom/Requirements-install-check.cmake index f70ce28..1c2f057 100644 --- a/Tests/RunCMake/InstallSbom/Requirements-install-check.cmake +++ b/Tests/RunCMake/InstallSbom/Requirements-install-check.cmake
@@ -1,3 +1,3 @@ -file(READ "${RunCMake_TEST_INSTALL_DIR}/bar.spdx.json" BAR_CONTENT) -file(READ "${RunCMake_TEST_INSTALL_DIR}/foo.spdx.json" FOO_CONTENT) +file(READ "${RunCMake_TEST_INSTALL_DIR}/bar_sbom.spdx.json" BAR_CONTENT) +file(READ "${RunCMake_TEST_INSTALL_DIR}/foo_sbom.spdx.json" FOO_CONTENT) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements-install-check.cmake)
diff --git a/Tests/RunCMake/InstallSbom/Requirements.cmake b/Tests/RunCMake/InstallSbom/Requirements.cmake index a437e54..350fd85 100644 --- a/Tests/RunCMake/InstallSbom/Requirements.cmake +++ b/Tests/RunCMake/InstallSbom/Requirements.cmake
@@ -1,4 +1 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements.cmake) - -install(SBOM foo EXPORT foo DESTINATION .) -install(SBOM bar EXPORT bar DESTINATION .)
diff --git a/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake b/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake index ef6b864..fcf9986 100644 --- a/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake +++ b/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake
@@ -1,10 +1,19 @@ include(RunCMake) set(common_test_options - -Wno-dev - "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=ca494ed3-b261-4205-a01f-603c95e4cae0" + -Wno-author + "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317" ) +function(run_cmake_error test) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_OPTIONS ${common_test_options}) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=DEBUG) + endif() + run_cmake(${test}) +endfunction() + function(run_cmake_install test) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) @@ -17,15 +26,28 @@ run_cmake(${test}) set(RunCMake_TEST_NO_CLEAN TRUE) + set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Debug) + unset(RunCMake_TEST_OUTPUT_MERGE) run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug) endfunction() run_cmake_install(ApplicationTarget) +run_cmake_install(InstallExportPlusSbomSameSet) run_cmake_install(InterfaceTarget) run_cmake_install(SharedTarget) run_cmake_install(Requirements) +run_cmake_install(SbomNamespaceFallback) +run_cmake_install(MultiSetSingleSbom) +run_cmake_install(TargetInMultipleSets) +run_cmake_install(EmptyNamespaceFallback -Wauthor) +run_cmake_install(SbomNamespaceAmbiguity -Wauthor) run_cmake_install(IgnoresInterfaceDirs) run_cmake_install(MissingPackageNamespace) -run_cmake_install(ReferencesNonExportedTarget) +run_cmake_install(ProjectMetadata) + +run_cmake_error(ReferencesNonExportedTarget) +run_cmake_error(MultiNamespaceAmbiguity) +run_cmake_error(MultiSetAmbiguity) +run_cmake_error(DuplicateSbom)
diff --git a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-install-check.cmake b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-install-check.cmake new file mode 100644 index 0000000..c35bcc0 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-install-check.cmake
@@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/bar_sbom.spdx.json" BAR_CONTENT) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity-install-check.cmake)
diff --git a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-stderr.txt b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-stderr.txt new file mode 100644 index 0000000..ea4234f --- /dev/null +++ b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-stderr.txt
@@ -0,0 +1,4 @@ +CMake Warning \(author\) in CMakeLists\.txt: + Target "libc" references target "libb" which has no + install\(EXPORT\)/export\(EXPORT\) namespace and is covered by multiple SBOMs: + foo_sbom, foo_sbom2\. Attributing to "foo_sbom" \(first alphabetically\)\.
diff --git a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity.cmake b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity.cmake new file mode 100644 index 0000000..564979d --- /dev/null +++ b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity.cmake
@@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity.cmake) + +install(SBOM foo_sbom EXPORTS foo DESTINATION .) +install(SBOM foo_sbom2 EXPORTS foo DESTINATION .) +install(SBOM bar_sbom EXPORTS bar DESTINATION .)
diff --git a/Tests/RunCMake/InstallSbom/SbomNamespaceFallback-install-check.cmake b/Tests/RunCMake/InstallSbom/SbomNamespaceFallback-install-check.cmake new file mode 100644 index 0000000..dbbec2d --- /dev/null +++ b/Tests/RunCMake/InstallSbom/SbomNamespaceFallback-install-check.cmake
@@ -0,0 +1,3 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/bar.spdx.json" BAR_CONTENT) +file(READ "${RunCMake_TEST_INSTALL_DIR}/foo.spdx.json" FOO_CONTENT) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceFallback-install-check.cmake)
diff --git a/Tests/RunCMake/InstallSbom/SbomNamespaceFallback.cmake b/Tests/RunCMake/InstallSbom/SbomNamespaceFallback.cmake new file mode 100644 index 0000000..132e9c9 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/SbomNamespaceFallback.cmake
@@ -0,0 +1,4 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceFallback.cmake) + +install(SBOM foo EXPORTS foo DESTINATION .) +install(SBOM bar EXPORTS bar DESTINATION .)
diff --git a/Tests/RunCMake/InstallSbom/SharedTarget.cmake b/Tests/RunCMake/InstallSbom/SharedTarget.cmake index 63ba249..c3ee8f0 100644 --- a/Tests/RunCMake/InstallSbom/SharedTarget.cmake +++ b/Tests/RunCMake/InstallSbom/SharedTarget.cmake
@@ -2,6 +2,6 @@ install( SBOM shared_targets - EXPORT shared_targets + EXPORTS shared_targets DESTINATION . )
diff --git a/Tests/RunCMake/InstallSbom/TargetInMultipleSets-install-check.cmake b/Tests/RunCMake/InstallSbom/TargetInMultipleSets-install-check.cmake new file mode 100644 index 0000000..1d04796 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/TargetInMultipleSets-install-check.cmake
@@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake)
diff --git a/Tests/RunCMake/InstallSbom/TargetInMultipleSets.cmake b/Tests/RunCMake/InstallSbom/TargetInMultipleSets.cmake new file mode 100644 index 0000000..20ced6a --- /dev/null +++ b/Tests/RunCMake/InstallSbom/TargetInMultipleSets.cmake
@@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets.cmake) + +install(SBOM mySbom EXPORTS setA1 setA2 DESTINATION .)
diff --git a/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake b/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake index a4be34c..cc6b55f 100644 --- a/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake +++ b/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake
@@ -8,6 +8,7 @@ set(OPTIONS "BUILD" "BUILD_MAKE_PROGRAM" + "BUILD_MAKE_PROGRAM_CHANGE_DIR" "INSTALL" "INSTALL_PARALLEL" "TEST" @@ -16,6 +17,9 @@ "COPY_QUERIES_GENERATED" "STATIC_QUERY" "DYNAMIC_QUERY" + "CAPTURE_OUTPUT_QUERY" + "COMPILE_TRACE_QUERY" + "COMPILE_TRACE_QUERY_NULL" "TRACE_QUERY" "MANUAL_HOOK" "PRESERVE_DATA" @@ -24,7 +28,20 @@ "FAIL" "BAD_QUERY" ) - cmake_parse_arguments(ARGS "${OPTIONS}" "CHECK_SCRIPT;CONFIGURE_ARG" "" ${ARGN}) + cmake_parse_arguments(ARGS "${OPTIONS}" "CHECK_SCRIPT" "CONFIGURE_ARGS" ${ARGN}) + + if(NOT CMake_TEST_INSTRUMENTATION_VARIANT) + if(ARGS_BUILD_MAKE_PROGRAM) + return() + endif() + endif() + + if(CMake_TEST_INSTRUMENTATION_VARIANT STREQUAL "MakeProgram") + if(NOT ARGS_BUILD_MAKE_PROGRAM) + return() + endif() + endif() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}) set(v1 ${RunCMake_TEST_BINARY_DIR}/.cmake/instrumentation/v1) set(v1 ${v1} PARENT_SCOPE) @@ -55,7 +72,17 @@ if (ARGS_TRACE_QUERY) set(trace_query_hook_arg 1) endif() - set(GET_HOOK "\\\"${CMAKE_COMMAND}\\\" -P \\\"${RunCMake_SOURCE_DIR}/hook.cmake\\\" ${static_query_hook_arg} ${trace_query_hook_arg}") + set(ARGS_COMPILE_TRACE_QUERY ${ARGS_COMPILE_TRACE_QUERY} PARENT_SCOPE) + set(ARGS_COMPILE_TRACE_QUERY_NULL ${ARGS_COMPILE_TRACE_QUERY_NULL} PARENT_SCOPE) + set(GET_HOOK + "\\\"${CMAKE_COMMAND}\\\"" + "-DSTATIC_QUERY=${static_query_hook_arg}" + "-DTRACE_QUERY=${trace_query_hook_arg}" + "-DPython_EXECUTABLE=${Python_EXECUTABLE}" + "-DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}" + "-P \\\"${RunCMake_SOURCE_DIR}/hook.cmake\\\"" + ) + list(JOIN GET_HOOK " " GET_HOOK) # Load query JSON and cmake (with cmake_instrumentation(...)) files set(query ${query_dir}/${test}.json.in) @@ -63,11 +90,21 @@ if (EXISTS ${query}) file(MAKE_DIRECTORY ${v1}/query) configure_file(${query} ${v1}/query/${test}.json) - else() - if (NOT EXISTS ${cmake_file} AND NOT EXISTS ${cmake_file}.in) - set(cmake_file ${query_dir}/default.cmake) + else () + if (NOT EXISTS ${cmake_file}) + if (EXISTS ${cmake_file}.in) + cmake_path(GET cmake_file FILENAME cmake_filename) + configure_file( + "${cmake_file}.in" + "${RunCMake_TEST_BINARY_DIR}/${cmake_filename}" + @ONLY + ) + set(cmake_file "${RunCMake_TEST_BINARY_DIR}/${cmake_filename}") + else () + set(cmake_file "${query_dir}/default.cmake") + endif() endif() - list(APPEND ARGS_CONFIGURE_ARG "-DINSTRUMENT_COMMAND_FILE=${cmake_file}") + list(APPEND ARGS_CONFIGURE_ARGS "-DINSTRUMENT_COMMAND_FILE=${cmake_file}") endif() set(copy_loc ${RunCMake_TEST_BINARY_DIR}/query) @@ -93,10 +130,10 @@ # Configure Test Case set(RunCMake_TEST_NO_CLEAN 1) if (ARGS_FAIL) - list(APPEND ARGS_CONFIGURE_ARG "-DFAIL=ON") + list(APPEND ARGS_CONFIGURE_ARGS "-DFAIL=ON") endif() if (ARGS_DISABLE_TEST) - list(APPEND ARGS_CONFIGURE_ARG "-DDISABLE_TEST=ON") + list(APPEND ARGS_CONFIGURE_ARGS "-DDISABLE_TEST=ON") endif() set(RunCMake_TEST_SOURCE_DIR ${RunCMake_SOURCE_DIR}/project) if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) @@ -108,24 +145,21 @@ "${RunCMake_TEST_BINARY_DIR}/CMakePresets.json" @ONLY ) - configure_file( - "${cmake_file}.in" - "${RunCMake_TEST_BINARY_DIR}/cmake-command-workflow.cmake" - @ONLY - ) - foreach(f IN ITEMS CMakeLists.txt main.cxx lib.cxx lib.h shell_redirect.txt) + foreach(f IN ITEMS CMakeLists.txt main.c lib.c lib.h shell_redirect.txt) configure_file( "${RunCMake_TEST_SOURCE_DIR}/${f}" "${RunCMake_TEST_BINARY_DIR}/${f}" COPYONLY ) endforeach() + set(RunCMake_QUIET_ERROR 1) set(v1 ${RunCMake_TEST_BINARY_DIR}/build/.cmake/instrumentation/v1) run_cmake_command(${test}-workflow ${CMAKE_COMMAND} --workflow default) set(ARGS_NO_CONFIGURE TRUE) + unset(RunCMake_QUIET_ERROR) endif() if (NOT ARGS_NO_CONFIGURE) - run_cmake_with_options(${test} ${ARGS_CONFIGURE_ARG} ${maybe_CMAKE_BUILD_TYPE}) + run_cmake_with_options(${test} ${ARGS_CONFIGURE_ARGS} ${maybe_CMAKE_BUILD_TYPE}) endif() # Follow-up Commands @@ -147,19 +181,29 @@ # errors to different places. set(RunCMake_TEST_OUTPUT_MERGE 1) endif() + set(RunCMake_QUIET_ERROR 1) run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . ${cmake_build_args} -- ${additional_build_args} ) + unset(RunCMake_QUIET_ERROR) if (ARGS_FAIL) unset(RunCMake_TEST_OUTPUT_MERGE) endif() endif() if (ARGS_BUILD_MAKE_PROGRAM) set(RunCMake_TEST_OUTPUT_MERGE 1) + set(RunCMake_QUIET_ERROR 1) # Force reconfigure to test for double preBuild & postBuild hooks file(TOUCH ${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt) - run_cmake_command(${test}-make-program ${RunCMake_MAKE_PROGRAM}) + if (ARGS_BUILD_MAKE_PROGRAM_CHANGE_DIR) + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_BINARY_DIR}) + run_cmake_command(${test}-make-program ${RunCMake_MAKE_PROGRAM} -C ${RunCMake_TEST_BINARY_DIR}) + unset(RunCMake_TEST_COMMAND_WORKING_DIRECTORY) + else() + run_cmake_command(${test}-make-program ${RunCMake_MAKE_PROGRAM}) + endif() unset(RunCMake_TEST_OUTPUT_MERGE) + unset(RunCMake_QUIET_ERROR) endif() if (ARGS_INSTALL) run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --prefix install --config Debug) @@ -224,6 +268,9 @@ BUILD INSTALL TEST CHECK_SCRIPT check-data-dir.cmake ) +if(RunCMake_GENERATOR STREQUAL "Ninja") + set(CHECK_NINJA_INSTRUMENT_ORDER 1) +endif() instrument(disabled-test BUILD TEST DISABLE_TEST CHECK_SCRIPT check-data-dir.cmake @@ -233,9 +280,10 @@ CHECK_SCRIPT check-data-dir.cmake ) instrument(both-query - BUILD INSTALL TEST DYNAMIC_QUERY + BUILD INSTALL TEST STATIC_QUERY DYNAMIC_QUERY CAPTURE_OUTPUT_QUERY CHECK_SCRIPT check-data-dir.cmake ) +unset(CHECK_NINJA_INSTRUMENT_ORDER) # Test cmake_instrumentation command instrument(cmake-command @@ -255,7 +303,7 @@ BUILD INSTALL TEST INSTALL_PARALLEL DYNAMIC_QUERY CHECK_SCRIPT check-data-dir.cmake) instrument(cmake-command-initial-cache - CONFIGURE_ARG "-C ${RunCMake_BINARY_DIR}/initial.cmake" + CONFIGURE_ARGS "-C ${RunCMake_BINARY_DIR}/initial.cmake" ) instrument(cmake-command-resets-generated COPY_QUERIES_GENERATED @@ -283,24 +331,28 @@ # Test CUSTOM_CONTENT instrument(cmake-command-custom-content BUILD - CONFIGURE_ARG "-DN=1" + CONFIGURE_ARGS "-DN=1" ) instrument(cmake-command-custom-content BUILD PRESERVE_DATA - CONFIGURE_ARG "-DN=2" + CONFIGURE_ARGS "-DN=2" CHECK_SCRIPT check-custom-content.cmake ) -set(indexDir ${v1}/data/index) -set(fakeIndex ${indexDir}/index-0.json) -file(MAKE_DIRECTORY ${indexDir}) -file(TOUCH ${fakeIndex}) +if(NOT CMake_TEST_INSTRUMENTATION_VARIANT) + set(indexDir ${v1}/data/index) + set(fakeIndex ${indexDir}/index-0.json) + file(MAKE_DIRECTORY ${indexDir}) + file(TOUCH ${fakeIndex}) +endif() # fakeIndex newer than all content files prevents their deletion set(EXPECTED_CONTENT_FILES 2) instrument(cmake-command-custom-content NO_CONFIGURE MANUAL_HOOK PRESERVE_DATA CHECK_SCRIPT check-custom-content-removed.cmake ) -file(REMOVE ${fakeIndex}) +if(NOT CMake_TEST_INSTRUMENTATION_VARIANT) + file(REMOVE ${fakeIndex}) +endif() # old content files will be removed if no index file exists set(EXPECTED_CONTENT_FILES 1) instrument(cmake-command-custom-content @@ -326,6 +378,59 @@ CHECK_SCRIPT check-trace-removed.cmake ) +# Test capture output +instrument(cmake-command-capture-output + BUILD CAPTURE_OUTPUT_QUERY + CHECK_SCRIPT check-data-dir.cmake +) + +# Test compile trace collection +if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang") + if (CMAKE_C_COMPILER_VERSION VERSION_LESS 11.1) + set(Skip_COMPILE_TRACE_QUERY_Case 1) + elseif (CMAKE_C_COMPILER_VERSION VERSION_LESS 15) + set(Skip_COMPILE_TRACE_QUERY_ARG_Case 1) + endif() +elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") + if (CMAKE_C_COMPILER_VERSION VERSION_LESS 9) + set(Skip_COMPILE_TRACE_QUERY_Case 1) + elseif (CMAKE_C_COMPILER_VERSION VERSION_LESS 16) + set(Skip_COMPILE_TRACE_QUERY_ARG_Case 1) + endif() +else() + set(Skip_COMPILE_TRACE_QUERY_Case 1) +endif() +if("$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]") + # `-ftime-trace` with multiple `-arch` puts the trace file in TMPDIR. + set(Skip_COMPILE_TRACE_QUERY_Case 1) +endif() +if(RunCMake_GENERATOR MATCHES "NMake") + # `-ftime-trace=` is hidden by `@<< ... <<` response file syntax. + set(Skip_COMPILE_TRACE_QUERY_ARG_Case 1) +endif() +if (NOT Skip_COMPILE_TRACE_QUERY_Case) + instrument(cmake-command-compile-trace + BUILD COMPILE_TRACE_QUERY + CONFIGURE_ARGS + "-DINSTRUMENT_COMPILE_TRACE=DEFAULT" + "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" + CHECK_SCRIPT check-data-dir.cmake + ) + instrument(cmake-command-compile-trace-null + BUILD COMPILE_TRACE_QUERY_NULL + CHECK_SCRIPT check-data-dir.cmake + ) + if (NOT Skip_COMPILE_TRACE_QUERY_ARG_Case) + instrument(cmake-command-compile-trace-explicit + BUILD COMPILE_TRACE_QUERY + CONFIGURE_ARGS + "-DINSTRUMENT_COMPILE_TRACE=EXPLICIT" + "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" + CHECK_SCRIPT check-data-dir.cmake + ) + endif() +endif() + # Test make/ninja hooks if(RunCMake_GENERATOR STREQUAL "FASTBuild") # FIXME(#27184): This does not work for FASTBuild. @@ -357,4 +462,13 @@ instrument(cmake-command-build-snippet BUILD_MAKE_PROGRAM CHECK_SCRIPT check-data-dir.cmake) + if (NOT Skip_COMPILE_TRACE_QUERY_Case AND NOT RunCMake_GENERATOR STREQUAL "NMake Makefiles") + instrument(cmake-command-compile-trace-make-program + BUILD_MAKE_PROGRAM BUILD_MAKE_PROGRAM_CHANGE_DIR COMPILE_TRACE_QUERY + CONFIGURE_ARGS + "-DINSTRUMENT_COMPILE_TRACE=DEFAULT" + "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" + CHECK_SCRIPT check-data-dir.cmake + ) + endif() endif()
diff --git a/Tests/RunCMake/Instrumentation/check-custom-content.cmake b/Tests/RunCMake/Instrumentation/check-custom-content.cmake index ac358ef..fd6a68e 100644 --- a/Tests/RunCMake/Instrumentation/check-custom-content.cmake +++ b/Tests/RunCMake/Instrumentation/check-custom-content.cmake
@@ -18,7 +18,7 @@ # Check version string(JSON version GET "${contents}" version) json_assert_key("${content_file}" "${version}" major "1") - json_assert_key("${content_file}" "${version}" minor "0") + json_assert_key("${content_file}" "${version}" minor "1") # Check project name json_assert_key("${content_file}" "${contents}" project "instrumentation")
diff --git a/Tests/RunCMake/Instrumentation/check-data-dir.cmake b/Tests/RunCMake/Instrumentation/check-data-dir.cmake index a72a64e..3a73300 100644 --- a/Tests/RunCMake/Instrumentation/check-data-dir.cmake +++ b/Tests/RunCMake/Instrumentation/check-data-dir.cmake
@@ -21,9 +21,20 @@ list(APPEND FOUND_SNIPPETS ${role}) endif() + # Ensure launcher-driven snippets record stdout/stderr and tests keep merged output. + if (filename MATCHES "^(compile|link|custom|install|test)-") + if (ARGS_CAPTURE_OUTPUT_QUERY) + json_has_key("${snippet}" "${contents}" stdout) + json_has_key("${snippet}" "${contents}" stderr) + else() + json_missing_key("${snippet}" "${contents}" stdout) + json_missing_key("${snippet}" "${contents}" stderr) + endif() + endif() + # Verify target string(JSON target ERROR_VARIABLE noTarget GET "${contents}" target) - if (NOT target MATCHES NOTFOUND) + if (target) set(targets "main;lib;customTarget;TARGET_NAME") if (ARGS_FAIL) list(APPEND targets "dummy") @@ -39,18 +50,18 @@ string(JSON source GET "${contents}" source) string(JSON language GET "${contents}" language) string(JSON result GET "${contents}" result) - if (NOT language MATCHES "C\\+\\+") - json_error("${snippet}" "Expected C++ compile language") + if (NOT language STREQUAL "C") + json_error("${snippet}" "Expected C compile language") endif() - if (NOT source MATCHES "${target}.cxx$") + if (NOT source MATCHES "${target}.c$") json_error("${snippet}" "Unexpected source file") endif() if (ARGS_FAIL) - if (source MATCHES "dummy.cxx" AND result EQUAL 0) + if (source MATCHES "dummy.c" AND result EQUAL 0) json_error("${snippet}" "Expected nonzero exit code for compile command, got: ${result}" ) - elseif (NOT source MATCHES "dummy.cxx" AND NOT result EQUAL 0) + elseif (NOT source MATCHES "dummy.c" AND NOT result EQUAL 0) json_error("${snippet}" "Expected zero exit code for compile command, got: ${result}" ) @@ -62,6 +73,20 @@ ) endif() endif() + if (ARGS_COMPILE_TRACE_QUERY) + json_has_key("${snippet}" "${contents}" traceFile) + string(JSON jsonFile GET "${contents}" traceFile) + if (NOT EXISTS "${v1}/data/${jsonFile}" OR IS_DIRECTORY "${v1}/data/${jsonFile}") + json_error("${snippet}" "Missing copied compile JSON file: ${jsonFile}") + else() + read_json("${v1}/data/${jsonFile}" trace_contents) + json_has_key("${v1}/data/${jsonFile}" "${trace_contents}" traceEvents) + endif() + elseif (ARGS_COMPILE_TRACE_QUERY_NULL) + json_assert_key("${snippet}" "${contents}" traceFile null) + else() + json_missing_key("${snippet}" "${contents}" traceFile) + endif() endif() # Verify contents of link-* Snippets @@ -75,6 +100,16 @@ # Verify contents of custom-* Snippets if (filename MATCHES "^custom-") string(JSON outputs GET "${contents}" outputs) + if (ARGS_CAPTURE_OUTPUT_QUERY AND outputs MATCHES "output3") + string(JSON stdout GET "${contents}" stdout) + string(JSON stderr GET "${contents}" stderr) + if (NOT stdout MATCHES "test stdout") + json_error("${snippet}" "Expected custom command stdout to be captured") + endif() + if (NOT stderr MATCHES "test stderr") + json_error("${snippet}" "Expected custom command stderr to be captured") + endif() + endif() # if "outputs" is CMakeFiles/customTarget, should not have a "target" if (outputs MATCHES "customTarget") json_missing_key("${snippet}" "${contents}" target) @@ -88,7 +123,7 @@ endif() json_missing_key("${snippet}" "${contents}" target) # unrecognized outputs - elseif (NOT outputs MATCHES "shell_redirect\\.out") + elseif (NOT outputs MATCHES "shell_redirect\\.out|output3") json_error("${snippet}" "Custom command has unexpected outputs\n${outputs}") endif() endif() @@ -116,11 +151,20 @@ "Expected zero exit code for test, got: ${result}" ) endif() + if (ARGS_CAPTURE_OUTPUT_QUERY) + string(JSON output GET "${contents}" stdout) + if (NOT output MATCHES "test stdout") + json_error("${snippet}" "Expected test output to contain stdout") + endif() + if (NOT output MATCHES "test stderr") + json_error("${snippet}" "Expected test output to contain stderr") + endif() + endif() endif() endif() # Verify the overall result, in addition to the sub-commands above. - if (filename MATCHES "^cmakeInstall|^cmakeBuild|^ctest") + if (filename MATCHES "^(cmakeInstall|cmakeBuild|ctest)") string(JSON result GET "${contents}" result) if (ARGS_FAIL AND result EQUAL 0) json_error("${snippet}" @@ -134,7 +178,7 @@ endif() # Verify that Config is Debug - if (filename MATCHES "^test|^compile|^link|^custom|^install") + if (filename MATCHES "^(test|compile|link|custom|install)") string(JSON config GET "${contents}" config) if (NOT config STREQUAL "Debug") json_error(${snippet} "Unexpected config: ${config}") @@ -142,7 +186,7 @@ endif() # Verify command args were passed - if (filename MATCHES "^cmakeBuild|^ctest") + if (filename MATCHES "^(cmakeBuild|ctest)") string(JSON command GET "${contents}" command) if (NOT command MATCHES "Debug") json_error(${snippet} "Command value missing passed arguments") @@ -196,6 +240,14 @@ add_error("custom command with shell redirection did not run") endif() +if (CHECK_NINJA_INSTRUMENT_ORDER) + set(rules_ninja "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/rules.ninja") + file(READ "${rules_ninja}" ninja_content) + if (NOT ninja_content MATCHES "command = \"[^\"]*ctest(\.exe)?\" --instrument") + add_error("Expected compile command to start with ctest --instrument") + endif() +endif() + # Look for build snippet, which may not appear immediately if (ARGS_BUILD_MAKE_PROGRAM) set(NUM_TRIES 30)
diff --git a/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake b/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake index 01336e7..60c0515 100644 --- a/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake +++ b/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake
@@ -5,7 +5,7 @@ set(RunCMake_TEST_FAILED "preBuild hook did not run\n") else() file(READ ${v1}/preBuild.hook preBuildErrors) - if (NOT preBuildErrors MATCHES "^$") + if (preBuildErrors) string(APPEND RunCMake_TEST_FAILED "Errors found in data during preBuild hook:\n${preBuildErrors}\n") endif() endif() @@ -36,7 +36,7 @@ string(APPEND RunCMake_TEST_FAILED "postBuild hook did not run\n") else() file(READ ${v1}/postBuild.hook postBuildErrors) - if (NOT postBuildErrors MATCHES "^$") + if (postBuildErrors) string(APPEND RunCMake_TEST_FAILED "Errors found in data during postBuild hook:\n${postBuildErrors}\n") endif() endif()
diff --git a/Tests/RunCMake/Instrumentation/check-query-dir.cmake b/Tests/RunCMake/Instrumentation/check-query-dir.cmake index 0ce6ed5..b320479 100644 --- a/Tests/RunCMake/Instrumentation/check-query-dir.cmake +++ b/Tests/RunCMake/Instrumentation/check-query-dir.cmake
@@ -1,12 +1,19 @@ include(${CMAKE_CURRENT_LIST_DIR}/verify-snippet.cmake) include(${CMAKE_CURRENT_LIST_DIR}/json.cmake) include(${CMAKE_CURRENT_LIST_DIR}/validate_schema.cmake) -file(GLOB_RECURSE queries LIST_DIRECTORIES false ${v1}/query/*) +file(GLOB_RECURSE queries LIST_DIRECTORIES false ${v1}/query/*) foreach(query ${queries}) validate_schema( "${query}" "${CMAKE_CURRENT_LIST_DIR}/../../../Help/manual/instrumentation/query-v1-schema.json" "${schema_validate_result}" ) + if (RunCMake_TEST_FAILED) + add_error("${RunCMake_TEST_FAILED}") + endif() endforeach() + +if (ERROR_MESSAGE) + message(FATAL_ERROR ${ERROR_MESSAGE}) +endif()
diff --git a/Tests/RunCMake/Instrumentation/cmake-command-failures-build-stdout.txt b/Tests/RunCMake/Instrumentation/cmake-command-failures-build-stdout.txt index 2d19ba1..5537f8f 100644 --- a/Tests/RunCMake/Instrumentation/cmake-command-failures-build-stdout.txt +++ b/Tests/RunCMake/Instrumentation/cmake-command-failures-build-stdout.txt
@@ -1 +1 @@ -.*dummy\.cxx.* +.*dummy\.c.*
diff --git a/Tests/RunCMake/Instrumentation/cmake-command-unsupported-data-version-stderr.txt b/Tests/RunCMake/Instrumentation/cmake-command-unsupported-data-version-stderr.txt index d8c3ec7..5864c2b 100644 --- a/Tests/RunCMake/Instrumentation/cmake-command-unsupported-data-version-stderr.txt +++ b/Tests/RunCMake/Instrumentation/cmake-command-unsupported-data-version-stderr.txt
@@ -1,6 +1,6 @@ CMake Error at [^ ]*\(cmake_instrumentation\): - cmake_instrumentation given an unsupported DATA_VERSION "1\.1" \(the only - currently supported version is 1\.0\)\. + cmake_instrumentation given an unsupported DATA_VERSION "2\.1" \(the maximum + currently supported version is 1\.1\)\. Call Stack \(most recent call first\): CMakeLists\.txt:5 \(include\)
diff --git a/Tests/RunCMake/Instrumentation/hook.cmake b/Tests/RunCMake/Instrumentation/hook.cmake index 3326a9b..e1d2908 100644 --- a/Tests/RunCMake/Instrumentation/hook.cmake +++ b/Tests/RunCMake/Instrumentation/hook.cmake
@@ -1,18 +1,42 @@ cmake_minimum_required(VERSION 3.30) include(${CMAKE_CURRENT_LIST_DIR}/json.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/validate_schema.cmake) include(${CMAKE_CURRENT_LIST_DIR}/verify-snippet.cmake) include(${CMAKE_CURRENT_LIST_DIR}/verify-trace.cmake) # Test CALLBACK script. Prints output information and verifies index file -# Called as: cmake -P hook.cmake [CheckForStaticQuery?] [CheckForTrace?] [index.json] -if (NOT ${CMAKE_ARGV3}) - set(hasStaticInfo "UNEXPECTED") +# Called as: cmake -P -DSTATIC_QUERY=<ON|OFF> -DTRACE_QUERY=<ON|OFF> \ +# -DCMake_TEST_JSON_SCHEMA=<ON|OFF> -DPython_EXECUTABLE=<path> \ +# hook.cmake index-*.json + +# Get the index file as the last argument. +math(EXPR last_arg_idx "${CMAKE_ARGC} - 1") +set(index "${CMAKE_ARGV${last_arg_idx}}") +if(NOT index MATCHES "index-.*\.json") + message(FATAL_ERROR "Received unexpected index argument: ${index}") endif() -if (NOT ${CMAKE_ARGV4}) - set(hasTrace "UNEXPECTED") -endif() -set(index ${CMAKE_ARGV5}) + +# Verify that we received the expected arguments. +function(check_args vars) + foreach(var IN LISTS vars) + if (NOT DEFINED ${var}) + message(FATAL_ERROR "Expected argument ${var}, but none given.") + endif() + endforeach() +endfunction() +check_args("STATIC_QUERY;TRACE_QUERY;Python_EXECUTABLE;CMake_TEST_JSON_SCHEMA") + +function(init_query_var input_var output_var) + set(${output_var}) + if (NOT ${input_var}) + set(${output_var} "UNEXPECTED") + endif() + return(PROPAGATE ${output_var}) +endfunction() +init_query_var(STATIC_QUERY hasStaticInfo) +init_query_var(TRACE_QUERY hasTrace) + cmake_path(GET index PARENT_PATH indexDir) cmake_path(GET indexDir PARENT_PATH dataDir) cmake_path(GET dataDir PARENT_PATH v1) @@ -29,12 +53,23 @@ return(PROPAGATE ERROR_MESSAGE) endfunction() +validate_schema( + "${index}" + "${CMAKE_CURRENT_LIST_DIR}/../../../Help/manual/instrumentation/index-v1-schema.json" + # We expect to always generate valid index files. + 0 +) +if (RunCMake_TEST_FAILED) + add_error("${RunCMake_TEST_FAILED}") + unset(RunCMake_TEST_FAILED) +endif() + json_has_key("${index}" "${contents}" version) string(JSON version_major GET "${contents}" version major) string(JSON version_minor GET "${contents}" version minor) -if (NOT version_major EQUAL 1 OR NOT version_minor EQUAL 0) - add_error("Version must be 1.0, got: ${version_major}.${version_minor}") +if (NOT version_major EQUAL 1 OR NOT version_minor LESS_EQUAL 1) + add_error("Version must be <= 1.1, got: ${version_major}.${version_minor}") endif() json_has_key("${index}" "${contents}" buildDir) @@ -171,6 +206,6 @@ endif() file(WRITE ${v1}/${hook}.hook "${ERROR_MESSAGE}") -if (NOT ERROR_MESSAGE MATCHES "^$") +if (ERROR_MESSAGE) message(FATAL_ERROR ${ERROR_MESSAGE}) endif()
diff --git a/Tests/RunCMake/Instrumentation/json.cmake b/Tests/RunCMake/Instrumentation/json.cmake index d45e769..1c59475 100644 --- a/Tests/RunCMake/Instrumentation/json.cmake +++ b/Tests/RunCMake/Instrumentation/json.cmake
@@ -25,9 +25,9 @@ cmake_parse_arguments(ARG "UNEXPECTED" "" "" ${ARGN}) unset(missingKey) string(JSON ${key} ERROR_VARIABLE missingKey GET "${json}" ${key}) - if (NOT ARG_UNEXPECTED AND NOT missingKey MATCHES NOTFOUND) + if (NOT ARG_UNEXPECTED AND missingKey) json_error("${file}" "Missing key \'${key}\':\n${json}") - elseif (ARG_UNEXPECTED AND missingKey MATCHES NOTFOUND) + elseif (ARG_UNEXPECTED AND NOT missingKey) json_error("${file}" "\nUnexpected key \'${key}\':\n${json}") endif() return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE ${key}) @@ -36,7 +36,7 @@ # Check if the JSON string `json` does not have `key`. function(json_missing_key file json key) string(JSON data ERROR_VARIABLE missingKey GET "${json}" ${key}) - if (missingKey MATCHES NOTFOUND) + if (NOT missingKey) json_error("${file}" "Has unexpected ${key}.") endif() return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE) @@ -46,7 +46,7 @@ # If successful, return its value in `key`. function(json_assert_key file json key expected) string(JSON data ERROR_VARIABLE missingKey GET "${json}" ${key}) - if (NOT missingKey MATCHES NOTFOUND) + if (missingKey) json_error("${file}" "Missing ${key}.") endif() if (NOT ${data} MATCHES ${expected})
diff --git a/Tests/RunCMake/Instrumentation/project/CMakeLists.txt b/Tests/RunCMake/Instrumentation/project/CMakeLists.txt index f713c8a..a932dca 100644 --- a/Tests/RunCMake/Instrumentation/project/CMakeLists.txt +++ b/Tests/RunCMake/Instrumentation/project/CMakeLists.txt
@@ -1,13 +1,22 @@ cmake_minimum_required(VERSION 4.3) -project(instrumentation) +project(instrumentation C) enable_testing() if (EXISTS ${INSTRUMENT_COMMAND_FILE}) include(${INSTRUMENT_COMMAND_FILE}) endif() -add_executable(main main.cxx) -add_library(lib lib.cxx) +add_executable(main main.c) +add_library(lib lib.c) target_link_libraries(main lib) + +if (INSTRUMENT_COMPILE_TRACE STREQUAL "DEFAULT") + target_compile_options(main PRIVATE "-ftime-trace") + target_compile_options(lib PRIVATE "-ftime-trace") +elseif (INSTRUMENT_COMPILE_TRACE STREQUAL "EXPLICIT") + target_compile_options(main PRIVATE "-ftime-trace=main.json") + target_compile_options(lib PRIVATE "-ftime-trace=lib.json") +endif() + add_custom_command(TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E true ) @@ -15,6 +24,11 @@ COMMAND ${CMAKE_COMMAND} -E true OUTPUT output1 output2 ) +add_custom_command( + COMMAND $<TARGET_FILE:main> + OUTPUT output3 + DEPENDS main +) file(COPY_FILE shell_redirect.txt ${CMAKE_CURRENT_BINARY_DIR}/shell_redirect.in) if(CMAKE_GENERATOR STREQUAL "Watcom WMake") @@ -35,10 +49,10 @@ ) endif() -set_property(SOURCE output1 output2 PROPERTY SYMBOLIC 1) +set_property(SOURCE output1 output2 output3 PROPERTY SYMBOLIC 1) add_custom_target(customTarget ALL COMMAND ${CMAKE_COMMAND} -E true - DEPENDS output1 shell_redirect.out + DEPENDS output1 output3 shell_redirect.out ) add_test(NAME test COMMAND $<TARGET_FILE:main>) if(DISABLE_TEST) @@ -49,10 +63,10 @@ set_target_properties(lib PROPERTIES LABELS "label3") if (FAIL) - file(WRITE "${CMAKE_BINARY_DIR}/dummy.cxx" + file(WRITE "${CMAKE_BINARY_DIR}/dummy.c" "#error \"something which will not compile\"\n" ) - add_executable(dummy "${CMAKE_BINARY_DIR}/dummy.cxx") + add_executable(dummy "${CMAKE_BINARY_DIR}/dummy.c") add_test(NAME dummy COMMAND ${CMAKE_COMMAND} -E false) install(CODE "message(FATAL_ERROR \"Failed install.\")") endif()
diff --git a/Tests/RunCMake/Instrumentation/project/lib.c b/Tests/RunCMake/Instrumentation/project/lib.c new file mode 100644 index 0000000..abe3b78 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/project/lib.c
@@ -0,0 +1,4 @@ +int lib(void) +{ + return 0; +}
diff --git a/Tests/RunCMake/Instrumentation/project/lib.cxx b/Tests/RunCMake/Instrumentation/project/lib.cxx deleted file mode 100644 index c912397..0000000 --- a/Tests/RunCMake/Instrumentation/project/lib.cxx +++ /dev/null
@@ -1,4 +0,0 @@ -int lib() -{ - return 0; -}
diff --git a/Tests/RunCMake/Instrumentation/project/lib.h b/Tests/RunCMake/Instrumentation/project/lib.h index 9f73b0d..a9ec1de 100644 --- a/Tests/RunCMake/Instrumentation/project/lib.h +++ b/Tests/RunCMake/Instrumentation/project/lib.h
@@ -1 +1 @@ -int lib(); +int lib(void);
diff --git a/Tests/RunCMake/Instrumentation/project/main.c b/Tests/RunCMake/Instrumentation/project/main.c new file mode 100644 index 0000000..78085f2 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/project/main.c
@@ -0,0 +1,10 @@ +#include <stdio.h> + +#include "lib.h" + +int main(void) +{ + fprintf(stdout, "test stdout\n"); + fprintf(stderr, "test stderr\n"); + return lib(); +}
diff --git a/Tests/RunCMake/Instrumentation/project/main.cxx b/Tests/RunCMake/Instrumentation/project/main.cxx deleted file mode 100644 index 0362522..0000000 --- a/Tests/RunCMake/Instrumentation/project/main.cxx +++ /dev/null
@@ -1,5 +0,0 @@ -#include "lib.h" -int main() -{ - return lib(); -}
diff --git a/Tests/RunCMake/Instrumentation/query/bad-version-minor.json.in b/Tests/RunCMake/Instrumentation/query/bad-version-minor.json.in index de6bc6b..afe9a55 100644 --- a/Tests/RunCMake/Instrumentation/query/bad-version-minor.json.in +++ b/Tests/RunCMake/Instrumentation/query/bad-version-minor.json.in
@@ -1,6 +1,6 @@ { "version": { "major": 1, - "minor": 1 + "minor": 9 } }
diff --git a/Tests/RunCMake/Instrumentation/query/bad-version.json.in b/Tests/RunCMake/Instrumentation/query/bad-version.json.in index de6bc6b..afe9a55 100644 --- a/Tests/RunCMake/Instrumentation/query/bad-version.json.in +++ b/Tests/RunCMake/Instrumentation/query/bad-version.json.in
@@ -1,6 +1,6 @@ { "version": { "major": 1, - "minor": 1 + "minor": 9 } }
diff --git a/Tests/RunCMake/Instrumentation/query/both-query.json.in b/Tests/RunCMake/Instrumentation/query/both-query.json.in index 4f8ecb2..0990d94 100644 --- a/Tests/RunCMake/Instrumentation/query/both-query.json.in +++ b/Tests/RunCMake/Instrumentation/query/both-query.json.in
@@ -5,6 +5,7 @@ }, "options": [ "staticSystemInformation", - "dynamicSystemInformation" + "dynamicSystemInformation", + "captureOutput" ] }
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-capture-output.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-capture-output.cmake new file mode 100644 index 0000000..17c4362 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-capture-output.cmake
@@ -0,0 +1,5 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1.1 + OPTIONS captureOutput +)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-cmake-build.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-cmake-build.cmake deleted file mode 100644 index 1b0f5b1..0000000 --- a/Tests/RunCMake/Instrumentation/query/cmake-command-cmake-build.cmake +++ /dev/null
@@ -1,7 +0,0 @@ -file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}/../hook.cmake" hook_path) -cmake_instrumentation( - API_VERSION 1 - DATA_VERSION 1 - HOOKS preBuild postBuild postCMakeBuild - CALLBACK ${CMAKE_COMMAND} -P ${hook_path} 0 0 -)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-cmake-build.cmake.in b/Tests/RunCMake/Instrumentation/query/cmake-command-cmake-build.cmake.in new file mode 100644 index 0000000..f7f723a --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-cmake-build.cmake.in
@@ -0,0 +1,6 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + HOOKS preBuild postBuild postCMakeBuild + CALLBACK @GET_HOOK@ +)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-explicit.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-explicit.cmake new file mode 100644 index 0000000..110de96 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-explicit.cmake
@@ -0,0 +1,5 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + OPTIONS compileTrace +)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-make-program.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-make-program.cmake new file mode 100644 index 0000000..110de96 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-make-program.cmake
@@ -0,0 +1,5 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + OPTIONS compileTrace +)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-null.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-null.cmake new file mode 100644 index 0000000..110de96 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace-null.cmake
@@ -0,0 +1,5 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + OPTIONS compileTrace +)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace.cmake new file mode 100644 index 0000000..110de96 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-compile-trace.cmake
@@ -0,0 +1,5 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + OPTIONS compileTrace +)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake deleted file mode 100644 index a13033d..0000000 --- a/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake +++ /dev/null
@@ -1,7 +0,0 @@ -file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}/../hook.cmake" hook_path) -cmake_instrumentation( - API_VERSION 1 - DATA_VERSION 1 - HOOKS preBuild postBuild - CALLBACK ${CMAKE_COMMAND} -P ${hook_path} 0 0 -)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake.in b/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake.in new file mode 100644 index 0000000..49d2ba6 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake.in
@@ -0,0 +1,6 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + HOOKS preBuild postBuild + CALLBACK @GET_HOOK@ +)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-trace.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-trace.cmake deleted file mode 100644 index 972b3a7..0000000 --- a/Tests/RunCMake/Instrumentation/query/cmake-command-trace.cmake +++ /dev/null
@@ -1,8 +0,0 @@ -file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}/../hook.cmake" hook_path) -cmake_instrumentation( - API_VERSION 1 - DATA_VERSION 1 - OPTIONS trace - HOOKS postBuild postCMakeInstall postCTest - CALLBACK ${CMAKE_COMMAND} -P ${hook_path} 0 1 -)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-trace.cmake.in b/Tests/RunCMake/Instrumentation/query/cmake-command-trace.cmake.in new file mode 100644 index 0000000..9abb7b2 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-trace.cmake.in
@@ -0,0 +1,7 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + OPTIONS trace + HOOKS postBuild postCMakeInstall postCTest + CALLBACK @GET_HOOK@ +)
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-unsupported-data-version.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-unsupported-data-version.cmake index 94aa71f..952b92e 100644 --- a/Tests/RunCMake/Instrumentation/query/cmake-command-unsupported-data-version.cmake +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-unsupported-data-version.cmake
@@ -1,4 +1,4 @@ cmake_instrumentation( API_VERSION 1 - DATA_VERSION 1.1 + DATA_VERSION 2.1 )
diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-workflow.cmake.in b/Tests/RunCMake/Instrumentation/query/cmake-command-workflow.cmake.in index 65b1f6a..9f8674d 100644 --- a/Tests/RunCMake/Instrumentation/query/cmake-command-workflow.cmake.in +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-workflow.cmake.in
@@ -2,5 +2,5 @@ API_VERSION 1 DATA_VERSION 1 HOOKS postCMakeWorkflow - CALLBACK "@CMAKE_COMMAND@" -P "@RunCMake_TEST_SOURCE_DIR@/../hook.cmake" 0 0 + CALLBACK @GET_HOOK@ )
diff --git a/Tests/RunCMake/Instrumentation/query/dynamic-query.json.in b/Tests/RunCMake/Instrumentation/query/dynamic-query.json.in index 7a34ac8..487b507 100644 --- a/Tests/RunCMake/Instrumentation/query/dynamic-query.json.in +++ b/Tests/RunCMake/Instrumentation/query/dynamic-query.json.in
@@ -1,7 +1,6 @@ { "version": 1, "options": [ - "staticSystemInformation", "dynamicSystemInformation" ] }
diff --git a/Tests/RunCMake/Instrumentation/query/hooks-invalid-version-ignored.json.in b/Tests/RunCMake/Instrumentation/query/hooks-invalid-version-ignored.json.in index c99213d..0aeed54 100644 --- a/Tests/RunCMake/Instrumentation/query/hooks-invalid-version-ignored.json.in +++ b/Tests/RunCMake/Instrumentation/query/hooks-invalid-version-ignored.json.in
@@ -1,7 +1,7 @@ { "version": { "major": 1, - "minor": 1 + "minor": 9 }, "hooks": ["postCMakeBuild"], "callbacks": ["@GET_HOOK@"]
diff --git a/Tests/RunCMake/Instrumentation/verify-snippet.cmake b/Tests/RunCMake/Instrumentation/verify-snippet.cmake index 1a7ca99..4170c4c 100644 --- a/Tests/RunCMake/Instrumentation/verify-snippet.cmake +++ b/Tests/RunCMake/Instrumentation/verify-snippet.cmake
@@ -2,6 +2,7 @@ include_guard() include(${CMAKE_CURRENT_LIST_DIR}/json.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/validate_schema.cmake) function(snippet_has_fields snippet contents) get_filename_component(filename "${snippet}" NAME) @@ -41,20 +42,20 @@ if(ARGS_DYNAMIC_QUERY) json_has_key("${snippet}" "${contents}" dynamicSystemInformation) string(JSON dynamicSystemInfo ERROR_VARIABLE noInfo GET "${contents}" dynamicSystemInformation) - if (noInfo MATCHES NOTFOUND) + if (NOT noInfo) json_has_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage) json_has_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed) - json_has_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage) - json_has_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed) + json_has_key("${snippet}" ${dynamicSystemInfo} afterCPULoadAverage) + json_has_key("${snippet}" ${dynamicSystemInfo} afterHostMemoryUsed) endif() else() json_missing_key("${snippet}" "${contents}" dynamicSystemInformation) string(JSON dynamicSystemInfo ERROR_VARIABLE noInfo GET "${contents}" dynamicSystemInformation) - if (noInfo MATCHES NOTFOUND) + if (NOT noInfo) json_missing_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage) json_missing_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed) - json_missing_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage) - json_missing_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed) + json_missing_key("${snippet}" ${dynamicSystemInfo} afterCPULoadAverage) + json_missing_key("${snippet}" ${dynamicSystemInfo} afterHostMemoryUsed) endif() endif() return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE) @@ -77,8 +78,8 @@ snippet_valid_timing("${contents}") string(JSON version_major GET "${contents}" version major) string(JSON version_minor GET "${contents}" version minor) - if (NOT version_major EQUAL 1 OR NOT version_minor EQUAL 0) - json_error("${snippet}" "Version must be 1.0, got: ${version_major}.${version_minor}") + if (NOT version_major EQUAL 1 OR NOT version_minor LESS_EQUAL 1) + json_error("${snippet}" "Version must be <= 1.1, got: ${version_major}.${version_minor}") endif() get_filename_component(filename "${snippet}" NAME) string(JSON result GET "${contents}" result) @@ -88,11 +89,11 @@ json_error("${snippet}" "Result must be integer, got: ${result}") endif() string(JSON outputs ERROR_VARIABLE noOutputs GET "${contents}" outputs) - if (NOT outputs MATCHES NOTFOUND) + if (outputs) string(JSON outputSizes ERROR_VARIABLE noOutputSizes GET "${contents}" outputSizes) list(LENGTH outputs outputsLen) list(LENGTH outputSizes outputSizesLen) - if (outputSizes MATCHES NOTFOUND OR NOT outputsLen EQUAL outputSizesLen) + if (NOT outputSizes OR NOT outputsLen EQUAL outputSizesLen) json_error("${snippet}" "outputs and outputSizes do not match") endif() endif() @@ -108,5 +109,15 @@ json_error("${snippet}" "Role \"${role}\" doesn't match snippet filename") endif() + validate_schema( + "${snippet}" + "${CMAKE_CURRENT_LIST_DIR}/../../../Help/manual/instrumentation/snippet-v1-schema.json" + # We expect to always generate valid snippet files. + 0 + ) + if (RunCMake_TEST_FAILED) + add_error("${RunCMake_TEST_FAILED}") + endif() + return(PROPAGATE ERROR_MESSAGE RunCMake_TEST_FAILED role) endfunction()
diff --git a/Tests/RunCMake/LinkFlags/CMAKE_LANG_LINK_FLAGS-CMP0210-WARN-stdout.txt b/Tests/RunCMake/LinkFlags/CMAKE_LANG_LINK_FLAGS-CMP0210-WARN-stdout.txt index 08f32c9..c14bead 100644 --- a/Tests/RunCMake/LinkFlags/CMAKE_LANG_LINK_FLAGS-CMP0210-WARN-stdout.txt +++ b/Tests/RunCMake/LinkFlags/CMAKE_LANG_LINK_FLAGS-CMP0210-WARN-stdout.txt
@@ -1,17 +1,19 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0210 is not set: CMAKE_<LANG>_LINK_FLAGS adds link flags to all target types\. Run "cmake --help-policy CMP0210" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. For compatibility with older versions of CMake, CMAKE_C_LINK_FLAGS will be ignored for all non-EXECUTABLE targets which use these flags. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0210 is not set: CMAKE_<LANG>_LINK_FLAGS adds link flags to all target types\. Run "cmake --help-policy CMP0210" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. For compatibility with older versions of CMake, CMAKE_CXX_LINK_FLAGS will be ignored for all non-EXECUTABLE targets which use these flags. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD-stderr.txt b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD-stderr.txt index 92ec2d4..f50645b 100644 --- a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD-stderr.txt +++ b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0141-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0141-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0141 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/MSVCDefaultFlags/CMP0092-OLD-stderr.txt b/Tests/RunCMake/MSVCDefaultFlags/CMP0092-OLD-stderr.txt index 4c33e7f..3d51000 100644 --- a/Tests/RunCMake/MSVCDefaultFlags/CMP0092-OLD-stderr.txt +++ b/Tests/RunCMake/MSVCDefaultFlags/CMP0092-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0092-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0092-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0092 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/MSVCDefaultFlags/CMP0197-NEW-config.txt b/Tests/RunCMake/MSVCDefaultFlags/CMP0197-NEW-config.txt index ca1c340..cd5b09f 100644 --- a/Tests/RunCMake/MSVCDefaultFlags/CMP0197-NEW-config.txt +++ b/Tests/RunCMake/MSVCDefaultFlags/CMP0197-NEW-config.txt
@@ -1,2 +1,2 @@ -[ ,][/-][Mm][Aa][Cc][Hh][Ii][Nn][Ee]:[A-Za-z0-9_]+[ ]|[Vv][Cc](98|7)\\\\[Bb][Ii][Nn]\\\\[Ll][Ii][Nn][Kk]\.[Ee][Xx][Ee] [^ +[ ,][/-][Mm][Aa][Cc][Hh][Ii][Nn][Ee]:[A-Za-z0-9_]+[ ]|[Vv][Cc](98|7)\\\\[Bb][Ii][Nn]\\\\[Ll][Ii][Nn][Kk]\.[Ee][Xx][Ee]"? [^ ]*@[^ ]+\\\\nm
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD-stderr.txt b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD-stderr.txt index 9163c3d..0a32608 100644 --- a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD-stderr.txt +++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0091-OLD\.cmake:[0-9] \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0091-OLD\.cmake:[0-9] \(cmake_policy\): The OLD behavior for policy CMP0091 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/MSVCRuntimeTypeInfo/CMP0117-OLD-stderr.txt b/Tests/RunCMake/MSVCRuntimeTypeInfo/CMP0117-OLD-stderr.txt index 4499d97..b6b1ab4 100644 --- a/Tests/RunCMake/MSVCRuntimeTypeInfo/CMP0117-OLD-stderr.txt +++ b/Tests/RunCMake/MSVCRuntimeTypeInfo/CMP0117-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0117-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0117-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0117 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt b/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt index 1255df4..65bbb75 100644 --- a/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt +++ b/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0113-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0113-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0113 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/MultiLint/RunCMakeTest.cmake b/Tests/RunCMake/MultiLint/RunCMakeTest.cmake index b9b69fb..a42945a 100644 --- a/Tests/RunCMake/MultiLint/RunCMakeTest.cmake +++ b/Tests/RunCMake/MultiLint/RunCMakeTest.cmake
@@ -28,9 +28,9 @@ run_multilint(genex) endif() -function(run_skip_linting test_name prop_sf prop_tgt) - set(RunCMake_TEST_VARIANT_DESCRIPTION " (prop_sf=${prop_sf}, prop_tgt=${prop_tgt})") - list(APPEND RunCMake_TEST_OPTIONS "-Dprop_sf=${prop_sf}" "-Dprop_tgt=${prop_tgt}") +function(run_skip_linting test_name prop_fs prop_sf prop_tgt) + set(RunCMake_TEST_VARIANT_DESCRIPTION " (prop_fs=${prop_fs}, prop_sf=${prop_sf}, prop_tgt=${prop_tgt})") + list(APPEND RunCMake_TEST_OPTIONS "-Dprop_fs=${prop_fs}" "-Dprop_sf=${prop_sf}" "-Dprop_tgt=${prop_tgt}") set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test_name}-build") set(RunCMake_TEST_NO_CLEAN 1) @@ -40,52 +40,90 @@ run_cmake_command(${test_name}-Build ${CMAKE_COMMAND} --build .) endfunction() -# There are a few `SKIP_LINTING` source/target propertiy combinations +# There are a few `SKIP_LINTING` file set/source/target propertiy combinations # that affect the final result: # -# prop_sf prop_tgt result -# --------------------------- -# - - OFF -# OFF - OFF -# ON - ON -# - OFF OFF -# OFF OFF OFF -# ON OFF ON -# - ON ON -# OFF ON OFF -# ON ON ON +# prop_fs prop_sf prop_tgt result +# ------------------------------------ +# - - - OFF +# OFF - - OFF +# ON - - ON +# - OFF - OFF +# OFF OFF - OFF +# ON OFF - ON +# - ON - ON +# OFF ON - OFF +# ON ON - ON +# - - OFF OFF +# OFF - OFF OFF +# ON - OFF ON +# - - ON ON +# OFF - ON OFF +# ON - ON ON +# - OFF OFF OFF +# OFF OFF OFF OFF +# ON OFF OFF ON +# - OFF ON OFF +# OFF OFF ON OFF +# ON OFF ON ON +# - ON OFF ON +# OFF ON OFF OFF +# ON ON OFF ON +# - ON ON ON +# OFF ON ON OFF +# ON ON ON ON # # where `-` means unset property. # # Here's the same table for convenience sorted by `result`: # -# prop_sf prop_tgt result -# --------------------------- -# - - OFF -# OFF - OFF -# - OFF OFF -# OFF OFF OFF -# OFF ON OFF -# ON - ON -# ON OFF ON -# - ON ON -# ON ON ON +# prop_fs prop_sf prop_tgt result +# ------------------------------------ +# - - - OFF +# OFF - - OFF +# - OFF - OFF +# OFF OFF - OFF +# OFF ON - OFF +# - - OFF OFF +# OFF - OFF OFF +# OFF - ON OFF +# - OFF OFF OFF +# OFF OFF OFF OFF +# - OFF ON OFF +# OFF OFF ON OFF +# OFF ON OFF OFF +# OFF ON ON OFF +# ON - - ON +# ON OFF - ON +# - ON - ON +# ON ON - ON +# ON - OFF ON +# - - ON ON +# ON - ON ON +# ON OFF OFF ON +# ON OFF ON ON +# - ON OFF ON +# ON ON OFF ON +# - ON ON ON +# ON ON ON ON foreach(lang IN ITEMS C CXX) # Testing `SKIP_LINTING=OFF` (first half of the table above) - set(prop_sf_OFF_variants "-" OFF "-" OFF OFF) - set(prop_tgt_OFF_variants "-" "-" OFF OFF ON) - foreach(prop_fs prop_tgt IN ZIP_LISTS prop_sf_OFF_variants prop_tgt_OFF_variants) - run_skip_linting(${lang}_skip_linting_OFF "${prop_fs}" "${prop_tgt}") + set(prop_fs_OFF_variants "-" OFF "-" OFF OFF "-" OFF OFF "-" OFF "-" OFF OFF OFF) + set(prop_sf_OFF_variants "-" "-" OFF OFF ON "-" "-" "-" OFF OFF OFF OFF ON ON) + set(prop_tgt_OFF_variants "-" "-" "-" "-" "-" OFF OFF ON OFF OFF ON ON OFF ON) + foreach(prop_fs prop_sf prop_tgt IN ZIP_LISTS prop_fs_OFF_variants prop_sf_OFF_variants prop_tgt_OFF_variants) + run_skip_linting(${lang}_skip_linting_OFF "${prop_fs}" "${prop_sf}" "${prop_tgt}") endforeach() # Testing `SKIP_LINTING=ON` (second half of the table above) - set(prop_sf_ON_variants ON ON "-" ON) - set(prop_tgt_ON_variants "-" OFF ON ON) - foreach(prop_fs prop_tgt IN ZIP_LISTS prop_sf_ON_variants prop_tgt_ON_variants) - run_skip_linting(${lang}_skip_linting_ON "${prop_fs}" "${prop_tgt}") + set(prop_fs_ON_variants ON ON "-" ON ON "-" ON ON ON "-" ON "-" ON) + set(prop_sf_ON_variants "-" OFF ON ON "-" "-" "-" OFF OFF ON ON ON ON) + set(prop_tgt_ON_variants "-" "-" "-" "-" OFF ON ON OFF ON OFF OFF ON ON) + foreach(prop_fs prop_sf prop_tgt IN ZIP_LISTS prop_fs_ON_variants prop_sf_ON_variants prop_tgt_ON_variants) + run_skip_linting(${lang}_skip_linting_ON "${prop_fs}" "${prop_sf}" "${prop_tgt}") if(NOT RunCMake_GENERATOR STREQUAL "Watcom WMake") - run_skip_linting(${lang}-launch_skip_linting_ON "${prop_fs}" "${prop_tgt}") + run_skip_linting(${lang}-launch_skip_linting_ON "${prop_fs}" "${prop_sf}" "${prop_tgt}") endif() endforeach() endforeach()
diff --git a/Tests/RunCMake/MultiLint/setup_skip_linter_test.cmake b/Tests/RunCMake/MultiLint/setup_skip_linter_test.cmake index ab8d646..a655339 100644 --- a/Tests/RunCMake/MultiLint/setup_skip_linter_test.cmake +++ b/Tests/RunCMake/MultiLint/setup_skip_linter_test.cmake
@@ -12,7 +12,12 @@ set(CMAKE_${lang}_CPPCHECK "${maybe_genex_pre}${PSEUDO_CPPCHECK}${maybe_genex_post}" -error) string(TOLOWER "${lang}" ext) - add_executable(main main.${ext}) + add_executable(main) + target_sources(main PRIVATE FILE_SET SOURCES FILES main.${ext}) + + if(NOT prop_fs STREQUAL "-") + set_property(FILE_SET SOURCES TARGET main PROPERTY SKIP_LINTING ${prop_fs}) + endif() if(NOT prop_sf STREQUAL "-") set_source_files_properties(main.${ext} PROPERTIES SKIP_LINTING ${prop_sf})
diff --git a/Tests/RunCMake/Ninja/FileSetJobPool-check.cmake b/Tests/RunCMake/Ninja/FileSetJobPool-check.cmake new file mode 100644 index 0000000..0998a22 --- /dev/null +++ b/Tests/RunCMake/Ninja/FileSetJobPool-check.cmake
@@ -0,0 +1,17 @@ +set(log "${RunCMake_BINARY_DIR}/FileSetJobPool-build/build.ninja") +file(READ "${log}" build_file) +if(NOT "${build_file}" MATCHES "pool = file_set_compile_pool") + string(CONCAT RunCMake_TEST_FAILED "Log file:\n ${log}\n" "does not have expected line: pool = file_set_compile_pool") +endif() +if(NOT "${build_file}" MATCHES "pool = target_link_pool") + string(CONCAT RunCMake_TEST_FAILED "Log file:\n ${log}\n" "does not have expected line: pool = target_link_pool") +endif() +# Even though `source_file_compile_pool` and `target_compile_pool` were defined as the target's compile jobs pool, +# since the file set property overrides it with `file_set_compile_pool` pool, the source file compile job +# and the target compile job pool should not exist in the generated Ninja file. +if("${build_file}" MATCHES "pool = source_file_compile_pool") + string(CONCAT RunCMake_TEST_FAILED "Log file:\n ${log}\n" "have unexpected line: pool = source_file_compile_pool") +endif() +if("${build_file}" MATCHES "pool = target_compile_pool") + string(CONCAT RunCMake_TEST_FAILED "Log file:\n ${log}\n" "have unexpected line: pool = target_compile_pool") +endif()
diff --git a/Tests/RunCMake/Ninja/FileSetJobPool.cmake b/Tests/RunCMake/Ninja/FileSetJobPool.cmake new file mode 100644 index 0000000..cd9dfad --- /dev/null +++ b/Tests/RunCMake/Ninja/FileSetJobPool.cmake
@@ -0,0 +1,16 @@ +set_property(GLOBAL PROPERTY JOB_POOLS source_file_compile_tool=2 file_set_compile_pool=2 + target_compile_pool=4 target_link_pool=1) + +enable_language(C) + +set_property(SOURCE hello.c PROPERTY JOB_POOL_COMPILE "source_file_compile_pool") + +add_executable(hello) +target_sources(hello PRIVATE FILE_SET SOURCES FILES hello.c) + +set_property(FILE_SET SOURCES TARGET hello PROPERTY JOB_POOL_COMPILE "file_set_compile_pool") + +set_property(TARGET hello PROPERTY JOB_POOL_COMPILE "target_compile_pool") +set_property(TARGET hello PROPERTY JOB_POOL_LINK "target_link_pool") + +include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake index 32eda9d..65202f7 100644 --- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake +++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
@@ -121,6 +121,7 @@ run_cmake_with_options(CustomCommandDepfileAsByproduct -DCMAKE_BUILD_TYPE=Debug) run_cmake(CustomCommandJobPool) run_cmake(SourceFileJobPool) +run_cmake(FileSetJobPool) run_cmake(JobPoolUsesTerminal) run_cmake(RspFileC)
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbg-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbg-debug-in-release-graph-ninja-stdout.txt index 80f91fe..11f43f0 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbg-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbg-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Release[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Release[\/]echo(\.exe)? \[3/3\] Generating echo_dbg_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Debug' 'echo_dbg_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Debug' 'echo_dbg_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbg-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbg-debug-ninja-stdout.txt index e3241f9..b080c0c 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbg-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbg-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_dbg_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_dbg_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_dbg_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-debug-in-release-graph-ninja-stdout.txt index 6de28a4..419b72f 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Release[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Release[\/]echo(\.exe)? \[3/3\] Generating echo_dbgx_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Debug' 'echo_dbgx_Debug\.txt' 'echo_dbgx_byproduct_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Debug' 'echo_dbgx_Debug\.txt' 'echo_dbgx_byproduct_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-debug-ninja-stdout.txt index df6cfe3..df5b943 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_dbgx_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_dbgx_Debug\.txt' 'echo_dbgx_byproduct_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_dbgx_Debug\.txt' 'echo_dbgx_byproduct_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-release-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-release-ninja-stdout.txt index b21e200..17df629 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-release-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_dbgx-release-ninja-stdout.txt
@@ -2,4 +2,4 @@ \[2/3\] Linking C executable Release[\/]echo(\.exe)? \[3/3\] [^ ]* -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release' 'echo_dbgx_byproduct_Release\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release' 'echo_dbgx_byproduct_Release\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-in-release-graph-ninja-stdout.txt index 80e9c2f..7789418 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-in-release-graph-ninja-stdout.txt
@@ -1,2 +1,2 @@ ^\[1/1\] Generating echo_depend_target\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-ninja-stdout.txt index 1a79877..c269aee 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-ninja-stdout.txt
@@ -1,2 +1,2 @@ ^\[1/1\] Generating echo_depend_target\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-release-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-release-ninja-stdout.txt index 80e9c2f..7789418 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-release-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-release-ninja-stdout.txt
@@ -1,2 +1,2 @@ ^\[1/1\] Generating echo_depend_target\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex-debug-in-release-graph-ninja-stdout.txt index 8de83b0..9f2f5f5 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Release[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Release[\/]echo(\.exe)? \[3/3\] Generating echo_genex_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release' 'echo_genex_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release' 'echo_genex_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex-debug-ninja-stdout.txt index 2ead6d9..b4cc0f6 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_genex_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_genex_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_genex_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex_out-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex_out-debug-in-release-graph-ninja-stdout.txt index 0e424ea..c68a0bc 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex_out-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex_out-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_genex_out_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_genex_out_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_genex_out_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex_out-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex_out-debug-ninja-stdout.txt index 7f8fff7..d72612a 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex_out-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_genex_out-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_genex_out_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_genex_out_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_genex_out_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_byproduct-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_byproduct-debug-ninja-stdout.txt index 472b8a4..b6de861 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_byproduct-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_byproduct-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_no_cross_byproduct_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_no_cross_byproduct_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_no_cross_byproduct_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_byproduct_if-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_byproduct_if-debug-ninja-stdout.txt index 5fc23a1..af04312 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_byproduct_if-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_byproduct_if-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_no_cross_byproduct_if_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_no_cross_byproduct_if_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_no_cross_byproduct_if_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_output-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_output-debug-ninja-stdout.txt index 1e1315b..184c612 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_output-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_output-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_no_cross_output\.txt, echo_no_cross_output_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_no_cross_output_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_no_cross_output_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_output_if-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_output_if-debug-ninja-stdout.txt index 7fdd5ae..780ba27 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_output_if-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_no_cross_output_if-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_no_cross_output_a\.txt, echo_no_cross_output_if_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_no_cross_output_if_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_no_cross_output_if_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_raw-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_raw-debug-in-release-graph-ninja-stdout.txt index 4738869..63d79e7 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_raw-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_raw-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Release[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Release[\/]echo(\.exe)? \[3/3\] Generating echo_raw_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Debug' 'echo_raw_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Debug' 'echo_raw_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_raw-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_raw-debug-ninja-stdout.txt index 397948c..4351268 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_raw-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_raw-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] Generating echo_raw_Debug\.txt -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_raw_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_raw_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex-debug-in-release-graph-ninja-stdout.txt index cbb82c3..572d0ef 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Release[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Release[\/]echo(\.exe)? \[3/3\] echo_target_genex -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release' 'echo_target_genex_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release' 'echo_target_genex_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex-debug-ninja-stdout.txt index 4588dac..e7ca898 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] echo_target_genex -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_target_genex_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_target_genex_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex_out-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex_out-debug-in-release-graph-ninja-stdout.txt index 2a5757a..583d61f 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex_out-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex_out-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] echo_target_genex_out -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_target_genex_out_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_target_genex_out_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex_out-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex_out-debug-ninja-stdout.txt index 016a41a..d51116b 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex_out-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_genex_out-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] echo_target_genex_out -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_target_genex_out_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_target_genex_out_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_raw-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_raw-debug-in-release-graph-ninja-stdout.txt index 1b9eb3a..7bb0289 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_raw-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_raw-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Release[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Release[\/]echo(\.exe)? \[3/3\] echo_target_raw -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Debug' 'echo_target_raw_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Debug' 'echo_target_raw_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_raw-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_raw-debug-ninja-stdout.txt index dc167bb..0f2938a 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_raw-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_target_raw-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] echo_target_raw -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_target_raw_Debug\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'echo_target_raw_Debug\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-target_no_cross_byproduct-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-target_no_cross_byproduct-debug-in-release-graph-ninja-stdout.txt index 70820dc..6ed1519 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-target_no_cross_byproduct-debug-in-release-graph-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-target_no_cross_byproduct-debug-in-release-graph-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Release[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Release[\/]echo(\.exe)? \[3/3\] target_no_cross_byproduct -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release' 'target_no_cross_byproduct\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release' 'target_no_cross_byproduct\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-target_no_cross_byproduct-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-target_no_cross_byproduct-debug-ninja-stdout.txt index e142213..36df34c 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-target_no_cross_byproduct-debug-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-target_no_cross_byproduct-debug-ninja-stdout.txt
@@ -1,4 +1,4 @@ ^\[1/3\] Building C object CMakeFiles[\/]echo\.dir[\/]Debug[\/]echo\.c\.(o|obj) \[2/3\] Linking C executable Debug[\/]echo(\.exe)? \[3/3\] target_no_cross_byproduct -'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'target_no_cross_byproduct\.txt'$ +'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfigCustomCommands[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug' 'target_no_cross_byproduct\.txt'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake index aef8870..a977cce 100644 --- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
@@ -81,6 +81,8 @@ ############################################################################### +if(CMake_TEST_NinjaMultiConfig_VARIANT STREQUAL "Simple") + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Simple-build) # IMPORTANT: Setting RelWithDebInfo as the first item in CMAKE_CONFIGURATION_TYPES # generates a build.ninja file with that configuration @@ -194,6 +196,8 @@ run_cmake_build(SimpleCrossConfigs all-all-in-release-graph Release all:all) run_cmake_build(SimpleCrossConfigs all-relwithdebinfo-in-release-graph Release all:RelWithDebInfo) +elseif(CMake_TEST_NinjaMultiConfig_VARIANT STREQUAL "CustomCommands") + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PostBuild-build) set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all") run_cmake_configure(PostBuild) @@ -391,6 +395,8 @@ run_cmake(CustomCommandDepfileAsOutput) run_cmake(CustomCommandDepfileAsByproduct) +else() + set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all") run_cmake(PerConfigSources) unset(RunCMake_TEST_OPTIONS) @@ -446,7 +452,7 @@ #run_cmake_configure(AutoMocExecutable) #run_cmake_build(AutoMocExecutable debug-in-release-graph Release exe) -# Need to test this manually because run_cmake() adds --no-warn-unused-cli +# Need to test this manually because run_cmake() adds -Wno-unused-cli set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/NoUnusedVariables-build) run_cmake_command(NoUnusedVariables ${CMAKE_COMMAND} ${CMAKE_CURRENT_LIST_DIR} -G "Ninja Multi-Config" @@ -563,3 +569,4 @@ endforeach() endif() endif() +endif()
diff --git a/Tests/RunCMake/NinjaMultiConfig/Simple-reconfigure-config-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/Simple-reconfigure-config-ninja-stdout.txt index 8c1c301..e1f9366 100644 --- a/Tests/RunCMake/NinjaMultiConfig/Simple-reconfigure-config-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/Simple-reconfigure-config-ninja-stdout.txt
@@ -1,4 +1,4 @@ -- Configuring done \([0-9]+\.[0-9]s\) -- Generating done \([0-9]+\.[0-9]s\) -- Build files have been written to: [^ -]*/Tests/RunCMake/NinjaMultiConfig/Simple-build +]*/Tests/RunCMake/NinjaMultiConfigSimple/Simple-build
diff --git a/Tests/RunCMake/NinjaMultiConfig/Simple-reconfigure-noconfig-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/Simple-reconfigure-noconfig-ninja-stdout.txt index 8c1c301..e1f9366 100644 --- a/Tests/RunCMake/NinjaMultiConfig/Simple-reconfigure-noconfig-ninja-stdout.txt +++ b/Tests/RunCMake/NinjaMultiConfig/Simple-reconfigure-noconfig-ninja-stdout.txt
@@ -1,4 +1,4 @@ -- Configuring done \([0-9]+\.[0-9]s\) -- Generating done \([0-9]+\.[0-9]s\) -- Build files have been written to: [^ -]*/Tests/RunCMake/NinjaMultiConfig/Simple-build +]*/Tests/RunCMake/NinjaMultiConfigSimple/Simple-build
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-stderr.txt b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-stderr.txt new file mode 100644 index 0000000..6f0c756 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-stderr.txt
@@ -0,0 +1,12 @@ +^CMake Warning \(deprecated\) at CMP0154-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0154 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/ObsoleteQtMacros/AutomocMacro-WARN-stderr.txt b/Tests/RunCMake/ObsoleteQtMacros/AutomocMacro-WARN-stderr.txt index 193b751..fe97f9a 100644 --- a/Tests/RunCMake/ObsoleteQtMacros/AutomocMacro-WARN-stderr.txt +++ b/Tests/RunCMake/ObsoleteQtMacros/AutomocMacro-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Deprecation Warning at .*/Modules/Qt4Macros\.cmake:[^ ]+ \(message\): +CMake Warning \(deprecated\) at .*/Modules/Qt4Macros\.cmake:[^ ]+ \(message\): The qt4_automoc macro is obsolete\. Use the CMAKE_AUTOMOC feature instead\. Call Stack \(most recent call first\): AutomocMacro-WARN\.cmake:7 \(qt4_automoc\)
diff --git a/Tests/RunCMake/ObsoleteQtMacros/AutomocMacro-WARN.cmake b/Tests/RunCMake/ObsoleteQtMacros/AutomocMacro-WARN.cmake index c0c64a5..aea9c5d 100644 --- a/Tests/RunCMake/ObsoleteQtMacros/AutomocMacro-WARN.cmake +++ b/Tests/RunCMake/ObsoleteQtMacros/AutomocMacro-WARN.cmake
@@ -1,7 +1,7 @@ find_package(Qt4 REQUIRED) -set(CMAKE_WARN_DEPRECATED 1) +cmake_diagnostic(SET CMD_DEPRECATED WARN) add_library(foo SHARED empty.cpp) qt4_automoc(foo_moc_srcs empty.cpp)
diff --git a/Tests/RunCMake/ObsoleteQtMacros/UseModulesMacro-WARN-stderr.txt b/Tests/RunCMake/ObsoleteQtMacros/UseModulesMacro-WARN-stderr.txt index 14a707b..9387f4f 100644 --- a/Tests/RunCMake/ObsoleteQtMacros/UseModulesMacro-WARN-stderr.txt +++ b/Tests/RunCMake/ObsoleteQtMacros/UseModulesMacro-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Deprecation Warning at .*/Modules/Qt4Macros\.cmake:[^ ]+ \(message\): +CMake Warning \(deprecated\) at .*/Modules/Qt4Macros\.cmake:[^ ]+ \(message\): The qt4_use_modules function is obsolete\. Use target_link_libraries with IMPORTED targets instead\. Call Stack \(most recent call first\):
diff --git a/Tests/RunCMake/ObsoleteQtMacros/UseModulesMacro-WARN.cmake b/Tests/RunCMake/ObsoleteQtMacros/UseModulesMacro-WARN.cmake index e86a372..9bd3cc3 100644 --- a/Tests/RunCMake/ObsoleteQtMacros/UseModulesMacro-WARN.cmake +++ b/Tests/RunCMake/ObsoleteQtMacros/UseModulesMacro-WARN.cmake
@@ -1,7 +1,7 @@ find_package(Qt4 REQUIRED) -set(CMAKE_WARN_DEPRECATED 1) +cmake_diagnostic(SET CMD_DEPRECATED WARN) add_library(foo SHARED empty.cpp) qt4_use_modules(foo LINK_PRIVATE Core)
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.50.35724.0.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.50.35724.0.input new file mode 100644 index 0000000..f79c6a3 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.50.35724.0.input
@@ -0,0 +1,19 @@ +CMAKE_LANG=C +CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/18/Professional/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/link.exe +CMAKE_C_COMPILER_ABI= +CMAKE_C_COMPILER_AR= +CMAKE_C_COMPILER_ARCHITECTURE_ID=x64 +CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN= +CMAKE_C_COMPILER_ID=MSVC +CMAKE_C_COMPILER_LAUNCHER= +CMAKE_C_COMPILER_LOADED=1 +CMAKE_C_COMPILER_RANLIB= +CMAKE_C_COMPILER_TARGET= +CMAKE_C_COMPILER_VERSION=19.50.35724.0 +CMAKE_C_COMPILER_VERSION_INTERNAL= +CMAKE_HOST_SYSTEM_NAME=Windows +Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp' + +Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_42252 +[1/2] "C:\Program Files\Microsoft Visual Studio\18\Professional\VC\Tools\MSVC\14.50.35717\bin\Hostx64\x64\cl.exe" /nologo -D_MBCS /DWIN32 /D_WINDOWS /W3 /MDd /Zi /Ob0 /Od /RTC1 /showIncludes /FoCMakeFiles\cmTC_42252.dir\CMakeCCompilerABI.c.obj /FdCMakeFiles\cmTC_42252.dir\ /FS -c "C:\DoesNotExist\CMake\Modules\CMakeCCompilerABI.c" +[2/2] C:\WINDOWS\system32\cmd.exe /C "cd . && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E vs_link_exe --msvc-ver=1950 --intdir=CMakeFiles\cmTC_42252.dir --rc="C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\rc.exe" --mt="C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\mt.exe" --manifests -- "C:\Program Files\Microsoft Visual Studio\18\Professional\VC\Tools\MSVC\14.50.35717\bin\Hostx64\x64\link.exe" /nologo CMakeFiles\cmTC_42252.dir\CMakeCCompilerABI.c.obj /out:cmTC_42252.exe /implib:cmTC_42252.lib /pdb:cmTC_42252.pdb /version:0.0 /machine:x64 /debug /INCREMENTAL /subsystem:console kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.50.35724.0.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.50.35724.0.input new file mode 100644 index 0000000..4e2983a --- /dev/null +++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.50.35724.0.input
@@ -0,0 +1,19 @@ +CMAKE_LANG=CXX +CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/18/Professional/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/link.exe +CMAKE_CXX_COMPILER_ABI= +CMAKE_CXX_COMPILER_AR= +CMAKE_CXX_COMPILER_ARCHITECTURE_ID=x64 +CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN= +CMAKE_CXX_COMPILER_ID=MSVC +CMAKE_CXX_COMPILER_LAUNCHER= +CMAKE_CXX_COMPILER_LOADED=1 +CMAKE_CXX_COMPILER_RANLIB= +CMAKE_CXX_COMPILER_TARGET= +CMAKE_CXX_COMPILER_VERSION=19.50.35724.0 +CMAKE_CXX_COMPILER_VERSION_INTERNAL= +CMAKE_HOST_SYSTEM_NAME=Windows +Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp' + +Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_6fe1d +[1/2] "C:\Program Files\Microsoft Visual Studio\18\Professional\VC\Tools\MSVC\14.50.35717\bin\Hostx64\x64\cl.exe" /nologo /TP -D_MBCS /DWIN32 /D_WINDOWS /W3 /GR /EHsc /MDd /Zi /Ob0 /Od /RTC1 /showIncludes /FoCMakeFiles\cmTC_6fe1d.dir\CMakeCXXCompilerABI.cpp.obj /FdCMakeFiles\cmTC_6fe1d.dir\ /FS -c "C:\DoesNotExist\CMake\Modules\CMakeCXXCompilerABI.cpp" +[2/2] C:\WINDOWS\system32\cmd.exe /C "cd . && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E vs_link_exe --msvc-ver=1950 --intdir=CMakeFiles\cmTC_6fe1d.dir --rc="C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\rc.exe" --mt="C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\mt.exe" --manifests -- "C:\Program Files\Microsoft Visual Studio\18\Professional\VC\Tools\MSVC\14.50.35717\bin\Hostx64\x64\link.exe" /nologo CMakeFiles\cmTC_6fe1d.dir\CMakeCXXCompilerABI.cpp.obj /out:cmTC_6fe1d.exe /implib:cmTC_6fe1d.lib /pdb:cmTC_6fe1d.pdb /version:0.0 /machine:x64 /debug /INCREMENTAL /subsystem:console kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake index b887e77..5fd0e92 100644 --- a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake +++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
@@ -62,6 +62,7 @@ sunos5.11_i386-C-GNU-5.5.0 sunos5.11_i386-CXX-GNU-5.5.0 sunos5.11_i386-Fortran-GNU-5.5.0 windows_x86_64-C-MSVC-19.36.32543.0 windows_x86_64-CXX-MSVC-19.36.32543.0 windows_x86_64-C-MSVC-19.38.33130.0-VS windows_x86_64-CXX-MSVC-19.38.33130.0-VS + windows_x86_64-C-MSVC-19.50.35724.0 windows_x86_64-CXX-MSVC-19.50.35724.0 windows_x86_64-C-Clang-17.0.1-MSVC windows_x86_64-CXX-Clang-17.0.1-MSVC windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC windows_x86_64-C-Intel-2021.9.0.20230302 windows_x86_64-CXX-Intel-2021.9.0.20230302 windows_x86_64-Fortran-Intel-2021.9.0.20230302
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.50.35724.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.50.35724.0.output new file mode 100644 index 0000000..86d56a8 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.50.35724.0.output
@@ -0,0 +1,3 @@ +libs= +dirs= +linker_tool=C:/Program Files/Microsoft Visual Studio/18/Professional/VC/Tools/MSVC/14\.50\.35717/bin/Hostx64/x64/link\.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.50.35724.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.50.35724.0.output new file mode 100644 index 0000000..86d56a8 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.50.35724.0.output
@@ -0,0 +1,3 @@ +libs= +dirs= +linker_tool=C:/Program Files/Microsoft Visual Studio/18/Professional/VC/Tools/MSVC/14\.50\.35717/bin/Hostx64/x64/link\.exe
diff --git a/Tests/RunCMake/Policy/MinVersionLargerThanMax-stderr.txt b/Tests/RunCMake/Policy/MinVersionLargerThanMax-stderr.txt index 1fe985d..3a74173 100644 --- a/Tests/RunCMake/Policy/MinVersionLargerThanMax-stderr.txt +++ b/Tests/RunCMake/Policy/MinVersionLargerThanMax-stderr.txt
@@ -1,4 +1,4 @@ CMake Error at MinVersionLargerThanMax\.cmake:[0-9]+ \(cmake_policy\): - Policy VERSION range "3\.12\.\.\.3\.8" specifies a larger minimum than maximum\. + Policy VERSION range "3\.12\.\.\.3\.8" specifies a later minimum than maximum\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/PrecompileHeaders-Reuse/PchReuseFrom-CMP0141-OLD-stderr.txt b/Tests/RunCMake/PrecompileHeaders-Reuse/PchReuseFrom-CMP0141-OLD-stderr.txt index ae26054..1d15d51 100644 --- a/Tests/RunCMake/PrecompileHeaders-Reuse/PchReuseFrom-CMP0141-OLD-stderr.txt +++ b/Tests/RunCMake/PrecompileHeaders-Reuse/PchReuseFrom-CMP0141-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at PchReuseFrom-CMP0141-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at PchReuseFrom-CMP0141-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0141 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/PrecompileHeaders/DisabledPchEnv-stdout.txt b/Tests/RunCMake/PrecompileHeaders/DisabledPchEnv-stdout.txt new file mode 100644 index 0000000..58d0988 --- /dev/null +++ b/Tests/RunCMake/PrecompileHeaders/DisabledPchEnv-stdout.txt
@@ -0,0 +1,2 @@ +-- ENV{CMAKE_DISABLE_PRECOMPILE_HEADERS}='ON' +-- CMAKE_DISABLE_PRECOMPILE_HEADERS='ON'
diff --git a/Tests/RunCMake/PrecompileHeaders/DisabledPchEnv.cmake b/Tests/RunCMake/PrecompileHeaders/DisabledPchEnv.cmake new file mode 100644 index 0000000..fa277aa --- /dev/null +++ b/Tests/RunCMake/PrecompileHeaders/DisabledPchEnv.cmake
@@ -0,0 +1,2 @@ +message(STATUS "ENV{CMAKE_DISABLE_PRECOMPILE_HEADERS}='$ENV{CMAKE_DISABLE_PRECOMPILE_HEADERS}'") +message(STATUS "CMAKE_DISABLE_PRECOMPILE_HEADERS='${CMAKE_DISABLE_PRECOMPILE_HEADERS}'")
diff --git a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake index 58c45f5..d4af640 100644 --- a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake +++ b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
@@ -9,6 +9,11 @@ endfunction() run_cmake(DisabledPch) +block() + set(ENV{CMAKE_DISABLE_PRECOMPILE_HEADERS} "ON") + run_cmake(DisabledPchEnv) + unset(ENV{CMAKE_DISABLE_PRECOMPILE_HEADERS}) +endblock() run_cmake(PchDebugGenex) if (RunCMake_GENERATOR MATCHES "(Ninja|Makefiles|Visual Studio)") run_cmake(PchDebugGenexShort)
diff --git a/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake b/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake index 60a93a0..2f8d2d4 100644 --- a/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake +++ b/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake
@@ -5,10 +5,17 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) -add_executable(pch-test main.cpp non-pch.cpp) +add_executable(pch-test main.cpp non-pch1.cpp) target_precompile_headers(pch-test PRIVATE pch.h) -set_source_files_properties(non-pch.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON) +set_source_files_properties(non-pch1.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON) + +target_sources(pch-test PRIVATE FILE_SET f1 TYPE SOURCES FILES non-pch2.cpp) +set_property(FILE_SET f1 TARGET pch-test PROPERTY SKIP_PRECOMPILE_HEADERS ON) + +target_sources(pch-test PRIVATE FILE_SET f2 TYPE SOURCES FILES non-pch3.cpp) + +set_source_files_properties(non-pch3.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON) enable_testing() add_test(NAME pch-test COMMAND pch-test)
diff --git a/Tests/RunCMake/PrecompileHeaders/non-pch.cpp b/Tests/RunCMake/PrecompileHeaders/non-pch1.cpp similarity index 100% rename from Tests/RunCMake/PrecompileHeaders/non-pch.cpp rename to Tests/RunCMake/PrecompileHeaders/non-pch1.cpp
diff --git a/Tests/RunCMake/PrecompileHeaders/non-pch.cpp b/Tests/RunCMake/PrecompileHeaders/non-pch2.cpp similarity index 100% copy from Tests/RunCMake/PrecompileHeaders/non-pch.cpp copy to Tests/RunCMake/PrecompileHeaders/non-pch2.cpp
diff --git a/Tests/RunCMake/PrecompileHeaders/non-pch.cpp b/Tests/RunCMake/PrecompileHeaders/non-pch3.cpp similarity index 100% copy from Tests/RunCMake/PrecompileHeaders/non-pch.cpp copy to Tests/RunCMake/PrecompileHeaders/non-pch3.cpp
diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index e3409b8..bf83507 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake
@@ -105,6 +105,9 @@ else() set(maybe_input_file "") endif() + if (RunCMake_QUIET_ERROR) + set(maybe_quiet_error "ERROR_QUIET") + endif() if(NOT RunCMake_TEST_COMMAND) if(NOT DEFINED RunCMake_TEST_OPTIONS) set(RunCMake_TEST_OPTIONS "") @@ -134,7 +137,7 @@ endif() list(APPEND RunCMake_TEST_COMMAND -DRunCMake_TEST=${test} - --no-warn-unused-cli + -Wno-unused-cli ) else() set(RunCMake_TEST_OPTIONS "") @@ -157,6 +160,7 @@ ENCODING UTF8 ${maybe_timeout} ${maybe_input_file} + ${maybe_quiet_error} )]]) else() set(expect_result "") @@ -234,7 +238,7 @@ ) if(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION) string(REGEX REPLACE [[ -^CMake Deprecation Warning at [^ +^CMake Warning \(deprecated\) at [^ ]*CMakeLists.txt:1 \(cmake_minimum_required\): Compatibility with CMake < 3\.10 will be removed from a future version of CMake. @@ -242,6 +246,8 @@ Update the VERSION argument <min> value\. Or, use the <min>\.\.\.<max> syntax to tell CMake that the project requires at least <min> but has been updated to work with policies introduced by <max> or earlier\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + ]] "" actual_stderr "${actual_stderr}") endif()
diff --git a/Tests/RunCMake/Rust/Editions-build-Ninja-stdout.txt b/Tests/RunCMake/Rust/Editions-build-Ninja-stdout.txt new file mode 100644 index 0000000..21786cd --- /dev/null +++ b/Tests/RunCMake/Rust/Editions-build-Ninja-stdout.txt
@@ -0,0 +1,5 @@ +\[1/5\] Building Rust object CMakeFiles[\\/]Edition\.20[0-9][0-9]\.dir[\\/](Debug[\\/])?libedition_20[0-9][0-9]\.rs\.rlib +\[2/5\] Building Rust object CMakeFiles[\\/]Edition\.20[0-9][0-9]\.dir[\\/](Debug[\\/])?libedition_20[0-9][0-9]\.rs\.rlib +\[3/5\] Building Rust object CMakeFiles[\\/]Edition\.20[0-9][0-9]\.dir[\\/](Debug[\\/])?libedition_20[0-9][0-9]\.rs\.rlib +\[4/5\] Building Rust object CMakeFiles[\\/]Edition\.20[0-9][0-9]\.dir[\\/](Debug[\\/])?libedition_20[0-9][0-9]\.rs\.rlib +\[5/5\] Linking Rust executable (Debug[\\/])?Editions
diff --git a/Tests/RunCMake/Rust/Editions-build-UnixMakefiles-stdout.txt b/Tests/RunCMake/Rust/Editions-build-UnixMakefiles-stdout.txt new file mode 100644 index 0000000..a946b1e --- /dev/null +++ b/Tests/RunCMake/Rust/Editions-build-UnixMakefiles-stdout.txt
@@ -0,0 +1,10 @@ +\[ 20%\] Building Rust object CMakeFiles[\\/]Edition.20[0-9][0-9]\.dir[\\/]libedition_20[0-9][0-9]\.rs\.rlib +\[ 20%\] Built target Edition\.20[0-9][0-9] +\[ 40%\] Building Rust object CMakeFiles[\\/]Edition.20[0-9][0-9]\.dir[\\/]libedition_20[0-9][0-9]\.rs\.rlib +\[ 40%\] Built target Edition\.20[0-9][0-9] +\[ 60%\] Building Rust object CMakeFiles[\\/]Edition.20[0-9][0-9]\.dir[\\/]libedition_20[0-9][0-9]\.rs\.rlib +\[ 60%\] Built target Edition\.20[0-9][0-9] +\[ 80%\] Building Rust object CMakeFiles[\\/]Edition.20[0-9][0-9]\.dir[\\/]libedition_20[0-9][0-9]\.rs\.rlib +\[ 80%\] Built target Edition\.20[0-9][0-9] +\[100%\] Linking Rust executable Editions +\[100%\] Built target Editions
diff --git a/Tests/RunCMake/Rust/Editions-stderr.txt b/Tests/RunCMake/Rust/Editions-stderr.txt new file mode 100644 index 0000000..b86f7c3 --- /dev/null +++ b/Tests/RunCMake/Rust/Editions-stderr.txt
@@ -0,0 +1,7 @@ +^CMake Warning \(experimental\) at Editions\.cmake:[0-9]+ \(enable_language\): + CMake's support for the Rust programming language 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-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/Rust/Editions-stdout.txt b/Tests/RunCMake/Rust/Editions-stdout.txt new file mode 100644 index 0000000..a5017df --- /dev/null +++ b/Tests/RunCMake/Rust/Editions-stdout.txt
@@ -0,0 +1,3 @@ +-- Configuring done \([.0-9]+s\) +-- Generating done \([.0-9]+s\) +-- Build files have been written to: .*[\\/]Editions-build
diff --git a/Tests/RunCMake/Rust/Editions.cmake b/Tests/RunCMake/Rust/Editions.cmake new file mode 100644 index 0000000..95b408f --- /dev/null +++ b/Tests/RunCMake/Rust/Editions.cmake
@@ -0,0 +1,32 @@ +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") +enable_language(Rust) + +# No Edition set, rustc defaults to 2015. +add_library(Edition.2015 OBJECT edition_2015.rs) +set_target_properties(Edition.2015 PROPERTIES Rust_EDITION 2015) + +# Explicitly set edition on target, without default value from variable. +add_library(Edition.2018 OBJECT edition_2018.rs) +set_target_properties(Edition.2018 PROPERTIES Rust_EDITION 2018) + +# Implicitly set edition on target from variable +set(CMAKE_Rust_EDITION 2021) +add_library(Edition.2021 OBJECT edition_2021.rs) + +# Explicitly override the edition. +add_library(Edition.2024 OBJECT edition_2024.rs) +set_target_properties(Edition.2024 PROPERTIES Rust_EDITION 2024) + +# Rust guarantees that we can link multiple crates with different editions +# together. We specify explicitly the edition to make sure CMake properly pass +# the edition in the linking step too. +unset(CMAKE_Rust_EDITION) +add_executable(Editions editions.rs) +set_target_properties(Editions PROPERTIES Rust_EDITION 2018) +target_link_libraries( + Editions PRIVATE + Edition.2015 + Edition.2018 + Edition.2021 + Edition.2024 +)
diff --git a/Tests/RunCMake/Rust/Enable-stderr.txt b/Tests/RunCMake/Rust/Enable-stderr.txt index 5f0fc64..be2db74 100644 --- a/Tests/RunCMake/Rust/Enable-stderr.txt +++ b/Tests/RunCMake/Rust/Enable-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at Enable\.cmake:[0-9]+ \(enable_language\): +^CMake Warning \(experimental\) at Enable\.cmake:[0-9]+ \(enable_language\): CMake's support for the Rust programming language 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/Rust/Enable.cmake b/Tests/RunCMake/Rust/Enable.cmake index 89d9c54..9bdff73 100644 --- a/Tests/RunCMake/Rust/Enable.cmake +++ b/Tests/RunCMake/Rust/Enable.cmake
@@ -1,3 +1,3 @@ -set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c") +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") enable_language(Rust) message(STATUS "CMAKE_Rust_COMPILER='${CMAKE_Rust_COMPILER}'")
diff --git a/Tests/RunCMake/Rust/RunCMakeTest.cmake b/Tests/RunCMake/Rust/RunCMakeTest.cmake index 5178b4f..ae6f602 100644 --- a/Tests/RunCMake/Rust/RunCMakeTest.cmake +++ b/Tests/RunCMake/Rust/RunCMakeTest.cmake
@@ -2,3 +2,16 @@ run_cmake(Enable) run_cmake(EnableExperimental) + +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Editions-build) + run_cmake(Editions) + set(RunCMake_TEST_NO_CLEAN 1) + if (RunCMake_GENERATOR MATCHES "Ninja") + run_cmake_command(Editions-build-Ninja ${CMAKE_COMMAND} --build . --config Debug) + elseif(RunCMake_GENERATOR MATCHES "Makefiles") + run_cmake_command(Editions-build-UnixMakefiles ${CMAKE_COMMAND} --build . --config Debug) + else() + message(FATAL_ERROR "Unsupported ${RunCMake_GENERATOR} generator!") + endif() +endblock()
diff --git a/Tests/RunCMake/Rust/edition_2015.rs b/Tests/RunCMake/Rust/edition_2015.rs new file mode 100644 index 0000000..3c85b92 --- /dev/null +++ b/Tests/RunCMake/Rust/edition_2015.rs
@@ -0,0 +1,2 @@ +// dyn is as an identifier is only allowed in Rust 2015 +pub fn dyn() {}
diff --git a/Tests/RunCMake/Rust/edition_2018.rs b/Tests/RunCMake/Rust/edition_2018.rs new file mode 100644 index 0000000..2e68a3a --- /dev/null +++ b/Tests/RunCMake/Rust/edition_2018.rs
@@ -0,0 +1,18 @@ +#![allow(rust_2021_compatibility)] + +pub trait Foo {} + +struct FooA {} +impl Foo for FooA {} + +struct FooB {} +impl Foo for FooB {} + +// dyn keyword is available in 2018+ +pub fn make_foo(value: i32) -> Box<dyn Foo> { + match value { + // Unsupported in 2021+, should be 0..=42 + 0...42 => Box::new(FooA{}), + _ => Box::new(FooB{}), + } +}
diff --git a/Tests/RunCMake/Rust/edition_2021.rs b/Tests/RunCMake/Rust/edition_2021.rs new file mode 100644 index 0000000..ad113c7 --- /dev/null +++ b/Tests/RunCMake/Rust/edition_2021.rs
@@ -0,0 +1,5 @@ +// gen is becoming a keyword in 2024 +pub fn gen() -> i32 { + // This does not compile before Rust 2021 + [1, 2, 3].into_iter().reduce(|acc, n| acc + n).unwrap_or(0) +}
diff --git a/Tests/RunCMake/Rust/edition_2024.rs b/Tests/RunCMake/Rust/edition_2024.rs new file mode 100644 index 0000000..4ba8093 --- /dev/null +++ b/Tests/RunCMake/Rust/edition_2024.rs
@@ -0,0 +1,8 @@ +pub fn add_options(a: Option<i32>, b: Option<i32>) -> Option<i32> { + // let chains only allowed in Rust 2024 + if let Some(a) = a && let Some(b) = b { + Some(a + b) + } else { + None + } +}
diff --git a/Tests/RunCMake/Rust/editions.rs b/Tests/RunCMake/Rust/editions.rs new file mode 100644 index 0000000..01aaa74 --- /dev/null +++ b/Tests/RunCMake/Rust/editions.rs
@@ -0,0 +1,12 @@ + +use edition_2015; +use edition_2018; +use edition_2021; +use edition_2024; + +fn main() { + edition_2015::r#dyn(); + let _ = edition_2018::make_foo(1337); + let _ = edition_2021::r#gen(); + let _ = edition_2024::add_options(None, None); +}
diff --git a/Tests/RunCMake/Sbom/ApplicationTarget-install-check.cmake b/Tests/RunCMake/Sbom/ApplicationTarget-install-check.cmake index c0d3cd4..0b63fe0 100644 --- a/Tests/RunCMake/Sbom/ApplicationTarget-install-check.cmake +++ b/Tests/RunCMake/Sbom/ApplicationTarget-install-check.cmake
@@ -29,7 +29,7 @@ set(APPLICATION_EXPECTED [=[ { - "spdxId" : "application#Package", + "spdxId" : "urn:application#Package", "name" : "application", "software_primaryPurpose" : "application", "type" : "software_Package" @@ -38,7 +38,7 @@ set(DEPENDENCY_EXPECTED [=[ { - "spdxId" : "bar:bar#Package", + "spdxId" : "urn:bar:bar#Package", "name" : "bar:bar", "originatedBy" : [ @@ -74,7 +74,7 @@ string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement" "0") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element" "0") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/Assertions.cmake b/Tests/RunCMake/Sbom/Assertions.cmake index 772103b..86ff872 100644 --- a/Tests/RunCMake/Sbom/Assertions.cmake +++ b/Tests/RunCMake/Sbom/Assertions.cmake
@@ -30,109 +30,6 @@ set(RunCMake_TEST_FAILED "${msg}" PARENT_SCOPE) endmacro() -function(_json_verify_subset out_success actual_node expected_node path_list) - string(JSON expected_type ERROR_VARIABLE expected_is_primitive TYPE "${expected_node}") - - if(expected_is_primitive OR NOT (expected_type STREQUAL "OBJECT" OR expected_type STREQUAL "ARRAY")) - if("${actual_node}" STREQUAL "${expected_node}") - set(${out_success} TRUE PARENT_SCOPE) - else() - _json_fail_at("Value Mismatch" "${actual_node}" "${expected_node}" "${path_list}") - set(${out_success} FALSE PARENT_SCOPE) - endif() - return() - endif() - - string(JSON actual_type ERROR_VARIABLE actual_err TYPE "${actual_node}") - if(actual_err) - _json_fail_at("Invalid JSON" "${actual_node}" "Valid JSON Structure" "${path_list}") - set(${out_success} FALSE PARENT_SCOPE) - return() - endif() - - if(NOT actual_type STREQUAL expected_type) - _json_fail_at("Type Mismatch" "${actual_type}" "${expected_type}" "${path_list}") - set(${out_success} FALSE PARENT_SCOPE) - return() - endif() - - if(expected_type STREQUAL "OBJECT") - string(JSON exp_len LENGTH "${expected_node}") - math(EXPR exp_last "${exp_len}-1") - - foreach(idx RANGE ${exp_last}) - string(JSON key MEMBER "${expected_node}" "${idx}") - - string(JSON act_child ERROR_VARIABLE err GET "${actual_node}" "${key}") - if(err) - _json_fail_at("Missing Key" "<missing>" "${key}" "${path_list}") - set(${out_success} FALSE PARENT_SCOPE) - return() - endif() - - string(JSON exp_child GET "${expected_node}" "${key}") - set(next_path ${path_list}) - list(APPEND next_path "${key}") - - _json_verify_subset(sub_ok "${act_child}" "${exp_child}" "${next_path}") - if(NOT sub_ok) - set(${out_success} FALSE PARENT_SCOPE) - return() - endif() - endforeach() - - set(${out_success} TRUE PARENT_SCOPE) - return() - - elseif(expected_type STREQUAL "ARRAY") - string(JSON exp_len LENGTH "${expected_node}") - string(JSON act_len LENGTH "${actual_node}") - - if(act_len LESS exp_len) - _json_fail_at("Array Length" "${act_len}" ">= ${exp_len}" "${path_list}") - set(${out_success} FALSE PARENT_SCOPE) - return() - endif() - - math(EXPR exp_last "${exp_len}-1") - math(EXPR act_last "${act_len}-1") - set(used_indices "") - - foreach(exp_idx RANGE ${exp_last}) - string(JSON exp_item GET "${expected_node}" "${exp_idx}") - set(found_match FALSE) - - foreach(act_idx RANGE ${act_last}) - if("${act_idx}" IN_LIST used_indices) - continue() - endif() - - string(JSON act_item GET "${actual_node}" "${act_idx}") - unset(RunCMake_TEST_FAILED) - set(next_path ${path_list}) - list(APPEND next_path "${act_idx}") - - _json_verify_subset(is_match "${act_item}" "${exp_item}" "${next_path}") - - if(is_match) - set(found_match TRUE) - list(APPEND used_indices "${act_idx}") - break() - endif() - endforeach() - - if(NOT found_match) - _json_fail_at("Array Element Missing" "<no match found>" "Element [${exp_idx}]" "${path_list}") - set(${out_success} FALSE PARENT_SCOPE) - return() - endif() - endforeach() - - set(${out_success} TRUE PARENT_SCOPE) - return() - endif() -endfunction() - function(expect_object actual_json expected_var) if(NOT DEFINED "${expected_var}") @@ -150,11 +47,20 @@ set(actual_node "${actual_json}") endif() - unset(RunCMake_TEST_FAILED) - _json_verify_subset(success "${actual_node}" "${expected_node}" "${path_segments}") + string(JSON actual_type TYPE "${actual_node}") + string(JSON expected_type TYPE "${expected_node}") - if(NOT success) - set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + set(pattern "${expected_node}") + set(fail_type "Partial Equality Mismatch") + if(actual_type STREQUAL "ARRAY" AND expected_type STREQUAL "OBJECT") + set(pattern "[ ${expected_node} ]") + set(fail_type "Object Not Found in Array") + endif() + + string(JSON is_equal PARTIAL_EQUAL "${pattern}" "${actual_node}") + + if(NOT is_equal) + _json_fail_at("${fail_type}" "${actual_node}" "${expected_node}" "${path_segments}") endif() endfunction()
diff --git a/Tests/RunCMake/Sbom/DuplicateSbom.cmake b/Tests/RunCMake/Sbom/DuplicateSbom.cmake new file mode 100644 index 0000000..b44eb08 --- /dev/null +++ b/Tests/RunCMake/Sbom/DuplicateSbom.cmake
@@ -0,0 +1,6 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) +include(GNUInstallDirs) + +add_library(liba INTERFACE) + +install(TARGETS liba EXPORT setA DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/EmptyNamespaceFallback-install-check.cmake b/Tests/RunCMake/Sbom/EmptyNamespaceFallback-install-check.cmake new file mode 100644 index 0000000..dd5a6b1 --- /dev/null +++ b/Tests/RunCMake/Sbom/EmptyNamespaceFallback-install-check.cmake
@@ -0,0 +1,48 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(SPDX_DOCUMENT_EXPECTED [=[ +{ + "spdxId" : "urn:mySbom#SPDXDocument", + "name" : "mySbom", + "type" : "SpdxDocument" +} +]=]) + +set(LIBB_EXPECTED [=[ +{ + "spdxId" : "urn:libb#Package", + "name" : "libb", + "software_primaryPurpose" : "library", + "type" : "software_Package" +} +]=]) + +set(LIBA_BARE_EXPECTED [=[ +{ + "spdxId" : "urn:liba#Package", + "name" : "liba", + "type" : "software_Package" +} +]=]) + +set(LIBB_BUILD_REQUIRES_EXPECTED [=[ +{ + "description" : "Required Build-Time Libraries", + "from" : "urn:libb#Package", + "relationshipType" : "dependsOn", + "spdxId" : "urn:Shared#Relationship", + "to" : + [ + "urn:liba#Package" + ], + "type" : "Relationship" +} +]=]) + +string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) +expect_array("${SPDX_DOCUMENT}" 1 "rootElement") +expect_object("${SPDX_DOCUMENT}" LIBB_EXPECTED "rootElement") +expect_object("${SPDX_DOCUMENT}" LIBA_BARE_EXPECTED "element") +string(JSON BUILD_REQUIRES GET "${content}" "@graph" "2") +expect_object("${BUILD_REQUIRES}" LIBB_BUILD_REQUIRES_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/EmptyNamespaceFallback.cmake b/Tests/RunCMake/Sbom/EmptyNamespaceFallback.cmake new file mode 100644 index 0000000..e0719d2 --- /dev/null +++ b/Tests/RunCMake/Sbom/EmptyNamespaceFallback.cmake
@@ -0,0 +1,9 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) +include(GNUInstallDirs) + +add_library(liba INTERFACE) +add_library(libb INTERFACE) +target_link_libraries(libb INTERFACE liba) + +install(TARGETS liba EXPORT setA DESTINATION .) +install(TARGETS libb EXPORT setB DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/InstallExportPlusSbomSameSet.cmake b/Tests/RunCMake/Sbom/InstallExportPlusSbomSameSet.cmake new file mode 100644 index 0000000..1268fbe --- /dev/null +++ b/Tests/RunCMake/Sbom/InstallExportPlusSbomSameSet.cmake
@@ -0,0 +1,12 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) +include(GNUInstallDirs) + +# Guards against the issue described here: +# https://gitlab.kitware.com/cmake/cmake/-/work_items/27721 + +add_library(liba INTERFACE) +add_library(libb INTERFACE) +target_link_libraries(libb INTERFACE liba) + +install(TARGETS liba EXPORT export_set_a DESTINATION .) +install(TARGETS libb EXPORT export_set_b DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/InterfaceTarget-install-check.cmake b/Tests/RunCMake/Sbom/InterfaceTarget-install-check.cmake index be35c1a..7033cf5 100644 --- a/Tests/RunCMake/Sbom/InterfaceTarget-install-check.cmake +++ b/Tests/RunCMake/Sbom/InterfaceTarget-install-check.cmake
@@ -31,7 +31,7 @@ { "spdxId" : "urn:interface#Package", "name" : "interface", - "software_primaryPurpose" : "application", + "software_primaryPurpose" : "library", "type" : "software_Package" } ]=]) @@ -74,7 +74,7 @@ string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement" "0") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element" "0") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/MissingPackageNamespace-install-check.cmake b/Tests/RunCMake/Sbom/MissingPackageNamespace-install-check.cmake index a332f27..b5864dc 100644 --- a/Tests/RunCMake/Sbom/MissingPackageNamespace-install-check.cmake +++ b/Tests/RunCMake/Sbom/MissingPackageNamespace-install-check.cmake
@@ -38,16 +38,16 @@ set(DEPENDENCY_EXPECTED [=[ { - "spdxId" : "urn:bar:bar#Package", - "name" : "bar:bar", + "spdxId" : "urn:baz:baz#Package", + "name" : "baz:baz", "originatedBy" : [ { - "name" : "bar", + "name" : "baz", "type" : "Organization" } ], - "software_packageVersion" : "1.3.5", + "software_packageVersion" : "1.8.5", "type" : "software_Package" } ]=]) @@ -61,7 +61,7 @@ "spdxId" : "urn:Static#Relationship", "to" : [ - "urn:bar:bar#Package" + "urn:baz:baz#Package" ], "type" : "Relationship" } @@ -74,7 +74,7 @@ string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement" "0") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element" "0") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/MultiNamespaceAmbiguity.cmake b/Tests/RunCMake/Sbom/MultiNamespaceAmbiguity.cmake new file mode 100644 index 0000000..e0719d2 --- /dev/null +++ b/Tests/RunCMake/Sbom/MultiNamespaceAmbiguity.cmake
@@ -0,0 +1,9 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) +include(GNUInstallDirs) + +add_library(liba INTERFACE) +add_library(libb INTERFACE) +target_link_libraries(libb INTERFACE liba) + +install(TARGETS liba EXPORT setA DESTINATION .) +install(TARGETS libb EXPORT setB DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/MultiSetAmbiguity.cmake b/Tests/RunCMake/Sbom/MultiSetAmbiguity.cmake new file mode 100644 index 0000000..eab71b1 --- /dev/null +++ b/Tests/RunCMake/Sbom/MultiSetAmbiguity.cmake
@@ -0,0 +1,10 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) +include(GNUInstallDirs) + +add_library(liba INTERFACE) +add_library(libb INTERFACE) +target_link_libraries(libb INTERFACE liba) + +install(TARGETS liba EXPORT setA1 DESTINATION .) +install(TARGETS liba EXPORT setA2 DESTINATION .) +install(TARGETS libb EXPORT setB DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/MultiSetSingleSbom-install-check.cmake b/Tests/RunCMake/Sbom/MultiSetSingleSbom-install-check.cmake new file mode 100644 index 0000000..50ac5a5 --- /dev/null +++ b/Tests/RunCMake/Sbom/MultiSetSingleSbom-install-check.cmake
@@ -0,0 +1,49 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(SPDX_DOCUMENT_EXPECTED [=[ +{ + "spdxId" : "urn:mySbom#SPDXDocument", + "name" : "mySbom", + "type" : "SpdxDocument" +} +]=]) + +set(LIBA_EXPECTED [=[ +{ + "spdxId" : "urn:liba#Package", + "name" : "liba", + "software_primaryPurpose" : "library", + "type" : "software_Package" +} +]=]) + +set(LIBB_EXPECTED [=[ +{ + "spdxId" : "urn:libb#Package", + "name" : "libb", + "software_primaryPurpose" : "library", + "type" : "software_Package" +} +]=]) + +set(LIBB_LINKED_LIBRARIES_EXPECTED [=[ +{ + "description" : "Linked Libraries", + "from" : "urn:libb#Package", + "relationshipType" : "dependsOn", + "to" : + [ + "urn:liba#Package" + ], + "type" : "Relationship" +} +]=]) + +expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") +string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) +expect_array("${SPDX_DOCUMENT}" 2 "rootElement") +expect_object("${SPDX_DOCUMENT}" LIBA_EXPECTED "rootElement") +expect_object("${SPDX_DOCUMENT}" LIBB_EXPECTED "rootElement") +string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") +expect_object("${LINKED_LIBRARIES}" LIBB_LINKED_LIBRARIES_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/MultiSetSingleSbom.cmake b/Tests/RunCMake/Sbom/MultiSetSingleSbom.cmake new file mode 100644 index 0000000..e0719d2 --- /dev/null +++ b/Tests/RunCMake/Sbom/MultiSetSingleSbom.cmake
@@ -0,0 +1,9 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) +include(GNUInstallDirs) + +add_library(liba INTERFACE) +add_library(libb INTERFACE) +target_link_libraries(libb INTERFACE liba) + +install(TARGETS liba EXPORT setA DESTINATION .) +install(TARGETS libb EXPORT setB DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake index 00e6e37..697eb70 100644 --- a/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake +++ b/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake
@@ -31,7 +31,7 @@ { "spdxId" : "urn:test#Package", "name" : "test", - "software_primaryPurpose" : "application", + "software_primaryPurpose" : "library", "type" : "software_Package" } ]=]) @@ -43,4 +43,4 @@ string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement" "0") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement")
diff --git a/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake b/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake new file mode 100644 index 0000000..33fca83 --- /dev/null +++ b/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake
@@ -0,0 +1,21 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(DOCUMENT_METADATA_EXPECTED [=[ +{ + "description" : "An eloquent description", + "dataLicense" : "BSD-3" +} +]=]) + +set(PACKAGE_METADATA_EXPECTED [=[ +{ + "spdxId" : "urn:test#Package", + "software_packageVersion" : "1.3.4", + "software_homePage" : "www.example.com", + "software_downloadLocation" : "https://example.com/test_targets.tar.gz" +} +]=]) + +string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +expect_object("${SPDX_DOCUMENT}" DOCUMENT_METADATA_EXPECTED) +expect_object("${SPDX_DOCUMENT}" PACKAGE_METADATA_EXPECTED "rootElement")
diff --git a/Tests/RunCMake/Sbom/ReferencesNonExportedTarget-install-check.cmake b/Tests/RunCMake/Sbom/ReferencesNonExportedTarget-install-check.cmake deleted file mode 100644 index 8ccbbb3..0000000 --- a/Tests/RunCMake/Sbom/ReferencesNonExportedTarget-install-check.cmake +++ /dev/null
@@ -1,81 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) - -set(CREATION_INFO_EXPECTED [=[ -{ - "@id" : "_:Build#CreationInfo", - "comment" : "This SBOM was generated from the CMakeLists.txt File", - "created" : "2026-02-17T10:34:34Z", - "createdBy" : - [ - "https://gitlab.kitware.com/cmake/cmake" - ], - "specVersion" : "3.0.1", - "type" : "CreationInfo" -} -]=]) - -set(SPDX_DOCUMENT_EXPECTED [=[ -{ - "name" : "application_targets", - "profileConformance" : - [ - "core", - "software" - ], - "creationInfo" : "_:Build#CreationInfo", - "spdxId" : "urn:application_targets#SPDXDocument", - "type" : "SpdxDocument" -} -]=]) - -set(APPLICATION_EXPECTED [=[ -{ - "spdxId" : "application#Package", - "name" : "application", - "software_primaryPurpose" : "application", - "type" : "software_Package" -} -]=]) - -set(DEPENDENCY_EXPECTED [=[ -{ - "spdxId" : "bar:bar#Package", - "name" : "bar:bar", - "originatedBy" : - [ - { - "name" : "bar", - "type" : "Organization" - } - ], - "software_packageVersion" : "1.3.5", - "type" : "software_Package" -} -]=]) - -set(BUILD_LINKED_LIBRARIES_EXPECTED [=[ -{ - "creationInfo" : "_:Build#CreationInfo", - "description" : "Linked Libraries", - "from" : "urn:application#Package", - "relationshipType" : "dependsOn", - "spdxId" : "urn:Static#Relationship", - "to" : - [ - "urn:bar:bar#Package" - ], - "type" : "Relationship" -} -]=]) - - -expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON CREATION_INFO GET "${content}" "@graph" "0") -expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) - -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") -expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement" "0") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element" "0") -string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") -expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/Requirements-install-check.cmake b/Tests/RunCMake/Sbom/Requirements-install-check.cmake index d70adb9..92f2010 100644 --- a/Tests/RunCMake/Sbom/Requirements-install-check.cmake +++ b/Tests/RunCMake/Sbom/Requirements-install-check.cmake
@@ -15,30 +15,30 @@ ]=]) -set(BAR_SPDX_DOCUMENT [=[ +set(BAR_SPDX_DOCUMENT_EXPECTED [=[ { "creationInfo" : "_:Build#CreationInfo", - "name" : "bar", + "name" : "bar_sbom", "profileConformance" : [ "core", "software" ], - "spdxId" : "urn:bar#SPDXDocument", + "spdxId" : "urn:bar_sbom#SPDXDocument", "type" : "SpdxDocument" } ]=]) -set(FOO_SPDX_DOCUMENT [=[ +set(FOO_SPDX_DOCUMENT_EXPECTED [=[ { "creationInfo" : "_:Build#CreationInfo", - "name" : "foo", + "name" : "foo_sbom", "profileConformance" : [ "core", "software" ], - "spdxId" : "urn:foo#SPDXDocument", + "spdxId" : "urn:foo_sbom#SPDXDocument", "type" : "SpdxDocument" } ]=]) @@ -46,9 +46,19 @@ set(FOO_LIBB [=[ { "creationInfo" : "_:Build#CreationInfo", - "name" : "libd", + "name" : "libb", "software_primaryPurpose" : "library", - "spdxId" : "urn:libd#Package", + "spdxId" : "urn:libb#Package", + "type" : "software_Package" +} +]=]) + +set(BAR_LIBB [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "libb", + "software_primaryPurpose" : "library", + "spdxId" : "urn:libb#Package", "type" : "software_Package" } ]=]) @@ -67,7 +77,7 @@ { "spdxId" : "urn:libd#Package", "name" : "libd", - "software_primaryPurpose" : "LIBRARY", + "software_primaryPurpose" : "library", "type" : "software_Package" } ]=]) @@ -126,14 +136,14 @@ expect_value("${FOO_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") string(JSON FOO_CREATION_INFO GET "${FOO_CONTENT}" "@graph" "0") string(JSON FOO_SPDX_DOCUMENT GET "${FOO_CONTENT}" "@graph" "1") -expect_object("${FOO_SPDX_DOCUMENT}" FOO_SPDX_DOCUMENT) -expect_object("${FOO_SPDX_DOCUMENT}" FOO_LIBB "rootElement" "0") +expect_object("${FOO_SPDX_DOCUMENT}" FOO_SPDX_DOCUMENT_EXPECTED) +expect_object("${FOO_SPDX_DOCUMENT}" FOO_LIBB "rootElement") expect_value("${BAR_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") string(JSON BAR_CREATION_INFO GET "${BAR_CONTENT}" "@graph" "0") string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" "@graph" "1") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_SPDX_DOCUMENT) -expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC "rootElement" "0") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBD "rootElement" "1") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_TEST "element" "0") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element" "1") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_SPDX_DOCUMENT_EXPECTED) +expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC "rootElement") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBD "rootElement") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_TEST "element") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element")
diff --git a/Tests/RunCMake/Sbom/Requirements.cmake b/Tests/RunCMake/Sbom/Requirements.cmake index 1750fc2..5f3e4d7 100644 --- a/Tests/RunCMake/Sbom/Requirements.cmake +++ b/Tests/RunCMake/Sbom/Requirements.cmake
@@ -19,7 +19,8 @@ target_link_libraries(libd INTERFACE test::liba foo bar) install(TARGETS libb EXPORT foo DESTINATION .) -install(SBOM foo EXPORT foo DESTINATION .) +install(EXPORT foo NAMESPACE foo:: DESTINATION lib/cmake/foo) +install(SBOM foo_sbom EXPORTS foo DESTINATION .) install(TARGETS libc libd EXPORT bar DESTINATION .) -install(SBOM bar EXPORT bar DESTINATION .) +install(SBOM bar_sbom EXPORTS bar DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity-install-check.cmake b/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity-install-check.cmake new file mode 100644 index 0000000..47d12bc --- /dev/null +++ b/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity-install-check.cmake
@@ -0,0 +1,49 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(BAR_SPDX_DOCUMENT_EXPECTED [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "bar_sbom", + "profileConformance" : + [ + "core", + "software" + ], + "spdxId" : "urn:bar_sbom#SPDXDocument", + "type" : "SpdxDocument" +} +]=]) + +set(BAR_LIBC [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "libc", + "software_primaryPurpose" : "library", + "spdxId" : "urn:libc#Package", + "type" : "software_Package" +} +]=]) + +set(BAR_DEPENDENCY_FOO [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "foo_sbom:libb", + "originatedBy" : + [ + { + "creationInfo" : "_:Build#CreationInfo", + "name" : "foo_sbom", + "spdxId" : "urn:foo_sbom#Organization", + "type" : "Organization" + } + ], + "spdxId" : "urn:foo_sbom:libb#Package", + "type" : "software_Package" +} +]=]) + +expect_value("${BAR_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") +string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" "@graph" "1") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_SPDX_DOCUMENT_EXPECTED) +expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC "rootElement") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element")
diff --git a/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity.cmake b/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity.cmake new file mode 100644 index 0000000..ba1be45 --- /dev/null +++ b/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity.cmake
@@ -0,0 +1,9 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) + +add_library(libb INTERFACE) +add_library(libc INTERFACE) + +target_link_libraries(libc INTERFACE libb) + +install(TARGETS libb EXPORT foo DESTINATION .) +install(TARGETS libc EXPORT bar DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/SbomNamespaceFallback-install-check.cmake b/Tests/RunCMake/Sbom/SbomNamespaceFallback-install-check.cmake new file mode 100644 index 0000000..d80856c --- /dev/null +++ b/Tests/RunCMake/Sbom/SbomNamespaceFallback-install-check.cmake
@@ -0,0 +1,78 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(FOO_SPDX_DOCUMENT_EXPECTED [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "foo", + "profileConformance" : + [ + "core", + "software" + ], + "spdxId" : "urn:foo#SPDXDocument", + "type" : "SpdxDocument" +} +]=]) + +set(BAR_SPDX_DOCUMENT_EXPECTED [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "bar", + "profileConformance" : + [ + "core", + "software" + ], + "spdxId" : "urn:bar#SPDXDocument", + "type" : "SpdxDocument" +} +]=]) + +set(FOO_LIBB [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "libb", + "software_primaryPurpose" : "library", + "spdxId" : "urn:libb#Package", + "type" : "software_Package" +} +]=]) + +set(BAR_LIBC [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "libc", + "software_primaryPurpose" : "library", + "spdxId" : "urn:libc#Package", + "type" : "software_Package" +} +]=]) + +set(BAR_DEPENDENCY_FOO [=[ +{ + "creationInfo" : "_:Build#CreationInfo", + "name" : "foo:libb", + "originatedBy" : + [ + { + "creationInfo" : "_:Build#CreationInfo", + "name" : "foo", + "spdxId" : "urn:foo#Organization", + "type" : "Organization" + } + ], + "spdxId" : "urn:foo:libb#Package", + "type" : "software_Package" +} +]=]) + +expect_value("${FOO_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") +string(JSON FOO_SPDX_DOCUMENT GET "${FOO_CONTENT}" "@graph" "1") +expect_object("${FOO_SPDX_DOCUMENT}" FOO_SPDX_DOCUMENT_EXPECTED) +expect_object("${FOO_SPDX_DOCUMENT}" FOO_LIBB "rootElement") + +expect_value("${BAR_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") +string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" "@graph" "1") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_SPDX_DOCUMENT_EXPECTED) +expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC "rootElement") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element")
diff --git a/Tests/RunCMake/Sbom/SbomNamespaceFallback.cmake b/Tests/RunCMake/Sbom/SbomNamespaceFallback.cmake new file mode 100644 index 0000000..ba1be45 --- /dev/null +++ b/Tests/RunCMake/Sbom/SbomNamespaceFallback.cmake
@@ -0,0 +1,9 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) + +add_library(libb INTERFACE) +add_library(libc INTERFACE) + +target_link_libraries(libc INTERFACE libb) + +install(TARGETS libb EXPORT foo DESTINATION .) +install(TARGETS libc EXPORT bar DESTINATION .)
diff --git a/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake b/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake index c0d3cd4..7114364 100644 --- a/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake +++ b/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake
@@ -15,39 +15,39 @@ set(SPDX_DOCUMENT_EXPECTED [=[ { - "name" : "application_targets", + "name" : "shared_targets", "profileConformance" : [ "core", "software" ], "creationInfo" : "_:Build#CreationInfo", - "spdxId" : "urn:application_targets#SPDXDocument", + "spdxId" : "urn:shared_targets#SPDXDocument", "type" : "SpdxDocument" } ]=]) set(APPLICATION_EXPECTED [=[ { - "spdxId" : "application#Package", - "name" : "application", - "software_primaryPurpose" : "application", + "spdxId" : "urn:shared#Package", + "name" : "shared", + "software_primaryPurpose" : "library", "type" : "software_Package" } ]=]) set(DEPENDENCY_EXPECTED [=[ { - "spdxId" : "bar:bar#Package", - "name" : "bar:bar", + "spdxId" : "urn:foo:foo#Package", + "name" : "foo:foo", "originatedBy" : [ { - "name" : "bar", + "name" : "foo", "type" : "Organization" } ], - "software_packageVersion" : "1.3.5", + "software_packageVersion" : "1.2.3", "type" : "software_Package" } ]=]) @@ -56,12 +56,12 @@ { "creationInfo" : "_:Build#CreationInfo", "description" : "Linked Libraries", - "from" : "urn:application#Package", + "from" : "urn:shared#Package", "relationshipType" : "dependsOn", "spdxId" : "urn:Static#Relationship", "to" : [ - "urn:bar:bar#Package" + "urn:foo:foo#Package" ], "type" : "Relationship" } @@ -74,7 +74,7 @@ string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement" "0") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element" "0") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/TargetInMultipleSets-install-check.cmake b/Tests/RunCMake/Sbom/TargetInMultipleSets-install-check.cmake new file mode 100644 index 0000000..76c00f7 --- /dev/null +++ b/Tests/RunCMake/Sbom/TargetInMultipleSets-install-check.cmake
@@ -0,0 +1,24 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(SPDX_DOCUMENT_EXPECTED [=[ +{ + "spdxId" : "urn:mySbom#SPDXDocument", + "name" : "mySbom", + "type" : "SpdxDocument" +} +]=]) + +set(LIBA_EXPECTED [=[ +{ + "spdxId" : "urn:liba#Package", + "name" : "liba", + "software_primaryPurpose" : "library", + "type" : "software_Package" +} +]=]) + +string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) +# liba should only appear once. +expect_array("${SPDX_DOCUMENT}" 1 "rootElement") +expect_object("${SPDX_DOCUMENT}" LIBA_EXPECTED "rootElement")
diff --git a/Tests/RunCMake/Sbom/TargetInMultipleSets.cmake b/Tests/RunCMake/Sbom/TargetInMultipleSets.cmake new file mode 100644 index 0000000..ba1986c --- /dev/null +++ b/Tests/RunCMake/Sbom/TargetInMultipleSets.cmake
@@ -0,0 +1,7 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) +include(GNUInstallDirs) + +add_library(liba INTERFACE) + +install(TARGETS liba EXPORT setA1 DESTINATION .) +install(TARGETS liba EXPORT setA2 DESTINATION .)
diff --git a/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt b/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt index 1ce6af6..9b7b347 100644 --- a/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt +++ b/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt
@@ -1,3 +1,3 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(author\) in CMakeLists\.txt: Unknown Swift_COMPILATION_MODE on target 'greetings_who_knows' -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt b/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt index 507fb5c..b392ea8 100644 --- a/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt +++ b/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt
@@ -1 +1,2 @@ -swiftc(\.exe)? .* -output-file-map CMakeFiles(/|\\)greetings_default\.dir(/|\\)(Debug)?(/|\\)output-file-map\.json .* +swiftc(\.exe)?"? [^ +]* -output-file-map CMakeFiles(/|\\)greetings_default\.dir(/|\\)(Debug)?(/|\\)output-file-map\.json .*
diff --git a/Tests/RunCMake/Swift/CMP0195-NEW-build-stdout.txt b/Tests/RunCMake/Swift/CMP0195-NEW-build-stdout.txt index f09d187..57a8a52 100644 --- a/Tests/RunCMake/Swift/CMP0195-NEW-build-stdout.txt +++ b/Tests/RunCMake/Swift/CMP0195-NEW-build-stdout.txt
@@ -1 +1,3 @@ -swiftc(\.exe)? .* -emit-module -emit-module-path El(/|\\)((Debug|Release)(/|\\))?El\.swiftmodule(/|\\)[-_a-z0-9]+\.swiftmodule -module-name El +swiftc(\.exe)?"? [^ +]* -module-name El [^ +]* -emit-module -emit-module-path El(/|\\)((Debug|Release)(/|\\))?El\.swiftmodule(/|\\)[-_a-z0-9]+\.swiftmodule
diff --git a/Tests/RunCMake/Swift/CMP0195-OLD-build-stdout.txt b/Tests/RunCMake/Swift/CMP0195-OLD-build-stdout.txt index 0be100d..65cbf1f 100644 --- a/Tests/RunCMake/Swift/CMP0195-OLD-build-stdout.txt +++ b/Tests/RunCMake/Swift/CMP0195-OLD-build-stdout.txt
@@ -1 +1,3 @@ -swiftc(\.exe)? .* -emit-module -emit-module-path El(/|\\)((Debug|Release)(/|\\))?El\.swiftmodule -module-name El +swiftc(\.exe)?"? [^ +]* -module-name El [^ +]* -emit-module -emit-module-path El(/|\\)((Debug|Release)(/|\\))?El\.swiftmodule
diff --git a/Tests/RunCMake/Swift/CMP0214-NEW-CMP0157-OLD-check.cmake b/Tests/RunCMake/Swift/CMP0214-NEW-CMP0157-OLD-check.cmake new file mode 100644 index 0000000..00694c5 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0214-NEW-CMP0157-OLD-check.cmake
@@ -0,0 +1,18 @@ +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +# With CMP0157 OLD (no split build), CMAKE_SHARED_LINKER_FLAGS should NOT be +# passed to Swift shared libraries. +if(build_ninja MATCHES "Swift_SHARED_LIBRARY_LINKER[^\n]*(\n [^\n]+)*\n LINK_FLAGS = [^\n]*-foo") + string(APPEND RunCMake_TEST_FAILED "Build file:\n ${path}\nunexpectedly has -foo in LINK_FLAGS for Swift shared library\n") +endif() + +# With CMP0157 OLD (no split build), CMAKE_EXE_LINKER_FLAGS should NOT be +# passed to Swift executables even with CMP0214 NEW. +if(build_ninja MATCHES "Swift_EXECUTABLE_LINKER[^\n]*(\n [^\n]+)*\n LINK_FLAGS = [^\n]*-foo") + string(APPEND RunCMake_TEST_FAILED "Build file:\n ${path}\nunexpectedly has -foo in LINK_FLAGS for Swift executable\n") +endif()
diff --git a/Tests/RunCMake/Swift/CMP0214-NEW-CMP0157-OLD.cmake b/Tests/RunCMake/Swift/CMP0214-NEW-CMP0157-OLD.cmake new file mode 100644 index 0000000..15b3108 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0214-NEW-CMP0157-OLD.cmake
@@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 4.3) +cmake_policy(SET CMP0157 OLD) +cmake_policy(SET CMP0214 NEW) +include(LinkerFlags-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0214-NEW-check.cmake b/Tests/RunCMake/Swift/CMP0214-NEW-check.cmake new file mode 100644 index 0000000..d0b94aa --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0214-NEW-check.cmake
@@ -0,0 +1,17 @@ +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +# With CMP0157 NEW (split build), CMAKE_SHARED_LINKER_FLAGS should be passed +# to Swift shared libraries regardless of CMP0214. +if(NOT build_ninja MATCHES "Swift_SHARED_LIBRARY_LINKER[^\n]*(\n [^\n]+)*\n LINK_FLAGS = [^\n]*-foo") + string(APPEND RunCMake_TEST_FAILED "Build file:\n ${path}\ndoes not have -foo in LINK_FLAGS for Swift shared library\n") +endif() + +# With CMP0214 NEW, CMAKE_EXE_LINKER_FLAGS should be passed to Swift executables. +if(NOT build_ninja MATCHES "Swift_EXECUTABLE_LINKER[^\n]*(\n [^\n]+)*\n LINK_FLAGS = [^\n]*-foo") + string(APPEND RunCMake_TEST_FAILED "Build file:\n ${path}\ndoes not have -foo in LINK_FLAGS for Swift executable\n") +endif()
diff --git a/Tests/RunCMake/Swift/CMP0214-NEW.cmake b/Tests/RunCMake/Swift/CMP0214-NEW.cmake new file mode 100644 index 0000000..9c5787b --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0214-NEW.cmake
@@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 4.3) +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0214 NEW) +include(LinkerFlags-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0214-OLD-check.cmake b/Tests/RunCMake/Swift/CMP0214-OLD-check.cmake new file mode 100644 index 0000000..9c28856 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0214-OLD-check.cmake
@@ -0,0 +1,17 @@ +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +# With CMP0157 NEW (split build), CMAKE_SHARED_LINKER_FLAGS should still be +# passed to Swift shared libraries even with CMP0214 OLD. +if(NOT build_ninja MATCHES "Swift_SHARED_LIBRARY_LINKER[^\n]*(\n [^\n]+)*\n LINK_FLAGS = [^\n]*-foo") + string(APPEND RunCMake_TEST_FAILED "Build file:\n ${path}\ndoes not have -foo in LINK_FLAGS for Swift shared library\n") +endif() + +# With CMP0214 OLD, CMAKE_EXE_LINKER_FLAGS should NOT be passed to Swift executables. +if(build_ninja MATCHES "Swift_EXECUTABLE_LINKER[^\n]*(\n [^\n]+)*\n LINK_FLAGS = [^\n]*-foo") + string(APPEND RunCMake_TEST_FAILED "Build file:\n ${path}\nunexpectedly has -foo in LINK_FLAGS for Swift executable\n") +endif()
diff --git a/Tests/RunCMake/Swift/CMP0214-OLD.cmake b/Tests/RunCMake/Swift/CMP0214-OLD.cmake new file mode 100644 index 0000000..59c64f9 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0214-OLD.cmake
@@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 4.3) +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0214 OLD) +include(LinkerFlags-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0216-NEW-check.cmake b/Tests/RunCMake/Swift/CMP0216-NEW-check.cmake new file mode 100644 index 0000000..02b763a --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0216-NEW-check.cmake
@@ -0,0 +1,21 @@ +if(RunCMake_GENERATOR MATCHES "Ninja") + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") + else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") + endif() + file(READ "${path}" build_ninja) + + if(NOT build_ninja MATCHES "-package-name") + set(RunCMake_TEST_FAILED + "Expected -package-name in generated Ninja build files when CMP0216 is NEW") + endif() +elseif(RunCMake_GENERATOR MATCHES "Xcode") + file(READ + "${RunCMake_TEST_BINARY_DIR}/CMP0216-NEW.xcodeproj/project.pbxproj" + pbxproj) + if(NOT pbxproj MATCHES "SWIFT_PACKAGE_NAME") + set(RunCMake_TEST_FAILED + "Expected SWIFT_PACKAGE_NAME in generated Xcode project when CMP0216 is NEW") + endif() +endif()
diff --git a/Tests/RunCMake/Swift/CMP0216-NEW.cmake b/Tests/RunCMake/Swift/CMP0216-NEW.cmake new file mode 100644 index 0000000..0aaf393 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0216-NEW.cmake
@@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 4.0) + +cmake_policy(SET CMP0216 NEW) +include(CMP0216-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0216-OLD-check.cmake b/Tests/RunCMake/Swift/CMP0216-OLD-check.cmake new file mode 100644 index 0000000..abfe69c --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0216-OLD-check.cmake
@@ -0,0 +1,20 @@ +if(RunCMake_GENERATOR MATCHES "Ninja") + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") + else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") + endif() + file(READ "${path}" build_ninja) + if(_content MATCHES "-package-name") + set(RunCMake_TEST_FAILED + "Unexpected -package-name in generated Ninja build files when CMP0216 is OLD") + endif() +elseif(RunCMake_GENERATOR MATCHES "Xcode") + file(READ + "${RunCMake_TEST_BINARY_DIR}/CMP0216-OLD.xcodeproj/project.pbxproj" + pbxproj) + if(pbxproj MATCHES "SWIFT_PACKAGE_NAME") + set(RunCMake_TEST_FAILED + "Unexpected SWIFT_PACKAGE_NAME in generated Xcode project when CMP0216 is OLD") + endif() +endif()
diff --git a/Tests/RunCMake/Swift/CMP0216-OLD.cmake b/Tests/RunCMake/Swift/CMP0216-OLD.cmake new file mode 100644 index 0000000..f3dc575 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0216-OLD.cmake
@@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 4.0) + +cmake_policy(SET CMP0216 OLD) +include(CMP0216-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0216-common.cmake b/Tests/RunCMake/Swift/CMP0216-common.cmake new file mode 100644 index 0000000..ae695d8 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0216-common.cmake
@@ -0,0 +1,3 @@ +enable_language(Swift) + +add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/CompileCommands.cmake b/Tests/RunCMake/Swift/CompileCommands.cmake index f0a88da..2e95093 100644 --- a/Tests/RunCMake/Swift/CompileCommands.cmake +++ b/Tests/RunCMake/Swift/CompileCommands.cmake
@@ -4,6 +4,7 @@ if(POLICY CMP0195) cmake_policy(SET CMP0195 NEW) endif() +cmake_policy(SET CMP0216 NEW) set(CMAKE_Swift_COMPILATION_MODE "singlefile")
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparately-build-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparately-build-check.cmake new file mode 100644 index 0000000..dcae06c --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparately-build-check.cmake
@@ -0,0 +1,18 @@ +string( + REGEX MATCHALL + "swiftc(\\.exe)?\"? [^\n]* -emit-module-path [^\n]*L\\.swiftmodule" + swift_module_commands "${actual_stdout}") + +set(emit_module_commands "${swift_module_commands}") +list(FILTER emit_module_commands EXCLUDE REGEX " -c ") +if(NOT emit_module_commands) + string(APPEND RunCMake_TEST_FAILED + "Expected an emit-module command with '-emit-module-path ... L.swiftmodule'.\n") +endif() + +set(compile_commands "${swift_module_commands}") +list(FILTER compile_commands INCLUDE REGEX " -c ") +if(compile_commands) + string(APPEND RunCMake_TEST_FAILED + "Compile command (-c) should not contain '-emit-module-path' when emitting module separately.\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparately.cmake b/Tests/RunCMake/Swift/EmitModuleSeparately.cmake new file mode 100644 index 0000000..31955ba --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparately.cmake
@@ -0,0 +1,13 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0195 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR} ") +endif() + +enable_language(Swift) + +add_library(L STATIC L.swift) +add_library(LClient STATIC LClient.swift) +target_link_libraries(LClient PRIVATE L)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle-build-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle-build-check.cmake new file mode 100644 index 0000000..b0777b9 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle-build-check.cmake
@@ -0,0 +1,32 @@ +# Older Swift compilers may not set CMAKE_Swift_MODULE_TRIPLE +# Check that we built something before verifying the output +if(EXISTS "${RunCMake_TEST_BINARY_DIR}/no-swift-module-triple") + return() +endif() + +string( + REGEX MATCHALL + "swiftc(\\.exe)?\"? [^\n]* -emit-module-path [^\n]*L\\.swiftmodule[^ \n]*" + swift_module_commands "${actual_stdout}") + +set(emit_module_commands "${swift_module_commands}") +list(FILTER emit_module_commands EXCLUDE REGEX " -c ") +if(NOT emit_module_commands) + string(APPEND RunCMake_TEST_FAILED + "Expected an emit-module command with '-emit-module-path ... L.swiftmodule'.\n") +endif() + +set(compile_commands "${swift_module_commands}") +list(FILTER compile_commands INCLUDE REGEX " -c ") +if(compile_commands) + string(APPEND RunCMake_TEST_FAILED + "Compile command (-c) should not contain '-emit-module-path' when emitting module separately.\n") +endif() + +# CMP0195=NEW: the emit-module path should be directory-style +# (L.swiftmodule/<triple>.swiftmodule), not flat. +list(FILTER emit_module_commands INCLUDE REGEX "L\\.swiftmodule(/|\\\\)[-_a-zA-Z0-9]+\\.swiftmodule") +if(NOT emit_module_commands) + string(APPEND RunCMake_TEST_FAILED + "Expected directory-style -emit-module-path (L.swiftmodule/<triple>.swiftmodule) from CMP0195=NEW.\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle.cmake new file mode 100644 index 0000000..f4662f1 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle.cmake
@@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 4.0) + +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0195 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# Older Swift compilers may not set CMAKE_Swift_MODULE_TRIPLE +# Don't build anything if we can't do the nested module structure +if(NOT CMAKE_Swift_MODULE_TRIPLE) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/no-swift-module-triple" "") + return() +endif() + +add_library(L STATIC L.swift) +add_library(LClient STATIC LClient.swift) +target_link_libraries(LClient PRIVATE L)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake new file mode 100644 index 0000000..90038ea --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake
@@ -0,0 +1,14 @@ +# The link edge for a Swift library must implicitly depend on the +# .swiftmodule so the emit-module edge runs even when no other target +# in the build depends on it. +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +if(NOT build_ninja MATCHES "build [^\n]*(libL\\.a|L\\.lib)[^\n]*:.*\\|[^\n]*L\\.swiftmodule") + string(APPEND RunCMake_TEST_FAILED + "Link edge for L does not depend on L.swiftmodule.\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake new file mode 100644 index 0000000..6eec25c --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake
@@ -0,0 +1,13 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0195 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# A standalone library with no dependent Swift targets. The link edge +# must still depend on the .swiftmodule so the emit-module edge runs. +add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake new file mode 100644 index 0000000..2aca41d --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake
@@ -0,0 +1,14 @@ +# Same as EmitModuleSeparatelyLinkDep but with a custom module directory. +# The link edge must depend on the actual emit-module output path, not a +# default that doesn't match the target's configuration. +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +if(NOT build_ninja MATCHES "build [^\n]*(libL\\.a|L\\.lib)[^\n]*:.*\\|[^\n]*custom(/|\\\\)[^\n]*L\\.swiftmodule") + string(APPEND RunCMake_TEST_FAILED + "Link edge for L does not depend on custom/.../L.swiftmodule.\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake new file mode 100644 index 0000000..5dc0533 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake
@@ -0,0 +1,16 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0195 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# A standalone library with a custom module output path (like swiftCore +# in the Swift stdlib). The link edge must depend on the actual +# emit-module output, not GetSwiftModulePath(). +add_library(L STATIC L.swift) +set_target_properties(L PROPERTIES + Swift_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/custom)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake new file mode 100644 index 0000000..b42cfd1 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake
@@ -0,0 +1,28 @@ +# The Swift object compile edge must order-only depend on the .swiftmodule +# produced by the separate emit-module edge. Both edges share the same +# -output-file-map; running them concurrently corrupts the module-level +# swift-dependencies file referenced by that map. +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +# Find the object compile edge for L and confirm L.swiftmodule appears +# after the order-only `||` separator. The edge ends at the next `build ` +# at the start of a line (or end of file). +string(REGEX MATCH + "build [^\n]*L\\.swift\\.o[^\n]*:[^\n]*(\n [^\n]+)*" + obj_edge "${build_ninja}") + +if(NOT obj_edge) + string(APPEND RunCMake_TEST_FAILED + "Could not find object compile edge for L.swift.\n") + return() +endif() + +if(NOT obj_edge MATCHES "\\|\\|[^\n]*L\\.swiftmodule") + string(APPEND RunCMake_TEST_FAILED + "Object compile edge for L.swift is missing an order-only dependency on L.swiftmodule.\nEdge:\n${obj_edge}\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake new file mode 100644 index 0000000..2c643e0 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake
@@ -0,0 +1,15 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0195 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# The compile and emit-module edges share the same -output-file-map. +# Within the target, the object compile edge must order-only depend on the +# .swiftmodule so the two edges do not run concurrently and corrupt the +# shared module-level swift-dependencies entry. +add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/Swift/EmitModuleSeparatelyRequiresPolicies-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/Swift/EmitModuleSeparatelyRequiresPolicies-result.txt
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyRequiresPolicies-stderr.txt b/Tests/RunCMake/Swift/EmitModuleSeparatelyRequiresPolicies-stderr.txt new file mode 100644 index 0000000..4619f1b --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyRequiresPolicies-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error at [^ +]*CMakeSwiftInformation\.cmake:[0-9]+ \(message\): + Policy CMP0215 is set to 'NEW', which requires policies CMP0157 and CMP0195 + to be set to 'NEW', but CMP0157 and CMP0195 are not\.
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyRequiresPolicies.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyRequiresPolicies.cmake new file mode 100644 index 0000000..416efd5 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyRequiresPolicies.cmake
@@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 4.0) + +# CMP0157 must be set before the Swift language is enabled. +cmake_policy(SET CMP0157 OLD) +cmake_policy(SET CMP0195 OLD) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +# CMP0215=NEW requires both CMP0157=NEW and CMP0195=NEW; neither is set here, +# so enabling the Swift language must fail and name both policies. +enable_language(Swift)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyResponseFile-build-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyResponseFile-build-check.cmake new file mode 100644 index 0000000..a6a4e3d --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyResponseFile-build-check.cmake
@@ -0,0 +1,17 @@ +# Older Swift compilers may not set CMAKE_Swift_MODULE_TRIPLE +# Check that we built something before verifying the output +if(EXISTS "${RunCMake_TEST_BINARY_DIR}/no-swift-module-triple") + return() +endif() + +if(NOT actual_stdout MATCHES + "swiftc(\\.exe)?\"? [^\n]* -c @CMakeFiles(/|\\\\)L\\.dir(/|\\\\)(Debug(/|\\\\))?L\\.o(bj)?\\.swift\\.rsp") + string(APPEND RunCMake_TEST_FAILED + "No Swift compile response-file command found for target L.\n") +endif() + +if(NOT actual_stdout MATCHES + "swiftc(\\.exe)?\"? [^\n]* -emit-module @.*L\\.swiftmodule(/|\\\\)[-_a-zA-Z0-9]+\\.swiftmodule\\.rsp") + string(APPEND RunCMake_TEST_FAILED + "No Swift emit-module response-file command found for target L.\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyResponseFile.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyResponseFile.cmake new file mode 100644 index 0000000..b9d900b --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyResponseFile.cmake
@@ -0,0 +1,22 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0195 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR} ") +endif() + +set(CMAKE_NINJA_FORCE_RESPONSE_FILE TRUE) + +enable_language(Swift) + +# Older Swift compilers may not set CMAKE_Swift_MODULE_TRIPLE +# Don't build anything if we can't do the nested module structure +if(NOT CMAKE_Swift_MODULE_TRIPLE) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/no-swift-module-triple" "") + return() +endif() + +add_library(L STATIC L.swift) +add_library(LClient STATIC LClient.swift) +target_link_libraries(LClient PRIVATE L)
diff --git a/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt b/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt index d687c36..6f3edf3 100644 --- a/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt +++ b/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt
@@ -1,2 +1,2 @@ -swiftc(\.exe)? -j [0-9]+ -num-threads [0-9]+ -c @CMakeFiles(/|\\)L\.dir(/|\\)(Debug(/|\\))?L\.o(bj)?\.swift\.rsp -.*swiftc(\.exe)? -emit-library -static -o (Debug(/|\\))?(libL\.a|L\.lib) @CMakeFiles(/|\\)L\.(Debug\.)?rsp +swiftc(\.exe)?"? -j [0-9]+ -num-threads [0-9]+ -c @CMakeFiles(/|\\)L\.dir(/|\\)(Debug(/|\\))?L\.o(bj)?\.swift\.rsp +.*swiftc(\.exe)?"? -emit-library -static -o (Debug(/|\\))?(libL\.a|L\.lib) @CMakeFiles(/|\\)L\.(Debug\.)?rsp
diff --git a/Tests/RunCMake/Swift/LClient.swift b/Tests/RunCMake/Swift/LClient.swift new file mode 100644 index 0000000..709f4e4 --- /dev/null +++ b/Tests/RunCMake/Swift/LClient.swift
@@ -0,0 +1,2 @@ +import L +public func consume() -> Int { return ThirtyTwo }
diff --git a/Tests/RunCMake/Swift/LinkerFlags-common.cmake b/Tests/RunCMake/Swift/LinkerFlags-common.cmake new file mode 100644 index 0000000..2fdb177 --- /dev/null +++ b/Tests/RunCMake/Swift/LinkerFlags-common.cmake
@@ -0,0 +1,8 @@ +enable_language(Swift) + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -foo") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -foo") + +add_library(L SHARED L.swift) +add_executable(E E.swift) +target_link_libraries(E PRIVATE L)
diff --git a/Tests/RunCMake/Swift/MultiLangChain.cmake b/Tests/RunCMake/Swift/MultiLangChain.cmake index f8dccc9..ec56cd9 100644 --- a/Tests/RunCMake/Swift/MultiLangChain.cmake +++ b/Tests/RunCMake/Swift/MultiLangChain.cmake
@@ -1,5 +1,6 @@ cmake_policy(SET CMP0157 NEW) cmake_policy(SET CMP0195 NEW) +cmake_policy(SET CMP0216 NEW) enable_language(Swift C)
diff --git a/Tests/RunCMake/Swift/PackageName-check.cmake b/Tests/RunCMake/Swift/PackageName-check.cmake new file mode 100644 index 0000000..cc9177b --- /dev/null +++ b/Tests/RunCMake/Swift/PackageName-check.cmake
@@ -0,0 +1,27 @@ +if(RunCMake_GENERATOR MATCHES "Ninja") + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") + else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") + endif() + file(READ "${path}" build_ninja) + + if(NOT build_ninja MATCHES "Swift_COMPILER__L1[^\n]*(\n [^\n]+)*\n FLAGS = [^\n]*-package-name SwiftPackageTest") + string(APPEND RunCMake_TEST_FAILED + "Expected L1 compile FLAGS to contain -package-name SwiftPackageTest\n") + endif() + + if(build_ninja MATCHES "Swift_COMPILER__L2[^\n]*(\n [^\n]+)*\n FLAGS = [^\n]*-package-name") + string(APPEND RunCMake_TEST_FAILED + "L2 compile FLAGS should not contain -package-name\n") + endif() + +elseif(RunCMake_GENERATOR MATCHES "Xcode") + file(READ + "${RunCMake_TEST_BINARY_DIR}/PackageName.xcodeproj/project.pbxproj" + pbxproj) + if(NOT pbxproj MATCHES "SWIFT_PACKAGE_NAME = SwiftPackageTest") + string(APPEND RunCMake_TEST_FAILED + "Expected SWIFT_PACKAGE_NAME = SwiftPackageTest in Xcode project for L1\n") + endif() +endif()
diff --git a/Tests/RunCMake/Swift/PackageName.cmake b/Tests/RunCMake/Swift/PackageName.cmake new file mode 100644 index 0000000..caf138b --- /dev/null +++ b/Tests/RunCMake/Swift/PackageName.cmake
@@ -0,0 +1,13 @@ +cmake_policy(SET CMP0157 NEW) + +enable_language(Swift) + +add_library(L1 L.swift) +set_target_properties(L1 PROPERTIES + Swift_PACKAGE_NAME "SwiftPackageTest" +) + +add_library(L2 L.swift) +set_target_properties(L2 PROPERTIES + Swift_PACKAGE_NAME "" +)
diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake index bab9230..55e0594 100644 --- a/Tests/RunCMake/Swift/RunCMakeTest.cmake +++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake
@@ -74,6 +74,11 @@ endif() endblock() +if(CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 6.2 + OR XCODE_VERSION VERSION_GREATER_EQUAL 26) + run_cmake(SwiftPtrSize) +endif() + if(RunCMake_GENERATOR MATCHES "Ninja") block() if (CMAKE_SYSTEM_NAME MATCHES "Windows") @@ -147,6 +152,46 @@ endblock() block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EmitModuleSeparately-build) + run_cmake(EmitModuleSeparately) + set(RunCMake_TEST_NO_CLEAN 1) + # -v: verbose to capture executed commands -n: dry-run to avoid actually compiling + run_cmake_command(EmitModuleSeparately-build ${CMAKE_COMMAND} --build . -- -vn) + endblock() + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EmitModuleSeparatelyResponseFile-build) + run_cmake(EmitModuleSeparatelyResponseFile) + set(RunCMake_TEST_NO_CLEAN 1) + # -v: verbose to capture executed commands -n: dry-run to avoid actually compiling + run_cmake_command(EmitModuleSeparatelyResponseFile-build ${CMAKE_COMMAND} --build . -- -vn) + endblock() + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EmitModuleSeparatelyDirectoryStyle-build) + run_cmake(EmitModuleSeparatelyDirectoryStyle) + set(RunCMake_TEST_NO_CLEAN 1) + # -v: verbose to capture executed commands -n: dry-run to avoid actually compiling + run_cmake_command(EmitModuleSeparatelyDirectoryStyle-build ${CMAKE_COMMAND} --build . -- -vn) + endblock() + + block() + run_cmake(EmitModuleSeparatelyLinkDep) + endblock() + + block() + run_cmake(EmitModuleSeparatelyLinkDepCustomPath) + endblock() + + block() + run_cmake(EmitModuleSeparatelyOrdering) + endblock() + + block() + run_cmake(EmitModuleSeparatelyRequiresPolicies) + endblock() + + block() if(CMAKE_SYSTEM_NAME MATCHES Windows) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ImportLibraryFlags-build) run_cmake(ImportLibraryFlags) @@ -156,9 +201,47 @@ endblock() block() + if(CMAKE_SYSTEM_NAME MATCHES Windows) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TargetPDBFile-build) + run_cmake(TargetPDBFile) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(TargetPDBFile-build ${CMAKE_COMMAND} --build . -- -n -v) + endif() + endblock() + + block() + if(CMAKE_SYSTEM_NAME MATCHES Windows) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TargetPDBFileDwarf-build) + run_cmake(TargetPDBFileDwarf) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(TargetPDBFileDwarf-build ${CMAKE_COMMAND} --build . -- -v) + run_cmake_command(TargetPDBFileDwarf-install ${CMAKE_COMMAND} --install . + --prefix "${RunCMake_TEST_BINARY_DIR}/install") + endif() + endblock() + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SwiftModuleNameHyphen-build) + run_cmake(SwiftModuleNameHyphen) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(SwiftModuleNameHyphen-build ${CMAKE_COMMAND} --build . -- -vn) + endblock() + + block() set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SwiftLibraryModuleCommand-build) run_cmake(SwiftLibraryModuleCommand) set(RunCMake_TEST_NO_CLEAN 1) run_cmake_command(SwiftLibraryModuleCommand-check ${CMAKE_COMMAND} --build . -- -n -v) endblock() + + run_cmake(CMP0214-NEW) + run_cmake(CMP0214-OLD) + run_cmake(CMP0214-NEW-CMP0157-OLD) +endif() + +if(NOT RunCMake_GENERATOR STREQUAL "Xcode" OR + (RunCMake_GENERATOR STREQUAL "Xcode" AND XCODE_VERSION VERSION_GREATER_EQUAL 15.0)) + run_cmake(PackageName) + run_cmake(CMP0216-OLD) + run_cmake(CMP0216-NEW) endif()
diff --git a/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout-darwin.txt b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout-darwin.txt index 7ba5345..2108a8f 100644 --- a/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout-darwin.txt +++ b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout-darwin.txt
@@ -1,20 +1,20 @@ -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library -static[^ -]* -emit-module -emit-module-path (Debug/|Release/)?StaticLibrary\.swiftmodule[^ ]* -module-name StaticLibrary [^ +]* -emit-module -emit-module-path (Debug/|Release/)?StaticLibrary\.swiftmodule [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library[^ -]* -emit-module -emit-module-path (debug|release)/modules/DynamicLibrary\.swiftmodule[^ ]* -module-name DynamicLibrary [^ +]* -emit-module -emit-module-path (debug|release)/modules/DynamicLibrary\.swiftmodule [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -emit-library [^ ]* -Xlinker -install_name -Xlinker @rpath/libDynamicLibrary\.dylib -o ([A-Za-z]+/)?libDynamicLibrary\.dylib [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library[^ -]* -emit-module -emit-module-path Modules/(Debug/|Release/)?DynamicLibrary2\.swiftmodule[^ -]* -module-name DynamicLibrary2[^ +]* -module-name DynamicLibrary2 [^ +]* -emit-module -emit-module-path Modules/(Debug/|Release/)?DynamicLibrary2\.swiftmodule [^ ]* -.*swiftc(\.exe)? -j [0-9]+ -num-threads [0-9]+ -c -module-name Executable +.*swiftc(\.exe)?"? -j [0-9]+ -num-threads [0-9]+ -c -module-name Executable
diff --git a/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout-windows.txt b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout-windows.txt index 2f9bbbc..1ad6315 100644 --- a/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout-windows.txt +++ b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout-windows.txt
@@ -1,20 +1,20 @@ -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library -static[^ -]* -emit-module -emit-module-path (Debug\\|Release\\)?StaticLibrary\.swiftmodule[^ ]* -module-name StaticLibrary [^ +]* -emit-module -emit-module-path (Debug\\|Release\\)?StaticLibrary\.swiftmodule [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library[^ -]* -emit-module -emit-module-path (debug|release)\\modules\\DynamicLibrary\.swiftmodule[^ ]* -module-name DynamicLibrary [^ +]* -emit-module -emit-module-path (debug|release)\\modules\\DynamicLibrary\.swiftmodule [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -emit-library [^ ]* -Xlinker -implib:(Debug\\|Release\\)?DynamicLibrary\.lib +-o ([A-Za-z]+/)?(Debug\\|Release\\)?DynamicLibrary\.dll [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library[^ -]* -emit-module -emit-module-path Modules\\(Debug\\|Release\\)?DynamicLibrary2\.swiftmodule[^ ]* -module-name DynamicLibrary2 [^ +]* -emit-module -emit-module-path Modules\\(Debug\\|Release\\)?DynamicLibrary2\.swiftmodule [^ ]* -.*swiftc(\.exe)? -j [0-9]+ -num-threads [0-9]+ -c -module-name Executable +.*swiftc(\.exe)?"? -j [0-9]+ -num-threads [0-9]+ -c -module-name Executable
diff --git a/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout.txt b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout.txt index e74fdf9..f9f261b 100644 --- a/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout.txt +++ b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout.txt
@@ -1,20 +1,20 @@ -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library -static[^ -]* -emit-module -emit-module-path (Debug/|Release/)?StaticLibrary\.swiftmodule[^ ]* -module-name StaticLibrary [^ +]* -emit-module -emit-module-path (Debug/|Release/)?StaticLibrary\.swiftmodule [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library[^ -]* -emit-module -emit-module-path (debug|release)/modules/DynamicLibrary\.swiftmodule[^ ]* -module-name DynamicLibrary [^ +]* -emit-module -emit-module-path (debug|release)/modules/DynamicLibrary\.swiftmodule [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -emit-library [^ ]* -Xlinker -soname -Xlinker libDynamicLibrary\.so -o ([A-Za-z]+/)?libDynamicLibrary\.so [^ ]* -.*swiftc(\.exe)? [^ +.*swiftc(\.exe)?"? [^ ]* -parse-as-library[^ -]* -emit-module -emit-module-path Modules/(Debug/|Release/)?DynamicLibrary2\.swiftmodule[^ -]* -module-name DynamicLibrary2[^ +]* -module-name DynamicLibrary2 [^ +]* -emit-module -emit-module-path Modules/(Debug/|Release/)?DynamicLibrary2\.swiftmodule [^ ]* -.*swiftc(\.exe)? -j [0-9]+ -num-threads [0-9]+ -c -module-name Executable +.*swiftc(\.exe)?"? -j [0-9]+ -num-threads [0-9]+ -c -module-name Executable
diff --git a/Tests/RunCMake/Swift/SwiftModuleNameHyphen-build-check.cmake b/Tests/RunCMake/Swift/SwiftModuleNameHyphen-build-check.cmake new file mode 100644 index 0000000..f0e0f6c --- /dev/null +++ b/Tests/RunCMake/Swift/SwiftModuleNameHyphen-build-check.cmake
@@ -0,0 +1,9 @@ +# The module name should have underscores, not hyphens. +if(NOT actual_stdout MATCHES "-module-name L_hyphen") + string(APPEND RunCMake_TEST_FAILED + "Expected '-module-name L_hyphen', hyphens should be replaced with underscores.\n") +endif() +if(actual_stdout MATCHES "-module-name L-hyphen") + string(APPEND RunCMake_TEST_FAILED + "Found '-module-name L-hyphen', hyphens are not valid in Swift module names.\n") +endif()
diff --git a/Tests/RunCMake/Swift/SwiftModuleNameHyphen.cmake b/Tests/RunCMake/Swift/SwiftModuleNameHyphen.cmake new file mode 100644 index 0000000..b1fa8ce --- /dev/null +++ b/Tests/RunCMake/Swift/SwiftModuleNameHyphen.cmake
@@ -0,0 +1,11 @@ +cmake_policy(SET CMP0157 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# A target with hyphens in the name. Swift module names cannot contain +# hyphens, so CMake should replace them with underscores. +add_library(L-hyphen STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/SwiftPtrSize-stdout.txt b/Tests/RunCMake/Swift/SwiftPtrSize-stdout.txt new file mode 100644 index 0000000..6f94fbf --- /dev/null +++ b/Tests/RunCMake/Swift/SwiftPtrSize-stdout.txt
@@ -0,0 +1,2 @@ +-- CMAKE_Swift_SIZEOF_DATA_PTR='[1-9][0-9]*' +-- CMAKE_SIZEOF_VOID_P='[1-9][0-9]*'
diff --git a/Tests/RunCMake/Swift/SwiftPtrSize.cmake b/Tests/RunCMake/Swift/SwiftPtrSize.cmake new file mode 100644 index 0000000..7b52d67 --- /dev/null +++ b/Tests/RunCMake/Swift/SwiftPtrSize.cmake
@@ -0,0 +1,4 @@ +enable_language(Swift) + +message(STATUS "CMAKE_Swift_SIZEOF_DATA_PTR='${CMAKE_Swift_SIZEOF_DATA_PTR}'") +message(STATUS "CMAKE_SIZEOF_VOID_P='${CMAKE_SIZEOF_VOID_P}'")
diff --git a/Tests/RunCMake/Swift/TargetPDBFile-build-stdout.txt b/Tests/RunCMake/Swift/TargetPDBFile-build-stdout.txt new file mode 100644 index 0000000..6eef06f --- /dev/null +++ b/Tests/RunCMake/Swift/TargetPDBFile-build-stdout.txt
@@ -0,0 +1,4 @@ +.*swiftc(\.exe)?"?[^ +]* -Xlinker -pdb:[^ +]*SwiftPDBCustom\.pdb[^ +]*
diff --git a/Tests/RunCMake/Swift/TargetPDBFile-check.cmake b/Tests/RunCMake/Swift/TargetPDBFile-check.cmake new file mode 100644 index 0000000..007c805 --- /dev/null +++ b/Tests/RunCMake/Swift/TargetPDBFile-check.cmake
@@ -0,0 +1,9 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script) +if(NOT install_script MATCHES "SwiftPDBCustom\\.pdb") + string(APPEND RunCMake_TEST_FAILED + "Generated install script does not reference SwiftPDBCustom.pdb\n") +endif() +if(NOT install_script MATCHES "OPTIONAL") + string(APPEND RunCMake_TEST_FAILED + "Generated install script does not preserve OPTIONAL install\n") +endif()
diff --git a/Tests/RunCMake/Swift/TargetPDBFile.cmake b/Tests/RunCMake/Swift/TargetPDBFile.cmake new file mode 100644 index 0000000..b5b7822 --- /dev/null +++ b/Tests/RunCMake/Swift/TargetPDBFile.cmake
@@ -0,0 +1,12 @@ +cmake_policy(SET CMP0157 NEW) + +enable_language(Swift) + +add_executable(SwiftPDB E.swift) +set_property(TARGET SwiftPDB PROPERTY PDB_NAME SwiftPDBCustom) +set_property(TARGET SwiftPDB PROPERTY PDB_OUTPUT_DIRECTORY + "$<1:${CMAKE_CURRENT_BINARY_DIR}/pdb>") + +install(FILES "$<TARGET_PDB_FILE:SwiftPDB>" + DESTINATION bin + OPTIONAL)
diff --git a/Tests/RunCMake/Swift/TargetPDBFileDwarf-build-check.cmake b/Tests/RunCMake/Swift/TargetPDBFileDwarf-build-check.cmake new file mode 100644 index 0000000..88ef741 --- /dev/null +++ b/Tests/RunCMake/Swift/TargetPDBFileDwarf-build-check.cmake
@@ -0,0 +1,9 @@ +file(GLOB_RECURSE pdb_files LIST_DIRECTORIES false + "${RunCMake_TEST_BINARY_DIR}/*.pdb") +foreach(pdb_file IN LISTS pdb_files) + get_filename_component(pdb_name "${pdb_file}" NAME) + if(pdb_name STREQUAL "SwiftPDBDwarf.pdb") + string(APPEND RunCMake_TEST_FAILED + "DWARF link unexpectedly produced ${pdb_file}\n") + endif() +endforeach()
diff --git a/Tests/RunCMake/Swift/TargetPDBFileDwarf-build-stdout.txt b/Tests/RunCMake/Swift/TargetPDBFileDwarf-build-stdout.txt new file mode 100644 index 0000000..e86126f --- /dev/null +++ b/Tests/RunCMake/Swift/TargetPDBFileDwarf-build-stdout.txt
@@ -0,0 +1,10 @@ +.*swiftc(\.exe)?"?[^ +]* -g[^ +]* -debug-info-format=dwarf[^ +]* +.*swiftc(\.exe)?"?[^ +]* -Xlinker -debug:dwarf[^ +]* -use-ld=lld[^ +]* -Xlinker -pdb:[^ +]*SwiftPDBDwarf\.pdb[^ +]*
diff --git a/Tests/RunCMake/Swift/TargetPDBFileDwarf-install-check.cmake b/Tests/RunCMake/Swift/TargetPDBFileDwarf-install-check.cmake new file mode 100644 index 0000000..eef58f8 --- /dev/null +++ b/Tests/RunCMake/Swift/TargetPDBFileDwarf-install-check.cmake
@@ -0,0 +1,4 @@ +if(EXISTS "${RunCMake_TEST_BINARY_DIR}/install/bin/SwiftPDBDwarf.pdb") + string(APPEND RunCMake_TEST_FAILED + "OPTIONAL install unexpectedly copied SwiftPDBDwarf.pdb\n") +endif()
diff --git a/Tests/RunCMake/Swift/TargetPDBFileDwarf.cmake b/Tests/RunCMake/Swift/TargetPDBFileDwarf.cmake new file mode 100644 index 0000000..64d301e --- /dev/null +++ b/Tests/RunCMake/Swift/TargetPDBFileDwarf.cmake
@@ -0,0 +1,12 @@ +cmake_policy(SET CMP0157 NEW) + +enable_language(Swift) + +add_executable(SwiftPDBDwarf E.swift) +set_property(TARGET SwiftPDBDwarf PROPERTY LINKER_TYPE LLD) +target_compile_options(SwiftPDBDwarf PRIVATE -g -debug-info-format=dwarf) +target_link_options(SwiftPDBDwarf PRIVATE LINKER:-debug:dwarf) + +install(FILES "$<TARGET_PDB_FILE:SwiftPDBDwarf>" + DESTINATION bin + OPTIONAL)
diff --git a/Tests/RunCMake/Syntax/Escape2-stderr.txt b/Tests/RunCMake/Syntax/Escape2-stderr.txt index a944694..9cc9c36 100644 --- a/Tests/RunCMake/Syntax/Escape2-stderr.txt +++ b/Tests/RunCMake/Syntax/Escape2-stderr.txt
@@ -1,7 +1,7 @@ -CMake Error at Escape2\.cmake:2 \(message\): +CMake Error at Escape2\.cmake:[0-9]+ \(message\): Syntax error in cmake code at - .*/Tests/RunCMake/Syntax/Escape2\.cmake:2 + .*/Tests/RunCMake/Syntax/Escape2\.cmake:[0-9]+ when parsing string @@ -9,5 +9,5 @@ Invalid character escape '\\' \(at end of input\)\. Call Stack \(most recent call first\): - Escape2\.cmake:5 \(escape\) + Escape2\.cmake:[0-9]+ \(escape\) CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/Syntax/Escape2.cmake b/Tests/RunCMake/Syntax/Escape2.cmake index 6e8b07b..2a40c6d 100644 --- a/Tests/RunCMake/Syntax/Escape2.cmake +++ b/Tests/RunCMake/Syntax/Escape2.cmake
@@ -2,4 +2,8 @@ message("${str}") endmacro () +if(POLICY CMP0219) + cmake_policy(SET CMP0219 OLD) +endif() + escape("\\")
diff --git a/Tests/RunCMake/Syntax/ParenInENV1-stderr.txt b/Tests/RunCMake/Syntax/ParenInENV1-stderr.txt index 8625a33..16f8762 100644 --- a/Tests/RunCMake/Syntax/ParenInENV1-stderr.txt +++ b/Tests/RunCMake/Syntax/ParenInENV1-stderr.txt
@@ -1,10 +1,10 @@ -CMake Warning \(dev\) at ParenInENV1\.cmake:2: +CMake Warning \(author\) at ParenInENV1\.cmake:2: Syntax Warning in cmake code at column 21 Argument not separated from preceding token by whitespace\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Error at ParenInENV1\.cmake:2 \(message\): Syntax error in cmake code at
diff --git a/Tests/RunCMake/Syntax/ParenNoSpace1-stderr.txt b/Tests/RunCMake/Syntax/ParenNoSpace1-stderr.txt index bf4fd06..a9d8d5d 100644 --- a/Tests/RunCMake/Syntax/ParenNoSpace1-stderr.txt +++ b/Tests/RunCMake/Syntax/ParenNoSpace1-stderr.txt
@@ -1,18 +1,18 @@ -CMake Warning \(dev\) at ParenNoSpace1\.cmake:1: +CMake Warning \(author\) at ParenNoSpace1\.cmake:1: Syntax Warning in cmake code at column 26 Argument not separated from preceding token by whitespace\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at ParenNoSpace1\.cmake:2: +CMake Warning \(author\) at ParenNoSpace1\.cmake:2: Syntax Warning in cmake code at column 26 Argument not separated from preceding token by whitespace\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Error at ParenNoSpace1\.cmake:3: Syntax Error in cmake code at column 29
diff --git a/Tests/RunCMake/Syntax/StringNoSpace-stderr.txt b/Tests/RunCMake/Syntax/StringNoSpace-stderr.txt index 49e3d62..92e542b 100644 --- a/Tests/RunCMake/Syntax/StringNoSpace-stderr.txt +++ b/Tests/RunCMake/Syntax/StringNoSpace-stderr.txt
@@ -1,18 +1,18 @@ -CMake Warning \(dev\) at StringNoSpace\.cmake:2: +CMake Warning \(author\) at StringNoSpace\.cmake:2: Syntax Warning in cmake code at column 28 Argument not separated from preceding token by whitespace\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at StringNoSpace\.cmake:2: +CMake Warning \(author\) at StringNoSpace\.cmake:2: Syntax Warning in cmake code at column 31 Argument not separated from preceding token by whitespace\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. \[1 \${var} \\n 4\] \[x\]
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index 79b1ff8..18d85ba 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -54,6 +54,10 @@ \* CMP0204 \* CMP0209 \* CMP0210 + \* CMP0211 + \* CMP0214 + \* CMP0215 + \* CMP0216 Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/TargetProperties/Deprecation-stderr.txt b/Tests/RunCMake/TargetProperties/Deprecation-stderr.txt index 910bc60..66979e1 100644 --- a/Tests/RunCMake/TargetProperties/Deprecation-stderr.txt +++ b/Tests/RunCMake/TargetProperties/Deprecation-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at Deprecation\.cmake:[0-9]+ \(target_link_libraries\): +^CMake Warning \(author\) at Deprecation\.cmake:[0-9]+ \(target_link_libraries\): The library that is being linked to, testLibDeprecation, is marked as being deprecated by the owner\. The message provided by the developer is: @@ -10,4 +10,4 @@ Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt b/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt index 1026962..91e6589 100644 --- a/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt +++ b/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0126-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0126-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0126 will be removed from a future version of CMake\. @@ -8,5 +8,7 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + try_compile CMP0126='OLD' VAR='2'
diff --git a/Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake b/Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake index b588ce0..5105e7a 100644 --- a/Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake +++ b/Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake
@@ -14,6 +14,7 @@ run_cmake_toolchain(CMP0126-NEW) run_cmake_toolchain(CMP0126-OLD) run_cmake_toolchain(CMP0126-WARN) +run_cmake_toolchain(SetCompilerLoaded) run_cmake_toolchain(SetCrossCompiling) function(run_IncludeDirectories)
diff --git a/Tests/RunCMake/ToolchainFile/SetCompilerLoaded-toolchain.cmake b/Tests/RunCMake/ToolchainFile/SetCompilerLoaded-toolchain.cmake new file mode 100644 index 0000000..b127236 --- /dev/null +++ b/Tests/RunCMake/ToolchainFile/SetCompilerLoaded-toolchain.cmake
@@ -0,0 +1 @@ +set(CMAKE_C_COMPILER_LOADED 1)
diff --git a/Tests/RunCMake/ToolchainFile/SetCompilerLoaded.cmake b/Tests/RunCMake/ToolchainFile/SetCompilerLoaded.cmake new file mode 100644 index 0000000..e731cbc --- /dev/null +++ b/Tests/RunCMake/ToolchainFile/SetCompilerLoaded.cmake
@@ -0,0 +1,5 @@ +enable_language(C) +try_compile(SHOULD_PASS SOURCE_FROM_CONTENT "main.c" "int main(void) { return 0; }") +if(NOT SHOULD_PASS) + message(FATAL_ERROR "try_compile failed but should have passed!") +endif()
diff --git a/Tests/RunCMake/ToolchainFile/SetCrossCompiling-stderr.txt b/Tests/RunCMake/ToolchainFile/SetCrossCompiling-stderr.txt index 1d1a17e..2629a8a 100644 --- a/Tests/RunCMake/ToolchainFile/SetCrossCompiling-stderr.txt +++ b/Tests/RunCMake/ToolchainFile/SetCrossCompiling-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(author\) at [^ ]*/Modules/CMakeDetermineSystem\.cmake:[0-9]+ \(message\): CMAKE_CROSSCOMPILING has been set by the project, toolchain file, or user\. CMake is resetting it to false because CMAKE_SYSTEM_NAME was not set\. To indicate cross compilation, only CMAKE_SYSTEM_NAME needs to be set\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(project\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/C-toolchain.cmake b/Tests/RunCMake/ToolchainSkipsChecks/C-toolchain.cmake new file mode 100644 index 0000000..1ed9733 --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/C-toolchain.cmake
@@ -0,0 +1,25 @@ +enable_language(C) + +set(vars + CMAKE_C_COMPILER_WORKS + CMAKE_C_COMPILER_ARCHITECTURE_ID + CMAKE_C_STANDARD_INCLUDE_DIRECTORIES + CMAKE_C_COMPILER_LINKER + CMAKE_C_COMPILER_LINKER_ID + CMAKE_C_COMPILER_LINKER_VERSION + CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT + CMAKE_C_IMPLICIT_LINK_LIBRARIES + CMAKE_C_IMPLICIT_LINK_DIRECTORIES + CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES + CMAKE_C_LIBRARY_ARCHITECTURE + CMAKE_C_COMPILE_FEATURES + CMAKE_C_USE_LINKER_INFORMATION + CMAKE_C_SIZEOF_DATA_PTR + CMAKE_C_BYTE_ORDER + CMAKE_C_COMPILER_ABI) +foreach (std IN ITEMS 23 17 11 99 90) + list(APPEND vars + CMAKE_C${std}_COMPILE_FEATURES) +endforeach () + +include("${CMAKE_CURRENT_LIST_DIR}/write-toolchain.cmake")
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/C.cmake b/Tests/RunCMake/ToolchainSkipsChecks/C.cmake new file mode 100644 index 0000000..cf47f17 --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/C.cmake
@@ -0,0 +1,15 @@ +enable_language(C) + +# See #27894 +if (NOT CMAKE_SIZEOF_VOID_P) + message(FATAL_ERROR + "`CMAKE_SIZEOF_VOID_P` not initialized from toolchain data") +endif () +if (CMAKE_C_COMPILER_ABI AND NOT CMAKE_INTERNAL_PLATFORM_ABI) + message(FATAL_ERROR + "`CMAKE_INTERNAL_PLATFORM_ABI` not initialized from toolchain data") +endif () +if (CMAKE_C_LIBRARY_ARCHITECTURE AND NOT CMAKE_LIBRARY_ARCHITECTURE) + message(FATAL_ERROR + "`CMAKE_LIBRARY_ARCHITECTURE` not initialized from toolchain data") +endif ()
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/CMakeLists.txt b/Tests/RunCMake/ToolchainSkipsChecks/CMakeLists.txt new file mode 100644 index 0000000..94e43ba --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.29) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/CXX-toolchain.cmake b/Tests/RunCMake/ToolchainSkipsChecks/CXX-toolchain.cmake new file mode 100644 index 0000000..7b1db55 --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/CXX-toolchain.cmake
@@ -0,0 +1,28 @@ +enable_language(CXX) + +set(vars + CMAKE_CXX_COMPILER_WORKS + CMAKE_CXX_COMPILER_ARCHITECTURE_ID + CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + CMAKE_CXX_COMPILER_LINKER + CMAKE_CXX_COMPILER_LINKER_ID + CMAKE_CXX_COMPILER_LINKER_VERSION + CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT + CMAKE_CXX_IMPLICIT_LINK_LIBRARIES + CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES + CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES + CMAKE_CXX_LIBRARY_ARCHITECTURE + CMAKE_CXX_COMPILE_FEATURES + CMAKE_CXX_USE_LINKER_INFORMATION + CMAKE_CXX_SIZEOF_DATA_PTR + CMAKE_CXX_BYTE_ORDER + CMAKE_CXX_COMPILER_ABI + + CMAKE_CXX_COMPILER_IMPORT_STD + CMAKE_CXX_STDLIB_MODULES_JSON) +foreach (std IN ITEMS 26 23 20 17 14 11 98) + list(APPEND vars + CMAKE_CXX${std}_COMPILE_FEATURES) +endforeach () + +include("${CMAKE_CURRENT_LIST_DIR}/write-toolchain.cmake")
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/CXX.cmake b/Tests/RunCMake/ToolchainSkipsChecks/CXX.cmake new file mode 100644 index 0000000..b566261 --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/CXX.cmake
@@ -0,0 +1,15 @@ +enable_language(CXX) + +# See #27894 +if (NOT CMAKE_SIZEOF_VOID_P) + message(FATAL_ERROR + "`CMAKE_SIZEOF_VOID_P` not initialized from toolchain data") +endif () +if (CMAKE_CXX_COMPILER_ABI AND NOT CMAKE_INTERNAL_PLATFORM_ABI) + message(FATAL_ERROR + "`CMAKE_INTERNAL_PLATFORM_ABI` not initialized from toolchain data") +endif () +if (CMAKE_CXX_LIBRARY_ARCHITECTURE AND NOT CMAKE_LIBRARY_ARCHITECTURE) + message(FATAL_ERROR + "`CMAKE_LIBRARY_ARCHITECTURE` not initialized from toolchain data") +endif ()
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/Fortran-toolchain.cmake b/Tests/RunCMake/ToolchainSkipsChecks/Fortran-toolchain.cmake new file mode 100644 index 0000000..ca8bd6e --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/Fortran-toolchain.cmake
@@ -0,0 +1,21 @@ +enable_language(Fortran) + +set(vars + CMAKE_Fortran_COMPILER_ARCHITECTURE_ID + CMAKE_Fortran_COMPILER_SUPPORTS_F90 + CMAKE_Fortran_COMPILER_WORKS + CMAKE_Fortran_STANDARD_INCLUDE_DIRECTORIES + CMAKE_Fortran_COMPILER_LINKER + CMAKE_Fortran_COMPILER_LINKER_ID + CMAKE_Fortran_COMPILER_LINKER_VERSION + CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT + CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES + CMAKE_Fortran_IMPLICIT_LINK_DIRECTORIES + CMAKE_Fortran_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES + CMAKE_Fortran_LIBRARY_ARCHITECTURE + CMAKE_Fortran_USE_LINKER_INFORMATION + CMAKE_Fortran_SIZEOF_DATA_PTR + CMAKE_Fortran_BYTE_ORDER + CMAKE_Fortran_COMPILER_ABI) + +include("${CMAKE_CURRENT_LIST_DIR}/write-toolchain.cmake")
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/Fortran.cmake b/Tests/RunCMake/ToolchainSkipsChecks/Fortran.cmake new file mode 100644 index 0000000..d2f8b80 --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/Fortran.cmake
@@ -0,0 +1,15 @@ +enable_language(Fortran) + +# See #27894 +if (NOT CMAKE_SIZEOF_VOID_P) + message(FATAL_ERROR + "`CMAKE_SIZEOF_VOID_P` not initialized from toolchain data") +endif () +if (CMAKE_Fortran_PLATFORM_ABI AND NOT CMAKE_INTERNAL_PLATFORM_ABI) + message(FATAL_ERROR + "`CMAKE_INTERNAL_PLATFORM_ABI` not initialized from toolchain data") +endif () +if (CMAKE_Fortran_LIBRARY_ARCHITECTURE AND NOT CMAKE_LIBRARY_ARCHITECTURE) + message(FATAL_ERROR + "`CMAKE_LIBRARY_ARCHITECTURE` not initialized from toolchain data") +endif ()
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/RunCMakeTest.cmake b/Tests/RunCMake/ToolchainSkipsChecks/RunCMakeTest.cmake new file mode 100644 index 0000000..570ba72 --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/RunCMakeTest.cmake
@@ -0,0 +1,16 @@ +include(RunCMake) + +function (run_cmake_lang lang) + if (NOT CMAKE_${lang}_COMPILER_ID) + return () + endif () + run_cmake(${lang}-toolchain) + set(RunCMake-check-file "check.cmake") + run_cmake_with_options(${lang} + # "-DCMAKE_${lang}_COMPILER_FORCED=1" + "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_BINARY_DIR}/${lang}-toolchain-build/Toolchain.cmake") +endfunction () + +run_cmake_lang(C) +run_cmake_lang(CXX) +run_cmake_lang(Fortran)
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/check.cmake b/Tests/RunCMake/ToolchainSkipsChecks/check.cmake new file mode 100644 index 0000000..df85718 --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/check.cmake
@@ -0,0 +1,27 @@ +set(configure_log "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/CMakeConfigureLog.yaml") +if (NOT EXISTS "${configure_log}") + set(RunCMake_TEST_FAILED "Configure log does not exist:\n ${configure_log}") + return () +endif () + +set(force_fail 0) +set(want_varname 0) +file(STRINGS "${configure_log}" lines REGEX "(kind:|variable:)") +foreach (line IN LISTS lines) + if (line MATCHES "kind:") + string(REGEX REPLACE ".*\"([^\"]+)\".*" "\\1" kind "${line}") + if (kind MATCHES "try_") + set(force_fail 1) + set(want_varname 1) + endif () + elseif (want_varname AND line MATCHES "variable:") + string(REGEX REPLACE ".*\"([^\"]+)\".*" "\\1" varname "${line}") + list(APPEND RunCMake_TEST_FAILED + "Found a `${kind}` event for `${varname}`") + set(want_varname 0) + endif () +endforeach () + +if (force_fail AND NOT RunCMake_TEST_FAILED) + set(RunCMake_TEST_FAILED "Found a failing event, but not its variable. Forcing a failure. See ${configure_log}") +endif ()
diff --git a/Tests/RunCMake/ToolchainSkipsChecks/write-toolchain.cmake b/Tests/RunCMake/ToolchainSkipsChecks/write-toolchain.cmake new file mode 100644 index 0000000..8ca1ee5 --- /dev/null +++ b/Tests/RunCMake/ToolchainSkipsChecks/write-toolchain.cmake
@@ -0,0 +1,6 @@ +set(toolchain_file "${CMAKE_BINARY_DIR}/Toolchain.cmake") +file(WRITE "${toolchain_file}" "") +foreach (var IN LISTS vars) + file(APPEND "${toolchain_file}" + "set(\"${var}\" [===[${${var}}]===])\n") +endforeach ()
diff --git a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake index 245a0fe..5ada09e 100644 --- a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake +++ b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake
@@ -42,6 +42,7 @@ run_cmake(unitybuild_batchsize) run_cmake(unitybuild_default_batchsize) run_cmake(unitybuild_skip) +run_cmake(unitybuild_fileset_skip) run_cmake(unitybuild_code_before_and_after_include) run_cmake(unitybuild_c_no_unity_build) run_cmake(unitybuild_c_no_unity_build_group)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_fileset_skip-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_fileset_skip-check.cmake new file mode 100644 index 0000000..18f7001 --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_fileset_skip-check.cmake
@@ -0,0 +1,30 @@ +set(unitybuild_c "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/fileset.dir/Unity/unity_0_c.c") +file(STRINGS ${unitybuild_c} unitybuild_c_strings) + +string(REGEX MATCH "\\/s[0-8].c" matched_files_0_8 ${unitybuild_c_strings}) +if(matched_files_0_8) + set(RunCMake_TEST_FAILED "Generated unity for fileset contains s0.c -> s8.c which should have been skipped") + return() +endif() + +string(REGEX MATCH "\\/s9.c" matched_files_9 ${unitybuild_c_strings}) +if(NOT matched_files_9) + set(RunCMake_TEST_FAILED "Generated unity for fileset should have contained s9.c!") + return() +endif() + + +set(unitybuild_c "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/fileset2.dir/Unity/unity_0_c.c") +file(STRINGS ${unitybuild_c} unitybuild_c_strings) + +string(REGEX MATCH "\\/s[0-8].c" matched_files_0_8 ${unitybuild_c_strings}) +if(matched_files_0_8) + set(RunCMake_TEST_FAILED "Generated unity for fileset2 contains s0.c -> s8.c which should have been skipped") + return() +endif() + +string(REGEX MATCH "\\/s9.c" matched_files_9 ${unitybuild_c_strings}) +if(NOT matched_files_9) + set(RunCMake_TEST_FAILED "Generated unity for fileset2 should have contained s9.c!") + return() +endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_fileset_skip.cmake b/Tests/RunCMake/UnityBuild/unitybuild_fileset_skip.cmake new file mode 100644 index 0000000..75aa0f8 --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_fileset_skip.cmake
@@ -0,0 +1,62 @@ +set(CMAKE_INTERMEDIATE_DIR_STRATEGY FULL CACHE STRING "" FORCE) + +project(unitybuild_skip C) + +set(srcs "") +foreach(s RANGE 0 9) + set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c") + file(WRITE "${src}" "int s${s}(void) { return 0; }\n") + list(APPEND srcs "${src}") +endforeach() + +add_library(fileset SHARED) +set_target_properties(fileset PROPERTIES UNITY_BUILD ON) + +target_sources(fileset PUBLIC FILE_SET s0 TYPE HEADERS BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s0.c) + +target_sources(fileset PUBLIC FILE_SET s1 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s1.c) +set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/s1.c PROPERTY HEADER_FILE_ONLY ON) + + +target_sources(fileset PUBLIC FILE_SET s2 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s2.c) +set_property(FILE_SET s2 TARGET fileset PROPERTY SKIP_UNITY_BUILD_INCLUSION ON) + +target_sources(fileset PUBLIC FILE_SET s3 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s3.c) +set_property(FILE_SET s3 TARGET fileset PROPERTY COMPILE_OPTIONS "opt") +set_property(FILE_SET s3 TARGET fileset PROPERTY INTERFACE_COMPILE_OPTIONS "opt") + +target_sources(fileset PUBLIC FILE_SET s4 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s4.c) +set_property(FILE_SET s4 TARGET fileset PROPERTY COMPILE_DEFINITIONS "def") +set_property(FILE_SET s4 TARGET fileset PROPERTY INTERFACE_COMPILE_DEFINITIONS "def") + +target_sources(fileset PUBLIC FILE_SET s5 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s5.c) +set_property(FILE_SET s5 TARGET fileset PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}") +set_property(FILE_SET s5 TARGET fileset PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}") + + +target_sources(fileset PUBLIC FILE_SET s6 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s6.c) +set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/s6.c PROPERTY COMPILE_OPTIONS "opt") + +target_sources(fileset PUBLIC FILE_SET s7 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s7.c) +set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/s7.c PROPERTY COMPILE_DEFINITIONS "def") + +target_sources(fileset PUBLIC FILE_SET s8 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s8.c) +set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/s8.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}") + + +target_sources(fileset PUBLIC FILE_SET s9 TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + FILES ${CMAKE_CURRENT_BINARY_DIR}/s9.c) + + +add_library(fileset2 SHARED) +set_target_properties(fileset2 PROPERTIES UNITY_BUILD ON) +target_link_libraries(fileset2 PRIVATE fileset)
diff --git a/Tests/RunCMake/UseSWIG/CMP0078-OLD-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0078-OLD-stderr.txt index 55d51e7..cf879f0 100644 --- a/Tests/RunCMake/UseSWIG/CMP0078-OLD-stderr.txt +++ b/Tests/RunCMake/UseSWIG/CMP0078-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0078-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0078-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0078 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/UseSWIG/CMP0078-WARN-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0078-WARN-stderr.txt index d6bbdc2..6305275 100644 --- a/Tests/RunCMake/UseSWIG/CMP0078-WARN-stderr.txt +++ b/Tests/RunCMake/UseSWIG/CMP0078-WARN-stderr.txt
@@ -1,10 +1,10 @@ -CMake Warning \(dev\) at .*/Modules/UseSWIG\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at .*/Modules/UseSWIG\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0078 is not set: UseSWIG generates standard target names\. Run "cmake --help-policy CMP0078" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMP0078-common\.cmake:8 \(swig_add_library\) CMP0078-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/UseSWIG/CMP0086-OLD-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0086-OLD-stderr.txt index ee43fef..1cb7ea2 100644 --- a/Tests/RunCMake/UseSWIG/CMP0086-OLD-stderr.txt +++ b/Tests/RunCMake/UseSWIG/CMP0086-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0086-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0086-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0086 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/UseSWIG/CMP0086-WARN-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0086-WARN-stderr.txt index 1c2c5e0..69739cd 100644 --- a/Tests/RunCMake/UseSWIG/CMP0086-WARN-stderr.txt +++ b/Tests/RunCMake/UseSWIG/CMP0086-WARN-stderr.txt
@@ -1,11 +1,11 @@ -CMake Warning \(dev\) at .*/Modules/UseSWIG\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at .*/Modules/UseSWIG\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0086 is not set: UseSWIG honors SWIG_MODULE_NAME via -module flag\. Run "cmake --help-policy CMP0086" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): .*/Modules/UseSWIG\.cmake:[0-9]+ \(SWIG_ADD_SOURCE_TO_MODULE\) CMP0086-common\.cmake:[0-9]+ \(swig_add_library\) CMP0086-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt index e09cc63..8560a7c 100644 --- a/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt +++ b/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0122-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0122-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0122 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/UseSWIG/CMP0122-WARN-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0122-WARN-stderr.txt index ecbe748..9ca9cd7 100644 --- a/Tests/RunCMake/UseSWIG/CMP0122-WARN-stderr.txt +++ b/Tests/RunCMake/UseSWIG/CMP0122-WARN-stderr.txt
@@ -1,10 +1,10 @@ -CMake Warning \(dev\) at .*/Modules/UseSWIG\.cmake:[0-9]+ \(message\): +CMake Warning \(policy\) at .*/Modules/UseSWIG\.cmake:[0-9]+ \(cmake_policy\): Policy CMP0122 is not set: UseSWIG use standard library name conventions for csharp language\. Run "cmake --help-policy CMP0122" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMP0122-common\.cmake:9 \(swig_add_library\) CMP0122-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/VS10Project/CustomCommandParallelDisable-stderr.txt b/Tests/RunCMake/VS10Project/CustomCommandParallelDisable-stderr.txt index 5c52c25..90ad79a 100644 --- a/Tests/RunCMake/VS10Project/CustomCommandParallelDisable-stderr.txt +++ b/Tests/RunCMake/VS10Project/CustomCommandParallelDisable-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CustomCommandParallelDisable\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CustomCommandParallelDisable\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0147 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/VS10Project/VsCSharpCustomTags-stderr.txt b/Tests/RunCMake/VS10Project/VsCSharpCustomTags-stderr.txt index 905b546..6f1a325 100644 --- a/Tests/RunCMake/VS10Project/VsCSharpCustomTags-stderr.txt +++ b/Tests/RunCMake/VS10Project/VsCSharpCustomTags-stderr.txt
@@ -1,3 +1,5 @@ -^CMake Deprecation Warning in CMakeLists\.txt: +^CMake Warning \(deprecated\) in CMakeLists\.txt: The C# target "foo" is of type STATIC_LIBRARY\. This is discouraged \(and - may be disabled in future\)\. Make it a SHARED library instead\.$ + may be disabled in future\)\. Make it a SHARED library instead\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/VS10Project/VsCSharpReferenceProps-stderr.txt b/Tests/RunCMake/VS10Project/VsCSharpReferenceProps-stderr.txt index 2409727..cb0f8e2 100644 --- a/Tests/RunCMake/VS10Project/VsCSharpReferenceProps-stderr.txt +++ b/Tests/RunCMake/VS10Project/VsCSharpReferenceProps-stderr.txt
@@ -1,8 +1,11 @@ -^CMake Deprecation Warning in CMakeLists\.txt: +^CMake Warning \(deprecated\) in CMakeLists\.txt: The C# target "foo2" is of type STATIC_LIBRARY\. This is discouraged \(and may be disabled in future\)\. Make it a SHARED library instead\. - - -CMake Deprecation Warning in CMakeLists\.txt: +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. ++ +CMake Warning \(deprecated\) in CMakeLists\.txt: The C# target "foo" is of type STATIC_LIBRARY\. This is discouraged \(and - may be disabled in future\)\. Make it a SHARED library instead\.$ + may be disabled in future\)\. Make it a SHARED library instead\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-stderr.txt b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-stderr.txt index 9d1b69e..b61084a 100644 --- a/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-stderr.txt +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0091-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0091-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0091 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/VSSolution/CMP0143-OLD-stderr.txt b/Tests/RunCMake/VSSolution/CMP0143-OLD-stderr.txt index ea6ac86..4c4f487 100644 --- a/Tests/RunCMake/VSSolution/CMP0143-OLD-stderr.txt +++ b/Tests/RunCMake/VSSolution/CMP0143-OLD-stderr.txt
@@ -1,8 +1,10 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): The OLD behavior for policy CMP0143 will be removed from a future version of CMake\. The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/VSSolution/DeployEnabled-check-sln.cmake b/Tests/RunCMake/VSSolution/DeployEnabled-check-sln.cmake index 4a013bb..4710337 100644 --- a/Tests/RunCMake/VSSolution/DeployEnabled-check-sln.cmake +++ b/Tests/RunCMake/VSSolution/DeployEnabled-check-sln.cmake
@@ -8,15 +8,15 @@ return() endif() +file(STRINGS "${vcSlnFile}" lines) - +# Foo set(FooProjGUID "") set(FoundFooProj FALSE) set(InFooProj FALSE) set(FoundReleaseDeploy FALSE) -set(DeployConfigs Debug MinSizeRel RelWithDebInfo ) +set(FooDeployConfigs Debug MinSizeRel RelWithDebInfo ) -file(STRINGS "${vcSlnFile}" lines) foreach(line IN LISTS lines) #message(STATUS "${line}") if( (NOT InFooProj ) AND (line MATCHES "^[ \\t]*Project\\(\"{[A-F0-9-]+}\"\\) = \"foo\", \"foo.vcxproj\", \"({[A-F0-9-]+})\"[ \\t]*$")) @@ -32,7 +32,7 @@ endif() if( line MATCHES "{[A-F0-9-]+}\\.([^\\|]+).*\\.Deploy\\.0" ) # Check that the other configurations ARE set to deploy. - list( REMOVE_ITEM DeployConfigs ${CMAKE_MATCH_1}) + list( REMOVE_ITEM FooDeployConfigs ${CMAKE_MATCH_1}) endif() endforeach() @@ -42,12 +42,45 @@ endif() if(NOT FoundFooProj) - set(RunCMake_TEST_FAILED "Failed to find foo project in the solution.") + set(RunCMake_TEST_FAILED "Failed to find 'foo' project in the solution.") return() endif() -list(LENGTH DeployConfigs length) +list(LENGTH FooDeployConfigs length) if( length GREATER 0 ) - set(RunCMake_TEST_FAILED "Failed to find Deploy lines for non-Release configurations. (${length})") + set(RunCMake_TEST_FAILED "Failed to find 'foo' Deploy lines for non-Release configurations. (${length})") + return() +endif() + +# Utility +set(UtilityProjGUID "") +set(InUtilityProj FALSE) +set(FoundUtilityProj FALSE) +set(UtilityDeployConfigs Debug MinSizeRel RelWithDebInfo Release) + +foreach(line IN LISTS lines) + if( (NOT InUtilityProj) AND (line MATCHES "^[ \\t]*Project\\(\"{[A-F0-9-]+}\"\\) = \"utility\", \"utility.vcxproj\", \"({[A-F0-9-]+})\"[ \\t]*$")) + # First, identify the GUID for the utility project, and record it. + set(FoundUtilityProj TRUE) + set(InUtilityProj TRUE) + set(UtilityProjGUID ${CMAKE_MATCH_1}) + elseif(InUtilityProj AND line MATCHES "EndProject") + set(InUtilityProj FALSE) + endif() + + if( line MATCHES "{[A-F0-9-]+}\\.([^\\|]+).*\\.Deploy\\.0" ) + # Check that the other configurations ARE set to deploy. + list( REMOVE_ITEM UtilityDeployConfigs ${CMAKE_MATCH_1}) + endif() +endforeach() + +if(NOT FoundUtilityProj) + set(RunCMake_TEST_FAILED "Failed to find 'utility' project in the solution.") + return() +endif() + +list(LENGTH UtilityDeployConfigs length) +if( length GREATER 0 ) + set(RunCMake_TEST_FAILED "Failed to find 'utility' Deploy lines for all configurations. (${length})") return() endif()
diff --git a/Tests/RunCMake/VSSolution/DeployEnabled-check-slnx.cmake b/Tests/RunCMake/VSSolution/DeployEnabled-check-slnx.cmake index 116cfd3..e0863f2 100644 --- a/Tests/RunCMake/VSSolution/DeployEnabled-check-slnx.cmake +++ b/Tests/RunCMake/VSSolution/DeployEnabled-check-slnx.cmake
@@ -23,4 +23,15 @@ <Deploy Solution="MinSizeRel\|\*"/> <Deploy Solution="RelWithDebInfo\|\*"/> </Project> + <Project Path="utility\.vcxproj" Type="8bc9ceb8-8b4a-11d0-8d11-00a0c91bc942" Id="[0-9a-f-]+"> + <BuildDependency Project="ZERO_CHECK\.vcxproj"/> + <Build Solution="Debug\|\*" Project="false"/> + <Deploy Solution="Debug\|\*"/> + <Build Solution="Release\|\*" Project="false"/> + <Deploy Solution="Release\|\*"/> + <Build Solution="MinSizeRel\|\*" Project="false"/> + <Deploy Solution="MinSizeRel\|\*"/> + <Build Solution="RelWithDebInfo\|\*" Project="false"/> + <Deploy Solution="RelWithDebInfo\|\*"/> + </Project> </Solution>$]])
diff --git a/Tests/RunCMake/VSSolution/DeployEnabled.cmake b/Tests/RunCMake/VSSolution/DeployEnabled.cmake index 02b42b2..4ba6d62 100644 --- a/Tests/RunCMake/VSSolution/DeployEnabled.cmake +++ b/Tests/RunCMake/VSSolution/DeployEnabled.cmake
@@ -10,3 +10,10 @@ PROPERTIES VS_SOLUTION_DEPLOY $<NOT:$<CONFIG:Release>> ) + +add_custom_target(utility) + +set_target_properties(utility + PROPERTIES + VS_SOLUTION_DEPLOY TRUE +)
diff --git a/Tests/RunCMake/VSSolution/StartupProjectMissing-stderr.txt b/Tests/RunCMake/VSSolution/StartupProjectMissing-stderr.txt index 535e672..79b3799 100644 --- a/Tests/RunCMake/VSSolution/StartupProjectMissing-stderr.txt +++ b/Tests/RunCMake/VSSolution/StartupProjectMissing-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) in CMakeLists\.txt: +^CMake Warning \(author\) in CMakeLists\.txt: Directory property VS_STARTUP_PROJECT specifies target 'DoesNotExist' that does not exist\. Ignoring\. -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake index 61e4df7..e9a5747 100644 --- a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake
@@ -46,6 +46,17 @@ run_cmake_build(VerifyInterfaceHeaderSets interface list) run_cmake_build(VerifyInterfaceHeaderSets interface skip_linting) +if(CMake_TEST_OBJC) + set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + run_cmake(VerifyInterfaceHeaderSetsObjC) + unset(RunCMake_TEST_OPTIONS) + run_cmake_build(VerifyInterfaceHeaderSetsObjC interface lang_test_objc) + run_cmake_build(VerifyInterfaceHeaderSetsObjC interface lang_test_objcxx) +endif() + set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON) run_cmake(AllVerifyInterfaceHeaderSets) unset(RunCMake_TEST_OPTIONS) @@ -94,6 +105,17 @@ run_cmake_build(VerifyPrivateHeaderSets private list) run_cmake_build(VerifyPrivateHeaderSets private skip_linting) +if(CMake_TEST_OBJC) + set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_PRIVATE_HEADER_SETS=ON) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + run_cmake(VerifyPrivateHeaderSetsObjC) + unset(RunCMake_TEST_OPTIONS) + run_cmake_build(VerifyPrivateHeaderSetsObjC private lang_test_objc) + run_cmake_build(VerifyPrivateHeaderSetsObjC private lang_test_objcxx) +endif() + set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_PRIVATE_HEADER_SETS=ON) run_cmake(AllVerifyPrivateHeaderSets) unset(RunCMake_TEST_OPTIONS) @@ -103,3 +125,21 @@ set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_PRIVATE_HEADER_SETS=ON) run_cmake(VerifyPrivateHeaderSetsNonexistent) unset(RunCMake_TEST_OPTIONS) + +# Test that verify targets are always created even with no headers +if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) +endif() +run_cmake(VerifyEmptyHeaderSets) +unset(RunCMake_TEST_OPTIONS) + +run_cmake_build(VerifyEmptyHeaderSets interface empty_iface) +run_cmake_build(VerifyEmptyHeaderSets private empty_priv) +run_cmake_build(VerifyEmptyHeaderSets interface all) +run_cmake_build(VerifyEmptyHeaderSets private all) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VerifyEmptyHeaderSets-build) +set(RunCMake_TEST_NO_CLEAN 1) +run_cmake_command(VerifyEmptyHeaderSets-all_verify_header_sets-Debug-build + ${CMAKE_COMMAND} --build . --config Debug --target all_verify_header_sets +)
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyEmptyHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyEmptyHeaderSets.cmake new file mode 100644 index 0000000..d1aeaca --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyEmptyHeaderSets.cmake
@@ -0,0 +1,9 @@ +enable_language(C) + +# Target with VERIFY_INTERFACE_HEADER_SETS ON but no interface file sets +add_library(empty_iface STATIC lib.c) +set_property(TARGET empty_iface PROPERTY VERIFY_INTERFACE_HEADER_SETS ON) + +# Target with VERIFY_PRIVATE_HEADER_SETS ON but no private file sets +add_library(empty_priv STATIC lib.c) +set_property(TARGET empty_priv PROPERTY VERIFY_PRIVATE_HEADER_SETS ON)
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-WARN-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-WARN-stderr.txt index a6d0bfc..006c7d2 100644 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-WARN-stderr.txt +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0209 is not set: Verify interface header sets checks executables without exports\. Run "cmake --help-policy CMP0209" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -7,4 +7,5 @@ enable exports\. Those headers would be verified under CMP0209 NEW behavior\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-result.txt deleted file mode 100644 index d197c91..0000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-result.txt +++ /dev/null
@@ -1 +0,0 @@ -[^0]
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9d..0000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-stderr.txt +++ /dev/null
@@ -1 +0,0 @@ -.*
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake index 2efd560..c96279a 100644 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake
@@ -83,3 +83,10 @@ target_sources(skip_linting INTERFACE FILE_SET HEADERS FILES lang_test.h skip_linting.h) set_property(SOURCE skip_linting.h PROPERTY LANGUAGE C) set_property(SOURCE skip_linting.h PROPERTY SKIP_LINTING TRUE) + +add_library(skip_linting2 STATIC lib.c) +target_sources(skip_linting2 INTERFACE FILE_SET HEADERS FILES lang_test.h) +target_sources(skip_linting2 INTERFACE FILE_SET skip_headers TYPE HEADERS FILES skip_linting2.h) +set_property(FILE_SET skip_headers TARGET skip_linting2 PROPERTY SKIP_LINTING TRUE) + +set_property(SOURCE skip_linting2.h PROPERTY LANGUAGE C)
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsObjC.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsObjC.cmake new file mode 100644 index 0000000..4830d24 --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsObjC.cmake
@@ -0,0 +1,12 @@ +cmake_policy(SET CMP0209 NEW) + +enable_language(C CXX OBJC OBJCXX) + +add_library(lang_test_objc STATIC lib.m) +target_sources(lang_test_objc INTERFACE FILE_SET HEADERS FILES lang_test.h) +target_compile_definitions(lang_test_objc INTERFACE EXPECT_OBJC) + +# OBJC + OBJCXX sources -> lattice promotes to OBJCXX for unlanguaged headers +add_library(lang_test_objcxx STATIC lib.m lib.mm) +target_sources(lang_test_objcxx INTERFACE FILE_SET HEADERS FILES lang_test.h) +target_compile_definitions(lang_test_objcxx INTERFACE EXPECT_CXX EXPECT_OBJC)
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-result.txt deleted file mode 100644 index d197c91..0000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-result.txt +++ /dev/null
@@ -1 +0,0 @@ -[^0]
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9d..0000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-stderr.txt +++ /dev/null
@@ -1 +0,0 @@ -.*
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-result.txt deleted file mode 100644 index d197c91..0000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-result.txt +++ /dev/null
@@ -1 +0,0 @@ -[^0]
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9d..0000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-stderr.txt +++ /dev/null
@@ -1 +0,0 @@ -.*
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-result.txt deleted file mode 100644 index d197c91..0000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-result.txt +++ /dev/null
@@ -1 +0,0 @@ -[^0]
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-stderr.txt deleted file mode 100644 index 8d98f9d..0000000 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-stderr.txt +++ /dev/null
@@ -1 +0,0 @@ -.*
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake index 1fc92e1..975a398 100644 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake
@@ -86,3 +86,10 @@ target_sources(skip_linting PRIVATE FILE_SET HEADERS FILES lang_test.h skip_linting.h) set_property(SOURCE skip_linting.h PROPERTY LANGUAGE C) set_property(SOURCE skip_linting.h PROPERTY SKIP_LINTING TRUE) + +add_library(skip_linting2 STATIC lib.c) +target_sources(skip_linting2 PRIVATE FILE_SET HEADERS FILES lang_test.h) +target_sources(skip_linting2 PRIVATE FILE_SET skip_headers TYPE HEADERS FILES skip_linting2.h) +set_property(FILE_SET skip_headers TARGET skip_linting2 PROPERTY SKIP_LINTING TRUE) + +set_property(SOURCE skip_linting2.h PROPERTY LANGUAGE C)
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsObjC.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsObjC.cmake new file mode 100644 index 0000000..53bfce2 --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsObjC.cmake
@@ -0,0 +1,12 @@ +cmake_policy(SET CMP0209 NEW) + +enable_language(C CXX OBJC OBJCXX) + +add_library(lang_test_objc STATIC lib.m) +target_sources(lang_test_objc PRIVATE FILE_SET HEADERS FILES lang_test.h) +target_compile_definitions(lang_test_objc PRIVATE EXPECT_OBJC) + +# OBJC + OBJCXX sources -> lattice promotes to OBJCXX for unlanguaged headers +add_library(lang_test_objcxx STATIC lib.m lib.mm) +target_sources(lang_test_objcxx PRIVATE FILE_SET HEADERS FILES lang_test.h) +target_compile_definitions(lang_test_objcxx PRIVATE EXPECT_CXX EXPECT_OBJC)
diff --git a/Tests/RunCMake/VerifyHeaderSets/lang_test.h b/Tests/RunCMake/VerifyHeaderSets/lang_test.h index 633a2a4..347a247 100644 --- a/Tests/RunCMake/VerifyHeaderSets/lang_test.h +++ b/Tests/RunCMake/VerifyHeaderSets/lang_test.h
@@ -5,4 +5,11 @@ # error "__cplusplus not defined but EXPECT_CXX defined" #endif +#if defined(__OBJC__) && !defined(EXPECT_OBJC) +# error "__OBJC__ defined but EXPECT_OBJC not defined" +#endif +#if !defined(__OBJC__) && defined(EXPECT_OBJC) +# error "__OBJC__ not defined but EXPECT_OBJC defined" +#endif + extern void lang_test_h(void);
diff --git a/Tests/RunCMake/VerifyHeaderSets/lib.m b/Tests/RunCMake/VerifyHeaderSets/lib.m new file mode 100644 index 0000000..5952ed2 --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/lib.m
@@ -0,0 +1,3 @@ +void lang_test_h(void) +{ +}
diff --git a/Tests/RunCMake/VerifyHeaderSets/lib.mm b/Tests/RunCMake/VerifyHeaderSets/lib.mm new file mode 100644 index 0000000..1119f87 --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/lib.mm
@@ -0,0 +1,3 @@ +void lang_test_h_mm(void) +{ +}
diff --git a/Tests/RunCMake/VerifyHeaderSets/skip_linting2.h b/Tests/RunCMake/VerifyHeaderSets/skip_linting2.h new file mode 100644 index 0000000..8a268bd --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/skip_linting2.h
@@ -0,0 +1,3 @@ +#error "This file should not be included" + +extern void skip_linting2_h(void);
diff --git a/Tests/RunCMake/WriteBasicConfigVersionFile/All.cmake b/Tests/RunCMake/WriteBasicConfigVersionFile/All.cmake index c32c92d..fa0c9c7 100644 --- a/Tests/RunCMake/WriteBasicConfigVersionFile/All.cmake +++ b/Tests/RunCMake/WriteBasicConfigVersionFile/All.cmake
@@ -5,8 +5,10 @@ set(COMPATIBILITIES AnyNewerVersion SameMajorVersion + SemanticVersion SameMinorVersion - ExactVersion) + SamePatchVersion + SameFullVersion) function(TEST_WRITE_BASIC_CONFIG_VERSION_FILE_PREPARE _version_installed) set(_same_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) @@ -70,11 +72,15 @@ _expected_compatible_AnyNewerVersion _expected_compatible_SameMajorVersion _expected_compatible_SameMinorVersion - _expected_compatible_ExactVersion) + _expected_compatible_SamePatchVersion + _expected_compatible_SameFullVersion + _expected_compatible_SemanticVersion) if("${_version_requested}" MATCHES [[^([0-9]+(\.[0-9]+)*)\.\.\.(<)?([0-9]+(\.[0-9]+)*)$]]) set (_compatibilities ${COMPATIBILITIES}) - # ExactVersion must not be tested - list(POP_BACK _compatibilities) + # SamePatchVersion does not support ranges. + # SameFullVersion warns and uses only the lower endpoint, so it is also + # excluded from the range matrix. + list(REMOVE_ITEM _compatibilities SamePatchVersion SameFullVersion) set(PACKAGE_FIND_VERSION_RANGE TRUE) set(PACKAGE_FIND_VERSION_RANGE_MIN INCLUDE) if ("${CMAKE_MATCH_3}" STREQUAL "<") @@ -163,811 +169,863 @@ endfunction() +test_write_basic_config_version_file_prepare(0) test_write_basic_config_version_file_prepare(4) +test_write_basic_config_version_file_prepare(0.5) test_write_basic_config_version_file_prepare(4.5) +test_write_basic_config_version_file_prepare(0.5.6) test_write_basic_config_version_file_prepare(4.5.6) +test_write_basic_config_version_file_prepare(0.5.6.7) test_write_basic_config_version_file_prepare(4.5.6.7) -# AnyNewerVersion -# | SameMajorVersion -# | | SameMinorVersion -# | | | ExactVersion -# | | | | -test_write_basic_config_version_file(4 0 1 0 0 0) # Request 0 -test_write_basic_config_version_file(4 2 1 0 0 0) # Request [older major] -test_write_basic_config_version_file(4 4 1 1 1 1) # Request [same major] -test_write_basic_config_version_file(4 9 0 0 0 0) # Request [newer major] - -test_write_basic_config_version_file(4 0.0 1 0 0 0) # Request 0.0 -test_write_basic_config_version_file(4 0.9 1 0 0 0) # Request 0.[newer minor] -test_write_basic_config_version_file(4 2.0 1 0 0 0) # Request [older major].0 -test_write_basic_config_version_file(4 2.9 1 0 0 0) # Request [older major].[newer minor] -test_write_basic_config_version_file(4 4.0 1 1 0 0) # Request [same major].0 -test_write_basic_config_version_file(4 4.9 0 0 0 0) # Request [same major].[newer minor] -test_write_basic_config_version_file(4 9.0 0 0 0 0) # Request [newer major].0 -test_write_basic_config_version_file(4 9.9 0 0 0 0) # Request [newer major].[newer minor] - -test_write_basic_config_version_file(4 0.0.0 1 0 0 0) # Request 0.0.0 -test_write_basic_config_version_file(4 0.0.9 1 0 0 0) # Request 0.0.[newer patch] -test_write_basic_config_version_file(4 0.9.0 1 0 0 0) # Request 0.[newer minor].0 -test_write_basic_config_version_file(4 0.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch] -test_write_basic_config_version_file(4 2.0.0 1 0 0 0) # Request [older major].0.0 -test_write_basic_config_version_file(4 2.0.9 1 0 0 0) # Request [older major].0.[newer patch] -test_write_basic_config_version_file(4 2.9.0 1 0 0 0) # Request [older major].[newer minor].0 -test_write_basic_config_version_file(4 2.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch] -test_write_basic_config_version_file(4 4.0.0 1 1 0 0) # Request [same major].0.0 -test_write_basic_config_version_file(4 4.0.9 0 0 0 0) # Request [same major].0.[newer patch] -test_write_basic_config_version_file(4 4.9.0 0 0 0 0) # Request [same major].[newer minor].0 -test_write_basic_config_version_file(4 4.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch] -test_write_basic_config_version_file(4 9.0.0 0 0 0 0) # Request [newer major].0.0 -test_write_basic_config_version_file(4 9.0.9 0 0 0 0) # Request [newer major].0.[newer patch] -test_write_basic_config_version_file(4 9.9.0 0 0 0 0) # Request [newer major].[newer minor].0 -test_write_basic_config_version_file(4 9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch] - -test_write_basic_config_version_file(4 0.0.0.0 1 0 0 0) # Request 0.0.0.0 -test_write_basic_config_version_file(4 0.0.0.9 1 0 0 0) # Request 0.0.0.[newer tweak] -test_write_basic_config_version_file(4 0.0.9.0 1 0 0 0) # Request 0.0.[newer patch].0 -test_write_basic_config_version_file(4 0.0.9.9 1 0 0 0) # Request 0.0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4 0.9.0.0 1 0 0 0) # Request 0.[newer minor].0.0 -test_write_basic_config_version_file(4 0.9.0.9 1 0 0 0) # Request 0.[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4 0.9.9.0 1 0 0 0) # Request 0.[newer minor].[newer patch].0 -test_write_basic_config_version_file(4 0.9.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4 2.0.0.0 1 0 0 0) # Request [older major].0.0.0 -test_write_basic_config_version_file(4 2.0.0.9 1 0 0 0) # Request [older major].0.0.[newer tweak] -test_write_basic_config_version_file(4 2.0.9.0 1 0 0 0) # Request [older major].0.[newer patch].0 -test_write_basic_config_version_file(4 2.0.9.9 1 0 0 0) # Request [older major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4 2.9.0.0 1 0 0 0) # Request [older major].[newer minor].0.0 -test_write_basic_config_version_file(4 2.9.0.9 1 0 0 0) # Request [older major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4 2.9.9.0 1 0 0 0) # Request [older major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4 2.9.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4 4.0.0.0 1 1 0 0) # Request [same major].0.0.0 -test_write_basic_config_version_file(4 4.0.0.9 0 0 0 0) # Request [same major].0.0.[newer tweak] -test_write_basic_config_version_file(4 4.0.9.0 0 0 0 0) # Request [same major].0.[newer patch].0 -test_write_basic_config_version_file(4 4.0.9.9 0 0 0 0) # Request [same major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4 4.9.0.0 0 0 0 0) # Request [same major].[newer minor].0.0 -test_write_basic_config_version_file(4 4.9.0.9 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4 4.9.9.0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4 4.9.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4 9.0.0.0 0 0 0 0) # Request [newer major].0.0.0 -test_write_basic_config_version_file(4 9.0.0.9 0 0 0 0) # Request [newer major].0.0.[newer tweak] -test_write_basic_config_version_file(4 9.0.9.0 0 0 0 0) # Request [newer major].0.[newer patch].0 -test_write_basic_config_version_file(4 9.0.9.9 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4 9.9.0.0 0 0 0 0) # Request [newer major].[newer minor].0.0 -test_write_basic_config_version_file(4 9.9.0.9 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4 9.9.9.0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4 9.9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] +# AnyNewerVersion +# | SameMajorVersion +# | | SameMinorVersion +# | | | SamePatchVersion +# | | | | SameFullVersion +# | | | | | SemanticVersion +# | | | | | | +test_write_basic_config_version_file(0 0 1 1 1 1 1 1) +test_write_basic_config_version_file(0 0.1 0 0 0 0 0 0) +test_write_basic_config_version_file(0 1 0 0 0 0 0 0) +test_write_basic_config_version_file(4 0 1 0 0 0 0 0) # Request 0 +test_write_basic_config_version_file(4 2 1 0 0 0 0 0) # Request [older major] +test_write_basic_config_version_file(4 4 1 1 1 1 1 1) # Request [same major] +test_write_basic_config_version_file(4 9 0 0 0 0 0 0) # Request [newer major] -test_write_basic_config_version_file(4.5 0 1 0 0 0) # Request 0 -test_write_basic_config_version_file(4.5 2 1 0 0 0) # Request [older major] -test_write_basic_config_version_file(4.5 4 1 1 0 0) # Request [same major] -test_write_basic_config_version_file(4.5 9 0 0 0 0) # Request [newer major] +test_write_basic_config_version_file(4 0.0 1 0 0 0 0 0) # Request 0.0 +test_write_basic_config_version_file(4 0.9 1 0 0 0 0 0) # Request 0.[newer minor] +test_write_basic_config_version_file(4 2.0 1 0 0 0 0 0) # Request [older major].0 +test_write_basic_config_version_file(4 2.9 1 0 0 0 0 0) # Request [older major].[newer minor] +test_write_basic_config_version_file(4 4.0 1 1 0 0 0 1) # Request [same major].0 +test_write_basic_config_version_file(4 4.9 0 0 0 0 0 0) # Request [same major].[newer minor] +test_write_basic_config_version_file(4 9.0 0 0 0 0 0 0) # Request [newer major].0 +test_write_basic_config_version_file(4 9.9 0 0 0 0 0 0) # Request [newer major].[newer minor] -test_write_basic_config_version_file(4.5 0.0 1 0 0 0) # Request 0.0 -test_write_basic_config_version_file(4.5 0.2 1 0 0 0) # Request 0.[older minor] -test_write_basic_config_version_file(4.5 0.5 1 0 0 0) # Request 0.[same minor] -test_write_basic_config_version_file(4.5 0.9 1 0 0 0) # Request 0.[newer minor] -test_write_basic_config_version_file(4.5 2.0 1 0 0 0) # Request [older major].0 -test_write_basic_config_version_file(4.5 2.2 1 0 0 0) # Request [older major].[older minor] -test_write_basic_config_version_file(4.5 2.5 1 0 0 0) # Request [older major].[same minor] -test_write_basic_config_version_file(4.5 2.9 1 0 0 0) # Request [older major].[newer minor] -test_write_basic_config_version_file(4.5 4.0 1 1 0 0) # Request [same major].0 -test_write_basic_config_version_file(4.5 4.2 1 1 0 0) # Request [same major].[older minor] -test_write_basic_config_version_file(4.5 4.5 1 1 1 1) # Request [same major].[same minor] -test_write_basic_config_version_file(4.5 4.9 0 0 0 0) # Request [same major].[newer minor] -test_write_basic_config_version_file(4.5 9.0 0 0 0 0) # Request [newer major].0 -test_write_basic_config_version_file(4.5 9.1 0 0 0 0) # Request [newer major].[older minor] -test_write_basic_config_version_file(4.5 9.5 0 0 0 0) # Request [newer major].[same minor] -test_write_basic_config_version_file(4.5 9.9 0 0 0 0) # Request [newer major].[newer minor] +test_write_basic_config_version_file(4 0.0.0 1 0 0 0 0 0) # Request 0.0.0 +test_write_basic_config_version_file(4 0.0.9 1 0 0 0 0 0) # Request 0.0.[newer patch] +test_write_basic_config_version_file(4 0.9.0 1 0 0 0 0 0) # Request 0.[newer minor].0 +test_write_basic_config_version_file(4 0.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch] +test_write_basic_config_version_file(4 2.0.0 1 0 0 0 0 0) # Request [older major].0.0 +test_write_basic_config_version_file(4 2.0.9 1 0 0 0 0 0) # Request [older major].0.[newer patch] +test_write_basic_config_version_file(4 2.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].0 +test_write_basic_config_version_file(4 2.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch] +test_write_basic_config_version_file(4 4.0.0 1 1 0 0 0 1) # Request [same major].0.0 +test_write_basic_config_version_file(4 4.0.9 0 0 0 0 0 0) # Request [same major].0.[newer patch] +test_write_basic_config_version_file(4 4.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].0 +test_write_basic_config_version_file(4 4.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch] +test_write_basic_config_version_file(4 9.0.0 0 0 0 0 0 0) # Request [newer major].0.0 +test_write_basic_config_version_file(4 9.0.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch] +test_write_basic_config_version_file(4 9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0 +test_write_basic_config_version_file(4 9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5 0.0.0 1 0 0 0) # Request 0.0.0 -test_write_basic_config_version_file(4.5 0.0.9 1 0 0 0) # Request 0.0.[newer patch] -test_write_basic_config_version_file(4.5 0.2.0 1 0 0 0) # Request 0.[older minor].0 -test_write_basic_config_version_file(4.5 0.2.9 1 0 0 0) # Request 0.[older minor].[newer patch] -test_write_basic_config_version_file(4.5 0.5.0 1 0 0 0) # Request 0.[same minor].0 -test_write_basic_config_version_file(4.5 0.5.9 1 0 0 0) # Request 0.[same minor].[newer patch] -test_write_basic_config_version_file(4.5 0.9.0 1 0 0 0) # Request 0.[newer minor].0 -test_write_basic_config_version_file(4.5 0.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch] -test_write_basic_config_version_file(4.5 2.0.0 1 0 0 0) # Request [older major].0.0 -test_write_basic_config_version_file(4.5 2.0.9 1 0 0 0) # Request [older major].0.[newer patch] -test_write_basic_config_version_file(4.5 2.2.0 1 0 0 0) # Request [older major].[older minor].0 -test_write_basic_config_version_file(4.5 2.2.9 1 0 0 0) # Request [older major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5 2.5.0 1 0 0 0) # Request [older major].[same minor].0 -test_write_basic_config_version_file(4.5 2.5.9 1 0 0 0) # Request [older major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5 2.9.0 1 0 0 0) # Request [older major].[newer minor].0 -test_write_basic_config_version_file(4.5 2.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5 4.0.0 1 1 0 0) # Request [same major].0.0 -test_write_basic_config_version_file(4.5 4.0.9 1 1 0 0) # Request [same major].0.[newer patch] -test_write_basic_config_version_file(4.5 4.2.0 1 1 0 0) # Request [same major].[older minor].0 -test_write_basic_config_version_file(4.5 4.2.9 1 1 0 0) # Request [same major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5 4.5.0 1 1 1 0) # Request [same major].[same minor].0 -test_write_basic_config_version_file(4.5 4.5.9 0 0 0 0) # Request [same major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5 4.9.0 0 0 0 0) # Request [same major].[newer minor].0 -test_write_basic_config_version_file(4.5 4.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5 9.0.0 0 0 0 0) # Request [newer major].0.0 -test_write_basic_config_version_file(4.5 9.0.9 0 0 0 0) # Request [newer major].0.[newer patch] -test_write_basic_config_version_file(4.5 9.2.0 0 0 0 0) # Request [newer major].[older minor].0 -test_write_basic_config_version_file(4.5 9.2.9 0 0 0 0) # Request [newer major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5 9.5.0 0 0 0 0) # Request [newer major].[same minor].0 -test_write_basic_config_version_file(4.5 9.5.9 0 0 0 0) # Request [newer major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5 9.9.0 0 0 0 0) # Request [newer major].[newer minor].0 -test_write_basic_config_version_file(4.5 9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch] - -test_write_basic_config_version_file(4.5 0.0.0.0 1 0 0 0) # Request 0.0.0.0 -test_write_basic_config_version_file(4.5 0.0.0.9 1 0 0 0) # Request 0.0.0.[newer tweak] -test_write_basic_config_version_file(4.5 0.0.9.0 1 0 0 0) # Request 0.0.[newer patch].0 -test_write_basic_config_version_file(4.5 0.0.9.9 1 0 0 0) # Request 0.0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 0.2.0.0 1 0 0 0) # Request 0.[older minor].0.0 -test_write_basic_config_version_file(4.5 0.2.0.9 1 0 0 0) # Request 0.[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 0.2.9.0 1 0 0 0) # Request 0.[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5 0.2.9.9 1 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 0.5.0.0 1 0 0 0) # Request 0.[same minor].0.0 -test_write_basic_config_version_file(4.5 0.5.0.9 1 0 0 0) # Request 0.[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 0.5.9.0 1 0 0 0) # Request 0.[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5 0.5.9.9 1 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 0.9.0.0 1 0 0 0) # Request 0.[newer minor].0.0 -test_write_basic_config_version_file(4.5 0.9.0.9 1 0 0 0) # Request 0.[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 0.9.9.0 1 0 0 0) # Request 0.[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5 0.9.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 2.0.0.0 1 0 0 0) # Request [older major].0.0.0 -test_write_basic_config_version_file(4.5 2.0.0.9 1 0 0 0) # Request [older major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5 2.0.9.0 1 0 0 0) # Request [older major].0.[newer patch].0 -test_write_basic_config_version_file(4.5 2.0.9.9 1 0 0 0) # Request [older major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 2.2.0.0 1 0 0 0) # Request [older major].[older minor].0.0 -test_write_basic_config_version_file(4.5 2.2.0.9 1 0 0 0) # Request [older major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 2.2.9.0 1 0 0 0) # Request [older major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5 2.2.9.9 1 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 2.5.0.0 1 0 0 0) # Request [older major].[same minor].0.0 -test_write_basic_config_version_file(4.5 2.5.0.9 1 0 0 0) # Request [older major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 2.5.9.0 1 0 0 0) # Request [older major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5 2.5.9.9 1 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 2.9.0.0 1 0 0 0) # Request [older major].[newer minor].0.0 -test_write_basic_config_version_file(4.5 2.9.0.9 1 0 0 0) # Request [older major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 2.9.9.0 1 0 0 0) # Request [older major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5 2.9.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 4.0.0.0 1 1 0 0) # Request [same major].0.0.0 -test_write_basic_config_version_file(4.5 4.0.0.9 1 1 0 0) # Request [same major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5 4.0.9.0 1 1 0 0) # Request [same major].0.[newer patch].0 -test_write_basic_config_version_file(4.5 4.0.9.9 1 1 0 0) # Request [same major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 4.2.0.0 1 1 0 0) # Request [same major].[older minor].0.0 -test_write_basic_config_version_file(4.5 4.2.0.9 1 1 0 0) # Request [same major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 4.2.9.0 1 1 0 0) # Request [same major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5 4.2.9.9 1 1 0 0) # Request [same major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 4.5.0.0 1 1 1 0) # Request [same major].[same minor].0.0 -test_write_basic_config_version_file(4.5 4.5.0.9 0 0 0 0) # Request [same major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 4.5.9.0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5 4.5.9.9 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 4.9.0.0 0 0 0 0) # Request [same major].[newer minor].0.0 -test_write_basic_config_version_file(4.5 4.9.0.9 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 4.9.9.0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5 4.9.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 9.0.0.0 0 0 0 0) # Request [newer major].0.0.0 -test_write_basic_config_version_file(4.5 9.0.0.9 0 0 0 0) # Request [newer major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5 9.0.9.0 0 0 0 0) # Request [newer major].0.[newer patch].0 -test_write_basic_config_version_file(4.5 9.0.9.9 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 9.2.0.0 0 0 0 0) # Request [newer major].[older minor].0.0 -test_write_basic_config_version_file(4.5 9.2.0.9 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 9.2.9.0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5 9.2.9.9 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 9.5.0.0 0 0 0 0) # Request [newer major].[same minor].0.0 -test_write_basic_config_version_file(4.5 9.5.0.9 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 9.5.9.0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5 9.5.9.9 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 9.9.0.0 0 0 0 0) # Request [newer major].[newer minor].0.0 -test_write_basic_config_version_file(4.5 9.9.0.9 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5 9.9.9.0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5 9.9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4 0.0.0.0 1 0 0 0 0 0) # Request 0.0.0.0 +test_write_basic_config_version_file(4 0.0.0.9 1 0 0 0 0 0) # Request 0.0.0.[newer tweak] +test_write_basic_config_version_file(4 0.0.9.0 1 0 0 0 0 0) # Request 0.0.[newer patch].0 +test_write_basic_config_version_file(4 0.0.9.9 1 0 0 0 0 0) # Request 0.0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4 0.9.0.0 1 0 0 0 0 0) # Request 0.[newer minor].0.0 +test_write_basic_config_version_file(4 0.9.0.9 1 0 0 0 0 0) # Request 0.[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4 0.9.9.0 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].0 +test_write_basic_config_version_file(4 0.9.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4 2.0.0.0 1 0 0 0 0 0) # Request [older major].0.0.0 +test_write_basic_config_version_file(4 2.0.0.9 1 0 0 0 0 0) # Request [older major].0.0.[newer tweak] +test_write_basic_config_version_file(4 2.0.9.0 1 0 0 0 0 0) # Request [older major].0.[newer patch].0 +test_write_basic_config_version_file(4 2.0.9.9 1 0 0 0 0 0) # Request [older major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4 2.9.0.0 1 0 0 0 0 0) # Request [older major].[newer minor].0.0 +test_write_basic_config_version_file(4 2.9.0.9 1 0 0 0 0 0) # Request [older major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4 2.9.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4 2.9.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4 4.0.0.0 1 1 0 0 0 1) # Request [same major].0.0.0 +test_write_basic_config_version_file(4 4.0.0.9 0 0 0 0 0 0) # Request [same major].0.0.[newer tweak] +test_write_basic_config_version_file(4 4.0.9.0 0 0 0 0 0 0) # Request [same major].0.[newer patch].0 +test_write_basic_config_version_file(4 4.0.9.9 0 0 0 0 0 0) # Request [same major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4 4.9.0.0 0 0 0 0 0 0) # Request [same major].[newer minor].0.0 +test_write_basic_config_version_file(4 4.9.0.9 0 0 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4 4.9.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4 4.9.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4 9.0.0.0 0 0 0 0 0 0) # Request [newer major].0.0.0 +test_write_basic_config_version_file(4 9.0.0.9 0 0 0 0 0 0) # Request [newer major].0.0.[newer tweak] +test_write_basic_config_version_file(4 9.0.9.0 0 0 0 0 0 0) # Request [newer major].0.[newer patch].0 +test_write_basic_config_version_file(4 9.0.9.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4 9.9.0.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0.0 +test_write_basic_config_version_file(4 9.9.0.9 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4 9.9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4 9.9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0 1 0 0 0) # Request 0 -test_write_basic_config_version_file(4.5.6 2 1 0 0 0) # Request [older major] -test_write_basic_config_version_file(4.5.6 4 1 1 0 0) # Request [same major] -test_write_basic_config_version_file(4.5.6 9 0 0 0 0) # Request [newer major] - -test_write_basic_config_version_file(4.5.6 0.0 1 0 0 0) # Request 0.0 -test_write_basic_config_version_file(4.5.6 0.2 1 0 0 0) # Request 0.[older minor] -test_write_basic_config_version_file(4.5.6 0.5 1 0 0 0) # Request 0.[same minor] -test_write_basic_config_version_file(4.5.6 0.9 1 0 0 0) # Request 0.[newer minor] -test_write_basic_config_version_file(4.5.6 2.0 1 0 0 0) # Request [older major].0 -test_write_basic_config_version_file(4.5.6 2.2 1 0 0 0) # Request [older major].[older minor] -test_write_basic_config_version_file(4.5.6 2.5 1 0 0 0) # Request [older major].[same minor] -test_write_basic_config_version_file(4.5.6 2.9 1 0 0 0) # Request [older major].[newer minor] -test_write_basic_config_version_file(4.5.6 4.0 1 1 0 0) # Request [same major].0 -test_write_basic_config_version_file(4.5.6 4.2 1 1 0 0) # Request [same major].[older minor] -test_write_basic_config_version_file(4.5.6 4.5 1 1 1 0) # Request [same major].[same minor] -test_write_basic_config_version_file(4.5.6 4.9 0 0 0 0) # Request [same major].[newer minor] -test_write_basic_config_version_file(4.5.6 9.0 0 0 0 0) # Request [newer major].0 -test_write_basic_config_version_file(4.5.6 9.1 0 0 0 0) # Request [newer major].[older minor] -test_write_basic_config_version_file(4.5.6 9.5 0 0 0 0) # Request [newer major].[same minor] -test_write_basic_config_version_file(4.5.6 9.9 0 0 0 0) # Request [newer major].[newer minor] - -test_write_basic_config_version_file(4.5.6 0.0.0 1 0 0 0) # Request 0.0.0 -test_write_basic_config_version_file(4.5.6 0.0.2 1 0 0 0) # Request 0.0.[older patch] -test_write_basic_config_version_file(4.5.6 0.0.6 1 0 0 0) # Request 0.0.[same patch] -test_write_basic_config_version_file(4.5.6 0.0.9 1 0 0 0) # Request 0.0.[newer patch] -test_write_basic_config_version_file(4.5.6 0.2.0 1 0 0 0) # Request 0.[older minor].0 -test_write_basic_config_version_file(4.5.6 0.2.2 1 0 0 0) # Request 0.[older minor].[older patch] -test_write_basic_config_version_file(4.5.6 0.2.6 1 0 0 0) # Request 0.[older minor].[same patch] -test_write_basic_config_version_file(4.5.6 0.2.9 1 0 0 0) # Request 0.[older minor].[newer patch] -test_write_basic_config_version_file(4.5.6 0.5.0 1 0 0 0) # Request 0.[same minor].0 -test_write_basic_config_version_file(4.5.6 0.5.2 1 0 0 0) # Request 0.[same minor].[older patch] -test_write_basic_config_version_file(4.5.6 0.5.6 1 0 0 0) # Request 0.[same minor].[same patch] -test_write_basic_config_version_file(4.5.6 0.5.9 1 0 0 0) # Request 0.[same minor].[newer patch] -test_write_basic_config_version_file(4.5.6 0.9.0 1 0 0 0) # Request 0.[newer minor].0 -test_write_basic_config_version_file(4.5.6 0.9.2 1 0 0 0) # Request 0.[newer minor].[older patch] -test_write_basic_config_version_file(4.5.6 0.9.6 1 0 0 0) # Request 0.[newer minor].[same patch] -test_write_basic_config_version_file(4.5.6 0.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.6 2.0.0 1 0 0 0) # Request [older major].0.0 -test_write_basic_config_version_file(4.5.6 2.0.2 1 0 0 0) # Request [older major].0.[older patch] -test_write_basic_config_version_file(4.5.6 2.0.6 1 0 0 0) # Request [older major].0.[same patch] -test_write_basic_config_version_file(4.5.6 2.0.9 1 0 0 0) # Request [older major].0.[newer patch] -test_write_basic_config_version_file(4.5.6 2.2.0 1 0 0 0) # Request [older major].[older minor].0 -test_write_basic_config_version_file(4.5.6 2.2.2 1 0 0 0) # Request [older major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.6 2.2.6 1 0 0 0) # Request [older major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.6 2.2.9 1 0 0 0) # Request [older major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.6 2.5.0 1 0 0 0) # Request [older major].[same minor].0 -test_write_basic_config_version_file(4.5.6 2.5.2 1 0 0 0) # Request [older major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.6 2.5.6 1 0 0 0) # Request [older major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.6 2.5.9 1 0 0 0) # Request [older major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.6 2.9.0 1 0 0 0) # Request [older major].[newer minor].0 -test_write_basic_config_version_file(4.5.6 2.9.2 1 0 0 0) # Request [older major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.6 2.9.6 1 0 0 0) # Request [older major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.6 2.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.6 4.0.0 1 1 0 0) # Request [same major].0.0 -test_write_basic_config_version_file(4.5.6 4.0.2 1 1 0 0) # Request [same major].0.[older patch] -test_write_basic_config_version_file(4.5.6 4.0.6 1 1 0 0) # Request [same major].0.[same patch] -test_write_basic_config_version_file(4.5.6 4.0.9 1 1 0 0) # Request [same major].0.[newer patch] -test_write_basic_config_version_file(4.5.6 4.2.0 1 1 0 0) # Request [same major].[older minor].0 -test_write_basic_config_version_file(4.5.6 4.2.2 1 1 0 0) # Request [same major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.6 4.2.6 1 1 0 0) # Request [same major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.6 4.2.9 1 1 0 0) # Request [same major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.6 4.5.0 1 1 1 0) # Request [same major].[same minor].0 -test_write_basic_config_version_file(4.5.6 4.5.2 1 1 1 0) # Request [same major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.6 4.5.6 1 1 1 1) # Request [same major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.6 4.5.9 0 0 0 0) # Request [same major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.6 4.9.0 0 0 0 0) # Request [same major].[newer minor].0 -test_write_basic_config_version_file(4.5.6 4.9.2 0 0 0 0) # Request [same major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.6 4.9.6 0 0 0 0) # Request [same major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.6 4.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.6 9.0.0 0 0 0 0) # Request [newer major].0.0 -test_write_basic_config_version_file(4.5.6 9.0.2 0 0 0 0) # Request [newer major].0.[older patch] -test_write_basic_config_version_file(4.5.6 9.0.6 0 0 0 0) # Request [newer major].0.[same patch] -test_write_basic_config_version_file(4.5.6 9.0.9 0 0 0 0) # Request [newer major].0.[newer patch] -test_write_basic_config_version_file(4.5.6 9.2.0 0 0 0 0) # Request [newer major].[older minor].0 -test_write_basic_config_version_file(4.5.6 9.2.2 0 0 0 0) # Request [newer major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.6 9.2.6 0 0 0 0) # Request [newer major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.6 9.2.9 0 0 0 0) # Request [newer major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.6 9.5.0 0 0 0 0) # Request [newer major].[same minor].0 -test_write_basic_config_version_file(4.5.6 9.5.2 0 0 0 0) # Request [newer major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.6 9.5.6 0 0 0 0) # Request [newer major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.6 9.5.9 0 0 0 0) # Request [newer major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.6 9.9.0 0 0 0 0) # Request [newer major].[newer minor].0 -test_write_basic_config_version_file(4.5.6 9.9.2 0 0 0 0) # Request [newer major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.6 9.9.6 0 0 0 0) # Request [newer major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.6 9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch] - -test_write_basic_config_version_file(4.5.6 0.0.0.0 1 0 0 0) # Request 0.0.0.0 -test_write_basic_config_version_file(4.5.6 0.0.0.9 1 0 0 0) # Request 0.0.0.[newer tweak] -test_write_basic_config_version_file(4.5.6 0.0.2.0 1 0 0 0) # Request 0.0.[older patch].0 -test_write_basic_config_version_file(4.5.6 0.0.2.9 1 0 0 0) # Request 0.0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.0.6.0 1 0 0 0) # Request 0.0.[same patch].0 -test_write_basic_config_version_file(4.5.6 0.0.6.9 1 0 0 0) # Request 0.0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.0.9.0 1 0 0 0) # Request 0.0.[newer patch].0 -test_write_basic_config_version_file(4.5.6 0.0.9.9 1 0 0 0) # Request 0.0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.2.0.0 1 0 0 0) # Request 0.[older minor].0.0 -test_write_basic_config_version_file(4.5.6 0.2.0.9 1 0 0 0) # Request 0.[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 0.2.2.0 1 0 0 0) # Request 0.[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 0.2.2.9 1 0 0 0) # Request 0.[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.2.6.0 1 0 0 0) # Request 0.[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 0.2.6.9 1 0 0 0) # Request 0.[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.2.9.0 1 0 0 0) # Request 0.[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 0.2.9.9 1 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.5.0.0 1 0 0 0) # Request 0.[same minor].0.0 -test_write_basic_config_version_file(4.5.6 0.5.0.9 1 0 0 0) # Request 0.[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 0.5.2.0 1 0 0 0) # Request 0.[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 0.5.2.9 1 0 0 0) # Request 0.[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.5.6.0 1 0 0 0) # Request 0.[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 0.5.6.9 1 0 0 0) # Request 0.[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.5.9.0 1 0 0 0) # Request 0.[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 0.5.9.9 1 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.9.0.0 1 0 0 0) # Request 0.[newer minor].0.0 -test_write_basic_config_version_file(4.5.6 0.9.0.9 1 0 0 0) # Request 0.[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 0.9.2.0 1 0 0 0) # Request 0.[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 0.9.2.9 1 0 0 0) # Request 0.[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.9.6.0 1 0 0 0) # Request 0.[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 0.9.6.9 1 0 0 0) # Request 0.[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 0.9.9.0 1 0 0 0) # Request 0.[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 0.9.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.0.0.0 1 0 0 0) # Request [older major].0.0.0 -test_write_basic_config_version_file(4.5.6 2.0.0.9 1 0 0 0) # Request [older major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.6 2.0.2.0 1 0 0 0) # Request [older major].0.[older patch].0 -test_write_basic_config_version_file(4.5.6 2.0.2.9 1 0 0 0) # Request [older major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.0.6.0 1 0 0 0) # Request [older major].0.[same patch].0 -test_write_basic_config_version_file(4.5.6 2.0.6.9 1 0 0 0) # Request [older major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.0.9.0 1 0 0 0) # Request [older major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.6 2.0.9.9 1 0 0 0) # Request [older major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.2.0.0 1 0 0 0) # Request [older major].[older minor].0.0 -test_write_basic_config_version_file(4.5.6 2.2.0.9 1 0 0 0) # Request [older major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 2.2.2.0 1 0 0 0) # Request [older major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 2.2.2.9 1 0 0 0) # Request [older major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.2.6.0 1 0 0 0) # Request [older major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 2.2.6.9 1 0 0 0) # Request [older major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.2.9.0 1 0 0 0) # Request [older major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 2.2.9.9 1 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.5.0.0 1 0 0 0) # Request [older major].[same minor].0.0 -test_write_basic_config_version_file(4.5.6 2.5.0.9 1 0 0 0) # Request [older major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 2.5.2.0 1 0 0 0) # Request [older major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 2.5.2.9 1 0 0 0) # Request [older major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.5.6.0 1 0 0 0) # Request [older major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 2.5.6.9 1 0 0 0) # Request [older major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.5.9.0 1 0 0 0) # Request [older major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 2.5.9.9 1 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.9.0.0 1 0 0 0) # Request [older major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.6 2.9.0.9 1 0 0 0) # Request [older major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 2.9.2.0 1 0 0 0) # Request [older major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 2.9.2.9 1 0 0 0) # Request [older major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.9.6.0 1 0 0 0) # Request [older major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 2.9.6.9 1 0 0 0) # Request [older major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 2.9.9.0 1 0 0 0) # Request [older major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 2.9.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.0.0.0 1 1 0 0) # Request [same major].0.0.0 -test_write_basic_config_version_file(4.5.6 4.0.0.9 1 1 0 0) # Request [same major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.6 4.0.2.0 1 1 0 0) # Request [same major].0.[older patch].0 -test_write_basic_config_version_file(4.5.6 4.0.2.9 1 1 0 0) # Request [same major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.0.6.0 1 1 0 0) # Request [same major].0.[same patch].0 -test_write_basic_config_version_file(4.5.6 4.0.6.9 1 1 0 0) # Request [same major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.0.9.0 1 1 0 0) # Request [same major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.6 4.0.9.9 1 1 0 0) # Request [same major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.2.0.0 1 1 0 0) # Request [same major].[older minor].0.0 -test_write_basic_config_version_file(4.5.6 4.2.0.9 1 1 0 0) # Request [same major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 4.2.2.0 1 1 0 0) # Request [same major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 4.2.2.9 1 1 0 0) # Request [same major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.2.6.0 1 1 0 0) # Request [same major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 4.2.6.9 1 1 0 0) # Request [same major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.2.9.0 1 1 0 0) # Request [same major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 4.2.9.9 1 1 0 0) # Request [same major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.5.0.0 1 1 1 0) # Request [same major].[same minor].0.0 -test_write_basic_config_version_file(4.5.6 4.5.0.9 1 1 1 0) # Request [same major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 4.5.2.0 1 1 1 0) # Request [same major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 4.5.2.9 1 1 1 0) # Request [same major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.5.6.0 1 1 1 1) # Request [same major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 4.5.6.9 0 0 0 1) # Request [same major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.5.9.0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 4.5.9.9 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.9.0.0 0 0 0 0) # Request [same major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.6 4.9.0.9 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 4.9.2.0 0 0 0 0) # Request [same major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 4.9.2.9 0 0 0 0) # Request [same major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.9.6.0 0 0 0 0) # Request [same major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 4.9.6.9 0 0 0 0) # Request [same major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 4.9.9.0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 4.9.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.0.0.0 0 0 0 0) # Request [newer major].0.0.0 -test_write_basic_config_version_file(4.5.6 9.0.0.9 0 0 0 0) # Request [newer major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.6 9.0.2.0 0 0 0 0) # Request [newer major].0.[older patch].0 -test_write_basic_config_version_file(4.5.6 9.0.2.9 0 0 0 0) # Request [newer major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.0.6.0 0 0 0 0) # Request [newer major].0.[same patch].0 -test_write_basic_config_version_file(4.5.6 9.0.6.9 0 0 0 0) # Request [newer major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.0.9.0 0 0 0 0) # Request [newer major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.6 9.0.9.9 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.2.0.0 0 0 0 0) # Request [newer major].[older minor].0.0 -test_write_basic_config_version_file(4.5.6 9.2.0.9 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 9.2.2.0 0 0 0 0) # Request [newer major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 9.2.2.9 0 0 0 0) # Request [newer major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.2.6.0 0 0 0 0) # Request [newer major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 9.2.6.9 0 0 0 0) # Request [newer major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.2.9.0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 9.2.9.9 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.5.0.0 0 0 0 0) # Request [newer major].[same minor].0.0 -test_write_basic_config_version_file(4.5.6 9.5.0.9 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 9.5.2.0 0 0 0 0) # Request [newer major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 9.5.2.9 0 0 0 0) # Request [newer major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.5.6.0 0 0 0 0) # Request [newer major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 9.5.6.9 0 0 0 0) # Request [newer major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.5.9.0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 9.5.9.9 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.9.0.0 0 0 0 0) # Request [newer major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.6 9.9.0.9 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6 9.9.2.0 0 0 0 0) # Request [newer major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.6 9.9.2.9 0 0 0 0) # Request [newer major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.9.6.0 0 0 0 0) # Request [newer major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.6 9.9.6.9 0 0 0 0) # Request [newer major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6 9.9.9.0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6 9.9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(0.5 0 1 1 0 0 0 0) +test_write_basic_config_version_file(0.5 0.2 1 1 0 0 0 0) +test_write_basic_config_version_file(0.5 0.5 1 1 1 1 1 1) +test_write_basic_config_version_file(0.5 0.9 0 0 0 0 0 0) +test_write_basic_config_version_file(0.5 1.0 0 0 0 0 0 0) -test_write_basic_config_version_file(4.5.6.7 0 1 0 0 0) # Request 0 -test_write_basic_config_version_file(4.5.6.7 2 1 0 0 0) # Request [older major] -test_write_basic_config_version_file(4.5.6.7 4 1 1 0 0) # Request [same major] -test_write_basic_config_version_file(4.5.6.7 9 0 0 0 0) # Request [newer major] +test_write_basic_config_version_file(4.5 0 1 0 0 0 0 0) # Request 0 +test_write_basic_config_version_file(4.5 2 1 0 0 0 0 0) # Request [older major] +test_write_basic_config_version_file(4.5 4 1 1 0 0 0 1) # Request [same major] +test_write_basic_config_version_file(4.5 9 0 0 0 0 0 0) # Request [newer major] -test_write_basic_config_version_file(4.5.6.7 0.0 1 0 0 0) # Request 0.0 -test_write_basic_config_version_file(4.5.6.7 0.2 1 0 0 0) # Request 0.[older minor] -test_write_basic_config_version_file(4.5.6.7 0.5 1 0 0 0) # Request 0.[same minor] -test_write_basic_config_version_file(4.5.6.7 0.9 1 0 0 0) # Request 0.[newer minor] -test_write_basic_config_version_file(4.5.6.7 2.0 1 0 0 0) # Request [older major].0 -test_write_basic_config_version_file(4.5.6.7 2.2 1 0 0 0) # Request [older major].[older minor] -test_write_basic_config_version_file(4.5.6.7 2.5 1 0 0 0) # Request [older major].[same minor] -test_write_basic_config_version_file(4.5.6.7 2.9 1 0 0 0) # Request [older major].[newer minor] -test_write_basic_config_version_file(4.5.6.7 4.0 1 1 0 0) # Request [same major].0 -test_write_basic_config_version_file(4.5.6.7 4.2 1 1 0 0) # Request [same major].[older minor] -test_write_basic_config_version_file(4.5.6.7 4.5 1 1 1 0) # Request [same major].[same minor] -test_write_basic_config_version_file(4.5.6.7 4.9 0 0 0 0) # Request [same major].[newer minor] -test_write_basic_config_version_file(4.5.6.7 9.0 0 0 0 0) # Request [newer major].0 -test_write_basic_config_version_file(4.5.6.7 9.1 0 0 0 0) # Request [newer major].[older minor] -test_write_basic_config_version_file(4.5.6.7 9.5 0 0 0 0) # Request [newer major].[same minor] -test_write_basic_config_version_file(4.5.6.7 9.9 0 0 0 0) # Request [newer major].[newer minor] +test_write_basic_config_version_file(4.5 0.0 1 0 0 0 0 0) # Request 0.0 +test_write_basic_config_version_file(4.5 0.2 1 0 0 0 0 0) # Request 0.[older minor] +test_write_basic_config_version_file(4.5 0.5 1 0 0 0 0 0) # Request 0.[same minor] +test_write_basic_config_version_file(4.5 0.9 1 0 0 0 0 0) # Request 0.[newer minor] +test_write_basic_config_version_file(4.5 2.0 1 0 0 0 0 0) # Request [older major].0 +test_write_basic_config_version_file(4.5 2.2 1 0 0 0 0 0) # Request [older major].[older minor] +test_write_basic_config_version_file(4.5 2.5 1 0 0 0 0 0) # Request [older major].[same minor] +test_write_basic_config_version_file(4.5 2.9 1 0 0 0 0 0) # Request [older major].[newer minor] +test_write_basic_config_version_file(4.5 4.0 1 1 0 0 0 1) # Request [same major].0 +test_write_basic_config_version_file(4.5 4.2 1 1 0 0 0 1) # Request [same major].[older minor] +test_write_basic_config_version_file(4.5 4.5 1 1 1 1 1 1) # Request [same major].[same minor] +test_write_basic_config_version_file(4.5 4.9 0 0 0 0 0 0) # Request [same major].[newer minor] +test_write_basic_config_version_file(4.5 9.0 0 0 0 0 0 0) # Request [newer major].0 +test_write_basic_config_version_file(4.5 9.1 0 0 0 0 0 0) # Request [newer major].[older minor] +test_write_basic_config_version_file(4.5 9.5 0 0 0 0 0 0) # Request [newer major].[same minor] +test_write_basic_config_version_file(4.5 9.9 0 0 0 0 0 0) # Request [newer major].[newer minor] -test_write_basic_config_version_file(4.5.6.7 0.0.0 1 0 0 0) # Request 0.0.0 -test_write_basic_config_version_file(4.5.6.7 0.0.2 1 0 0 0) # Request 0.0.[older patch] -test_write_basic_config_version_file(4.5.6.7 0.0.6 1 0 0 0) # Request 0.0.[same patch] -test_write_basic_config_version_file(4.5.6.7 0.0.9 1 0 0 0) # Request 0.0.[newer patch] -test_write_basic_config_version_file(4.5.6.7 0.2.0 1 0 0 0) # Request 0.[older minor].0 -test_write_basic_config_version_file(4.5.6.7 0.2.2 1 0 0 0) # Request 0.[older minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 0.2.6 1 0 0 0) # Request 0.[older minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 0.2.9 1 0 0 0) # Request 0.[older minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 0.5.0 1 0 0 0) # Request 0.[same minor].0 -test_write_basic_config_version_file(4.5.6.7 0.5.2 1 0 0 0) # Request 0.[same minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 0.5.6 1 0 0 0) # Request 0.[same minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 0.5.9 1 0 0 0) # Request 0.[same minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 0.9.0 1 0 0 0) # Request 0.[newer minor].0 -test_write_basic_config_version_file(4.5.6.7 0.9.2 1 0 0 0) # Request 0.[newer minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 0.9.6 1 0 0 0) # Request 0.[newer minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 0.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 2.0.0 1 0 0 0) # Request [older major].0.0 -test_write_basic_config_version_file(4.5.6.7 2.0.2 1 0 0 0) # Request [older major].0.[older patch] -test_write_basic_config_version_file(4.5.6.7 2.0.6 1 0 0 0) # Request [older major].0.[same patch] -test_write_basic_config_version_file(4.5.6.7 2.0.9 1 0 0 0) # Request [older major].0.[newer patch] -test_write_basic_config_version_file(4.5.6.7 2.2.0 1 0 0 0) # Request [older major].[older minor].0 -test_write_basic_config_version_file(4.5.6.7 2.2.2 1 0 0 0) # Request [older major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 2.2.6 1 0 0 0) # Request [older major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 2.2.9 1 0 0 0) # Request [older major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 2.5.0 1 0 0 0) # Request [older major].[same minor].0 -test_write_basic_config_version_file(4.5.6.7 2.5.2 1 0 0 0) # Request [older major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 2.5.6 1 0 0 0) # Request [older major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 2.5.9 1 0 0 0) # Request [older major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 2.9.0 1 0 0 0) # Request [older major].[newer minor].0 -test_write_basic_config_version_file(4.5.6.7 2.9.2 1 0 0 0) # Request [older major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 2.9.6 1 0 0 0) # Request [older major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 2.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 4.0.0 1 1 0 0) # Request [same major].0.0 -test_write_basic_config_version_file(4.5.6.7 4.0.2 1 1 0 0) # Request [same major].0.[older patch] -test_write_basic_config_version_file(4.5.6.7 4.0.6 1 1 0 0) # Request [same major].0.[same patch] -test_write_basic_config_version_file(4.5.6.7 4.0.9 1 1 0 0) # Request [same major].0.[newer patch] -test_write_basic_config_version_file(4.5.6.7 4.2.0 1 1 0 0) # Request [same major].[older minor].0 -test_write_basic_config_version_file(4.5.6.7 4.2.2 1 1 0 0) # Request [same major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 4.2.6 1 1 0 0) # Request [same major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 4.2.9 1 1 0 0) # Request [same major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 4.5.0 1 1 1 0) # Request [same major].[same minor].0 -test_write_basic_config_version_file(4.5.6.7 4.5.2 1 1 1 0) # Request [same major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 4.5.6 1 1 1 1) # Request [same major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 4.5.9 0 0 0 0) # Request [same major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 4.9.0 0 0 0 0) # Request [same major].[newer minor].0 -test_write_basic_config_version_file(4.5.6.7 4.9.2 0 0 0 0) # Request [same major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 4.9.6 0 0 0 0) # Request [same major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 4.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 9.0.0 0 0 0 0) # Request [newer major].0.0 -test_write_basic_config_version_file(4.5.6.7 9.0.2 0 0 0 0) # Request [newer major].0.[older patch] -test_write_basic_config_version_file(4.5.6.7 9.0.6 0 0 0 0) # Request [newer major].0.[same patch] -test_write_basic_config_version_file(4.5.6.7 9.0.9 0 0 0 0) # Request [newer major].0.[newer patch] -test_write_basic_config_version_file(4.5.6.7 9.2.0 0 0 0 0) # Request [newer major].[older minor].0 -test_write_basic_config_version_file(4.5.6.7 9.2.2 0 0 0 0) # Request [newer major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 9.2.6 0 0 0 0) # Request [newer major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 9.2.9 0 0 0 0) # Request [newer major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 9.5.0 0 0 0 0) # Request [newer major].[same minor].0 -test_write_basic_config_version_file(4.5.6.7 9.5.2 0 0 0 0) # Request [newer major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 9.5.6 0 0 0 0) # Request [newer major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 9.5.9 0 0 0 0) # Request [newer major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 9.9.0 0 0 0 0) # Request [newer major].[newer minor].0 -test_write_basic_config_version_file(4.5.6.7 9.9.2 0 0 0 0) # Request [newer major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.6.7 9.9.6 0 0 0 0) # Request [newer major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.6.7 9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5 0.0.0 1 0 0 0 0 0) # Request 0.0.0 +test_write_basic_config_version_file(4.5 0.0.9 1 0 0 0 0 0) # Request 0.0.[newer patch] +test_write_basic_config_version_file(4.5 0.2.0 1 0 0 0 0 0) # Request 0.[older minor].0 +test_write_basic_config_version_file(4.5 0.2.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch] +test_write_basic_config_version_file(4.5 0.5.0 1 0 0 0 0 0) # Request 0.[same minor].0 +test_write_basic_config_version_file(4.5 0.5.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch] +test_write_basic_config_version_file(4.5 0.9.0 1 0 0 0 0 0) # Request 0.[newer minor].0 +test_write_basic_config_version_file(4.5 0.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch] +test_write_basic_config_version_file(4.5 2.0.0 1 0 0 0 0 0) # Request [older major].0.0 +test_write_basic_config_version_file(4.5 2.0.9 1 0 0 0 0 0) # Request [older major].0.[newer patch] +test_write_basic_config_version_file(4.5 2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].0 +test_write_basic_config_version_file(4.5 2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5 2.5.0 1 0 0 0 0 0) # Request [older major].[same minor].0 +test_write_basic_config_version_file(4.5 2.5.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5 2.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].0 +test_write_basic_config_version_file(4.5 2.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5 4.0.0 1 1 0 0 0 1) # Request [same major].0.0 +test_write_basic_config_version_file(4.5 4.0.9 1 1 0 0 0 1) # Request [same major].0.[newer patch] +test_write_basic_config_version_file(4.5 4.2.0 1 1 0 0 0 1) # Request [same major].[older minor].0 +test_write_basic_config_version_file(4.5 4.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5 4.5.0 1 1 1 0 0 1) # Request [same major].[same minor].0 +test_write_basic_config_version_file(4.5 4.5.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5 4.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].0 +test_write_basic_config_version_file(4.5 4.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5 9.0.0 0 0 0 0 0 0) # Request [newer major].0.0 +test_write_basic_config_version_file(4.5 9.0.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch] +test_write_basic_config_version_file(4.5 9.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].0 +test_write_basic_config_version_file(4.5 9.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5 9.5.0 0 0 0 0 0 0) # Request [newer major].[same minor].0 +test_write_basic_config_version_file(4.5 9.5.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5 9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0 +test_write_basic_config_version_file(4.5 9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.6.7 0.0.0.0 1 0 0 0) # Request 0.0.0.0 -test_write_basic_config_version_file(4.5.6.7 0.0.0.2 1 0 0 0) # Request 0.0.0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.0.7 1 0 0 0) # Request 0.0.0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.0.9 1 0 0 0) # Request 0.0.0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.2.0 1 0 0 0) # Request 0.0.[older patch].0 -test_write_basic_config_version_file(4.5.6.7 0.0.2.2 1 0 0 0) # Request 0.0.[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.2.7 1 0 0 0) # Request 0.0.[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.2.9 1 0 0 0) # Request 0.0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.6.0 1 0 0 0) # Request 0.0.[same patch].0 -test_write_basic_config_version_file(4.5.6.7 0.0.6.2 1 0 0 0) # Request 0.0.[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.6.7 1 0 0 0) # Request 0.0.[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.6.9 1 0 0 0) # Request 0.0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.9.0 1 0 0 0) # Request 0.0.[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 0.0.9.2 1 0 0 0) # Request 0.0.[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.9.7 1 0 0 0) # Request 0.0.[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.0.9.9 1 0 0 0) # Request 0.0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.0.0 1 0 0 0) # Request 0.[older minor].0.0 -test_write_basic_config_version_file(4.5.6.7 0.2.0.2 1 0 0 0) # Request 0.[older minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.0.7 1 0 0 0) # Request 0.[older minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.0.9 1 0 0 0) # Request 0.[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.2.0 1 0 0 0) # Request 0.[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 0.2.2.2 1 0 0 0) # Request 0.[older minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.2.7 1 0 0 0) # Request 0.[older minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.2.9 1 0 0 0) # Request 0.[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.6.0 1 0 0 0) # Request 0.[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 0.2.6.2 1 0 0 0) # Request 0.[older minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.6.7 1 0 0 0) # Request 0.[older minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.6.9 1 0 0 0) # Request 0.[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.9.0 1 0 0 0) # Request 0.[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 0.2.9.2 1 0 0 0) # Request 0.[older minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.9.7 1 0 0 0) # Request 0.[older minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.2.9.9 1 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.0.0 1 0 0 0) # Request 0.[same minor].0.0 -test_write_basic_config_version_file(4.5.6.7 0.5.0.2 1 0 0 0) # Request 0.[same minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.0.7 1 0 0 0) # Request 0.[same minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.0.9 1 0 0 0) # Request 0.[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.2.0 1 0 0 0) # Request 0.[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 0.5.2.2 1 0 0 0) # Request 0.[same minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.2.7 1 0 0 0) # Request 0.[same minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.2.9 1 0 0 0) # Request 0.[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.6.0 1 0 0 0) # Request 0.[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 0.5.6.2 1 0 0 0) # Request 0.[same minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.6.7 1 0 0 0) # Request 0.[same minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.6.9 1 0 0 0) # Request 0.[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.9.0 1 0 0 0) # Request 0.[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 0.5.9.2 1 0 0 0) # Request 0.[same minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.9.7 1 0 0 0) # Request 0.[same minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.5.9.9 1 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.0.0 1 0 0 0) # Request 0.[newer minor].0.0 -test_write_basic_config_version_file(4.5.6.7 0.9.0.2 1 0 0 0) # Request 0.[newer minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.0.7 1 0 0 0) # Request 0.[newer minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.0.9 1 0 0 0) # Request 0.[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.2.0 1 0 0 0) # Request 0.[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 0.9.2.2 1 0 0 0) # Request 0.[newer minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.2.7 1 0 0 0) # Request 0.[newer minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.2.9 1 0 0 0) # Request 0.[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.6.0 1 0 0 0) # Request 0.[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 0.9.6.2 1 0 0 0) # Request 0.[newer minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.6.7 1 0 0 0) # Request 0.[newer minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.6.9 1 0 0 0) # Request 0.[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.9.0 1 0 0 0) # Request 0.[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 0.9.9.2 1 0 0 0) # Request 0.[newer minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.9.7 1 0 0 0) # Request 0.[newer minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 0.9.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.0.0 1 0 0 0) # Request [older major].0.0.0 -test_write_basic_config_version_file(4.5.6.7 2.0.0.2 1 0 0 0) # Request [older major].0.0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.0.7 1 0 0 0) # Request [older major].0.0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.0.9 1 0 0 0) # Request [older major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.2.0 1 0 0 0) # Request [older major].0.[older patch].0 -test_write_basic_config_version_file(4.5.6.7 2.0.2.2 1 0 0 0) # Request [older major].0.[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.2.7 1 0 0 0) # Request [older major].0.[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.2.9 1 0 0 0) # Request [older major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.6.0 1 0 0 0) # Request [older major].0.[same patch].0 -test_write_basic_config_version_file(4.5.6.7 2.0.6.2 1 0 0 0) # Request [older major].0.[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.6.7 1 0 0 0) # Request [older major].0.[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.6.9 1 0 0 0) # Request [older major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.9.0 1 0 0 0) # Request [older major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 2.0.9.2 1 0 0 0) # Request [older major].0.[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.9.7 1 0 0 0) # Request [older major].0.[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.0.9.9 1 0 0 0) # Request [older major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.0.0 1 0 0 0) # Request [older major].[older minor].0.0 -test_write_basic_config_version_file(4.5.6.7 2.2.0.2 1 0 0 0) # Request [older major].[older minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.0.7 1 0 0 0) # Request [older major].[older minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.0.9 1 0 0 0) # Request [older major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.2.0 1 0 0 0) # Request [older major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 2.2.2.2 1 0 0 0) # Request [older major].[older minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.2.7 1 0 0 0) # Request [older major].[older minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.2.9 1 0 0 0) # Request [older major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.6.0 1 0 0 0) # Request [older major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 2.2.6.2 1 0 0 0) # Request [older major].[older minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.6.7 1 0 0 0) # Request [older major].[older minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.6.9 1 0 0 0) # Request [older major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.9.0 1 0 0 0) # Request [older major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 2.2.9.2 1 0 0 0) # Request [older major].[older minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.9.7 1 0 0 0) # Request [older major].[older minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.2.9.9 1 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.0.0 1 0 0 0) # Request [older major].[same minor].0.0 -test_write_basic_config_version_file(4.5.6.7 2.5.0.2 1 0 0 0) # Request [older major].[same minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.0.7 1 0 0 0) # Request [older major].[same minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.0.9 1 0 0 0) # Request [older major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.2.0 1 0 0 0) # Request [older major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 2.5.2.2 1 0 0 0) # Request [older major].[same minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.2.7 1 0 0 0) # Request [older major].[same minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.2.9 1 0 0 0) # Request [older major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.6.0 1 0 0 0) # Request [older major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 2.5.6.2 1 0 0 0) # Request [older major].[same minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.6.7 1 0 0 0) # Request [older major].[same minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.6.9 1 0 0 0) # Request [older major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.9.0 1 0 0 0) # Request [older major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 2.5.9.2 1 0 0 0) # Request [older major].[same minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.9.7 1 0 0 0) # Request [older major].[same minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.5.9.9 1 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.0.0 1 0 0 0) # Request [older major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.6.7 2.9.0.2 1 0 0 0) # Request [older major].[newer minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.0.7 1 0 0 0) # Request [older major].[newer minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.0.9 1 0 0 0) # Request [older major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.2.0 1 0 0 0) # Request [older major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 2.9.2.2 1 0 0 0) # Request [older major].[newer minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.2.7 1 0 0 0) # Request [older major].[newer minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.2.9 1 0 0 0) # Request [older major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.6.0 1 0 0 0) # Request [older major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 2.9.6.2 1 0 0 0) # Request [older major].[newer minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.6.7 1 0 0 0) # Request [older major].[newer minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.6.9 1 0 0 0) # Request [older major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.9.0 1 0 0 0) # Request [older major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 2.9.9.2 1 0 0 0) # Request [older major].[newer minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.9.7 1 0 0 0) # Request [older major].[newer minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 2.9.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.0.0 1 1 0 0) # Request [same major].0.0.0 -test_write_basic_config_version_file(4.5.6.7 4.0.0.2 1 1 0 0) # Request [same major].0.0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.0.7 1 1 0 0) # Request [same major].0.0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.0.9 1 1 0 0) # Request [same major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.2.0 1 1 0 0) # Request [same major].0.[older patch].0 -test_write_basic_config_version_file(4.5.6.7 4.0.2.2 1 1 0 0) # Request [same major].0.[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.2.7 1 1 0 0) # Request [same major].0.[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.2.9 1 1 0 0) # Request [same major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.6.0 1 1 0 0) # Request [same major].0.[same patch].0 -test_write_basic_config_version_file(4.5.6.7 4.0.6.2 1 1 0 0) # Request [same major].0.[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.6.7 1 1 0 0) # Request [same major].0.[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.6.9 1 1 0 0) # Request [same major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.9.0 1 1 0 0) # Request [same major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 4.0.9.2 1 1 0 0) # Request [same major].0.[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.9.7 1 1 0 0) # Request [same major].0.[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.0.9.9 1 1 0 0) # Request [same major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.0.0 1 1 0 0) # Request [same major].[older minor].0.0 -test_write_basic_config_version_file(4.5.6.7 4.2.0.2 1 1 0 0) # Request [same major].[older minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.0.7 1 1 0 0) # Request [same major].[older minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.0.9 1 1 0 0) # Request [same major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.2.0 1 1 0 0) # Request [same major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 4.2.2.2 1 1 0 0) # Request [same major].[older minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.2.7 1 1 0 0) # Request [same major].[older minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.2.9 1 1 0 0) # Request [same major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.6.0 1 1 0 0) # Request [same major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 4.2.6.2 1 1 0 0) # Request [same major].[older minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.6.7 1 1 0 0) # Request [same major].[older minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.6.9 1 1 0 0) # Request [same major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.9.0 1 1 0 0) # Request [same major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 4.2.9.2 1 1 0 0) # Request [same major].[older minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.9.7 1 1 0 0) # Request [same major].[older minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.2.9.9 1 1 0 0) # Request [same major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.0.0 1 1 1 0) # Request [same major].[same minor].0.0 -test_write_basic_config_version_file(4.5.6.7 4.5.0.2 1 1 1 0) # Request [same major].[same minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.0.7 1 1 1 0) # Request [same major].[same minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.0.9 1 1 1 0) # Request [same major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.2.0 1 1 1 0) # Request [same major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 4.5.2.2 1 1 1 0) # Request [same major].[same minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.2.7 1 1 1 0) # Request [same major].[same minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.2.9 1 1 1 0) # Request [same major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.6.0 1 1 1 1) # Request [same major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 4.5.6.2 1 1 1 1) # Request [same major].[same minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.6.7 1 1 1 1) # Request [same major].[same minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.6.9 0 0 0 1) # Request [same major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.9.0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 4.5.9.2 0 0 0 0) # Request [same major].[same minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.9.7 0 0 0 0) # Request [same major].[same minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.5.9.9 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.0.0 0 0 0 0) # Request [same major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.6.7 4.9.0.2 0 0 0 0) # Request [same major].[newer minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.0.7 0 0 0 0) # Request [same major].[newer minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.0.9 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.2.0 0 0 0 0) # Request [same major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 4.9.2.2 0 0 0 0) # Request [same major].[newer minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.2.7 0 0 0 0) # Request [same major].[newer minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.2.9 0 0 0 0) # Request [same major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.6.0 0 0 0 0) # Request [same major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 4.9.6.2 0 0 0 0) # Request [same major].[newer minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.6.7 0 0 0 0) # Request [same major].[newer minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.6.9 0 0 0 0) # Request [same major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.9.0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 4.9.9.2 0 0 0 0) # Request [same major].[newer minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.9.7 0 0 0 0) # Request [same major].[newer minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 4.9.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.0.0 0 0 0 0) # Request [newer major].0.0.0 -test_write_basic_config_version_file(4.5.6.7 9.0.0.2 0 0 0 0) # Request [newer major].0.0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.0.7 0 0 0 0) # Request [newer major].0.0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.0.9 0 0 0 0) # Request [newer major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.2.0 0 0 0 0) # Request [newer major].0.[older patch].0 -test_write_basic_config_version_file(4.5.6.7 9.0.2.2 0 0 0 0) # Request [newer major].0.[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.2.7 0 0 0 0) # Request [newer major].0.[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.2.9 0 0 0 0) # Request [newer major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.6.0 0 0 0 0) # Request [newer major].0.[same patch].0 -test_write_basic_config_version_file(4.5.6.7 9.0.6.2 0 0 0 0) # Request [newer major].0.[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.6.7 0 0 0 0) # Request [newer major].0.[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.6.9 0 0 0 0) # Request [newer major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.9.0 0 0 0 0) # Request [newer major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 9.0.9.2 0 0 0 0) # Request [newer major].0.[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.9.7 0 0 0 0) # Request [newer major].0.[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.0.9.9 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.0.0 0 0 0 0) # Request [newer major].[older minor].0.0 -test_write_basic_config_version_file(4.5.6.7 9.2.0.2 0 0 0 0) # Request [newer major].[older minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.0.7 0 0 0 0) # Request [newer major].[older minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.0.9 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.2.0 0 0 0 0) # Request [newer major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 9.2.2.2 0 0 0 0) # Request [newer major].[older minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.2.7 0 0 0 0) # Request [newer major].[older minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.2.9 0 0 0 0) # Request [newer major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.6.0 0 0 0 0) # Request [newer major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 9.2.6.2 0 0 0 0) # Request [newer major].[older minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.6.7 0 0 0 0) # Request [newer major].[older minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.6.9 0 0 0 0) # Request [newer major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.9.0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 9.2.9.2 0 0 0 0) # Request [newer major].[older minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.9.7 0 0 0 0) # Request [newer major].[older minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.2.9.9 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.0.0 0 0 0 0) # Request [newer major].[same minor].0.0 -test_write_basic_config_version_file(4.5.6.7 9.5.0.2 0 0 0 0) # Request [newer major].[same minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.0.7 0 0 0 0) # Request [newer major].[same minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.0.9 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.2.0 0 0 0 0) # Request [newer major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 9.5.2.2 0 0 0 0) # Request [newer major].[same minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.2.7 0 0 0 0) # Request [newer major].[same minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.2.9 0 0 0 0) # Request [newer major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.6.0 0 0 0 0) # Request [newer major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 9.5.6.2 0 0 0 0) # Request [newer major].[same minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.6.7 0 0 0 0) # Request [newer major].[same minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.6.9 0 0 0 0) # Request [newer major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.9.0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 9.5.9.2 0 0 0 0) # Request [newer major].[same minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.9.7 0 0 0 0) # Request [newer major].[same minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.5.9.9 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.0.0 0 0 0 0) # Request [newer major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.6.7 9.9.0.2 0 0 0 0) # Request [newer major].[newer minor].0.[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.0.7 0 0 0 0) # Request [newer major].[newer minor].0.[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.0.9 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.2.0 0 0 0 0) # Request [newer major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.6.7 9.9.2.2 0 0 0 0) # Request [newer major].[newer minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.2.7 0 0 0 0) # Request [newer major].[newer minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.2.9 0 0 0 0) # Request [newer major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.6.0 0 0 0 0) # Request [newer major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.6.7 9.9.6.2 0 0 0 0) # Request [newer major].[newer minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.6.7 0 0 0 0) # Request [newer major].[newer minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.6.9 0 0 0 0) # Request [newer major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.9.0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.6.7 9.9.9.2 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.9.7 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.5.6.7 9.9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] - -test_write_basic_config_version_file(4 0...5 1 0 0 0) -test_write_basic_config_version_file(4 2...5 1 0 0 0) -test_write_basic_config_version_file(4 2...4 1 0 0 0) -test_write_basic_config_version_file(4 4...<5 1 1 0 0) -test_write_basic_config_version_file(4 9...10 0 0 0 0) - -test_write_basic_config_version_file(4 0.1...5 1 0 0 0) -test_write_basic_config_version_file(4 2.1...5 1 0 0 0) -test_write_basic_config_version_file(4 2.8...5 1 0 0 0) -test_write_basic_config_version_file(4 2.1...4 1 0 0 0) -test_write_basic_config_version_file(4 2.8...4 1 0 0 0) -test_write_basic_config_version_file(4 4.0...<5 1 1 0 0) -test_write_basic_config_version_file(4 4.8...<5 0 0 0 0) -test_write_basic_config_version_file(4 4.1...5 0 0 0 0) -test_write_basic_config_version_file(4 4.8...5 0 0 0 0) -test_write_basic_config_version_file(4 9.1...10 0 0 0 0) -test_write_basic_config_version_file(4 9.8...10 0 0 0 0) +test_write_basic_config_version_file(4.5 0.0.0.0 1 0 0 0 0 0) # Request 0.0.0.0 +test_write_basic_config_version_file(4.5 0.0.0.9 1 0 0 0 0 0) # Request 0.0.0.[newer tweak] +test_write_basic_config_version_file(4.5 0.0.9.0 1 0 0 0 0 0) # Request 0.0.[newer patch].0 +test_write_basic_config_version_file(4.5 0.0.9.9 1 0 0 0 0 0) # Request 0.0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 0.2.0.0 1 0 0 0 0 0) # Request 0.[older minor].0.0 +test_write_basic_config_version_file(4.5 0.2.0.9 1 0 0 0 0 0) # Request 0.[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 0.2.9.0 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5 0.2.9.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 0.5.0.0 1 0 0 0 0 0) # Request 0.[same minor].0.0 +test_write_basic_config_version_file(4.5 0.5.0.9 1 0 0 0 0 0) # Request 0.[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 0.5.9.0 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5 0.5.9.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 0.9.0.0 1 0 0 0 0 0) # Request 0.[newer minor].0.0 +test_write_basic_config_version_file(4.5 0.9.0.9 1 0 0 0 0 0) # Request 0.[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 0.9.9.0 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5 0.9.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 2.0.0.0 1 0 0 0 0 0) # Request [older major].0.0.0 +test_write_basic_config_version_file(4.5 2.0.0.9 1 0 0 0 0 0) # Request [older major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5 2.0.9.0 1 0 0 0 0 0) # Request [older major].0.[newer patch].0 +test_write_basic_config_version_file(4.5 2.0.9.9 1 0 0 0 0 0) # Request [older major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 2.2.0.0 1 0 0 0 0 0) # Request [older major].[older minor].0.0 +test_write_basic_config_version_file(4.5 2.2.0.9 1 0 0 0 0 0) # Request [older major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 2.2.9.0 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5 2.2.9.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 2.5.0.0 1 0 0 0 0 0) # Request [older major].[same minor].0.0 +test_write_basic_config_version_file(4.5 2.5.0.9 1 0 0 0 0 0) # Request [older major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 2.5.9.0 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5 2.5.9.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 2.9.0.0 1 0 0 0 0 0) # Request [older major].[newer minor].0.0 +test_write_basic_config_version_file(4.5 2.9.0.9 1 0 0 0 0 0) # Request [older major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 2.9.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5 2.9.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 4.0.0.0 1 1 0 0 0 1) # Request [same major].0.0.0 +test_write_basic_config_version_file(4.5 4.0.0.9 1 1 0 0 0 1) # Request [same major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5 4.0.9.0 1 1 0 0 0 1) # Request [same major].0.[newer patch].0 +test_write_basic_config_version_file(4.5 4.0.9.9 1 1 0 0 0 1) # Request [same major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 4.2.0.0 1 1 0 0 0 1) # Request [same major].[older minor].0.0 +test_write_basic_config_version_file(4.5 4.2.0.9 1 1 0 0 0 1) # Request [same major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 4.2.9.0 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5 4.2.9.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 4.5.0.0 1 1 1 0 0 1) # Request [same major].[same minor].0.0 +test_write_basic_config_version_file(4.5 4.5.0.9 0 0 0 0 0 0) # Request [same major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 4.5.9.0 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5 4.5.9.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 4.9.0.0 0 0 0 0 0 0) # Request [same major].[newer minor].0.0 +test_write_basic_config_version_file(4.5 4.9.0.9 0 0 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 4.9.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5 4.9.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 9.0.0.0 0 0 0 0 0 0) # Request [newer major].0.0.0 +test_write_basic_config_version_file(4.5 9.0.0.9 0 0 0 0 0 0) # Request [newer major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5 9.0.9.0 0 0 0 0 0 0) # Request [newer major].0.[newer patch].0 +test_write_basic_config_version_file(4.5 9.0.9.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 9.2.0.0 0 0 0 0 0 0) # Request [newer major].[older minor].0.0 +test_write_basic_config_version_file(4.5 9.2.0.9 0 0 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 9.2.9.0 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5 9.2.9.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 9.5.0.0 0 0 0 0 0 0) # Request [newer major].[same minor].0.0 +test_write_basic_config_version_file(4.5 9.5.0.9 0 0 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 9.5.9.0 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5 9.5.9.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5 9.9.0.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0.0 +test_write_basic_config_version_file(4.5 9.9.0.9 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5 9.9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5 9.9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5 0.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5 2.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5 2.8...5 1 0 0 0) -test_write_basic_config_version_file(4.5 2.1...4 0 0 0 0) -test_write_basic_config_version_file(4.5 2.8...4 0 0 0 0) -test_write_basic_config_version_file(4.5 2.8...4.8 1 0 0 0) -test_write_basic_config_version_file(4.5 4.1...<5 1 1 0 0) -test_write_basic_config_version_file(4.5 4.8...<5 0 0 0 0) -test_write_basic_config_version_file(4.5 4.5...4.5.8 1 1 1 0) -test_write_basic_config_version_file(4.5 4.5...<4.6 1 1 1 0) -test_write_basic_config_version_file(4.5 4.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5 4.8...5 0 0 0 0) -test_write_basic_config_version_file(4.5 9.1...10 0 0 0 0) -test_write_basic_config_version_file(4.5 9.8...10 0 0 0 0) +test_write_basic_config_version_file(0.5.6 0.2.6 1 1 0 0 0 0) +test_write_basic_config_version_file(0.5.6 0.5 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.6 0.5.2 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.6 0.5.6 1 1 1 1 1 1) +test_write_basic_config_version_file(0.5.6 0.5.9 0 0 0 0 0 0) +test_write_basic_config_version_file(0.5.6 0.9.0 0 0 0 0 0 0) -test_write_basic_config_version_file(4.5.6 0.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5.6 2.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5.6 2.8...5 1 0 0 0) -test_write_basic_config_version_file(4.5.6 2.1...4 0 0 0 0) -test_write_basic_config_version_file(4.5.6 2.8...4 0 0 0 0) -test_write_basic_config_version_file(4.5.6 2.8...4.8 1 0 0 0) -test_write_basic_config_version_file(4.5.6 4.1...<5 1 1 0 0) -test_write_basic_config_version_file(4.5.6 4.8...<5 0 0 0 0) -test_write_basic_config_version_file(4.5.6 4.5...4.5.4 0 0 0 0) -test_write_basic_config_version_file(4.5.6 4.5...4.5.8 1 1 1 0) -test_write_basic_config_version_file(4.5.6 4.5...<4.6 1 1 1 0) -test_write_basic_config_version_file(4.5.6 4.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5.6 4.8...5 0 0 0 0) -test_write_basic_config_version_file(4.5.6 9.1...10 0 0 0 0) -test_write_basic_config_version_file(4.5.6 9.8...10 0 0 0 0) +test_write_basic_config_version_file(4.5.6 0 1 0 0 0 0 0) # Request 0 +test_write_basic_config_version_file(4.5.6 2 1 0 0 0 0 0) # Request [older major] +test_write_basic_config_version_file(4.5.6 4 1 1 0 0 0 1) # Request [same major] +test_write_basic_config_version_file(4.5.6 9 0 0 0 0 0 0) # Request [newer major] + +test_write_basic_config_version_file(4.5.6 0.0 1 0 0 0 0 0) # Request 0.0 +test_write_basic_config_version_file(4.5.6 0.2 1 0 0 0 0 0) # Request 0.[older minor] +test_write_basic_config_version_file(4.5.6 0.5 1 0 0 0 0 0) # Request 0.[same minor] +test_write_basic_config_version_file(4.5.6 0.9 1 0 0 0 0 0) # Request 0.[newer minor] +test_write_basic_config_version_file(4.5.6 2.0 1 0 0 0 0 0) # Request [older major].0 +test_write_basic_config_version_file(4.5.6 2.2 1 0 0 0 0 0) # Request [older major].[older minor] +test_write_basic_config_version_file(4.5.6 2.5 1 0 0 0 0 0) # Request [older major].[same minor] +test_write_basic_config_version_file(4.5.6 2.9 1 0 0 0 0 0) # Request [older major].[newer minor] +test_write_basic_config_version_file(4.5.6 4.0 1 1 0 0 0 1) # Request [same major].0 +test_write_basic_config_version_file(4.5.6 4.2 1 1 0 0 0 1) # Request [same major].[older minor] +test_write_basic_config_version_file(4.5.6 4.5 1 1 1 0 0 1) # Request [same major].[same minor] +test_write_basic_config_version_file(4.5.6 4.9 0 0 0 0 0 0) # Request [same major].[newer minor] +test_write_basic_config_version_file(4.5.6 9.0 0 0 0 0 0 0) # Request [newer major].0 +test_write_basic_config_version_file(4.5.6 9.1 0 0 0 0 0 0) # Request [newer major].[older minor] +test_write_basic_config_version_file(4.5.6 9.5 0 0 0 0 0 0) # Request [newer major].[same minor] +test_write_basic_config_version_file(4.5.6 9.9 0 0 0 0 0 0) # Request [newer major].[newer minor] + +test_write_basic_config_version_file(4.5.6 0.0.0 1 0 0 0 0 0) # Request 0.0.0 +test_write_basic_config_version_file(4.5.6 0.0.2 1 0 0 0 0 0) # Request 0.0.[older patch] +test_write_basic_config_version_file(4.5.6 0.0.6 1 0 0 0 0 0) # Request 0.0.[same patch] +test_write_basic_config_version_file(4.5.6 0.0.9 1 0 0 0 0 0) # Request 0.0.[newer patch] +test_write_basic_config_version_file(4.5.6 0.2.0 1 0 0 0 0 0) # Request 0.[older minor].0 +test_write_basic_config_version_file(4.5.6 0.2.2 1 0 0 0 0 0) # Request 0.[older minor].[older patch] +test_write_basic_config_version_file(4.5.6 0.2.6 1 0 0 0 0 0) # Request 0.[older minor].[same patch] +test_write_basic_config_version_file(4.5.6 0.2.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch] +test_write_basic_config_version_file(4.5.6 0.5.0 1 0 0 0 0 0) # Request 0.[same minor].0 +test_write_basic_config_version_file(4.5.6 0.5.2 1 0 0 0 0 0) # Request 0.[same minor].[older patch] +test_write_basic_config_version_file(4.5.6 0.5.6 1 0 0 0 0 0) # Request 0.[same minor].[same patch] +test_write_basic_config_version_file(4.5.6 0.5.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch] +test_write_basic_config_version_file(4.5.6 0.9.0 1 0 0 0 0 0) # Request 0.[newer minor].0 +test_write_basic_config_version_file(4.5.6 0.9.2 1 0 0 0 0 0) # Request 0.[newer minor].[older patch] +test_write_basic_config_version_file(4.5.6 0.9.6 1 0 0 0 0 0) # Request 0.[newer minor].[same patch] +test_write_basic_config_version_file(4.5.6 0.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.6 2.0.0 1 0 0 0 0 0) # Request [older major].0.0 +test_write_basic_config_version_file(4.5.6 2.0.2 1 0 0 0 0 0) # Request [older major].0.[older patch] +test_write_basic_config_version_file(4.5.6 2.0.6 1 0 0 0 0 0) # Request [older major].0.[same patch] +test_write_basic_config_version_file(4.5.6 2.0.9 1 0 0 0 0 0) # Request [older major].0.[newer patch] +test_write_basic_config_version_file(4.5.6 2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].0 +test_write_basic_config_version_file(4.5.6 2.2.2 1 0 0 0 0 0) # Request [older major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.6 2.2.6 1 0 0 0 0 0) # Request [older major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.6 2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.6 2.5.0 1 0 0 0 0 0) # Request [older major].[same minor].0 +test_write_basic_config_version_file(4.5.6 2.5.2 1 0 0 0 0 0) # Request [older major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.6 2.5.6 1 0 0 0 0 0) # Request [older major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.6 2.5.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.6 2.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].0 +test_write_basic_config_version_file(4.5.6 2.9.2 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.6 2.9.6 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.6 2.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.6 4.0.0 1 1 0 0 0 1) # Request [same major].0.0 +test_write_basic_config_version_file(4.5.6 4.0.2 1 1 0 0 0 1) # Request [same major].0.[older patch] +test_write_basic_config_version_file(4.5.6 4.0.6 1 1 0 0 0 1) # Request [same major].0.[same patch] +test_write_basic_config_version_file(4.5.6 4.0.9 1 1 0 0 0 1) # Request [same major].0.[newer patch] +test_write_basic_config_version_file(4.5.6 4.2.0 1 1 0 0 0 1) # Request [same major].[older minor].0 +test_write_basic_config_version_file(4.5.6 4.2.2 1 1 0 0 0 1) # Request [same major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.6 4.2.6 1 1 0 0 0 1) # Request [same major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.6 4.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.6 4.5.0 1 1 1 0 0 1) # Request [same major].[same minor].0 +test_write_basic_config_version_file(4.5.6 4.5.2 1 1 1 0 0 1) # Request [same major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.6 4.5.6 1 1 1 1 1 1) # Request [same major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.6 4.5.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.6 4.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].0 +test_write_basic_config_version_file(4.5.6 4.9.2 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.6 4.9.6 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.6 4.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.6 9.0.0 0 0 0 0 0 0) # Request [newer major].0.0 +test_write_basic_config_version_file(4.5.6 9.0.2 0 0 0 0 0 0) # Request [newer major].0.[older patch] +test_write_basic_config_version_file(4.5.6 9.0.6 0 0 0 0 0 0) # Request [newer major].0.[same patch] +test_write_basic_config_version_file(4.5.6 9.0.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch] +test_write_basic_config_version_file(4.5.6 9.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].0 +test_write_basic_config_version_file(4.5.6 9.2.2 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.6 9.2.6 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.6 9.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.6 9.5.0 0 0 0 0 0 0) # Request [newer major].[same minor].0 +test_write_basic_config_version_file(4.5.6 9.5.2 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.6 9.5.6 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.6 9.5.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.6 9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0 +test_write_basic_config_version_file(4.5.6 9.9.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.6 9.9.6 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.6 9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch] + +test_write_basic_config_version_file(4.5.6 0.0.0.0 1 0 0 0 0 0) # Request 0.0.0.0 +test_write_basic_config_version_file(4.5.6 0.0.0.9 1 0 0 0 0 0) # Request 0.0.0.[newer tweak] +test_write_basic_config_version_file(4.5.6 0.0.2.0 1 0 0 0 0 0) # Request 0.0.[older patch].0 +test_write_basic_config_version_file(4.5.6 0.0.2.9 1 0 0 0 0 0) # Request 0.0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.0.6.0 1 0 0 0 0 0) # Request 0.0.[same patch].0 +test_write_basic_config_version_file(4.5.6 0.0.6.9 1 0 0 0 0 0) # Request 0.0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.0.9.0 1 0 0 0 0 0) # Request 0.0.[newer patch].0 +test_write_basic_config_version_file(4.5.6 0.0.9.9 1 0 0 0 0 0) # Request 0.0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.2.0.0 1 0 0 0 0 0) # Request 0.[older minor].0.0 +test_write_basic_config_version_file(4.5.6 0.2.0.9 1 0 0 0 0 0) # Request 0.[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 0.2.2.0 1 0 0 0 0 0) # Request 0.[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 0.2.2.9 1 0 0 0 0 0) # Request 0.[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.2.6.0 1 0 0 0 0 0) # Request 0.[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 0.2.6.9 1 0 0 0 0 0) # Request 0.[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.2.9.0 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 0.2.9.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.5.0.0 1 0 0 0 0 0) # Request 0.[same minor].0.0 +test_write_basic_config_version_file(4.5.6 0.5.0.9 1 0 0 0 0 0) # Request 0.[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 0.5.2.0 1 0 0 0 0 0) # Request 0.[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 0.5.2.9 1 0 0 0 0 0) # Request 0.[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.5.6.0 1 0 0 0 0 0) # Request 0.[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 0.5.6.9 1 0 0 0 0 0) # Request 0.[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.5.9.0 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 0.5.9.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.9.0.0 1 0 0 0 0 0) # Request 0.[newer minor].0.0 +test_write_basic_config_version_file(4.5.6 0.9.0.9 1 0 0 0 0 0) # Request 0.[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 0.9.2.0 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 0.9.2.9 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.9.6.0 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 0.9.6.9 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 0.9.9.0 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 0.9.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.0.0.0 1 0 0 0 0 0) # Request [older major].0.0.0 +test_write_basic_config_version_file(4.5.6 2.0.0.9 1 0 0 0 0 0) # Request [older major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.6 2.0.2.0 1 0 0 0 0 0) # Request [older major].0.[older patch].0 +test_write_basic_config_version_file(4.5.6 2.0.2.9 1 0 0 0 0 0) # Request [older major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.0.6.0 1 0 0 0 0 0) # Request [older major].0.[same patch].0 +test_write_basic_config_version_file(4.5.6 2.0.6.9 1 0 0 0 0 0) # Request [older major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.0.9.0 1 0 0 0 0 0) # Request [older major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.6 2.0.9.9 1 0 0 0 0 0) # Request [older major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.2.0.0 1 0 0 0 0 0) # Request [older major].[older minor].0.0 +test_write_basic_config_version_file(4.5.6 2.2.0.9 1 0 0 0 0 0) # Request [older major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 2.2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 2.2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.2.6.0 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 2.2.6.9 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.2.9.0 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 2.2.9.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.5.0.0 1 0 0 0 0 0) # Request [older major].[same minor].0.0 +test_write_basic_config_version_file(4.5.6 2.5.0.9 1 0 0 0 0 0) # Request [older major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 2.5.2.0 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 2.5.2.9 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.5.6.0 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 2.5.6.9 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.5.9.0 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 2.5.9.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.9.0.0 1 0 0 0 0 0) # Request [older major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.6 2.9.0.9 1 0 0 0 0 0) # Request [older major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 2.9.2.0 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 2.9.2.9 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.9.6.0 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 2.9.6.9 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 2.9.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 2.9.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.0.0.0 1 1 0 0 0 1) # Request [same major].0.0.0 +test_write_basic_config_version_file(4.5.6 4.0.0.9 1 1 0 0 0 1) # Request [same major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.6 4.0.2.0 1 1 0 0 0 1) # Request [same major].0.[older patch].0 +test_write_basic_config_version_file(4.5.6 4.0.2.9 1 1 0 0 0 1) # Request [same major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.0.6.0 1 1 0 0 0 1) # Request [same major].0.[same patch].0 +test_write_basic_config_version_file(4.5.6 4.0.6.9 1 1 0 0 0 1) # Request [same major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.0.9.0 1 1 0 0 0 1) # Request [same major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.6 4.0.9.9 1 1 0 0 0 1) # Request [same major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.2.0.0 1 1 0 0 0 1) # Request [same major].[older minor].0.0 +test_write_basic_config_version_file(4.5.6 4.2.0.9 1 1 0 0 0 1) # Request [same major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 4.2.2.0 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 4.2.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.2.6.0 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 4.2.6.9 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.2.9.0 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 4.2.9.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.5.0.0 1 1 1 0 0 1) # Request [same major].[same minor].0.0 +test_write_basic_config_version_file(4.5.6 4.5.0.9 1 1 1 0 0 1) # Request [same major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 4.5.2.0 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 4.5.2.9 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.5.6.0 1 1 1 1 0 1) # Request [same major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 4.5.6.9 0 0 0 1 0 0) # Request [same major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.5.9.0 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 4.5.9.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.9.0.0 0 0 0 0 0 0) # Request [same major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.6 4.9.0.9 0 0 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 4.9.2.0 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 4.9.2.9 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.9.6.0 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 4.9.6.9 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 4.9.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 4.9.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.0.0.0 0 0 0 0 0 0) # Request [newer major].0.0.0 +test_write_basic_config_version_file(4.5.6 9.0.0.9 0 0 0 0 0 0) # Request [newer major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.6 9.0.2.0 0 0 0 0 0 0) # Request [newer major].0.[older patch].0 +test_write_basic_config_version_file(4.5.6 9.0.2.9 0 0 0 0 0 0) # Request [newer major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.0.6.0 0 0 0 0 0 0) # Request [newer major].0.[same patch].0 +test_write_basic_config_version_file(4.5.6 9.0.6.9 0 0 0 0 0 0) # Request [newer major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.0.9.0 0 0 0 0 0 0) # Request [newer major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.6 9.0.9.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.2.0.0 0 0 0 0 0 0) # Request [newer major].[older minor].0.0 +test_write_basic_config_version_file(4.5.6 9.2.0.9 0 0 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 9.2.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 9.2.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.2.6.0 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 9.2.6.9 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.2.9.0 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 9.2.9.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.5.0.0 0 0 0 0 0 0) # Request [newer major].[same minor].0.0 +test_write_basic_config_version_file(4.5.6 9.5.0.9 0 0 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 9.5.2.0 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 9.5.2.9 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.5.6.0 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 9.5.6.9 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.5.9.0 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 9.5.9.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.9.0.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.6 9.9.0.9 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6 9.9.2.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.6 9.9.2.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.9.6.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.6 9.9.6.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6 9.9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6 9.9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] + + +test_write_basic_config_version_file(0.5.6.7 0.2.6.7 1 1 0 0 0 0) +test_write_basic_config_version_file(0.5.6.7 0.5 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.6.7 0.5.2 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.6.7 0.5.6 1 1 1 1 0 1) +test_write_basic_config_version_file(0.5.6.7 0.5.6.2 1 1 1 1 0 1) +test_write_basic_config_version_file(0.5.6.7 0.5.6.7 1 1 1 1 1 1) +test_write_basic_config_version_file(0.5.6.7 0.5.6.9 0 0 0 1 0 0) +test_write_basic_config_version_file(0.5.6.7 0.5.9.0 0 0 0 0 0 0) +test_write_basic_config_version_file(0.5.6.7 0.9.0.0 0 0 0 0 0 0) + + +test_write_basic_config_version_file(4.5.6.7 0 1 0 0 0 0 0) # Request 0 +test_write_basic_config_version_file(4.5.6.7 2 1 0 0 0 0 0) # Request [older major] +test_write_basic_config_version_file(4.5.6.7 4 1 1 0 0 0 1) # Request [same major] +test_write_basic_config_version_file(4.5.6.7 9 0 0 0 0 0 0) # Request [newer major] + +test_write_basic_config_version_file(4.5.6.7 0.0 1 0 0 0 0 0) # Request 0.0 +test_write_basic_config_version_file(4.5.6.7 0.2 1 0 0 0 0 0) # Request 0.[older minor] +test_write_basic_config_version_file(4.5.6.7 0.5 1 0 0 0 0 0) # Request 0.[same minor] +test_write_basic_config_version_file(4.5.6.7 0.9 1 0 0 0 0 0) # Request 0.[newer minor] +test_write_basic_config_version_file(4.5.6.7 2.0 1 0 0 0 0 0) # Request [older major].0 +test_write_basic_config_version_file(4.5.6.7 2.2 1 0 0 0 0 0) # Request [older major].[older minor] +test_write_basic_config_version_file(4.5.6.7 2.5 1 0 0 0 0 0) # Request [older major].[same minor] +test_write_basic_config_version_file(4.5.6.7 2.9 1 0 0 0 0 0) # Request [older major].[newer minor] +test_write_basic_config_version_file(4.5.6.7 4.0 1 1 0 0 0 1) # Request [same major].0 +test_write_basic_config_version_file(4.5.6.7 4.2 1 1 0 0 0 1) # Request [same major].[older minor] +test_write_basic_config_version_file(4.5.6.7 4.5 1 1 1 0 0 1) # Request [same major].[same minor] +test_write_basic_config_version_file(4.5.6.7 4.9 0 0 0 0 0 0) # Request [same major].[newer minor] +test_write_basic_config_version_file(4.5.6.7 9.0 0 0 0 0 0 0) # Request [newer major].0 +test_write_basic_config_version_file(4.5.6.7 9.1 0 0 0 0 0 0) # Request [newer major].[older minor] +test_write_basic_config_version_file(4.5.6.7 9.5 0 0 0 0 0 0) # Request [newer major].[same minor] +test_write_basic_config_version_file(4.5.6.7 9.9 0 0 0 0 0 0) # Request [newer major].[newer minor] + +test_write_basic_config_version_file(4.5.6.7 0.0.0 1 0 0 0 0 0) # Request 0.0.0 +test_write_basic_config_version_file(4.5.6.7 0.0.2 1 0 0 0 0 0) # Request 0.0.[older patch] +test_write_basic_config_version_file(4.5.6.7 0.0.6 1 0 0 0 0 0) # Request 0.0.[same patch] +test_write_basic_config_version_file(4.5.6.7 0.0.9 1 0 0 0 0 0) # Request 0.0.[newer patch] +test_write_basic_config_version_file(4.5.6.7 0.2.0 1 0 0 0 0 0) # Request 0.[older minor].0 +test_write_basic_config_version_file(4.5.6.7 0.2.2 1 0 0 0 0 0) # Request 0.[older minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 0.2.6 1 0 0 0 0 0) # Request 0.[older minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 0.2.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 0.5.0 1 0 0 0 0 0) # Request 0.[same minor].0 +test_write_basic_config_version_file(4.5.6.7 0.5.2 1 0 0 0 0 0) # Request 0.[same minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 0.5.6 1 0 0 0 0 0) # Request 0.[same minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 0.5.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 0.9.0 1 0 0 0 0 0) # Request 0.[newer minor].0 +test_write_basic_config_version_file(4.5.6.7 0.9.2 1 0 0 0 0 0) # Request 0.[newer minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 0.9.6 1 0 0 0 0 0) # Request 0.[newer minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 0.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 2.0.0 1 0 0 0 0 0) # Request [older major].0.0 +test_write_basic_config_version_file(4.5.6.7 2.0.2 1 0 0 0 0 0) # Request [older major].0.[older patch] +test_write_basic_config_version_file(4.5.6.7 2.0.6 1 0 0 0 0 0) # Request [older major].0.[same patch] +test_write_basic_config_version_file(4.5.6.7 2.0.9 1 0 0 0 0 0) # Request [older major].0.[newer patch] +test_write_basic_config_version_file(4.5.6.7 2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].0 +test_write_basic_config_version_file(4.5.6.7 2.2.2 1 0 0 0 0 0) # Request [older major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 2.2.6 1 0 0 0 0 0) # Request [older major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 2.5.0 1 0 0 0 0 0) # Request [older major].[same minor].0 +test_write_basic_config_version_file(4.5.6.7 2.5.2 1 0 0 0 0 0) # Request [older major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 2.5.6 1 0 0 0 0 0) # Request [older major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 2.5.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 2.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].0 +test_write_basic_config_version_file(4.5.6.7 2.9.2 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 2.9.6 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 2.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 4.0.0 1 1 0 0 0 1) # Request [same major].0.0 +test_write_basic_config_version_file(4.5.6.7 4.0.2 1 1 0 0 0 1) # Request [same major].0.[older patch] +test_write_basic_config_version_file(4.5.6.7 4.0.6 1 1 0 0 0 1) # Request [same major].0.[same patch] +test_write_basic_config_version_file(4.5.6.7 4.0.9 1 1 0 0 0 1) # Request [same major].0.[newer patch] +test_write_basic_config_version_file(4.5.6.7 4.2.0 1 1 0 0 0 1) # Request [same major].[older minor].0 +test_write_basic_config_version_file(4.5.6.7 4.2.2 1 1 0 0 0 1) # Request [same major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 4.2.6 1 1 0 0 0 1) # Request [same major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 4.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 4.5.0 1 1 1 0 0 1) # Request [same major].[same minor].0 +test_write_basic_config_version_file(4.5.6.7 4.5.2 1 1 1 0 0 1) # Request [same major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 4.5.6 1 1 1 1 0 1) # Request [same major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 4.5.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 4.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].0 +test_write_basic_config_version_file(4.5.6.7 4.9.2 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 4.9.6 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 4.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 9.0.0 0 0 0 0 0 0) # Request [newer major].0.0 +test_write_basic_config_version_file(4.5.6.7 9.0.2 0 0 0 0 0 0) # Request [newer major].0.[older patch] +test_write_basic_config_version_file(4.5.6.7 9.0.6 0 0 0 0 0 0) # Request [newer major].0.[same patch] +test_write_basic_config_version_file(4.5.6.7 9.0.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch] +test_write_basic_config_version_file(4.5.6.7 9.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].0 +test_write_basic_config_version_file(4.5.6.7 9.2.2 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 9.2.6 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 9.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 9.5.0 0 0 0 0 0 0) # Request [newer major].[same minor].0 +test_write_basic_config_version_file(4.5.6.7 9.5.2 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 9.5.6 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 9.5.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.6.7 9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0 +test_write_basic_config_version_file(4.5.6.7 9.9.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.6.7 9.9.6 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.6.7 9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch] + +test_write_basic_config_version_file(4.5.6.7 0.0.0.0 1 0 0 0 0 0) # Request 0.0.0.0 +test_write_basic_config_version_file(4.5.6.7 0.0.0.2 1 0 0 0 0 0) # Request 0.0.0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.0.7 1 0 0 0 0 0) # Request 0.0.0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.0.9 1 0 0 0 0 0) # Request 0.0.0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.2.0 1 0 0 0 0 0) # Request 0.0.[older patch].0 +test_write_basic_config_version_file(4.5.6.7 0.0.2.2 1 0 0 0 0 0) # Request 0.0.[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.2.7 1 0 0 0 0 0) # Request 0.0.[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.2.9 1 0 0 0 0 0) # Request 0.0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.6.0 1 0 0 0 0 0) # Request 0.0.[same patch].0 +test_write_basic_config_version_file(4.5.6.7 0.0.6.2 1 0 0 0 0 0) # Request 0.0.[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.6.7 1 0 0 0 0 0) # Request 0.0.[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.6.9 1 0 0 0 0 0) # Request 0.0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.9.0 1 0 0 0 0 0) # Request 0.0.[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 0.0.9.2 1 0 0 0 0 0) # Request 0.0.[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.9.7 1 0 0 0 0 0) # Request 0.0.[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.0.9.9 1 0 0 0 0 0) # Request 0.0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.0.0 1 0 0 0 0 0) # Request 0.[older minor].0.0 +test_write_basic_config_version_file(4.5.6.7 0.2.0.2 1 0 0 0 0 0) # Request 0.[older minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.0.7 1 0 0 0 0 0) # Request 0.[older minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.0.9 1 0 0 0 0 0) # Request 0.[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.2.0 1 0 0 0 0 0) # Request 0.[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 0.2.2.2 1 0 0 0 0 0) # Request 0.[older minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.2.7 1 0 0 0 0 0) # Request 0.[older minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.2.9 1 0 0 0 0 0) # Request 0.[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.6.0 1 0 0 0 0 0) # Request 0.[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 0.2.6.2 1 0 0 0 0 0) # Request 0.[older minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.6.7 1 0 0 0 0 0) # Request 0.[older minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.6.9 1 0 0 0 0 0) # Request 0.[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.9.0 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 0.2.9.2 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.9.7 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.2.9.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.0.0 1 0 0 0 0 0) # Request 0.[same minor].0.0 +test_write_basic_config_version_file(4.5.6.7 0.5.0.2 1 0 0 0 0 0) # Request 0.[same minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.0.7 1 0 0 0 0 0) # Request 0.[same minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.0.9 1 0 0 0 0 0) # Request 0.[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.2.0 1 0 0 0 0 0) # Request 0.[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 0.5.2.2 1 0 0 0 0 0) # Request 0.[same minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.2.7 1 0 0 0 0 0) # Request 0.[same minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.2.9 1 0 0 0 0 0) # Request 0.[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.6.0 1 0 0 0 0 0) # Request 0.[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 0.5.6.2 1 0 0 0 0 0) # Request 0.[same minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.6.7 1 0 0 0 0 0) # Request 0.[same minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.6.9 1 0 0 0 0 0) # Request 0.[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.9.0 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 0.5.9.2 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.9.7 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.5.9.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.0.0 1 0 0 0 0 0) # Request 0.[newer minor].0.0 +test_write_basic_config_version_file(4.5.6.7 0.9.0.2 1 0 0 0 0 0) # Request 0.[newer minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.0.7 1 0 0 0 0 0) # Request 0.[newer minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.0.9 1 0 0 0 0 0) # Request 0.[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.2.0 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 0.9.2.2 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.2.7 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.2.9 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.6.0 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 0.9.6.2 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.6.7 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.6.9 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.9.0 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 0.9.9.2 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.9.7 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 0.9.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.0.0 1 0 0 0 0 0) # Request [older major].0.0.0 +test_write_basic_config_version_file(4.5.6.7 2.0.0.2 1 0 0 0 0 0) # Request [older major].0.0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.0.7 1 0 0 0 0 0) # Request [older major].0.0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.0.9 1 0 0 0 0 0) # Request [older major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.2.0 1 0 0 0 0 0) # Request [older major].0.[older patch].0 +test_write_basic_config_version_file(4.5.6.7 2.0.2.2 1 0 0 0 0 0) # Request [older major].0.[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.2.7 1 0 0 0 0 0) # Request [older major].0.[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.2.9 1 0 0 0 0 0) # Request [older major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.6.0 1 0 0 0 0 0) # Request [older major].0.[same patch].0 +test_write_basic_config_version_file(4.5.6.7 2.0.6.2 1 0 0 0 0 0) # Request [older major].0.[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.6.7 1 0 0 0 0 0) # Request [older major].0.[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.6.9 1 0 0 0 0 0) # Request [older major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.9.0 1 0 0 0 0 0) # Request [older major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 2.0.9.2 1 0 0 0 0 0) # Request [older major].0.[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.9.7 1 0 0 0 0 0) # Request [older major].0.[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.0.9.9 1 0 0 0 0 0) # Request [older major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.0.0 1 0 0 0 0 0) # Request [older major].[older minor].0.0 +test_write_basic_config_version_file(4.5.6.7 2.2.0.2 1 0 0 0 0 0) # Request [older major].[older minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.0.7 1 0 0 0 0 0) # Request [older major].[older minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.0.9 1 0 0 0 0 0) # Request [older major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 2.2.2.2 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.2.7 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.6.0 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 2.2.6.2 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.6.7 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.6.9 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.9.0 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 2.2.9.2 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.9.7 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.2.9.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.0.0 1 0 0 0 0 0) # Request [older major].[same minor].0.0 +test_write_basic_config_version_file(4.5.6.7 2.5.0.2 1 0 0 0 0 0) # Request [older major].[same minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.0.7 1 0 0 0 0 0) # Request [older major].[same minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.0.9 1 0 0 0 0 0) # Request [older major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.2.0 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 2.5.2.2 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.2.7 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.2.9 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.6.0 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 2.5.6.2 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.6.7 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.6.9 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.9.0 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 2.5.9.2 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.9.7 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.5.9.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.0.0 1 0 0 0 0 0) # Request [older major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.6.7 2.9.0.2 1 0 0 0 0 0) # Request [older major].[newer minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.0.7 1 0 0 0 0 0) # Request [older major].[newer minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.0.9 1 0 0 0 0 0) # Request [older major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.2.0 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 2.9.2.2 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.2.7 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.2.9 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.6.0 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 2.9.6.2 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.6.7 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.6.9 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 2.9.9.2 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.9.7 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 2.9.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.0.0 1 1 0 0 0 1) # Request [same major].0.0.0 +test_write_basic_config_version_file(4.5.6.7 4.0.0.2 1 1 0 0 0 1) # Request [same major].0.0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.0.7 1 1 0 0 0 1) # Request [same major].0.0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.0.9 1 1 0 0 0 1) # Request [same major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.2.0 1 1 0 0 0 1) # Request [same major].0.[older patch].0 +test_write_basic_config_version_file(4.5.6.7 4.0.2.2 1 1 0 0 0 1) # Request [same major].0.[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.2.7 1 1 0 0 0 1) # Request [same major].0.[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.2.9 1 1 0 0 0 1) # Request [same major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.6.0 1 1 0 0 0 1) # Request [same major].0.[same patch].0 +test_write_basic_config_version_file(4.5.6.7 4.0.6.2 1 1 0 0 0 1) # Request [same major].0.[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.6.7 1 1 0 0 0 1) # Request [same major].0.[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.6.9 1 1 0 0 0 1) # Request [same major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.9.0 1 1 0 0 0 1) # Request [same major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 4.0.9.2 1 1 0 0 0 1) # Request [same major].0.[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.9.7 1 1 0 0 0 1) # Request [same major].0.[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.0.9.9 1 1 0 0 0 1) # Request [same major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.0.0 1 1 0 0 0 1) # Request [same major].[older minor].0.0 +test_write_basic_config_version_file(4.5.6.7 4.2.0.2 1 1 0 0 0 1) # Request [same major].[older minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.0.7 1 1 0 0 0 1) # Request [same major].[older minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.0.9 1 1 0 0 0 1) # Request [same major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.2.0 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 4.2.2.2 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.2.7 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.6.0 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 4.2.6.2 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.6.7 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.6.9 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.9.0 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 4.2.9.2 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.9.7 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.2.9.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.0.0 1 1 1 0 0 1) # Request [same major].[same minor].0.0 +test_write_basic_config_version_file(4.5.6.7 4.5.0.2 1 1 1 0 0 1) # Request [same major].[same minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.0.7 1 1 1 0 0 1) # Request [same major].[same minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.0.9 1 1 1 0 0 1) # Request [same major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.2.0 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 4.5.2.2 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.2.7 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.2.9 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.6.0 1 1 1 1 0 1) # Request [same major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 4.5.6.2 1 1 1 1 0 1) # Request [same major].[same minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.6.7 1 1 1 1 1 1) # Request [same major].[same minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.6.9 0 0 0 1 0 0) # Request [same major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.9.0 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 4.5.9.2 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.9.7 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.5.9.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.0.0 0 0 0 0 0 0) # Request [same major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.6.7 4.9.0.2 0 0 0 0 0 0) # Request [same major].[newer minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.0.7 0 0 0 0 0 0) # Request [same major].[newer minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.0.9 0 0 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.2.0 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 4.9.2.2 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.2.7 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.2.9 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.6.0 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 4.9.6.2 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.6.7 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.6.9 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 4.9.9.2 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.9.7 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 4.9.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.0.0 0 0 0 0 0 0) # Request [newer major].0.0.0 +test_write_basic_config_version_file(4.5.6.7 9.0.0.2 0 0 0 0 0 0) # Request [newer major].0.0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.0.7 0 0 0 0 0 0) # Request [newer major].0.0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.0.9 0 0 0 0 0 0) # Request [newer major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.2.0 0 0 0 0 0 0) # Request [newer major].0.[older patch].0 +test_write_basic_config_version_file(4.5.6.7 9.0.2.2 0 0 0 0 0 0) # Request [newer major].0.[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.2.7 0 0 0 0 0 0) # Request [newer major].0.[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.2.9 0 0 0 0 0 0) # Request [newer major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.6.0 0 0 0 0 0 0) # Request [newer major].0.[same patch].0 +test_write_basic_config_version_file(4.5.6.7 9.0.6.2 0 0 0 0 0 0) # Request [newer major].0.[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.6.7 0 0 0 0 0 0) # Request [newer major].0.[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.6.9 0 0 0 0 0 0) # Request [newer major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.9.0 0 0 0 0 0 0) # Request [newer major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 9.0.9.2 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.9.7 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.0.9.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.0.0 0 0 0 0 0 0) # Request [newer major].[older minor].0.0 +test_write_basic_config_version_file(4.5.6.7 9.2.0.2 0 0 0 0 0 0) # Request [newer major].[older minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.0.7 0 0 0 0 0 0) # Request [newer major].[older minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.0.9 0 0 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 9.2.2.2 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.2.7 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.6.0 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 9.2.6.2 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.6.7 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.6.9 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.9.0 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 9.2.9.2 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.9.7 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.2.9.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.0.0 0 0 0 0 0 0) # Request [newer major].[same minor].0.0 +test_write_basic_config_version_file(4.5.6.7 9.5.0.2 0 0 0 0 0 0) # Request [newer major].[same minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.0.7 0 0 0 0 0 0) # Request [newer major].[same minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.0.9 0 0 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.2.0 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 9.5.2.2 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.2.7 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.2.9 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.6.0 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 9.5.6.2 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.6.7 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.6.9 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.9.0 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 9.5.9.2 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.9.7 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.5.9.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.0.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.6.7 9.9.0.2 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.0.7 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.0.9 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.2.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.6.7 9.9.2.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.2.7 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.2.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.6.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.6.7 9.9.6.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.6.7 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.6.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.6.7 9.9.9.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.9.7 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.5.6.7 9.9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] + + +test_write_basic_config_version_file(0 0...1 1 0 0 0 0 0) +test_write_basic_config_version_file(0 0...<1 1 1 0 1 0 0) + + +test_write_basic_config_version_file(4 0...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2...4 1 0 0 0 0 0) +test_write_basic_config_version_file(4 4...<5 1 1 0 0 0 1) +test_write_basic_config_version_file(4 9...10 0 0 0 0 0 0) + +test_write_basic_config_version_file(4 0.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2.8...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2.1...4 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2.8...4 1 0 0 0 0 0) +test_write_basic_config_version_file(4 4.0...<5 1 1 0 0 0 1) +test_write_basic_config_version_file(4 4.8...<5 0 0 0 0 0 0) +test_write_basic_config_version_file(4 4.1...5 0 0 0 0 0 0) +test_write_basic_config_version_file(4 4.8...5 0 0 0 0 0 0) +test_write_basic_config_version_file(4 9.1...10 0 0 0 0 0 0) +test_write_basic_config_version_file(4 9.8...10 0 0 0 0 0 0) + + +test_write_basic_config_version_file(0.5 0.1...<0.6 1 1 0 0 0 0) +test_write_basic_config_version_file(0.5 0.1...1 1 0 0 0 0 0) + +test_write_basic_config_version_file(0.5.6.7 0.5...<0.6 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.6.7 0.2...<0.6 1 1 0 0 0 0) + + +test_write_basic_config_version_file(4.5 0.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5 2.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5 2.8...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5 2.1...4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5 2.8...4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5 2.8...4.8 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5 4.1...<5 1 1 0 0 0 1) +test_write_basic_config_version_file(4.5 4.8...<5 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5 4.5...4.5.8 1 1 1 0 0 1) +test_write_basic_config_version_file(4.5 4.5...<4.6 1 1 1 0 0 1) +test_write_basic_config_version_file(4.5 4.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5 4.8...5 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5 9.1...10 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5 9.8...10 0 0 0 0 0 0) + + +test_write_basic_config_version_file(0.5.6 0.5...<0.6 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.6 0.2...<0.6 1 1 0 0 0 0) + + +test_write_basic_config_version_file(4.5.6 0.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 2.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 2.8...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 2.1...4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 2.8...4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 2.8...4.8 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 4.1...<5 1 1 0 0 0 1) +test_write_basic_config_version_file(4.5.6 4.8...<5 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 4.5...4.5.4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 4.5...4.5.8 1 1 1 0 0 1) +test_write_basic_config_version_file(4.5.6 4.5...<4.6 1 1 1 0 0 1) +test_write_basic_config_version_file(4.5.6 4.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 4.8...5 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 9.1...10 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.6 9.8...10 0 0 0 0 0 0)
diff --git a/Tests/RunCMake/WriteBasicConfigVersionFile/ArchIndependent.cmake b/Tests/RunCMake/WriteBasicConfigVersionFile/ArchIndependent.cmake index 13ec5d1..211d3ae 100644 --- a/Tests/RunCMake/WriteBasicConfigVersionFile/ArchIndependent.cmake +++ b/Tests/RunCMake/WriteBasicConfigVersionFile/ArchIndependent.cmake
@@ -7,8 +7,10 @@ set(_compatibilities AnyNewerVersion SameMajorVersion + SemanticVersion SameMinorVersion - ExactVersion) + SamePatchVersion + SameFullVersion) function(test_write_basic_config_version_file_arch_prepare filename_out compat arch_independent arch) if(arch_independent)
diff --git a/Tests/RunCMake/WriteBasicConfigVersionFile/ExactVersionDeprecatedAlias.cmake b/Tests/RunCMake/WriteBasicConfigVersionFile/ExactVersionDeprecatedAlias.cmake new file mode 100644 index 0000000..43c8e67 --- /dev/null +++ b/Tests/RunCMake/WriteBasicConfigVersionFile/ExactVersionDeprecatedAlias.cmake
@@ -0,0 +1,25 @@ +include(WriteBasicConfigVersionFile) + +# Verify that ExactVersion remains accepted as a deprecated alias and +# generates the same config version file as SamePatchVersion. +set(_installed_version 1.2.3.4) +set(_exact_file "${CMAKE_CURRENT_BINARY_DIR}/ExactVersionConfigVersion.cmake") +set(_same_patch_file "${CMAKE_CURRENT_BINARY_DIR}/SamePatchVersionConfigVersion.cmake") + +write_basic_config_version_file( + "${_exact_file}" + VERSION "${_installed_version}" + COMPATIBILITY ExactVersion) + +write_basic_config_version_file( + "${_same_patch_file}" + VERSION "${_installed_version}" + COMPATIBILITY SamePatchVersion) + +file(READ "${_exact_file}" _exact_content) +file(READ "${_same_patch_file}" _same_patch_content) + +if(NOT _exact_content STREQUAL _same_patch_content) + message(SEND_ERROR + "ExactVersion should generate the same config version file as SamePatchVersion.") +endif()
diff --git a/Tests/RunCMake/WriteBasicConfigVersionFile/LeadingZeroes.cmake b/Tests/RunCMake/WriteBasicConfigVersionFile/LeadingZeroes.cmake index 9f4674c..720331a 100644 --- a/Tests/RunCMake/WriteBasicConfigVersionFile/LeadingZeroes.cmake +++ b/Tests/RunCMake/WriteBasicConfigVersionFile/LeadingZeroes.cmake
@@ -5,8 +5,10 @@ set(COMPATIBILITIES AnyNewerVersion SameMajorVersion + SemanticVersion SameMinorVersion - ExactVersion + SamePatchVersion + SameFullVersion ) function(TEST_WRITE_BASIC_CONFIG_VERSION_FILE_PREPARE _version_installed) @@ -67,11 +69,15 @@ _expected_compatible_AnyNewerVersion _expected_compatible_SameMajorVersion _expected_compatible_SameMinorVersion - _expected_compatible_ExactVersion) + _expected_compatible_SamePatchVersion + _expected_compatible_SameFullVersion + _expected_compatible_SemanticVersion) if("${_version_requested}" MATCHES [[^([0-9]+(\.[0-9]+)*)\.\.\.(<)?([0-9]+(\.[0-9]+)*)$]]) set (_compatibilities ${COMPATIBILITIES}) - # ExactVersion must not be tested - list(POP_BACK _compatibilities) + # SamePatchVersion does not support ranges. + # SameFullVersion warns and uses only the lower endpoint, so it is also + # excluded from the range matrix. + list(REMOVE_ITEM _compatibilities SamePatchVersion SameFullVersion) set(PACKAGE_FIND_VERSION_RANGE TRUE) set(PACKAGE_FIND_VERSION_RANGE_MIN INCLUDE) if ("${CMAKE_MATCH_3}" STREQUAL "<") @@ -155,744 +161,771 @@ test_write_basic_config_version_file_prepare(4) +test_write_basic_config_version_file_prepare(0.05) test_write_basic_config_version_file_prepare(4.05) +test_write_basic_config_version_file_prepare(0.5.06) test_write_basic_config_version_file_prepare(4.5.06) +test_write_basic_config_version_file_prepare(0.05.06.007) test_write_basic_config_version_file_prepare(4.05.06.007) - -# AnyNewerVersion -# | SameMajorVersion -# | | SameMinorVersion -# | | | ExactVersion -# | | | | -test_write_basic_config_version_file(4.05 0 1 0 0 0) # Request 0 -test_write_basic_config_version_file(4.05 2 1 0 0 0) # Request [older major] -test_write_basic_config_version_file(4.05 4 1 1 0 0) # Request [same major] -test_write_basic_config_version_file(4.05 9 0 0 0 0) # Request [newer major] - -test_write_basic_config_version_file(4.05 0.0 1 0 0 0) # Request 0.0 -test_write_basic_config_version_file(4.05 0.2 1 0 0 0) # Request 0.[older minor] -test_write_basic_config_version_file(4.05 0.5 1 0 0 0) # Request 0.[same minor] -test_write_basic_config_version_file(4.05 0.9 1 0 0 0) # Request 0.[newer minor] -test_write_basic_config_version_file(4.05 2.0 1 0 0 0) # Request [older major].0 -test_write_basic_config_version_file(4.05 2.2 1 0 0 0) # Request [older major].[older minor] -test_write_basic_config_version_file(4.05 2.5 1 0 0 0) # Request [older major].[same minor] -test_write_basic_config_version_file(4.05 2.9 1 0 0 0) # Request [older major].[newer minor] -test_write_basic_config_version_file(4.05 4.0 1 1 0 0) # Request [same major].0 -test_write_basic_config_version_file(4.05 4.2 1 1 0 0) # Request [same major].[older minor] -test_write_basic_config_version_file(4.05 4.05 1 1 1 1) # Request [same major].[same minor] -test_write_basic_config_version_file(4.05 4.9 0 0 0 0) # Request [same major].[newer minor] -test_write_basic_config_version_file(4.05 9.0 0 0 0 0) # Request [newer major].0 -test_write_basic_config_version_file(4.05 9.1 0 0 0 0) # Request [newer major].[older minor] -test_write_basic_config_version_file(4.05 9.5 0 0 0 0) # Request [newer major].[same minor] -test_write_basic_config_version_file(4.05 9.9 0 0 0 0) # Request [newer major].[newer minor] - -test_write_basic_config_version_file(4.05 0.0.0 1 0 0 0) # Request 0.0.0 -test_write_basic_config_version_file(4.05 0.0.9 1 0 0 0) # Request 0.0.[newer patch] -test_write_basic_config_version_file(4.05 0.2.0 1 0 0 0) # Request 0.[older minor].0 -test_write_basic_config_version_file(4.05 0.2.9 1 0 0 0) # Request 0.[older minor].[newer patch] -test_write_basic_config_version_file(4.05 0.5.0 1 0 0 0) # Request 0.[same minor].0 -test_write_basic_config_version_file(4.05 0.5.9 1 0 0 0) # Request 0.[same minor].[newer patch] -test_write_basic_config_version_file(4.05 0.9.0 1 0 0 0) # Request 0.[newer minor].0 -test_write_basic_config_version_file(4.05 0.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch] -test_write_basic_config_version_file(4.05 2.0.0 1 0 0 0) # Request [older major].0.0 -test_write_basic_config_version_file(4.05 2.0.9 1 0 0 0) # Request [older major].0.[newer patch] -test_write_basic_config_version_file(4.05 2.2.0 1 0 0 0) # Request [older major].[older minor].0 -test_write_basic_config_version_file(4.05 2.2.9 1 0 0 0) # Request [older major].[older minor].[newer patch] -test_write_basic_config_version_file(4.05 2.5.0 1 0 0 0) # Request [older major].[same minor].0 -test_write_basic_config_version_file(4.05 2.5.9 1 0 0 0) # Request [older major].[same minor].[newer patch] -test_write_basic_config_version_file(4.05 2.9.0 1 0 0 0) # Request [older major].[newer minor].0 -test_write_basic_config_version_file(4.05 2.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.05 4.0.0 1 1 0 0) # Request [same major].0.0 -test_write_basic_config_version_file(4.05 4.0.9 1 1 0 0) # Request [same major].0.[newer patch] -test_write_basic_config_version_file(4.05 4.2.0 1 1 0 0) # Request [same major].[older minor].0 -test_write_basic_config_version_file(4.05 4.2.9 1 1 0 0) # Request [same major].[older minor].[newer patch] -test_write_basic_config_version_file(4.05 4.05.0 1 1 1 0) # Request [same major].[same minor].0 -test_write_basic_config_version_file(4.05 4.5.9 0 0 0 0) # Request [same major].[same minor].[newer patch] -test_write_basic_config_version_file(4.05 4.9.0 0 0 0 0) # Request [same major].[newer minor].0 -test_write_basic_config_version_file(4.05 4.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.05 9.0.0 0 0 0 0) # Request [newer major].0.0 -test_write_basic_config_version_file(4.05 9.0.9 0 0 0 0) # Request [newer major].0.[newer patch] -test_write_basic_config_version_file(4.05 9.2.0 0 0 0 0) # Request [newer major].[older minor].0 -test_write_basic_config_version_file(4.05 9.2.9 0 0 0 0) # Request [newer major].[older minor].[newer patch] -test_write_basic_config_version_file(4.05 9.5.0 0 0 0 0) # Request [newer major].[same minor].0 -test_write_basic_config_version_file(4.05 9.5.9 0 0 0 0) # Request [newer major].[same minor].[newer patch] -test_write_basic_config_version_file(4.05 9.9.0 0 0 0 0) # Request [newer major].[newer minor].0 -test_write_basic_config_version_file(4.05 9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch] - -test_write_basic_config_version_file(4.05 0.0.0.0 1 0 0 0) # Request 0.0.0.0 -test_write_basic_config_version_file(4.05 0.0.0.9 1 0 0 0) # Request 0.0.0.[newer tweak] -test_write_basic_config_version_file(4.05 0.0.9.0 1 0 0 0) # Request 0.0.[newer patch].0 -test_write_basic_config_version_file(4.05 0.0.9.9 1 0 0 0) # Request 0.0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 0.2.0.0 1 0 0 0) # Request 0.[older minor].0.0 -test_write_basic_config_version_file(4.05 0.2.0.9 1 0 0 0) # Request 0.[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 0.2.9.0 1 0 0 0) # Request 0.[older minor].[newer patch].0 -test_write_basic_config_version_file(4.05 0.2.9.9 1 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 0.5.0.0 1 0 0 0) # Request 0.[same minor].0.0 -test_write_basic_config_version_file(4.05 0.5.0.9 1 0 0 0) # Request 0.[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 0.5.9.0 1 0 0 0) # Request 0.[same minor].[newer patch].0 -test_write_basic_config_version_file(4.05 0.5.9.9 1 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 0.9.0.0 1 0 0 0) # Request 0.[newer minor].0.0 -test_write_basic_config_version_file(4.05 0.9.0.9 1 0 0 0) # Request 0.[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 0.9.9.0 1 0 0 0) # Request 0.[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.05 0.9.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 2.0.0.0 1 0 0 0) # Request [older major].0.0.0 -test_write_basic_config_version_file(4.05 2.0.0.9 1 0 0 0) # Request [older major].0.0.[newer tweak] -test_write_basic_config_version_file(4.05 2.0.9.0 1 0 0 0) # Request [older major].0.[newer patch].0 -test_write_basic_config_version_file(4.05 2.0.9.9 1 0 0 0) # Request [older major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 2.2.0.0 1 0 0 0) # Request [older major].[older minor].0.0 -test_write_basic_config_version_file(4.05 2.2.0.9 1 0 0 0) # Request [older major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 2.2.9.0 1 0 0 0) # Request [older major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.05 2.2.9.9 1 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 2.5.0.0 1 0 0 0) # Request [older major].[same minor].0.0 -test_write_basic_config_version_file(4.05 2.5.0.9 1 0 0 0) # Request [older major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 2.5.9.0 1 0 0 0) # Request [older major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.05 2.5.9.9 1 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 2.9.0.0 1 0 0 0) # Request [older major].[newer minor].0.0 -test_write_basic_config_version_file(4.05 2.9.0.9 1 0 0 0) # Request [older major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 2.9.9.0 1 0 0 0) # Request [older major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.05 2.9.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 4.0.0.0 1 1 0 0) # Request [same major].0.0.0 -test_write_basic_config_version_file(4.05 4.0.0.9 1 1 0 0) # Request [same major].0.0.[newer tweak] -test_write_basic_config_version_file(4.05 4.0.9.0 1 1 0 0) # Request [same major].0.[newer patch].0 -test_write_basic_config_version_file(4.05 4.0.9.9 1 1 0 0) # Request [same major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 4.2.0.0 1 1 0 0) # Request [same major].[older minor].0.0 -test_write_basic_config_version_file(4.05 4.2.0.9 1 1 0 0) # Request [same major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 4.2.9.0 1 1 0 0) # Request [same major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.05 4.2.9.9 1 1 0 0) # Request [same major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 4.5.0.0 1 1 1 0) # Request [same major].[same minor].0.0 -test_write_basic_config_version_file(4.05 4.5.0.9 0 0 0 0) # Request [same major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 4.5.9.0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.05 4.5.9.9 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 4.9.0.0 0 0 0 0) # Request [same major].[newer minor].0.0 -test_write_basic_config_version_file(4.05 4.9.0.9 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 4.9.9.0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.05 4.9.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 9.0.0.0 0 0 0 0) # Request [newer major].0.0.0 -test_write_basic_config_version_file(4.05 9.0.0.9 0 0 0 0) # Request [newer major].0.0.[newer tweak] -test_write_basic_config_version_file(4.05 9.0.9.0 0 0 0 0) # Request [newer major].0.[newer patch].0 -test_write_basic_config_version_file(4.05 9.0.9.9 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 9.2.0.0 0 0 0 0) # Request [newer major].[older minor].0.0 -test_write_basic_config_version_file(4.05 9.2.0.9 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 9.2.9.0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.05 9.2.9.9 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 9.5.0.0 0 0 0 0) # Request [newer major].[same minor].0.0 -test_write_basic_config_version_file(4.05 9.5.0.9 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 9.5.9.0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.05 9.5.9.9 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05 9.9.0.0 0 0 0 0) # Request [newer major].[newer minor].0.0 -test_write_basic_config_version_file(4.05 9.9.0.9 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.05 9.9.9.0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.05 9.9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] +# AnyNewerVersion +# | SameMajorVersion +# | | SameMinorVersion +# | | | SamePatchVersion +# | | | | SameFullVersion +# | | | | | SemanticVersion +# | | | | | | +test_write_basic_config_version_file(0.05 0 1 1 0 0 0 0) +test_write_basic_config_version_file(0.05 0.2 1 1 0 0 0 0) +test_write_basic_config_version_file(0.05 0.5 1 1 1 0 0 1) +test_write_basic_config_version_file(0.05 0.9 0 0 0 0 0 0) -test_write_basic_config_version_file(4.5.06 0 1 0 0 0) # Request 0 -test_write_basic_config_version_file(4.5.06 2 1 0 0 0) # Request [older major] -test_write_basic_config_version_file(4.5.06 4 1 1 0 0) # Request [same major] -test_write_basic_config_version_file(4.5.06 9 0 0 0 0) # Request [newer major] +test_write_basic_config_version_file(4.05 0 1 0 0 0 0 0) # Request 0 +test_write_basic_config_version_file(4.05 2 1 0 0 0 0 0) # Request [older major] +test_write_basic_config_version_file(4.05 4 1 1 0 0 0 1) # Request [same major] +test_write_basic_config_version_file(4.05 9 0 0 0 0 0 0) # Request [newer major] -test_write_basic_config_version_file(4.5.06 0.0 1 0 0 0) # Request 0.0 -test_write_basic_config_version_file(4.5.06 0.2 1 0 0 0) # Request 0.[older minor] -test_write_basic_config_version_file(4.5.06 0.5 1 0 0 0) # Request 0.[same minor] -test_write_basic_config_version_file(4.5.06 0.9 1 0 0 0) # Request 0.[newer minor] -test_write_basic_config_version_file(4.5.06 2.0 1 0 0 0) # Request [older major].0 -test_write_basic_config_version_file(4.5.06 2.2 1 0 0 0) # Request [older major].[older minor] -test_write_basic_config_version_file(4.5.06 2.5 1 0 0 0) # Request [older major].[same minor] -test_write_basic_config_version_file(4.5.06 2.9 1 0 0 0) # Request [older major].[newer minor] -test_write_basic_config_version_file(4.5.06 4.0 1 1 0 0) # Request [same major].0 -test_write_basic_config_version_file(4.5.06 4.2 1 1 0 0) # Request [same major].[older minor] -test_write_basic_config_version_file(4.5.06 4.5 1 1 1 0) # Request [same major].[same minor] -test_write_basic_config_version_file(4.5.06 4.9 0 0 0 0) # Request [same major].[newer minor] -test_write_basic_config_version_file(4.5.06 9.0 0 0 0 0) # Request [newer major].0 -test_write_basic_config_version_file(4.5.06 9.1 0 0 0 0) # Request [newer major].[older minor] -test_write_basic_config_version_file(4.5.06 9.5 0 0 0 0) # Request [newer major].[same minor] -test_write_basic_config_version_file(4.5.06 9.9 0 0 0 0) # Request [newer major].[newer minor] +test_write_basic_config_version_file(4.05 0.0 1 0 0 0 0 0) # Request 0.0 +test_write_basic_config_version_file(4.05 0.2 1 0 0 0 0 0) # Request 0.[older minor] +test_write_basic_config_version_file(4.05 0.5 1 0 0 0 0 0) # Request 0.[same minor] +test_write_basic_config_version_file(4.05 0.9 1 0 0 0 0 0) # Request 0.[newer minor] +test_write_basic_config_version_file(4.05 2.0 1 0 0 0 0 0) # Request [older major].0 +test_write_basic_config_version_file(4.05 2.2 1 0 0 0 0 0) # Request [older major].[older minor] +test_write_basic_config_version_file(4.05 2.5 1 0 0 0 0 0) # Request [older major].[same minor] +test_write_basic_config_version_file(4.05 2.9 1 0 0 0 0 0) # Request [older major].[newer minor] +test_write_basic_config_version_file(4.05 4.0 1 1 0 0 0 1) # Request [same major].0 +test_write_basic_config_version_file(4.05 4.2 1 1 0 0 0 1) # Request [same major].[older minor] +test_write_basic_config_version_file(4.05 4.05 1 1 1 1 1 1) # Request [same major].[same minor] +test_write_basic_config_version_file(4.05 4.9 0 0 0 0 0 0) # Request [same major].[newer minor] +test_write_basic_config_version_file(4.05 9.0 0 0 0 0 0 0) # Request [newer major].0 +test_write_basic_config_version_file(4.05 9.1 0 0 0 0 0 0) # Request [newer major].[older minor] +test_write_basic_config_version_file(4.05 9.5 0 0 0 0 0 0) # Request [newer major].[same minor] +test_write_basic_config_version_file(4.05 9.9 0 0 0 0 0 0) # Request [newer major].[newer minor] -test_write_basic_config_version_file(4.5.06 0.0.0 1 0 0 0) # Request 0.0.0 -test_write_basic_config_version_file(4.5.06 0.0.2 1 0 0 0) # Request 0.0.[older patch] -test_write_basic_config_version_file(4.5.06 0.0.6 1 0 0 0) # Request 0.0.[same patch] -test_write_basic_config_version_file(4.5.06 0.0.9 1 0 0 0) # Request 0.0.[newer patch] -test_write_basic_config_version_file(4.5.06 0.2.0 1 0 0 0) # Request 0.[older minor].0 -test_write_basic_config_version_file(4.5.06 0.2.2 1 0 0 0) # Request 0.[older minor].[older patch] -test_write_basic_config_version_file(4.5.06 0.2.6 1 0 0 0) # Request 0.[older minor].[same patch] -test_write_basic_config_version_file(4.5.06 0.2.9 1 0 0 0) # Request 0.[older minor].[newer patch] -test_write_basic_config_version_file(4.5.06 0.5.0 1 0 0 0) # Request 0.[same minor].0 -test_write_basic_config_version_file(4.5.06 0.5.2 1 0 0 0) # Request 0.[same minor].[older patch] -test_write_basic_config_version_file(4.5.06 0.5.6 1 0 0 0) # Request 0.[same minor].[same patch] -test_write_basic_config_version_file(4.5.06 0.5.9 1 0 0 0) # Request 0.[same minor].[newer patch] -test_write_basic_config_version_file(4.5.06 0.9.0 1 0 0 0) # Request 0.[newer minor].0 -test_write_basic_config_version_file(4.5.06 0.9.2 1 0 0 0) # Request 0.[newer minor].[older patch] -test_write_basic_config_version_file(4.5.06 0.9.6 1 0 0 0) # Request 0.[newer minor].[same patch] -test_write_basic_config_version_file(4.5.06 0.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.06 2.0.0 1 0 0 0) # Request [older major].0.0 -test_write_basic_config_version_file(4.5.06 2.0.2 1 0 0 0) # Request [older major].0.[older patch] -test_write_basic_config_version_file(4.5.06 2.0.6 1 0 0 0) # Request [older major].0.[same patch] -test_write_basic_config_version_file(4.5.06 2.0.9 1 0 0 0) # Request [older major].0.[newer patch] -test_write_basic_config_version_file(4.5.06 2.2.0 1 0 0 0) # Request [older major].[older minor].0 -test_write_basic_config_version_file(4.5.06 2.2.2 1 0 0 0) # Request [older major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.06 2.2.6 1 0 0 0) # Request [older major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.06 2.2.9 1 0 0 0) # Request [older major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.06 2.5.0 1 0 0 0) # Request [older major].[same minor].0 -test_write_basic_config_version_file(4.5.06 2.5.2 1 0 0 0) # Request [older major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.06 2.5.6 1 0 0 0) # Request [older major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.06 2.5.9 1 0 0 0) # Request [older major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.06 2.9.0 1 0 0 0) # Request [older major].[newer minor].0 -test_write_basic_config_version_file(4.5.06 2.9.2 1 0 0 0) # Request [older major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.06 2.9.6 1 0 0 0) # Request [older major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.06 2.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.06 4.0.0 1 1 0 0) # Request [same major].0.0 -test_write_basic_config_version_file(4.5.06 4.0.2 1 1 0 0) # Request [same major].0.[older patch] -test_write_basic_config_version_file(4.5.06 4.0.6 1 1 0 0) # Request [same major].0.[same patch] -test_write_basic_config_version_file(4.5.06 4.0.9 1 1 0 0) # Request [same major].0.[newer patch] -test_write_basic_config_version_file(4.5.06 4.2.0 1 1 0 0) # Request [same major].[older minor].0 -test_write_basic_config_version_file(4.5.06 4.2.2 1 1 0 0) # Request [same major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.06 4.2.6 1 1 0 0) # Request [same major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.06 4.2.9 1 1 0 0) # Request [same major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.06 4.5.0 1 1 1 0) # Request [same major].[same minor].0 -test_write_basic_config_version_file(4.5.06 4.5.2 1 1 1 0) # Request [same major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.06 4.5.06 1 1 1 1) # Request [same major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.06 4.5.9 0 0 0 0) # Request [same major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.06 4.9.0 0 0 0 0) # Request [same major].[newer minor].0 -test_write_basic_config_version_file(4.5.06 4.9.2 0 0 0 0) # Request [same major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.06 4.9.6 0 0 0 0) # Request [same major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.06 4.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.06 9.0.0 0 0 0 0) # Request [newer major].0.0 -test_write_basic_config_version_file(4.5.06 9.0.2 0 0 0 0) # Request [newer major].0.[older patch] -test_write_basic_config_version_file(4.5.06 9.0.6 0 0 0 0) # Request [newer major].0.[same patch] -test_write_basic_config_version_file(4.5.06 9.0.9 0 0 0 0) # Request [newer major].0.[newer patch] -test_write_basic_config_version_file(4.5.06 9.2.0 0 0 0 0) # Request [newer major].[older minor].0 -test_write_basic_config_version_file(4.5.06 9.2.2 0 0 0 0) # Request [newer major].[older minor].[older patch] -test_write_basic_config_version_file(4.5.06 9.2.6 0 0 0 0) # Request [newer major].[older minor].[same patch] -test_write_basic_config_version_file(4.5.06 9.2.9 0 0 0 0) # Request [newer major].[older minor].[newer patch] -test_write_basic_config_version_file(4.5.06 9.5.0 0 0 0 0) # Request [newer major].[same minor].0 -test_write_basic_config_version_file(4.5.06 9.5.2 0 0 0 0) # Request [newer major].[same minor].[older patch] -test_write_basic_config_version_file(4.5.06 9.5.6 0 0 0 0) # Request [newer major].[same minor].[same patch] -test_write_basic_config_version_file(4.5.06 9.5.9 0 0 0 0) # Request [newer major].[same minor].[newer patch] -test_write_basic_config_version_file(4.5.06 9.9.0 0 0 0 0) # Request [newer major].[newer minor].0 -test_write_basic_config_version_file(4.5.06 9.9.2 0 0 0 0) # Request [newer major].[newer minor].[older patch] -test_write_basic_config_version_file(4.5.06 9.9.6 0 0 0 0) # Request [newer major].[newer minor].[same patch] -test_write_basic_config_version_file(4.5.06 9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.05 0.0.0 1 0 0 0 0 0) # Request 0.0.0 +test_write_basic_config_version_file(4.05 0.0.9 1 0 0 0 0 0) # Request 0.0.[newer patch] +test_write_basic_config_version_file(4.05 0.2.0 1 0 0 0 0 0) # Request 0.[older minor].0 +test_write_basic_config_version_file(4.05 0.2.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch] +test_write_basic_config_version_file(4.05 0.5.0 1 0 0 0 0 0) # Request 0.[same minor].0 +test_write_basic_config_version_file(4.05 0.5.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch] +test_write_basic_config_version_file(4.05 0.9.0 1 0 0 0 0 0) # Request 0.[newer minor].0 +test_write_basic_config_version_file(4.05 0.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch] +test_write_basic_config_version_file(4.05 2.0.0 1 0 0 0 0 0) # Request [older major].0.0 +test_write_basic_config_version_file(4.05 2.0.9 1 0 0 0 0 0) # Request [older major].0.[newer patch] +test_write_basic_config_version_file(4.05 2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].0 +test_write_basic_config_version_file(4.05 2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch] +test_write_basic_config_version_file(4.05 2.5.0 1 0 0 0 0 0) # Request [older major].[same minor].0 +test_write_basic_config_version_file(4.05 2.5.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch] +test_write_basic_config_version_file(4.05 2.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].0 +test_write_basic_config_version_file(4.05 2.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.05 4.0.0 1 1 0 0 0 1) # Request [same major].0.0 +test_write_basic_config_version_file(4.05 4.0.9 1 1 0 0 0 1) # Request [same major].0.[newer patch] +test_write_basic_config_version_file(4.05 4.2.0 1 1 0 0 0 1) # Request [same major].[older minor].0 +test_write_basic_config_version_file(4.05 4.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch] +test_write_basic_config_version_file(4.05 4.05.0 1 1 1 0 0 1) # Request [same major].[same minor].0 +test_write_basic_config_version_file(4.05 4.5.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch] +test_write_basic_config_version_file(4.05 4.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].0 +test_write_basic_config_version_file(4.05 4.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.05 9.0.0 0 0 0 0 0 0) # Request [newer major].0.0 +test_write_basic_config_version_file(4.05 9.0.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch] +test_write_basic_config_version_file(4.05 9.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].0 +test_write_basic_config_version_file(4.05 9.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch] +test_write_basic_config_version_file(4.05 9.5.0 0 0 0 0 0 0) # Request [newer major].[same minor].0 +test_write_basic_config_version_file(4.05 9.5.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch] +test_write_basic_config_version_file(4.05 9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0 +test_write_basic_config_version_file(4.05 9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.5.06 0.0.0.0 1 0 0 0) # Request 0.0.0.0 -test_write_basic_config_version_file(4.5.06 0.0.0.9 1 0 0 0) # Request 0.0.0.[newer tweak] -test_write_basic_config_version_file(4.5.06 0.0.2.0 1 0 0 0) # Request 0.0.[older patch].0 -test_write_basic_config_version_file(4.5.06 0.0.2.9 1 0 0 0) # Request 0.0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.0.6.0 1 0 0 0) # Request 0.0.[same patch].0 -test_write_basic_config_version_file(4.5.06 0.0.6.9 1 0 0 0) # Request 0.0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.0.9.0 1 0 0 0) # Request 0.0.[newer patch].0 -test_write_basic_config_version_file(4.5.06 0.0.9.9 1 0 0 0) # Request 0.0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.2.0.0 1 0 0 0) # Request 0.[older minor].0.0 -test_write_basic_config_version_file(4.5.06 0.2.0.9 1 0 0 0) # Request 0.[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 0.2.2.0 1 0 0 0) # Request 0.[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 0.2.2.9 1 0 0 0) # Request 0.[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.2.6.0 1 0 0 0) # Request 0.[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 0.2.6.9 1 0 0 0) # Request 0.[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.2.9.0 1 0 0 0) # Request 0.[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 0.2.9.9 1 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.5.0.0 1 0 0 0) # Request 0.[same minor].0.0 -test_write_basic_config_version_file(4.5.06 0.5.0.9 1 0 0 0) # Request 0.[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 0.5.2.0 1 0 0 0) # Request 0.[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 0.5.2.9 1 0 0 0) # Request 0.[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.5.6.0 1 0 0 0) # Request 0.[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 0.5.6.9 1 0 0 0) # Request 0.[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.5.9.0 1 0 0 0) # Request 0.[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 0.5.9.9 1 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.9.0.0 1 0 0 0) # Request 0.[newer minor].0.0 -test_write_basic_config_version_file(4.5.06 0.9.0.9 1 0 0 0) # Request 0.[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 0.9.2.0 1 0 0 0) # Request 0.[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 0.9.2.9 1 0 0 0) # Request 0.[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.9.6.0 1 0 0 0) # Request 0.[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 0.9.6.9 1 0 0 0) # Request 0.[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.9.9.0 1 0 0 0) # Request 0.[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 0.9.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.0.0.0 1 0 0 0) # Request [older major].0.0.0 -test_write_basic_config_version_file(4.5.06 2.0.0.9 1 0 0 0) # Request [older major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.06 2.0.2.0 1 0 0 0) # Request [older major].0.[older patch].0 -test_write_basic_config_version_file(4.5.06 2.0.2.9 1 0 0 0) # Request [older major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.0.6.0 1 0 0 0) # Request [older major].0.[same patch].0 -test_write_basic_config_version_file(4.5.06 2.0.6.9 1 0 0 0) # Request [older major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.0.9.0 1 0 0 0) # Request [older major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.06 2.0.9.9 1 0 0 0) # Request [older major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.2.0.0 1 0 0 0) # Request [older major].[older minor].0.0 -test_write_basic_config_version_file(4.5.06 2.2.0.9 1 0 0 0) # Request [older major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 2.2.2.0 1 0 0 0) # Request [older major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 2.2.2.9 1 0 0 0) # Request [older major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.2.6.0 1 0 0 0) # Request [older major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 2.2.6.9 1 0 0 0) # Request [older major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.2.9.0 1 0 0 0) # Request [older major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 2.2.9.9 1 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.5.0.0 1 0 0 0) # Request [older major].[same minor].0.0 -test_write_basic_config_version_file(4.5.06 2.5.0.9 1 0 0 0) # Request [older major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 2.5.2.0 1 0 0 0) # Request [older major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 2.5.2.9 1 0 0 0) # Request [older major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.5.6.0 1 0 0 0) # Request [older major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 2.5.6.9 1 0 0 0) # Request [older major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.5.9.0 1 0 0 0) # Request [older major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 2.5.9.9 1 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.9.0.0 1 0 0 0) # Request [older major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.06 2.9.0.9 1 0 0 0) # Request [older major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 2.9.2.0 1 0 0 0) # Request [older major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 2.9.2.9 1 0 0 0) # Request [older major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.9.6.0 1 0 0 0) # Request [older major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 2.9.6.9 1 0 0 0) # Request [older major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 2.9.9.0 1 0 0 0) # Request [older major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 2.9.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.0.0.0 1 1 0 0) # Request [same major].0.0.0 -test_write_basic_config_version_file(4.5.06 4.0.0.9 1 1 0 0) # Request [same major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.06 4.0.2.0 1 1 0 0) # Request [same major].0.[older patch].0 -test_write_basic_config_version_file(4.5.06 4.0.2.9 1 1 0 0) # Request [same major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.0.6.0 1 1 0 0) # Request [same major].0.[same patch].0 -test_write_basic_config_version_file(4.5.06 4.0.6.9 1 1 0 0) # Request [same major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.0.9.0 1 1 0 0) # Request [same major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.06 4.0.9.9 1 1 0 0) # Request [same major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.2.0.0 1 1 0 0) # Request [same major].[older minor].0.0 -test_write_basic_config_version_file(4.5.06 4.2.0.9 1 1 0 0) # Request [same major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 4.2.2.0 1 1 0 0) # Request [same major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 4.2.2.9 1 1 0 0) # Request [same major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.2.6.0 1 1 0 0) # Request [same major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 4.2.6.9 1 1 0 0) # Request [same major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.2.9.0 1 1 0 0) # Request [same major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 4.2.9.9 1 1 0 0) # Request [same major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.5.0.0 1 1 1 0) # Request [same major].[same minor].0.0 -test_write_basic_config_version_file(4.5.06 4.5.0.9 1 1 1 0) # Request [same major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 4.5.2.0 1 1 1 0) # Request [same major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 4.5.2.9 1 1 1 0) # Request [same major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.5.06.0 1 1 1 1) # Request [same major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 4.5.06.9 0 0 0 1) # Request [same major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.5.9.0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 4.5.9.9 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.9.0.0 0 0 0 0) # Request [same major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.06 4.9.0.9 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 4.9.2.0 0 0 0 0) # Request [same major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 4.9.2.9 0 0 0 0) # Request [same major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.9.6.0 0 0 0 0) # Request [same major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 4.9.6.9 0 0 0 0) # Request [same major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 4.9.9.0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 4.9.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.0.0.0 0 0 0 0) # Request [newer major].0.0.0 -test_write_basic_config_version_file(4.5.06 9.0.0.9 0 0 0 0) # Request [newer major].0.0.[newer tweak] -test_write_basic_config_version_file(4.5.06 9.0.2.0 0 0 0 0) # Request [newer major].0.[older patch].0 -test_write_basic_config_version_file(4.5.06 9.0.2.9 0 0 0 0) # Request [newer major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.0.6.0 0 0 0 0) # Request [newer major].0.[same patch].0 -test_write_basic_config_version_file(4.5.06 9.0.6.9 0 0 0 0) # Request [newer major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.0.9.0 0 0 0 0) # Request [newer major].0.[newer patch].0 -test_write_basic_config_version_file(4.5.06 9.0.9.9 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.2.0.0 0 0 0 0) # Request [newer major].[older minor].0.0 -test_write_basic_config_version_file(4.5.06 9.2.0.9 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 9.2.2.0 0 0 0 0) # Request [newer major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 9.2.2.9 0 0 0 0) # Request [newer major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.2.6.0 0 0 0 0) # Request [newer major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 9.2.6.9 0 0 0 0) # Request [newer major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.2.9.0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 9.2.9.9 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.5.0.0 0 0 0 0) # Request [newer major].[same minor].0.0 -test_write_basic_config_version_file(4.5.06 9.5.0.9 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 9.5.2.0 0 0 0 0) # Request [newer major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 9.5.2.9 0 0 0 0) # Request [newer major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.5.6.0 0 0 0 0) # Request [newer major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 9.5.6.9 0 0 0 0) # Request [newer major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.5.9.0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 9.5.9.9 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.9.0.0 0 0 0 0) # Request [newer major].[newer minor].0.0 -test_write_basic_config_version_file(4.5.06 9.9.0.9 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.5.06 9.9.2.0 0 0 0 0) # Request [newer major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.5.06 9.9.2.9 0 0 0 0) # Request [newer major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.9.6.0 0 0 0 0) # Request [newer major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.5.06 9.9.6.9 0 0 0 0) # Request [newer major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 9.9.9.0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.5.06 9.9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 0.0.0.0 1 0 0 0 0 0) # Request 0.0.0.0 +test_write_basic_config_version_file(4.05 0.0.0.9 1 0 0 0 0 0) # Request 0.0.0.[newer tweak] +test_write_basic_config_version_file(4.05 0.0.9.0 1 0 0 0 0 0) # Request 0.0.[newer patch].0 +test_write_basic_config_version_file(4.05 0.0.9.9 1 0 0 0 0 0) # Request 0.0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 0.2.0.0 1 0 0 0 0 0) # Request 0.[older minor].0.0 +test_write_basic_config_version_file(4.05 0.2.0.9 1 0 0 0 0 0) # Request 0.[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 0.2.9.0 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].0 +test_write_basic_config_version_file(4.05 0.2.9.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 0.5.0.0 1 0 0 0 0 0) # Request 0.[same minor].0.0 +test_write_basic_config_version_file(4.05 0.5.0.9 1 0 0 0 0 0) # Request 0.[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 0.5.9.0 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].0 +test_write_basic_config_version_file(4.05 0.5.9.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 0.9.0.0 1 0 0 0 0 0) # Request 0.[newer minor].0.0 +test_write_basic_config_version_file(4.05 0.9.0.9 1 0 0 0 0 0) # Request 0.[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 0.9.9.0 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.05 0.9.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 2.0.0.0 1 0 0 0 0 0) # Request [older major].0.0.0 +test_write_basic_config_version_file(4.05 2.0.0.9 1 0 0 0 0 0) # Request [older major].0.0.[newer tweak] +test_write_basic_config_version_file(4.05 2.0.9.0 1 0 0 0 0 0) # Request [older major].0.[newer patch].0 +test_write_basic_config_version_file(4.05 2.0.9.9 1 0 0 0 0 0) # Request [older major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 2.2.0.0 1 0 0 0 0 0) # Request [older major].[older minor].0.0 +test_write_basic_config_version_file(4.05 2.2.0.9 1 0 0 0 0 0) # Request [older major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 2.2.9.0 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.05 2.2.9.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 2.5.0.0 1 0 0 0 0 0) # Request [older major].[same minor].0.0 +test_write_basic_config_version_file(4.05 2.5.0.9 1 0 0 0 0 0) # Request [older major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 2.5.9.0 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.05 2.5.9.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 2.9.0.0 1 0 0 0 0 0) # Request [older major].[newer minor].0.0 +test_write_basic_config_version_file(4.05 2.9.0.9 1 0 0 0 0 0) # Request [older major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 2.9.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.05 2.9.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 4.0.0.0 1 1 0 0 0 1) # Request [same major].0.0.0 +test_write_basic_config_version_file(4.05 4.0.0.9 1 1 0 0 0 1) # Request [same major].0.0.[newer tweak] +test_write_basic_config_version_file(4.05 4.0.9.0 1 1 0 0 0 1) # Request [same major].0.[newer patch].0 +test_write_basic_config_version_file(4.05 4.0.9.9 1 1 0 0 0 1) # Request [same major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 4.2.0.0 1 1 0 0 0 1) # Request [same major].[older minor].0.0 +test_write_basic_config_version_file(4.05 4.2.0.9 1 1 0 0 0 1) # Request [same major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 4.2.9.0 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.05 4.2.9.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 4.5.0.0 1 1 1 0 0 1) # Request [same major].[same minor].0.0 +test_write_basic_config_version_file(4.05 4.5.0.9 0 0 0 0 0 0) # Request [same major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 4.5.9.0 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.05 4.5.9.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 4.9.0.0 0 0 0 0 0 0) # Request [same major].[newer minor].0.0 +test_write_basic_config_version_file(4.05 4.9.0.9 0 0 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 4.9.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.05 4.9.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 9.0.0.0 0 0 0 0 0 0) # Request [newer major].0.0.0 +test_write_basic_config_version_file(4.05 9.0.0.9 0 0 0 0 0 0) # Request [newer major].0.0.[newer tweak] +test_write_basic_config_version_file(4.05 9.0.9.0 0 0 0 0 0 0) # Request [newer major].0.[newer patch].0 +test_write_basic_config_version_file(4.05 9.0.9.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 9.2.0.0 0 0 0 0 0 0) # Request [newer major].[older minor].0.0 +test_write_basic_config_version_file(4.05 9.2.0.9 0 0 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 9.2.9.0 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.05 9.2.9.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 9.5.0.0 0 0 0 0 0 0) # Request [newer major].[same minor].0.0 +test_write_basic_config_version_file(4.05 9.5.0.9 0 0 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 9.5.9.0 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.05 9.5.9.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05 9.9.0.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0.0 +test_write_basic_config_version_file(4.05 9.9.0.9 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.05 9.9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.05 9.9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0 1 0 0 0) # Request 0 -test_write_basic_config_version_file(4.05.06.007 2 1 0 0 0) # Request [older major] -test_write_basic_config_version_file(4.05.06.007 4 1 1 0 0) # Request [same major] -test_write_basic_config_version_file(4.05.06.007 9 0 0 0 0) # Request [newer major] - -test_write_basic_config_version_file(4.05.06.007 0.0 1 0 0 0) # Request 0.0 -test_write_basic_config_version_file(4.05.06.007 0.2 1 0 0 0) # Request 0.[older minor] -test_write_basic_config_version_file(4.05.06.007 0.5 1 0 0 0) # Request 0.[same minor] -test_write_basic_config_version_file(4.05.06.007 0.9 1 0 0 0) # Request 0.[newer minor] -test_write_basic_config_version_file(4.05.06.007 2.0 1 0 0 0) # Request [older major].0 -test_write_basic_config_version_file(4.05.06.007 2.2 1 0 0 0) # Request [older major].[older minor] -test_write_basic_config_version_file(4.05.06.007 2.5 1 0 0 0) # Request [older major].[same minor] -test_write_basic_config_version_file(4.05.06.007 2.9 1 0 0 0) # Request [older major].[newer minor] -test_write_basic_config_version_file(4.05.06.007 4.0 1 1 0 0) # Request [same major].0 -test_write_basic_config_version_file(4.05.06.007 4.2 1 1 0 0) # Request [same major].[older minor] -test_write_basic_config_version_file(4.05.06.007 4.5 1 1 1 0) # Request [same major].[same minor] -test_write_basic_config_version_file(4.05.06.007 4.9 0 0 0 0) # Request [same major].[newer minor] -test_write_basic_config_version_file(4.05.06.007 9.0 0 0 0 0) # Request [newer major].0 -test_write_basic_config_version_file(4.05.06.007 9.1 0 0 0 0) # Request [newer major].[older minor] -test_write_basic_config_version_file(4.05.06.007 9.5 0 0 0 0) # Request [newer major].[same minor] -test_write_basic_config_version_file(4.05.06.007 9.9 0 0 0 0) # Request [newer major].[newer minor] - -test_write_basic_config_version_file(4.05.06.007 0.0.0 1 0 0 0) # Request 0.0.0 -test_write_basic_config_version_file(4.05.06.007 0.0.2 1 0 0 0) # Request 0.0.[older patch] -test_write_basic_config_version_file(4.05.06.007 0.0.6 1 0 0 0) # Request 0.0.[same patch] -test_write_basic_config_version_file(4.05.06.007 0.0.9 1 0 0 0) # Request 0.0.[newer patch] -test_write_basic_config_version_file(4.05.06.007 0.2.0 1 0 0 0) # Request 0.[older minor].0 -test_write_basic_config_version_file(4.05.06.007 0.2.2 1 0 0 0) # Request 0.[older minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 0.2.6 1 0 0 0) # Request 0.[older minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 0.2.9 1 0 0 0) # Request 0.[older minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 0.5.0 1 0 0 0) # Request 0.[same minor].0 -test_write_basic_config_version_file(4.05.06.007 0.5.2 1 0 0 0) # Request 0.[same minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 0.5.6 1 0 0 0) # Request 0.[same minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 0.5.9 1 0 0 0) # Request 0.[same minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 0.9.0 1 0 0 0) # Request 0.[newer minor].0 -test_write_basic_config_version_file(4.05.06.007 0.9.2 1 0 0 0) # Request 0.[newer minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 0.9.6 1 0 0 0) # Request 0.[newer minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 0.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 2.0.0 1 0 0 0) # Request [older major].0.0 -test_write_basic_config_version_file(4.05.06.007 2.0.2 1 0 0 0) # Request [older major].0.[older patch] -test_write_basic_config_version_file(4.05.06.007 2.0.6 1 0 0 0) # Request [older major].0.[same patch] -test_write_basic_config_version_file(4.05.06.007 2.0.9 1 0 0 0) # Request [older major].0.[newer patch] -test_write_basic_config_version_file(4.05.06.007 2.2.0 1 0 0 0) # Request [older major].[older minor].0 -test_write_basic_config_version_file(4.05.06.007 2.2.2 1 0 0 0) # Request [older major].[older minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 2.2.6 1 0 0 0) # Request [older major].[older minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 2.2.9 1 0 0 0) # Request [older major].[older minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 2.5.0 1 0 0 0) # Request [older major].[same minor].0 -test_write_basic_config_version_file(4.05.06.007 2.5.2 1 0 0 0) # Request [older major].[same minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 2.5.6 1 0 0 0) # Request [older major].[same minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 2.5.9 1 0 0 0) # Request [older major].[same minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 2.9.0 1 0 0 0) # Request [older major].[newer minor].0 -test_write_basic_config_version_file(4.05.06.007 2.9.2 1 0 0 0) # Request [older major].[newer minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 2.9.6 1 0 0 0) # Request [older major].[newer minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 2.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 4.0.0 1 1 0 0) # Request [same major].0.0 -test_write_basic_config_version_file(4.05.06.007 4.0.2 1 1 0 0) # Request [same major].0.[older patch] -test_write_basic_config_version_file(4.05.06.007 4.0.6 1 1 0 0) # Request [same major].0.[same patch] -test_write_basic_config_version_file(4.05.06.007 4.0.9 1 1 0 0) # Request [same major].0.[newer patch] -test_write_basic_config_version_file(4.05.06.007 4.2.0 1 1 0 0) # Request [same major].[older minor].0 -test_write_basic_config_version_file(4.05.06.007 4.2.2 1 1 0 0) # Request [same major].[older minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 4.2.6 1 1 0 0) # Request [same major].[older minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 4.2.9 1 1 0 0) # Request [same major].[older minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 4.5.0 1 1 1 0) # Request [same major].[same minor].0 -test_write_basic_config_version_file(4.05.06.007 4.5.2 1 1 1 0) # Request [same major].[same minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 4.05.06 1 1 1 1) # Request [same major].[same minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 4.5.9 0 0 0 0) # Request [same major].[same minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 4.9.0 0 0 0 0) # Request [same major].[newer minor].0 -test_write_basic_config_version_file(4.05.06.007 4.9.2 0 0 0 0) # Request [same major].[newer minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 4.9.6 0 0 0 0) # Request [same major].[newer minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 4.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 9.0.0 0 0 0 0) # Request [newer major].0.0 -test_write_basic_config_version_file(4.05.06.007 9.0.2 0 0 0 0) # Request [newer major].0.[older patch] -test_write_basic_config_version_file(4.05.06.007 9.0.6 0 0 0 0) # Request [newer major].0.[same patch] -test_write_basic_config_version_file(4.05.06.007 9.0.9 0 0 0 0) # Request [newer major].0.[newer patch] -test_write_basic_config_version_file(4.05.06.007 9.2.0 0 0 0 0) # Request [newer major].[older minor].0 -test_write_basic_config_version_file(4.05.06.007 9.2.2 0 0 0 0) # Request [newer major].[older minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 9.2.6 0 0 0 0) # Request [newer major].[older minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 9.2.9 0 0 0 0) # Request [newer major].[older minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 9.5.0 0 0 0 0) # Request [newer major].[same minor].0 -test_write_basic_config_version_file(4.05.06.007 9.5.2 0 0 0 0) # Request [newer major].[same minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 9.5.6 0 0 0 0) # Request [newer major].[same minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 9.5.9 0 0 0 0) # Request [newer major].[same minor].[newer patch] -test_write_basic_config_version_file(4.05.06.007 9.9.0 0 0 0 0) # Request [newer major].[newer minor].0 -test_write_basic_config_version_file(4.05.06.007 9.9.2 0 0 0 0) # Request [newer major].[newer minor].[older patch] -test_write_basic_config_version_file(4.05.06.007 9.9.6 0 0 0 0) # Request [newer major].[newer minor].[same patch] -test_write_basic_config_version_file(4.05.06.007 9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch] - -test_write_basic_config_version_file(4.05.06.007 0.0.0.0 1 0 0 0) # Request 0.0.0.0 -test_write_basic_config_version_file(4.05.06.007 0.0.0.2 1 0 0 0) # Request 0.0.0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.0.7 1 0 0 0) # Request 0.0.0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.0.9 1 0 0 0) # Request 0.0.0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.2.0 1 0 0 0) # Request 0.0.[older patch].0 -test_write_basic_config_version_file(4.05.06.007 0.0.2.2 1 0 0 0) # Request 0.0.[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.2.7 1 0 0 0) # Request 0.0.[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.2.9 1 0 0 0) # Request 0.0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.6.0 1 0 0 0) # Request 0.0.[same patch].0 -test_write_basic_config_version_file(4.05.06.007 0.0.6.2 1 0 0 0) # Request 0.0.[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.6.7 1 0 0 0) # Request 0.0.[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.6.9 1 0 0 0) # Request 0.0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.9.0 1 0 0 0) # Request 0.0.[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 0.0.9.2 1 0 0 0) # Request 0.0.[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.9.7 1 0 0 0) # Request 0.0.[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.0.9.9 1 0 0 0) # Request 0.0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.0.0 1 0 0 0) # Request 0.[older minor].0.0 -test_write_basic_config_version_file(4.05.06.007 0.2.0.2 1 0 0 0) # Request 0.[older minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.0.7 1 0 0 0) # Request 0.[older minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.0.9 1 0 0 0) # Request 0.[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.2.0 1 0 0 0) # Request 0.[older minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 0.2.2.2 1 0 0 0) # Request 0.[older minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.2.7 1 0 0 0) # Request 0.[older minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.2.9 1 0 0 0) # Request 0.[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.6.0 1 0 0 0) # Request 0.[older minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 0.2.6.2 1 0 0 0) # Request 0.[older minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.6.7 1 0 0 0) # Request 0.[older minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.6.9 1 0 0 0) # Request 0.[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.9.0 1 0 0 0) # Request 0.[older minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 0.2.9.2 1 0 0 0) # Request 0.[older minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.9.7 1 0 0 0) # Request 0.[older minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.2.9.9 1 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.0.0 1 0 0 0) # Request 0.[same minor].0.0 -test_write_basic_config_version_file(4.05.06.007 0.5.0.2 1 0 0 0) # Request 0.[same minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.0.7 1 0 0 0) # Request 0.[same minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.0.9 1 0 0 0) # Request 0.[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.2.0 1 0 0 0) # Request 0.[same minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 0.5.2.2 1 0 0 0) # Request 0.[same minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.2.7 1 0 0 0) # Request 0.[same minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.2.9 1 0 0 0) # Request 0.[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.6.0 1 0 0 0) # Request 0.[same minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 0.5.6.2 1 0 0 0) # Request 0.[same minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.6.7 1 0 0 0) # Request 0.[same minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.6.9 1 0 0 0) # Request 0.[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.9.0 1 0 0 0) # Request 0.[same minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 0.5.9.2 1 0 0 0) # Request 0.[same minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.9.7 1 0 0 0) # Request 0.[same minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.5.9.9 1 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.0.0 1 0 0 0) # Request 0.[newer minor].0.0 -test_write_basic_config_version_file(4.05.06.007 0.9.0.2 1 0 0 0) # Request 0.[newer minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.0.7 1 0 0 0) # Request 0.[newer minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.0.9 1 0 0 0) # Request 0.[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.2.0 1 0 0 0) # Request 0.[newer minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 0.9.2.2 1 0 0 0) # Request 0.[newer minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.2.7 1 0 0 0) # Request 0.[newer minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.2.9 1 0 0 0) # Request 0.[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.6.0 1 0 0 0) # Request 0.[newer minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 0.9.6.2 1 0 0 0) # Request 0.[newer minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.6.7 1 0 0 0) # Request 0.[newer minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.6.9 1 0 0 0) # Request 0.[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.9.0 1 0 0 0) # Request 0.[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 0.9.9.2 1 0 0 0) # Request 0.[newer minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.9.7 1 0 0 0) # Request 0.[newer minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 0.9.9.9 1 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.0.0 1 0 0 0) # Request [older major].0.0.0 -test_write_basic_config_version_file(4.05.06.007 2.0.0.2 1 0 0 0) # Request [older major].0.0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.0.7 1 0 0 0) # Request [older major].0.0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.0.9 1 0 0 0) # Request [older major].0.0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.2.0 1 0 0 0) # Request [older major].0.[older patch].0 -test_write_basic_config_version_file(4.05.06.007 2.0.2.2 1 0 0 0) # Request [older major].0.[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.2.7 1 0 0 0) # Request [older major].0.[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.2.9 1 0 0 0) # Request [older major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.6.0 1 0 0 0) # Request [older major].0.[same patch].0 -test_write_basic_config_version_file(4.05.06.007 2.0.6.2 1 0 0 0) # Request [older major].0.[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.6.7 1 0 0 0) # Request [older major].0.[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.6.9 1 0 0 0) # Request [older major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.9.0 1 0 0 0) # Request [older major].0.[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 2.0.9.2 1 0 0 0) # Request [older major].0.[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.9.7 1 0 0 0) # Request [older major].0.[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.0.9.9 1 0 0 0) # Request [older major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.0.0 1 0 0 0) # Request [older major].[older minor].0.0 -test_write_basic_config_version_file(4.05.06.007 2.2.0.2 1 0 0 0) # Request [older major].[older minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.0.7 1 0 0 0) # Request [older major].[older minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.0.9 1 0 0 0) # Request [older major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.2.0 1 0 0 0) # Request [older major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 2.2.2.2 1 0 0 0) # Request [older major].[older minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.2.7 1 0 0 0) # Request [older major].[older minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.2.9 1 0 0 0) # Request [older major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.6.0 1 0 0 0) # Request [older major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 2.2.6.2 1 0 0 0) # Request [older major].[older minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.6.7 1 0 0 0) # Request [older major].[older minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.6.9 1 0 0 0) # Request [older major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.9.0 1 0 0 0) # Request [older major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 2.2.9.2 1 0 0 0) # Request [older major].[older minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.9.7 1 0 0 0) # Request [older major].[older minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.2.9.9 1 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.0.0 1 0 0 0) # Request [older major].[same minor].0.0 -test_write_basic_config_version_file(4.05.06.007 2.5.0.2 1 0 0 0) # Request [older major].[same minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.0.7 1 0 0 0) # Request [older major].[same minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.0.9 1 0 0 0) # Request [older major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.2.0 1 0 0 0) # Request [older major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 2.5.2.2 1 0 0 0) # Request [older major].[same minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.2.7 1 0 0 0) # Request [older major].[same minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.2.9 1 0 0 0) # Request [older major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.6.0 1 0 0 0) # Request [older major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 2.5.6.2 1 0 0 0) # Request [older major].[same minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.6.7 1 0 0 0) # Request [older major].[same minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.6.9 1 0 0 0) # Request [older major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.9.0 1 0 0 0) # Request [older major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 2.5.9.2 1 0 0 0) # Request [older major].[same minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.9.7 1 0 0 0) # Request [older major].[same minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.5.9.9 1 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.0.0 1 0 0 0) # Request [older major].[newer minor].0.0 -test_write_basic_config_version_file(4.05.06.007 2.9.0.2 1 0 0 0) # Request [older major].[newer minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.0.7 1 0 0 0) # Request [older major].[newer minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.0.9 1 0 0 0) # Request [older major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.2.0 1 0 0 0) # Request [older major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 2.9.2.2 1 0 0 0) # Request [older major].[newer minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.2.7 1 0 0 0) # Request [older major].[newer minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.2.9 1 0 0 0) # Request [older major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.6.0 1 0 0 0) # Request [older major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 2.9.6.2 1 0 0 0) # Request [older major].[newer minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.6.7 1 0 0 0) # Request [older major].[newer minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.6.9 1 0 0 0) # Request [older major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.9.0 1 0 0 0) # Request [older major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 2.9.9.2 1 0 0 0) # Request [older major].[newer minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.9.7 1 0 0 0) # Request [older major].[newer minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 2.9.9.9 1 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.0.0 1 1 0 0) # Request [same major].0.0.0 -test_write_basic_config_version_file(4.05.06.007 4.0.0.2 1 1 0 0) # Request [same major].0.0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.0.7 1 1 0 0) # Request [same major].0.0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.0.9 1 1 0 0) # Request [same major].0.0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.2.0 1 1 0 0) # Request [same major].0.[older patch].0 -test_write_basic_config_version_file(4.05.06.007 4.0.2.2 1 1 0 0) # Request [same major].0.[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.2.7 1 1 0 0) # Request [same major].0.[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.2.9 1 1 0 0) # Request [same major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.6.0 1 1 0 0) # Request [same major].0.[same patch].0 -test_write_basic_config_version_file(4.05.06.007 4.0.6.2 1 1 0 0) # Request [same major].0.[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.6.7 1 1 0 0) # Request [same major].0.[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.6.9 1 1 0 0) # Request [same major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.9.0 1 1 0 0) # Request [same major].0.[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 4.0.9.2 1 1 0 0) # Request [same major].0.[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.9.7 1 1 0 0) # Request [same major].0.[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.0.9.9 1 1 0 0) # Request [same major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.0.0 1 1 0 0) # Request [same major].[older minor].0.0 -test_write_basic_config_version_file(4.05.06.007 4.2.0.2 1 1 0 0) # Request [same major].[older minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.0.7 1 1 0 0) # Request [same major].[older minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.0.9 1 1 0 0) # Request [same major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.2.0 1 1 0 0) # Request [same major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 4.2.2.2 1 1 0 0) # Request [same major].[older minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.2.7 1 1 0 0) # Request [same major].[older minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.2.9 1 1 0 0) # Request [same major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.6.0 1 1 0 0) # Request [same major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 4.2.6.2 1 1 0 0) # Request [same major].[older minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.6.7 1 1 0 0) # Request [same major].[older minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.6.9 1 1 0 0) # Request [same major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.9.0 1 1 0 0) # Request [same major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 4.2.9.2 1 1 0 0) # Request [same major].[older minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.9.7 1 1 0 0) # Request [same major].[older minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.2.9.9 1 1 0 0) # Request [same major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.0.0 1 1 1 0) # Request [same major].[same minor].0.0 -test_write_basic_config_version_file(4.05.06.007 4.5.0.2 1 1 1 0) # Request [same major].[same minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.0.7 1 1 1 0) # Request [same major].[same minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.0.9 1 1 1 0) # Request [same major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.2.0 1 1 1 0) # Request [same major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 4.5.2.2 1 1 1 0) # Request [same major].[same minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.2.7 1 1 1 0) # Request [same major].[same minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.2.9 1 1 1 0) # Request [same major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.05.06.007 1 1 1 1) # Request [same major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 4.05.06.2 1 1 1 1) # Request [same major].[same minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.05.06.7 1 1 1 1) # Request [same major].[same minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.05.06.9 0 0 0 1) # Request [same major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.9.0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 4.5.9.2 0 0 0 0) # Request [same major].[same minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.9.7 0 0 0 0) # Request [same major].[same minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.5.9.9 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.0.0 0 0 0 0) # Request [same major].[newer minor].0.0 -test_write_basic_config_version_file(4.05.06.007 4.9.0.2 0 0 0 0) # Request [same major].[newer minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.0.7 0 0 0 0) # Request [same major].[newer minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.0.9 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.2.0 0 0 0 0) # Request [same major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 4.9.2.2 0 0 0 0) # Request [same major].[newer minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.2.7 0 0 0 0) # Request [same major].[newer minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.2.9 0 0 0 0) # Request [same major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.6.0 0 0 0 0) # Request [same major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 4.9.6.2 0 0 0 0) # Request [same major].[newer minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.6.7 0 0 0 0) # Request [same major].[newer minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.6.9 0 0 0 0) # Request [same major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.9.0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 4.9.9.2 0 0 0 0) # Request [same major].[newer minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.9.7 0 0 0 0) # Request [same major].[newer minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 4.9.9.9 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.0.0 0 0 0 0) # Request [newer major].0.0.0 -test_write_basic_config_version_file(4.05.06.007 9.0.0.2 0 0 0 0) # Request [newer major].0.0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.0.7 0 0 0 0) # Request [newer major].0.0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.0.9 0 0 0 0) # Request [newer major].0.0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.2.0 0 0 0 0) # Request [newer major].0.[older patch].0 -test_write_basic_config_version_file(4.05.06.007 9.0.2.2 0 0 0 0) # Request [newer major].0.[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.2.7 0 0 0 0) # Request [newer major].0.[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.2.9 0 0 0 0) # Request [newer major].0.[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.6.0 0 0 0 0) # Request [newer major].0.[same patch].0 -test_write_basic_config_version_file(4.05.06.007 9.0.6.2 0 0 0 0) # Request [newer major].0.[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.6.7 0 0 0 0) # Request [newer major].0.[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.6.9 0 0 0 0) # Request [newer major].0.[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.9.0 0 0 0 0) # Request [newer major].0.[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 9.0.9.2 0 0 0 0) # Request [newer major].0.[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.9.7 0 0 0 0) # Request [newer major].0.[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.0.9.9 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.0.0 0 0 0 0) # Request [newer major].[older minor].0.0 -test_write_basic_config_version_file(4.05.06.007 9.2.0.2 0 0 0 0) # Request [newer major].[older minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.0.7 0 0 0 0) # Request [newer major].[older minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.0.9 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.2.0 0 0 0 0) # Request [newer major].[older minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 9.2.2.2 0 0 0 0) # Request [newer major].[older minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.2.7 0 0 0 0) # Request [newer major].[older minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.2.9 0 0 0 0) # Request [newer major].[older minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.6.0 0 0 0 0) # Request [newer major].[older minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 9.2.6.2 0 0 0 0) # Request [newer major].[older minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.6.7 0 0 0 0) # Request [newer major].[older minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.6.9 0 0 0 0) # Request [newer major].[older minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.9.0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 9.2.9.2 0 0 0 0) # Request [newer major].[older minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.9.7 0 0 0 0) # Request [newer major].[older minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.2.9.9 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.0.0 0 0 0 0) # Request [newer major].[same minor].0.0 -test_write_basic_config_version_file(4.05.06.007 9.5.0.2 0 0 0 0) # Request [newer major].[same minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.0.7 0 0 0 0) # Request [newer major].[same minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.0.9 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.2.0 0 0 0 0) # Request [newer major].[same minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 9.5.2.2 0 0 0 0) # Request [newer major].[same minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.2.7 0 0 0 0) # Request [newer major].[same minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.2.9 0 0 0 0) # Request [newer major].[same minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.6.0 0 0 0 0) # Request [newer major].[same minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 9.5.6.2 0 0 0 0) # Request [newer major].[same minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.6.7 0 0 0 0) # Request [newer major].[same minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.6.9 0 0 0 0) # Request [newer major].[same minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.9.0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 9.5.9.2 0 0 0 0) # Request [newer major].[same minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.9.7 0 0 0 0) # Request [newer major].[same minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.5.9.9 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.0.0 0 0 0 0) # Request [newer major].[newer minor].0.0 -test_write_basic_config_version_file(4.05.06.007 9.9.0.2 0 0 0 0) # Request [newer major].[newer minor].0.[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.0.7 0 0 0 0) # Request [newer major].[newer minor].0.[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.0.9 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.2.0 0 0 0 0) # Request [newer major].[newer minor].[older patch].0 -test_write_basic_config_version_file(4.05.06.007 9.9.2.2 0 0 0 0) # Request [newer major].[newer minor].[older patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.2.7 0 0 0 0) # Request [newer major].[newer minor].[older patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.2.9 0 0 0 0) # Request [newer major].[newer minor].[older patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.6.0 0 0 0 0) # Request [newer major].[newer minor].[same patch].0 -test_write_basic_config_version_file(4.05.06.007 9.9.6.2 0 0 0 0) # Request [newer major].[newer minor].[same patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.6.7 0 0 0 0) # Request [newer major].[newer minor].[same patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.6.9 0 0 0 0) # Request [newer major].[newer minor].[same patch].[newer tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.9.0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 -test_write_basic_config_version_file(4.05.06.007 9.9.9.2 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[older tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.9.7 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[same tweak] -test_write_basic_config_version_file(4.05.06.007 9.9.9.9 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] - -test_write_basic_config_version_file(4 0...5 1 0 0 0) -test_write_basic_config_version_file(4 2...5 1 0 0 0) -test_write_basic_config_version_file(4 2...4 1 0 0 0) -test_write_basic_config_version_file(4 4...<5 1 1 0 0) -test_write_basic_config_version_file(4 9...10 0 0 0 0) - -test_write_basic_config_version_file(4 0.1...5 1 0 0 0) -test_write_basic_config_version_file(4 2.1...5 1 0 0 0) -test_write_basic_config_version_file(4 2.8...5 1 0 0 0) -test_write_basic_config_version_file(4 2.1...4 1 0 0 0) -test_write_basic_config_version_file(4 2.8...4 1 0 0 0) -test_write_basic_config_version_file(4 4.0...<5 1 1 0 0) -test_write_basic_config_version_file(4 4.8...<5 0 0 0 0) -test_write_basic_config_version_file(4 4.1...5 0 0 0 0) -test_write_basic_config_version_file(4 4.8...5 0 0 0 0) -test_write_basic_config_version_file(4 9.1...10 0 0 0 0) -test_write_basic_config_version_file(4 9.8...10 0 0 0 0) +test_write_basic_config_version_file(0.5.06 0.2.6 1 1 0 0 0 0) +test_write_basic_config_version_file(0.5.06 0.5.0 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.06 0.5.2 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.06 0.5.06 1 1 1 1 1 1) +test_write_basic_config_version_file(0.5.06 0.5.9 0 0 0 0 0 0) +test_write_basic_config_version_file(0.5.06 0.9.0 0 0 0 0 0 0) -test_write_basic_config_version_file(4.05 0.1...5 1 0 0 0) -test_write_basic_config_version_file(4.05 2.1...5 1 0 0 0) -test_write_basic_config_version_file(4.05 2.8...5 1 0 0 0) -test_write_basic_config_version_file(4.05 2.1...4 0 0 0 0) -test_write_basic_config_version_file(4.05 2.8...4 0 0 0 0) -test_write_basic_config_version_file(4.05 2.8...4.8 1 0 0 0) -test_write_basic_config_version_file(4.05 4.1...<5 1 1 0 0) -test_write_basic_config_version_file(4.05 4.8...<5 0 0 0 0) -test_write_basic_config_version_file(4.05 4.5...4.5.8 1 1 1 0) -test_write_basic_config_version_file(4.05 4.5...<4.6 1 1 1 0) -test_write_basic_config_version_file(4.05 4.1...5 1 0 0 0) -test_write_basic_config_version_file(4.05 4.8...5 0 0 0 0) -test_write_basic_config_version_file(4.05 9.1...10 0 0 0 0) -test_write_basic_config_version_file(4.05 9.8...10 0 0 0 0) +test_write_basic_config_version_file(4.5.06 0 1 0 0 0 0 0) # Request 0 +test_write_basic_config_version_file(4.5.06 2 1 0 0 0 0 0) # Request [older major] +test_write_basic_config_version_file(4.5.06 4 1 1 0 0 0 1) # Request [same major] +test_write_basic_config_version_file(4.5.06 9 0 0 0 0 0 0) # Request [newer major] + +test_write_basic_config_version_file(4.5.06 0.0 1 0 0 0 0 0) # Request 0.0 +test_write_basic_config_version_file(4.5.06 0.2 1 0 0 0 0 0) # Request 0.[older minor] +test_write_basic_config_version_file(4.5.06 0.5 1 0 0 0 0 0) # Request 0.[same minor] +test_write_basic_config_version_file(4.5.06 0.9 1 0 0 0 0 0) # Request 0.[newer minor] +test_write_basic_config_version_file(4.5.06 2.0 1 0 0 0 0 0) # Request [older major].0 +test_write_basic_config_version_file(4.5.06 2.2 1 0 0 0 0 0) # Request [older major].[older minor] +test_write_basic_config_version_file(4.5.06 2.5 1 0 0 0 0 0) # Request [older major].[same minor] +test_write_basic_config_version_file(4.5.06 2.9 1 0 0 0 0 0) # Request [older major].[newer minor] +test_write_basic_config_version_file(4.5.06 4.0 1 1 0 0 0 1) # Request [same major].0 +test_write_basic_config_version_file(4.5.06 4.2 1 1 0 0 0 1) # Request [same major].[older minor] +test_write_basic_config_version_file(4.5.06 4.5 1 1 1 0 0 1) # Request [same major].[same minor] +test_write_basic_config_version_file(4.5.06 4.9 0 0 0 0 0 0) # Request [same major].[newer minor] +test_write_basic_config_version_file(4.5.06 9.0 0 0 0 0 0 0) # Request [newer major].0 +test_write_basic_config_version_file(4.5.06 9.1 0 0 0 0 0 0) # Request [newer major].[older minor] +test_write_basic_config_version_file(4.5.06 9.5 0 0 0 0 0 0) # Request [newer major].[same minor] +test_write_basic_config_version_file(4.5.06 9.9 0 0 0 0 0 0) # Request [newer major].[newer minor] + +test_write_basic_config_version_file(4.5.06 0.0.0 1 0 0 0 0 0) # Request 0.0.0 +test_write_basic_config_version_file(4.5.06 0.0.2 1 0 0 0 0 0) # Request 0.0.[older patch] +test_write_basic_config_version_file(4.5.06 0.0.6 1 0 0 0 0 0) # Request 0.0.[same patch] +test_write_basic_config_version_file(4.5.06 0.0.9 1 0 0 0 0 0) # Request 0.0.[newer patch] +test_write_basic_config_version_file(4.5.06 0.2.0 1 0 0 0 0 0) # Request 0.[older minor].0 +test_write_basic_config_version_file(4.5.06 0.2.2 1 0 0 0 0 0) # Request 0.[older minor].[older patch] +test_write_basic_config_version_file(4.5.06 0.2.6 1 0 0 0 0 0) # Request 0.[older minor].[same patch] +test_write_basic_config_version_file(4.5.06 0.2.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch] +test_write_basic_config_version_file(4.5.06 0.5.0 1 0 0 0 0 0) # Request 0.[same minor].0 +test_write_basic_config_version_file(4.5.06 0.5.2 1 0 0 0 0 0) # Request 0.[same minor].[older patch] +test_write_basic_config_version_file(4.5.06 0.5.6 1 0 0 0 0 0) # Request 0.[same minor].[same patch] +test_write_basic_config_version_file(4.5.06 0.5.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch] +test_write_basic_config_version_file(4.5.06 0.9.0 1 0 0 0 0 0) # Request 0.[newer minor].0 +test_write_basic_config_version_file(4.5.06 0.9.2 1 0 0 0 0 0) # Request 0.[newer minor].[older patch] +test_write_basic_config_version_file(4.5.06 0.9.6 1 0 0 0 0 0) # Request 0.[newer minor].[same patch] +test_write_basic_config_version_file(4.5.06 0.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.06 2.0.0 1 0 0 0 0 0) # Request [older major].0.0 +test_write_basic_config_version_file(4.5.06 2.0.2 1 0 0 0 0 0) # Request [older major].0.[older patch] +test_write_basic_config_version_file(4.5.06 2.0.6 1 0 0 0 0 0) # Request [older major].0.[same patch] +test_write_basic_config_version_file(4.5.06 2.0.9 1 0 0 0 0 0) # Request [older major].0.[newer patch] +test_write_basic_config_version_file(4.5.06 2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].0 +test_write_basic_config_version_file(4.5.06 2.2.2 1 0 0 0 0 0) # Request [older major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.06 2.2.6 1 0 0 0 0 0) # Request [older major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.06 2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.06 2.5.0 1 0 0 0 0 0) # Request [older major].[same minor].0 +test_write_basic_config_version_file(4.5.06 2.5.2 1 0 0 0 0 0) # Request [older major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.06 2.5.6 1 0 0 0 0 0) # Request [older major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.06 2.5.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.06 2.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].0 +test_write_basic_config_version_file(4.5.06 2.9.2 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.06 2.9.6 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.06 2.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.06 4.0.0 1 1 0 0 0 1) # Request [same major].0.0 +test_write_basic_config_version_file(4.5.06 4.0.2 1 1 0 0 0 1) # Request [same major].0.[older patch] +test_write_basic_config_version_file(4.5.06 4.0.6 1 1 0 0 0 1) # Request [same major].0.[same patch] +test_write_basic_config_version_file(4.5.06 4.0.9 1 1 0 0 0 1) # Request [same major].0.[newer patch] +test_write_basic_config_version_file(4.5.06 4.2.0 1 1 0 0 0 1) # Request [same major].[older minor].0 +test_write_basic_config_version_file(4.5.06 4.2.2 1 1 0 0 0 1) # Request [same major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.06 4.2.6 1 1 0 0 0 1) # Request [same major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.06 4.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.06 4.5.0 1 1 1 0 0 1) # Request [same major].[same minor].0 +test_write_basic_config_version_file(4.5.06 4.5.2 1 1 1 0 0 1) # Request [same major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.06 4.5.06 1 1 1 1 1 1) # Request [same major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.06 4.5.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.06 4.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].0 +test_write_basic_config_version_file(4.5.06 4.9.2 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.06 4.9.6 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.06 4.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.5.06 9.0.0 0 0 0 0 0 0) # Request [newer major].0.0 +test_write_basic_config_version_file(4.5.06 9.0.2 0 0 0 0 0 0) # Request [newer major].0.[older patch] +test_write_basic_config_version_file(4.5.06 9.0.6 0 0 0 0 0 0) # Request [newer major].0.[same patch] +test_write_basic_config_version_file(4.5.06 9.0.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch] +test_write_basic_config_version_file(4.5.06 9.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].0 +test_write_basic_config_version_file(4.5.06 9.2.2 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch] +test_write_basic_config_version_file(4.5.06 9.2.6 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch] +test_write_basic_config_version_file(4.5.06 9.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch] +test_write_basic_config_version_file(4.5.06 9.5.0 0 0 0 0 0 0) # Request [newer major].[same minor].0 +test_write_basic_config_version_file(4.5.06 9.5.2 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch] +test_write_basic_config_version_file(4.5.06 9.5.6 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch] +test_write_basic_config_version_file(4.5.06 9.5.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch] +test_write_basic_config_version_file(4.5.06 9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0 +test_write_basic_config_version_file(4.5.06 9.9.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch] +test_write_basic_config_version_file(4.5.06 9.9.6 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch] +test_write_basic_config_version_file(4.5.06 9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch] + +test_write_basic_config_version_file(4.5.06 0.0.0.0 1 0 0 0 0 0) # Request 0.0.0.0 +test_write_basic_config_version_file(4.5.06 0.0.0.9 1 0 0 0 0 0) # Request 0.0.0.[newer tweak] +test_write_basic_config_version_file(4.5.06 0.0.2.0 1 0 0 0 0 0) # Request 0.0.[older patch].0 +test_write_basic_config_version_file(4.5.06 0.0.2.9 1 0 0 0 0 0) # Request 0.0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.0.6.0 1 0 0 0 0 0) # Request 0.0.[same patch].0 +test_write_basic_config_version_file(4.5.06 0.0.6.9 1 0 0 0 0 0) # Request 0.0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.0.9.0 1 0 0 0 0 0) # Request 0.0.[newer patch].0 +test_write_basic_config_version_file(4.5.06 0.0.9.9 1 0 0 0 0 0) # Request 0.0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.2.0.0 1 0 0 0 0 0) # Request 0.[older minor].0.0 +test_write_basic_config_version_file(4.5.06 0.2.0.9 1 0 0 0 0 0) # Request 0.[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 0.2.2.0 1 0 0 0 0 0) # Request 0.[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 0.2.2.9 1 0 0 0 0 0) # Request 0.[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.2.6.0 1 0 0 0 0 0) # Request 0.[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 0.2.6.9 1 0 0 0 0 0) # Request 0.[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.2.9.0 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 0.2.9.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.5.0.0 1 0 0 0 0 0) # Request 0.[same minor].0.0 +test_write_basic_config_version_file(4.5.06 0.5.0.9 1 0 0 0 0 0) # Request 0.[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 0.5.2.0 1 0 0 0 0 0) # Request 0.[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 0.5.2.9 1 0 0 0 0 0) # Request 0.[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.5.6.0 1 0 0 0 0 0) # Request 0.[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 0.5.6.9 1 0 0 0 0 0) # Request 0.[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.5.9.0 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 0.5.9.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.9.0.0 1 0 0 0 0 0) # Request 0.[newer minor].0.0 +test_write_basic_config_version_file(4.5.06 0.9.0.9 1 0 0 0 0 0) # Request 0.[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 0.9.2.0 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 0.9.2.9 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.9.6.0 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 0.9.6.9 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 0.9.9.0 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 0.9.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.0.0.0 1 0 0 0 0 0) # Request [older major].0.0.0 +test_write_basic_config_version_file(4.5.06 2.0.0.9 1 0 0 0 0 0) # Request [older major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.06 2.0.2.0 1 0 0 0 0 0) # Request [older major].0.[older patch].0 +test_write_basic_config_version_file(4.5.06 2.0.2.9 1 0 0 0 0 0) # Request [older major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.0.6.0 1 0 0 0 0 0) # Request [older major].0.[same patch].0 +test_write_basic_config_version_file(4.5.06 2.0.6.9 1 0 0 0 0 0) # Request [older major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.0.9.0 1 0 0 0 0 0) # Request [older major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.06 2.0.9.9 1 0 0 0 0 0) # Request [older major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.2.0.0 1 0 0 0 0 0) # Request [older major].[older minor].0.0 +test_write_basic_config_version_file(4.5.06 2.2.0.9 1 0 0 0 0 0) # Request [older major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 2.2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 2.2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.2.6.0 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 2.2.6.9 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.2.9.0 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 2.2.9.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.5.0.0 1 0 0 0 0 0) # Request [older major].[same minor].0.0 +test_write_basic_config_version_file(4.5.06 2.5.0.9 1 0 0 0 0 0) # Request [older major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 2.5.2.0 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 2.5.2.9 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.5.6.0 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 2.5.6.9 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.5.9.0 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 2.5.9.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.9.0.0 1 0 0 0 0 0) # Request [older major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.06 2.9.0.9 1 0 0 0 0 0) # Request [older major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 2.9.2.0 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 2.9.2.9 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.9.6.0 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 2.9.6.9 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 2.9.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 2.9.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.0.0.0 1 1 0 0 0 1) # Request [same major].0.0.0 +test_write_basic_config_version_file(4.5.06 4.0.0.9 1 1 0 0 0 1) # Request [same major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.06 4.0.2.0 1 1 0 0 0 1) # Request [same major].0.[older patch].0 +test_write_basic_config_version_file(4.5.06 4.0.2.9 1 1 0 0 0 1) # Request [same major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.0.6.0 1 1 0 0 0 1) # Request [same major].0.[same patch].0 +test_write_basic_config_version_file(4.5.06 4.0.6.9 1 1 0 0 0 1) # Request [same major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.0.9.0 1 1 0 0 0 1) # Request [same major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.06 4.0.9.9 1 1 0 0 0 1) # Request [same major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.2.0.0 1 1 0 0 0 1) # Request [same major].[older minor].0.0 +test_write_basic_config_version_file(4.5.06 4.2.0.9 1 1 0 0 0 1) # Request [same major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 4.2.2.0 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 4.2.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.2.6.0 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 4.2.6.9 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.2.9.0 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 4.2.9.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.5.0.0 1 1 1 0 0 1) # Request [same major].[same minor].0.0 +test_write_basic_config_version_file(4.5.06 4.5.0.9 1 1 1 0 0 1) # Request [same major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 4.5.2.0 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 4.5.2.9 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.5.06.0 1 1 1 1 0 1) # Request [same major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 4.5.06.9 0 0 0 1 0 0) # Request [same major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.5.9.0 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 4.5.9.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.9.0.0 0 0 0 0 0 0) # Request [same major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.06 4.9.0.9 0 0 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 4.9.2.0 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 4.9.2.9 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.9.6.0 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 4.9.6.9 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 4.9.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 4.9.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.0.0.0 0 0 0 0 0 0) # Request [newer major].0.0.0 +test_write_basic_config_version_file(4.5.06 9.0.0.9 0 0 0 0 0 0) # Request [newer major].0.0.[newer tweak] +test_write_basic_config_version_file(4.5.06 9.0.2.0 0 0 0 0 0 0) # Request [newer major].0.[older patch].0 +test_write_basic_config_version_file(4.5.06 9.0.2.9 0 0 0 0 0 0) # Request [newer major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.0.6.0 0 0 0 0 0 0) # Request [newer major].0.[same patch].0 +test_write_basic_config_version_file(4.5.06 9.0.6.9 0 0 0 0 0 0) # Request [newer major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.0.9.0 0 0 0 0 0 0) # Request [newer major].0.[newer patch].0 +test_write_basic_config_version_file(4.5.06 9.0.9.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.2.0.0 0 0 0 0 0 0) # Request [newer major].[older minor].0.0 +test_write_basic_config_version_file(4.5.06 9.2.0.9 0 0 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 9.2.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 9.2.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.2.6.0 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 9.2.6.9 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.2.9.0 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 9.2.9.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.5.0.0 0 0 0 0 0 0) # Request [newer major].[same minor].0.0 +test_write_basic_config_version_file(4.5.06 9.5.0.9 0 0 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 9.5.2.0 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 9.5.2.9 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.5.6.0 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 9.5.6.9 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.5.9.0 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 9.5.9.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.9.0.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0.0 +test_write_basic_config_version_file(4.5.06 9.9.0.9 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.5.06 9.9.2.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.5.06 9.9.2.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.9.6.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.5.06 9.9.6.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.5.06 9.9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.5.06 9.9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] -test_write_basic_config_version_file(4.5.06 0.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5.06 2.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5.06 2.8...5 1 0 0 0) -test_write_basic_config_version_file(4.5.06 2.1...4 0 0 0 0) -test_write_basic_config_version_file(4.5.06 2.8...4 0 0 0 0) -test_write_basic_config_version_file(4.5.06 2.8...4.8 1 0 0 0) -test_write_basic_config_version_file(4.5.06 4.1...<5 1 1 0 0) -test_write_basic_config_version_file(4.5.06 4.8...<5 0 0 0 0) -test_write_basic_config_version_file(4.5.06 4.5...4.5.4 0 0 0 0) -test_write_basic_config_version_file(4.5.06 4.5...4.5.8 1 1 1 0) -test_write_basic_config_version_file(4.5.06 4.5...<4.6 1 1 1 0) -test_write_basic_config_version_file(4.5.06 4.1...5 1 0 0 0) -test_write_basic_config_version_file(4.5.06 4.8...5 0 0 0 0) -test_write_basic_config_version_file(4.5.06 9.1...10 0 0 0 0) -test_write_basic_config_version_file(4.5.06 9.8...10 0 0 0 0) +test_write_basic_config_version_file(4.05.06.007 0 1 0 0 0 0 0) # Request 0 +test_write_basic_config_version_file(4.05.06.007 2 1 0 0 0 0 0) # Request [older major] +test_write_basic_config_version_file(4.05.06.007 4 1 1 0 0 0 1) # Request [same major] +test_write_basic_config_version_file(4.05.06.007 9 0 0 0 0 0 0) # Request [newer major] + +test_write_basic_config_version_file(4.05.06.007 0.0 1 0 0 0 0 0) # Request 0.0 +test_write_basic_config_version_file(4.05.06.007 0.2 1 0 0 0 0 0) # Request 0.[older minor] +test_write_basic_config_version_file(4.05.06.007 0.5 1 0 0 0 0 0) # Request 0.[same minor] +test_write_basic_config_version_file(4.05.06.007 0.9 1 0 0 0 0 0) # Request 0.[newer minor] +test_write_basic_config_version_file(4.05.06.007 2.0 1 0 0 0 0 0) # Request [older major].0 +test_write_basic_config_version_file(4.05.06.007 2.2 1 0 0 0 0 0) # Request [older major].[older minor] +test_write_basic_config_version_file(4.05.06.007 2.5 1 0 0 0 0 0) # Request [older major].[same minor] +test_write_basic_config_version_file(4.05.06.007 2.9 1 0 0 0 0 0) # Request [older major].[newer minor] +test_write_basic_config_version_file(4.05.06.007 4.0 1 1 0 0 0 1) # Request [same major].0 +test_write_basic_config_version_file(4.05.06.007 4.2 1 1 0 0 0 1) # Request [same major].[older minor] +test_write_basic_config_version_file(4.05.06.007 4.5 1 1 1 0 0 1) # Request [same major].[same minor] +test_write_basic_config_version_file(4.05.06.007 4.9 0 0 0 0 0 0) # Request [same major].[newer minor] +test_write_basic_config_version_file(4.05.06.007 9.0 0 0 0 0 0 0) # Request [newer major].0 +test_write_basic_config_version_file(4.05.06.007 9.1 0 0 0 0 0 0) # Request [newer major].[older minor] +test_write_basic_config_version_file(4.05.06.007 9.5 0 0 0 0 0 0) # Request [newer major].[same minor] +test_write_basic_config_version_file(4.05.06.007 9.9 0 0 0 0 0 0) # Request [newer major].[newer minor] + +test_write_basic_config_version_file(4.05.06.007 0.0.0 1 0 0 0 0 0) # Request 0.0.0 +test_write_basic_config_version_file(4.05.06.007 0.0.2 1 0 0 0 0 0) # Request 0.0.[older patch] +test_write_basic_config_version_file(4.05.06.007 0.0.6 1 0 0 0 0 0) # Request 0.0.[same patch] +test_write_basic_config_version_file(4.05.06.007 0.0.9 1 0 0 0 0 0) # Request 0.0.[newer patch] +test_write_basic_config_version_file(4.05.06.007 0.2.0 1 0 0 0 0 0) # Request 0.[older minor].0 +test_write_basic_config_version_file(4.05.06.007 0.2.2 1 0 0 0 0 0) # Request 0.[older minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 0.2.6 1 0 0 0 0 0) # Request 0.[older minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 0.2.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 0.5.0 1 0 0 0 0 0) # Request 0.[same minor].0 +test_write_basic_config_version_file(4.05.06.007 0.5.2 1 0 0 0 0 0) # Request 0.[same minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 0.5.6 1 0 0 0 0 0) # Request 0.[same minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 0.5.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 0.9.0 1 0 0 0 0 0) # Request 0.[newer minor].0 +test_write_basic_config_version_file(4.05.06.007 0.9.2 1 0 0 0 0 0) # Request 0.[newer minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 0.9.6 1 0 0 0 0 0) # Request 0.[newer minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 0.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 2.0.0 1 0 0 0 0 0) # Request [older major].0.0 +test_write_basic_config_version_file(4.05.06.007 2.0.2 1 0 0 0 0 0) # Request [older major].0.[older patch] +test_write_basic_config_version_file(4.05.06.007 2.0.6 1 0 0 0 0 0) # Request [older major].0.[same patch] +test_write_basic_config_version_file(4.05.06.007 2.0.9 1 0 0 0 0 0) # Request [older major].0.[newer patch] +test_write_basic_config_version_file(4.05.06.007 2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].0 +test_write_basic_config_version_file(4.05.06.007 2.2.2 1 0 0 0 0 0) # Request [older major].[older minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 2.2.6 1 0 0 0 0 0) # Request [older major].[older minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 2.5.0 1 0 0 0 0 0) # Request [older major].[same minor].0 +test_write_basic_config_version_file(4.05.06.007 2.5.2 1 0 0 0 0 0) # Request [older major].[same minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 2.5.6 1 0 0 0 0 0) # Request [older major].[same minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 2.5.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 2.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].0 +test_write_basic_config_version_file(4.05.06.007 2.9.2 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 2.9.6 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 2.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 4.0.0 1 1 0 0 0 1) # Request [same major].0.0 +test_write_basic_config_version_file(4.05.06.007 4.0.2 1 1 0 0 0 1) # Request [same major].0.[older patch] +test_write_basic_config_version_file(4.05.06.007 4.0.6 1 1 0 0 0 1) # Request [same major].0.[same patch] +test_write_basic_config_version_file(4.05.06.007 4.0.9 1 1 0 0 0 1) # Request [same major].0.[newer patch] +test_write_basic_config_version_file(4.05.06.007 4.2.0 1 1 0 0 0 1) # Request [same major].[older minor].0 +test_write_basic_config_version_file(4.05.06.007 4.2.2 1 1 0 0 0 1) # Request [same major].[older minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 4.2.6 1 1 0 0 0 1) # Request [same major].[older minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 4.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 4.5.0 1 1 1 0 0 1) # Request [same major].[same minor].0 +test_write_basic_config_version_file(4.05.06.007 4.5.2 1 1 1 0 0 1) # Request [same major].[same minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 4.05.06 1 1 1 1 0 1) # Request [same major].[same minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 4.5.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 4.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].0 +test_write_basic_config_version_file(4.05.06.007 4.9.2 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 4.9.6 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 4.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 9.0.0 0 0 0 0 0 0) # Request [newer major].0.0 +test_write_basic_config_version_file(4.05.06.007 9.0.2 0 0 0 0 0 0) # Request [newer major].0.[older patch] +test_write_basic_config_version_file(4.05.06.007 9.0.6 0 0 0 0 0 0) # Request [newer major].0.[same patch] +test_write_basic_config_version_file(4.05.06.007 9.0.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch] +test_write_basic_config_version_file(4.05.06.007 9.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].0 +test_write_basic_config_version_file(4.05.06.007 9.2.2 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 9.2.6 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 9.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 9.5.0 0 0 0 0 0 0) # Request [newer major].[same minor].0 +test_write_basic_config_version_file(4.05.06.007 9.5.2 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 9.5.6 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 9.5.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch] +test_write_basic_config_version_file(4.05.06.007 9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0 +test_write_basic_config_version_file(4.05.06.007 9.9.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch] +test_write_basic_config_version_file(4.05.06.007 9.9.6 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch] +test_write_basic_config_version_file(4.05.06.007 9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch] + +test_write_basic_config_version_file(4.05.06.007 0.0.0.0 1 0 0 0 0 0) # Request 0.0.0.0 +test_write_basic_config_version_file(4.05.06.007 0.0.0.2 1 0 0 0 0 0) # Request 0.0.0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.0.7 1 0 0 0 0 0) # Request 0.0.0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.0.9 1 0 0 0 0 0) # Request 0.0.0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.2.0 1 0 0 0 0 0) # Request 0.0.[older patch].0 +test_write_basic_config_version_file(4.05.06.007 0.0.2.2 1 0 0 0 0 0) # Request 0.0.[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.2.7 1 0 0 0 0 0) # Request 0.0.[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.2.9 1 0 0 0 0 0) # Request 0.0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.6.0 1 0 0 0 0 0) # Request 0.0.[same patch].0 +test_write_basic_config_version_file(4.05.06.007 0.0.6.2 1 0 0 0 0 0) # Request 0.0.[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.6.7 1 0 0 0 0 0) # Request 0.0.[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.6.9 1 0 0 0 0 0) # Request 0.0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.9.0 1 0 0 0 0 0) # Request 0.0.[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 0.0.9.2 1 0 0 0 0 0) # Request 0.0.[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.9.7 1 0 0 0 0 0) # Request 0.0.[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.0.9.9 1 0 0 0 0 0) # Request 0.0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.0.0 1 0 0 0 0 0) # Request 0.[older minor].0.0 +test_write_basic_config_version_file(4.05.06.007 0.2.0.2 1 0 0 0 0 0) # Request 0.[older minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.0.7 1 0 0 0 0 0) # Request 0.[older minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.0.9 1 0 0 0 0 0) # Request 0.[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.2.0 1 0 0 0 0 0) # Request 0.[older minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 0.2.2.2 1 0 0 0 0 0) # Request 0.[older minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.2.7 1 0 0 0 0 0) # Request 0.[older minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.2.9 1 0 0 0 0 0) # Request 0.[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.6.0 1 0 0 0 0 0) # Request 0.[older minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 0.2.6.2 1 0 0 0 0 0) # Request 0.[older minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.6.7 1 0 0 0 0 0) # Request 0.[older minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.6.9 1 0 0 0 0 0) # Request 0.[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.9.0 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 0.2.9.2 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.9.7 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.2.9.9 1 0 0 0 0 0) # Request 0.[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.0.0 1 0 0 0 0 0) # Request 0.[same minor].0.0 +test_write_basic_config_version_file(4.05.06.007 0.5.0.2 1 0 0 0 0 0) # Request 0.[same minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.0.7 1 0 0 0 0 0) # Request 0.[same minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.0.9 1 0 0 0 0 0) # Request 0.[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.2.0 1 0 0 0 0 0) # Request 0.[same minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 0.5.2.2 1 0 0 0 0 0) # Request 0.[same minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.2.7 1 0 0 0 0 0) # Request 0.[same minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.2.9 1 0 0 0 0 0) # Request 0.[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.6.0 1 0 0 0 0 0) # Request 0.[same minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 0.5.6.2 1 0 0 0 0 0) # Request 0.[same minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.6.7 1 0 0 0 0 0) # Request 0.[same minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.6.9 1 0 0 0 0 0) # Request 0.[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.9.0 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 0.5.9.2 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.9.7 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.5.9.9 1 0 0 0 0 0) # Request 0.[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.0.0 1 0 0 0 0 0) # Request 0.[newer minor].0.0 +test_write_basic_config_version_file(4.05.06.007 0.9.0.2 1 0 0 0 0 0) # Request 0.[newer minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.0.7 1 0 0 0 0 0) # Request 0.[newer minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.0.9 1 0 0 0 0 0) # Request 0.[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.2.0 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 0.9.2.2 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.2.7 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.2.9 1 0 0 0 0 0) # Request 0.[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.6.0 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 0.9.6.2 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.6.7 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.6.9 1 0 0 0 0 0) # Request 0.[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.9.0 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 0.9.9.2 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.9.7 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 0.9.9.9 1 0 0 0 0 0) # Request 0.[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.0.0 1 0 0 0 0 0) # Request [older major].0.0.0 +test_write_basic_config_version_file(4.05.06.007 2.0.0.2 1 0 0 0 0 0) # Request [older major].0.0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.0.7 1 0 0 0 0 0) # Request [older major].0.0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.0.9 1 0 0 0 0 0) # Request [older major].0.0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.2.0 1 0 0 0 0 0) # Request [older major].0.[older patch].0 +test_write_basic_config_version_file(4.05.06.007 2.0.2.2 1 0 0 0 0 0) # Request [older major].0.[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.2.7 1 0 0 0 0 0) # Request [older major].0.[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.2.9 1 0 0 0 0 0) # Request [older major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.6.0 1 0 0 0 0 0) # Request [older major].0.[same patch].0 +test_write_basic_config_version_file(4.05.06.007 2.0.6.2 1 0 0 0 0 0) # Request [older major].0.[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.6.7 1 0 0 0 0 0) # Request [older major].0.[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.6.9 1 0 0 0 0 0) # Request [older major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.9.0 1 0 0 0 0 0) # Request [older major].0.[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 2.0.9.2 1 0 0 0 0 0) # Request [older major].0.[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.9.7 1 0 0 0 0 0) # Request [older major].0.[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.0.9.9 1 0 0 0 0 0) # Request [older major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.0.0 1 0 0 0 0 0) # Request [older major].[older minor].0.0 +test_write_basic_config_version_file(4.05.06.007 2.2.0.2 1 0 0 0 0 0) # Request [older major].[older minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.0.7 1 0 0 0 0 0) # Request [older major].[older minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.0.9 1 0 0 0 0 0) # Request [older major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.2.0 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 2.2.2.2 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.2.7 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.2.9 1 0 0 0 0 0) # Request [older major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.6.0 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 2.2.6.2 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.6.7 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.6.9 1 0 0 0 0 0) # Request [older major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.9.0 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 2.2.9.2 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.9.7 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.2.9.9 1 0 0 0 0 0) # Request [older major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.0.0 1 0 0 0 0 0) # Request [older major].[same minor].0.0 +test_write_basic_config_version_file(4.05.06.007 2.5.0.2 1 0 0 0 0 0) # Request [older major].[same minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.0.7 1 0 0 0 0 0) # Request [older major].[same minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.0.9 1 0 0 0 0 0) # Request [older major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.2.0 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 2.5.2.2 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.2.7 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.2.9 1 0 0 0 0 0) # Request [older major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.6.0 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 2.5.6.2 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.6.7 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.6.9 1 0 0 0 0 0) # Request [older major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.9.0 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 2.5.9.2 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.9.7 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.5.9.9 1 0 0 0 0 0) # Request [older major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.0.0 1 0 0 0 0 0) # Request [older major].[newer minor].0.0 +test_write_basic_config_version_file(4.05.06.007 2.9.0.2 1 0 0 0 0 0) # Request [older major].[newer minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.0.7 1 0 0 0 0 0) # Request [older major].[newer minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.0.9 1 0 0 0 0 0) # Request [older major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.2.0 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 2.9.2.2 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.2.7 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.2.9 1 0 0 0 0 0) # Request [older major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.6.0 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 2.9.6.2 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.6.7 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.6.9 1 0 0 0 0 0) # Request [older major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.9.0 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 2.9.9.2 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.9.7 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 2.9.9.9 1 0 0 0 0 0) # Request [older major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.0.0 1 1 0 0 0 1) # Request [same major].0.0.0 +test_write_basic_config_version_file(4.05.06.007 4.0.0.2 1 1 0 0 0 1) # Request [same major].0.0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.0.7 1 1 0 0 0 1) # Request [same major].0.0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.0.9 1 1 0 0 0 1) # Request [same major].0.0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.2.0 1 1 0 0 0 1) # Request [same major].0.[older patch].0 +test_write_basic_config_version_file(4.05.06.007 4.0.2.2 1 1 0 0 0 1) # Request [same major].0.[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.2.7 1 1 0 0 0 1) # Request [same major].0.[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.2.9 1 1 0 0 0 1) # Request [same major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.6.0 1 1 0 0 0 1) # Request [same major].0.[same patch].0 +test_write_basic_config_version_file(4.05.06.007 4.0.6.2 1 1 0 0 0 1) # Request [same major].0.[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.6.7 1 1 0 0 0 1) # Request [same major].0.[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.6.9 1 1 0 0 0 1) # Request [same major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.9.0 1 1 0 0 0 1) # Request [same major].0.[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 4.0.9.2 1 1 0 0 0 1) # Request [same major].0.[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.9.7 1 1 0 0 0 1) # Request [same major].0.[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.0.9.9 1 1 0 0 0 1) # Request [same major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.0.0 1 1 0 0 0 1) # Request [same major].[older minor].0.0 +test_write_basic_config_version_file(4.05.06.007 4.2.0.2 1 1 0 0 0 1) # Request [same major].[older minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.0.7 1 1 0 0 0 1) # Request [same major].[older minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.0.9 1 1 0 0 0 1) # Request [same major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.2.0 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 4.2.2.2 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.2.7 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.2.9 1 1 0 0 0 1) # Request [same major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.6.0 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 4.2.6.2 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.6.7 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.6.9 1 1 0 0 0 1) # Request [same major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.9.0 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 4.2.9.2 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.9.7 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.2.9.9 1 1 0 0 0 1) # Request [same major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.0.0 1 1 1 0 0 1) # Request [same major].[same minor].0.0 +test_write_basic_config_version_file(4.05.06.007 4.5.0.2 1 1 1 0 0 1) # Request [same major].[same minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.0.7 1 1 1 0 0 1) # Request [same major].[same minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.0.9 1 1 1 0 0 1) # Request [same major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.2.0 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 4.5.2.2 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.2.7 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.2.9 1 1 1 0 0 1) # Request [same major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.05.06.007 1 1 1 1 1 1) # Request [same major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 4.05.06.2 1 1 1 1 0 1) # Request [same major].[same minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.05.06.7 1 1 1 1 0 1) # Request [same major].[same minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.05.06.9 0 0 0 1 0 0) # Request [same major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.9.0 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 4.5.9.2 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.9.7 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.5.9.9 0 0 0 0 0 0) # Request [same major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.0.0 0 0 0 0 0 0) # Request [same major].[newer minor].0.0 +test_write_basic_config_version_file(4.05.06.007 4.9.0.2 0 0 0 0 0 0) # Request [same major].[newer minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.0.7 0 0 0 0 0 0) # Request [same major].[newer minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.0.9 0 0 0 0 0 0) # Request [same major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.2.0 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 4.9.2.2 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.2.7 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.2.9 0 0 0 0 0 0) # Request [same major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.6.0 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 4.9.6.2 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.6.7 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.6.9 0 0 0 0 0 0) # Request [same major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.9.0 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 4.9.9.2 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.9.7 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 4.9.9.9 0 0 0 0 0 0) # Request [same major].[newer minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.0.0 0 0 0 0 0 0) # Request [newer major].0.0.0 +test_write_basic_config_version_file(4.05.06.007 9.0.0.2 0 0 0 0 0 0) # Request [newer major].0.0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.0.7 0 0 0 0 0 0) # Request [newer major].0.0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.0.9 0 0 0 0 0 0) # Request [newer major].0.0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.2.0 0 0 0 0 0 0) # Request [newer major].0.[older patch].0 +test_write_basic_config_version_file(4.05.06.007 9.0.2.2 0 0 0 0 0 0) # Request [newer major].0.[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.2.7 0 0 0 0 0 0) # Request [newer major].0.[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.2.9 0 0 0 0 0 0) # Request [newer major].0.[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.6.0 0 0 0 0 0 0) # Request [newer major].0.[same patch].0 +test_write_basic_config_version_file(4.05.06.007 9.0.6.2 0 0 0 0 0 0) # Request [newer major].0.[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.6.7 0 0 0 0 0 0) # Request [newer major].0.[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.6.9 0 0 0 0 0 0) # Request [newer major].0.[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.9.0 0 0 0 0 0 0) # Request [newer major].0.[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 9.0.9.2 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.9.7 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.0.9.9 0 0 0 0 0 0) # Request [newer major].0.[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.0.0 0 0 0 0 0 0) # Request [newer major].[older minor].0.0 +test_write_basic_config_version_file(4.05.06.007 9.2.0.2 0 0 0 0 0 0) # Request [newer major].[older minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.0.7 0 0 0 0 0 0) # Request [newer major].[older minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.0.9 0 0 0 0 0 0) # Request [newer major].[older minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.2.0 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 9.2.2.2 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.2.7 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.2.9 0 0 0 0 0 0) # Request [newer major].[older minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.6.0 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 9.2.6.2 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.6.7 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.6.9 0 0 0 0 0 0) # Request [newer major].[older minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.9.0 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 9.2.9.2 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.9.7 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.2.9.9 0 0 0 0 0 0) # Request [newer major].[older minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.0.0 0 0 0 0 0 0) # Request [newer major].[same minor].0.0 +test_write_basic_config_version_file(4.05.06.007 9.5.0.2 0 0 0 0 0 0) # Request [newer major].[same minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.0.7 0 0 0 0 0 0) # Request [newer major].[same minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.0.9 0 0 0 0 0 0) # Request [newer major].[same minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.2.0 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 9.5.2.2 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.2.7 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.2.9 0 0 0 0 0 0) # Request [newer major].[same minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.6.0 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 9.5.6.2 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.6.7 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.6.9 0 0 0 0 0 0) # Request [newer major].[same minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.9.0 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 9.5.9.2 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.9.7 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.5.9.9 0 0 0 0 0 0) # Request [newer major].[same minor].[newer patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.0.0 0 0 0 0 0 0) # Request [newer major].[newer minor].0.0 +test_write_basic_config_version_file(4.05.06.007 9.9.0.2 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.0.7 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.0.9 0 0 0 0 0 0) # Request [newer major].[newer minor].0.[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.2.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].0 +test_write_basic_config_version_file(4.05.06.007 9.9.2.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.2.7 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.2.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[older patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.6.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].0 +test_write_basic_config_version_file(4.05.06.007 9.9.6.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.6.7 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.6.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[same patch].[newer tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.9.0 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].0 +test_write_basic_config_version_file(4.05.06.007 9.9.9.2 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[older tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.9.7 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[same tweak] +test_write_basic_config_version_file(4.05.06.007 9.9.9.9 0 0 0 0 0 0) # Request [newer major].[newer minor].[newer patch].[newer tweak] + + +test_write_basic_config_version_file(4 0...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2...4 1 0 0 0 0 0) +test_write_basic_config_version_file(4 4...<5 1 1 0 0 0 1) +test_write_basic_config_version_file(4 9...10 0 0 0 0 0 0) + +test_write_basic_config_version_file(4 0.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2.8...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2.1...4 1 0 0 0 0 0) +test_write_basic_config_version_file(4 2.8...4 1 0 0 0 0 0) +test_write_basic_config_version_file(4 4.0...<5 1 1 0 0 0 1) +test_write_basic_config_version_file(4 4.8...<5 0 0 0 0 0 0) +test_write_basic_config_version_file(4 4.1...5 0 0 0 0 0 0) +test_write_basic_config_version_file(4 4.8...5 0 0 0 0 0 0) +test_write_basic_config_version_file(4 9.1...10 0 0 0 0 0 0) +test_write_basic_config_version_file(4 9.8...10 0 0 0 0 0 0) + + +test_write_basic_config_version_file(0.05 0.1...<0.6 1 1 0 0 0 0) +test_write_basic_config_version_file(0.05 0.1...1 1 0 0 0 0 0) + + +test_write_basic_config_version_file(4.05 0.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.05 2.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.05 2.8...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.05 2.1...4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.05 2.8...4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.05 2.8...4.8 1 0 0 0 0 0) +test_write_basic_config_version_file(4.05 4.1...<5 1 1 0 0 0 1) +test_write_basic_config_version_file(4.05 4.8...<5 0 0 0 0 0 0) +test_write_basic_config_version_file(4.05 4.5...4.5.8 1 1 1 0 0 1) +test_write_basic_config_version_file(4.05 4.5...<4.6 1 1 1 0 0 1) +test_write_basic_config_version_file(4.05 4.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.05 4.8...5 0 0 0 0 0 0) +test_write_basic_config_version_file(4.05 9.1...10 0 0 0 0 0 0) +test_write_basic_config_version_file(4.05 9.8...10 0 0 0 0 0 0) + + +test_write_basic_config_version_file(0.5.06 0.5...<0.6 1 1 1 0 0 1) +test_write_basic_config_version_file(0.5.06 0.2...<0.6 1 1 0 0 0 0) + + +test_write_basic_config_version_file(4.5.06 0.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 2.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 2.8...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 2.1...4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 2.8...4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 2.8...4.8 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 4.1...<5 1 1 0 0 0 1) +test_write_basic_config_version_file(4.5.06 4.8...<5 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 4.5...4.5.4 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 4.5...4.5.8 1 1 1 0 0 1) +test_write_basic_config_version_file(4.5.06 4.5...<4.6 1 1 1 0 0 1) +test_write_basic_config_version_file(4.5.06 4.1...5 1 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 4.8...5 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 9.1...10 0 0 0 0 0 0) +test_write_basic_config_version_file(4.5.06 9.8...10 0 0 0 0 0 0)
diff --git a/Tests/RunCMake/WriteBasicConfigVersionFile/RunCMakeTest.cmake b/Tests/RunCMake/WriteBasicConfigVersionFile/RunCMakeTest.cmake index 8a9db15..5a541dc 100644 --- a/Tests/RunCMake/WriteBasicConfigVersionFile/RunCMakeTest.cmake +++ b/Tests/RunCMake/WriteBasicConfigVersionFile/RunCMakeTest.cmake
@@ -3,3 +3,4 @@ run_cmake(All) run_cmake(ArchIndependent) run_cmake(LeadingZeroes) +run_cmake(ExactVersionDeprecatedAlias)
diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-Direct-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-Direct-stderr.txt index 53f603e..3b185a1 100644 --- a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-Direct-stderr.txt +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-Direct-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0120-OLD-Direct\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0120-OLD-Direct\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0120 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-stderr.txt index fea708f..85bfb85 100644 --- a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-stderr.txt +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0120-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0120-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0120 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-WARN-Direct-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-WARN-Direct-stderr.txt index 522c8fd..819b39a 100644 --- a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-WARN-Direct-stderr.txt +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-WARN-Direct-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(author\) at [^ ]*/Modules/WriteCompilerDetectionHeader\.cmake:[0-9]+ \(message\): The WriteCompilerDetectionHeader module will be removed by policy CMP0120\. Projects should be ported away from the module, perhaps by bundling a copy @@ -6,4 +6,4 @@ Call Stack \(most recent call first\): CMP0120-WARN-Direct\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-WARN-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-WARN-stderr.txt index 2c68c97..dfe14c5 100644 --- a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-WARN-stderr.txt +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-WARN-stderr.txt
@@ -1,13 +1,13 @@ -^CMake Warning \(dev\) at CMP0120-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0120-WARN\.cmake:[0-9]+ \(include\): Policy CMP0120 is not set: The WriteCompilerDetectionHeader module is removed\. Run "cmake --help-policy CMP0120" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at [^ +CMake Warning \(author\) at [^ ]*/Modules/WriteCompilerDetectionHeader\.cmake:[0-9]+ \(message\): The WriteCompilerDetectionHeader module will be removed by policy CMP0120\. Projects should be ported away from the module, perhaps by bundling a copy @@ -15,4 +15,4 @@ Call Stack \(most recent call first\): CMP0120-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksMultiTargets-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksMultiTargets-check.cmake new file mode 100644 index 0000000..1d2ee36 --- /dev/null +++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksMultiTargets-check.cmake
@@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/findAttribute.cmake) + +findAttribute(${test} "CodeSignOnCopy" TRUE 2)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksMultiTargets.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksMultiTargets.cmake new file mode 100644 index 0000000..3ab035a --- /dev/null +++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksMultiTargets.cmake
@@ -0,0 +1,20 @@ +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") + +add_library(MTestLib SHARED TestLib/TestLib.c TestLib/TestLib.h) +set_target_properties(MTestLib PROPERTIES + FRAMEWORK 1 + PUBLIC_HEADER TestLib/TestLib.h + ) + +add_executable(mapp1 MACOSX_BUNDLE main.m) +add_executable(mapp2 MACOSX_BUNDLE main.m) + +set_target_properties(mapp1 PROPERTIES + XCODE_EMBED_FRAMEWORKS MTestLib + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON +) + +set_target_properties(mapp2 PROPERTIES + XCODE_EMBED_FRAMEWORKS MTestLib + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON +)
diff --git a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake index 26749cd..70b74b8 100644 --- a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
@@ -57,6 +57,19 @@ ) endblock() +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EmbedFrameworksMultiTargets-build) + run_cmake(EmbedFrameworksMultiTargets) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(EmbedFrameworksMultiTargets-build + ${CMAKE_COMMAND} --build . + --config Debug + --target MTestLib + --target mapp1 + --target mapp2 + ) +endblock() + function(TestAppExtension platform) set(testName EmbedAppExtensions-${platform}) if(NOT platform STREQUAL "macOS")
diff --git a/Tests/RunCMake/XcodeProject-Embed/findAttribute.cmake b/Tests/RunCMake/XcodeProject-Embed/findAttribute.cmake index b1963e2..770532e 100644 --- a/Tests/RunCMake/XcodeProject-Embed/findAttribute.cmake +++ b/Tests/RunCMake/XcodeProject-Embed/findAttribute.cmake
@@ -1,19 +1,23 @@ cmake_policy(VERSION 3.1...3.20) function(findAttribute project attr expectPresent) + set(expectCount ${ARGV3}) + + if(NOT expectPresent) + set(expectCount 0) + endif() + execute_process( - COMMAND grep ${attr} ${RunCMake_TEST_BINARY_DIR}/${project}.xcodeproj/project.pbxproj - OUTPUT_VARIABLE output_var - RESULT_VARIABLE result_var + COMMAND grep -c ${attr} ${RunCMake_TEST_BINARY_DIR}/${project}.xcodeproj/project.pbxproj + OUTPUT_VARIABLE actualCount + OUTPUT_STRIP_TRAILING_WHITESPACE ) - if(${expectPresent}) - if(result_var) - set(RunCMake_TEST_FAILED "${attr} attribute is not set" PARENT_SCOPE) - endif() - else() - if(NOT result_var) - set(RunCMake_TEST_FAILED "${attr} attribute is set" PARENT_SCOPE) - endif() + if(NOT actualCount MATCHES "^[0-9]+$") + set(actualCount 0) + endif() + + if(NOT ("${expectCount}" STREQUAL "") AND NOT (actualCount EQUAL expectCount)) + set(RunCMake_TEST_FAILED "${attr} expected ${expectCount} matches, but found ${actualCount}" PARENT_SCOPE) endif() endfunction()
diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake index 2fafdab..463613d 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake
@@ -14,6 +14,8 @@ ]]) set(prototypes_objc [[ #import <CoreFoundation/CoreFoundation.h> +#import <CoreGraphics/CoreGraphics.h> +#import <CoreAudio/CoreAudio.h> ]]) set(impl [[ { @@ -25,6 +27,7 @@ { CFStringRef cfStr = CFSTR("This is a string"); printf("%p %p %ld\n", compress, res_close, (long)CFStringGetLength(cfStr)); + printf("%p %llu\n", (void *)CGColorSpaceCreateDeviceRGB, (unsigned long long)AudioGetCurrentHostTime()); return func1() + func2() + func3() + func4() + func5() + func6() + func7(); } ]]) @@ -137,6 +140,8 @@ imported2 ${libresolv} ${CoreFoundation} + "$<LINK_LIBRARY:FRAMEWORK,CoreGraphics>" + "$<LINK_LIBRARY:FRAMEWORK,CoreAudio.framework>" "${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/sharedFrameworkExt.framework" "${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/staticFrameworkExt.framework" ) @@ -157,6 +162,8 @@ imported2 ${libresolv} ${CoreFoundation} + "$<LINK_LIBRARY:FRAMEWORK,CoreGraphics>" + "$<LINK_LIBRARY:FRAMEWORK,CoreAudio.framework>" "$<LINK_LIBRARY:WEAK_FRAMEWORK,${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/sharedFrameworkExt.framework>" "${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/staticFrameworkExt.framework" ) @@ -177,6 +184,8 @@ imported2 ${libresolv} ${CoreFoundation} + "$<LINK_LIBRARY:FRAMEWORK,CoreGraphics>" + "$<LINK_LIBRARY:FRAMEWORK,CoreAudio.framework>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/sharedFrameworkExt.framework>" "${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/staticFrameworkExt.framework" )
diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_BUILT_ONLY-check.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_BUILT_ONLY-check.cmake index 75edc5c..0c5ca6b 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_BUILT_ONLY-check.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_BUILT_ONLY-check.cmake
@@ -6,14 +6,14 @@ foreach(mainTarget IN ITEMS app1 app2 shared1 shared3 module1 sharedFramework1) checkFlags(OTHER_LDFLAGS ${mainTarget} - "obj2;${libz};${libresolv};CoreFoundation;sharedFrameworkExt;staticFrameworkExt" + "obj2;${libz};${libresolv};CoreFoundation;-framework CoreGraphics;-framework CoreAudio;sharedFrameworkExt;staticFrameworkExt" "static2;shared2;staticFramework2;sharedFramework2" ) endforeach() foreach(mainTarget IN ITEMS app3 shared4) checkFlags(OTHER_LDFLAGS ${mainTarget} - "obj2;${libz};${libresolv};CoreFoundation;sharedFrameworkExt;staticFrameworkExt;shared2;sharedFramework2" + "obj2;${libz};${libresolv};CoreFoundation;-framework CoreGraphics;-framework CoreAudio;sharedFrameworkExt;staticFrameworkExt;shared2;sharedFramework2" "static2;staticFramework2" ) endforeach()
diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_KNOWN_LOCATION-check.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_KNOWN_LOCATION-check.cmake index 1044f78..692c948 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_KNOWN_LOCATION-check.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_KNOWN_LOCATION-check.cmake
@@ -6,14 +6,14 @@ foreach(mainTarget IN ITEMS app1 app2 shared1 shared3 module1 sharedFramework1) checkFlags(OTHER_LDFLAGS ${mainTarget} - "obj2" + "obj2;-framework CoreGraphics;-framework CoreAudio" "static2;shared2;staticFramework2;sharedFramework2;${libz};${libresolv};CoreFoundation;sharedFrameworkExt;staticFrameworkExt" ) endforeach() foreach(mainTarget IN ITEMS app3 shared4) checkFlags(OTHER_LDFLAGS ${mainTarget} - "obj2;shared2;sharedFramework2;sharedFrameworkExt" + "obj2;shared2;sharedFramework2;sharedFrameworkExt;-framework CoreGraphics;-framework CoreAudio" "static2;staticFramework2;${libz};${libresolv};CoreFoundation;staticFrameworkExt" ) endforeach()
diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_NONE-check.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_NONE-check.cmake index b67632b..1845d9f 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_NONE-check.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_NONE-check.cmake
@@ -6,7 +6,7 @@ foreach(mainTarget IN ITEMS app1 app2 app3 shared1 shared3 shared4 module1 sharedFramework1) checkFlags(OTHER_LDFLAGS ${mainTarget} - "static2;shared2;staticFramework2;sharedFramework2;obj2;${libz};${libresolv};CoreFoundation;sharedFrameworkExt;staticFrameworkExt" + "static2;shared2;staticFramework2;sharedFramework2;obj2;${libz};${libresolv};CoreFoundation;-framework CoreGraphics;-framework CoreAudio;sharedFrameworkExt;staticFrameworkExt" "" ) endforeach()
diff --git a/Tests/RunCMake/XcodeProject/SearchPaths-stderr.txt b/Tests/RunCMake/XcodeProject/SearchPaths-stderr.txt index 74abd7d..4b93d20 100644 --- a/Tests/RunCMake/XcodeProject/SearchPaths-stderr.txt +++ b/Tests/RunCMake/XcodeProject/SearchPaths-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at SearchPaths\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at SearchPaths\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0142 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_custom_command/CMP0175-WARN-stderr.txt b/Tests/RunCMake/add_custom_command/CMP0175-WARN-stderr.txt index cd09395..611f155 100644 --- a/Tests/RunCMake/add_custom_command/CMP0175-WARN-stderr.txt +++ b/Tests/RunCMake/add_custom_command/CMP0175-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): +CMake Warning \(policy\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): The following keywords are not supported when using add_custom_command\(TARGET\): DEPENDS, DEPENDS_EXPLICIT_ONLY, DEPFILE, JOB_POOL, MAIN_DEPENDENCY\. @@ -9,9 +9,10 @@ Call Stack \(most recent call first\): CMP0175-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): +CMake Warning \(policy\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): The following keywords are not supported when using add_custom_command\(TARGET\): IMPLICIT_DEPENDS\. @@ -21,9 +22,10 @@ Call Stack \(most recent call first\): CMP0175-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): +CMake Warning \(policy\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): Exactly one of PRE_BUILD, PRE_LINK, or POST_BUILD must be given\. Assuming POST_BUILD to preserve backward compatibility\. @@ -33,9 +35,10 @@ Call Stack \(most recent call first\): CMP0175-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): +CMake Warning \(policy\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): Exactly one of PRE_BUILD, PRE_LINK, or POST_BUILD must be given\. Assuming POST_BUILD to preserve backward compatibility\. @@ -45,9 +48,10 @@ Call Stack \(most recent call first\): CMP0175-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): +CMake Warning \(policy\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): At least one COMMAND must be given\. Policy CMP0175 is not set: add_custom_command\(\) rejects invalid arguments\. @@ -56,9 +60,10 @@ Call Stack \(most recent call first\): CMP0175-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): +CMake Warning \(policy\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): The following keywords are not supported when using add_custom_command\(OUTPUT\): OUTPUTS, POST_BUILD, PRE_BUILD, PRE_LINK, SOURCE\. @@ -69,4 +74,5 @@ Call Stack \(most recent call first\): CMP0175-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_library/CMP0073-stderr.txt b/Tests/RunCMake/add_library/CMP0073-stderr.txt index 9932c7b..0d581ac 100644 --- a/Tests/RunCMake/add_library/CMP0073-stderr.txt +++ b/Tests/RunCMake/add_library/CMP0073-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0073\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0073\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0073 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN-stderr.txt b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN-stderr.txt index b0c5dfb..1a1893c 100644 --- a/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN-stderr.txt +++ b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN-stderr.txt
@@ -1,7 +1,7 @@ -^CMake Warning \(dev\) at TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN\.cmake:[0-9]+ \(add_library\): +^CMake Warning \(author\) at TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN\.cmake:[0-9]+ \(add_library\): ADD_LIBRARY called with SHARED option but the target platform does not support dynamic linking\. Building a STATIC library instead\. This may lead to problems\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/add_subdirectory/CMP0082-OLD-stderr.txt b/Tests/RunCMake/add_subdirectory/CMP0082-OLD-stderr.txt index 0fc4962..8416ab5 100644 --- a/Tests/RunCMake/add_subdirectory/CMP0082-OLD-stderr.txt +++ b/Tests/RunCMake/add_subdirectory/CMP0082-OLD-stderr.txt
@@ -1,8 +1,10 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0082 will be removed from a future version of CMake\. The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_subdirectory/CMP0082-WARN-Nested-stderr.txt b/Tests/RunCMake/add_subdirectory/CMP0082-WARN-Nested-stderr.txt index 3624c43..ec95bcc 100644 --- a/Tests/RunCMake/add_subdirectory/CMP0082-WARN-Nested-stderr.txt +++ b/Tests/RunCMake/add_subdirectory/CMP0082-WARN-Nested-stderr.txt
@@ -1,7 +1,7 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0082 is not set: Install rules from add_subdirectory\(\) are interleaved with those in caller\. Run "cmake --help-policy CMP0082" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_subdirectory/CMP0082-WARN-NestedSub-stderr.txt b/Tests/RunCMake/add_subdirectory/CMP0082-WARN-NestedSub-stderr.txt index 9362905..5d35660 100644 --- a/Tests/RunCMake/add_subdirectory/CMP0082-WARN-NestedSub-stderr.txt +++ b/Tests/RunCMake/add_subdirectory/CMP0082-WARN-NestedSub-stderr.txt
@@ -1,7 +1,7 @@ -CMake Warning \(dev\) in CMP0082-NestedSub/CMakeLists\.txt: +CMake Warning \(policy\) in CMP0082-NestedSub/CMakeLists\.txt: Policy CMP0082 is not set: Install rules from add_subdirectory\(\) are interleaved with those in caller\. Run "cmake --help-policy CMP0082" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_subdirectory/CMP0082-WARN-stderr.txt b/Tests/RunCMake/add_subdirectory/CMP0082-WARN-stderr.txt index 3624c43..ec95bcc 100644 --- a/Tests/RunCMake/add_subdirectory/CMP0082-WARN-stderr.txt +++ b/Tests/RunCMake/add_subdirectory/CMP0082-WARN-stderr.txt
@@ -1,7 +1,7 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0082 is not set: Install rules from add_subdirectory\(\) are interleaved with those in caller\. Run "cmake --help-policy CMP0082" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt index f9d5628..b8b40fe 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-AlphaNumeric\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-AlphaNumeric\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt index 65219b2..5d8166b 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-BracketArgument\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-BracketArgument\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt index fc30a0c..c7a9840 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-EscapedSpecialChars\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-EscapedSpecialChars\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt index 22492de..a66a875 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-FormerInvalidSpecialChars\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-FormerInvalidSpecialChars\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt index 9bdbf31..c5e2350 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-FormerInvalidSpecialChars\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-FormerInvalidSpecialChars\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -8,4 +8,6 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMP0110-OLD-FormerInvalidSpecialCharsMC\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt index 5b0117e..b5f9133 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-LeadingAndTrailingWhitespace\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-LeadingAndTrailingWhitespace\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt index 115378f..38cd45a 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-OtherSpecialChars\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-OtherSpecialChars\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt index 8caa862..ddf0538 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-Quote\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-Quote\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt index 66b60ba..a80a4f9 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-Semicolon\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-Semicolon\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt index 93dee43..0609098 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-Space\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-Space\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt index 253fc88..c3c40ce 100644 --- a/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0110-OLD-ValidSpecialChars\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0110-OLD-ValidSpecialChars\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0110 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/add_test/CMP0110-WARN-BracketArgument-stderr.txt b/Tests/RunCMake/add_test/CMP0110-WARN-BracketArgument-stderr.txt index 19e60c1..ea9ee70 100644 --- a/Tests/RunCMake/add_test/CMP0110-WARN-BracketArgument-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-WARN-BracketArgument-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0110 is not set: add_test\(\) supports arbitrary characters in test names\. Run "cmake --help-policy CMP0110" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -8,4 +8,5 @@ `\[=\[InBracketBeforeSemi;InBracketAfterSemi\]=\]´ -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/CMP0110-WARN-EscapedSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-WARN-EscapedSpecialChars-stderr.txt index 057f2d6..4314c62 100644 --- a/Tests/RunCMake/add_test/CMP0110-WARN-EscapedSpecialChars-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-WARN-EscapedSpecialChars-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0110 is not set: add_test\(\) supports arbitrary characters in test names\. Run "cmake --help-policy CMP0110" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -8,4 +8,5 @@ `\(\) #´ -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/CMP0110-WARN-FormerInvalidSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-WARN-FormerInvalidSpecialChars-stderr.txt index 8c2e01b..ea899d7 100644 --- a/Tests/RunCMake/add_test/CMP0110-WARN-FormerInvalidSpecialChars-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-WARN-FormerInvalidSpecialChars-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0110 is not set: add_test\(\) supports arbitrary characters in test names\. Run "cmake --help-policy CMP0110" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -10,4 +10,5 @@ "\\´ -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/CMP0110-WARN-FormerInvalidSpecialCharsMC-stderr.txt b/Tests/RunCMake/add_test/CMP0110-WARN-FormerInvalidSpecialCharsMC-stderr.txt index 8c2e01b..ea899d7 100644 --- a/Tests/RunCMake/add_test/CMP0110-WARN-FormerInvalidSpecialCharsMC-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-WARN-FormerInvalidSpecialCharsMC-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0110 is not set: add_test\(\) supports arbitrary characters in test names\. Run "cmake --help-policy CMP0110" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -10,4 +10,5 @@ "\\´ -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/CMP0110-WARN-LeadingAndTrailingWhitespace-stderr.txt b/Tests/RunCMake/add_test/CMP0110-WARN-LeadingAndTrailingWhitespace-stderr.txt index a392fed..e25b73b 100644 --- a/Tests/RunCMake/add_test/CMP0110-WARN-LeadingAndTrailingWhitespace-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-WARN-LeadingAndTrailingWhitespace-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0110 is not set: add_test\(\) supports arbitrary characters in test names\. Run "cmake --help-policy CMP0110" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -8,4 +8,5 @@ ` SurroundedByWhitespace ´ -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/CMP0110-WARN-Quote-stderr.txt b/Tests/RunCMake/add_test/CMP0110-WARN-Quote-stderr.txt index 39f3a92..ad39c0d 100644 --- a/Tests/RunCMake/add_test/CMP0110-WARN-Quote-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-WARN-Quote-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0110 is not set: add_test\(\) supports arbitrary characters in test names\. Run "cmake --help-policy CMP0110" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -8,4 +8,5 @@ `BeforeQuote""AfterEscapedQuote´ -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/CMP0110-WARN-Semicolon-stderr.txt b/Tests/RunCMake/add_test/CMP0110-WARN-Semicolon-stderr.txt index 5101618..6c268ee 100644 --- a/Tests/RunCMake/add_test/CMP0110-WARN-Semicolon-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-WARN-Semicolon-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0110 is not set: add_test\(\) supports arbitrary characters in test names\. Run "cmake --help-policy CMP0110" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -8,4 +8,5 @@ `BeforeSemi;AfterSemi´ -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/CMP0110-WARN-Space-stderr.txt b/Tests/RunCMake/add_test/CMP0110-WARN-Space-stderr.txt index 862eaa2..bfe60b7 100644 --- a/Tests/RunCMake/add_test/CMP0110-WARN-Space-stderr.txt +++ b/Tests/RunCMake/add_test/CMP0110-WARN-Space-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in CMakeLists\.txt: +CMake Warning \(policy\) in CMakeLists\.txt: Policy CMP0110 is not set: add_test\(\) supports arbitrary characters in test names\. Run "cmake --help-policy CMP0110" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -8,4 +8,5 @@ `BeforeSpace AfterSpace´ -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/add_test/RunCMakeTest.cmake b/Tests/RunCMake/add_test/RunCMakeTest.cmake index 6e7d53a..6ab1a4d 100644 --- a/Tests/RunCMake/add_test/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_test/RunCMakeTest.cmake
@@ -58,3 +58,61 @@ run_cmake_command(TestLauncher-test ${CMAKE_CTEST_COMMAND} -C Debug -V) endblock() unset(RunCMake_TEST_OPTIONS) + +function(run_testdependency_case CASE_NAME EXPECT_PRESENT) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TestDependency-${CASE_NAME}-build) + run_cmake(TestDependency-${CASE_NAME}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(TestDependency-${CASE_NAME}-check + ${CMAKE_COMMAND} + -DRunCMake_TEST_BINARY_DIR=${RunCMake_TEST_BINARY_DIR} + -Dexpect_present=${EXPECT_PRESENT} + -P ${RunCMake_SOURCE_DIR}/TestDependency-check-targets.cmake) + unset(RunCMake_TEST_NO_CLEAN) +endfunction() + +if(RunCMake_GENERATOR MATCHES Ninja) + block() + run_testdependency_case(DEFAULT FALSE) + run_testdependency_case(OFF FALSE) + run_cmake(TestDependency-ON-invalid-test-name) + + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TestDependency-ON-build) + run_testdependency_case(ON TRUE) + + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + block() + set(RunCMake_TEST_BINARY_DIR + ${RunCMake_BINARY_DIR}/TestDependency-ON-file-generate-config-build) + run_cmake(TestDependency-ON-file-generate-config) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(TestDependency-ON-file-generate-config-build + ${CMAKE_COMMAND} --build . --config Debug + --target test_prep/FileGenerateConfigTest) + endblock() + set(TestDependency_CONFIG Debug) + else() + set(TestDependency_CONFIG "") + endif() + + set(TestDependency_BUILD_CONFIG_ARG) + if(TestDependency_CONFIG) + set(TestDependency_BUILD_CONFIG_ARG --config ${TestDependency_CONFIG}) + endif() + + set(RunCMake_TEST_OUTPUT_MERGE 1) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(TestDependency-ON-all + ${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG} + --target test_prep/all) + run_cmake_command(TestDependency-ON-build + ${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG} + --target test_prep/TargetBuildTest) + run_cmake_command(TestDependency-ON-build-check + ${CMAKE_COMMAND} + -DRunCMake_TEST_BINARY_DIR=${RunCMake_TEST_BINARY_DIR} + -P ${RunCMake_SOURCE_DIR}/TestDependency-build-check.cmake) + unset(RunCMake_TEST_OUTPUT_MERGE) + unset(RunCMake_TEST_NO_CLEAN) + endblock() +endif()
diff --git a/Tests/RunCMake/add_test/TestDependency-DEFAULT.cmake b/Tests/RunCMake/add_test/TestDependency-DEFAULT.cmake new file mode 100644 index 0000000..7397066 --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency-DEFAULT.cmake
@@ -0,0 +1,2 @@ +cmake_minimum_required(VERSION 4.3) +include(${CMAKE_CURRENT_LIST_DIR}/TestDependency.cmake)
diff --git a/Tests/RunCMake/add_test/TestDependency-OFF.cmake b/Tests/RunCMake/add_test/TestDependency-OFF.cmake new file mode 100644 index 0000000..5ae6829 --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency-OFF.cmake
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +set(CMAKE_TEST_BUILD_DEPENDS OFF) +include(${CMAKE_CURRENT_LIST_DIR}/TestDependency.cmake)
diff --git a/Tests/RunCMake/add_test/TestDependency-ON-file-generate-config.cmake b/Tests/RunCMake/add_test/TestDependency-ON-file-generate-config.cmake new file mode 100644 index 0000000..60eb52e --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency-ON-file-generate-config.cmake
@@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.3) +set(CMAKE_TEST_BUILD_DEPENDS ON) + +project(TestDependencyFileGenerateConfig C) + +enable_testing() + +add_executable(TestDependencyGenexFileGenerate main.c) + +set(rc_file + "${CMAKE_CURRENT_BINARY_DIR}/gen/TestDependencyGenexFileGenerate/$<CONFIG>/version.rc") +file(GENERATE + OUTPUT "${rc_file}" + CONTENT "// Config: $<CONFIG>\n1 RCDATA { \"$<CONFIG>\" }\n" + TARGET TestDependencyGenexFileGenerate) +target_sources(TestDependencyGenexFileGenerate PRIVATE "${rc_file}") + +add_test(NAME FileGenerateConfigTest + COMMAND $<TARGET_FILE:TestDependencyGenexFileGenerate>)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/add_test/TestDependency-ON-invalid-test-name-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/add_test/TestDependency-ON-invalid-test-name-result.txt
diff --git a/Tests/RunCMake/add_test/TestDependency-ON-invalid-test-name-stderr.txt b/Tests/RunCMake/add_test/TestDependency-ON-invalid-test-name-stderr.txt new file mode 100644 index 0000000..7df35de --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency-ON-invalid-test-name-stderr.txt
@@ -0,0 +1,5 @@ +CMake Error at TestDependency-ON-invalid-test-name.cmake:[0-9]+ \(add_test\): + add_test Cannot set build dependencies for a test with NAME "Target Build + Test Invalid Name" which is not a valid target name. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_test/TestDependency-ON-invalid-test-name.cmake b/Tests/RunCMake/add_test/TestDependency-ON-invalid-test-name.cmake new file mode 100644 index 0000000..9264f72 --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency-ON-invalid-test-name.cmake
@@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.3) +set(CMAKE_TEST_BUILD_DEPENDS ON) + +project(TestDependencyInvalidTestName C) + +enable_testing() + +add_custom_target(TestDependencyPrereq + COMMAND + "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyPrereq-built.txt" + BYPRODUCTS + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyPrereq-built.txt" + VERBATIM) + +add_test(NAME "Target Build Test Invalid Name" + COMMAND + "${CMAKE_COMMAND}" -E true + BUILD_DEPENDS + TestDependencyPrereq)
diff --git a/Tests/RunCMake/add_test/TestDependency-ON.cmake b/Tests/RunCMake/add_test/TestDependency-ON.cmake new file mode 100644 index 0000000..7a4d3ab --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency-ON.cmake
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +set(CMAKE_TEST_BUILD_DEPENDS ON) +include(${CMAKE_CURRENT_LIST_DIR}/TestDependency.cmake)
diff --git a/Tests/RunCMake/add_test/TestDependency-build-check.cmake b/Tests/RunCMake/add_test/TestDependency-build-check.cmake new file mode 100644 index 0000000..7234dbc --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency-build-check.cmake
@@ -0,0 +1,52 @@ +foreach(name IN ITEMS + TestDependencyExe + TestDependencyGenex + TestDependencyPrereq + TestDependencyFile + TestDependencySubdirPrereq + TestDependencySubdirFile) + set(candidates) + list(APPEND candidates + "${RunCMake_TEST_BINARY_DIR}/${name}-built.txt") + list(APPEND candidates + "${RunCMake_TEST_BINARY_DIR}/TestDependencySubdir/${name}-built.txt") + + set(found FALSE) + foreach(path IN LISTS candidates) + if(EXISTS "${path}") + set(found TRUE) + break() + endif() + endforeach() + + if(NOT found) + message(FATAL_ERROR + "Expected ${name}-built.txt not found.") + endif() +endforeach() + +set(prereq_file "${RunCMake_TEST_BINARY_DIR}/TestDependencyPrereq-built.txt") +if(NOT EXISTS "${prereq_file}") + message(FATAL_ERROR + "Expected TestDependencyPrereq-built.txt to be generated by DEPENDS target.") +endif() + +set(file_dep_file "${RunCMake_TEST_BINARY_DIR}/TestDependencyFile-built.txt") +if(NOT EXISTS "${file_dep_file}") + message(FATAL_ERROR + "Expected TestDependencyFile-built.txt to be generated by DEPENDS file.") +endif() + +set(subdir_prereq_file + "${RunCMake_TEST_BINARY_DIR}/TestDependencySubdir/TestDependencySubdirPrereq-built.txt") +if(NOT EXISTS "${subdir_prereq_file}") + message(FATAL_ERROR + "Expected TestDependencySubdirPrereq-built.txt to be generated by duplicate test DEPENDS target.") +endif() + +set(subdir_file_dep_file + "${RunCMake_TEST_BINARY_DIR}/TestDependencySubdir/TestDependencySubdirFile-built.txt") +if(NOT EXISTS "${subdir_file_dep_file}") + message(FATAL_ERROR + "Expected TestDependencySubdirFile-built.txt to be generated by duplicate test DEPENDS file.") +endif()
diff --git a/Tests/RunCMake/add_test/TestDependency-check-targets.cmake b/Tests/RunCMake/add_test/TestDependency-check-targets.cmake new file mode 100644 index 0000000..9185ed9 --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency-check-targets.cmake
@@ -0,0 +1,28 @@ +if(NOT DEFINED RunCMake_TEST_BINARY_DIR) + message(FATAL_ERROR "RunCMake_TEST_BINARY_DIR not set") +endif() + +if(NOT DEFINED expect_present) + message(FATAL_ERROR "expect_present not set") +endif() + +file(GLOB ninja_files LIST_DIRECTORIES false + "${RunCMake_TEST_BINARY_DIR}/*.ninja" + "${RunCMake_TEST_BINARY_DIR}/*.ninja.in") + +set(found FALSE) +foreach(ninja_file IN LISTS ninja_files) + file(READ "${ninja_file}" content) + if(content MATCHES "test_prep.all|test_prep.TargetBuildTest") + set(found TRUE) + break() + endif() +endforeach() + +if(expect_present AND NOT found) + message(FATAL_ERROR "Expected test_prep targets to be present in ninja files.") +endif() + +if(NOT expect_present AND found) + message(FATAL_ERROR "Expected test_prep targets to be absent from ninja files.") +endif()
diff --git a/Tests/RunCMake/add_test/TestDependency.cmake b/Tests/RunCMake/add_test/TestDependency.cmake new file mode 100644 index 0000000..312dd8a --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependency.cmake
@@ -0,0 +1,49 @@ +project(TestDependency C) + +enable_testing() + +add_executable(TestDependencyExe main.c) +add_custom_command(TARGET TestDependencyExe POST_BUILD + COMMAND + "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyExe-built.txt" + BYPRODUCTS + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyExe-built.txt") + +add_executable(TestDependencyGenex main.c) +add_custom_command(TARGET TestDependencyGenex POST_BUILD + COMMAND + "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyGenex-built.txt" + BYPRODUCTS + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyGenex-built.txt") + +add_custom_target(TestDependencyPrereq + COMMAND + "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyPrereq-built.txt" + BYPRODUCTS + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyPrereq-built.txt" + VERBATIM) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyFile-built.txt" + COMMAND + "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyFile-built.txt" + VERBATIM) + +add_custom_target( + FileDependsTarget + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyFile-built.txt" +) + +add_subdirectory(TestDependencySubdir) + +add_test(NAME TargetBuildTest + COMMAND + TestDependencyExe + $<TARGET_FILE:TestDependencyGenex> + BUILD_DEPENDS + TestDependencyPrereq + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyFile-built.txt")
diff --git a/Tests/RunCMake/add_test/TestDependencySubdir/CMakeLists.txt b/Tests/RunCMake/add_test/TestDependencySubdir/CMakeLists.txt new file mode 100644 index 0000000..262230d --- /dev/null +++ b/Tests/RunCMake/add_test/TestDependencySubdir/CMakeLists.txt
@@ -0,0 +1,25 @@ +add_custom_target(TestDependencySubdirPrereq + COMMAND + "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirPrereq-built.txt" + BYPRODUCTS + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirPrereq-built.txt" + VERBATIM) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirFile-built.txt" + COMMAND + "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirFile-built.txt" + VERBATIM) + +add_custom_target(TestDependencySubdirFile + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirFile-built.txt") + +# Duplicate test name with parent directory +add_test(NAME TargetBuildTest + COMMAND + "${CMAKE_COMMAND}" -E true + BUILD_DEPENDS + TestDependencySubdirPrereq + "${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirFile-built.txt")
diff --git a/Tests/RunCMake/alias_targets/duplicate-target-CMP0107-OLD-stderr.txt b/Tests/RunCMake/alias_targets/duplicate-target-CMP0107-OLD-stderr.txt index f7c5fb0..1c16d1c 100644 --- a/Tests/RunCMake/alias_targets/duplicate-target-CMP0107-OLD-stderr.txt +++ b/Tests/RunCMake/alias_targets/duplicate-target-CMP0107-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at duplicate-target-CMP0107-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at duplicate-target-CMP0107-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0107 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/block/EndWithArgument-stderr.txt b/Tests/RunCMake/block/EndWithArgument-stderr.txt index 934d864..4afc84f 100644 --- a/Tests/RunCMake/block/EndWithArgument-stderr.txt +++ b/Tests/RunCMake/block/EndWithArgument-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) in EndWithArgument\.cmake: +CMake Warning \(author\) in EndWithArgument\.cmake: A logical block closing on the line .+/Tests/RunCMake/block/EndWithArgument\.cmake:[0-9]+ \(endblock\) @@ -6,4 +6,4 @@ has unexpected arguments\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/block/Scope-POLICIES-stderr.txt b/Tests/RunCMake/block/Scope-POLICIES-stderr.txt index 84b99aa..90617e1 100644 --- a/Tests/RunCMake/block/Scope-POLICIES-stderr.txt +++ b/Tests/RunCMake/block/Scope-POLICIES-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Scope-POLICIES\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Scope-POLICIES\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0139 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/block/Scope-VARIABLES-stderr.txt b/Tests/RunCMake/block/Scope-VARIABLES-stderr.txt index 458daa7..013b05a 100644 --- a/Tests/RunCMake/block/Scope-VARIABLES-stderr.txt +++ b/Tests/RunCMake/block/Scope-VARIABLES-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Scope-VARIABLES\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Scope-VARIABLES\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0139 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/block/Scope-stderr.txt b/Tests/RunCMake/block/Scope-stderr.txt index af84e88..1b2addf 100644 --- a/Tests/RunCMake/block/Scope-stderr.txt +++ b/Tests/RunCMake/block/Scope-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Scope\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Scope\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0139 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/build_command/BeforeProject-stderr.txt b/Tests/RunCMake/build_command/BeforeProject-stderr.txt index 9869265..103b446 100644 --- a/Tests/RunCMake/build_command/BeforeProject-stderr.txt +++ b/Tests/RunCMake/build_command/BeforeProject-stderr.txt
@@ -1,7 +1,7 @@ -CMake Warning \(dev\) at BeforeProject\.cmake:2 \(message\): +CMake Warning \(author\) at BeforeProject\.cmake:2 \(message\): build_command\(\) returned: .*cmake.* --build \..* Call Stack \(most recent call first\): CMakeLists\.txt:5 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake index 803518a..89a5f51 100644 --- a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
@@ -15,6 +15,14 @@ run_cmake_script(Locale) unset(RunCMake-stdout-file) +if(CMake_TEST_LOCALE_C_UTF8 OR CMAKE_HOST_WIN32) + set(RunCMake-stdout-file Locale-stdout-utf-8.txt) + run_cmake_command(LocaleC-UTF8 ${CMAKE_COMMAND} -E env LC_CTYPE=C ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/Locale.cmake) + run_cmake_command(LocaleBad-UTF8 ${CMAKE_COMMAND} -E env LC_CTYPE=Bad ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/Locale.cmake) + run_cmake_command(LocalePOSIX-UTF8 ${CMAKE_COMMAND} -E env LC_CTYPE=POSIX ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/Locale.cmake) + unset(RunCMake-stdout-file) +endif() + run_cmake(UnitTest) run_cmake(Exherbo) run_cmake(Ubuntu)
diff --git a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-quiet.cmake b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-quiet.cmake new file mode 100644 index 0000000..35387f4 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-quiet.cmake
@@ -0,0 +1,25 @@ +cmake_diagnostic(SET CMD_EXPERIMENTAL IGNORE) + +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "01234567-0123-0123-0123-0123456789ab") + +cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED + "CxxImportStd" + feature_present) + +if (NOT feature_present STREQUAL "FALSE") + message(FATAL_ERROR + "Expected the `CxxImportStd` feature to be disabled.") +endif () + +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") + +cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED + "CxxImportStd" + feature_present) + +if (NOT feature_present STREQUAL "TRUE") + message(FATAL_ERROR + "Expected the `CxxImportStd` feature to be enabled.") +endif ()
diff --git a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set-stderr.txt b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set-stderr.txt index 5b2791e..499bac2 100644 --- a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set-stderr.txt +++ b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at Experimental/CxxImportStd-set\.cmake:4 \(cmake_language\): +^CMake Warning \(experimental\) at Experimental/CxxImportStd-set\.cmake:4 \(cmake_language\): CMake's support for `import std;` in C\+\+23 and newer 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set.cmake b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set.cmake index 66720a9..f4740c2 100644 --- a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set.cmake +++ b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set.cmake
@@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + "f35a9ac6-8463-4d38-8eec-5d6008153e7d") cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED "CxxImportStd"
diff --git a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-wrong-stderr.txt b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-wrong-stderr.txt index 3a9a9dd..ec68e09 100644 --- a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-wrong-stderr.txt +++ b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-wrong-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at Experimental/CxxImportStd-wrong\.cmake:4 \(cmake_language\): +^CMake Warning \(experimental\) at Experimental/CxxImportStd-wrong\.cmake:4 \(cmake_language\): CMAKE_EXPERIMENTAL_CXX_IMPORT_STD is set to incorrect value 01234567-0123-0123-0123-0123456789ab @@ -9,9 +9,10 @@ this CMake's version of the feature\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\. + -CMake Warning \(dev\) at Experimental/CxxImportStd-wrong\.cmake:19 \(cmake_language\): +CMake Warning \(experimental\) at Experimental/CxxImportStd-wrong\.cmake:19 \(cmake_language\): CMAKE_EXPERIMENTAL_CXX_IMPORT_STD is set to incorrect value 76543210-3210-3210-3210-ba9876543210 @@ -22,4 +23,5 @@ this CMake's version of the feature\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-quiet.cmake b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-quiet.cmake new file mode 100644 index 0000000..cb49601 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-quiet.cmake
@@ -0,0 +1,25 @@ +cmake_diagnostic(SET CMD_EXPERIMENTAL IGNORE) + +set(CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES + "01234567-0123-0123-0123-0123456789ab") + +cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED + "ExportPackageDependencies" + feature_present) + +if (NOT feature_present STREQUAL "FALSE") + message(FATAL_ERROR + "Expected the `ExportPackageDependencies` feature to be disabled.") +endif () + +set(CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES + "1942b4fa-b2c5-4546-9385-83f254070067") + +cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED + "ExportPackageDependencies" + feature_present) + +if (NOT feature_present STREQUAL "TRUE") + message(FATAL_ERROR + "Expected the `ExportPackageDependencies` feature to be enabled.") +endif ()
diff --git a/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set-stderr.txt b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set-stderr.txt index 29e1b60..5f147cc 100644 --- a/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set-stderr.txt +++ b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at Experimental/ExportPackageDependencies-set\.cmake:4 \(cmake_language\): +^CMake Warning \(experimental\) at Experimental/ExportPackageDependencies-set\.cmake:4 \(cmake_language\): CMake's EXPORT_PACKAGE_DEPENDENCIES 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-wrong-stderr.txt b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-wrong-stderr.txt index 3ed3cfd..15f86ba 100644 --- a/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-wrong-stderr.txt +++ b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-wrong-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at Experimental/ExportPackageDependencies-wrong\.cmake:4 \(cmake_language\): +^CMake Warning \(experimental\) at Experimental/ExportPackageDependencies-wrong\.cmake:4 \(cmake_language\): CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES is set to incorrect value 01234567-0123-0123-0123-0123456789ab @@ -9,9 +9,10 @@ this CMake's version of the feature\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\. + -CMake Warning \(dev\) at Experimental/ExportPackageDependencies-wrong\.cmake:19 \(cmake_language\): +CMake Warning \(experimental\) at Experimental/ExportPackageDependencies-wrong\.cmake:19 \(cmake_language\): CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES is set to incorrect value 76543210-3210-3210-3210-ba9876543210 @@ -22,4 +23,5 @@ this CMake's version of the feature\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/cmake_language/RunCMakeTest.cmake b/Tests/RunCMake/cmake_language/RunCMakeTest.cmake index e032366..807d39e 100644 --- a/Tests/RunCMake/cmake_language/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_language/RunCMakeTest.cmake
@@ -160,9 +160,11 @@ run_cmake(Experimental/CxxImportStd-set) run_cmake(Experimental/CxxImportStd-unset) run_cmake(Experimental/CxxImportStd-wrong) +run_cmake(Experimental/CxxImportStd-quiet) run_cmake(Experimental/ExportPackageDependencies-set) run_cmake(Experimental/ExportPackageDependencies-unset) run_cmake(Experimental/ExportPackageDependencies-wrong) +run_cmake(Experimental/ExportPackageDependencies-quiet) run_cmake(Experimental/Unknown) run_cmake(trace)
diff --git a/Tests/RunCMake/cmake_minimum_required/BeforeVersionDeprecated-stderr.txt b/Tests/RunCMake/cmake_minimum_required/BeforeVersionDeprecated-stderr.txt index 06dc7d0..ff8f31f 100644 --- a/Tests/RunCMake/cmake_minimum_required/BeforeVersionDeprecated-stderr.txt +++ b/Tests/RunCMake/cmake_minimum_required/BeforeVersionDeprecated-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at BeforeVersionDeprecated\.cmake:1 \(cmake_minimum_required\): +^CMake Warning \(deprecated\) at BeforeVersionDeprecated\.cmake:1 \(cmake_minimum_required\): Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. @@ -7,8 +7,10 @@ to work with policies introduced by <max> or earlier\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Deprecation Warning at BeforeVersionDeprecated\.cmake:5 \(cmake_policy\): +CMake Warning \(deprecated\) at BeforeVersionDeprecated\.cmake:5 \(cmake_policy\): Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. @@ -16,4 +18,6 @@ to tell CMake that the project requires at least <min> but has been updated to work with policies introduced by <max> or earlier\. Call Stack \(most recent call first\): - CMakeLists\.txt:3 \(include\)$ + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/cmake_minimum_required/RangeBad-stderr.txt b/Tests/RunCMake/cmake_minimum_required/RangeBad-stderr.txt index 2d7afc0..3d0c2f7 100644 --- a/Tests/RunCMake/cmake_minimum_required/RangeBad-stderr.txt +++ b/Tests/RunCMake/cmake_minimum_required/RangeBad-stderr.txt
@@ -1,56 +1,56 @@ -^CMake Error at RangeBad\.cmake:1 \(cmake_minimum_required\): +^CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_minimum_required\): cmake_minimum_required VERSION "3\.11\.\.\." does not have a version on both sides of "\.\.\."\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:2 \(cmake_minimum_required\): +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_minimum_required\): cmake_minimum_required VERSION "\.\.\.3\.11" does not have a version on both sides of "\.\.\."\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:3 \(cmake_minimum_required\): +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_minimum_required\): cmake_minimum_required VERSION "\.\.\." does not have a version on both sides of "\.\.\."\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:4 \(cmake_minimum_required\): +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_minimum_required\): Invalid policy max version value "4"\. A numeric major\.minor\[\.patch\[\.tweak\]\] must be given\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:5 \(cmake_minimum_required\): - Policy VERSION range "3\.11\.\.\.3\.10" specifies a larger minimum than maximum\. +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_minimum_required\): + Policy VERSION range "3\.11\.\.\.3\.10" specifies a later minimum than maximum\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:6 \(cmake_policy\): +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_policy\): cmake_policy VERSION "3\.11\.\.\." does not have a version on both sides of "\.\.\."\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:7 \(cmake_policy\): +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_policy\): cmake_policy VERSION "\.\.\.3\.11" does not have a version on both sides of "\.\.\."\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:8 \(cmake_policy\): +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_policy\): cmake_policy VERSION "\.\.\." does not have a version on both sides of "\.\.\."\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:9 \(cmake_policy\): +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_policy\): Invalid policy max version value "4"\. A numeric major\.minor\[\.patch\[\.tweak\]\] must be given\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Error at RangeBad\.cmake:10 \(cmake_policy\): - Policy VERSION range "3\.11\.\.\.3\.10" specifies a larger minimum than maximum\. +CMake Error at RangeBad\.cmake:[0-9]+ \(cmake_policy\): + Policy VERSION range "3\.11\.\.\.3\.10" specifies a later minimum than maximum\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/ArgN.cmake b/Tests/RunCMake/cmake_parse_arguments/ArgN.cmake new file mode 100644 index 0000000..2977339 --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/ArgN.cmake
@@ -0,0 +1,41 @@ +include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake) + +function(test1) + cmake_parse_arguments(PARSE_ARGN + pref "OPT1;OPT2" "SINGLE1;SINGLE2" "MULTI1;MULTI2") + + TEST(pref_OPT1 TRUE) + TEST(pref_OPT2 FALSE) + TEST(pref_SINGLE1 "foo;bar") + TEST(pref_SINGLE2 UNDEFINED) + TEST(pref_MULTI1 bar foo bar) + TEST(pref_MULTI2 UNDEFINED) + TEST(pref_UNPARSED_ARGUMENTS UNDEFINED) +endfunction() +test1(OPT1 SINGLE1 "foo;bar" MULTI1 bar foo bar) + +function(test2 arg1 arg2) + cmake_parse_arguments(PARSE_ARGN + pref "OPT1;OPT2" "SINGLE1;SINGLE2" "MULTI1;MULTI2") + + TEST(arg1 "first named") + TEST(arg2 "second named") + TEST(pref_OPT1 TRUE) + TEST(pref_OPT2 FALSE) + TEST(pref_SINGLE1 "foo;bar") + TEST(pref_SINGLE2 UNDEFINED) + TEST(pref_MULTI1 bar "foo;bar") + TEST(pref_MULTI2 UNDEFINED) + TEST(pref_UNPARSED_ARGUMENTS UNDEFINED) +endfunction() +test2("first named" "second named" + OPT1 SINGLE1 "foo;bar" MULTI1 bar "foo;bar") + +function(test3 arg1) + cmake_parse_arguments(PARSE_ARGN + pref "" "" "") + + TEST(arg1 "first named") + TEST(pref_UNPARSED_ARGUMENTS "foo;bar" dog cat) +endfunction() +test3("first named" "foo;bar" dog cat)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN1-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/cmake_parse_arguments/BadArgN1-result.txt
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN1-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN1-stderr.txt new file mode 100644 index 0000000..221701a --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN1-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at BadArgN1\.cmake:[0-9]+ \(cmake_parse_arguments\): + PARSE_ARGN must be called with exactly 5 arguments\. +Call Stack \(most recent call first\): + BadArgN1\.cmake:[0-9]+ \(test1\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN1.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgN1.cmake new file mode 100644 index 0000000..67cf5e0 --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN1.cmake
@@ -0,0 +1,4 @@ +function(test1) + cmake_parse_arguments(PARSE_ARGN pref "" "" "" extra) +endfunction() +test1()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN2-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/cmake_parse_arguments/BadArgN2-result.txt
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN2-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN2-stderr.txt new file mode 100644 index 0000000..806756a --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN2-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at BadArgN2\.cmake:[0-9]+ \(cmake_parse_arguments\): + PARSE_ARGN called with _FUNCTION_ARGNC='' that is not an unsigned integer +Call Stack \(most recent call first\): + BadArgN2\.cmake:[0-9]+ \(test2\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN2.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgN2.cmake new file mode 100644 index 0000000..0e91679 --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN2.cmake
@@ -0,0 +1,5 @@ +function(test2) + unset(_FUNCTION_ARGNC) + cmake_parse_arguments(PARSE_ARGN pref "" "" "") +endfunction() +test2()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN3-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/cmake_parse_arguments/BadArgN3-result.txt
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN3-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN3-stderr.txt new file mode 100644 index 0000000..8bda707 --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN3-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at BadArgN3\.cmake:[0-9]+ \(cmake_parse_arguments\): + PARSE_ARGN called with ARGC='bad' that is not an unsigned integer +Call Stack \(most recent call first\): + BadArgN3\.cmake:[0-9]+ \(test3\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN3.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgN3.cmake new file mode 100644 index 0000000..90f2c6e --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN3.cmake
@@ -0,0 +1,5 @@ +function(test3) + set(ARGC bad) + cmake_parse_arguments(PARSE_ARGN pref "" "" "") +endfunction() +test3()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN4-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/cmake_parse_arguments/BadArgN4-result.txt
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN4-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN4-stderr.txt new file mode 100644 index 0000000..c9617a4 --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN4-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at BadArgN4\.cmake:[0-9]+ \(cmake_parse_arguments\): + PARSE_ARGN called with ARGV0 not set +Call Stack \(most recent call first\): + BadArgN4\.cmake:[0-9]+ \(test4\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN4.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgN4.cmake new file mode 100644 index 0000000..627592a --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN4.cmake
@@ -0,0 +1,5 @@ +function(test4) + unset(ARGV0) + cmake_parse_arguments(PARSE_ARGN pref "" "" "") +endfunction() +test4(arg)
diff --git a/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake b/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake index 028bfaf..2b374ee 100644 --- a/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake +++ b/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake
@@ -1,4 +1,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake) +cmake_policy(SET CMP0219 OLD) # example from the documentation # OPTIONAL is a keyword and therefore terminates the definition of
diff --git a/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN-stderr.txt index 0815ed3..84b67b7 100644 --- a/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN-stderr.txt +++ b/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN-stderr.txt
@@ -1,11 +1,11 @@ Testing CMP0174 = NEW Testing CMP0174 = OLD Testing CMP0174 = WARN -CMake Warning \(dev\) at CornerCasesArgvN\.cmake:[0-9]+ \(cmake_parse_arguments\): +CMake Warning \(author\) at CornerCasesArgvN\.cmake:[0-9]+ \(cmake_parse_arguments\): The P1 keyword was followed by an empty string or no value at all\. Policy CMP0174 is not set, so cmake_parse_arguments\(\) will unset the arg_P1 variable rather than setting it to an empty string\. Call Stack \(most recent call first\): CornerCasesArgvN\.cmake:[0-9]+ \(test_cmp0174_warn\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake b/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake index 505840d..dc530a6 100644 --- a/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
@@ -6,9 +6,14 @@ run_cmake(CornerCases) run_cmake(Errors) run_cmake(ArgvN) +run_cmake(ArgN) run_cmake(BadArgvN1) run_cmake(BadArgvN2) run_cmake(BadArgvN3) run_cmake(BadArgvN4) +run_cmake(BadArgN1) +run_cmake(BadArgN2) +run_cmake(BadArgN3) +run_cmake(BadArgN4) run_cmake(CornerCasesArgvN) run_cmake(KeyWordsMissingValues)
diff --git a/Tests/RunCMake/configure_file/UnknownArg-stderr.txt b/Tests/RunCMake/configure_file/UnknownArg-stderr.txt index 66a46f3..76319d1 100644 --- a/Tests/RunCMake/configure_file/UnknownArg-stderr.txt +++ b/Tests/RunCMake/configure_file/UnknownArg-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at UnknownArg\.cmake:1 \(configure_file\): +CMake Warning \(author\) at UnknownArg\.cmake:1 \(configure_file\): configure_file called with unknown argument\(s\): COPY_ONLY @@ -7,4 +7,4 @@ Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/continue/ContinueInMacro-stdout.txt b/Tests/RunCMake/continue/ContinueInMacro-stdout.txt new file mode 100644 index 0000000..de5bb32 --- /dev/null +++ b/Tests/RunCMake/continue/ContinueInMacro-stdout.txt
@@ -0,0 +1,7 @@ +-- start +-- 1 before continue +-- 2 before continue +-- 3 before continue +-- 4 before continue +-- 5 before continue +-- end
diff --git a/Tests/RunCMake/continue/ContinueInMacro.cmake b/Tests/RunCMake/continue/ContinueInMacro.cmake new file mode 100644 index 0000000..4e3d8ca --- /dev/null +++ b/Tests/RunCMake/continue/ContinueInMacro.cmake
@@ -0,0 +1,11 @@ +macro(wrapped_continue) + continue() +endmacro() + +message(STATUS "start") +foreach(iter RANGE 1 5) + message(STATUS "${iter} before continue") + wrapped_continue() + message(STATUS "${iter} after continue") +endforeach() +message(STATUS "end")
diff --git a/Tests/RunCMake/continue/RunCMakeTest.cmake b/Tests/RunCMake/continue/RunCMakeTest.cmake index 37caf9c..9ba7ae6 100644 --- a/Tests/RunCMake/continue/RunCMakeTest.cmake +++ b/Tests/RunCMake/continue/RunCMakeTest.cmake
@@ -4,6 +4,7 @@ run_cmake(ContinueForEachInLists) run_cmake(ContinueNestedForeach) run_cmake(ContinueWhile) +run_cmake(ContinueInMacro) run_cmake(NoArgumentsToContinue) run_cmake(NoEnclosingBlock) run_cmake(NoEnclosingBlockInFunction)
diff --git a/Tests/RunCMake/create_test_sourcelist/CMakeLists.txt b/Tests/RunCMake/create_test_sourcelist/CMakeLists.txt new file mode 100644 index 0000000..902f0bb --- /dev/null +++ b/Tests/RunCMake/create_test_sourcelist/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.10) +project(${RunCMake_TEST} LANGUAGES NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/create_test_sourcelist/Discovery-test-stdout.txt b/Tests/RunCMake/create_test_sourcelist/Discovery-test-stdout.txt new file mode 100644 index 0000000..9157e8c --- /dev/null +++ b/Tests/RunCMake/create_test_sourcelist/Discovery-test-stdout.txt
@@ -0,0 +1,9 @@ +Test project [^ +]*/Tests/RunCMake/create_test_sourcelist/Discovery-build + Start 1: case_one +.* + Start 2: case_two +.* + Start 3: case_three +.* +100% tests passed out of 3
diff --git a/Tests/RunCMake/create_test_sourcelist/Discovery.cmake b/Tests/RunCMake/create_test_sourcelist/Discovery.cmake new file mode 100644 index 0000000..45fae1b --- /dev/null +++ b/Tests/RunCMake/create_test_sourcelist/Discovery.cmake
@@ -0,0 +1,18 @@ +enable_language(C) +enable_testing() + +create_test_sourcelist(DRIVER_SRCS + main.c + case_one.c + case_two.c + case_three.c +) + +add_executable(test_driver ${DRIVER_SRCS}) + +discover_tests(COMMAND test_driver + DISCOVERY_ARGS -N + DISCOVERY_MATCH ".*" + TEST_NAME "\\0" + TEST_ARGS "\\0" +)
diff --git a/Tests/RunCMake/create_test_sourcelist/RunCMakeTest.cmake b/Tests/RunCMake/create_test_sourcelist/RunCMakeTest.cmake new file mode 100644 index 0000000..925c58a --- /dev/null +++ b/Tests/RunCMake/create_test_sourcelist/RunCMakeTest.cmake
@@ -0,0 +1,19 @@ +include(RunCMake) + +# Isolate our ctest runs from external environment. +unset(ENV{CTEST_PARALLEL_LEVEL}) +unset(ENV{CTEST_OUTPUT_ON_FAILURE}) + +if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) +endif() + +function(run_case CASE) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE}-build) + run_cmake(${CASE}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${CASE}-build ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(${CASE}-test ${CMAKE_CTEST_COMMAND} -C Debug) +endfunction() + +run_case(Discovery)
diff --git a/Tests/RunCMake/create_test_sourcelist/case_one.c b/Tests/RunCMake/create_test_sourcelist/case_one.c new file mode 100644 index 0000000..8e84f6f --- /dev/null +++ b/Tests/RunCMake/create_test_sourcelist/case_one.c
@@ -0,0 +1,5 @@ +int case_one(int argc, char** argv) +{ + (void)argc, (void)argv; + return 0; +}
diff --git a/Tests/RunCMake/create_test_sourcelist/case_three.c b/Tests/RunCMake/create_test_sourcelist/case_three.c new file mode 100644 index 0000000..8003555 --- /dev/null +++ b/Tests/RunCMake/create_test_sourcelist/case_three.c
@@ -0,0 +1,5 @@ +int case_three(int argc, char** argv) +{ + (void)argc, (void)argv; + return 0; +}
diff --git a/Tests/RunCMake/create_test_sourcelist/case_two.c b/Tests/RunCMake/create_test_sourcelist/case_two.c new file mode 100644 index 0000000..253a059 --- /dev/null +++ b/Tests/RunCMake/create_test_sourcelist/case_two.c
@@ -0,0 +1,5 @@ +int case_two(int argc, char** argv) +{ + (void)argc, (void)argv; + return 0; +}
diff --git a/Tests/RunCMake/ctest_build/BuildConfigurationCLIVar-check.cmake b/Tests/RunCMake/ctest_build/BuildConfigurationCLIVar-check.cmake new file mode 100644 index 0000000..6e5aaaa --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildConfigurationCLIVar-check.cmake
@@ -0,0 +1,13 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + elseif(NOT build_xml MATCHES "--config.*my-config") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --config argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/BuildConfigurationCLIVar-stderr.txt b/Tests/RunCMake/ctest_build/BuildConfigurationCLIVar-stderr.txt new file mode 100644 index 0000000..de5ab4e --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildConfigurationCLIVar-stderr.txt
@@ -0,0 +1 @@ +Error\(s\) when building project
diff --git a/Tests/RunCMake/ctest_build/BuildDirectories-check.cmake b/Tests/RunCMake/ctest_build/BuildDirectories-check.cmake new file mode 100644 index 0000000..e704c52 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildDirectories-check.cmake
@@ -0,0 +1,18 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml LIMIT 4096) + if(NOT build_xml MATCHES [[<SourceDirectory>]]) + string(REPLACE "\n" "\n " build_xml " ${build_xml}") + set(RunCMake_TEST_FAILED + "Build.xml does not have expected attribute:\n${build_xml}" + ) + endif() + if(NOT build_xml MATCHES [[<BinaryDirectory>]]) + string(REPLACE "\n" "\n " build_xml " ${build_xml}") + set(RunCMake_TEST_FAILED + "Build.xml does not have expected attribute:\n${build_xml}" + ) + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/BuildDirectories-result.txt b/Tests/RunCMake/ctest_build/BuildDirectories-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildDirectories-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_build/BuildDirectories-stderr.txt b/Tests/RunCMake/ctest_build/BuildDirectories-stderr.txt new file mode 100644 index 0000000..bbe9410 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildDirectories-stderr.txt
@@ -0,0 +1 @@ +^Error\(s\) when building project
diff --git a/Tests/RunCMake/ctest_build/BuildPreset-check.cmake b/Tests/RunCMake/ctest_build/BuildPreset-check.cmake new file mode 100644 index 0000000..7dd0228 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPreset-check.cmake
@@ -0,0 +1,10 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/BuildPresetBadFile-result.txt b/Tests/RunCMake/ctest_build/BuildPresetBadFile-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetBadFile-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_build/BuildPresetBadFile-stderr.txt b/Tests/RunCMake/ctest_build/BuildPresetBadFile-stderr.txt new file mode 100644 index 0000000..3cfe9ae --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetBadFile-stderr.txt
@@ -0,0 +1 @@ +File not found: /nonexistent/path/presets.json$
diff --git a/Tests/RunCMake/ctest_build/BuildPresetCLIVar-check.cmake b/Tests/RunCMake/ctest_build/BuildPresetCLIVar-check.cmake new file mode 100644 index 0000000..7dd0228 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetCLIVar-check.cmake
@@ -0,0 +1,10 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/BuildPresetCLIVar-stderr.txt b/Tests/RunCMake/ctest_build/BuildPresetCLIVar-stderr.txt new file mode 100644 index 0000000..de5ab4e --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetCLIVar-stderr.txt
@@ -0,0 +1 @@ +Error\(s\) when building project
diff --git a/Tests/RunCMake/ctest_build/BuildPresetFromFile-check.cmake b/Tests/RunCMake/ctest_build/BuildPresetFromFile-check.cmake new file mode 100644 index 0000000..7ca59c0 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetFromFile-check.cmake
@@ -0,0 +1,14 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() + if(NOT build_xml MATCHES "--presets-file") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --presets-file argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/BuildPresetFromFileVar-check.cmake b/Tests/RunCMake/ctest_build/BuildPresetFromFileVar-check.cmake new file mode 100644 index 0000000..7ca59c0 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetFromFileVar-check.cmake
@@ -0,0 +1,14 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() + if(NOT build_xml MATCHES "--presets-file") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --presets-file argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/BuildPresetGenericVar-check.cmake b/Tests/RunCMake/ctest_build/BuildPresetGenericVar-check.cmake new file mode 100644 index 0000000..7dd0228 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetGenericVar-check.cmake
@@ -0,0 +1,10 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/BuildPresetVar-check.cmake b/Tests/RunCMake/ctest_build/BuildPresetVar-check.cmake new file mode 100644 index 0000000..7dd0228 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetVar-check.cmake
@@ -0,0 +1,10 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/CMakePresets.json.in b/Tests/RunCMake/ctest_build/CMakePresets.json.in new file mode 100644 index 0000000..fca5b7e --- /dev/null +++ b/Tests/RunCMake/ctest_build/CMakePresets.json.in
@@ -0,0 +1,25 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "my-configure-preset", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/../BuildPreset-build" + }, + { + "name": "my-build-preset", + "inherits": "my-configure-preset" + } + ], + "buildPresets": [ + { + "name": "my-build-preset", + "configurePreset": "my-configure-preset" + } + ] +}
diff --git a/Tests/RunCMake/ctest_build/ConfigurationTypeCLIVar-check.cmake b/Tests/RunCMake/ctest_build/ConfigurationTypeCLIVar-check.cmake new file mode 100644 index 0000000..476eab7 --- /dev/null +++ b/Tests/RunCMake/ctest_build/ConfigurationTypeCLIVar-check.cmake
@@ -0,0 +1,13 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + elseif(NOT build_xml MATCHES "--config.*my-type") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --config argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif()
diff --git a/Tests/RunCMake/ctest_build/ConfigurationTypeCLIVar-stderr.txt b/Tests/RunCMake/ctest_build/ConfigurationTypeCLIVar-stderr.txt new file mode 100644 index 0000000..de5ab4e --- /dev/null +++ b/Tests/RunCMake/ctest_build/ConfigurationTypeCLIVar-stderr.txt
@@ -0,0 +1 @@ +Error\(s\) when building project
diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake index f45a972..37995fe 100644 --- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
@@ -61,12 +61,124 @@ run_ctest(BuildCommandFailure) unset(RunCMake_BUILD_COMMAND) +set(RunCMake_USE_CUSTOM_BUILD_COMMAND TRUE) +set(RunCMake_BUILD_COMMAND "${FAKE_BUILD_COMMAND_EXE}") +run_ctest(BuildDirectories) +unset(RunCMake_BUILD_COMMAND) + set(RunCMake_USE_LAUNCHERS FALSE) set(RunCMake_BUILD_COMMAND "${COLOR_WARNING}") run_ctest(IgnoreColor) unset(RunCMake_BUILD_COMMAND) set(RunCMake_USE_CUSTOM_BUILD_COMMAND FALSE) +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPreset") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +run_ctest_build(BuildPreset PRESET my-build-preset) +unset(RunCMake_TEST_SOURCE_DIR) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetVar") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +set(CASE_TEST_PREFIX_CODE [[set(CTEST_BUILD_PRESET "my-build-preset")]]) +run_ctest(BuildPresetVar) +unset(CASE_TEST_PREFIX_CODE) +unset(RunCMake_TEST_SOURCE_DIR) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetGenericVar") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +set(CASE_TEST_PREFIX_CODE [[set(CTEST_PRESET "my-build-preset")]]) +run_ctest(BuildPresetGenericVar) +unset(CASE_TEST_PREFIX_CODE) +unset(RunCMake_TEST_SOURCE_DIR) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetFromFile") +set(custom_presets_file + "${RunCMake_BINARY_DIR}/BuildPresetFromFile/custom-presets.json") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) +run_ctest_build(BuildPresetFromFile + PRESET my-build-preset + PRESETS_FILE "${custom_presets_file}") +unset(RunCMake_TEST_SOURCE_DIR) +unset(custom_presets_file) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetFromFileVar") +set(custom_presets_file + "${RunCMake_BINARY_DIR}/BuildPresetFromFileVar/custom-presets.json") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) +set(CASE_TEST_PREFIX_CODE +"set(CTEST_BUILD_PRESET \"my-build-preset\") +set(CTEST_PRESETS_FILE \"${custom_presets_file}\")") +run_ctest(BuildPresetFromFileVar) +unset(CASE_TEST_PREFIX_CODE) +unset(RunCMake_TEST_SOURCE_DIR) +unset(custom_presets_file) + +run_ctest_build(BuildPresetBadFile + PRESET my-build-preset + PRESETS_FILE /nonexistent/path/presets.json) + +# Helper for tests that verify -D variables reach ctest_build() in -M/-T mode. +# Accepts a case name followed by the -D arguments to pass to ctest. +# Sets up a source directory with CMakePresets.json and a binary directory with +# DartConfiguration.tcl, then runs ctest with -M Experimental -T Build -V. +function(run_build_cli_var_test CASE_NAME) + set(case_source_dir "${RunCMake_BINARY_DIR}/${CASE_NAME}") + set(case_binary_dir "${RunCMake_BINARY_DIR}/${CASE_NAME}-build") + file(MAKE_DIRECTORY "${case_source_dir}") + configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${case_source_dir}/CMakePresets.json" + @ONLY) + file(REMOVE_RECURSE "${case_binary_dir}") + file(MAKE_DIRECTORY "${case_binary_dir}") + file(WRITE "${case_binary_dir}/DartConfiguration.tcl" + "BuildDirectory: ${case_binary_dir}\n" + "SourceDirectory: ${case_source_dir}\n" + "MakeCommand: \"${CMAKE_COMMAND}\" --build \"${case_binary_dir}\"\n") + set(RunCMake_TEST_SOURCE_DIR "${case_source_dir}") + set(RunCMake_TEST_BINARY_DIR "${case_binary_dir}") + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${CASE_NAME} + ${CMAKE_CTEST_COMMAND} + -M Experimental + ${ARGN} + -T Build + -V) +endfunction() + +# Verify that CTEST_BUILD_PRESET passed via -D on the command line reaches +# ctest_build() when ctest is run with -M/-T. +run_build_cli_var_test(BuildPresetCLIVar + -D "CTEST_BUILD_PRESET=my-build-preset") + +# Verify that CTEST_BUILD_CONFIGURATION passed via -D on the command line +# reaches ctest_build() when ctest is run with -M/-T. +run_build_cli_var_test(BuildConfigurationCLIVar + -D "CTEST_BUILD_PRESET=my-build-preset" + -D "CTEST_BUILD_CONFIGURATION=my-config") + +# Verify that CTEST_CONFIGURATION_TYPE passed via -D on the command line +# reaches ctest_build() when ctest is run with -M/-T. +run_build_cli_var_test(ConfigurationTypeCLIVar + -D "CTEST_BUILD_PRESET=my-build-preset" + -D "CTEST_CONFIGURATION_TYPE=my-type") + +set(RunCMake_USE_CUSTOM_BUILD_COMMAND FALSE) if(RunCMake_GENERATOR MATCHES "Ninja") function(run_NinjaLauncherSingleBuildFailure) set(LANG C)
diff --git a/Tests/RunCMake/ctest_configure/CMakePresets.json.in b/Tests/RunCMake/ctest_configure/CMakePresets.json.in new file mode 100644 index 0000000..4bd4d58 --- /dev/null +++ b/Tests/RunCMake/ctest_configure/CMakePresets.json.in
@@ -0,0 +1,21 @@ +{ + "version": 1, + "cmakeMinimumRequired": { + "major": 3, + "minor": 18, + "patch": 0 + }, + "configurePresets": [ + { + "name": "my-preset", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "MY_CUSTOM_VAR": { + "type": "STRING", + "value": "this-gets-set" + } + } + } + ] +}
diff --git a/Tests/RunCMake/ctest_configure/ConfigurePreset-check.cmake b/Tests/RunCMake/ctest_configure/ConfigurePreset-check.cmake new file mode 100644 index 0000000..b89b32b --- /dev/null +++ b/Tests/RunCMake/ctest_configure/ConfigurePreset-check.cmake
@@ -0,0 +1,25 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif()
diff --git a/Tests/RunCMake/ctest_configure/ConfigurePresetFromFile-check.cmake b/Tests/RunCMake/ctest_configure/ConfigurePresetFromFile-check.cmake new file mode 100644 index 0000000..68c0d27 --- /dev/null +++ b/Tests/RunCMake/ctest_configure/ConfigurePresetFromFile-check.cmake
@@ -0,0 +1,29 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() + if(NOT configure_xml MATCHES "\"--presets-file\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --presets-file argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif()
diff --git a/Tests/RunCMake/ctest_configure/ConfigurePresetFromFileVar-check.cmake b/Tests/RunCMake/ctest_configure/ConfigurePresetFromFileVar-check.cmake new file mode 100644 index 0000000..68c0d27 --- /dev/null +++ b/Tests/RunCMake/ctest_configure/ConfigurePresetFromFileVar-check.cmake
@@ -0,0 +1,29 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() + if(NOT configure_xml MATCHES "\"--presets-file\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --presets-file argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif()
diff --git a/Tests/RunCMake/ctest_configure/ConfigurePresetGenericVar-check.cmake b/Tests/RunCMake/ctest_configure/ConfigurePresetGenericVar-check.cmake new file mode 100644 index 0000000..b89b32b --- /dev/null +++ b/Tests/RunCMake/ctest_configure/ConfigurePresetGenericVar-check.cmake
@@ -0,0 +1,25 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif()
diff --git a/Tests/RunCMake/ctest_configure/ConfigurePresetVar-check.cmake b/Tests/RunCMake/ctest_configure/ConfigurePresetVar-check.cmake new file mode 100644 index 0000000..b89b32b --- /dev/null +++ b/Tests/RunCMake/ctest_configure/ConfigurePresetVar-check.cmake
@@ -0,0 +1,25 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif()
diff --git a/Tests/RunCMake/ctest_configure/RunCMakeTest.cmake b/Tests/RunCMake/ctest_configure/RunCMakeTest.cmake index fc1b02c..a98b64c 100644 --- a/Tests/RunCMake/ctest_configure/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_configure/RunCMakeTest.cmake
@@ -8,3 +8,51 @@ endfunction() run_ctest_configure(ConfigureQuiet QUIET) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePreset") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +run_ctest_configure(ConfigurePreset PRESET my-preset) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePresetVar") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +set(CASE_TEST_PREFIX_CODE [[set(CTEST_CONFIGURE_PRESET "my-preset")]]) +run_ctest(ConfigurePresetVar) +unset(CASE_TEST_PREFIX_CODE) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePresetGenericVar") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +set(CASE_TEST_PREFIX_CODE [[set(CTEST_PRESET "my-preset")]]) +run_ctest(ConfigurePresetGenericVar) +unset(CASE_TEST_PREFIX_CODE) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePresetFromFile") +set(custom_presets_file "${RunCMake_BINARY_DIR}/ConfigurePresetFromFile/custom-presets.json") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) +run_ctest_configure(ConfigurePresetFromFile PRESET my-preset PRESETS_FILE "${custom_presets_file}") +unset(custom_presets_file) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePresetFromFileVar") +set(custom_presets_file "${RunCMake_BINARY_DIR}/ConfigurePresetFromFileVar/custom-presets.json") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) +set(CASE_TEST_PREFIX_CODE +"set(CTEST_CONFIGURE_PRESET \"my-preset\") +set(CTEST_PRESETS_FILE \"${custom_presets_file}\")") +run_ctest(ConfigurePresetFromFileVar) +unset(CASE_TEST_PREFIX_CODE) +unset(custom_presets_file) +unset(RunCMake_TEST_SOURCE_DIR)
diff --git a/Tests/RunCMake/ctest_configure/test.cmake.in b/Tests/RunCMake/ctest_configure/test.cmake.in index fa456f3..2d23360 100644 --- a/Tests/RunCMake/ctest_configure/test.cmake.in +++ b/Tests/RunCMake/ctest_configure/test.cmake.in
@@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.10) +@CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/ctest_disabled_test/DisableCleanupTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisableCleanupTest-stdout.txt index a78dfe1..f44584a 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisableCleanupTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisableCleanupTest-stdout.txt
@@ -3,7 +3,7 @@ Start 2: CleanupTest 2/2 Test #2: CleanupTest \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*\Not Run \(Disabled\) +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec +
diff --git a/Tests/RunCMake/ctest_disabled_test/DisableRequiredTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisableRequiredTest-stdout.txt index f56fb5c..a88606f 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisableRequiredTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisableRequiredTest-stdout.txt
@@ -5,7 +5,7 @@ Start 3: SuccessfulCleanupTest 3/3 Test #3: SuccessfulCleanupTest \.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2 + Total Test time \(real\) = +[0-9\.]+ sec +
diff --git a/Tests/RunCMake/ctest_disabled_test/DisableSetupTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisableSetupTest-stdout.txt index 28a1ff5..3cd2573 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisableSetupTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisableSetupTest-stdout.txt
@@ -5,7 +5,7 @@ Start 3: SuccessfulCleanupTest 3/3 Test #3: SuccessfulCleanupTest \.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2 + Total Test time \(real\) = +[0-9\.]+ sec +
diff --git a/Tests/RunCMake/ctest_fixtures/exclude_any_bar-stdout.txt b/Tests/RunCMake/ctest_fixtures/exclude_any_bar-stdout.txt index 620cde7..b0b0793 100644 --- a/Tests/RunCMake/ctest_fixtures/exclude_any_bar-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/exclude_any_bar-stdout.txt
@@ -10,6 +10,6 @@ Start 6: two 5/5 Test #6: two +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 5 +100% tests passed out of 5 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/exclude_any_foo-stdout.txt b/Tests/RunCMake/ctest_fixtures/exclude_any_foo-stdout.txt index d6ab3ec..0f8917c 100644 --- a/Tests/RunCMake/ctest_fixtures/exclude_any_foo-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/exclude_any_foo-stdout.txt
@@ -8,6 +8,6 @@ Start 7: cleanupBar 4/4 Test #7: cleanupBar +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 4 +100% tests passed out of 4 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/exclude_any_foobar-stdout.txt b/Tests/RunCMake/ctest_fixtures/exclude_any_foobar-stdout.txt index ce9c1aa..ccc0c95 100644 --- a/Tests/RunCMake/ctest_fixtures/exclude_any_foobar-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/exclude_any_foobar-stdout.txt
@@ -4,6 +4,6 @@ Start 6: two 2/2 Test #6: two +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/exclude_cleanup_bar-stdout.txt b/Tests/RunCMake/ctest_fixtures/exclude_cleanup_bar-stdout.txt index c7c7da8..9a5ff40 100644 --- a/Tests/RunCMake/ctest_fixtures/exclude_cleanup_bar-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/exclude_cleanup_bar-stdout.txt
@@ -10,6 +10,6 @@ Start 6: two 5/5 Test #6: two +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 5 +100% tests passed out of 5 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/exclude_cleanup_foo-stdout.txt b/Tests/RunCMake/ctest_fixtures/exclude_cleanup_foo-stdout.txt index 58888a4..dbbaa2b 100644 --- a/Tests/RunCMake/ctest_fixtures/exclude_cleanup_foo-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/exclude_cleanup_foo-stdout.txt
@@ -10,6 +10,6 @@ Start 7: cleanupBar 5/5 Test #7: cleanupBar +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 5 +100% tests passed out of 5 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/exclude_setup_bar-stdout.txt b/Tests/RunCMake/ctest_fixtures/exclude_setup_bar-stdout.txt index cba4f1a..d19d832 100644 --- a/Tests/RunCMake/ctest_fixtures/exclude_setup_bar-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/exclude_setup_bar-stdout.txt
@@ -12,6 +12,6 @@ Start 7: cleanupBar 6/6 Test #7: cleanupBar +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 6 +100% tests passed out of 6 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/exclude_setup_foo-stdout.txt b/Tests/RunCMake/ctest_fixtures/exclude_setup_foo-stdout.txt index a0c8337..bd3590a 100644 --- a/Tests/RunCMake/ctest_fixtures/exclude_setup_foo-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/exclude_setup_foo-stdout.txt
@@ -10,6 +10,6 @@ Start 7: cleanupBar 5/5 Test #7: cleanupBar +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 5 +100% tests passed out of 5 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/one-stdout.txt b/Tests/RunCMake/ctest_fixtures/one-stdout.txt index a0005ba..cb06efb 100644 --- a/Tests/RunCMake/ctest_fixtures/one-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/one-stdout.txt
@@ -8,6 +8,6 @@ Start 5: cleanupFoo 4/4 Test #5: cleanupFoo +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 4 +100% tests passed out of 4 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/setupFoo-stdout.txt b/Tests/RunCMake/ctest_fixtures/setupFoo-stdout.txt index a45b66f..14df56d 100644 --- a/Tests/RunCMake/ctest_fixtures/setupFoo-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/setupFoo-stdout.txt
@@ -2,6 +2,6 @@ Start 3: setupFoo 1/1 Test #3: setupFoo +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/three-stdout.txt b/Tests/RunCMake/ctest_fixtures/three-stdout.txt index 2c33c6b..f125153 100644 --- a/Tests/RunCMake/ctest_fixtures/three-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/three-stdout.txt
@@ -12,6 +12,6 @@ Start 7: cleanupBar 6/6 Test #7: cleanupBar +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 6 +100% tests passed out of 6 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/two-stdout.txt b/Tests/RunCMake/ctest_fixtures/two-stdout.txt index 4c03789..f20de15 100644 --- a/Tests/RunCMake/ctest_fixtures/two-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/two-stdout.txt
@@ -6,6 +6,6 @@ Start 7: cleanupBar 3/3 Test #7: cleanupBar +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 3 +100% tests passed out of 3 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_fixtures/unused-stdout.txt b/Tests/RunCMake/ctest_fixtures/unused-stdout.txt index c0a21b0..e714f52 100644 --- a/Tests/RunCMake/ctest_fixtures/unused-stdout.txt +++ b/Tests/RunCMake/ctest_fixtures/unused-stdout.txt
@@ -4,6 +4,6 @@ Start 13: cleanupUnused 2/2 Test #13: cleanupUnused +.+ +Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_memcheck/CMakePresets.json.in b/Tests/RunCMake/ctest_memcheck/CMakePresets.json.in new file mode 100644 index 0000000..c6b2174 --- /dev/null +++ b/Tests/RunCMake/ctest_memcheck/CMakePresets.json.in
@@ -0,0 +1,26 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "my-configure-preset", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ], + "testPresets": [ + { + "name": "my-include-preset", + "configurePreset": "my-configure-preset", + "filter": { + "include": { + "name": "^test1$" + } + } + } + ] +}
diff --git a/Tests/RunCMake/ctest_memcheck/DummyBC-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyBC-stdout.txt index b37e9e8..32ef557 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyBC-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyBC-stdout.txt
@@ -1,3 +1,3 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_memcheck/DummyBCNoLogFile-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyBCNoLogFile-stdout.txt index b37e9e8..32ef557 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyBCNoLogFile-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyBCNoLogFile-stdout.txt
@@ -1,3 +1,3 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_memcheck/DummyPurify-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyPurify-stdout.txt index e988b8f..15373ae 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyPurify-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyPurify-stdout.txt
@@ -1,6 +1,6 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 .* -- Processing memory checking output: MemCheck log files can be found here:.*corresponds to test number.
diff --git a/Tests/RunCMake/ctest_memcheck/DummyPurifyNoLogFile-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyPurifyNoLogFile-stdout.txt index b37e9e8..32ef557 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyPurifyNoLogFile-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyPurifyNoLogFile-stdout.txt
@@ -1,3 +1,3 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_memcheck/DummyQuiet-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyQuiet-stdout.txt index 58f55a5..722d5ed 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyQuiet-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyQuiet-stdout.txt
@@ -1 +1 @@ -0 tests failed out of 1$ +100% tests passed out of 1$
diff --git a/Tests/RunCMake/ctest_memcheck/DummyValgrind-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyValgrind-stdout.txt index e988b8f..15373ae 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyValgrind-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyValgrind-stdout.txt
@@ -1,6 +1,6 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 .* -- Processing memory checking output: MemCheck log files can be found here:.*corresponds to test number.
diff --git a/Tests/RunCMake/ctest_memcheck/DummyValgrindCustomOptions-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyValgrindCustomOptions-stdout.txt index e988b8f..15373ae 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyValgrindCustomOptions-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyValgrindCustomOptions-stdout.txt
@@ -1,6 +1,6 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 .* -- Processing memory checking output: MemCheck log files can be found here:.*corresponds to test number.
diff --git a/Tests/RunCMake/ctest_memcheck/DummyValgrindFailPost-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyValgrindFailPost-stdout.txt index e988b8f..15373ae 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyValgrindFailPost-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyValgrindFailPost-stdout.txt
@@ -1,6 +1,6 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 .* -- Processing memory checking output: MemCheck log files can be found here:.*corresponds to test number.
diff --git a/Tests/RunCMake/ctest_memcheck/DummyValgrindIgnoreMemcheck-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyValgrindIgnoreMemcheck-stdout.txt index 9743ada..6db1106 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyValgrindIgnoreMemcheck-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyValgrindIgnoreMemcheck-stdout.txt
@@ -1,7 +1,7 @@ 2/2 Test #2: RunCMakeAgain .* 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 .* -- Processing memory checking output: MemCheck log files can be found here:.*corresponds to test number.
diff --git a/Tests/RunCMake/ctest_memcheck/DummyValgrindNoDefects-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyValgrindNoDefects-stdout.txt index e988b8f..15373ae 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyValgrindNoDefects-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyValgrindNoDefects-stdout.txt
@@ -1,6 +1,6 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 .* -- Processing memory checking output: MemCheck log files can be found here:.*corresponds to test number.
diff --git a/Tests/RunCMake/ctest_memcheck/DummyValgrindNoLogFile-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyValgrindNoLogFile-stdout.txt index b37e9e8..32ef557 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyValgrindNoLogFile-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyValgrindNoLogFile-stdout.txt
@@ -1,3 +1,3 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_memcheck/DummyValgrindPrePost-stdout.txt b/Tests/RunCMake/ctest_memcheck/DummyValgrindPrePost-stdout.txt index e988b8f..15373ae 100644 --- a/Tests/RunCMake/ctest_memcheck/DummyValgrindPrePost-stdout.txt +++ b/Tests/RunCMake/ctest_memcheck/DummyValgrindPrePost-stdout.txt
@@ -1,6 +1,6 @@ 1/1 MemCheck #1: RunCMake .+ Passed +[0-9]+\.[0-9]+ sec -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 .* -- Processing memory checking output: MemCheck log files can be found here:.*corresponds to test number.
diff --git a/Tests/RunCMake/ctest_memcheck/RunCMakeTest.cmake b/Tests/RunCMake/ctest_memcheck/RunCMakeTest.cmake index 3f6501e..6e5efb9 100644 --- a/Tests/RunCMake/ctest_memcheck/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_memcheck/RunCMakeTest.cmake
@@ -198,3 +198,43 @@ unset(CTEST_MEMCHECK_ARGS) unset(CTEST_SUFFIX_CODE) unset(CTEST_EXTRA_CODE) + +#----------------------------------------------------------------------------- +block() + set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/TestPresetInclude") + set(CMAKELISTS_EXTRA_CODE [[ +foreach(i RANGE 1 3) + add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true) +endforeach() +]]) + configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) + set(CTEST_MEMCHECK_ARGS "PRESET my-include-preset") + run_mc_test(TestPresetInclude "${PSEUDO_VALGRIND}") +endblock() + +block() + set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/TestPresetFromFile") + set(custom_presets_file + "${RunCMake_BINARY_DIR}/TestPresetFromFile/custom-presets.json") + set(CMAKELISTS_EXTRA_CODE [[ +foreach(i RANGE 1 3) + add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true) +endforeach() +]]) + configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) + set(CTEST_MEMCHECK_ARGS + "PRESET my-include-preset PRESETS_FILE \"${custom_presets_file}\"") + run_mc_test(TestPresetFromFile "${PSEUDO_VALGRIND}") +endblock() + +block() + set(CTEST_MEMCHECK_ARGS + "PRESET my-include-preset PRESETS_FILE /nonexistent/path/presets.json") + run_mc_test(TestPresetBadFile "${PSEUDO_VALGRIND}") +endblock()
diff --git a/Tests/RunCMake/ctest_memcheck/TestPresetBadFile-result.txt b/Tests/RunCMake/ctest_memcheck/TestPresetBadFile-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_memcheck/TestPresetBadFile-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_memcheck/TestPresetBadFile-stderr.txt b/Tests/RunCMake/ctest_memcheck/TestPresetBadFile-stderr.txt new file mode 100644 index 0000000..3cfe9ae --- /dev/null +++ b/Tests/RunCMake/ctest_memcheck/TestPresetBadFile-stderr.txt
@@ -0,0 +1 @@ +File not found: /nonexistent/path/presets.json$
diff --git a/Tests/RunCMake/ctest_memcheck/TestPresetFromFile-stdout.txt b/Tests/RunCMake/ctest_memcheck/TestPresetFromFile-stdout.txt new file mode 100644 index 0000000..7ec28f0 --- /dev/null +++ b/Tests/RunCMake/ctest_memcheck/TestPresetFromFile-stdout.txt
@@ -0,0 +1,8 @@ +1/1 MemCheck #[0-9]+: test1 .+ Passed +[0-9]+\.[0-9]+ sec + +100% tests passed out of 1 +.* +-- Processing memory checking output: +MemCheck log files can be found here:.*corresponds to test number. +.*MemoryChecker.*log +Memory checking results:
diff --git a/Tests/RunCMake/ctest_memcheck/TestPresetInclude-stdout.txt b/Tests/RunCMake/ctest_memcheck/TestPresetInclude-stdout.txt new file mode 100644 index 0000000..7ec28f0 --- /dev/null +++ b/Tests/RunCMake/ctest_memcheck/TestPresetInclude-stdout.txt
@@ -0,0 +1,8 @@ +1/1 MemCheck #[0-9]+: test1 .+ Passed +[0-9]+\.[0-9]+ sec + +100% tests passed out of 1 +.* +-- Processing memory checking output: +MemCheck log files can be found here:.*corresponds to test number. +.*MemoryChecker.*log +Memory checking results:
diff --git a/Tests/RunCMake/ctest_skipped_test/SkipCleanupTest-stdout.txt b/Tests/RunCMake/ctest_skipped_test/SkipCleanupTest-stdout.txt index 07a515b..c31f650 100644 --- a/Tests/RunCMake/ctest_skipped_test/SkipCleanupTest-stdout.txt +++ b/Tests/RunCMake/ctest_skipped_test/SkipCleanupTest-stdout.txt
@@ -3,7 +3,7 @@ Start 2: CleanupTest 2/2 Test #2: CleanupTest \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*\Skipped +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2 + Total Test time \(real\) = +[0-9\.]+ sec +
diff --git a/Tests/RunCMake/ctest_skipped_test/SkipRequiredTest-stdout.txt b/Tests/RunCMake/ctest_skipped_test/SkipRequiredTest-stdout.txt index 821dfd9..53fcee4 100644 --- a/Tests/RunCMake/ctest_skipped_test/SkipRequiredTest-stdout.txt +++ b/Tests/RunCMake/ctest_skipped_test/SkipRequiredTest-stdout.txt
@@ -5,7 +5,7 @@ Start 3: SuccessfulCleanupTest 3/3 Test #3: SuccessfulCleanupTest \.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 3 +100% tests passed out of 3 + Total Test time \(real\) = +[0-9\.]+ sec +
diff --git a/Tests/RunCMake/ctest_skipped_test/SkipSetupTest-stdout.txt b/Tests/RunCMake/ctest_skipped_test/SkipSetupTest-stdout.txt index a544530..d3fa9f0 100644 --- a/Tests/RunCMake/ctest_skipped_test/SkipSetupTest-stdout.txt +++ b/Tests/RunCMake/ctest_skipped_test/SkipSetupTest-stdout.txt
@@ -5,7 +5,7 @@ Start 3: SuccessfulCleanupTest 3/3 Test #3: SuccessfulCleanupTest \.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 3 +100% tests passed out of 3 + Total Test time \(real\) = +[0-9\.]+ sec +
diff --git a/Tests/RunCMake/ctest_skipped_test/SkipTest-stdout.txt b/Tests/RunCMake/ctest_skipped_test/SkipTest-stdout.txt index d01ee3e..2352591 100644 --- a/Tests/RunCMake/ctest_skipped_test/SkipTest-stdout.txt +++ b/Tests/RunCMake/ctest_skipped_test/SkipTest-stdout.txt
@@ -3,7 +3,7 @@ Start 2: SkipTest 2/2 Test #2: SkipTest \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*\Skipped +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2 + Total Test time \(real\) = +[0-9\.]+ sec +
diff --git a/Tests/RunCMake/ctest_submit/BadPARTS-variable-result.txt b/Tests/RunCMake/ctest_submit/BadPARTS-variable-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/BadPARTS-variable-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/BadPARTS-variable-stderr.txt b/Tests/RunCMake/ctest_submit/BadPARTS-variable-stderr.txt new file mode 100644 index 0000000..7dd60d7 --- /dev/null +++ b/Tests/RunCMake/ctest_submit/BadPARTS-variable-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .*/Tests/RunCMake/ctest_submit/BadPARTS-variable/test\.cmake:[0-9]+ \(ctest_submit\): + Part name "bad-part" is invalid\.
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt index a42e3f4..f6dcbc8 100644 --- a/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt +++ b/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt
@@ -1 +1 @@ - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt index 093eefb..cb14372 100644 --- a/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt +++ b/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt
@@ -1,3 +1,3 @@ *Error when uploading file: .*/Configure\.xml - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) *Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt index a42e3f4..f6dcbc8 100644 --- a/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt +++ b/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt
@@ -1 +1 @@ - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt index a42e3f4..f6dcbc8 100644 --- a/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt
@@ -1 +1 @@ - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt index 9c3402e..d1a06c4 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt b/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt index 2c5f900..8210ee3 100644 --- a/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt +++ b/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt
@@ -1,3 +1,3 @@ *Error when uploading file: .*/Done\.xml - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) *Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/PARTSDone-variable-check.cmake b/Tests/RunCMake/ctest_submit/PARTSDone-variable-check.cmake new file mode 100644 index 0000000..562b151 --- /dev/null +++ b/Tests/RunCMake/ctest_submit/PARTSDone-variable-check.cmake
@@ -0,0 +1,14 @@ +# Verify that set(CTEST_SUBMIT_PARTS Done) caused only Done.xml to be submitted. +# The upload attempt appears in stderr as "Error when uploading file: .../Done.xml". +if(NOT actual_stderr MATCHES "Error when uploading file:[^\n]*/Done\\.xml") + set(RunCMake_TEST_FAILED + "Expected stderr to contain an upload attempt for Done.xml.\nActual stderr:\n${actual_stderr}") +endif() + +# No other XML part files should have been uploaded. +foreach(unexpected IN ITEMS Configure Build Test Update Coverage MemCheck Notes Upload) + if(actual_stderr MATCHES "Error when uploading file:[^\n]*/${unexpected}\\.xml") + string(APPEND RunCMake_TEST_FAILED + "\nUnexpected upload attempt for ${unexpected}.xml found in stderr.") + endif() +endforeach()
diff --git a/Tests/RunCMake/ctest_submit/PARTSDone-variable-result.txt b/Tests/RunCMake/ctest_submit/PARTSDone-variable-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/PARTSDone-variable-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/PARTSDone-variable-stderr.txt b/Tests/RunCMake/ctest_submit/PARTSDone-variable-stderr.txt new file mode 100644 index 0000000..8210ee3 --- /dev/null +++ b/Tests/RunCMake/ctest_submit/PARTSDone-variable-stderr.txt
@@ -0,0 +1,3 @@ + *Error when uploading file: .*/Done\.xml + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/RunCMakeTest.cmake b/Tests/RunCMake/ctest_submit/RunCMakeTest.cmake index 3c77ddb..ac6da2d 100644 --- a/Tests/RunCMake/ctest_submit/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_submit/RunCMakeTest.cmake
@@ -29,6 +29,19 @@ run_ctest_submit(PARTSCDashUpload PARTS Configure CDASH_UPLOAD) run_ctest_submit(PARTSCDashUploadType PARTS Configure CDASH_UPLOAD_TYPE) run_ctest_submit(PARTSDone PARTS Done) + +# Test CTEST_SUBMIT_PARTS with a valid value. +block() + set(CASE_TEST_PREFIX_CODE "set(CTEST_SUBMIT_PARTS Done)") + run_ctest_submit(PARTSDone-variable) +endblock() + +# Test CTEST_SUBMIT_PARTS with an invalid value. +block() + set(CASE_TEST_PREFIX_CODE "set(CTEST_SUBMIT_PARTS bad-part)") + run_ctest_submit(BadPARTS-variable) +endblock() + run_ctest_submit(CDashUploadPARTS CDASH_UPLOAD bad-upload PARTS) run_ctest_submit(CDashUploadFILES CDASH_UPLOAD bad-upload FILES) run_ctest_submit(CDashUploadNone CDASH_UPLOAD)
diff --git a/Tests/RunCMake/ctest_test/CMakePresets.json.in b/Tests/RunCMake/ctest_test/CMakePresets.json.in new file mode 100644 index 0000000..a973948 --- /dev/null +++ b/Tests/RunCMake/ctest_test/CMakePresets.json.in
@@ -0,0 +1,39 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "my-configure-preset", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + }, + { + "name": "my-include-preset", + "inherits": "my-configure-preset" + } + ], + "testPresets": [ + { + "name": "my-include-preset", + "configurePreset": "my-configure-preset", + "filter": { + "include": { + "name": "^test1$" + } + } + }, + { + "name": "my-exclude-preset", + "configurePreset": "my-configure-preset", + "filter": { + "exclude": { + "name": "^test3$" + } + } + } + ] +}
diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadInvalid-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadInvalid-stdout.txt index b70e11b..3bb26f4 100644 --- a/Tests/RunCMake/ctest_test/CTestTestLoadInvalid-stdout.txt +++ b/Tests/RunCMake/ctest_test/CTestTestLoadInvalid-stdout.txt
@@ -9,6 +9,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadPass-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadPass-stdout.txt index 4ffa0de..a41636d 100644 --- a/Tests/RunCMake/ctest_test/CTestTestLoadPass-stdout.txt +++ b/Tests/RunCMake/ctest_test/CTestTestLoadPass-stdout.txt
@@ -9,6 +9,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt index 07eab96..5cc84e5 100644 --- a/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt +++ b/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt
@@ -10,6 +10,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt index b6015f8..d78646b 100644 --- a/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt +++ b/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt
@@ -10,6 +10,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/ResourceLock-stdout.txt b/Tests/RunCMake/ctest_test/ResourceLock-stdout.txt index 9c67d6d..0e82b49 100644 --- a/Tests/RunCMake/ctest_test/ResourceLock-stdout.txt +++ b/Tests/RunCMake/ctest_test/ResourceLock-stdout.txt
@@ -9,4 +9,4 @@ Start 5: test4 4/4 Test #5: test4 \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 4 +100% tests passed out of 4
diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake index 730efa8..59cbb80 100644 --- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
@@ -13,6 +13,95 @@ run_ctest_test(TestQuiet QUIET) +block() + set(CASE_CMAKELISTS_SUFFIX_CODE [[ +foreach(i RANGE 1 3) + add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true) +endforeach() +]]) + foreach(case IN ITEMS + TestPresetBadName + TestPresetExclude + TestPresetInclude + TestPresetOverride + TestPresetVar + TestPresetGenericVar + ) + configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_BINARY_DIR}/${case}/CMakePresets.json" + @ONLY) + endforeach() + run_ctest_test(TestPresetBadName PRESET nonexistent-preset) + run_ctest_test(TestPresetExclude PRESET my-exclude-preset) + run_ctest_test(TestPresetInclude PRESET my-include-preset) + run_ctest_test(TestPresetOverride PRESET my-include-preset INCLUDE test2) + + set(CASE_TEST_PREFIX_CODE [[set(CTEST_TEST_PRESET "my-include-preset")]]) + run_ctest(TestPresetVar) + unset(CASE_TEST_PREFIX_CODE) + + set(CASE_TEST_PREFIX_CODE [[set(CTEST_PRESET "my-include-preset")]]) + run_ctest(TestPresetGenericVar) + unset(CASE_TEST_PREFIX_CODE) + + # Verify that CTEST_TEST_PRESET passed via -D on the command line reaches + # ctest_test() when ctest is run with -M/-T. + set(case_source_dir "${RunCMake_BINARY_DIR}/TestPresetCLIVar") + set(case_binary_dir "${RunCMake_BINARY_DIR}/TestPresetCLIVar-build") + configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${case_source_dir}/CMakePresets.json" + @ONLY) + file(REMOVE_RECURSE "${case_binary_dir}") + file(MAKE_DIRECTORY "${case_binary_dir}") + file(WRITE "${case_binary_dir}/DartConfiguration.tcl" + "BuildDirectory: ${case_binary_dir}\nSourceDirectory: ${case_source_dir}\n") + file(WRITE "${case_binary_dir}/CTestTestfile.cmake" + "add_test(test1 \"${CMAKE_COMMAND}\" -E true)\n" + "add_test(test2 \"${CMAKE_COMMAND}\" -E true)\n" + "add_test(test3 \"${CMAKE_COMMAND}\" -E true)\n") + set(RunCMake_TEST_BINARY_DIR "${case_binary_dir}") + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(TestPresetCLIVar + ${CMAKE_CTEST_COMMAND} + -C Debug + -M Experimental + -D "CTEST_TEST_PRESET=my-include-preset" + -T Test + -V) + + set(custom_presets_file + "${RunCMake_BINARY_DIR}/TestPresetFileInclude/custom-presets.json") + configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) + set(CASE_CTEST_TEST_RAW_ARGS + "PRESET my-include-preset PRESETS_FILE \"${custom_presets_file}\"") + run_ctest(TestPresetFileInclude) + unset(CASE_CTEST_TEST_RAW_ARGS) + unset(custom_presets_file) + + set(custom_presets_file + "${RunCMake_BINARY_DIR}/TestPresetFromFileVar/custom-presets.json") + configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) + set(CASE_TEST_PREFIX_CODE +"set(CTEST_TEST_PRESET \"my-include-preset\") +set(CTEST_PRESETS_FILE \"${custom_presets_file}\")") + run_ctest(TestPresetFromFileVar) + unset(CASE_TEST_PREFIX_CODE) + unset(custom_presets_file) + + set(CASE_CTEST_TEST_RAW_ARGS + "PRESET my-include-preset PRESETS_FILE /nonexistent/path/presets.json") + run_ctest(TestPresetBadFile) + unset(CASE_CTEST_TEST_RAW_ARGS) +endblock() + set(CASE_CMAKELISTS_SUFFIX_CODE [[ foreach(i RANGE 1 4) add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true) @@ -260,6 +349,15 @@ endfunction() run_environment() +# Test setting of CTEST_TEST_COVERAGE_TOOL +function(run_CTestTestCoverageTool) + set(CASE_CTEST_TEST_ARGS "COVERAGE_TOOL" "LLVM-COV") + set(CASE_TEST_PREFIX_CODE [[ unset(ENV{LLVM_PROFILE_FILE})]]) + + run_ctest(TestCTestTestCoverageTool -VV) +endfunction() +run_CTestTestCoverageTool() + # test for OUTPUT_JUNIT run_ctest_test(OutputJUnit OUTPUT_JUNIT junit.xml REPEAT UNTIL_FAIL:2)
diff --git a/Tests/RunCMake/ctest_test/SerialOrder-stdout.txt b/Tests/RunCMake/ctest_test/SerialOrder-stdout.txt index 81e97e0..52ff903 100644 --- a/Tests/RunCMake/ctest_test/SerialOrder-stdout.txt +++ b/Tests/RunCMake/ctest_test/SerialOrder-stdout.txt
@@ -13,4 +13,4 @@ Start 3: test2 6/6 Test #3: test2 \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 6 +100% tests passed out of 6
diff --git a/Tests/RunCMake/ctest_test/SkipReturnCode-stdout.txt b/Tests/RunCMake/ctest_test/SkipReturnCode-stdout.txt index 9d0b06e..74b59f1 100644 --- a/Tests/RunCMake/ctest_test/SkipReturnCode-stdout.txt +++ b/Tests/RunCMake/ctest_test/SkipReturnCode-stdout.txt
@@ -5,4 +5,4 @@ Start 2: skip 2/2 Test #2: skip \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*Skipped +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 2 +100% tests passed out of 2
diff --git a/Tests/RunCMake/ctest_test/TestCTestTestCoverageTool-stdout.txt b/Tests/RunCMake/ctest_test/TestCTestTestCoverageTool-stdout.txt new file mode 100644 index 0000000..768bb03 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestCTestTestCoverageTool-stdout.txt
@@ -0,0 +1,3 @@ +1: Using environment variable LLVM_PROFILE_FILE=[^ +]*/Tests/RunCMake/ctest_test/[^ +]*_%p.profraw
diff --git a/Tests/RunCMake/ctest_test/TestEnvironment-check.cmake b/Tests/RunCMake/ctest_test/TestEnvironment-check.cmake index 91c9731..7892454 100644 --- a/Tests/RunCMake/ctest_test/TestEnvironment-check.cmake +++ b/Tests/RunCMake/ctest_test/TestEnvironment-check.cmake
@@ -2,7 +2,7 @@ string(REGEX REPLACE "^([^\n]*)\n.*$" "\\1" _date "${_tag}") file(READ "${RunCMake_TEST_BINARY_DIR}/Testing/${_date}/Test.xml" _test_contents) -if(NOT _test_contents MATCHES "<Value>ENV1=env1\nENV2=env2\n#CTEST_RESOURCE_GROUP_COUNT=</Value>") +if(NOT _test_contents MATCHES "<Value>#CTEST_RESOURCE_GROUP_COUNT=\nENV1=env1\nENV2=env2</Value>") string(APPEND RunCMake_TEST_FAILED "Could not find expected environment variables in Test.xml") endif() if(_test_contents MATCHES "BAD_ENVIRONMENT_VARIABLE")
diff --git a/Tests/RunCMake/ctest_test/TestLoadInvalid-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadInvalid-stdout.txt index 0ad3fd1..043ccf8 100644 --- a/Tests/RunCMake/ctest_test/TestLoadInvalid-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestLoadInvalid-stdout.txt
@@ -9,6 +9,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestLoadOrder-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadOrder-stdout.txt index 4f7329b..c9b2521 100644 --- a/Tests/RunCMake/ctest_test/TestLoadOrder-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestLoadOrder-stdout.txt
@@ -9,6 +9,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestLoadPass-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadPass-stdout.txt index c56bed3..f5a8bf4 100644 --- a/Tests/RunCMake/ctest_test/TestLoadPass-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestLoadPass-stdout.txt
@@ -9,6 +9,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt index 4521c58..a4da046 100644 --- a/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt
@@ -10,6 +10,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt index c0b73ad..a8c1ac4 100644 --- a/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt
@@ -10,6 +10,6 @@ ]*)* 1/1 Test #1: RunCMakeVersion \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestPresetBadFile-result.txt b/Tests/RunCMake/ctest_test/TestPresetBadFile-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetBadFile-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_test/TestPresetBadFile-stderr.txt b/Tests/RunCMake/ctest_test/TestPresetBadFile-stderr.txt new file mode 100644 index 0000000..3cfe9ae --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetBadFile-stderr.txt
@@ -0,0 +1 @@ +File not found: /nonexistent/path/presets.json$
diff --git a/Tests/RunCMake/ctest_test/TestPresetBadName-result.txt b/Tests/RunCMake/ctest_test/TestPresetBadName-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetBadName-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_test/TestPresetBadName-stderr.txt b/Tests/RunCMake/ctest_test/TestPresetBadName-stderr.txt new file mode 100644 index 0000000..a986c28 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetBadName-stderr.txt
@@ -0,0 +1,4 @@ +Cannot instantiate test handler ctest_test +CMake Error at [^ +]*/test\.cmake:[0-9]+ \(ctest_test\): + ctest_test No such test preset
diff --git a/Tests/RunCMake/ctest_test/TestPresetCLIVar-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetCLIVar-stdout.txt new file mode 100644 index 0000000..913fad5 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetCLIVar-stdout.txt
@@ -0,0 +1 @@ +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec
diff --git a/Tests/RunCMake/ctest_test/TestPresetExclude-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetExclude-stdout.txt new file mode 100644 index 0000000..271af64 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetExclude-stdout.txt
@@ -0,0 +1,6 @@ + Start [0-9]+: test1 +[0-9]+/3 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec + Start [0-9]+: test2 +[0-9]+/3 Test #[0-9]+: test2 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 3
diff --git a/Tests/RunCMake/ctest_test/TestPresetFileInclude-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetFileInclude-stdout.txt new file mode 100644 index 0000000..a4bf544 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetFileInclude-stdout.txt
@@ -0,0 +1,4 @@ + Start [0-9]+: test1 +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_test/TestPresetFromFileVar-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetFromFileVar-stdout.txt new file mode 100644 index 0000000..a4bf544 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetFromFileVar-stdout.txt
@@ -0,0 +1,4 @@ + Start [0-9]+: test1 +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_test/TestPresetGenericVar-stderr.txt b/Tests/RunCMake/ctest_test/TestPresetGenericVar-stderr.txt new file mode 100644 index 0000000..e00bb59 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetGenericVar-stderr.txt
@@ -0,0 +1 @@ +No build preset named "my-include-preset" found, ignoring CTEST_PRESET\.
diff --git a/Tests/RunCMake/ctest_test/TestPresetGenericVar-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetGenericVar-stdout.txt new file mode 100644 index 0000000..a4bf544 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetGenericVar-stdout.txt
@@ -0,0 +1,4 @@ + Start [0-9]+: test1 +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_test/TestPresetInclude-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetInclude-stdout.txt new file mode 100644 index 0000000..a4bf544 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetInclude-stdout.txt
@@ -0,0 +1,4 @@ + Start [0-9]+: test1 +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_test/TestPresetOverride-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetOverride-stdout.txt new file mode 100644 index 0000000..3529cf6 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetOverride-stdout.txt
@@ -0,0 +1,4 @@ + Start [0-9]+: test2 +1/1 Test #[0-9]+: test2 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_test/TestPresetVar-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetVar-stdout.txt new file mode 100644 index 0000000..a4bf544 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetVar-stdout.txt
@@ -0,0 +1,4 @@ + Start [0-9]+: test1 +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1
diff --git a/Tests/RunCMake/ctest_test/TestRepeatAfterTimeout-stdout.txt b/Tests/RunCMake/ctest_test/TestRepeatAfterTimeout-stdout.txt index 761780d..0096d19 100644 --- a/Tests/RunCMake/ctest_test/TestRepeatAfterTimeout-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestRepeatAfterTimeout-stdout.txt
@@ -5,6 +5,6 @@ Start 1: testRepeat Test #1: testRepeat \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestRepeatUntilPass-stdout.txt b/Tests/RunCMake/ctest_test/TestRepeatUntilPass-stdout.txt index 3cec368..472449f 100644 --- a/Tests/RunCMake/ctest_test/TestRepeatUntilPass-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestRepeatUntilPass-stdout.txt
@@ -5,6 +5,6 @@ Start 1: testRepeat Test #1: testRepeat \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 + Total Test time \(real\) = +[0-9\.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestsFromFile-exclude-empty-stdout.txt b/Tests/RunCMake/ctest_test/TestsFromFile-exclude-empty-stdout.txt index cb6063c..ac8562a 100644 --- a/Tests/RunCMake/ctest_test/TestsFromFile-exclude-empty-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestsFromFile-exclude-empty-stdout.txt
@@ -9,4 +9,4 @@ +Start 4: Test11 4/4 Test #4: Test11 \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 4 +100% tests passed out of 4
diff --git a/Tests/RunCMake/ctest_test/TestsFromFile-exclude-stdout.txt b/Tests/RunCMake/ctest_test/TestsFromFile-exclude-stdout.txt index 5005c80..be82138 100644 --- a/Tests/RunCMake/ctest_test/TestsFromFile-exclude-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestsFromFile-exclude-stdout.txt
@@ -7,5 +7,5 @@ +Start 4: Test11 3/3 Test #4: Test11 \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 3 +100% tests passed out of 3 +
diff --git a/Tests/RunCMake/ctest_test/TestsFromFile-include-stdout.txt b/Tests/RunCMake/ctest_test/TestsFromFile-include-stdout.txt index 4722fcb..76bcee1 100644 --- a/Tests/RunCMake/ctest_test/TestsFromFile-include-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestsFromFile-include-stdout.txt
@@ -3,5 +3,5 @@ +Start 2: Test1 1/1 Test #2: Test1 \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec + -100% tests passed, 0 tests failed out of 1 +100% tests passed out of 1 +
diff --git a/Tests/RunCMake/discover_tests/CMakeLists.txt b/Tests/RunCMake/discover_tests/CMakeLists.txt new file mode 100644 index 0000000..902f0bb --- /dev/null +++ b/Tests/RunCMake/discover_tests/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.10) +project(${RunCMake_TEST} LANGUAGES NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/discover_tests/COMMAND_EXPAND_LISTS-stdout.txt b/Tests/RunCMake/discover_tests/COMMAND_EXPAND_LISTS-stdout.txt new file mode 100644 index 0000000..6995e16 --- /dev/null +++ b/Tests/RunCMake/discover_tests/COMMAND_EXPAND_LISTS-stdout.txt
@@ -0,0 +1,7 @@ + Test #[0-9]*: ExpandLists.OFF.case_one;two + Test #[0-9]*: ExpandLists.OFF.case_three + Test #[0-9]*: ExpandLists.ON.case_one + Test #[0-9]*: ExpandLists.ON.case_two + Test #[0-9]*: ExpandLists.ON.case_three + +Total Tests: 5
diff --git a/Tests/RunCMake/discover_tests/ENVIRONMENT-stdout.txt b/Tests/RunCMake/discover_tests/ENVIRONMENT-stdout.txt new file mode 100644 index 0000000..1657610 --- /dev/null +++ b/Tests/RunCMake/discover_tests/ENVIRONMENT-stdout.txt
@@ -0,0 +1,7 @@ + Start [0-9]*: Env.case_two +.* + +100% tests passed out of 1 + +Label Time Summary: +FOOBAR
diff --git a/Tests/RunCMake/discover_tests/PROPERTY-stdout.txt b/Tests/RunCMake/discover_tests/PROPERTY-stdout.txt new file mode 100644 index 0000000..0b05156 --- /dev/null +++ b/Tests/RunCMake/discover_tests/PROPERTY-stdout.txt
@@ -0,0 +1,4 @@ + Test #[0-9]*: DT.case_one + Test #[0-9]*: DT.case_three + +Total Tests: 2
diff --git a/Tests/RunCMake/discover_tests/RunCMakeTest.cmake b/Tests/RunCMake/discover_tests/RunCMakeTest.cmake new file mode 100644 index 0000000..4042d0a --- /dev/null +++ b/Tests/RunCMake/discover_tests/RunCMakeTest.cmake
@@ -0,0 +1,45 @@ +include(RunCMake) + +# Isolate our ctest runs from external environment. +unset(ENV{CTEST_PARALLEL_LEVEL}) +unset(ENV{CTEST_OUTPUT_ON_FAILURE}) + +if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) +endif() + +block() + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/basic-build") + run_cmake(basic) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(basic-build ${CMAKE_COMMAND} --build . --config Debug) + + run_cmake_command(PROPERTY ${CMAKE_CTEST_COMMAND} -C Debug -N -L LBL1) + + set(ENV{TEST_LABEL} "FOO") + run_cmake_command(ENVIRONMENT ${CMAKE_CTEST_COMMAND} -C Debug -R "Env.") + + run_cmake_command(COMMAND_EXPAND_LISTS + ${CMAKE_CTEST_COMMAND} -C Debug -N -R "ExpandLists.") +endblock() + +block() + run_cmake(bad-discovery-properties-cmake) + run_cmake(bad-test-properties-cmake) +endblock() + +function(run_case CASE) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE}-build) + run_cmake(${CASE}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${CASE}-build ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(${CASE}-test ${CMAKE_CTEST_COMMAND} -C Debug) +endfunction() + +run_case(bad-discovery-properties-ctest) +run_case(bad-test-properties-ctest) + +run_case(bad-command) +run_case(bad-regex) +run_case(discovery-failure) +run_case(discovery-timeout)
diff --git a/Tests/RunCMake/discover_tests/bad-command-test-result.txt b/Tests/RunCMake/discover_tests/bad-command-test-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-command-test-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/discover_tests/bad-command-test-stderr.txt b/Tests/RunCMake/discover_tests/bad-command-test-stderr.txt new file mode 100644 index 0000000..231b307 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-command-test-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error at .* \(discover_tests\): + discover_tests failed to run command:.* + + no such file or directory
diff --git a/Tests/RunCMake/discover_tests/bad-command.cmake b/Tests/RunCMake/discover_tests/bad-command.cmake new file mode 100644 index 0000000..0718468 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-command.cmake
@@ -0,0 +1,11 @@ +enable_language(C) +enable_testing() + +add_executable(fake_discovery fake_discovery.c) + +discover_tests(COMMAND fake_discovery_notfound + DISCOVERY_ARGS --list + DISCOVERY_MATCH "^([^,]+),([^,]+)$" + TEST_NAME "DT.\\1" + TEST_ARGS --run "\\1" +)
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/discover_tests/bad-discovery-properties-cmake-result.txt similarity index 100% copy from Tests/RunCMake/target_sources/FileSetFramework-result.txt copy to Tests/RunCMake/discover_tests/bad-discovery-properties-cmake-result.txt
diff --git a/Tests/RunCMake/discover_tests/bad-discovery-properties-cmake-stderr.txt b/Tests/RunCMake/discover_tests/bad-discovery-properties-cmake-stderr.txt new file mode 100644 index 0000000..4a0ae0c --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-discovery-properties-cmake-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .* \(discover_tests\): + discover_tests DISCOVERY_PROPERTIES must be key-value pairs.
diff --git a/Tests/RunCMake/discover_tests/bad-discovery-properties-cmake.cmake b/Tests/RunCMake/discover_tests/bad-discovery-properties-cmake.cmake new file mode 100644 index 0000000..dc74e53 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-discovery-properties-cmake.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +enable_testing() + +include("${CMAKE_SOURCE_DIR}/shared/bad-discovery-properties.cmake")
diff --git a/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest-test-result.txt b/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest-test-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest-test-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest-test-stderr.txt b/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest-test-stderr.txt new file mode 100644 index 0000000..4a0ae0c --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest-test-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .* \(discover_tests\): + discover_tests DISCOVERY_PROPERTIES must be key-value pairs.
diff --git a/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest.cmake b/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest.cmake new file mode 100644 index 0000000..7526e09 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-discovery-properties-ctest.cmake
@@ -0,0 +1,5 @@ +enable_language(C) +enable_testing() + +set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES + "${CMAKE_SOURCE_DIR}/shared/bad-discovery-properties.cmake")
diff --git a/Tests/RunCMake/discover_tests/bad-regex-test-result.txt b/Tests/RunCMake/discover_tests/bad-regex-test-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-regex-test-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/discover_tests/bad-regex-test-stderr.txt b/Tests/RunCMake/discover_tests/bad-regex-test-stderr.txt new file mode 100644 index 0000000..2380d40 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-regex-test-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .* \(discover_tests\): + discover_tests DISCOVERY_MATCH failed to compile regex "\(".
diff --git a/Tests/RunCMake/discover_tests/bad-regex.cmake b/Tests/RunCMake/discover_tests/bad-regex.cmake new file mode 100644 index 0000000..6acc44a --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-regex.cmake
@@ -0,0 +1,11 @@ +enable_language(C) +enable_testing() + +add_executable(fake_discovery fake_discovery.c) + +discover_tests(COMMAND fake_discovery + DISCOVERY_ARGS --list + DISCOVERY_MATCH "(" + TEST_NAME "DT.\\1" + TEST_ARGS --run "\\1" +)
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/discover_tests/bad-test-properties-cmake-result.txt similarity index 100% copy from Tests/RunCMake/target_sources/FileSetFramework-result.txt copy to Tests/RunCMake/discover_tests/bad-test-properties-cmake-result.txt
diff --git a/Tests/RunCMake/discover_tests/bad-test-properties-cmake-stderr.txt b/Tests/RunCMake/discover_tests/bad-test-properties-cmake-stderr.txt new file mode 100644 index 0000000..2ee9482 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-test-properties-cmake-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .* \(discover_tests\): + discover_tests TEST_PROPERTIES must be key-value pairs.
diff --git a/Tests/RunCMake/discover_tests/bad-test-properties-cmake.cmake b/Tests/RunCMake/discover_tests/bad-test-properties-cmake.cmake new file mode 100644 index 0000000..555d3cc --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-test-properties-cmake.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +enable_testing() + +include("${CMAKE_SOURCE_DIR}/shared/bad-test-properties.cmake")
diff --git a/Tests/RunCMake/discover_tests/bad-test-properties-ctest-test-result.txt b/Tests/RunCMake/discover_tests/bad-test-properties-ctest-test-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-test-properties-ctest-test-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/discover_tests/bad-test-properties-ctest-test-stderr.txt b/Tests/RunCMake/discover_tests/bad-test-properties-ctest-test-stderr.txt new file mode 100644 index 0000000..2ee9482 --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-test-properties-ctest-test-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .* \(discover_tests\): + discover_tests TEST_PROPERTIES must be key-value pairs.
diff --git a/Tests/RunCMake/discover_tests/bad-test-properties-ctest.cmake b/Tests/RunCMake/discover_tests/bad-test-properties-ctest.cmake new file mode 100644 index 0000000..fb535af --- /dev/null +++ b/Tests/RunCMake/discover_tests/bad-test-properties-ctest.cmake
@@ -0,0 +1,5 @@ +enable_language(C) +enable_testing() + +set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES + "${CMAKE_SOURCE_DIR}/shared/bad-test-properties.cmake")
diff --git a/Tests/RunCMake/discover_tests/basic.cmake b/Tests/RunCMake/discover_tests/basic.cmake new file mode 100644 index 0000000..88e00c3 --- /dev/null +++ b/Tests/RunCMake/discover_tests/basic.cmake
@@ -0,0 +1,39 @@ +enable_language(C) +enable_testing() + +add_executable(fake_discovery fake_discovery.c) + +discover_tests(COMMAND fake_discovery + DISCOVERY_ARGS --list_tests + DISCOVERY_MATCH "^([^,]+),([^,]+)$" + TEST_NAME "DT.\\1" + TEST_ARGS "\\1" + TEST_PROPERTIES + LABELS "\\2" +) + +discover_tests(COMMAND fake_discovery + DISCOVERY_ARGS --list_env + DISCOVERY_MATCH "^([^,]+),([^,]+)$" + DISCOVERY_PROPERTIES + ENVIRONMENT "TEST_NAME=two" + ENVIRONMENT_MODIFICATION "TEST_LABEL=string_append:BAR" + TEST_NAME "Env.\\1" + TEST_ARGS "\\1" + TEST_PROPERTIES + LABELS "\\2" +) + +discover_tests(COMMAND fake_discovery --list_args "one;two" + DISCOVERY_ARGS "three" + DISCOVERY_MATCH ".*" + TEST_NAME "ExpandLists.OFF.\\0" + TEST_ARGS "" +) + +discover_tests(COMMAND fake_discovery --list_args "one;two" COMMAND_EXPAND_LISTS + DISCOVERY_ARGS "three" + DISCOVERY_MATCH ".*" + TEST_NAME "ExpandLists.ON.\\0" + TEST_ARGS "" +)
diff --git a/Tests/RunCMake/discover_tests/discovery-failure-test-result.txt b/Tests/RunCMake/discover_tests/discovery-failure-test-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/discover_tests/discovery-failure-test-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/discover_tests/discovery-failure-test-stderr.txt b/Tests/RunCMake/discover_tests/discovery-failure-test-stderr.txt new file mode 100644 index 0000000..7f46757 --- /dev/null +++ b/Tests/RunCMake/discover_tests/discovery-failure-test-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error at .* \(discover_tests\): + discover_tests failed to run command:.* + + discovery failure
diff --git a/Tests/RunCMake/discover_tests/discovery-failure.cmake b/Tests/RunCMake/discover_tests/discovery-failure.cmake new file mode 100644 index 0000000..57eb9d2 --- /dev/null +++ b/Tests/RunCMake/discover_tests/discovery-failure.cmake
@@ -0,0 +1,13 @@ +enable_language(C) +enable_testing() + +add_executable(fake_discovery fake_discovery.c) + +discover_tests(COMMAND fake_discovery + DISCOVERY_ARGS --list_fail + DISCOVERY_MATCH "^([^,]+),([^,]+)$" + TEST_NAME "DT.\\1" + TEST_ARGS "\\1" + TEST_PROPERTIES + LABELS "\\2" +)
diff --git a/Tests/RunCMake/discover_tests/discovery-timeout-test-result.txt b/Tests/RunCMake/discover_tests/discovery-timeout-test-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/discover_tests/discovery-timeout-test-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/discover_tests/discovery-timeout-test-stderr.txt b/Tests/RunCMake/discover_tests/discovery-timeout-test-stderr.txt new file mode 100644 index 0000000..ceb29f2 --- /dev/null +++ b/Tests/RunCMake/discover_tests/discovery-timeout-test-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error at .* \(discover_tests\): + discover_tests failed to run command:.* + + Process terminated due to timeout
diff --git a/Tests/RunCMake/discover_tests/discovery-timeout.cmake b/Tests/RunCMake/discover_tests/discovery-timeout.cmake new file mode 100644 index 0000000..6655b14 --- /dev/null +++ b/Tests/RunCMake/discover_tests/discovery-timeout.cmake
@@ -0,0 +1,15 @@ +enable_language(C) +enable_testing() + +add_executable(fake_discovery fake_discovery.c) + +discover_tests(COMMAND fake_discovery + DISCOVERY_ARGS --list_timeout + DISCOVERY_MATCH "^([^,]+),([^,]+)$" + DISCOVERY_PROPERTIES + TIMEOUT "0.1" + TEST_NAME "DT.\\1" + TEST_ARGS "\\1" + TEST_PROPERTIES + LABELS "\\2" +)
diff --git a/Tests/RunCMake/discover_tests/fake_discovery.c b/Tests/RunCMake/discover_tests/fake_discovery.c new file mode 100644 index 0000000..722072c --- /dev/null +++ b/Tests/RunCMake/discover_tests/fake_discovery.c
@@ -0,0 +1,111 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +#if defined(_MSC_VER) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(_WIN32) +# include <windows.h> +#else +# include <unistd.h> +#endif + +static int list_tests(int ac, char** av) +{ + (void)ac, (void)av; + printf("this line should not match\n"); + printf("case_one,LBL1\n"); + printf("\n"); + printf("case_two,LBL2\n"); + printf("garbage,still,has,commas\n"); + printf("case_three,LBL1\n"); + return 0; +} + +static int list_fail(int ac, char** av) +{ + (void)ac, (void)av; + fprintf(stderr, "discovery failure\n"); + return -1; +} + +static int list_timeout(int ac, char** av) +{ + (void)ac, (void)av; + printf("case_one,LBL1\n"); +#if defined(_WIN32) + Sleep(5000); +#else + sleep(5); +#endif + return 0; +} + +static int list_args(int ac, char** av) +{ + int i; + for (i = 0; i < ac; i++) { + printf("case_%s\n", av[i]); + } + return 0; +} + +static int list_env(int ac, char** av) +{ + (void)ac, (void)av; + printf("case_%s,%s\n", getenv("TEST_NAME"), getenv("TEST_LABEL")); + return 0; +} + +static int success(int ac, char** av) +{ + (void)ac, (void)av; + return 0; +} + +typedef int (*function_ptr)(int, char**); + +struct function_entry +{ + char const* name; + function_ptr function; +}; + +struct function_entry const function_map[] = { + { "--list_tests", list_tests }, + { "--list_fail", list_fail }, + { "--list_timeout", list_timeout }, + { "--list_args", list_args }, + { "--list_env", list_env }, + { "case_one", success }, + { "case_two", success }, + { "case_three", success }, + { NULL, NULL }, +}; + +static function_ptr lookup(char const* name) +{ + struct function_entry const* it = function_map; + for (; it->name != NULL; ++it) { + if (strcmp(name, it->name) == 0) { + return it->function; + } + } + return NULL; +} + +int main(int argc, char** argv) +{ + function_ptr fn = argc > 1 ? lookup(argv[1]) : NULL; + if (fn == NULL) { + fprintf(stderr, "invalid argument\n"); + return -1; + } + + return fn(argc - 2, argv + 2); +}
diff --git a/Tests/RunCMake/discover_tests/shared/bad-discovery-properties.cmake b/Tests/RunCMake/discover_tests/shared/bad-discovery-properties.cmake new file mode 100644 index 0000000..875efb6 --- /dev/null +++ b/Tests/RunCMake/discover_tests/shared/bad-discovery-properties.cmake
@@ -0,0 +1,8 @@ +discover_tests(COMMAND fake_discovery + DISCOVERY_ARGS --list + DISCOVERY_MATCH "^([^,]+),([^,]+)$" + DISCOVERY_PROPERTIES + LABELS UNIT AUTO + TEST_NAME "DT.\\1" + TEST_ARGS --run "\\1" +)
diff --git a/Tests/RunCMake/discover_tests/shared/bad-test-properties.cmake b/Tests/RunCMake/discover_tests/shared/bad-test-properties.cmake new file mode 100644 index 0000000..23bbc2a --- /dev/null +++ b/Tests/RunCMake/discover_tests/shared/bad-test-properties.cmake
@@ -0,0 +1,8 @@ +discover_tests(COMMAND fake_discovery + DISCOVERY_ARGS --list + DISCOVERY_MATCH "^([^,]+),([^,]+)$" + TEST_NAME "DT.\\1" + TEST_ARGS --run "\\1" + TEST_PROPERTIES + LABELS UNIT AUTO +)
diff --git a/Tests/RunCMake/execute_process/EncodingUnknown-stderr.txt b/Tests/RunCMake/execute_process/EncodingUnknown-stderr.txt index 43c250f..ca6adca 100644 --- a/Tests/RunCMake/execute_process/EncodingUnknown-stderr.txt +++ b/Tests/RunCMake/execute_process/EncodingUnknown-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at EncodingUnknown\.cmake:[0-9]+ \(execute_process\): +^CMake Warning \(author\) at EncodingUnknown\.cmake:[0-9]+ \(execute_process\): ENCODING option given unknown value "unknown"\. Ignoring\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/execute_process/Environment-stdout.txt b/Tests/RunCMake/execute_process/Environment-stdout.txt new file mode 100644 index 0000000..747b1ab --- /dev/null +++ b/Tests/RunCMake/execute_process/Environment-stdout.txt
@@ -0,0 +1 @@ +CMAKE_TEST_RUNCMAKE_EXECUTE_PROCESS_ENVIRONMENT=expected_value
diff --git a/Tests/RunCMake/execute_process/Environment.cmake b/Tests/RunCMake/execute_process/Environment.cmake new file mode 100644 index 0000000..86273b5 --- /dev/null +++ b/Tests/RunCMake/execute_process/Environment.cmake
@@ -0,0 +1,5 @@ +execute_process( + COMMAND ${CMAKE_COMMAND} -E environment + ENVIRONMENT + "CMAKE_TEST_RUNCMAKE_EXECUTE_PROCESS_ENVIRONMENT=expected_value" + )
diff --git a/Tests/RunCMake/execute_process/EnvironmentModification-stdout.txt b/Tests/RunCMake/execute_process/EnvironmentModification-stdout.txt new file mode 100644 index 0000000..eb07dc9 --- /dev/null +++ b/Tests/RunCMake/execute_process/EnvironmentModification-stdout.txt
@@ -0,0 +1 @@ +CMAKE_TEST_RUNCMAKE_EXECUTE_PROCESS_ENVIRONMENT=kaesekuchen
diff --git a/Tests/RunCMake/execute_process/EnvironmentModification.cmake b/Tests/RunCMake/execute_process/EnvironmentModification.cmake new file mode 100644 index 0000000..cd23fd3 --- /dev/null +++ b/Tests/RunCMake/execute_process/EnvironmentModification.cmake
@@ -0,0 +1,6 @@ +set(ENV{CMAKE_TEST_RUNCMAKE_EXECUTE_PROCESS_ENVIRONMENT} "kaese") +execute_process( + COMMAND ${CMAKE_COMMAND} -E environment + ENVIRONMENT_MODIFICATION + "CMAKE_TEST_RUNCMAKE_EXECUTE_PROCESS_ENVIRONMENT=string_append:kuchen" + )
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/execute_process/EnvironmentModificationError-result.txt similarity index 100% copy from Tests/RunCMake/target_sources/FileSetFramework-result.txt copy to Tests/RunCMake/execute_process/EnvironmentModificationError-result.txt
diff --git a/Tests/RunCMake/execute_process/EnvironmentModificationError-stderr.txt b/Tests/RunCMake/execute_process/EnvironmentModificationError-stderr.txt new file mode 100644 index 0000000..a3d4c09 --- /dev/null +++ b/Tests/RunCMake/execute_process/EnvironmentModificationError-stderr.txt
@@ -0,0 +1 @@ +CMake Error: Error: Unrecognized environment manipulation argument: invalid
diff --git a/Tests/RunCMake/execute_process/EnvironmentModificationError.cmake b/Tests/RunCMake/execute_process/EnvironmentModificationError.cmake new file mode 100644 index 0000000..9a9a7ed --- /dev/null +++ b/Tests/RunCMake/execute_process/EnvironmentModificationError.cmake
@@ -0,0 +1,5 @@ +execute_process( + COMMAND ${CMAKE_COMMAND} -E environment + ENVIRONMENT_MODIFICATION + "CMAKE_TEST_RUNCMAKE_EXECUTE_PROCESS_ENVIRONMENT=invalid:value" + )
diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake index 973ff12..3137d66 100644 --- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake +++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake
@@ -19,6 +19,10 @@ run_cmake_script(EncodingUTF8 -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE}) endif() +run_cmake(Environment) +run_cmake(EnvironmentModification) +run_cmake(EnvironmentModificationError) + if(EXIT_CODE_EXE) run_cmake_command(ExitValues ${CMAKE_COMMAND} -DEXIT_CODE_EXE=${EXIT_CODE_EXE} -P ${RunCMake_SOURCE_DIR}/ExitValues.cmake) endif()
diff --git a/Tests/RunCMake/exit_crash.c b/Tests/RunCMake/exit_crash.c index 3872abe..caa5377 100644 --- a/Tests/RunCMake/exit_crash.c +++ b/Tests/RunCMake/exit_crash.c
@@ -1,3 +1,7 @@ +#ifdef __POCC__ +# pragma warn(disable : 2801) /* Store to a non-writable location. */ +#endif + int main(int argc, char const* argv[]) { #ifndef __clang_analyzer__ /* Suppress clang-analyzer warnings */
diff --git a/Tests/RunCMake/export/EmptyExportFile-CMP0208-WARN-stderr.txt b/Tests/RunCMake/export/EmptyExportFile-CMP0208-WARN-stderr.txt index 93a1072..7c07b9a 100644 --- a/Tests/RunCMake/export/EmptyExportFile-CMP0208-WARN-stderr.txt +++ b/Tests/RunCMake/export/EmptyExportFile-CMP0208-WARN-stderr.txt
@@ -1,15 +1,16 @@ -CMake Warning \(dev\) at EmptyExportFile-CMP0208-WARN\.cmake:[0-9]+ \(export\): +CMake Warning \(author\) at EmptyExportFile-CMP0208-WARN\.cmake:[0-9]+ \(export\): export EXPORT given invalid argument: FILE: missing required value Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at EmptyExportFile-CMP0208-WARN\.cmake:[0-9]+ \(export\): +CMake Warning \(policy\) at EmptyExportFile-CMP0208-WARN\.cmake:[0-9]+ \(export\): Policy CMP0208 is not set: export\(EXPORT\) does not allow empty arguments\. Run "cmake --help-policy CMP0208" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/export/FindDependencyExport-stderr.txt b/Tests/RunCMake/export/FindDependencyExport-stderr.txt index 446b1b1..62db3ad 100644 --- a/Tests/RunCMake/export/FindDependencyExport-stderr.txt +++ b/Tests/RunCMake/export/FindDependencyExport-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at FindDependencyExport\.cmake:[0-9]+ \(export\): +^CMake Warning \(experimental\) at FindDependencyExport\.cmake:[0-9]+ \(export\): CMake's EXPORT_PACKAGE_DEPENDENCIES 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/export/FindDependencyExportFetchContent-stderr.txt b/Tests/RunCMake/export/FindDependencyExportFetchContent-stderr.txt index 86ed890..9a8acae 100644 --- a/Tests/RunCMake/export/FindDependencyExportFetchContent-stderr.txt +++ b/Tests/RunCMake/export/FindDependencyExportFetchContent-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at FindDependencyExportFetchContent\.cmake:[0-9]+ \(install\): +^CMake Warning \(experimental\) at FindDependencyExportFetchContent\.cmake:[0-9]+ \(install\): CMake's EXPORT_PACKAGE_DEPENDENCIES 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/export/FindDependencyExportShared-stderr.txt b/Tests/RunCMake/export/FindDependencyExportShared-stderr.txt index c47a0f8..75da61b 100644 --- a/Tests/RunCMake/export/FindDependencyExportShared-stderr.txt +++ b/Tests/RunCMake/export/FindDependencyExportShared-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at FindDependencyExportShared\.cmake:[0-9]+ \(export\): +^CMake Warning \(experimental\) at FindDependencyExportShared\.cmake:[0-9]+ \(export\): CMake's EXPORT_PACKAGE_DEPENDENCIES 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/export/FindDependencyExportStatic-stderr.txt b/Tests/RunCMake/export/FindDependencyExportStatic-stderr.txt index 347b65e..53f9133 100644 --- a/Tests/RunCMake/export/FindDependencyExportStatic-stderr.txt +++ b/Tests/RunCMake/export/FindDependencyExportStatic-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at FindDependencyExportStatic\.cmake:[0-9]+ \(export\): +^CMake Warning \(experimental\) at FindDependencyExportStatic\.cmake:[0-9]+ \(export\): CMake's EXPORT_PACKAGE_DEPENDENCIES 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-OLD-stderr.txt b/Tests/RunCMake/export/Repeat-CMP0103-OLD-stderr.txt index ada0a43..45ddc2b 100644 --- a/Tests/RunCMake/export/Repeat-CMP0103-OLD-stderr.txt +++ b/Tests/RunCMake/export/Repeat-CMP0103-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Repeat-CMP0103-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Repeat-CMP0103-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0103 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-WARN-stderr.txt b/Tests/RunCMake/export/Repeat-CMP0103-WARN-stderr.txt index 01bcfbc..4ad1850 100644 --- a/Tests/RunCMake/export/Repeat-CMP0103-WARN-stderr.txt +++ b/Tests/RunCMake/export/Repeat-CMP0103-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at Repeat\.cmake:[0-9]+ \(export\): +CMake Warning \(policy\) at Repeat\.cmake:[0-9]+ \(export\): Policy CMP0103 is not set: Multiple export\(\) with same FILE without APPEND is not allowed\. Run "cmake --help-policy CMP0103" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -11,9 +11,10 @@ Call Stack \(most recent call first\): Repeat-CMP0103-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at Repeat/CMakeLists\.txt:[0-9]+ \(export\): +CMake Warning \(policy\) at Repeat/CMakeLists\.txt:[0-9]+ \(export\): Policy CMP0103 is not set: Multiple export\(\) with same FILE without APPEND is not allowed\. Run "cmake --help-policy CMP0103" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -23,4 +24,5 @@ .+/foo\.cmake Did you miss 'APPEND' keyword\? -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/export/SymbolicComponent-check.cmake b/Tests/RunCMake/export/SymbolicComponent-check.cmake index 43d96fc..617a648 100644 --- a/Tests/RunCMake/export/SymbolicComponent-check.cmake +++ b/Tests/RunCMake/export/SymbolicComponent-check.cmake
@@ -1,4 +1,4 @@ file(READ "${RunCMake_TEST_BINARY_DIR}/foo.cmake" foo) -if("${foo}" MATCHES "add_library\(foo INTERFACE SYMBOLIC IMPORTED\)") - string(APPEND RunCMake_TEST_FAILED "Symbolic Component Foo was not exported\n") +if(NOT "${foo}" MATCHES "add_library\\(foo INTERFACE SYMBOLIC IMPORTED\\)") + string(APPEND RunCMake_TEST_FAILED "Interface library 'foo' Component Foo was not exported\n") endif()
diff --git a/Tests/RunCMake/file-CREATE_LINK/CMP0205-HardLink-WARN-stderr.txt b/Tests/RunCMake/file-CREATE_LINK/CMP0205-HardLink-WARN-stderr.txt index 3cd83de..46f8f26 100644 --- a/Tests/RunCMake/file-CREATE_LINK/CMP0205-HardLink-WARN-stderr.txt +++ b/Tests/RunCMake/file-CREATE_LINK/CMP0205-HardLink-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(policy\) at [^ ]*/CMP0205-common\.cmake:[0-9]+ \(file\): Path @@ -15,9 +15,10 @@ ]*/CMP0205-common-WARN\.cmake:[0-9]+ \(include\) [^ ]*/CMP0205-HardLink-WARN\.cmake:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at [^ +CMake Warning \(policy\) at [^ ]*/CMP0205-common\.cmake:[0-9]+ \(file\): Path @@ -34,4 +35,5 @@ ]*/CMP0205-common-WARN\.cmake:[0-9]+ \(include\) [^ ]*/CMP0205-HardLink-WARN\.cmake:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/file-CREATE_LINK/CMP0205-SymLink-WARN-stderr.txt b/Tests/RunCMake/file-CREATE_LINK/CMP0205-SymLink-WARN-stderr.txt index baa9f8d..720409a 100644 --- a/Tests/RunCMake/file-CREATE_LINK/CMP0205-SymLink-WARN-stderr.txt +++ b/Tests/RunCMake/file-CREATE_LINK/CMP0205-SymLink-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at [^ +^CMake Warning \(policy\) at [^ ]*/CMP0205-common\.cmake:[0-9]+ \(file\): Path @@ -15,4 +15,5 @@ ]*/CMP0205-common-WARN\.cmake:[0-9]+ \(include\) [^ ]*/CMP0205-SymLink-WARN\.cmake:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/file-DOWNLOAD/unused-argument-stderr.txt b/Tests/RunCMake/file-DOWNLOAD/unused-argument-stderr.txt index 576fbbd..fa636f7 100644 --- a/Tests/RunCMake/file-DOWNLOAD/unused-argument-stderr.txt +++ b/Tests/RunCMake/file-DOWNLOAD/unused-argument-stderr.txt
@@ -1,6 +1,6 @@ -^CMake Warning \(dev\) at common\.cmake:[0-9]+ \(file\): +^CMake Warning \(author\) at common\.cmake:[0-9]+ \(file\): Unexpected argument: JUNK Call Stack \(most recent call first\): unused-argument\.cmake:[0-9]+ \(file_download\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/CMP0207-WARN-all-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/CMP0207-WARN-all-stderr.txt index 1d8f189..6618aa0 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/CMP0207-WARN-all-stderr.txt +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/CMP0207-WARN-all-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at cmake_install\.cmake:[0-9]+ \(file\): +^CMake Warning \(policy\) at cmake_install\.cmake:[0-9]+ \(file\): Policy CMP0207 is not set: file\(GET_RUNTIME_DEPENDENCIES\) normalizes paths before matching\. Run "cmake --help-policy CMP0207" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -11,9 +11,10 @@ "[^"]*test\.dll" -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at cmake_install\.cmake:[0-9]+ \(file\): +CMake Warning \(policy\) at cmake_install\.cmake:[0-9]+ \(file\): Policy CMP0207 is not set: file\(GET_RUNTIME_DEPENDENCIES\) normalizes paths before matching\. Run "cmake --help-policy CMP0207" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -26,9 +27,10 @@ "[^"]*test\.dll" -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at cmake_install\.cmake:[0-9]+ \(file\): +CMake Warning \(policy\) at cmake_install\.cmake:[0-9]+ \(file\): Policy CMP0207 is not set: file\(GET_RUNTIME_DEPENDENCIES\) normalizes paths before matching\. Run "cmake --help-policy CMP0207" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -41,4 +43,5 @@ "[^"]*test\.dll" -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs1-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs1-stderr.txt index 0874473..9988919 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs1-stderr.txt +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs1-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at badargs1\.cmake:[0-9]+ \(file\): +^CMake Warning \(author\) at badargs1\.cmake:[0-9]+ \(file\): You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is probably not what you intended to do\. Instead, please consider using it in an install\(CODE\) or install\(SCRIPT\) command\. For example: @@ -10,7 +10,7 @@ ]]\) Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Error at badargs1\.cmake:[0-9]+ \(file\): file Unrecognized argument: "invalid"
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt index 39f307d..b09799f 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at badargs2\.cmake:[0-9]+ \(file\): +^CMake Warning \(author\) at badargs2\.cmake:[0-9]+ \(file\): You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is probably not what you intended to do\. Instead, please consider using it in an install\(CODE\) or install\(SCRIPT\) command\. For example: @@ -10,7 +10,7 @@ ]]\) Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Error at badargs2\.cmake:[0-9]+ \(file\): Error after keyword "BUNDLE_EXECUTABLE":
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-notfile.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-notfile.cmake index 3e4c434..80ad6ca 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-notfile.cmake +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-notfile.cmake
@@ -20,6 +20,10 @@ install(CODE [[ file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>") + + # workaround for rdar://FB22863998, an otool bug: + file(TOUCH "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>/dummy.txt") + file(GET_RUNTIME_DEPENDENCIES EXECUTABLES "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>"
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/project-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/project-stderr.txt index f14a10d..1fc9424 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/project-stderr.txt +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/project-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at project\.cmake:[0-9]+ \(file\): +^CMake Warning \(author\) at project\.cmake:[0-9]+ \(file\): You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is probably not what you intended to do\. Instead, please consider using it in an install\(CODE\) or install\(SCRIPT\) command\. For example: @@ -10,4 +10,4 @@ ]]\) Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stderr.txt b/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stderr.txt index 3f4b928..b075f31 100644 --- a/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stderr.txt +++ b/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0159-Common\.cmake:[0-9]+ \(file\): +CMake Warning \(policy\) at CMP0159-Common\.cmake:[0-9]+ \(file\): Policy CMP0159 is not set: file\(STRINGS\) with REGEX updates CMAKE_MATCH_<n>\. Run "cmake --help-policy CMP0159" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -7,4 +7,5 @@ Call Stack \(most recent call first\): CMP0159-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late-stderr.txt b/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late-stderr.txt index af722a4..44c8a5b 100644 --- a/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late-stderr.txt +++ b/Tests/RunCMake/file/GLOB-warn-CONFIGURE_DEPENDS-late-stderr.txt
@@ -1,6 +1,6 @@ -^CMake Warning \(dev\) at GLOB-warn-CONFIGURE_DEPENDS-late\.cmake:[0-9]+ \(file\): +^CMake Warning \(author\) at GLOB-warn-CONFIGURE_DEPENDS-late\.cmake:[0-9]+ \(file\): CONFIGURE_DEPENDS flag was given after a glob expression was already evaluated\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version-stderr.txt b/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version-stderr.txt index 8d22332..f906110 100644 --- a/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version-stderr.txt +++ b/Tests/RunCMake/file/GLOB_RECURSE-warn-CONFIGURE_DEPENDS-ninja-version-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\): +^CMake Warning \(author\): The detected version of Ninja: .* @@ -10,4 +10,4 @@ Any pre-check scripts, such as those generated for file\(GLOB CONFIGURE_DEPENDS\), will not be run by Ninja\. -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/file/LOCK-include-stdout.txt b/Tests/RunCMake/file/LOCK-include-stdout.txt new file mode 100644 index 0000000..a6e0bab --- /dev/null +++ b/Tests/RunCMake/file/LOCK-include-stdout.txt
@@ -0,0 +1,3 @@ +-- File locked before include +-- File locked in include +-- File locked after include
diff --git a/Tests/RunCMake/file/LOCK-include.cmake b/Tests/RunCMake/file/LOCK-include.cmake new file mode 100644 index 0000000..1cb0ed8 --- /dev/null +++ b/Tests/RunCMake/file/LOCK-include.cmake
@@ -0,0 +1,13 @@ +set(FILE_TO_LOCK "${CMAKE_CURRENT_BINARY_DIR}/file.lock") + +file(LOCK "${FILE_TO_LOCK}" GUARD FILE TIMEOUT 0) +message(STATUS "File locked before include") +file(LOCK "${FILE_TO_LOCK}" RELEASE) + +include("${CMAKE_CURRENT_LIST_DIR}/lock_include_example.cmake") + +# Since the scope is 'GUARD FILE', after the 'include()' call, the file should +# be in an unlocked state +file(LOCK "${FILE_TO_LOCK}" GUARD FILE TIMEOUT 0) +message(STATUS "File locked after include") +file(LOCK "${FILE_TO_LOCK}" RELEASE)
diff --git a/Tests/RunCMake/file/LOCK-script-stdout.txt b/Tests/RunCMake/file/LOCK-script-stdout.txt new file mode 100644 index 0000000..e3e15b5 --- /dev/null +++ b/Tests/RunCMake/file/LOCK-script-stdout.txt
@@ -0,0 +1,3 @@ +-- File locked in script +-- File locked in include +-- File locked in script with include
diff --git a/Tests/RunCMake/file/LOCK-script.cmake b/Tests/RunCMake/file/LOCK-script.cmake new file mode 100644 index 0000000..47b06bb --- /dev/null +++ b/Tests/RunCMake/file/LOCK-script.cmake
@@ -0,0 +1,17 @@ +set(FILE_TO_LOCK "${CMAKE_CURRENT_BINARY_DIR}/file.lock") + +execute_process( + COMMAND + "${CMAKE_COMMAND}" + -D "FILE_TO_LOCK=${FILE_TO_LOCK}" + -P "${CMAKE_CURRENT_LIST_DIR}/lock_script_example.cmake" + COMMAND_ERROR_IS_FATAL ANY +) + +execute_process( + COMMAND + "${CMAKE_COMMAND}" + -D "FILE_TO_LOCK=${FILE_TO_LOCK}" + -P "${CMAKE_CURRENT_LIST_DIR}/lock_script_include_example.cmake" + COMMAND_ERROR_IS_FATAL ANY +)
diff --git a/Tests/RunCMake/file/READ_SYMLINK-abs-stdout.txt b/Tests/RunCMake/file/READ_SYMLINK-abs-stdout.txt new file mode 100644 index 0000000..048c98e --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-abs-stdout.txt
@@ -0,0 +1,2 @@ +^-- result: '0' +-- target: '[^']*/Tests/RunCMake/file/READ_SYMLINK-target.txt'$
diff --git a/Tests/RunCMake/file/READ_SYMLINK-abs.cmake b/Tests/RunCMake/file/READ_SYMLINK-abs.cmake new file mode 100644 index 0000000..6ed242c --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-abs.cmake
@@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/READ_SYMLINK-common.cmake)
diff --git a/Tests/RunCMake/file/READ_SYMLINK-common.cmake b/Tests/RunCMake/file/READ_SYMLINK-common.cmake new file mode 100644 index 0000000..c8f7ae4 --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-common.cmake
@@ -0,0 +1,6 @@ +file(READ_SYMLINK "${link}" target RESULT result) +if(CMAKE_HOST_WIN32) + string(REPLACE [[\]] [[/]] target "${target}") +endif() +message(STATUS "result: '${result}'") +message(STATUS "target: '${target}'")
diff --git a/Tests/RunCMake/file/READ_SYMLINK-noexist-capture-stdout.txt b/Tests/RunCMake/file/READ_SYMLINK-noexist-capture-stdout.txt new file mode 100644 index 0000000..94d119c --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-noexist-capture-stdout.txt
@@ -0,0 +1,2 @@ +-- result: 'READ_SYMLINK requested of path that is not a symlink: + [^']*/Tests/RunCMake/file/READ_SYMLINK-noexist-capture-build/rel\.sym'
diff --git a/Tests/RunCMake/file/READ_SYMLINK-noexist-capture.cmake b/Tests/RunCMake/file/READ_SYMLINK-noexist-capture.cmake new file mode 100644 index 0000000..69947a0 --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-noexist-capture.cmake
@@ -0,0 +1,5 @@ +file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" target RESULT result) +message(STATUS "result: '${result}'") +if(DEFINED target) + message(FATAL_ERROR "Target variable should not be defined on failure, got:\n '${target}'") +endif()
diff --git a/Tests/RunCMake/file/READ_SYMLINK-noexist-stderr.txt b/Tests/RunCMake/file/READ_SYMLINK-noexist-stderr.txt index 32b3e85..4673f9a 100644 --- a/Tests/RunCMake/file/READ_SYMLINK-noexist-stderr.txt +++ b/Tests/RunCMake/file/READ_SYMLINK-noexist-stderr.txt
@@ -1,6 +1,6 @@ -^CMake Error at READ_SYMLINK-noexist\.cmake:[0-9]+ \(file\): +^CMake Error at [^ +]*/Tests/RunCMake/file/READ_SYMLINK-noexist\.cmake:[0-9]+ \(file\): file READ_SYMLINK requested of path that is not a symlink: - .*/Tests/RunCMake/file/READ_SYMLINK-noexist-build/rel\.sym -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + [^ +]*/Tests/RunCMake/file/READ_SYMLINK-noexist-build/rel\.sym$
diff --git a/Tests/RunCMake/file/READ_SYMLINK-notsymlink-capture-stdout.txt b/Tests/RunCMake/file/READ_SYMLINK-notsymlink-capture-stdout.txt new file mode 100644 index 0000000..f27f8df --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-notsymlink-capture-stdout.txt
@@ -0,0 +1,2 @@ +-- result: 'READ_SYMLINK requested of path that is not a symlink: + [^']*/Tests/RunCMake/file/READ_SYMLINK-notsymlink-capture-build/rel\.sym'
diff --git a/Tests/RunCMake/file/READ_SYMLINK-notsymlink-capture.cmake b/Tests/RunCMake/file/READ_SYMLINK-notsymlink-capture.cmake new file mode 100644 index 0000000..69947a0 --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-notsymlink-capture.cmake
@@ -0,0 +1,5 @@ +file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" target RESULT result) +message(STATUS "result: '${result}'") +if(DEFINED target) + message(FATAL_ERROR "Target variable should not be defined on failure, got:\n '${target}'") +endif()
diff --git a/Tests/RunCMake/file/READ_SYMLINK-notsymlink-stderr.txt b/Tests/RunCMake/file/READ_SYMLINK-notsymlink-stderr.txt index 63e32ed..2486ca3 100644 --- a/Tests/RunCMake/file/READ_SYMLINK-notsymlink-stderr.txt +++ b/Tests/RunCMake/file/READ_SYMLINK-notsymlink-stderr.txt
@@ -1,6 +1,6 @@ -^CMake Error at READ_SYMLINK-notsymlink\.cmake:[0-9]+ \(file\): +^CMake Error at [^ +]*Tests/RunCMake/file/READ_SYMLINK-notsymlink\.cmake:[0-9]+ \(file\): file READ_SYMLINK requested of path that is not a symlink: - .*/Tests/RunCMake/file/READ_SYMLINK-notsymlink-build/rel\.sym -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + [^ +]*/Tests/RunCMake/file/READ_SYMLINK-notsymlink-build/rel\.sym$
diff --git a/Tests/RunCMake/file/READ_SYMLINK-rel-stdout.txt b/Tests/RunCMake/file/READ_SYMLINK-rel-stdout.txt new file mode 100644 index 0000000..a28693f --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-rel-stdout.txt
@@ -0,0 +1,2 @@ +^-- result: '0' +-- target: 'READ_SYMLINK-target.txt'$
diff --git a/Tests/RunCMake/file/READ_SYMLINK-rel.cmake b/Tests/RunCMake/file/READ_SYMLINK-rel.cmake new file mode 100644 index 0000000..6ed242c --- /dev/null +++ b/Tests/RunCMake/file/READ_SYMLINK-rel.cmake
@@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/READ_SYMLINK-common.cmake)
diff --git a/Tests/RunCMake/file/READ_SYMLINK.cmake b/Tests/RunCMake/file/READ_SYMLINK.cmake deleted file mode 100644 index 865a2e9..0000000 --- a/Tests/RunCMake/file/READ_SYMLINK.cmake +++ /dev/null
@@ -1,13 +0,0 @@ -execute_process(COMMAND - ${CMAKE_COMMAND} -E create_symlink "test.txt" "${CMAKE_CURRENT_BINARY_DIR}/rel.sym") -file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" result) -if(NOT result STREQUAL "test.txt") - message(SEND_ERROR "Relative symlink is \"${result}\", should be \"test.txt\"") -endif() - -execute_process(COMMAND - ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_BINARY_DIR}/test.txt" "${CMAKE_CURRENT_BINARY_DIR}/abs.sym") -file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/abs.sym" result) -if(NOT result MATCHES "^.*/Tests/RunCMake/file/READ_SYMLINK-build/test\\.txt$") - message(SEND_ERROR "Absolute symlink is \"${result}\", should be \"*/Tests/RunCMake/file/READ_SYMLINK-build/test.txt\"") -endif()
diff --git a/Tests/RunCMake/file/REAL_PATH-non-existing-stderr.txt b/Tests/RunCMake/file/REAL_PATH-non-existing-stderr.txt index ef038a7..18cc422 100644 --- a/Tests/RunCMake/file/REAL_PATH-non-existing-stderr.txt +++ b/Tests/RunCMake/file/REAL_PATH-non-existing-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at REAL_PATH-non-existing\.cmake:[0-9]+ \(file\): +^CMake Warning \(author\) at REAL_PATH-non-existing\.cmake:[0-9]+ \(file\): Given path: dir/bad-magic-file\.txt @@ -6,4 +6,4 @@ does not refer to an existing path on disk\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/file/REAL_PATH-stderr.txt b/Tests/RunCMake/file/REAL_PATH-stderr.txt new file mode 100644 index 0000000..7ea4904 --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-stderr.txt
@@ -0,0 +1,12 @@ +^(CMake Warning \(deprecated\) at REAL_PATH\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0152 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.)?$
diff --git a/Tests/RunCMake/file/REMOVE-empty-stderr.txt b/Tests/RunCMake/file/REMOVE-empty-stderr.txt index f036024..91ca229 100644 --- a/Tests/RunCMake/file/REMOVE-empty-stderr.txt +++ b/Tests/RunCMake/file/REMOVE-empty-stderr.txt
@@ -1,11 +1,11 @@ -^CMake Warning \(dev\) at REMOVE-empty\.cmake:1 \(file\): +^CMake Warning \(author\) at REMOVE-empty\.cmake:1 \(file\): Ignoring empty file name in REMOVE\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9] \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -CMake Warning \(dev\) at REMOVE-empty\.cmake:2 \(file\): +CMake Warning \(author\) at REMOVE-empty\.cmake:2 \(file\): Ignoring empty file name in REMOVE_RECURSE\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9] \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index 5007532..3771db4 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake
@@ -30,6 +30,8 @@ run_cmake(LOCK-error-timeout) run_cmake(LOCK-error-unknown-option) run_cmake(LOCK-lowercase) +run_cmake(LOCK-script) +run_cmake(LOCK-include) run_cmake(READ_ELF) run_cmake(GLOB) run_cmake(GLOB_RECURSE) @@ -86,9 +88,6 @@ ) run_cmake(GLOB_RECURSE-cyclic-recursion) run_cmake(INSTALL-SYMLINK) - run_cmake(READ_SYMLINK) - run_cmake(READ_SYMLINK-noexist) - run_cmake(READ_SYMLINK-notsymlink) if(NOT CYGWIN) run_cmake(INSTALL-FOLLOW_SYMLINK_CHAIN) endif() @@ -96,6 +95,38 @@ run_cmake(LOCK-symlink-no-truncate) endif() +# Try creating symbolic links to read below. +# If this fails on the current filesystem, we'll skip those tests. +block(PROPAGATE READ_SYMLINK-link-rel READ_SYMLINK-link-abs) + set(READ_SYMLINK-dest-rel "READ_SYMLINK-target.txt") + set(READ_SYMLINK-dest-abs "${RunCMake_BINARY_DIR}/${READ_SYMLINK-dest-rel}") + file(WRITE "${READ_SYMLINK-dest-abs}" "") + foreach(link IN ITEMS rel abs) + set(READ_SYMLINK-link-${link} "${RunCMake_BINARY_DIR}/READ_SYMLINK-link-${link}.sym") + file(REMOVE "${READ_SYMLINK-link-${link}}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E create_symlink "${READ_SYMLINK-dest-${link}}" "${READ_SYMLINK-link-${link}}" + OUTPUT_VARIABLE create_symlink_stdout + ERROR_VARIABLE create_symlink_stderr + RESULT_VARIABLE create_symlink_result + ) + if(NOT create_symlink_result EQUAL 0 OR NOT EXISTS "${READ_SYMLINK-link-${link}}") + set(READ_SYMLINK-link-${link} "") + endif() + endforeach() +endblock() + +if(READ_SYMLINK-link-rel) + run_cmake_script(READ_SYMLINK-rel "-Dlink=${READ_SYMLINK-link-rel}") +endif() +if(READ_SYMLINK-link-abs) + run_cmake_script(READ_SYMLINK-abs "-Dlink=${READ_SYMLINK-link-abs}") +endif() +run_cmake_script(READ_SYMLINK-noexist) +run_cmake_script(READ_SYMLINK-noexist-capture) +run_cmake_script(READ_SYMLINK-notsymlink) +run_cmake_script(READ_SYMLINK-notsymlink-capture) + run_cmake(REAL_PATH-non-existing) run_cmake(REAL_PATH-existing) run_cmake(REAL_PATH-unexpected-arg)
diff --git a/Tests/RunCMake/file/UPLOAD-unused-argument-stderr.txt b/Tests/RunCMake/file/UPLOAD-unused-argument-stderr.txt index 40ef325..90fe29d 100644 --- a/Tests/RunCMake/file/UPLOAD-unused-argument-stderr.txt +++ b/Tests/RunCMake/file/UPLOAD-unused-argument-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at UPLOAD-unused-argument\.cmake:[0-9]+ \(file\): +^CMake Warning \(author\) at UPLOAD-unused-argument\.cmake:[0-9]+ \(file\): Unexpected argument: JUNK Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/file/lock_include_example.cmake b/Tests/RunCMake/file/lock_include_example.cmake new file mode 100644 index 0000000..0775adf --- /dev/null +++ b/Tests/RunCMake/file/lock_include_example.cmake
@@ -0,0 +1,2 @@ +file(LOCK "${FILE_TO_LOCK}" GUARD FILE TIMEOUT 0) +message(STATUS "File locked in include")
diff --git a/Tests/RunCMake/file/lock_script_example.cmake b/Tests/RunCMake/file/lock_script_example.cmake new file mode 100644 index 0000000..83236c4 --- /dev/null +++ b/Tests/RunCMake/file/lock_script_example.cmake
@@ -0,0 +1,2 @@ +file(LOCK "${FILE_TO_LOCK}" GUARD FILE TIMEOUT 0) +message(STATUS "File locked in script")
diff --git a/Tests/RunCMake/file/lock_script_include_example.cmake b/Tests/RunCMake/file/lock_script_include_example.cmake new file mode 100644 index 0000000..c0df069 --- /dev/null +++ b/Tests/RunCMake/file/lock_script_include_example.cmake
@@ -0,0 +1,6 @@ +include("${CMAKE_CURRENT_LIST_DIR}/lock_include_example.cmake") + +# Since the scope is 'GUARD FILE', after the 'include()' call, the file should +# be in an unlocked state +file(LOCK "${FILE_TO_LOCK}" GUARD FILE TIMEOUT 0) +message(STATUS "File locked in script with include")
diff --git a/Tests/RunCMake/find_file/NO_CACHE-stderr.txt b/Tests/RunCMake/find_file/NO_CACHE-stderr.txt index e99472c..4c41d94 100644 --- a/Tests/RunCMake/find_file/NO_CACHE-stderr.txt +++ b/Tests/RunCMake/find_file/NO_CACHE-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0125 will be removed from a future version of CMake\. @@ -8,3 +8,5 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.
diff --git a/Tests/RunCMake/find_file/Registry-query-stderr.txt b/Tests/RunCMake/find_file/Registry-query-stderr.txt index 0532022..b41aed6 100644 --- a/Tests/RunCMake/find_file/Registry-query-stderr.txt +++ b/Tests/RunCMake/find_file/Registry-query-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Registry-query\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Registry-query\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0134 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt index f6391ad..1f4da93 100644 --- a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt +++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@ find_file command specified for VALIDATOR is not a function: check\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_file\): + find_file command specified for VALIDATOR is not a function: check2\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake index ec0ce9a..861fbf5 100644 --- a/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake +++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@ endmacro() find_file(result NAMES input.txt VALIDATOR check) + +add_subdirectory(subdir) +find_file(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_file/subdir/CMakeLists.txt b/Tests/RunCMake/find_file/subdir/CMakeLists.txt new file mode 100644 index 0000000..1edb776 --- /dev/null +++ b/Tests/RunCMake/find_file/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@ + +macro(CHECK2 result path) +endmacro()
diff --git a/Tests/RunCMake/find_library/BuildTreePrefixNoLeak.cmake b/Tests/RunCMake/find_library/BuildTreePrefixNoLeak.cmake new file mode 100644 index 0000000..6a06760 --- /dev/null +++ b/Tests/RunCMake/find_library/BuildTreePrefixNoLeak.cmake
@@ -0,0 +1,32 @@ +# Regression net for the build-tree prefix leak fixed in the Platform modules. +# When CMake runs from its build tree, CMAKE_ROOT is the source tree rather +# than an install-tree resource directory. In that case, the historical +# dirname(dirname(CMAKE_ROOT)) install-prefix calculation must not be added to +# CMAKE_SYSTEM_PREFIX_PATH. + +get_property(_cmake_running_in_build_tree GLOBAL PROPERTY + _CMAKE_RUNNING_IN_BUILD_TREE) + +if(_cmake_running_in_build_tree) + get_filename_component(_leak_root_dir "${CMAKE_ROOT}" PATH) + get_filename_component(_leak_prefix "${_leak_root_dir}" PATH) + + # Empty means there is no candidate. "/" is already a standard entry, not + # evidence of this leak. + if(_leak_prefix AND + NOT _leak_prefix STREQUAL "/" AND + _leak_prefix IN_LIST CMAKE_SYSTEM_PREFIX_PATH) + # Corner case intentionally not excluded: checking the CMake *source* tree + # out into a system prefix such as /usr/src or /opt/<x> would make + # _leak_prefix a standard root and trip this assertion. Those are + # install/system trees, not dev/CI workspaces, so the corner is treated as + # an unsupported layout. + message(FATAL_ERROR + "CMAKE_SYSTEM_PREFIX_PATH leaked dirname(dirname(CMAKE_ROOT))=\"${_leak_prefix}\" " + "(CMAKE_ROOT=\"${CMAKE_ROOT}\"); the Platform/*.cmake build-tree guard regressed.") + endif() +endif() + +unset(_cmake_running_in_build_tree) +unset(_leak_root_dir) +unset(_leak_prefix)
diff --git a/Tests/RunCMake/find_library/NO_CACHE-stderr.txt b/Tests/RunCMake/find_library/NO_CACHE-stderr.txt index e99472c..4c41d94 100644 --- a/Tests/RunCMake/find_library/NO_CACHE-stderr.txt +++ b/Tests/RunCMake/find_library/NO_CACHE-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0125 will be removed from a future version of CMake\. @@ -8,3 +8,5 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.
diff --git a/Tests/RunCMake/find_library/Registry-query-stderr.txt b/Tests/RunCMake/find_library/Registry-query-stderr.txt index 0532022..b41aed6 100644 --- a/Tests/RunCMake/find_library/Registry-query-stderr.txt +++ b/Tests/RunCMake/find_library/Registry-query-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Registry-query\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Registry-query\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0134 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake index 32da627..118ef9b 100644 --- a/Tests/RunCMake/find_library/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake
@@ -3,6 +3,7 @@ run_cmake(ConfigureLogTransitions) run_cmake(ConfigureLogTransitionsSuppressed) run_cmake(Created) +run_cmake(BuildTreePrefixNoLeak) run_cmake(FromPrefixPath) run_cmake(FromPATHEnv) run_cmake_with_options(IgnoreInstallPrefix "-DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/IgnoreInstallPrefix-build/")
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt index 8f7f8c4..c220085 100644 --- a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt +++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@ find_library command specified for VALIDATOR is not a function: check\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_library\): + find_library command specified for VALIDATOR is not a function: check2\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake index fa90d72..f652828 100644 --- a/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake +++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@ endmacro() find_library(result NAMES input.txt VALIDATOR check) + +add_subdirectory(subdir) +find_library(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_library/subdir/CMakeLists.txt b/Tests/RunCMake/find_library/subdir/CMakeLists.txt new file mode 100644 index 0000000..1edb776 --- /dev/null +++ b/Tests/RunCMake/find_library/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@ + +macro(CHECK2 result path) +endmacro()
diff --git a/Tests/RunCMake/find_package/CMP0074-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0074-WARN-stderr.txt index fa4fded..9b69c1d 100644 --- a/Tests/RunCMake/find_package/CMP0074-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0074-WARN-stderr.txt
@@ -2,7 +2,7 @@ Foo_ROOT :<base>/foo/cmake_root ENV{Foo_ROOT} :<base>/foo/env_root + -CMake Warning \(dev\) at CMP0074-common\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0074-common\.cmake:[0-9]+ \(find_package\): Policy CMP0074 is not set: find_package uses <PackageName>_ROOT variables\. Run "cmake --help-policy CMP0074" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -20,7 +20,8 @@ CMP0074-common\.cmake:[0-9]+ \(RunPackageRootTest\) CMP0074-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + find_package\(Foo\) FOO_TEST_FILE_FOO :FOO_TEST_FILE_FOO-NOTFOUND
diff --git a/Tests/RunCMake/find_package/CMP0084-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0084-NEW-stderr.txt index 280ff8c..b6937e7 100644 --- a/Tests/RunCMake/find_package/CMP0084-NEW-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0084-NEW-stderr.txt
@@ -3,7 +3,7 @@ Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Warning \(dev\) at CMP0084-NEW\.cmake:[0-9]+ \(find_package\): +CMake Warning \(author\) at CMP0084-NEW\.cmake:[0-9]+ \(find_package\): FindQt\.cmake must either be part of this project itself, in this case adjust CMAKE_MODULE_PATH so that it points to the correct location inside its source tree\. @@ -17,4 +17,4 @@ Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0084-OLD-stderr.txt b/Tests/RunCMake/find_package/CMP0084-OLD-stderr.txt index 4e12fe0..fd1fe80 100644 --- a/Tests/RunCMake/find_package/CMP0084-OLD-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0084-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0084-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0084-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0084 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0084-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0084-WARN-stderr.txt index 9ecebd3..7e95b27 100644 --- a/Tests/RunCMake/find_package/CMP0084-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0084-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0084-WARN\.cmake:[0-9]+ \(find_package\): +^CMake Warning \(policy\) at CMP0084-WARN\.cmake:[0-9]+ \(find_package\): Policy CMP0084 is not set: The FindQt module does not exist for find_package\(\)\. Run "cmake --help-policy CMP0084" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt index 67a62a1..8d3a481 100644 --- a/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0144-OLD-CaseInsensitive\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0144-OLD-CaseInsensitive\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0144 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + ---------- FOO_ROOT : ''
diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt index 60545cc..2d29fb0 100644 --- a/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0144-OLD-CaseSensitive\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0144-OLD-CaseSensitive\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0144 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + ---------- FOO_ROOT : ''
diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt index d98b3f3..c41ba26 100644 --- a/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt
@@ -13,7 +13,7 @@ FOO_ROOT : '<base>/foo/cmake_root' ENV{FOO_ROOT}: '<base>/foo/env_root' + -CMake Warning \(dev\) at CMP0144-common\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0144-common\.cmake:[0-9]+ \(find_package\): Policy CMP0144 is not set: find_package uses upper-case <PACKAGENAME>_ROOT variables\. Run "cmake --help-policy CMP0144" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -29,7 +29,8 @@ CMP0144-common\.cmake:[0-9]+ \(RunTestCase\) CMP0144-WARN-CaseInsensitive\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + find_package\(Foo\) FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo\.h
diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt index 5acfaf6..d8d9d0d 100644 --- a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt
@@ -13,7 +13,7 @@ FOO_ROOT : '<base>/foo/cmake_root' ENV{FOO_ROOT}: '<base>/foo/env_root' + -CMake Warning \(dev\) at CMP0144-common\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0144-common\.cmake:[0-9]+ \(find_package\): Policy CMP0144 is not set: find_package uses upper-case <PACKAGENAME>_ROOT variables\. Run "cmake --help-policy CMP0144" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -34,7 +34,8 @@ CMP0144-common\.cmake:[0-9]+ \(RunTestCase\) CMP0144-WARN-CaseSensitive\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + find_package\(Foo\) FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
diff --git a/Tests/RunCMake/find_package/CMP0145-OLD-stderr.txt b/Tests/RunCMake/find_package/CMP0145-OLD-stderr.txt index 3b4955e..13b1f3c 100644 --- a/Tests/RunCMake/find_package/CMP0145-OLD-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0145-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0145-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0145-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0145 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt index 36c66ec..55bd669 100644 --- a/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt
@@ -1,8 +1,8 @@ -CMake Warning \(dev\) at CMP0145-WARN\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0145-WARN\.cmake:[0-9]+ \(find_package\): Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0146-OLD-stderr.txt b/Tests/RunCMake/find_package/CMP0146-OLD-stderr.txt index 459905c..18f257c 100644 --- a/Tests/RunCMake/find_package/CMP0146-OLD-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0146-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0146-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0146-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0146 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0146-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0146-WARN-stderr.txt index 2cd9c5f..8aa156f 100644 --- a/Tests/RunCMake/find_package/CMP0146-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0146-WARN-stderr.txt
@@ -1,8 +1,8 @@ -CMake Warning \(dev\) at CMP0146-WARN\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0146-WARN\.cmake:[0-9]+ \(find_package\): Policy CMP0146 is not set: The FindCUDA module is removed\. Run "cmake --help-policy CMP0146" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0147-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0147-NEW-stderr.txt index 2be74c2..cba4098 100644 --- a/Tests/RunCMake/find_package/CMP0147-NEW-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0147-NEW-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0147-common\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0147-common\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0146 will be removed from a future version of CMake\. @@ -9,6 +9,8 @@ Call Stack \(most recent call first\): CMP0147-NEW\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at [^ ]*/Modules/FindCUDA\.cmake:[0-9]+ \(message\):
diff --git a/Tests/RunCMake/find_package/CMP0147-OLD-stderr.txt b/Tests/RunCMake/find_package/CMP0147-OLD-stderr.txt index f0960b0..941c77a 100644 --- a/Tests/RunCMake/find_package/CMP0147-OLD-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0147-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0147-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0147-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0147 will be removed from a future version of CMake\. @@ -8,8 +8,10 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Deprecation Warning at CMP0147-common\.cmake:[0-9]+ \(cmake_policy\): +CMake Warning \(deprecated\) at CMP0147-common\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0146 will be removed from a future version of CMake\. @@ -19,4 +21,6 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMP0147-OLD\.cmake:2 \(include\) - CMakeLists\.txt:3 \(include\)$ + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0147-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0147-WARN-stderr.txt index 61c87c6..6f80508 100644 --- a/Tests/RunCMake/find_package/CMP0147-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0147-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0147-common\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0147-common\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0146 will be removed from a future version of CMake\. @@ -8,4 +8,6 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMP0147-WARN\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0148-Interp-OLD-stderr.txt b/Tests/RunCMake/find_package/CMP0148-Interp-OLD-stderr.txt index 683fd42..173eb5c 100644 --- a/Tests/RunCMake/find_package/CMP0148-Interp-OLD-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0148-Interp-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0148-Interp-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0148-Interp-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0148 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0148-Interp-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0148-Interp-WARN-stderr.txt index 2666c22..cec2c27 100644 --- a/Tests/RunCMake/find_package/CMP0148-Interp-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0148-Interp-WARN-stderr.txt
@@ -1,8 +1,8 @@ -CMake Warning \(dev\) at CMP0148-Interp-WARN\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0148-Interp-WARN\.cmake:[0-9]+ \(find_package\): Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules are removed\. Run "cmake --help-policy CMP0148" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0148-Libs-OLD-stderr.txt b/Tests/RunCMake/find_package/CMP0148-Libs-OLD-stderr.txt index 5dda78e..5f6e44d 100644 --- a/Tests/RunCMake/find_package/CMP0148-Libs-OLD-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0148-Libs-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0148-Libs-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0148-Libs-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0148 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0148-Libs-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0148-Libs-WARN-stderr.txt index 5210e51..68f09ad 100644 --- a/Tests/RunCMake/find_package/CMP0148-Libs-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0148-Libs-WARN-stderr.txt
@@ -1,8 +1,8 @@ -CMake Warning \(dev\) at CMP0148-Libs-WARN\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0148-Libs-WARN\.cmake:[0-9]+ \(find_package\): Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules are removed\. Run "cmake --help-policy CMP0148" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0167-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0167-WARN-stderr.txt index 72b5225..96311b8 100644 --- a/Tests/RunCMake/find_package/CMP0167-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0167-WARN-stderr.txt
@@ -1,8 +1,8 @@ -CMake Warning \(dev\) at CMP0167-WARN\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0167-WARN\.cmake:[0-9]+ \(find_package\): Policy CMP0167 is not set: The FindBoost module is removed\. Run "cmake --help-policy CMP0167" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0188-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0188-WARN-stderr.txt index de3b9f8..dd46455 100644 --- a/Tests/RunCMake/find_package/CMP0188-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0188-WARN-stderr.txt
@@ -1,8 +1,8 @@ -CMake Warning \(dev\) at CMP0188-WARN\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0188-WARN\.cmake:[0-9]+ \(find_package\): Policy CMP0188 is not set: The FindGCCXML module is removed\. Run "cmake --help-policy CMP0188" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0191-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0191-WARN-stderr.txt index c552623..64f4815 100644 --- a/Tests/RunCMake/find_package/CMP0191-WARN-stderr.txt +++ b/Tests/RunCMake/find_package/CMP0191-WARN-stderr.txt
@@ -1,8 +1,8 @@ -CMake Warning \(dev\) at CMP0191-WARN\.cmake:[0-9]+ \(find_package\): +CMake Warning \(policy\) at CMP0191-WARN\.cmake:[0-9]+ \(find_package\): Policy CMP0191 is not set: The FindCABLE module is removed\. Run "cmake --help-policy CMP0191" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_package/ConfigureLogTransitionsModule-stderr.txt b/Tests/RunCMake/find_package/ConfigureLogTransitionsModule-stderr.txt index f38a83b..0b0b2b7 100644 --- a/Tests/RunCMake/find_package/ConfigureLogTransitionsModule-stderr.txt +++ b/Tests/RunCMake/find_package/ConfigureLogTransitionsModule-stderr.txt
@@ -5,7 +5,7 @@ CMakeLists\.txt:[0-9]+ \(include\) -CMake Warning \(dev\) at ConfigureLogTransitionsModule\.cmake:[0-9]+ \(find_package\): +CMake Warning \(author\) at ConfigureLogTransitionsModule\.cmake:[0-9]+ \(find_package\): FindViaModule\.cmake must either be part of this project itself, in this case adjust CMAKE_MODULE_PATH so that it points to the correct location inside its source tree\. @@ -19,7 +19,7 @@ Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. NotFound -> NotFound CMake Warning at ConfigureLogTransitionsModule\.cmake:[0-9]+ \(find_package\): @@ -28,7 +28,7 @@ CMakeLists\.txt:[0-9]+ \(include\) -CMake Warning \(dev\) at ConfigureLogTransitionsModule\.cmake:[0-9]+ \(find_package\): +CMake Warning \(author\) at ConfigureLogTransitionsModule\.cmake:[0-9]+ \(find_package\): FindViaModule\.cmake must either be part of this project itself, in this case adjust CMAKE_MODULE_PATH so that it points to the correct location inside its source tree\. @@ -42,7 +42,7 @@ Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. NotFound -> Found Found -> Found @@ -53,7 +53,7 @@ CMakeLists\.txt:[0-9]+ \(include\) -CMake Warning \(dev\) at ConfigureLogTransitionsModule\.cmake:[0-9]+ \(find_package\): +CMake Warning \(author\) at ConfigureLogTransitionsModule\.cmake:[0-9]+ \(find_package\): FindViaModule\.cmake must either be part of this project itself, in this case adjust CMAKE_MODULE_PATH so that it points to the correct location inside its source tree\. @@ -67,6 +67,6 @@ Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. END$
diff --git a/Tests/RunCMake/find_package/MissingModule-stderr.txt b/Tests/RunCMake/find_package/MissingModule-stderr.txt index a606847..0d87e6c 100644 --- a/Tests/RunCMake/find_package/MissingModule-stderr.txt +++ b/Tests/RunCMake/find_package/MissingModule-stderr.txt
@@ -4,7 +4,7 @@ CMakeLists\.txt:3 \(include\) -CMake Warning \(dev\) at MissingModule\.cmake:1 \(find_package\): +CMake Warning \(author\) at MissingModule\.cmake:1 \(find_package\): FindNotHere\.cmake must either be part of this project itself, in this case adjust CMAKE_MODULE_PATH so that it points to the correct location inside its source tree\. @@ -18,7 +18,7 @@ Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Warning at MissingModule\.cmake:2 \(message\): This warning must be reachable\.
diff --git a/Tests/RunCMake/find_package/MissingModuleRequired-stderr.txt b/Tests/RunCMake/find_package/MissingModuleRequired-stderr.txt index 10ddca7..f1708a1 100644 --- a/Tests/RunCMake/find_package/MissingModuleRequired-stderr.txt +++ b/Tests/RunCMake/find_package/MissingModuleRequired-stderr.txt
@@ -4,7 +4,7 @@ CMakeLists\.txt:3 \(include\) -CMake Warning \(dev\) at MissingModuleRequired\.cmake:1 \(find_package\): +CMake Warning \(author\) at MissingModuleRequired\.cmake:1 \(find_package\): FindNotHere\.cmake must either be part of this project itself, in this case adjust CMAKE_MODULE_PATH so that it points to the correct location inside its source tree\. @@ -18,4 +18,4 @@ Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/find_package/MissingNormalForceRequired-stderr.txt b/Tests/RunCMake/find_package/MissingNormalForceRequired-stderr.txt index 295747b..995e8a9 100644 --- a/Tests/RunCMake/find_package/MissingNormalForceRequired-stderr.txt +++ b/Tests/RunCMake/find_package/MissingNormalForceRequired-stderr.txt
@@ -7,7 +7,7 @@ Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) + -CMake Warning \(dev\) at MissingNormalForceRequired\.cmake:2 \(find_package\): +CMake Warning \(author\) at MissingNormalForceRequired\.cmake:2 \(find_package\): FindNotHere\.cmake must either be part of this project itself, in this case adjust CMAKE_MODULE_PATH so that it points to the correct location inside its source tree\. @@ -21,4 +21,4 @@ Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/find_package/MissingNormalWarnNoModuleNew-stderr.txt b/Tests/RunCMake/find_package/MissingNormalWarnNoModuleNew-stderr.txt index 0d3b5fb..f351fe2 100644 --- a/Tests/RunCMake/find_package/MissingNormalWarnNoModuleNew-stderr.txt +++ b/Tests/RunCMake/find_package/MissingNormalWarnNoModuleNew-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at MissingNormalWarnNoModuleNew\.cmake:3 \(find_package\): +CMake Warning \(author\) at MissingNormalWarnNoModuleNew\.cmake:3 \(find_package\): find_package called without either MODULE or CONFIG option and no FindNotHere\.cmake module is in CMAKE_MODULE_PATH\. Add MODULE to exclusively request Module mode and fail if FindNotHere\.cmake is missing\. @@ -9,7 +9,7 @@ \(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this warning\.\) Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Warning at MissingNormalWarnNoModuleNew\.cmake:3 \(find_package\): By not providing "FindNotHere\.cmake" in CMAKE_MODULE_PATH this project has
diff --git a/Tests/RunCMake/find_package/MissingNormalWarnNoModuleOld-stderr.txt b/Tests/RunCMake/find_package/MissingNormalWarnNoModuleOld-stderr.txt index 5f45a51..b1df292 100644 --- a/Tests/RunCMake/find_package/MissingNormalWarnNoModuleOld-stderr.txt +++ b/Tests/RunCMake/find_package/MissingNormalWarnNoModuleOld-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at MissingNormalWarnNoModuleOld\.cmake:2 \(find_package\): +CMake Warning \(author\) at MissingNormalWarnNoModuleOld\.cmake:2 \(find_package\): find_package called without either MODULE or CONFIG option and no FindNotHere\.cmake module is in CMAKE_MODULE_PATH\. Add MODULE to exclusively request Module mode and fail if FindNotHere\.cmake is missing\. @@ -9,7 +9,7 @@ \(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this warning\.\) Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Warning at MissingNormalWarnNoModuleOld\.cmake:2 \(find_package\): By not providing "FindNotHere\.cmake" in CMAKE_MODULE_PATH this project has
diff --git a/Tests/RunCMake/find_package/ModuleNameCaseMismatch-stderr.txt b/Tests/RunCMake/find_package/ModuleNameCaseMismatch-stderr.txt new file mode 100644 index 0000000..c3b26e8 --- /dev/null +++ b/Tests/RunCMake/find_package/ModuleNameCaseMismatch-stderr.txt
@@ -0,0 +1,18 @@ +^CMake Warning \(author\) at ModuleNameCaseMismatch\.cmake:[0-9]+ \(find_package\): + The module name + + Findcasemodule\.cmake + + does not match the case of the module file name on disk + + [^ +]*/ModuleNameCaseMismatch/FindCaseModule\.cmake + + This may fail on case-sensitive file systems\. Use the module name + + FindCaseModule\.cmake + + instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/find_package/ModuleNameCaseMismatch.cmake b/Tests/RunCMake/find_package/ModuleNameCaseMismatch.cmake new file mode 100644 index 0000000..d3fae60 --- /dev/null +++ b/Tests/RunCMake/find_package/ModuleNameCaseMismatch.cmake
@@ -0,0 +1,7 @@ +list(PREPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_LIST_DIR}/ModuleNameCaseMismatch") +find_package(casemodule MODULE) + +if(NOT casemodule_FOUND) + message(FATAL_ERROR "FindCaseModule.cmake not included") +endif()
diff --git a/Tests/RunCMake/find_package/ModuleNameCaseMismatch/FindCaseModule.cmake b/Tests/RunCMake/find_package/ModuleNameCaseMismatch/FindCaseModule.cmake new file mode 100644 index 0000000..91a5182 --- /dev/null +++ b/Tests/RunCMake/find_package/ModuleNameCaseMismatch/FindCaseModule.cmake
@@ -0,0 +1 @@ +set(casemodule_FOUND TRUE)
diff --git a/Tests/RunCMake/find_package/Registry-query-stderr.txt b/Tests/RunCMake/find_package/Registry-query-stderr.txt index 0532022..b41aed6 100644 --- a/Tests/RunCMake/find_package/Registry-query-stderr.txt +++ b/Tests/RunCMake/find_package/Registry-query-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Registry-query\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Registry-query\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0134 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake index 1d96eeb..589eae5 100644 --- a/Tests/RunCMake/find_package/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -35,6 +35,9 @@ run_cmake(MissingConfigRequired) run_cmake(MissingConfigVersion) run_cmake(MixedModeOptions) +if(CMake_TEST_FILESYSTEM_CASE) + run_cmake(ModuleNameCaseMismatch) +endif() run_cmake_with_options(ModuleModeDebugPkg --debug-find-pkg=Foo,Zot) run_cmake(NestedConfig) run_cmake(PackageRoot)
diff --git a/Tests/RunCMake/find_path/NO_CACHE-stderr.txt b/Tests/RunCMake/find_path/NO_CACHE-stderr.txt index e99472c..4c41d94 100644 --- a/Tests/RunCMake/find_path/NO_CACHE-stderr.txt +++ b/Tests/RunCMake/find_path/NO_CACHE-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0125 will be removed from a future version of CMake\. @@ -8,3 +8,5 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.
diff --git a/Tests/RunCMake/find_path/Registry-query-stderr.txt b/Tests/RunCMake/find_path/Registry-query-stderr.txt index 0532022..b41aed6 100644 --- a/Tests/RunCMake/find_path/Registry-query-stderr.txt +++ b/Tests/RunCMake/find_path/Registry-query-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Registry-query\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Registry-query\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0134 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt index 9f0e3e8..bff9f35 100644 --- a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt +++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@ find_path command specified for VALIDATOR is not a function: check\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_path\): + find_path command specified for VALIDATOR is not a function: check2\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake index 62b44ed..f1faded 100644 --- a/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake +++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@ endmacro() find_path(result NAMES input.txt VALIDATOR check) + +add_subdirectory(subdir) +find_path(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_path/subdir/CMakeLists.txt b/Tests/RunCMake/find_path/subdir/CMakeLists.txt new file mode 100644 index 0000000..1edb776 --- /dev/null +++ b/Tests/RunCMake/find_path/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@ + +macro(CHECK2 result path) +endmacro()
diff --git a/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt b/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt index 4019de6..5ab5bde 100644 --- a/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt +++ b/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0109-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0109-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0109 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_program/CMP0109-WARN-stderr.txt b/Tests/RunCMake/find_program/CMP0109-WARN-stderr.txt index e540e81..60b12cb 100644 --- a/Tests/RunCMake/find_program/CMP0109-WARN-stderr.txt +++ b/Tests/RunCMake/find_program/CMP0109-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at CMP0109-Common\.cmake:4 \(find_program\): +^CMake Warning \(policy\) at CMP0109-Common\.cmake:4 \(find_program\): Policy CMP0109 is not set: find_program\(\) requires permission to execute but not to read\. Run "cmake --help-policy CMP0109" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -11,9 +11,10 @@ Call Stack \(most recent call first\): CMP0109-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at CMP0109-Common\.cmake:6 \(find_program\): +CMake Warning \(policy\) at CMP0109-Common\.cmake:6 \(find_program\): Policy CMP0109 is not set: find_program\(\) requires permission to execute but not to read\. Run "cmake --help-policy CMP0109" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -26,4 +27,5 @@ Call Stack \(most recent call first\): CMP0109-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/find_program/NO_CACHE-stderr.txt b/Tests/RunCMake/find_program/NO_CACHE-stderr.txt index e99472c..4c41d94 100644 --- a/Tests/RunCMake/find_program/NO_CACHE-stderr.txt +++ b/Tests/RunCMake/find_program/NO_CACHE-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0125 will be removed from a future version of CMake\. @@ -8,3 +8,5 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.
diff --git a/Tests/RunCMake/find_program/Registry-query-stderr.txt b/Tests/RunCMake/find_program/Registry-query-stderr.txt index 0532022..b41aed6 100644 --- a/Tests/RunCMake/find_program/Registry-query-stderr.txt +++ b/Tests/RunCMake/find_program/Registry-query-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at Registry-query\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at Registry-query\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0134 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt index 949f29d..567e577 100644 --- a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt +++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@ find_program command specified for VALIDATOR is not a function: check\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_program\): + find_program command specified for VALIDATOR is not a function: check2\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake index 43bb9ec..3e5e579 100644 --- a/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake +++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@ endmacro() find_program(result NAMES input.txt VALIDATOR check) + +add_subdirectory(subdir) +find_program(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_program/subdir/CMakeLists.txt b/Tests/RunCMake/find_program/subdir/CMakeLists.txt new file mode 100644 index 0000000..1edb776 --- /dev/null +++ b/Tests/RunCMake/find_program/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@ + +macro(CHECK2 result path) +endmacro()
diff --git a/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt b/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt index 1a7255e..adbd2dc 100644 --- a/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt +++ b/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at foreach-var-scope-CMP0124-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at foreach-var-scope-CMP0124-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0124 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/if/InvalidArgumentInParenthesis-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/if/InvalidArgumentInParenthesis-result.txt
diff --git a/Tests/RunCMake/if/InvalidArgumentInParenthesis-stderr.txt b/Tests/RunCMake/if/InvalidArgumentInParenthesis-stderr.txt new file mode 100644 index 0000000..1dec50f --- /dev/null +++ b/Tests/RunCMake/if/InvalidArgumentInParenthesis-stderr.txt
@@ -0,0 +1,8 @@ +^CMake Error at InvalidArgumentInParenthesis\.cmake:1 \(if\): + if given arguments: + + "\(" "A" "B" "\)" "OR" "\(" "" "\)" + + Unknown arguments specified +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/if/InvalidArgumentInParenthesis.cmake b/Tests/RunCMake/if/InvalidArgumentInParenthesis.cmake new file mode 100644 index 0000000..7969b7c --- /dev/null +++ b/Tests/RunCMake/if/InvalidArgumentInParenthesis.cmake
@@ -0,0 +1,5 @@ +if((A B) OR ("$ENV{NOT_AN_ENV_VARIABLE}")) + message(STATUS "Invalid evaluated to TRUE") +else() + message(STATUS "Invalid evaluated to FALSE") +endif()
diff --git a/Tests/RunCMake/if/RunCMakeTest.cmake b/Tests/RunCMake/if/RunCMakeTest.cmake index 366e8a3..ed01091 100644 --- a/Tests/RunCMake/if/RunCMakeTest.cmake +++ b/Tests/RunCMake/if/RunCMakeTest.cmake
@@ -1,6 +1,7 @@ include(RunCMake) run_cmake(InvalidArgument1) +run_cmake(InvalidArgumentInParenthesis) run_cmake(exists) if(NOT MSYS) # permissions and symbolic links are broken on MSYS
diff --git a/Tests/RunCMake/if/empty-elseif-warning-stderr.txt b/Tests/RunCMake/if/empty-elseif-warning-stderr.txt index eefc1f7..34544c1 100644 --- a/Tests/RunCMake/if/empty-elseif-warning-stderr.txt +++ b/Tests/RunCMake/if/empty-elseif-warning-stderr.txt
@@ -1,5 +1,5 @@ -CMake Warning \(dev\) at empty-elseif-warning\.cmake:3 \(elseif\): +CMake Warning \(author\) at empty-elseif-warning\.cmake:3 \(elseif\): ELSEIF called with no arguments, it will be skipped\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/include/CMP0146-OLD-stderr.txt b/Tests/RunCMake/include/CMP0146-OLD-stderr.txt index 459905c..18f257c 100644 --- a/Tests/RunCMake/include/CMP0146-OLD-stderr.txt +++ b/Tests/RunCMake/include/CMP0146-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0146-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0146-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0146 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0146-WARN-stderr.txt b/Tests/RunCMake/include/CMP0146-WARN-stderr.txt index aaaf1dc..14a399a 100644 --- a/Tests/RunCMake/include/CMP0146-WARN-stderr.txt +++ b/Tests/RunCMake/include/CMP0146-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0146-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0146-WARN\.cmake:[0-9]+ \(include\): Policy CMP0146 is not set: The FindCUDA module is removed\. Run "cmake --help-policy CMP0146" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0148-Interp-OLD-stderr.txt b/Tests/RunCMake/include/CMP0148-Interp-OLD-stderr.txt index 683fd42..173eb5c 100644 --- a/Tests/RunCMake/include/CMP0148-Interp-OLD-stderr.txt +++ b/Tests/RunCMake/include/CMP0148-Interp-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0148-Interp-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0148-Interp-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0148 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0148-Interp-WARN-stderr.txt b/Tests/RunCMake/include/CMP0148-Interp-WARN-stderr.txt index 5bd6935..5459fb6 100644 --- a/Tests/RunCMake/include/CMP0148-Interp-WARN-stderr.txt +++ b/Tests/RunCMake/include/CMP0148-Interp-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0148-Interp-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0148-Interp-WARN\.cmake:[0-9]+ \(include\): Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules are removed\. Run "cmake --help-policy CMP0148" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0148-Libs-OLD-stderr.txt b/Tests/RunCMake/include/CMP0148-Libs-OLD-stderr.txt index 5dda78e..5f6e44d 100644 --- a/Tests/RunCMake/include/CMP0148-Libs-OLD-stderr.txt +++ b/Tests/RunCMake/include/CMP0148-Libs-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0148-Libs-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0148-Libs-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0148 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0148-Libs-WARN-stderr.txt b/Tests/RunCMake/include/CMP0148-Libs-WARN-stderr.txt index c24abd4..d053726 100644 --- a/Tests/RunCMake/include/CMP0148-Libs-WARN-stderr.txt +++ b/Tests/RunCMake/include/CMP0148-Libs-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0148-Libs-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0148-Libs-WARN\.cmake:[0-9]+ \(include\): Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules are removed\. Run "cmake --help-policy CMP0148" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0167-WARN-stderr.txt b/Tests/RunCMake/include/CMP0167-WARN-stderr.txt index 41de30d..d2ad59d 100644 --- a/Tests/RunCMake/include/CMP0167-WARN-stderr.txt +++ b/Tests/RunCMake/include/CMP0167-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0167-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0167-WARN\.cmake:[0-9]+ \(include\): Policy CMP0167 is not set: The FindBoost module is removed\. Run "cmake --help-policy CMP0167" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0188-WARN-stderr.txt b/Tests/RunCMake/include/CMP0188-WARN-stderr.txt index c07bb89..0db95e0 100644 --- a/Tests/RunCMake/include/CMP0188-WARN-stderr.txt +++ b/Tests/RunCMake/include/CMP0188-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0188-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0188-WARN\.cmake:[0-9]+ \(include\): Policy CMP0188 is not set: The FindGCCXML module is removed\. Run "cmake --help-policy CMP0188" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0191-WARN-stderr.txt b/Tests/RunCMake/include/CMP0191-WARN-stderr.txt index ab4d433..61f2b44 100644 --- a/Tests/RunCMake/include/CMP0191-WARN-stderr.txt +++ b/Tests/RunCMake/include/CMP0191-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0191-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0191-WARN\.cmake:[0-9]+ \(include\): Policy CMP0191 is not set: The FindCABLE module is removed\. Run "cmake --help-policy CMP0191" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0196-WARN-stderr.txt b/Tests/RunCMake/include/CMP0196-WARN-stderr.txt index ad10bd4..5bf3a9a 100644 --- a/Tests/RunCMake/include/CMP0196-WARN-stderr.txt +++ b/Tests/RunCMake/include/CMP0196-WARN-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at CMP0196-WARN\.cmake:[0-9]+ \(include\): +^CMake Warning \(policy\) at CMP0196-WARN\.cmake:[0-9]+ \(include\): Policy CMP0196 is not set: The CMakeDetermineVSServicePack module is removed\. Run "cmake --help-policy CMP0196" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. - Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/include/CaseMismatch-stderr.txt b/Tests/RunCMake/include/CaseMismatch-stderr.txt new file mode 100644 index 0000000..1fb4d8a --- /dev/null +++ b/Tests/RunCMake/include/CaseMismatch-stderr.txt
@@ -0,0 +1,18 @@ +^CMake Warning \(author\) at CaseMismatch\.cmake:[0-9]+ \(include\): + The module name + + casemodule\.cmake + + does not match the case of the module file name on disk + + [^ +]*/CaseMismatch/CaseModule\.cmake + + This may fail on case-sensitive file systems\. Use the module name + + CaseModule\.cmake + + instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/include/CaseMismatch.cmake b/Tests/RunCMake/include/CaseMismatch.cmake new file mode 100644 index 0000000..54af68c --- /dev/null +++ b/Tests/RunCMake/include/CaseMismatch.cmake
@@ -0,0 +1,6 @@ +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CaseMismatch") +include(casemodule) + +if(NOT case_module_included) + message(FATAL_ERROR "CaseModule.cmake not included") +endif()
diff --git a/Tests/RunCMake/include/CaseMismatch/CaseModule.cmake b/Tests/RunCMake/include/CaseMismatch/CaseModule.cmake new file mode 100644 index 0000000..cf13bde --- /dev/null +++ b/Tests/RunCMake/include/CaseMismatch/CaseModule.cmake
@@ -0,0 +1 @@ +set(case_module_included TRUE)
diff --git a/Tests/RunCMake/include/EmptyString-stderr.txt b/Tests/RunCMake/include/EmptyString-stderr.txt index eb17524..2185963 100644 --- a/Tests/RunCMake/include/EmptyString-stderr.txt +++ b/Tests/RunCMake/include/EmptyString-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at EmptyString\.cmake:1 \(include\): +^CMake Warning \(author\) at EmptyString\.cmake:1 \(include\): include\(\) given empty file name \(ignored\)\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/include/EmptyStringOptional-stderr.txt b/Tests/RunCMake/include/EmptyStringOptional-stderr.txt index 832e645..3c58ee4 100644 --- a/Tests/RunCMake/include/EmptyStringOptional-stderr.txt +++ b/Tests/RunCMake/include/EmptyStringOptional-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Warning \(dev\) at EmptyStringOptional\.cmake:1 \(include\): +^CMake Warning \(author\) at EmptyStringOptional\.cmake:1 \(include\): include\(\) given empty file name \(ignored\)\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/include/RunCMakeTest.cmake b/Tests/RunCMake/include/RunCMakeTest.cmake index cd5eedb..921ebd4 100644 --- a/Tests/RunCMake/include/RunCMakeTest.cmake +++ b/Tests/RunCMake/include/RunCMakeTest.cmake
@@ -6,6 +6,9 @@ run_cmake(ExportExportInclude) run_cmake(IncludeIsDirectory) run_cmake(IncludeMalformed) +if(CMake_TEST_FILESYSTEM_CASE) + run_cmake(CaseMismatch) +endif() run_cmake(ParentVariableRoot) run_cmake(ParentVariableSubDir) run_cmake_script(ParentVariableScript)
diff --git a/Tests/RunCMake/install/CMP0087-OLD-stderr.txt b/Tests/RunCMake/install/CMP0087-OLD-stderr.txt index 201ebc6..a83c7d4 100644 --- a/Tests/RunCMake/install/CMP0087-OLD-stderr.txt +++ b/Tests/RunCMake/install/CMP0087-OLD-stderr.txt
@@ -1,8 +1,10 @@ -^CMake Deprecation Warning at CMP0087-OLD/CMakeLists\.txt:5 \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0087-OLD/CMakeLists\.txt:5 \(cmake_policy\): The OLD behavior for policy CMP0087 will be removed from a future version of CMake\. The cmake-policies\(7\) manual explains that the OLD behaviors of all policies are deprecated and that a policy should be set to OLD only under specific short-term circumstances\. Projects should be ported to the NEW - behavior and not rely on setting a policy to OLD\.$ + behavior and not rely on setting a policy to OLD\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/install/CMP0087-WARN-stderr.txt b/Tests/RunCMake/install/CMP0087-WARN-stderr.txt index 5df630d..95f6601 100644 --- a/Tests/RunCMake/install/CMP0087-WARN-stderr.txt +++ b/Tests/RunCMake/install/CMP0087-WARN-stderr.txt
@@ -1,5 +1,6 @@ -CMake Warning (dev) in CMakeLists\.txt: +CMake Warning (policy) in CMakeLists\.txt: Policy CMP0087 is not set: Install CODE|SCRIPT allow the use of generator expressions\. Run "cmake --help-policy CMP0087" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/install/CMP0177-WARN-stderr.txt b/Tests/RunCMake/install/CMP0177-WARN-stderr.txt index a7d8b2e..0750907 100644 --- a/Tests/RunCMake/install/CMP0177-WARN-stderr.txt +++ b/Tests/RunCMake/install/CMP0177-WARN-stderr.txt
@@ -1,35 +1,39 @@ -CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): +CMake Warning \(policy\) at CMP0177\.cmake:[0-9]+ \(install\): Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): CMP0177-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): +CMake Warning \(policy\) at CMP0177\.cmake:[0-9]+ \(install\): Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): CMP0177-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): +CMake Warning \(policy\) at CMP0177\.cmake:[0-9]+ \(install\): Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): CMP0177-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): +CMake Warning \(policy\) at CMP0177\.cmake:[0-9]+ \(install\): Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. Call Stack \(most recent call first\): CMP0177-WARN\.cmake:1 \(include\) CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-result.txt
diff --git a/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt new file mode 100644 index 0000000..f853cfe --- /dev/null +++ b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error \(install-absolute-destination\) at DIRECTORY-AbsoluteDest-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake new file mode 100644 index 0000000..a76ddb4 --- /dev/null +++ b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake
@@ -0,0 +1 @@ +install(DIRECTORY DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/EXPORT-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/EXPORT-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..fd8844b --- /dev/null +++ b/Tests/RunCMake/install/EXPORT-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(install-absolute-destination\) at EXPORT-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or +-Wno-install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/EXPORT-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/EXPORT-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..f66fdfb --- /dev/null +++ b/Tests/RunCMake/install/EXPORT-AbsoluteDest-warn.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +add_library(mylib STATIC empty.c) +install(TARGETS mylib EXPORT myexp DESTINATION lib) +install(EXPORT myexp DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/EXPORT-FindDependencyExport-stderr.txt b/Tests/RunCMake/install/EXPORT-FindDependencyExport-stderr.txt index bab3e64..7adf000 100644 --- a/Tests/RunCMake/install/EXPORT-FindDependencyExport-stderr.txt +++ b/Tests/RunCMake/install/EXPORT-FindDependencyExport-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at EXPORT-FindDependencyExport\.cmake:[0-9]+ \(export\): +^CMake Warning \(experimental\) at EXPORT-FindDependencyExport\.cmake:[0-9]+ \(export\): CMake's EXPORT_PACKAGE_DEPENDENCIES 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/install/EXPORT-Unwind-stderr.txt b/Tests/RunCMake/install/EXPORT-Unwind-stderr.txt index 30efcec..d732240 100644 --- a/Tests/RunCMake/install/EXPORT-Unwind-stderr.txt +++ b/Tests/RunCMake/install/EXPORT-Unwind-stderr.txt
@@ -1,6 +1,7 @@ -^CMake Warning \(dev\) at EXPORT-Unwind\.cmake:[0-9]+ \(install\): +^CMake Warning \(experimental\) at EXPORT-Unwind\.cmake:[0-9]+ \(install\): CMake's EXPORT_PACKAGE_DEPENDENCIES 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\.$ +This warning is for project developers\. Use -Wno-author or -Wno-experimental +to suppress it\.$
diff --git a/Tests/RunCMake/install/EXPORT_ANDROID_MK-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/EXPORT_ANDROID_MK-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..5ab5449 --- /dev/null +++ b/Tests/RunCMake/install/EXPORT_ANDROID_MK-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(install-absolute-destination\) at EXPORT_ANDROID_MK-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or +-Wno-install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/EXPORT_ANDROID_MK-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/EXPORT_ANDROID_MK-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..1bb5ed1 --- /dev/null +++ b/Tests/RunCMake/install/EXPORT_ANDROID_MK-AbsoluteDest-warn.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +add_library(mylib STATIC empty.c) +install(TARGETS mylib EXPORT myexp DESTINATION lib) +install(EXPORT_ANDROID_MK myexp DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/install/FILES-AbsoluteDest-error-result.txt
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt new file mode 100644 index 0000000..39ef858 --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error \(install-absolute-destination\) at FILES-AbsoluteDest-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake b/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake new file mode 100644 index 0000000..28cb56b --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake
@@ -0,0 +1 @@ +install(FILES empty.c DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..268a4f3 --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(install-absolute-destination\) at FILES-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or +-Wno-install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..28cb56b --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake
@@ -0,0 +1 @@ +install(FILES empty.c DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/FILES-DESTINATION-dot-all-stdout.txt b/Tests/RunCMake/install/FILES-DESTINATION-dot-all-stdout.txt new file mode 100644 index 0000000..d9fde44 --- /dev/null +++ b/Tests/RunCMake/install/FILES-DESTINATION-dot-all-stdout.txt
@@ -0,0 +1,2 @@ +-- Installing: [^ +]+/Tests/RunCMake/install/FILES-DESTINATION-dot-build/root-all/empty\.c
diff --git a/Tests/RunCMake/install/FILES-DESTINATION-dot.cmake b/Tests/RunCMake/install/FILES-DESTINATION-dot.cmake new file mode 100644 index 0000000..eb3b0d5 --- /dev/null +++ b/Tests/RunCMake/install/FILES-DESTINATION-dot.cmake
@@ -0,0 +1 @@ +install(FILES empty.c DESTINATION ".")
diff --git a/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..6fca9dd --- /dev/null +++ b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(install-absolute-destination\) at IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or +-Wno-install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..ee51026 --- /dev/null +++ b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +add_executable(myexe IMPORTED) +set_target_properties(myexe PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/myexe") +install(IMPORTED_RUNTIME_ARTIFACTS myexe RUNTIME DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/install/PACKAGE_INFO-AbsoluteDest-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/install/PACKAGE_INFO-AbsoluteDest-error-result.txt
diff --git a/Tests/RunCMake/install/PACKAGE_INFO-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/PACKAGE_INFO-AbsoluteDest-error-stderr.txt new file mode 100644 index 0000000..caf62a1 --- /dev/null +++ b/Tests/RunCMake/install/PACKAGE_INFO-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error \(install-absolute-destination\) at PACKAGE_INFO-AbsoluteDest-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/PACKAGE_INFO-AbsoluteDest-error.cmake b/Tests/RunCMake/install/PACKAGE_INFO-AbsoluteDest-error.cmake new file mode 100644 index 0000000..7e97c08 --- /dev/null +++ b/Tests/RunCMake/install/PACKAGE_INFO-AbsoluteDest-error.cmake
@@ -0,0 +1,5 @@ +enable_language(C) +project(PACKAGE_INFO-AbsoluteDest-error LANGUAGES C VERSION 1.0) +add_library(mylib STATIC empty.c) +install(TARGETS mylib EXPORT myexp DESTINATION lib) +install(PACKAGE_INFO mypkg EXPORT myexp DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..6c19354 --- /dev/null +++ b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(install-absolute-destination\) at RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or +-Wno-install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..c4ae1ba --- /dev/null +++ b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn.cmake
@@ -0,0 +1,4 @@ +install(RUNTIME_DEPENDENCY_SET deps + LIBRARY DESTINATION /absolute/path + RUNTIME DESTINATION /absolute/path +)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 818b898..9408443 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -82,6 +82,24 @@ run_cmake(FILES-DESTINATION-bad) run_cmake(FILES-RENAME-bad) run_cmake(TARGETS-DESTINATION-bad) +run_cmake_with_options(DIRECTORY-AbsoluteDest-error -Werror=install-absolute-destination) +run_cmake_with_options(FILES-AbsoluteDest-error -Werror=install-absolute-destination) +run_cmake_with_options(FILES-AbsoluteDest-warn -Winstall-absolute-destination) +run_cmake_with_options(TARGETS-AbsoluteDest-error -Werror=install-absolute-destination) +run_cmake_with_options(TARGETS-AbsoluteDest-warn -Winstall-absolute-destination) +run_cmake(TARGETS-AbsoluteDest-archive-error) +run_cmake(TARGETS-AbsoluteDest-library-error) +run_cmake(TARGETS-AbsoluteDest-runtime-error) +run_cmake_with_options(EXPORT-AbsoluteDest-warn -Winstall-absolute-destination) +run_cmake_with_options(EXPORT_ANDROID_MK-AbsoluteDest-warn -Winstall-absolute-destination) +run_cmake_with_options(PACKAGE_INFO-AbsoluteDest-error -Werror=install-absolute-destination) +run_cmake_with_options(SBOM-AbsoluteDest-warn + -Winstall-absolute-destination + -DCMAKE_EXPERIMENTAL_GENERATE_SBOM=2d856d6d-53e8-488b-a17f-d486d2cac317) +run_cmake_with_options(IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn -Winstall-absolute-destination) +if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$") + run_cmake_with_options(RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn -Winstall-absolute-destination) +endif() run_cmake(EXPORT-Component) run_cmake(EXPORT-FindDependencyExportGate) run_cmake(EXPORT-OldIFace) @@ -127,6 +145,7 @@ run_install_test(FILES-EXTRA_ISPC_TARGET_OBJECTS) endif() +run_install_test(FILES-DESTINATION-dot) run_install_test(TARGETS-InstallFromSubDir) run_install_test(TARGETS-OPTIONAL) @@ -214,6 +233,8 @@ unset(RunCMake_TEST_OPTIONS) run_cmake(TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict) run_cmake(RuntimeDependencies-COMPONENTS) + run_cmake(RuntimeDependencies-Default-COMPONENT-Name) + run_cmake(RuntimeDependencies-ProjectName-Placeholder-COMPONENT-Name) else() run_cmake(TARGETS-RUNTIME_DEPENDENCIES-unsupported) run_cmake(TARGETS-RUNTIME_DEPENDENCY_SET-unsupported)
diff --git a/Tests/RunCMake/install/RuntimeDependencies-Default-COMPONENT-Name.cmake b/Tests/RunCMake/install/RuntimeDependencies-Default-COMPONENT-Name.cmake new file mode 100644 index 0000000..47e8f36 --- /dev/null +++ b/Tests/RunCMake/install/RuntimeDependencies-Default-COMPONENT-Name.cmake
@@ -0,0 +1,21 @@ +enable_language(C) + +function(check_components value) + get_cmake_property(comp COMPONENTS) + if(NOT comp STREQUAL value) + message(FATAL_ERROR "Expected value of COMPONENTS:\n ${value}\nActual value of COMPONENTS:\n ${comp}") + endif() +endfunction() + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + add_library(tgt MODULE obj1.c) +else() + add_executable(tgt main.c) +endif() + +set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "default_name") +install(TARGETS tgt + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ) +check_components("default_name")
diff --git a/Tests/RunCMake/install/RuntimeDependencies-ProjectName-Placeholder-COMPONENT-Name.cmake b/Tests/RunCMake/install/RuntimeDependencies-ProjectName-Placeholder-COMPONENT-Name.cmake new file mode 100644 index 0000000..6701bcc --- /dev/null +++ b/Tests/RunCMake/install/RuntimeDependencies-ProjectName-Placeholder-COMPONENT-Name.cmake
@@ -0,0 +1,28 @@ +enable_language(C) + +function(check_components value) + get_cmake_property(comp COMPONENTS) + if(NOT comp STREQUAL value) + message(FATAL_ERROR "Expected value of COMPONENTS:\n ${value}\nActual value of COMPONENTS:\n ${comp}") + endif() +endfunction() + +project(hello_world) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + add_library(tgt MODULE obj1.c) + add_library(tgt2 MODULE obj1.c) +else() + add_executable(tgt main.c) + add_executable(tgt2 main.c) +endif() + +set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "<PROJECT_NAME>") +install(TARGETS tgt + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ) +install(TARGETS tgt2 + RUNTIME DESTINATION bin COMPONENT "other" + LIBRARY DESTINATION lib COMPONENT "other" + ) +check_components("hello_world;other")
diff --git a/Tests/RunCMake/install/SBOM-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/SBOM-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..3cded63 --- /dev/null +++ b/Tests/RunCMake/install/SBOM-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(install-absolute-destination\) at SBOM-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or +-Wno-install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/SBOM-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/SBOM-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..e89966e --- /dev/null +++ b/Tests/RunCMake/install/SBOM-AbsoluteDest-warn.cmake
@@ -0,0 +1,5 @@ +enable_language(C) +project(SBOM-AbsoluteDest-warn LANGUAGES C VERSION 1.0) +add_library(mylib STATIC empty.c) +install(TARGETS mylib EXPORT myexp DESTINATION lib) +install(SBOM mysbom EXPORTS myexp FORMAT "spdx-3.0+json" DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-result.txt
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt new file mode 100644 index 0000000..e23bde8 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-archive-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/archive +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake new file mode 100644 index 0000000..427403d --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +add_library(mylib STATIC empty.c) +cmake_diagnostic(SET CMD_INSTALL_ABSOLUTE_DESTINATION SEND_ERROR) +install(TARGETS mylib ARCHIVE DESTINATION /absolute/archive)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/install/TARGETS-AbsoluteDest-error-result.txt
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt new file mode 100644 index 0000000..4412369 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake new file mode 100644 index 0000000..e2ca4be --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake
@@ -0,0 +1,3 @@ +enable_language(C) +add_library(mylib STATIC empty.c) +install(TARGETS mylib DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-result.txt
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt new file mode 100644 index 0000000..66b9a39 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-library-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/lib +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake new file mode 100644 index 0000000..3885862 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +add_library(mylib MODULE empty.c) +cmake_diagnostic(SET CMD_INSTALL_ABSOLUTE_DESTINATION SEND_ERROR) +install(TARGETS mylib LIBRARY DESTINATION /absolute/lib)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-result.txt
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt new file mode 100644 index 0000000..b3d82e4 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt
@@ -0,0 +1,8 @@ +CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-runtime-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/bin +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake new file mode 100644 index 0000000..df0547b --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +add_executable(myexe empty.c) +cmake_diagnostic(SET CMD_INSTALL_ABSOLUTE_DESTINATION SEND_ERROR) +install(TARGETS myexe RUNTIME DESTINATION /absolute/bin)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..78f0b43 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(install-absolute-destination\) at TARGETS-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path: + + /absolute/path +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or +-Wno-install-absolute-destination to suppress it\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..e2ca4be --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake
@@ -0,0 +1,3 @@ +enable_language(C) +add_library(mylib STATIC empty.c) +install(TARGETS mylib DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt b/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt index 22d51d7..44d5ca9 100644 --- a/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt +++ b/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt
@@ -1,11 +1,11 @@ -^CMake Warning \(dev\) at TARGETS-Defaults-Cache\.cmake:[0-9]+ \(install\): +^CMake Warning \(author\) at TARGETS-Defaults-Cache\.cmake:[0-9]+ \(install\): Target lib3 has PRIVATE_HEADER files but no PRIVATE_HEADER DESTINATION\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at TARGETS-Defaults-Cache\.cmake:[0-9]+ \(install\): +CMake Warning \(author\) at TARGETS-Defaults-Cache\.cmake:[0-9]+ \(install\): Target lib4 has PUBLIC_HEADER files but no PUBLIC_HEADER DESTINATION\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt b/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt index 1c161c3..78ff17d 100644 --- a/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt +++ b/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt
@@ -1,11 +1,11 @@ -^CMake Warning \(dev\) at TARGETS-Defaults\.cmake:[0-9]+ \(install\): +^CMake Warning \(author\) at TARGETS-Defaults\.cmake:[0-9]+ \(install\): Target lib3 has PRIVATE_HEADER files but no PRIVATE_HEADER DESTINATION\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + -CMake Warning \(dev\) at TARGETS-Defaults\.cmake:[0-9]+ \(install\): +CMake Warning \(author\) at TARGETS-Defaults\.cmake:[0-9]+ \(install\): Target lib4 has PUBLIC_HEADER files but no PUBLIC_HEADER DESTINATION\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake index 930ef70..12648fa 100644 --- a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake
@@ -29,7 +29,7 @@ set(target "exe2_cmp0095_new") string(CONCAT regex "${prefix}${target}\"${wssl}" - [[NEW_RPATH "\\\$ORIGIN/../lib]]) + [[NEW_RPATH \[\[\$ORIGIN/../lib]]) check() set(target "exe3_cmp0095_old") @@ -44,7 +44,7 @@ set(target "exe3_cmp0095_new") string(CONCAT regex "${prefix}${target}\"${wssl}" - [[NEW_RPATH "\\\${ORIGIN}/../lib]]) + [[NEW_RPATH \[\[\${ORIGIN}/../lib]]) check() set(target "exe4_cmp0095_old") @@ -59,5 +59,5 @@ set(target "exe4_cmp0095_new") string(CONCAT regex "${prefix}${target}\"${wssl}" - [[NEW_RPATH "/foo/bar/\\\${PLATFORM}]]) + [[NEW_RPATH \[\[/foo/bar/\${PLATFORM}]]) check()
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt index d6b30f2..c490b05 100644 --- a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0095 will be removed from a future version of CMake\. @@ -8,9 +8,11 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(A_CMP0095\) - CMakeLists\.txt:[0-9]+ \(include\)( + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.( + -CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\): +CMake Warning \(policy\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\): Policy CMP0095 is not set: RPATH entries are properly escaped in the intermediary CMake install script\. Run "cmake --help-policy CMP0095" for policy details\. Use the cmake_policy command to set the policy and @@ -20,9 +22,10 @@ intermediary cmake_install\.cmake script\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.)+( +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.)+( + -CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\): +CMake Warning \(policy\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\): Policy CMP0095 is not set: RPATH entries are properly escaped in the intermediary CMake install script\. Run "cmake --help-policy CMP0095" for policy details\. Use the cmake_policy command to set the policy and @@ -32,4 +35,5 @@ intermediary cmake_install\.cmake script\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.)+$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.)+$
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake index f45b303..90fc03d 100644 --- a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake
@@ -11,6 +11,6 @@ if("x${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN}" STREQUAL "x\$ORIGIN") set(target "exe2") string(CONCAT regex "${prefix}${target}\"${wsnl}" - [[OLD_RPATH "\\\$ORIGIN]]) + [[OLD_RPATH \[\[\$ORIGIN]]) check() endif()
diff --git a/Tests/RunCMake/install/TARGETS-SymbolicComponent-all-check.cmake b/Tests/RunCMake/install/TARGETS-SymbolicComponent-all-check.cmake index bf0cb0e..d01d212 100644 --- a/Tests/RunCMake/install/TARGETS-SymbolicComponent-all-check.cmake +++ b/Tests/RunCMake/install/TARGETS-SymbolicComponent-all-check.cmake
@@ -1,5 +1,5 @@ file(READ "${RunCMake_TEST_BINARY_DIR}/root-all/foo.cmake" foo) -if("${foo}" MATCHES "add_library\(foo INTERFACE SYMBOLIC IMPORTED\)") +if(NOT "${foo}" MATCHES "add_library\\(foo INTERFACE SYMBOLIC IMPORTED\\)") string(APPEND RunCMake_TEST_FAILED "Symbolic Component Foo was not exported\n") endif()
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/install/empty.cxx similarity index 100% rename from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake rename to Tests/RunCMake/install/empty.cxx
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Exclude-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-Exclude-stderr.txt new file mode 100644 index 0000000..d9ba38d --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-Exclude-stderr.txt
@@ -0,0 +1,2 @@ +^mylist was: FILTER_THIS_BIT;DO_NOT_FILTER_THIS;thisisanitem;FILTER_THIS_THING +mylist is: DO_NOT_FILTER_THIS;thisisanitem$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Exclude.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-Exclude.cmake new file mode 100644 index 0000000..16e0624 --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-Exclude.cmake
@@ -0,0 +1,34 @@ +# Predicate: returns TRUE for items starting with "FILTER_THIS_" +function(starts_with_filter input result) + if(input MATCHES "^FILTER_THIS_") + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + +set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING) +message("mylist was: ${mylist}") +list(FILTER mylist EXCLUDE PREDICATE starts_with_filter) +message("mylist is: ${mylist}") + +# EXCLUDE on empty list +set(empty_list "") +list(FILTER empty_list EXCLUDE PREDICATE starts_with_filter) +if(NOT empty_list STREQUAL "") + message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE empty) is \"${empty_list}\", expected \"\"") +endif() + +# EXCLUDE where nothing matches (all elements kept) +set(mylist alpha bravo charlie) +list(FILTER mylist EXCLUDE PREDICATE starts_with_filter) +if(NOT mylist STREQUAL "alpha;bravo;charlie") + message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE no-match) is \"${mylist}\", expected \"alpha;bravo;charlie\"") +endif() + +# EXCLUDE where everything matches (all elements removed) +set(mylist FILTER_THIS_A FILTER_THIS_B FILTER_THIS_C) +list(FILTER mylist EXCLUDE PREDICATE starts_with_filter) +if(NOT mylist STREQUAL "") + message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE all-match) is \"${mylist}\", expected \"\"") +endif()
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Include-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-Include-stderr.txt new file mode 100644 index 0000000..5e5280d --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-Include-stderr.txt
@@ -0,0 +1,2 @@ +^mylist was: FILTER_THIS_BIT;DO_NOT_FILTER_THIS;thisisanitem;FILTER_THIS_THING +mylist is: FILTER_THIS_BIT;FILTER_THIS_THING$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Include.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-Include.cmake new file mode 100644 index 0000000..4d8aa32 --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-Include.cmake
@@ -0,0 +1,34 @@ +# Predicate: returns TRUE for items starting with "FILTER_THIS_" +function(starts_with_filter input result) + if(input MATCHES "^FILTER_THIS_") + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + +set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING) +message("mylist was: ${mylist}") +list(FILTER mylist INCLUDE PREDICATE starts_with_filter) +message("mylist is: ${mylist}") + +# INCLUDE on empty list +set(empty_list "") +list(FILTER empty_list INCLUDE PREDICATE starts_with_filter) +if(NOT empty_list STREQUAL "") + message(FATAL_ERROR "FILTER(INCLUDE PREDICATE empty) is \"${empty_list}\", expected \"\"") +endif() + +# INCLUDE where nothing matches (all elements removed) +set(mylist alpha bravo charlie) +list(FILTER mylist INCLUDE PREDICATE starts_with_filter) +if(NOT mylist STREQUAL "") + message(FATAL_ERROR "FILTER(INCLUDE PREDICATE no-match) is \"${mylist}\", expected \"\"") +endif() + +# INCLUDE where everything matches +set(mylist FILTER_THIS_A FILTER_THIS_B FILTER_THIS_C) +list(FILTER mylist INCLUDE PREDICATE starts_with_filter) +if(NOT mylist STREQUAL "FILTER_THIS_A;FILTER_THIS_B;FILTER_THIS_C") + message(FATAL_ERROR "FILTER(INCLUDE PREDICATE all-match) is \"${mylist}\", expected \"FILTER_THIS_A;FILTER_THIS_B;FILTER_THIS_C\"") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/FILTER-PREDICATE-Macro-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/FILTER-PREDICATE-Macro-result.txt
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Macro-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-Macro-stderr.txt new file mode 100644 index 0000000..545e41f --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-Macro-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at FILTER-PREDICATE-Macro\.cmake:11 \(list\): + list sub-command FILTER, mode PREDICATE: macro "is_short" may not be used + here; define it as a function\(\) instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Macro.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-Macro.cmake new file mode 100644 index 0000000..2678af5 --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-Macro.cmake
@@ -0,0 +1,11 @@ +macro(is_short input result) + string(LENGTH "${input}" _len) + if(_len LESS 4) + set(${result} TRUE) + else() + set(${result} FALSE) + endif() +endmacro() + +set(mylist alpha bravo charlie) +list(FILTER mylist INCLUDE PREDICATE is_short)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-result.txt
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-stderr.txt new file mode 100644 index 0000000..2fbc148 --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at FILTER-PREDICATE-NoOutput\.cmake:6 \(list\): + list sub-command FILTER, mode PREDICATE: function "bad_predicate" did not + set the output variable\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput.cmake new file mode 100644 index 0000000..f1f4621 --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput.cmake
@@ -0,0 +1,6 @@ +function(bad_predicate in out) + # Deliberately does NOT set ${out} +endfunction() + +set(mylist alpha bravo charlie) +list(FILTER mylist INCLUDE PREDICATE bad_predicate)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-result.txt
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-stderr.txt new file mode 100644 index 0000000..e3c7a10 --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at FILTER-PREDICATE-TooManyArguments\.cmake:6 \(list\): + list sub-command FILTER, mode PREDICATE requires five arguments\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments.cmake new file mode 100644 index 0000000..87a8532 --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments.cmake
@@ -0,0 +1,6 @@ +function(my_predicate in out) + set(${out} TRUE PARENT_SCOPE) +endfunction() + +set(mylist alpha bravo charlie) +list(FILTER mylist INCLUDE PREDICATE my_predicate extra_arg)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-result.txt
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-stderr.txt new file mode 100644 index 0000000..2b6234b --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at FILTER-PREDICATE-UnknownFunction\.cmake:2 \(list\): + list sub-command FILTER, mode PREDICATE: unknown function + "no_such_function"\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction.cmake new file mode 100644 index 0000000..48461bd --- /dev/null +++ b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction.cmake
@@ -0,0 +1,2 @@ +set(mylist alpha bravo charlie) +list(FILTER mylist INCLUDE PREDICATE no_such_function)
diff --git a/Tests/RunCMake/list/RunCMakeTest.cmake b/Tests/RunCMake/list/RunCMakeTest.cmake index e428cd0..a3b48c8 100644 --- a/Tests/RunCMake/list/RunCMakeTest.cmake +++ b/Tests/RunCMake/list/RunCMakeTest.cmake
@@ -37,6 +37,12 @@ run_cmake(FILTER-REGEX-InvalidOperator) run_cmake(FILTER-REGEX-Valid0) run_cmake(FILTER-REGEX-Valid1) +run_cmake(FILTER-PREDICATE-UnknownFunction) +run_cmake(FILTER-PREDICATE-Macro) +run_cmake(FILTER-PREDICATE-NoOutput) +run_cmake(FILTER-PREDICATE-TooManyArguments) +run_cmake(FILTER-PREDICATE-Include) +run_cmake(FILTER-PREDICATE-Exclude) run_cmake(JOIN-NoArguments) run_cmake(JOIN-NoVariable) @@ -64,6 +70,10 @@ run_cmake(TRANSFORM-REPLACE-InvalidRegex) run_cmake(TRANSFORM-REPLACE-InvalidReplace1) run_cmake(TRANSFORM-REPLACE-InvalidReplace2) +run_cmake(TRANSFORM-APPLY-NoFunction) +run_cmake(TRANSFORM-APPLY-UnknownFunction) +run_cmake(TRANSFORM-APPLY-Macro) +run_cmake(TRANSFORM-APPLY-NoOutput) # 'selector' oriented tests run_cmake(TRANSFORM-Selector-REGEX-NoArguments) run_cmake(TRANSFORM-Selector-REGEX-TooManyArguments) @@ -79,6 +89,10 @@ run_cmake(TRANSFORM-Selector-FOR-ZeroStepArgument) run_cmake(TRANSFORM-Selector-FOR-NegativeStepArgument) run_cmake(TRANSFORM-Selector-FOR-BackwardsRange) +run_cmake(TRANSFORM-Selector-PREDICATE-NoArguments) +run_cmake(TRANSFORM-Selector-PREDICATE-UnknownFunction) +run_cmake(TRANSFORM-Selector-PREDICATE-Macro) +run_cmake(TRANSFORM-Selector-PREDICATE-NoOutput) # 'output' oriented tests run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments) run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments) @@ -90,6 +104,9 @@ run_cmake(TRANSFORM-APPEND) run_cmake(TRANSFORM-PREPEND) run_cmake(TRANSFORM-REPLACE) +run_cmake(TRANSFORM-APPLY) +run_cmake(TRANSFORM-PREDICATE) +run_cmake(TRANSFORM-PREDICATE-Reentrant) run_cmake(CMP0186) # argument tests @@ -101,9 +118,19 @@ run_cmake(SORT-DuplicateCompareOption) run_cmake(SORT-DuplicateCaseOption) run_cmake(SORT-NoCaseOption) +run_cmake(SORT-COMPARATOR-UnknownFunction) +run_cmake(SORT-COMPARATOR-Macro) +run_cmake(SORT-COMPARATOR-NoOutput) +run_cmake(SORT-COMPARATOR-CompareConflict) +run_cmake(SORT-COMPARATOR-CompareConflictReverse) +run_cmake(SORT-COMPARATOR-NoFunction) +run_cmake(SORT-COMPARATOR-DuplicateOption) +run_cmake(SORT-COMPARATOR-NotStrictWeak) +run_cmake(SORT-COMPARATOR-NotStrictWeakLate) # Successful tests run_cmake(SORT) +run_cmake(SORT-COMPARATOR) # argument tests run_cmake(PREPEND-NoArgs)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-stderr.txt new file mode 100644 index 0000000..0224d8c --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at SORT-COMPARATOR-CompareConflict\.cmake:10 \(list\): + list sub-command SORT option "COMPARATOR" is incompatible with "COMPARE"\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict.cmake new file mode 100644 index 0000000..2e62821 --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict.cmake
@@ -0,0 +1,10 @@ +function(my_cmp a b out) + if("${a}" STRLESS "${b}") + set(${out} TRUE PARENT_SCOPE) + else() + set(${out} FALSE PARENT_SCOPE) + endif() +endfunction() + +set(mylist c a b) +list(SORT mylist COMPARE STRING COMPARATOR my_cmp)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-stderr.txt new file mode 100644 index 0000000..30659dc --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at SORT-COMPARATOR-CompareConflictReverse\.cmake:10 \(list\): + list sub-command SORT option "COMPARE" is incompatible with "COMPARATOR"\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse.cmake new file mode 100644 index 0000000..c08314d --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse.cmake
@@ -0,0 +1,10 @@ +function(my_cmp a b out) + if("${a}" STRLESS "${b}") + set(${out} TRUE PARENT_SCOPE) + else() + set(${out} FALSE PARENT_SCOPE) + endif() +endfunction() + +set(mylist c a b) +list(SORT mylist COMPARATOR my_cmp COMPARE STRING)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-stderr.txt new file mode 100644 index 0000000..b387e8e --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at SORT-COMPARATOR-DuplicateOption\.cmake:10 \(list\): + list sub-command SORT option "COMPARATOR" has been specified multiple + times\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption.cmake new file mode 100644 index 0000000..418a843 --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption.cmake
@@ -0,0 +1,10 @@ +function(my_cmp a b out) + if("${a}" STRLESS "${b}") + set(${out} TRUE PARENT_SCOPE) + else() + set(${out} FALSE PARENT_SCOPE) + endif() +endfunction() + +set(mylist c a b) +list(SORT mylist COMPARATOR my_cmp COMPARATOR my_cmp)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-Macro-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-Macro-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-Macro-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-Macro-stderr.txt new file mode 100644 index 0000000..8abed12 --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-Macro-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at SORT-COMPARATOR-Macro\.cmake:10 \(list\): + list sub-command SORT, COMPARATOR: macro "string_less_macro" may not be + used here; define it as a function\(\) instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-Macro.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-Macro.cmake new file mode 100644 index 0000000..3b9890e --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-Macro.cmake
@@ -0,0 +1,10 @@ +macro(string_less_macro a b result) + if(${a} STRLESS ${b}) + set(${result} TRUE) + else() + set(${result} FALSE) + endif() +endmacro() + +set(mylist c a b) +list(SORT mylist COMPARATOR string_less_macro)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-stderr.txt new file mode 100644 index 0000000..7b74e3b --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at SORT-COMPARATOR-NoFunction\.cmake:2 \(list\): + list sub-command SORT missing argument for option "COMPARATOR"\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction.cmake new file mode 100644 index 0000000..0c834f1 --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction.cmake
@@ -0,0 +1,2 @@ +set(mylist a b c) +list(SORT mylist COMPARATOR)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-stderr.txt new file mode 100644 index 0000000..af5e3cc --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at SORT-COMPARATOR-NoOutput\.cmake:6 \(list\): + list sub-command SORT, COMPARATOR: function "bad_comparator" did not set + the output variable\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput.cmake new file mode 100644 index 0000000..9c279f8 --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput.cmake
@@ -0,0 +1,6 @@ +function(bad_comparator a b out) + # Deliberately does NOT set ${out} +endfunction() + +set(mylist c a b) +list(SORT mylist COMPARATOR bad_comparator)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-stderr.txt new file mode 100644 index 0000000..2978265 --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at SORT-COMPARATOR-NotStrictWeak\.cmake:6 \(list\): + list sub-command SORT, COMPARATOR: function does not induce a strict weak + ordering\. The comparator returned TRUE for both \(a, b\) and \(b, a\)\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak.cmake new file mode 100644 index 0000000..bf7e88f --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak.cmake
@@ -0,0 +1,6 @@ +function(reflexive_comparator a b out) + set(${out} TRUE PARENT_SCOPE) +endfunction() + +set(mylist c a b) +list(SORT mylist COMPARATOR reflexive_comparator)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-stderr.txt new file mode 100644 index 0000000..6188b8d --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at SORT-COMPARATOR-NotStrictWeakLate\.cmake:19 \(list\): + list sub-command SORT, COMPARATOR: function does not induce a strict weak + ordering\. The comparator returned TRUE for both \(a, b\) and \(b, a\)\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate.cmake new file mode 100644 index 0000000..c0193ed --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate.cmake
@@ -0,0 +1,19 @@ +# Comparator that is correct for most pairs but violates strict weak ordering +# when both elements start with "b". +function(late_violation a b result) + string(SUBSTRING "${a}" 0 1 a_first) + string(SUBSTRING "${b}" 0 1 b_first) + if(a_first STREQUAL "b" AND b_first STREQUAL "b") + # Violates asymmetry: always returns TRUE for b-vs-b pairs + set(${result} TRUE PARENT_SCOPE) + else() + if("${a}" STRLESS "${b}") + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() + endif() +endfunction() + +set(mylist "a" "c" "bb" "ba") +list(SORT mylist COMPARATOR late_violation)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-result.txt
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-stderr.txt new file mode 100644 index 0000000..e5c9543 --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at SORT-COMPARATOR-UnknownFunction\.cmake:2 \(list\): + list sub-command SORT, COMPARATOR: unknown function "no_such_function"\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction.cmake new file mode 100644 index 0000000..8e5901b --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction.cmake
@@ -0,0 +1,2 @@ +set(mylist a b c) +list(SORT mylist COMPARATOR no_such_function)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR.cmake b/Tests/RunCMake/list/SORT-COMPARATOR.cmake new file mode 100644 index 0000000..3a561cc --- /dev/null +++ b/Tests/RunCMake/list/SORT-COMPARATOR.cmake
@@ -0,0 +1,119 @@ +# Comparator: returns TRUE if a < b (string comparison) +function(string_less a b result) + if("${a}" STRLESS "${b}") + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + +# Comparator: string length comparison +function(shorter_first a b result) + string(LENGTH "${a}" len_a) + string(LENGTH "${b}" len_b) + if(len_a LESS len_b) + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + +## Basic COMPARATOR with function +set(mylist c a b) +list(SORT mylist COMPARATOR string_less) +if(NOT mylist STREQUAL "a;b;c") + message(FATAL_ERROR "SORT(COMPARATOR function) is \"${mylist}\", expected \"a;b;c\"") +endif() + +## COMPARATOR with ORDER DESCENDING +set(mylist c a b) +list(SORT mylist COMPARATOR string_less ORDER DESCENDING) +if(NOT mylist STREQUAL "c;b;a") + message(FATAL_ERROR "SORT(COMPARATOR ORDER DESCENDING) is \"${mylist}\", expected \"c;b;a\"") +endif() + +## COMPARATOR with CASE INSENSITIVE +# With CASE INSENSITIVE, values are lowercased before being passed to the comparator +set(mylist "C" "a" "B") +list(SORT mylist COMPARATOR string_less CASE INSENSITIVE) +if(NOT mylist STREQUAL "a;B;C") + message(FATAL_ERROR "SORT(COMPARATOR CASE INSENSITIVE) is \"${mylist}\", expected \"a;B;C\"") +endif() + +## COMPARATOR with CASE INSENSITIVE and ORDER DESCENDING +set(mylist "C" "a" "B") +list(SORT mylist COMPARATOR string_less CASE INSENSITIVE ORDER DESCENDING) +if(NOT mylist STREQUAL "C;B;a") + message(FATAL_ERROR "SORT(COMPARATOR CASE INSENSITIVE ORDER DESCENDING) is \"${mylist}\", expected \"C;B;a\"") +endif() + +## Custom comparator (sort by string length) +set(mylist "bbb" "a" "cc") +list(SORT mylist COMPARATOR shorter_first) +if(NOT mylist STREQUAL "a;cc;bbb") + message(FATAL_ERROR "SORT(COMPARATOR shorter_first) is \"${mylist}\", expected \"a;cc;bbb\"") +endif() + +## Custom comparator with ORDER DESCENDING (sort by length descending) +set(mylist "bbb" "a" "cc") +list(SORT mylist COMPARATOR shorter_first ORDER DESCENDING) +if(NOT mylist STREQUAL "bbb;cc;a") + message(FATAL_ERROR "SORT(COMPARATOR shorter_first ORDER DESCENDING) is \"${mylist}\", expected \"bbb;cc;a\"") +endif() + +## Empty list (no-op) +set(empty_list "") +list(SORT empty_list COMPARATOR string_less) +if(NOT empty_list STREQUAL "") + message(FATAL_ERROR "SORT(COMPARATOR empty) is \"${empty_list}\", expected \"\"") +endif() + +## Single element list (no-op) +set(mylist "only") +list(SORT mylist COMPARATOR string_less) +if(NOT mylist STREQUAL "only") + message(FATAL_ERROR "SORT(COMPARATOR single) is \"${mylist}\", expected \"only\"") +endif() + +## Already sorted list +set(mylist a b c) +list(SORT mylist COMPARATOR string_less) +if(NOT mylist STREQUAL "a;b;c") + message(FATAL_ERROR "SORT(COMPARATOR already-sorted) is \"${mylist}\", expected \"a;b;c\"") +endif() + +## Recursive COMPARATOR - verify inner and outer output variables are distinct +# Inner comparator: checks that neither element equals its own output variable +# name (which would mean the outer and inner names collided), then performs a +# valid string comparison to satisfy strict weak ordering. +function(check_outer_name a b out) + if("${a}" STREQUAL "${out}") + message(FATAL_ERROR "Recursive COMPARATOR: inner and outer output variable names are the same: ${out}") + endif() + if("${b}" STREQUAL "${out}") + message(FATAL_ERROR "Recursive COMPARATOR: inner and outer output variable names are the same: ${out}") + endif() + if("${a}" STRLESS "${b}") + set(${out} TRUE PARENT_SCOPE) + else() + set(${out} FALSE PARENT_SCOPE) + endif() +endfunction() + +# Outer comparator: passes its own output variable name into a nested sort +# as a list element, so the inner comparator can compare the two names. +function(nested_comparator a b out) + set(_inner "${out}" dummy) + list(SORT _inner COMPARATOR check_outer_name) + if("${a}" STRLESS "${b}") + set(${out} TRUE PARENT_SCOPE) + else() + set(${out} FALSE PARENT_SCOPE) + endif() +endfunction() + +set(mylist c a b) +list(SORT mylist COMPARATOR nested_comparator) +if(NOT mylist STREQUAL "a;b;c") + message(FATAL_ERROR "SORT(COMPARATOR nested) is \"${mylist}\", expected \"a;b;c\"") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-Macro-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/TRANSFORM-APPLY-Macro-result.txt
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-Macro-stderr.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-Macro-stderr.txt new file mode 100644 index 0000000..cff7b0e --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY-Macro-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at TRANSFORM-APPLY-Macro\.cmake:6 \(list\): + list sub-command TRANSFORM, action APPLY: macro "wrap_angles" may not be + used here; define it as a function\(\) instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-Macro.cmake b/Tests/RunCMake/list/TRANSFORM-APPLY-Macro.cmake new file mode 100644 index 0000000..990b652 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY-Macro.cmake
@@ -0,0 +1,6 @@ +macro(wrap_angles in out) + set(${out} "<${in}>") +endmacro() + +set(mylist alpha bravo charlie) +list(TRANSFORM mylist APPLY wrap_angles)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-NoFunction-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/TRANSFORM-APPLY-NoFunction-result.txt
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-NoFunction-stderr.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-NoFunction-stderr.txt new file mode 100644 index 0000000..0e3ca85 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY-NoFunction-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at TRANSFORM-APPLY-NoFunction\.cmake:2 \(list\): + list sub-command TRANSFORM, action APPLY expects 1 argument\(s\)\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-NoFunction.cmake b/Tests/RunCMake/list/TRANSFORM-APPLY-NoFunction.cmake new file mode 100644 index 0000000..4e6ca06 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY-NoFunction.cmake
@@ -0,0 +1,2 @@ +set(mylist alpha bravo charlie) +list(TRANSFORM mylist APPLY)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-NoOutput-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/TRANSFORM-APPLY-NoOutput-result.txt
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-NoOutput-stderr.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-NoOutput-stderr.txt new file mode 100644 index 0000000..8c31e5c --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY-NoOutput-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at TRANSFORM-APPLY-NoOutput\.cmake:6 \(list\): + list sub-command TRANSFORM, action APPLY: function "my_bad_transform" did + not set the output variable\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-NoOutput.cmake b/Tests/RunCMake/list/TRANSFORM-APPLY-NoOutput.cmake new file mode 100644 index 0000000..b8d02ec --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY-NoOutput.cmake
@@ -0,0 +1,6 @@ +function(my_bad_transform in out) + # Deliberately does NOT set ${out} +endfunction() + +set(mylist alpha bravo charlie) +list(TRANSFORM mylist APPLY my_bad_transform)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-result.txt
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-stderr.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-stderr.txt new file mode 100644 index 0000000..cb59954 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at TRANSFORM-APPLY-UnknownFunction\.cmake:2 \(list\): + list sub-command TRANSFORM, action APPLY: unknown function + "no_such_function"\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction.cmake b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction.cmake new file mode 100644 index 0000000..5275439 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction.cmake
@@ -0,0 +1,2 @@ +set(mylist alpha bravo charlie) +list(TRANSFORM mylist APPLY no_such_function)
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY.cmake b/Tests/RunCMake/list/TRANSFORM-APPLY.cmake new file mode 100644 index 0000000..72ae560 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-APPLY.cmake
@@ -0,0 +1,113 @@ +# Define a transform function: add source prefix +function(add_src_prefix in out) + set(${out} "src/${in}" PARENT_SCOPE) +endfunction() + +# Define a transform function: add prefix +function(add_prefix in out) + set(${out} "prefix_${in}" PARENT_SCOPE) +endfunction() + +set(mylist alpha bravo charlie delta) + +# Basic APPLY - all elements +list(TRANSFORM mylist APPLY add_src_prefix OUTPUT_VARIABLE output) +if(NOT output STREQUAL "src/alpha;src/bravo;src/charlie;src/delta") + message(FATAL_ERROR "TRANSFORM(APPLY) is \"${output}\", expected is \"src/alpha;src/bravo;src/charlie;src/delta\"") +endif() + +# APPLY with OUTPUT_VARIABLE (verify original unchanged) +if(NOT mylist STREQUAL "alpha;bravo;charlie;delta") + message(FATAL_ERROR "Original list modified: \"${mylist}\", expected \"alpha;bravo;charlie;delta\"") +endif() + +# APPLY in-place +list(TRANSFORM mylist APPLY add_src_prefix) +if(NOT mylist STREQUAL "src/alpha;src/bravo;src/charlie;src/delta") + message(FATAL_ERROR "TRANSFORM(APPLY) in-place is \"${mylist}\", expected is \"src/alpha;src/bravo;src/charlie;src/delta\"") +endif() + +# APPLY with AT selector +set(mylist alpha bravo charlie delta) +list(TRANSFORM mylist APPLY add_src_prefix AT 1 3 OUTPUT_VARIABLE output) +if(NOT output STREQUAL "alpha;src/bravo;charlie;src/delta") + message(FATAL_ERROR "TRANSFORM(APPLY AT) is \"${output}\", expected is \"alpha;src/bravo;charlie;src/delta\"") +endif() + +# APPLY with AT selector and negative index +unset(output) +list(TRANSFORM mylist APPLY add_src_prefix AT 1 -2 OUTPUT_VARIABLE output) +if(NOT output STREQUAL "alpha;src/bravo;src/charlie;delta") + message(FATAL_ERROR "TRANSFORM(APPLY AT neg) is \"${output}\", expected is \"alpha;src/bravo;src/charlie;delta\"") +endif() + +# APPLY with FOR selector +unset(output) +list(TRANSFORM mylist APPLY add_src_prefix FOR 1 2 OUTPUT_VARIABLE output) +if(NOT output STREQUAL "alpha;src/bravo;src/charlie;delta") + message(FATAL_ERROR "TRANSFORM(APPLY FOR) is \"${output}\", expected is \"alpha;src/bravo;src/charlie;delta\"") +endif() + +# APPLY with FOR selector and step +unset(output) +list(TRANSFORM mylist APPLY add_src_prefix FOR 0 3 2 OUTPUT_VARIABLE output) +if(NOT output STREQUAL "src/alpha;bravo;src/charlie;delta") + message(FATAL_ERROR "TRANSFORM(APPLY FOR step) is \"${output}\", expected is \"src/alpha;bravo;src/charlie;delta\"") +endif() + +# APPLY with REGEX selector +unset(output) +list(TRANSFORM mylist APPLY add_src_prefix REGEX "(r|t)a" OUTPUT_VARIABLE output) +if(NOT output STREQUAL "alpha;src/bravo;charlie;src/delta") + message(FATAL_ERROR "TRANSFORM(APPLY REGEX) is \"${output}\", expected is \"alpha;src/bravo;charlie;src/delta\"") +endif() + +# APPLY with a different function +unset(output) +list(TRANSFORM mylist APPLY add_prefix OUTPUT_VARIABLE output) +if(NOT output STREQUAL "prefix_alpha;prefix_bravo;prefix_charlie;prefix_delta") + message(FATAL_ERROR "TRANSFORM(APPLY add_prefix) is \"${output}\", expected is \"prefix_alpha;prefix_bravo;prefix_charlie;prefix_delta\"") +endif() + +# APPLY on empty list +set(empty_list "") +list(TRANSFORM empty_list APPLY add_src_prefix OUTPUT_VARIABLE output) +if(NOT output STREQUAL "") + message(FATAL_ERROR "TRANSFORM(APPLY empty) is \"${output}\", expected is \"\"") +endif() + +# APPLY with function that returns empty string +function(make_empty in out) + set(${out} "" PARENT_SCOPE) +endfunction() + +set(mylist alpha bravo charlie) +list(TRANSFORM mylist APPLY make_empty) +if(NOT mylist STREQUAL ";;") + message(FATAL_ERROR "TRANSFORM(APPLY make_empty) is \"${mylist}\", expected is \";;\"") +endif() + +# Recursive APPLY: an APPLY function that itself calls list(TRANSFORM APPLY). +# The inner function returns the output variable name it was given. +function(return_out_var_name in out) + set(${out} "${out}" PARENT_SCOPE) +endfunction() + +# The outer function triggers a nested APPLY and verifies that the inner +# output variable name differs from its own. +function(inner_name_is_different in out) + set(_inner x) + list(TRANSFORM _inner APPLY return_out_var_name OUTPUT_VARIABLE _inner_out) + # _inner_out now holds the inner output variable name + if("${out}" STREQUAL "${_inner_out}") + set(${out} "FALSE" PARENT_SCOPE) + else() + set(${out} "TRUE" PARENT_SCOPE) + endif() +endfunction() + +set(mylist a b c) +list(TRANSFORM mylist APPLY inner_name_is_different OUTPUT_VARIABLE output) +if(NOT output STREQUAL "TRUE;TRUE;TRUE") + message(FATAL_ERROR "TRANSFORM(APPLY nested smoke) is \"${output}\", expected \"TRUE;TRUE;TRUE\"") +endif()
diff --git a/Tests/RunCMake/list/TRANSFORM-PREDICATE-Reentrant.cmake b/Tests/RunCMake/list/TRANSFORM-PREDICATE-Reentrant.cmake new file mode 100644 index 0000000..9c9bc26 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-PREDICATE-Reentrant.cmake
@@ -0,0 +1,91 @@ +# A PREDICATE function that reenters list(TRANSFORM) must not corrupt the +# outer transform. The nested action must match the outer one; a different +# action uses separate state. Each case checks the nested list too, so +# repairing the outer call by breaking the inner one still fails. + +# REPLACE, single element +function(pred_replace value out) + set(inner "hello") + list(TRANSFORM inner REPLACE "l" "L") + if(NOT inner STREQUAL "heLLo") + message(FATAL_ERROR "nested REPLACE is \"${inner}\", expected \"heLLo\"") + endif() + set(${out} TRUE PARENT_SCOPE) +endfunction() + +set(replace_single "aXa") +list(TRANSFORM replace_single REPLACE "X" "Z" PREDICATE pred_replace) +if(NOT replace_single STREQUAL "aZa") + message(FATAL_ERROR "replace_single is \"${replace_single}\", expected \"aZa\"") +endif() + +# REPLACE, only the last element reenters +# Nothing follows the reentering element: wrong value, not undefined behavior. +function(pred_replace_last value out) + if(value STREQUAL "cXc") + set(inner "hello") + list(TRANSFORM inner REPLACE "l" "L") + if(NOT inner STREQUAL "heLLo") + message(FATAL_ERROR "nested REPLACE is \"${inner}\", expected \"heLLo\"") + endif() + endif() + set(${out} TRUE PARENT_SCOPE) +endfunction() + +set(replace_last "aXa" "bXb" "cXc") +list(TRANSFORM replace_last REPLACE "X" "Z" PREDICATE pred_replace_last) +if(NOT replace_last STREQUAL "aZa;bZb;cZc") + message(FATAL_ERROR "replace_last is \"${replace_last}\", expected \"aZa;bZb;cZc\"") +endif() + +# APPEND +# Not redundant with REPLACE: the operand is a plain member, not a +# heap-allocated helper. +function(pred_append value out) + set(inner "q") + list(TRANSFORM inner APPEND "_NESTED") + if(NOT inner STREQUAL "q_NESTED") + message(FATAL_ERROR "nested APPEND is \"${inner}\", expected \"q_NESTED\"") + endif() + set(${out} TRUE PARENT_SCOPE) +endfunction() + +set(append_single "a") +list(TRANSFORM append_single APPEND "_OUTER" PREDICATE pred_append) +if(NOT append_single STREQUAL "a_OUTER") + message(FATAL_ERROR "append_single is \"${append_single}\", expected \"a_OUTER\"") +endif() + +# PREPEND +function(pred_prepend value out) + set(inner "q") + list(TRANSFORM inner PREPEND "NESTED_") + if(NOT inner STREQUAL "NESTED_q") + message(FATAL_ERROR "nested PREPEND is \"${inner}\", expected \"NESTED_q\"") + endif() + set(${out} TRUE PARENT_SCOPE) +endfunction() + +set(prepend_single "a") +list(TRANSFORM prepend_single PREPEND "OUTER_" PREDICATE pred_prepend) +if(NOT prepend_single STREQUAL "OUTER_a") + message(FATAL_ERROR "prepend_single is \"${prepend_single}\", expected \"OUTER_a\"") +endif() + +# TOUPPER, every element reenters +# The only case transforming an element after a reentering one, covering a +# selector that outlives the nested call. +function(pred_toupper value out) + set(inner a b) + list(TRANSFORM inner TOUPPER) + if(NOT inner STREQUAL "A;B") + message(FATAL_ERROR "nested TOUPPER is \"${inner}\", expected \"A;B\"") + endif() + set(${out} TRUE PARENT_SCOPE) +endfunction() + +set(toupper_all x y z) +list(TRANSFORM toupper_all TOUPPER PREDICATE pred_toupper) +if(NOT toupper_all STREQUAL "X;Y;Z") + message(FATAL_ERROR "toupper_all is \"${toupper_all}\", expected \"X;Y;Z\"") +endif()
diff --git a/Tests/RunCMake/list/TRANSFORM-PREDICATE.cmake b/Tests/RunCMake/list/TRANSFORM-PREDICATE.cmake new file mode 100644 index 0000000..f010f64 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-PREDICATE.cmake
@@ -0,0 +1,83 @@ +# Predicate: returns TRUE for items starting with "b" +function(starts_with_b input result) + if(input MATCHES "^b") + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + +set(mylist alpha bravo charlie bravo_two delta) + +# Basic PREDICATE with TOUPPER - only items starting with "b" are uppercased +list(TRANSFORM mylist TOUPPER PREDICATE starts_with_b OUTPUT_VARIABLE output) +if(NOT output STREQUAL "alpha;BRAVO;charlie;BRAVO_TWO;delta") + message(FATAL_ERROR "TRANSFORM(TOUPPER PREDICATE) is \"${output}\", expected \"alpha;BRAVO;charlie;BRAVO_TWO;delta\"") +endif() + +# Verify original list unchanged (OUTPUT_VARIABLE) +if(NOT mylist STREQUAL "alpha;bravo;charlie;bravo_two;delta") + message(FATAL_ERROR "Original list modified: \"${mylist}\"") +endif() + +# PREDICATE in-place +list(TRANSFORM mylist TOUPPER PREDICATE starts_with_b) +if(NOT mylist STREQUAL "alpha;BRAVO;charlie;BRAVO_TWO;delta") + message(FATAL_ERROR "TRANSFORM(TOUPPER PREDICATE in-place) is \"${mylist}\", expected \"alpha;BRAVO;charlie;BRAVO_TWO;delta\"") +endif() + +# PREDICATE combined with APPLY +function(add_prefix in out) + set(${out} "prefix_${in}" PARENT_SCOPE) +endfunction() + +set(mylist alpha bravo charlie delta) +list(TRANSFORM mylist APPLY add_prefix PREDICATE starts_with_b OUTPUT_VARIABLE output) +if(NOT output STREQUAL "alpha;prefix_bravo;charlie;delta") + message(FATAL_ERROR "TRANSFORM(APPLY PREDICATE) is \"${output}\", expected \"alpha;prefix_bravo;charlie;delta\"") +endif() + +# PREDICATE on empty list +set(empty_list "") +list(TRANSFORM empty_list TOUPPER PREDICATE starts_with_b OUTPUT_VARIABLE output) +if(NOT output STREQUAL "") + message(FATAL_ERROR "TRANSFORM(PREDICATE empty) is \"${output}\", expected \"\"") +endif() + +# PREDICATE where nothing matches (all elements unchanged) +set(mylist alpha charlie delta) +list(TRANSFORM mylist TOUPPER PREDICATE starts_with_b OUTPUT_VARIABLE output) +if(NOT output STREQUAL "alpha;charlie;delta") + message(FATAL_ERROR "TRANSFORM(PREDICATE no-match) is \"${output}\", expected \"alpha;charlie;delta\"") +endif() + +# PREDICATE where everything matches +set(mylist bravo bronze bull) +list(TRANSFORM mylist TOUPPER PREDICATE starts_with_b OUTPUT_VARIABLE output) +if(NOT output STREQUAL "BRAVO;BRONZE;BULL") + message(FATAL_ERROR "TRANSFORM(PREDICATE all-match) is \"${output}\", expected \"BRAVO;BRONZE;BULL\"") +endif() + +# Recursive PREDICATE: a PREDICATE function that itself calls list(TRANSFORM APPLY). +# The inner function returns the output variable name it was given. +function(return_pred_var_name in out) + set(${out} "${out}" PARENT_SCOPE) +endfunction() + +# The outer predicate triggers a nested APPLY and verifies that the inner +# output variable name differs from its own result variable. +function(inner_pred_name_is_different input result) + set(_inner x) + list(TRANSFORM _inner APPLY return_pred_var_name OUTPUT_VARIABLE _inner_names) + if("${result}" STREQUAL "${_inner_names}") + set(${result} FALSE PARENT_SCOPE) + else() + set(${result} TRUE PARENT_SCOPE) + endif() +endfunction() + +set(mylist x y z) +list(TRANSFORM mylist TOUPPER PREDICATE inner_pred_name_is_different OUTPUT_VARIABLE output) +if(NOT output STREQUAL "X;Y;Z") + message(FATAL_ERROR "TRANSFORM(PREDICATE nested smoke) is \"${output}\", expected \"X;Y;Z\"") +endif()
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro-result.txt
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro-stderr.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro-stderr.txt new file mode 100644 index 0000000..8c2b77d --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at TRANSFORM-Selector-PREDICATE-Macro\.cmake:11 \(list\): + list sub-command TRANSFORM, selector PREDICATE: macro "is_short" may not be + used here; define it as a function\(\) instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro.cmake b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro.cmake new file mode 100644 index 0000000..c9334e8 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro.cmake
@@ -0,0 +1,11 @@ +macro(is_short input result) + string(LENGTH "${input}" _len) + if(_len LESS 4) + set(${result} TRUE) + else() + set(${result} FALSE) + endif() +endmacro() + +set(mylist alpha bravo charlie) +list(TRANSFORM mylist TOUPPER PREDICATE is_short)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-result.txt
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-stderr.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-stderr.txt new file mode 100644 index 0000000..22c4133 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at TRANSFORM-Selector-PREDICATE-NoArguments\.cmake:2 \(list\): + list sub-command TRANSFORM, selector PREDICATE expects 'function name' + argument\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments.cmake b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments.cmake new file mode 100644 index 0000000..f263d8b --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments.cmake
@@ -0,0 +1,2 @@ +set(mylist alpha bravo charlie) +list(TRANSFORM mylist TOUPPER PREDICATE)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-result.txt
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-stderr.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-stderr.txt new file mode 100644 index 0000000..87310b2 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at TRANSFORM-Selector-PREDICATE-NoOutput\.cmake:6 \(list\): + list sub-command TRANSFORM, selector PREDICATE: function "bad_predicate" + did not set the output variable\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput.cmake b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput.cmake new file mode 100644 index 0000000..73b18c2 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput.cmake
@@ -0,0 +1,6 @@ +function(bad_predicate in out) + # Deliberately does NOT set ${out} +endfunction() + +set(mylist alpha bravo charlie) +list(TRANSFORM mylist TOUPPER PREDICATE bad_predicate)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-result.txt
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-stderr.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-stderr.txt new file mode 100644 index 0000000..21bea51 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at TRANSFORM-Selector-PREDICATE-UnknownFunction\.cmake:2 \(list\): + list sub-command TRANSFORM, selector PREDICATE: unknown function + "no_such_function"\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction.cmake b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction.cmake new file mode 100644 index 0000000..d497288 --- /dev/null +++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction.cmake
@@ -0,0 +1,2 @@ +set(mylist alpha bravo charlie) +list(TRANSFORM mylist TOUPPER PREDICATE no_such_function)
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/math/MATH-DivideMinByMinusOne-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/math/MATH-DivideMinByMinusOne-result.txt
diff --git a/Tests/RunCMake/math/MATH-DivideMinByMinusOne-stderr.txt b/Tests/RunCMake/math/MATH-DivideMinByMinusOne-stderr.txt new file mode 100644 index 0000000..7ea1553 --- /dev/null +++ b/Tests/RunCMake/math/MATH-DivideMinByMinusOne-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at MATH-DivideMinByMinusOne\.cmake:1 \(math\): + math cannot evaluate the expression: "~9223372036854775807/-1": signed + integer overflow in division\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-DivideMinByMinusOne.cmake b/Tests/RunCMake/math/MATH-DivideMinByMinusOne.cmake new file mode 100644 index 0000000..38277b8 --- /dev/null +++ b/Tests/RunCMake/math/MATH-DivideMinByMinusOne.cmake
@@ -0,0 +1 @@ +math(EXPR var "~9223372036854775807/-1")
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/math/MATH-ModMinByMinusOne-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/math/MATH-ModMinByMinusOne-result.txt
diff --git a/Tests/RunCMake/math/MATH-ModMinByMinusOne-stderr.txt b/Tests/RunCMake/math/MATH-ModMinByMinusOne-stderr.txt new file mode 100644 index 0000000..f2d7a1b --- /dev/null +++ b/Tests/RunCMake/math/MATH-ModMinByMinusOne-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at MATH-ModMinByMinusOne\.cmake:1 \(math\): + math cannot evaluate the expression: "~9223372036854775807%-1": signed + integer overflow in modulo\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-ModMinByMinusOne.cmake b/Tests/RunCMake/math/MATH-ModMinByMinusOne.cmake new file mode 100644 index 0000000..27e5f46 --- /dev/null +++ b/Tests/RunCMake/math/MATH-ModMinByMinusOne.cmake
@@ -0,0 +1 @@ +math(EXPR var "~9223372036854775807%-1")
diff --git a/Tests/RunCMake/math/MATH-ToleratedExpression-stderr.txt b/Tests/RunCMake/math/MATH-ToleratedExpression-stderr.txt index 4cc12fb..a39cf3a 100644 --- a/Tests/RunCMake/math/MATH-ToleratedExpression-stderr.txt +++ b/Tests/RunCMake/math/MATH-ToleratedExpression-stderr.txt
@@ -1,8 +1,8 @@ -^CMake Warning \(dev\) at MATH-ToleratedExpression\.cmake:1 \(math\): +^CMake Warning \(author\) at MATH-ToleratedExpression\.cmake:1 \(math\): Unexpected character in expression at position 1: ' Unexpected character in expression at position 7: ' Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/math/Overflow-stderr.txt b/Tests/RunCMake/math/Overflow-stderr.txt new file mode 100644 index 0000000..d469bc2 --- /dev/null +++ b/Tests/RunCMake/math/Overflow-stderr.txt
@@ -0,0 +1,71 @@ +^CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + left shift of negative value in: + + -4 << 1 + +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + shift exponent is negative in: + + 4 << -63 + +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + shift exponent is negative in: + + 4 >> -63 + +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + shift exponent is too large in: + + 4 << 65 + +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + shift exponent is too large in: + + 4 >> 65 + +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + signed integer overflow in: + + 9223372036854775807 \+ 1 + +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + signed integer overflow in: + + -9223372036854775807 - 2 + +This warning is for project developers\. Use -Wno-author to suppress it\. ++ +CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + signed integer overflow in: + + 9223372036854775807 \* 2 + +This warning is for project developers\. Use -Wno-author to suppress it\. + +CMake Warning \(author\) at [^ +]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\): + signed integer cannot negate: + + -9223372036854775808 + +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/math/Overflow-stdout.txt b/Tests/RunCMake/math/Overflow-stdout.txt new file mode 100644 index 0000000..5bcdcca --- /dev/null +++ b/Tests/RunCMake/math/Overflow-stdout.txt
@@ -0,0 +1,10 @@ +^-- -4 << 1: -8 +-- -4 >> 1: -2 +-- 4 << -63: 8 +-- 4 >> -63: 2 +-- 4 << 65: 8 +-- 4 >> 65: 2 +-- 0x7FFFFFFFFFFFFFFF \+ 1: -9223372036854775808 +-- -0x7FFFFFFFFFFFFFFF - 2: 9223372036854775807 +-- 0x7FFFFFFFFFFFFFFF \* 2: -2 +-- -~0x7FFFFFFFFFFFFFFF: -9223372036854775808$
diff --git a/Tests/RunCMake/math/Overflow.cmake b/Tests/RunCMake/math/Overflow.cmake new file mode 100644 index 0000000..c5a7464 --- /dev/null +++ b/Tests/RunCMake/math/Overflow.cmake
@@ -0,0 +1,15 @@ +foreach(expr IN ITEMS + "-4 << 1" + "-4 >> 1" + " 4 << -63" + " 4 >> -63" + " 4 << 65" + " 4 >> 65" + " 0x7FFFFFFFFFFFFFFF + 1" + "-0x7FFFFFFFFFFFFFFF - 2" + " 0x7FFFFFFFFFFFFFFF * 2" + "-~0x7FFFFFFFFFFFFFFF" + ) + math(EXPR result "${expr}") + message(STATUS "${expr}: ${result}") +endforeach()
diff --git a/Tests/RunCMake/math/RunCMakeTest.cmake b/Tests/RunCMake/math/RunCMakeTest.cmake index 88e7bd0..3e8a499 100644 --- a/Tests/RunCMake/math/RunCMakeTest.cmake +++ b/Tests/RunCMake/math/RunCMakeTest.cmake
@@ -8,3 +8,9 @@ run_cmake(MATH-ToleratedExpression) run_cmake(MATH-DivideByZero) run_cmake(MATH-ModByZero) +run_cmake(MATH-DivideMinByMinusOne) +run_cmake(MATH-ModMinByMinusOne) + +if(CMake_TEST_MATH_OVERFLOW) + run_cmake_script(Overflow) +endif()
diff --git a/Tests/RunCMake/message/CMakeLists.txt b/Tests/RunCMake/message/CMakeLists.txt index bf2ef15..8ad75a1 100644 --- a/Tests/RunCMake/message/CMakeLists.txt +++ b/Tests/RunCMake/message/CMakeLists.txt
@@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 4.0...4.4) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/message/defaultmessage-stderr.txt b/Tests/RunCMake/message/defaultmessage-stderr.txt index 86e882a..9b3c7d6 100644 --- a/Tests/RunCMake/message/defaultmessage-stderr.txt +++ b/Tests/RunCMake/message/defaultmessage-stderr.txt
@@ -1,11 +1,12 @@ -^CMake Deprecation Warning at defaultmessage\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at defaultmessage\.cmake:2 \(message\): This is a deprecation warning Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) - - -CMake Warning \(dev\) at defaultmessage\.cmake:4 \(message\): +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. ++ +CMake Warning \(author\) at defaultmessage\.cmake:4 \(message\): This is a author warning Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/message/errormessage_deprecated-stderr.txt b/Tests/RunCMake/message/errormessage_deprecated-stderr.txt index 8744f72..5275fbd 100644 --- a/Tests/RunCMake/message/errormessage_deprecated-stderr.txt +++ b/Tests/RunCMake/message/errormessage_deprecated-stderr.txt
@@ -1,4 +1,6 @@ -^CMake Deprecation Error at errormessage_deprecated\.cmake:3 \(message\): +^CMake Error \(deprecated\) at errormessage_deprecated\.cmake:3 \(message\): This is a deprecation error Call Stack \(most recent call first\): - CMakeLists\.txt:3 \(include\)$ + CMakeLists\.txt:3 \(include\) +This error is for project developers\. Use -Wno-error=author or +-Wno-error=deprecated to suppress it\.$
diff --git a/Tests/RunCMake/message/errormessage_deprecated.cmake b/Tests/RunCMake/message/errormessage_deprecated.cmake index 579275e..76429ea 100644 --- a/Tests/RunCMake/message/errormessage_deprecated.cmake +++ b/Tests/RunCMake/message/errormessage_deprecated.cmake
@@ -1,3 +1,3 @@ -set(CMAKE_ERROR_DEPRECATED ON) +cmake_diagnostic(SET CMD_DEPRECATED SEND_ERROR) message(DEPRECATION "This is a deprecation error")
diff --git a/Tests/RunCMake/message/errormessage_dev-stderr.txt b/Tests/RunCMake/message/errormessage_dev-stderr.txt index 2d8075a..c55dc79 100644 --- a/Tests/RunCMake/message/errormessage_dev-stderr.txt +++ b/Tests/RunCMake/message/errormessage_dev-stderr.txt
@@ -1,5 +1,5 @@ -^CMake Error \(dev\) at errormessage_dev\.cmake:3 \(message\): +^CMake Error \(author\) at errormessage_dev\.cmake:3 \(message\): This is a author error Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This error is for project developers\. Use -Wno-error=dev to suppress it\.$ +This error is for project developers\. Use -Wno-error=author to suppress it\.$
diff --git a/Tests/RunCMake/message/errormessage_dev.cmake b/Tests/RunCMake/message/errormessage_dev.cmake index 6ba1165..405e891 100644 --- a/Tests/RunCMake/message/errormessage_dev.cmake +++ b/Tests/RunCMake/message/errormessage_dev.cmake
@@ -1,3 +1,3 @@ -set(CMAKE_SUPPRESS_DEVELOPER_ERRORS OFF) +cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR) message(AUTHOR_WARNING "This is a author error")
diff --git a/Tests/RunCMake/message/message-checks-stderr.txt b/Tests/RunCMake/message/message-checks-stderr.txt index ca04ae4..c50e55d 100644 --- a/Tests/RunCMake/message/message-checks-stderr.txt +++ b/Tests/RunCMake/message/message-checks-stderr.txt
@@ -1,3 +1,3 @@ -^CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-checks\.cmake:13 \(message\): +^CMake Warning \(author\) at.*/Tests/RunCMake/message/message-checks\.cmake:13 \(message\): Ignored CHECK_FAIL without CHECK_START -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/message/message-internal-warning-stderr.txt b/Tests/RunCMake/message/message-internal-warning-stderr.txt index 32d5b46..a895bd0 100644 --- a/Tests/RunCMake/message/message-internal-warning-stderr.txt +++ b/Tests/RunCMake/message/message-internal-warning-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) in message-internal-warning\.cmake: +^CMake Warning \(author\) in message-internal-warning\.cmake: A logical block opening on the line .*Tests/RunCMake/message/message-internal-warning\.cmake:4 \(macro\) @@ -10,4 +10,4 @@ with mis-matching arguments\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/message/message-internal-warning.cmake b/Tests/RunCMake/message/message-internal-warning.cmake index 33993c7..f549988 100644 --- a/Tests/RunCMake/message/message-internal-warning.cmake +++ b/Tests/RunCMake/message/message-internal-warning.cmake
@@ -1,5 +1,5 @@ -set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ON) +cmake_diagnostic(SET CMD_AUTHOR WARN) macro(mymacro) endmacro(notmymacro)
diff --git a/Tests/RunCMake/message/message-log-level-debug-stderr.txt b/Tests/RunCMake/message/message-log-level-debug-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-log-level-debug-stderr.txt +++ b/Tests/RunCMake/message/message-log-level-debug-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-log-level-default-stderr.txt b/Tests/RunCMake/message/message-log-level-default-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-log-level-default-stderr.txt +++ b/Tests/RunCMake/message/message-log-level-default-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-log-level-notice-stderr.txt b/Tests/RunCMake/message/message-log-level-notice-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-log-level-notice-stderr.txt +++ b/Tests/RunCMake/message/message-log-level-notice-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-log-level-override-stderr.txt b/Tests/RunCMake/message/message-log-level-override-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-log-level-override-stderr.txt +++ b/Tests/RunCMake/message/message-log-level-override-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-log-level-status-stderr.txt b/Tests/RunCMake/message/message-log-level-status-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-log-level-status-stderr.txt +++ b/Tests/RunCMake/message/message-log-level-status-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-log-level-trace-stderr.txt b/Tests/RunCMake/message/message-log-level-trace-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-log-level-trace-stderr.txt +++ b/Tests/RunCMake/message/message-log-level-trace-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-log-level-verbose-stderr.txt b/Tests/RunCMake/message/message-log-level-verbose-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-log-level-verbose-stderr.txt +++ b/Tests/RunCMake/message/message-log-level-verbose-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-log-level-warning-stderr.txt b/Tests/RunCMake/message/message-log-level-warning-stderr.txt index c721b06..30b6b6b 100644 --- a/Tests/RunCMake/message/message-log-level-warning-stderr.txt +++ b/Tests/RunCMake/message/message-log-level-warning-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message$
diff --git a/Tests/RunCMake/message/message-loglevel-debug-stderr.txt b/Tests/RunCMake/message/message-loglevel-debug-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-loglevel-debug-stderr.txt +++ b/Tests/RunCMake/message/message-loglevel-debug-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-loglevel-default-stderr.txt b/Tests/RunCMake/message/message-loglevel-default-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-loglevel-default-stderr.txt +++ b/Tests/RunCMake/message/message-loglevel-default-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-loglevel-notice-stderr.txt b/Tests/RunCMake/message/message-loglevel-notice-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-loglevel-notice-stderr.txt +++ b/Tests/RunCMake/message/message-loglevel-notice-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-loglevel-status-stderr.txt b/Tests/RunCMake/message/message-loglevel-status-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-loglevel-status-stderr.txt +++ b/Tests/RunCMake/message/message-loglevel-status-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-loglevel-trace-stderr.txt b/Tests/RunCMake/message/message-loglevel-trace-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-loglevel-trace-stderr.txt +++ b/Tests/RunCMake/message/message-loglevel-trace-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-loglevel-verbose-stderr.txt b/Tests/RunCMake/message/message-loglevel-verbose-stderr.txt index efec736..672b700 100644 --- a/Tests/RunCMake/message/message-loglevel-verbose-stderr.txt +++ b/Tests/RunCMake/message/message-loglevel-verbose-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message
diff --git a/Tests/RunCMake/message/message-loglevel-warning-stderr.txt b/Tests/RunCMake/message/message-loglevel-warning-stderr.txt index c721b06..30b6b6b 100644 --- a/Tests/RunCMake/message/message-loglevel-warning-stderr.txt +++ b/Tests/RunCMake/message/message-loglevel-warning-stderr.txt
@@ -1,9 +1,11 @@ -^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): +^CMake Warning \(deprecated\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): Deprecation warning +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + -CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): +CMake Warning \(author\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): Author warning message -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. + CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): Warning message$
diff --git a/Tests/RunCMake/message/nomessage-internal-warning.cmake b/Tests/RunCMake/message/nomessage-internal-warning.cmake index 3ec2e28..a5d3cea 100644 --- a/Tests/RunCMake/message/nomessage-internal-warning.cmake +++ b/Tests/RunCMake/message/nomessage-internal-warning.cmake
@@ -1,5 +1,5 @@ -set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ON CACHE BOOL "") +cmake_diagnostic(SET CMD_AUTHOR IGNORE) macro(mymacro) endmacro(notmymacro)
diff --git a/Tests/RunCMake/message/nomessage.cmake b/Tests/RunCMake/message/nomessage.cmake index 78f4769..b793293 100644 --- a/Tests/RunCMake/message/nomessage.cmake +++ b/Tests/RunCMake/message/nomessage.cmake
@@ -1,8 +1,8 @@ -set(CMAKE_WARN_DEPRECATED OFF) +cmake_diagnostic(SET CMD_DEPRECATED IGNORE) message(DEPRECATION "This is not issued") -set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ON) +cmake_diagnostic(SET CMD_AUTHOR IGNORE) message(AUTHOR_WARNING "This is not issued")
diff --git a/Tests/RunCMake/message/warnmessage-stderr.txt b/Tests/RunCMake/message/warnmessage-stderr.txt index b02eef1..4b83580 100644 --- a/Tests/RunCMake/message/warnmessage-stderr.txt +++ b/Tests/RunCMake/message/warnmessage-stderr.txt
@@ -1,11 +1,12 @@ -^CMake Deprecation Warning at warnmessage\.cmake:4 \(message\): +^CMake Warning \(deprecated\) at warnmessage\.cmake:4 \(message\): This is a deprecation warning Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) - - -CMake Warning \(dev\) at warnmessage\.cmake:8 \(message\): +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. ++ +CMake Warning \(author\) at warnmessage\.cmake:8 \(message\): This is a author warning Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/message/warnmessage.cmake b/Tests/RunCMake/message/warnmessage.cmake index 53f2a43..0792aec 100644 --- a/Tests/RunCMake/message/warnmessage.cmake +++ b/Tests/RunCMake/message/warnmessage.cmake
@@ -1,8 +1,8 @@ -set(CMAKE_WARN_DEPRECATED ON) +cmake_diagnostic(SET CMD_DEPRECATED WARN) message(DEPRECATION "This is a deprecation warning") -set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS OFF) +cmake_diagnostic(SET CMD_AUTHOR WARN) message(AUTHOR_WARNING "This is a author warning")
diff --git a/Tests/RunCMake/option/CMP0077-OLD-stderr.txt b/Tests/RunCMake/option/CMP0077-OLD-stderr.txt index 2b688eb..4450772 100644 --- a/Tests/RunCMake/option/CMP0077-OLD-stderr.txt +++ b/Tests/RunCMake/option/CMP0077-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0077-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0077-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0077 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/option/CMP0077-WARN-stderr.txt b/Tests/RunCMake/option/CMP0077-WARN-stderr.txt index 1e86fb6..f9178a4 100644 --- a/Tests/RunCMake/option/CMP0077-WARN-stderr.txt +++ b/Tests/RunCMake/option/CMP0077-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0077-WARN\.cmake:5 \(option\): +CMake Warning \(policy\) at CMP0077-WARN\.cmake:5 \(option\): Policy CMP0077 is not set: option\(\) honors normal variables\. Run "cmake --help-policy CMP0077" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\.
diff --git a/Tests/RunCMake/project/CMP0096-OLD-stderr.txt b/Tests/RunCMake/project/CMP0096-OLD-stderr.txt index 319ff97..ee7b454 100644 --- a/Tests/RunCMake/project/CMP0096-OLD-stderr.txt +++ b/Tests/RunCMake/project/CMP0096-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0096-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0096-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0096 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/project/KeywordProjectName-stderr.txt b/Tests/RunCMake/project/KeywordProjectName-stderr.txt index 73ca463..f3fc504 100644 --- a/Tests/RunCMake/project/KeywordProjectName-stderr.txt +++ b/Tests/RunCMake/project/KeywordProjectName-stderr.txt
@@ -1,47 +1,47 @@ -CMake Warning \(dev\) at KeywordProjectName\.cmake:[0-9]+ \(project\): +CMake Warning \(author\) at KeywordProjectName\.cmake:[0-9]+ \(project\): project\(\) called with 'LANGUAGES' as first argument\. The first parameter should be the project name, not a keyword argument\. See the cmake-commands\(7\) manual for correct usage of the project\(\) command\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at KeywordProjectName\.cmake:[0-9]+ \(project\): +CMake Warning \(author\) at KeywordProjectName\.cmake:[0-9]+ \(project\): project\(\) called with 'VERSION' as first argument\. The first parameter should be the project name, not a keyword argument\. See the cmake-commands\(7\) manual for correct usage of the project\(\) command\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at KeywordProjectName\.cmake:[0-9]+ \(project\): +CMake Warning \(author\) at KeywordProjectName\.cmake:[0-9]+ \(project\): project\(\) called with 'COMPAT_VERSION' as first argument\. The first parameter should be the project name, not a keyword argument\. See the cmake-commands\(7\) manual for correct usage of the project\(\) command\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at KeywordProjectName\.cmake:[0-9]+ \(project\): +CMake Warning \(author\) at KeywordProjectName\.cmake:[0-9]+ \(project\): project\(\) called with 'SPDX_LICENSE' as first argument\. The first parameter should be the project name, not a keyword argument\. See the cmake-commands\(7\) manual for correct usage of the project\(\) command\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at KeywordProjectName\.cmake:[0-9]+ \(project\): +CMake Warning \(author\) at KeywordProjectName\.cmake:[0-9]+ \(project\): project\(\) called with 'DESCRIPTION' as first argument\. The first parameter should be the project name, not a keyword argument\. See the cmake-commands\(7\) manual for correct usage of the project\(\) command\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. -CMake Warning \(dev\) at KeywordProjectName\.cmake:[0-9]+ \(project\): +CMake Warning \(author\) at KeywordProjectName\.cmake:[0-9]+ \(project\): project\(\) called with 'HOMEPAGE_URL' as first argument\. The first parameter should be the project name, not a keyword argument\. See the cmake-commands\(7\) manual for correct usage of the project\(\) command\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/project/NoMinimumRequired-stderr.txt b/Tests/RunCMake/project/NoMinimumRequired-stderr.txt index 83e2ac9..74c835b 100644 --- a/Tests/RunCMake/project/NoMinimumRequired-stderr.txt +++ b/Tests/RunCMake/project/NoMinimumRequired-stderr.txt
@@ -1,5 +1,5 @@ -CMake Warning \(dev\) at CMakeLists\.txt:[0-9]+ \(project\): +CMake Warning \(author\) at CMakeLists\.txt:[0-9]+ \(project\): cmake_minimum_required\(\) should be called prior to this top-level project\(\) call\. Please see the cmake-commands\(7\) manual for usage documentation of both commands\. -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/project/VersionMax-stderr.txt b/Tests/RunCMake/project/VersionMax-stderr.txt index ac7a143..89de061 100644 --- a/Tests/RunCMake/project/VersionMax-stderr.txt +++ b/Tests/RunCMake/project/VersionMax-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at VersionMax\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at VersionMax\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0096 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/project_injected/Inject-stderr.txt b/Tests/RunCMake/project_injected/Inject-stderr.txt index a202a92..f7612f4 100644 --- a/Tests/RunCMake/project_injected/Inject-stderr.txt +++ b/Tests/RunCMake/project_injected/Inject-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) in CMakeLists\.txt: +^CMake Warning \(author\) in CMakeLists\.txt: No project\(\) command is present\. The top-level CMakeLists\.txt file must contain a literal, direct call to the project\(\) command\. Add a line of code such as @@ -9,4 +9,4 @@ CMake is pretending there is a "project\(Project\)" command on the first line\. -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/return/CMP0140-OLD-stderr.txt b/Tests/RunCMake/return/CMP0140-OLD-stderr.txt index bcd37c2..a0ca8a2 100644 --- a/Tests/RunCMake/return/CMP0140-OLD-stderr.txt +++ b/Tests/RunCMake/return/CMP0140-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0140-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0140-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0140 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/return/CMP0140-WARN-stderr.txt b/Tests/RunCMake/return/CMP0140-WARN-stderr.txt index 456cd6d..0971ee7 100644 --- a/Tests/RunCMake/return/CMP0140-WARN-stderr.txt +++ b/Tests/RunCMake/return/CMP0140-WARN-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMP0140-WARN\.cmake:[0-9]+ \(return\): +CMake Warning \(policy\) at CMP0140-WARN\.cmake:[0-9]+ \(return\): Policy CMP0140 is not set: The return\(\) command checks its arguments\. Run "cmake --help-policy CMP0140" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -9,4 +9,5 @@ Call Stack \(most recent call first\): CMP0140-WARN\.cmake:[0-9]+ \(func\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/set/ExtraEnvValue-stderr.txt b/Tests/RunCMake/set/ExtraEnvValue-stderr.txt index 6028057..168217b 100644 --- a/Tests/RunCMake/set/ExtraEnvValue-stderr.txt +++ b/Tests/RunCMake/set/ExtraEnvValue-stderr.txt
@@ -1,6 +1,6 @@ -CMake Warning \(dev\) at ExtraEnvValue\.cmake:1 \(set\): +CMake Warning \(author\) at ExtraEnvValue\.cmake:1 \(set\): Only the first value argument is used when setting an environment variable\. Argument 'value_2' and later are unused\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/set/UnknownCacheType-stderr.txt b/Tests/RunCMake/set/UnknownCacheType-stderr.txt index 62ea6cc..08e4494 100644 --- a/Tests/RunCMake/set/UnknownCacheType-stderr.txt +++ b/Tests/RunCMake/set/UnknownCacheType-stderr.txt
@@ -1,5 +1,5 @@ -CMake Warning \(dev\) at UnknownCacheType\.cmake:1 \(set\): +CMake Warning \(author\) at UnknownCacheType\.cmake:1 \(set\): implicitly converting 'unknown_type_sample' to 'STRING' type\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\.
diff --git a/Tests/RunCMake/set_property/Common.cmake b/Tests/RunCMake/set_property/Common.cmake index 3aa78ed..0673bc8 100644 --- a/Tests/RunCMake/set_property/Common.cmake +++ b/Tests/RunCMake/set_property/Common.cmake
@@ -1,3 +1,5 @@ +enable_language(C) + macro(test_target_property PROP) add_custom_target(CustomTarget) set_property(TARGET CustomTarget PROPERTY ${PROP} x) @@ -28,8 +30,8 @@ endmacro() macro(test_file_set_property PROP) - add_library(foo INTERFACE) - target_sources(foo INTERFACE FILE_SET foo TYPE HEADERS) + add_library(foo STATIC foo.c) + target_sources(foo PRIVATE FILE_SET foo TYPE HEADERS) set_property(FILE_SET foo TARGET foo PROPERTY ${PROP} x) set_property(FILE_SET foo TARGET foo PROPERTY ${PROP}) set_property(FILE_SET foo TARGET foo APPEND PROPERTY ${PROP})
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/set_property/foo.c similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/set_property/foo.c
diff --git a/Tests/RunCMake/string/JSON.cmake b/Tests/RunCMake/string/JSON.cmake index 1a988e1..5cbc933 100644 --- a/Tests/RunCMake/string/JSON.cmake +++ b/Tests/RunCMake/string/JSON.cmake
@@ -393,3 +393,320 @@ assert_strequal("${result}" "\"{}\"") string(JSON result STRING_ENCODE []) assert_strequal("${result}" "\"[]\"") + +string(JSON result PARTIAL_EQUAL +[=[ +{ + "foo":"bar" +} +]=] +[=[ +{ + "foo": "bar", + "extra": 1 +} +]=]) + +if(NOT result) + message(SEND_ERROR "Expected ON got ${result}") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +{ + "foo":"bar" +} +]=] +[=[ +{ + "foo1": "bar" +} +]=]) +if(result) + message(SEND_ERROR "EXPECTED OFF got ${result}") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +{ + "types" : { + "number" : 5 + } +} +]=] +[=[ +{ + "foo" : "bar", + "array" : [5, "val", {"some": "other"}, null], + "types" : { + "null" : null, + "number" : 5, + "string" : "foo", + "boolean" : false, + "array" : [1,2,3], + "object" : {} + }, + "values" : { + "null" : null, + "number" : 5, + "string" : "foo", + "false" : false, + "true" : true + }, + "object": { + "foo": "bar" + }, + "special" : { + "foo;bar" : "value1", + ";" : "value2", + "semicolon" : ";", + "list" : ["one", "two;three", "four"], + "quote" : "\"", + "\"" : "quote", + "backslash" : "\\", + "\\" : "backslash", + "slash" : "\/", + "\/" : "slash", + "newline" : "\n", + "\n" : "newline", + "return" : "\r", + "\r" : "return", + "tab" : "\t", + "\t" : "tab", + "backspace" : "\b", + "\b" : "backspace", + "formfeed" : "\f", + "\f" : "formfeed" + } +} +]=]) +if(NOT result) + message(SEND_ERROR "EXPECTED ON got ${result} for nested subset") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +{ + "types" : { + "number" : 6 + } +} +]=] +[=[ +{ + "foo" : "bar", + "array" : [5, "val", {"some": "other"}, null], + "types" : { + "null" : null, + "number" : 5, + "string" : "foo", + "boolean" : false, + "array" : [1,2,3], + "object" : {} + }, + "values" : { + "null" : null, + "number" : 5, + "string" : "foo", + "false" : false, + "true" : true + }, + "object": { + "foo": "bar" + }, + "special" : { + "foo;bar" : "value1", + ";" : "value2", + "semicolon" : ";", + "list" : ["one", "two;three", "four"], + "quote" : "\"", + "\"" : "quote", + "backslash" : "\\", + "\\" : "backslash", + "slash" : "\/", + "\/" : "slash", + "newline" : "\n", + "\n" : "newline", + "return" : "\r", + "\r" : "return", + "tab" : "\t", + "\t" : "tab", + "backspace" : "\b", + "\b" : "backspace", + "formfeed" : "\f", + "\f" : "formfeed" + } +} +]=]) +if(result) + message(SEND_ERROR "EXPECTED OFF got ${result} for nested mismatch") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +[ + 2, + 1 +] +]=] +[=[ +[ + 1, + 2, + 3 +] +]=]) +if(NOT result) + message(SEND_ERROR "Expected ON got ${result} for unordered array subset") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +[ + 2, + 4 +] +]=] +[=[ +[ + 1, + 2, + 3 +] +]=]) +if(result) + message(SEND_ERROR "Expected OFF got ${result} for missing array element") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +[ + 1, + 2, + 3 +] +]=] +[=[ +[ + 1, + 2 +] +]=]) +if(result) + message(SEND_ERROR "Expected OFF got ${result} for pattern larger than actual") +endif() + +string(JSON result PARTIAL_EQUAL + [=[ +[ + 1, + 1 +] +]=] +[=[ +[ + 1, + 2, + 1 +] +]=]) +if(NOT result) + message(SEND_ERROR "EXPECTED ON got ${result} for duplicate match") +endif() + +string(JSON result PARTIAL_EQUAL + [=[ +[ + { + "id":2 + }, + { + "id":1 + } +] +]=] +[=[ +[ + { + "id":1, + "name":"A" + }, + { + "id":2, + "name":"B" + }, + { + "id":3, + "name":"C" + } +] +]=]) +if(NOT result) + message(SEND_ERROR "EXPECTED ON got ${result} for array of object subset") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +[ + { + "id":4 + } +] +]=] +[=[ +[ + { + "id":1 + }, + { + "id":2 + } +] +]=]) +if(result) + message(SEND_ERROR "Expected OFF got ${result} for array of object mismatch") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +{ + "tags": [ + "b", + "a" + ] +} +]=] +[=[ +{ + "tags": [ + "a", + "b", + "c" + ], + "other": 123 +} +]=]) +if(NOT result) + message(SEND_ERROR "EXPECTED ON got ${result} for object+array partial match") +endif() + +string(JSON result PARTIAL_EQUAL +[=[ +{ + "tags": [ + "a", + "a" + ] +} +]=] +[=[ +{ + "tags": [ + "a", + "b" + ] +} +]=]) +if(result) + message(SEND_ERROR "Expected OFF got ${result} for duplicate array mismatch in object") +endif()
diff --git a/Tests/RunCMake/string/JSONWrongMode-stderr.txt b/Tests/RunCMake/string/JSONWrongMode-stderr.txt index b37a084..41ecc12 100644 --- a/Tests/RunCMake/string/JSONWrongMode-stderr.txt +++ b/Tests/RunCMake/string/JSONWrongMode-stderr.txt
@@ -1,5 +1,6 @@ CMake Error at JSONWrongMode\.cmake:1 \(string\): string sub-command JSON got an invalid mode 'FOO', expected one of GET, - GET_RAW, TYPE, MEMBER, LENGTH, REMOVE, SET, EQUAL, STRING_ENCODE\. + GET_RAW, TYPE, MEMBER, LENGTH, REMOVE, SET, EQUAL, PARTIAL_EQUAL, + STRING_ENCODE\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/target_compile_features/cxx_not_enabled-stderr.txt b/Tests/RunCMake/target_compile_features/cxx_not_enabled-stderr.txt deleted file mode 100644 index 9d5875d..0000000 --- a/Tests/RunCMake/target_compile_features/cxx_not_enabled-stderr.txt +++ /dev/null
@@ -1,2 +0,0 @@ -^CMake Error:.*CMake can not determine linker language for target: main.* -CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/target_compile_features/cxx_not_enabled.cmake b/Tests/RunCMake/target_compile_features/cxx_not_enabled.cmake index b7e9119..a0a2363 100644 --- a/Tests/RunCMake/target_compile_features/cxx_not_enabled.cmake +++ b/Tests/RunCMake/target_compile_features/cxx_not_enabled.cmake
@@ -1,2 +1,3 @@ +enable_language(C) add_executable(main empty.c) target_compile_features(main PRIVATE cxx_decltype)
diff --git a/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt b/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt index d3cc7b5..583d56f 100644 --- a/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt +++ b/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0101-BEFORE_keyword\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0101-BEFORE_keyword\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0101 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_GROUP/RunCMakeTest.cmake index 6dbadaa..e0af6af 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/RunCMakeTest.cmake
@@ -63,7 +63,7 @@ endif() # Feature RESCAN -if (CMAKE_SYSTEM_NAME MATCHES "Linux|BSD" +if (CMAKE_SYSTEM_NAME MATCHES "Linux|BSD|Haiku" OR (CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND (NOT CMAKE_C_COMPILER_ID STREQUAL "SunPro" OR CMAKE_C_COMPILER_VERSION VERSION_GREATER "5.9")) OR (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU")) run_cmake(rescan)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/imported-target-stdout.txt b/Tests/RunCMake/target_link_libraries-LINK_GROUP/imported-target-stdout.txt index 999e3a7..5bc7248 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/imported-target-stdout.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/imported-target-stdout.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at imported-target\.cmake:[0-9]+ \(add_library\): +CMake Warning \(author\) at imported-target\.cmake:[0-9]+ \(add_library\): The 'IMPORTED' target 'NS::lib2' uses the generator-expression '\$<LINK_GROUP>' with the feature 'feat', which is undefined or unsupported\. @@ -7,7 +7,7 @@ "CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED"\? Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Error at imported-target\.cmake:[0-9]+ \(add_library\): Feature 'feat', specified through generator-expression '\$<LINK_GROUP>' to
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt index 30b780a..5eb59ae 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at imported-target\.cmake:[0-9]+ \(add_library\): +CMake Warning \(author\) at imported-target\.cmake:[0-9]+ \(add_library\): The 'IMPORTED' target 'NS::lib2' uses the generator-expression '\$<LINK_LIBRARY>' with the feature 'whole_archive', which is undefined or unsupported\. @@ -8,7 +8,7 @@ "CMAKE_C_LINK_LIBRARY_USING_whole_archive_SUPPORTED"\? Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author to suppress it\. CMake Error at imported-target\.cmake:[0-9]+ \(add_library\): Feature 'whole_archive', specified through generator-expression
diff --git a/Tests/RunCMake/target_link_libraries/CMP0079-iface-OLD-stderr.txt b/Tests/RunCMake/target_link_libraries/CMP0079-iface-OLD-stderr.txt index 6757b3f..25d4c5c 100644 --- a/Tests/RunCMake/target_link_libraries/CMP0079-iface-OLD-stderr.txt +++ b/Tests/RunCMake/target_link_libraries/CMP0079-iface-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0079-iface-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0079-iface-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0079 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stderr.txt b/Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stderr.txt index 75c5b86..bca7b14 100644 --- a/Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stderr.txt +++ b/Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at CMP0079-iface/CMakeLists\.txt:[0-9]+ \(target_link_libraries\): +^CMake Warning \(policy\) at CMP0079-iface/CMakeLists\.txt:[0-9]+ \(target_link_libraries\): Policy CMP0079 is not set: target_link_libraries allows use with targets in other directories\. Run "cmake --help-policy CMP0079" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -14,9 +14,10 @@ will be looked up in the directory in which the target was created rather than in this calling directory\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + -CMake Warning \(dev\) at CMP0079-iface/CMakeLists\.txt:[0-9]+ \(target_link_libraries\): +CMake Warning \(policy\) at CMP0079-iface/CMakeLists\.txt:[0-9]+ \(target_link_libraries\): Policy CMP0079 is not set: target_link_libraries allows use with targets in other directories\. Run "cmake --help-policy CMP0079" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -32,4 +33,5 @@ will be looked up in the directory in which the target was created rather than in this calling directory\. -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/target_link_libraries/CMP0079-link-OLD-stderr.txt b/Tests/RunCMake/target_link_libraries/CMP0079-link-OLD-stderr.txt index d1fd088..413057b 100644 --- a/Tests/RunCMake/target_link_libraries/CMP0079-link-OLD-stderr.txt +++ b/Tests/RunCMake/target_link_libraries/CMP0079-link-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0079-link-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0079-link-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0079 will be removed from a future version of CMake\. @@ -8,6 +8,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + CMake Error at CMP0079-link/CMakeLists\.txt:[0-9]+ \(target_link_libraries\): Attempt to add link library "foo" to target "top" which is not built in
diff --git a/Tests/RunCMake/target_link_libraries/CMP0108-OLD-self-link-stderr.txt b/Tests/RunCMake/target_link_libraries/CMP0108-OLD-self-link-stderr.txt index 1d18819..1fafb18 100644 --- a/Tests/RunCMake/target_link_libraries/CMP0108-OLD-self-link-stderr.txt +++ b/Tests/RunCMake/target_link_libraries/CMP0108-OLD-self-link-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0108-OLD-self-link\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0108-OLD-self-link\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0108 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/target_sources/CMP0076-WARN-stderr.txt b/Tests/RunCMake/target_sources/CMP0076-WARN-stderr.txt index ab24b2a..defae82 100644 --- a/Tests/RunCMake/target_sources/CMP0076-WARN-stderr.txt +++ b/Tests/RunCMake/target_sources/CMP0076-WARN-stderr.txt
@@ -1,19 +1,21 @@ -CMake Warning \(dev\) at CMP0076-WARN/CMakeLists\.txt:[0-9]+ \(target_sources\): +CMake Warning \(policy\) at CMP0076-WARN/CMakeLists\.txt:[0-9]+ \(target_sources\): Policy CMP0076 is not set: target_sources\(\) command converts relative paths to absolute\. Run "cmake --help-policy CMP0076" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. An interface source of target "publiclib" has a relative path\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. -CMake Warning \(dev\) at CMP0076-WARN/CMakeLists\.txt:[0-9]+ \(target_sources\): +CMake Warning \(policy\) at CMP0076-WARN/CMakeLists\.txt:[0-9]+ \(target_sources\): Policy CMP0076 is not set: target_sources\(\) command converts relative paths to absolute\. Run "cmake --help-policy CMP0076" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. A private source from a directory other than that of target "publiclib" has a relative path\. -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. CMake Error in CMakeLists\.txt: Target "publiclib" contains relative path in its INTERFACE_SOURCES:
diff --git a/Tests/RunCMake/target_sources/CMP0211-Common.cmake b/Tests/RunCMake/target_sources/CMP0211-Common.cmake new file mode 100644 index 0000000..6b5bfcd --- /dev/null +++ b/Tests/RunCMake/target_sources/CMP0211-Common.cmake
@@ -0,0 +1,9 @@ + +enable_language(C) + +add_library(foo1 STATIC lib1.c) +target_sources(foo1 PRIVATE FILE_SET s1 TYPE SOURCES FILES lib2.c) +target_sources(foo1 PRIVATE FILE_SET s2 TYPE SOURCES FILES lib2.c) + +add_library(foo2 STATIC lib1.c lib2.c) +target_sources(foo2 PRIVATE FILE_SET s1 TYPE SOURCES FILES lib2.c)
diff --git a/Tests/RunCMake/target_sources/CMP0211-NEW-HEADERS.cmake b/Tests/RunCMake/target_sources/CMP0211-NEW-HEADERS.cmake new file mode 100644 index 0000000..66ee91d --- /dev/null +++ b/Tests/RunCMake/target_sources/CMP0211-NEW-HEADERS.cmake
@@ -0,0 +1,13 @@ + +cmake_policy(SET CMP0211 NEW) + +enable_language(C) + +# files in HEADERS file sets can also be specified in other file sets + +add_library(foo1 STATIC lib1.c) +target_sources(foo1 PRIVATE FILE_SET s1 TYPE SOURCES FILES lib2.c h1.h) +target_sources(foo1 PRIVATE FILE_SET h1 TYPE HEADERS FILES h1.h) + +add_library(foo2 STATIC lib1.c h1.h) +target_sources(foo2 PRIVATE FILE_SET h1 TYPE HEADERS FILES h1.h)
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/target_sources/CMP0211-NEW-result.txt similarity index 100% rename from Tests/RunCMake/target_sources/FileSetFramework-result.txt rename to Tests/RunCMake/target_sources/CMP0211-NEW-result.txt
diff --git a/Tests/RunCMake/target_sources/CMP0211-NEW-stderr.txt b/Tests/RunCMake/target_sources/CMP0211-NEW-stderr.txt new file mode 100644 index 0000000..f0ab98a --- /dev/null +++ b/Tests/RunCMake/target_sources/CMP0211-NEW-stderr.txt
@@ -0,0 +1,14 @@ +CMake Error in CMakeLists\.txt: + In target "foo1" the file + + .+/Tests/RunCMake/target_sources/lib2\.c + + already belongs to file set "s[12]"\. + + +CMake Error in CMakeLists\.txt: + In target "foo2" the file + + .+/Tests/RunCMake/target_sources/lib2\.c + + already belongs to file set "s1"\.
diff --git a/Tests/RunCMake/target_sources/CMP0211-NEW.cmake b/Tests/RunCMake/target_sources/CMP0211-NEW.cmake new file mode 100644 index 0000000..4d42024 --- /dev/null +++ b/Tests/RunCMake/target_sources/CMP0211-NEW.cmake
@@ -0,0 +1,4 @@ + +cmake_policy(SET CMP0211 NEW) + +include(CMP0211-Common.cmake)
diff --git a/Tests/RunCMake/target_sources/CMP0211-OLD.cmake b/Tests/RunCMake/target_sources/CMP0211-OLD.cmake new file mode 100644 index 0000000..4e32f39 --- /dev/null +++ b/Tests/RunCMake/target_sources/CMP0211-OLD.cmake
@@ -0,0 +1,4 @@ + +cmake_policy(SET CMP0211 OLD) + +include(CMP0211-Common.cmake)
diff --git a/Tests/RunCMake/target_sources/CMP0211-WARN-stderr.txt b/Tests/RunCMake/target_sources/CMP0211-WARN-stderr.txt new file mode 100644 index 0000000..c886339 --- /dev/null +++ b/Tests/RunCMake/target_sources/CMP0211-WARN-stderr.txt
@@ -0,0 +1,27 @@ +CMake Warning \(policy\) in CMakeLists\.txt: + Policy CMP0211 is not set: A file may belong to at most one non-HEADERS + file set in a target\. Run "cmake --help-policy CMP0211" for policy + details\. Use the cmake_policy command to set the policy and suppress this + warning\. + + In target "foo1" the file + + .+/Tests/RunCMake/target_sources/lib2\.c + + already belongs to file set "s[12]"\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. + +CMake Warning \(policy\) in CMakeLists\.txt: + Policy CMP0211 is not set: A file may belong to at most one non-HEADERS + file set in a target\. Run "cmake --help-policy CMP0211" for policy + details\. Use the cmake_policy command to set the policy and suppress this + warning\. + + In target "foo2" the file + + .+/Tests/RunCMake/target_sources/lib2\.c + + already belongs to file set "s1"\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.
diff --git a/Tests/RunCMake/target_sources/CMP0211-WARN.cmake b/Tests/RunCMake/target_sources/CMP0211-WARN.cmake new file mode 100644 index 0000000..78fa4b3 --- /dev/null +++ b/Tests/RunCMake/target_sources/CMP0211-WARN.cmake
@@ -0,0 +1,2 @@ + +include(CMP0211-Common.cmake)
diff --git a/Tests/RunCMake/target_sources/FileSetChangeType-stderr.txt b/Tests/RunCMake/target_sources/FileSetChangeType-stderr.txt index 85fc718..3fd5a97 100644 --- a/Tests/RunCMake/target_sources/FileSetChangeType-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetChangeType-stderr.txt
@@ -1,5 +1,5 @@ ^CMake Error at FileSetChangeType\.cmake:[0-9]+ \(target_sources\): - target_sources Type "RESOURCES" for file set "a" does not match original + target_sources Type "CXX_MODULES" for file set "a" does not match original type "HEADERS" Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetChangeType.cmake b/Tests/RunCMake/target_sources/FileSetChangeType.cmake index 69eb6bc..75b11e4 100644 --- a/Tests/RunCMake/target_sources/FileSetChangeType.cmake +++ b/Tests/RunCMake/target_sources/FileSetChangeType.cmake
@@ -2,4 +2,4 @@ add_library(lib1 STATIC empty.c) target_sources(lib1 PRIVATE FILE_SET a TYPE HEADERS) -target_sources(lib1 PRIVATE FILE_SET a TYPE RESOURCES) +target_sources(lib1 PRIVATE FILE_SET a TYPE CXX_MODULES)
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt index c13c8a8..85da541 100644 --- a/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt
@@ -1,4 +1,5 @@ ^CMake Error at FileSetDefaultWrongType\.cmake:[0-9]+ \(target_sources\): - target_sources File set TYPE may only be "HEADERS" or "CXX_MODULES" + target_sources FILE_SET names starting with a capital letter are reserved + for built-in file sets and may only be "HEADERS", "SOURCES", "CXX_MODULES" Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetDuplicateSource.cmake b/Tests/RunCMake/target_sources/FileSetDuplicateSource.cmake new file mode 100644 index 0000000..6fb1ab3 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetDuplicateSource.cmake
@@ -0,0 +1,6 @@ +enable_language(C) + +add_library(lib1 STATIC empty.c) + +# files are silently de-duplicated +target_sources(lib1 PRIVATE FILE_SET HEADERS FILES h1.h h1.h)
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-stderr.txt b/Tests/RunCMake/target_sources/FileSetFramework-stderr.txt deleted file mode 100644 index ae7026a..0000000 --- a/Tests/RunCMake/target_sources/FileSetFramework-stderr.txt +++ /dev/null
@@ -1,4 +0,0 @@ -^CMake Error at FileSetFramework\.cmake:[0-9]+ \(target_sources\): - target_sources FILE_SETs may not be added to FRAMEWORK targets -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/target_sources/FileSetFramework1-result.txt similarity index 100% copy from Tests/RunCMake/target_sources/FileSetFramework-result.txt copy to Tests/RunCMake/target_sources/FileSetFramework1-result.txt
diff --git a/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt b/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt new file mode 100644 index 0000000..5a2e65a --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt
@@ -0,0 +1,5 @@ +CMake Error at FileSetFramework1\.cmake:[0-9]+ \(target_sources\): + target_sources FILE_SETs, of type "HEADERS", may not be added to FRAMEWORK + targets +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetFramework.cmake b/Tests/RunCMake/target_sources/FileSetFramework1.cmake similarity index 100% rename from Tests/RunCMake/target_sources/FileSetFramework.cmake rename to Tests/RunCMake/target_sources/FileSetFramework1.cmake
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/target_sources/FileSetFramework2-result.txt similarity index 100% copy from Tests/RunCMake/target_sources/FileSetFramework-result.txt copy to Tests/RunCMake/target_sources/FileSetFramework2-result.txt
diff --git a/Tests/RunCMake/target_sources/FileSetFramework2-stderr.txt b/Tests/RunCMake/target_sources/FileSetFramework2-stderr.txt new file mode 100644 index 0000000..5c54059 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetFramework2-stderr.txt
@@ -0,0 +1,3 @@ +CMake Error in CMakeLists\.txt: + The file set "HEADERS", of type "HEADERS", is incompatible with the + "FRAMEWORK" target "lib1"\.
diff --git a/Tests/RunCMake/target_sources/FileSetFramework2.cmake b/Tests/RunCMake/target_sources/FileSetFramework2.cmake new file mode 100644 index 0000000..6d89d41 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetFramework2.cmake
@@ -0,0 +1,7 @@ +enable_language(C) + +add_library(lib1 SHARED lib1.c) +target_sources(lib1 + PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES h1.h + ) +set_property(TARGET lib1 PROPERTY FRAMEWORK ON)
diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/target_sources/FileSetIncompatibleScopes-result.txt similarity index 100% copy from Tests/RunCMake/target_sources/FileSetFramework-result.txt copy to Tests/RunCMake/target_sources/FileSetIncompatibleScopes-result.txt
diff --git a/Tests/RunCMake/target_sources/FileSetIncompatibleScopes-stderr.txt b/Tests/RunCMake/target_sources/FileSetIncompatibleScopes-stderr.txt new file mode 100644 index 0000000..9a6d7fd --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetIncompatibleScopes-stderr.txt
@@ -0,0 +1,12 @@ +CMake Error at FileSetIncompatibleScopes\.cmake:[0-9]+ \(target_sources\): + target_sources File set TYPE "CXX_MODULES" may not have "PUBLIC" or + "PRIVATE" scope on INTERFACE libraries\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\) + + +CMake Error at FileSetIncompatibleScopes\.cmake:[0-9]+ \(target_sources\): + target_sources File set TYPE "SOURCES" may not have "PUBLIC" or "PRIVATE" + scope on INTERFACE libraries\. +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/target_sources/FileSetIncompatibleScopes.cmake b/Tests/RunCMake/target_sources/FileSetIncompatibleScopes.cmake new file mode 100644 index 0000000..d183f29 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetIncompatibleScopes.cmake
@@ -0,0 +1,5 @@ +enable_language(C) + +add_library(lib1 INTERFACE) +target_sources(lib1 PUBLIC FILE_SET CXX_MODULES FILES lib1.c) +target_sources(lib1 PUBLIC FILE_SET SOURCES FILES lib2.c)
diff --git a/Tests/RunCMake/target_sources/FileSetProperties.cmake b/Tests/RunCMake/target_sources/FileSetProperties.cmake index 56cce08..8753eb6 100644 --- a/Tests/RunCMake/target_sources/FileSetProperties.cmake +++ b/Tests/RunCMake/target_sources/FileSetProperties.cmake
@@ -56,20 +56,20 @@ assert_prop_eq(lib1 INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/dir>") assert_prop_eq(lib1 INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>") -target_sources(lib1 PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES h1.h) +target_sources(lib1 PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES h4.h) assert_prop_eq(lib1 INTERFACE_HEADER_SETS "a;c;d;HEADERS") assert_prop_eq(lib1 HEADER_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") -assert_prop_eq(lib1 HEADER_SET "${CMAKE_CURRENT_SOURCE_DIR}/h1.h") +assert_prop_eq(lib1 HEADER_SET "${CMAKE_CURRENT_SOURCE_DIR}/h4.h") assert_prop_eq(lib1 HEADER_DIRS_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}") -assert_prop_eq(lib1 HEADER_SET_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/h1.h") +assert_prop_eq(lib1 HEADER_SET_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/h4.h") assert_prop_eq(lib1 INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/dir>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>") assert_prop_eq(lib1 INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>") -target_sources(lib1 PUBLIC FILE_SET HEADERS FILES h2.h) +target_sources(lib1 PUBLIC FILE_SET HEADERS FILES h5.h) assert_prop_eq(lib1 INTERFACE_HEADER_SETS "a;c;d;HEADERS") assert_prop_eq(lib1 HEADER_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") -assert_prop_eq(lib1 HEADER_SET "${CMAKE_CURRENT_SOURCE_DIR}/h1.h;${CMAKE_CURRENT_SOURCE_DIR}/h2.h") +assert_prop_eq(lib1 HEADER_SET "${CMAKE_CURRENT_SOURCE_DIR}/h4.h;${CMAKE_CURRENT_SOURCE_DIR}/h5.h") assert_prop_eq(lib1 HEADER_DIRS_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}") -assert_prop_eq(lib1 HEADER_SET_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/h1.h;${CMAKE_CURRENT_SOURCE_DIR}/h2.h") +assert_prop_eq(lib1 HEADER_SET_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/h4.h;${CMAKE_CURRENT_SOURCE_DIR}/h5.h") assert_prop_eq(lib1 INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/dir>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>") assert_prop_eq(lib1 INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
diff --git a/Tests/RunCMake/target_sources/FileSetSemicolon.cmake b/Tests/RunCMake/target_sources/FileSetSemicolon.cmake new file mode 100644 index 0000000..31430d8 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetSemicolon.cmake
@@ -0,0 +1,45 @@ +enable_language(C) + +add_library(lib1 STATIC empty.c) + +set_property( + SOURCE ${CMAKE_CURRENT_BINARY_DIR}/h\\;1.h + PROPERTY GENERATED TRUE +) + +set_property( + SOURCE ${CMAKE_CURRENT_BINARY_DIR}/h\\;2.h + PROPERTY GENERATED TRUE +) + +set_property( + SOURCE ${CMAKE_CURRENT_BINARY_DIR}/h\\;3.h + PROPERTY GENERATED TRUE +) + +target_sources(lib1 + PRIVATE FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR} + FILES ${CMAKE_CURRENT_BINARY_DIR}/h\\\\\\\\;1.h +) + +set_property(TARGET lib1 APPEND PROPERTY HEADER_SET + ${CMAKE_CURRENT_BINARY_DIR}/h\\\\\\\\;2.h + ${CMAKE_CURRENT_BINARY_DIR}/h\\\\\\\\;3.h +) + +# Ensure we get the expected number of escapes when reading back the file set +# files. +get_property(headers TARGET lib1 PROPERTY HEADER_SET) +if (NOT headers MATCHES "h\\\\\\\\\\\\;1[.]h") + message(STATUS "headers: '${headers}'") + message(SEND_ERROR "'h;1.h' has wrong level of escaping") +endif() +if (NOT headers MATCHES "h\\\\\\\\\\\\;2[.]h") + message(STATUS "headers: '${headers}'") + message(SEND_ERROR "'h;2.h' has wrong level of escaping") +endif() +if (NOT headers MATCHES "h\\\\\\\\\\\\;3[.]h$") + message(STATUS "headers: '${headers}'") + message(SEND_ERROR "'h;3.h' has wrong level of escaping") +endif()
diff --git a/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt index 9cc764f..f87e393 100644 --- a/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt
@@ -1,4 +1,5 @@ ^CMake Error at FileSetWrongType\.cmake:[0-9]+ \(target_sources\): - target_sources File set TYPE may only be "HEADERS" or "CXX_MODULES" + target_sources File set TYPE may only be "HEADERS", "SOURCES", + "CXX_MODULES" Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake index 57e8b89..3255620 100644 --- a/Tests/RunCMake/target_sources/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake
@@ -26,6 +26,7 @@ run_cmake(FileSetNoType) run_cmake(FileSetWrongType) run_cmake(FileSetDefaultWrongType) +run_cmake(FileSetIncompatibleScopes) run_cmake(FileSetChangeScope) run_cmake(FileSetChangeType) run_cmake(FileSetWrongBaseDirs) @@ -43,9 +44,16 @@ run_cmake(FileSetBadName) run_cmake(FileSetWrongSyntax) run_cmake(FileSetDirect) +run_cmake(FileSetDuplicateSource) +run_cmake(FileSetSemicolon) if(APPLE) - run_cmake(FileSetFramework) + run_cmake(FileSetFramework1) + run_cmake(FileSetFramework2) endif() +run_cmake(CMP0211-OLD) +run_cmake(CMP0211-NEW) +run_cmake(CMP0211-WARN) +run_cmake(CMP0211-NEW-HEADERS) set(RunCMake_TEST_NO_CLEAN 1) set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/FileSetGeneratedDependency-build")
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/target_sources/h4.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/target_sources/h4.h
diff --git a/Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake b/Tests/RunCMake/target_sources/h5.h similarity index 100% copy from Tests/RunCMake/CMakePresets/ArchToolsetStrategyIgnore.cmake copy to Tests/RunCMake/target_sources/h5.h
diff --git a/Tests/RunCMake/test_include_dirs/TID-test-stdout.txt b/Tests/RunCMake/test_include_dirs/TID-test-stdout.txt index 4810d8f..48d5489 100644 --- a/Tests/RunCMake/test_include_dirs/TID-test-stdout.txt +++ b/Tests/RunCMake/test_include_dirs/TID-test-stdout.txt
@@ -12,6 +12,6 @@ Start 6: zoo\.emu 6/6 Test #6: zoo\.emu .+ +Passed +[0-9\.]+ sec -100% tests passed, 0 tests failed out of 6 +100% tests passed out of 6 Total Test time \(real\) = +[0-9\.]+ sec
diff --git a/Tests/RunCMake/try_compile/CMP0066-stderr.txt b/Tests/RunCMake/try_compile/CMP0066-stderr.txt index fd29a7b..ec9d014 100644 --- a/Tests/RunCMake/try_compile/CMP0066-stderr.txt +++ b/Tests/RunCMake/try_compile/CMP0066-stderr.txt
@@ -1,15 +1,17 @@ -^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMakeLists\.txt:[0-9]+ \(cmake_policy\): Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument <min> value\. Or, use the <min>\.\.\.<max> syntax to tell CMake that the project requires at least <min> but has been updated to work with policies introduced by <max> or earlier\. +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + before try_compile with CMP0066 WARN-default after try_compile with CMP0066 WARN-default * -CMake Warning \(dev\) at CMP0066\.cmake:[0-9]+ \(try_compile\): +CMake Warning \(policy\) at CMP0066\.cmake:[0-9]+ \(try_compile\): Policy CMP0066 is not set: Honor per-config flags in try_compile\(\) source-file signature\. Run "cmake --help-policy CMP0066" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -20,9 +22,10 @@ test project\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. * -CMake Deprecation Warning at CMP0066\.cmake:[0-9]+ \(cmake_policy\): +CMake Warning \(deprecated\) at CMP0066\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0066 will be removed from a future version of CMake\. @@ -31,4 +34,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/try_compile/CMP0067-stderr.txt b/Tests/RunCMake/try_compile/CMP0067-stderr.txt index 070498c..08104fa 100644 --- a/Tests/RunCMake/try_compile/CMP0067-stderr.txt +++ b/Tests/RunCMake/try_compile/CMP0067-stderr.txt
@@ -3,7 +3,7 @@ before try_compile with CMP0067 WARN-default after try_compile with CMP0067 WARN-default before try_compile with CMP0067 WARN-enabled -CMake Warning \(dev\) at CMP0067\.cmake:[0-9]+ \(try_compile\): +CMake Warning \(policy\) at CMP0067\.cmake:[0-9]+ \(try_compile\): Policy CMP0067 is not set: Honor language standard in try_compile\(\) source-file signature\. Run "cmake --help-policy CMP0067" for policy details\. Use the cmake_policy command to set the policy and suppress this @@ -16,10 +16,11 @@ Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\. - +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\. ++ after try_compile with CMP0067 WARN-enabled -CMake Deprecation Warning at CMP0067\.cmake:[0-9]+ \(cmake_policy\): +CMake Warning \(deprecated\) at CMP0067\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0067 will be removed from a future version of CMake\. @@ -29,6 +30,8 @@ behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\. + before try_compile with CMP0067 OLD after try_compile with CMP0067 OLD
diff --git a/Tests/RunCMake/try_compile/ConfigureLog-config.txt b/Tests/RunCMake/try_compile/ConfigureLog-config.txt index bd956df..a3296d7 100644 --- a/Tests/RunCMake/try_compile/ConfigureLog-config.txt +++ b/Tests/RunCMake/try_compile/ConfigureLog-config.txt
@@ -29,7 +29,7 @@ ]*)+ buildResult: variable: "CMAKE_C_ABI_COMPILED" - cached: true + cached: false stdout: \|.* exitCode: 0( + - @@ -63,7 +63,7 @@ ]*)+ buildResult: variable: "CMAKE_C_FEATURE_TEST" - cached: true + cached: false stdout: \|.* exitCode: 0)* -
diff --git a/Tests/RunCMake/try_compile/CudaArchitectures-stderr.txt b/Tests/RunCMake/try_compile/CudaArchitectures-stderr.txt new file mode 100644 index 0000000..76948bd --- /dev/null +++ b/Tests/RunCMake/try_compile/CudaArchitectures-stderr.txt
@@ -0,0 +1,11 @@ +^CMake Debug Log at [^ +]*/Modules/CMakeDetermineCompilerABI\.cmake:[0-9]+ \(try_compile\): + Executing try_compile \(CMAKE_C_ABI_COMPILED\) in: + + [^ +]*/Tests/RunCMake/try_compile/CudaArchitectures-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+ +Call Stack \(most recent call first\): + [^ +]*/Modules/CMakeTestCCompiler\.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\) + CudaArchitectures\.cmake:[0-9]+ \(enable_language\) + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/try_compile/CudaArchitectures.cmake b/Tests/RunCMake/try_compile/CudaArchitectures.cmake new file mode 100644 index 0000000..527c8f8 --- /dev/null +++ b/Tests/RunCMake/try_compile/CudaArchitectures.cmake
@@ -0,0 +1,19 @@ +enable_language(C) + +set(CMAKE_CUDA_ARCHITECTURES SOME_VALUE) + +try_compile(result ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c + OUTPUT_VARIABLE out + ) +if(NOT result) + message(FATAL_ERROR "try_compile failed:\n${out}") +endif() + +# Check that the cache was populated with our custom variable. +file(STRINGS ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CMakeCache.txt entries + REGEX CMAKE_CUDA_ARCHITECTURES:UNINITIALIZED=${CMAKE_CUDA_ARCHITECTURES} + ) +if(NOT entries) + message(FATAL_ERROR "try_compile did not populate cache as expected") +endif()
diff --git a/Tests/RunCMake/try_compile/Inspect-config.txt b/Tests/RunCMake/try_compile/Inspect-config.txt index 9c9f27f..2c1d67e 100644 --- a/Tests/RunCMake/try_compile/Inspect-config.txt +++ b/Tests/RunCMake/try_compile/Inspect-config.txt
@@ -29,7 +29,7 @@ ]*)+ buildResult: variable: "CMAKE_C_ABI_COMPILED" - cached: true + cached: false stdout: \|.* exitCode: 0( + - @@ -60,7 +60,7 @@ ]*)+ buildResult: variable: "CMAKE_CXX_ABI_COMPILED" - cached: true + cached: false stdout: \|.* exitCode: 0( + -
diff --git a/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt b/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt index b6b5d23..44ad6c1 100644 --- a/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt +++ b/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt
@@ -1,4 +1,4 @@ -CMake Warning \(dev\) at ProjectCopyFile\.cmake:[0-9]+ \(try_compile\): +CMake Warning \(author\) at ProjectCopyFile\.cmake:[0-9]+ \(try_compile\): Unknown arguments: "COPY_FILE"
diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake index 3c684d2..07bf099 100644 --- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
@@ -52,7 +52,7 @@ set(RunCMake_TEST_OPTIONS --debug-trycompile) run_cmake(PlatformVariables) -run_cmake(WarnDeprecated) +run_cmake(CudaArchitectures) unset(RunCMake_TEST_OPTIONS) if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
diff --git a/Tests/RunCMake/try_compile/TopIncludes-config.txt b/Tests/RunCMake/try_compile/TopIncludes-config.txt index 498e044..1550777 100644 --- a/Tests/RunCMake/try_compile/TopIncludes-config.txt +++ b/Tests/RunCMake/try_compile/TopIncludes-config.txt
@@ -29,7 +29,7 @@ ]*)+ buildResult: variable: "CMAKE_C_ABI_COMPILED" - cached: true + cached: false stdout: \|.* exitCode: 0( + -
diff --git a/Tests/RunCMake/try_compile/TryRunArgs-stderr.txt b/Tests/RunCMake/try_compile/TryRunArgs-stderr.txt index 4faa271..ea9afb8 100644 --- a/Tests/RunCMake/try_compile/TryRunArgs-stderr.txt +++ b/Tests/RunCMake/try_compile/TryRunArgs-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at TryRunArgs\.cmake:[0-9]+ \(try_compile\): +^CMake Warning \(author\) at TryRunArgs\.cmake:[0-9]+ \(try_compile\): Unknown arguments: "COMPILE_OUTPUT_VARIABLE" @@ -15,4 +15,4 @@ "runArgs" Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/try_compile/WarnDeprecated-stderr.txt b/Tests/RunCMake/try_compile/WarnDeprecated-stderr.txt deleted file mode 100644 index 2c6b62f..0000000 --- a/Tests/RunCMake/try_compile/WarnDeprecated-stderr.txt +++ /dev/null
@@ -1,11 +0,0 @@ -^CMake Debug Log at [^ -]*/Modules/CMakeDetermineCompilerABI\.cmake:[0-9]+ \(try_compile\): - Executing try_compile \(CMAKE_C_ABI_COMPILED\) in: - - [^ -]*/Tests/RunCMake/try_compile/WarnDeprecated-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+ -Call Stack \(most recent call first\): - [^ -]*/Modules/CMakeTestCCompiler\.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\) - WarnDeprecated\.cmake:[0-9]+ \(enable_language\) - CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/try_compile/WarnDeprecated.cmake b/Tests/RunCMake/try_compile/WarnDeprecated.cmake deleted file mode 100644 index dfcb5f9..0000000 --- a/Tests/RunCMake/try_compile/WarnDeprecated.cmake +++ /dev/null
@@ -1,19 +0,0 @@ -enable_language(C) - -set(CMAKE_WARN_DEPRECATED SOME_VALUE) - -try_compile(result ${CMAKE_CURRENT_BINARY_DIR} - SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c - OUTPUT_VARIABLE out - ) -if(NOT result) - message(FATAL_ERROR "try_compile failed:\n${out}") -endif() - -# Check that the cache was populated with our custom variable. -file(STRINGS ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CMakeCache.txt entries - REGEX CMAKE_WARN_DEPRECATED:UNINITIALIZED=${CMAKE_WARN_DEPRECATED} - ) -if(NOT entries) - message(FATAL_ERROR "try_compile did not populate cache as expected") -endif()
diff --git a/Tests/RunCMake/try_run/ConfigureLog-config.txt b/Tests/RunCMake/try_run/ConfigureLog-config.txt index eaa6b7b..c55addb 100644 --- a/Tests/RunCMake/try_run/ConfigureLog-config.txt +++ b/Tests/RunCMake/try_run/ConfigureLog-config.txt
@@ -28,7 +28,7 @@ ]*)+ buildResult: variable: "CMAKE_C_ABI_COMPILED" - cached: true + cached: false stdout: \|.* exitCode: 0( + - @@ -61,7 +61,7 @@ ]*)+ buildResult: variable: "CMAKE_C_FEATURE_TEST" - cached: true + cached: false stdout: \|.* exitCode: 0)* -
diff --git a/Tests/RunCMake/while/CMP0130-OLD-stderr.txt b/Tests/RunCMake/while/CMP0130-OLD-stderr.txt index e673966..ff4f827 100644 --- a/Tests/RunCMake/while/CMP0130-OLD-stderr.txt +++ b/Tests/RunCMake/while/CMP0130-OLD-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Deprecation Warning at CMP0130-OLD\.cmake:[0-9]+ \(cmake_policy\): +^CMake Warning \(deprecated\) at CMP0130-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0130 will be removed from a future version of CMake\. @@ -7,4 +7,6 @@ specific short-term circumstances\. Projects should be ported to the NEW behavior and not rely on setting a policy to OLD\. Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-author or -Wno-deprecated +to suppress it\.$
diff --git a/Tests/RunCMake/while/CMP0130-WARN-stderr.txt b/Tests/RunCMake/while/CMP0130-WARN-stderr.txt index fd9ba22..956d68d 100644 --- a/Tests/RunCMake/while/CMP0130-WARN-stderr.txt +++ b/Tests/RunCMake/while/CMP0130-WARN-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) at CMP0130-common\.cmake:[0-9]+ \(while\): +^CMake Warning \(policy\) at CMP0130-common\.cmake:[0-9]+ \(while\): Policy CMP0130 is not set: while\(\) diagnoses condition evaluation errors\. Run "cmake --help-policy CMP0130" for policy details\. Use the cmake_policy command to set the policy and suppress this warning\. @@ -11,4 +11,5 @@ Call Stack \(most recent call first\): CMP0130-WARN\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author or -Wno-policy to +suppress it\.$
diff --git a/Tests/RunCMake/while/EndMismatch-stderr.txt b/Tests/RunCMake/while/EndMismatch-stderr.txt index f6c4dce..33d2c02 100644 --- a/Tests/RunCMake/while/EndMismatch-stderr.txt +++ b/Tests/RunCMake/while/EndMismatch-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Warning \(dev\) in EndMismatch\.cmake: +^CMake Warning \(author\) in EndMismatch\.cmake: A logical block opening on the line .*/Tests/RunCMake/while/EndMismatch\.cmake:1 \(while\) @@ -10,4 +10,4 @@ with mis-matching arguments\. Call Stack \(most recent call first\): CMakeLists\.txt:3 \(include\) -This warning is for project developers\. Use -Wno-dev to suppress it\.$ +This warning is for project developers\. Use -Wno-author to suppress it\.$
diff --git a/Tests/RunCMake/CommandLine/Werror_dev-result.txt b/Tests/RunCMake/while/InvalidArgument-result.txt similarity index 100% copy from Tests/RunCMake/CommandLine/Werror_dev-result.txt copy to Tests/RunCMake/while/InvalidArgument-result.txt
diff --git a/Tests/RunCMake/while/InvalidArgument-stderr.txt b/Tests/RunCMake/while/InvalidArgument-stderr.txt new file mode 100644 index 0000000..5ab8ad8 --- /dev/null +++ b/Tests/RunCMake/while/InvalidArgument-stderr.txt
@@ -0,0 +1,8 @@ +^CMake Error at InvalidArgument\.cmake:2 \(while\): + while\(\) given incorrect arguments: + + "\(" "A" "B" "\)" "OR" "\(" "" "\)" + + Unknown arguments specified +Call Stack \(most recent call first\): + CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/while/InvalidArgument.cmake b/Tests/RunCMake/while/InvalidArgument.cmake new file mode 100644 index 0000000..13323ca --- /dev/null +++ b/Tests/RunCMake/while/InvalidArgument.cmake
@@ -0,0 +1,6 @@ +cmake_policy(SET CMP0130 NEW) +while((A B) OR ("$ENV{NOT_AN_ENV_VARIABLE}")) + message(STATUS "Invalid evaluated to TRUE") + return() +endwhile() +message(STATUS "Invalid evaluated to FALSE")
diff --git a/Tests/RunCMake/while/RunCMakeTest.cmake b/Tests/RunCMake/while/RunCMakeTest.cmake index d018b16..315d489 100644 --- a/Tests/RunCMake/while/RunCMakeTest.cmake +++ b/Tests/RunCMake/while/RunCMakeTest.cmake
@@ -1,6 +1,7 @@ include(RunCMake) run_cmake(MissingArgument) +run_cmake(InvalidArgument) run_cmake(EndMissing) run_cmake(EndMismatch) run_cmake(EndAlone)
diff --git a/Tests/RustMix/CMakeLists.txt b/Tests/RustMix/CMakeLists.txt index 8551afd..029f534 100644 --- a/Tests/RustMix/CMakeLists.txt +++ b/Tests/RustMix/CMakeLists.txt
@@ -1,14 +1,30 @@ cmake_minimum_required(VERSION 4.2) -set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c") +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") -project(RustMix LANGUAGES C Rust) +project(RustMix LANGUAGES C CXX Rust) -add_library(liba STATIC liba.rs) -add_library(libb SHARED libb.rs) -add_library(libc OBJECT libc.rs) +add_library(rs_staticlib STATIC rs_staticlib.rs) +add_library(rs_cdylib SHARED rs_cdylib.rs) +add_library(rs_rlib OBJECT rs_rlib.rs) -add_executable(RustMix main.c) -target_link_libraries(RustMix liba) -target_link_libraries(RustMix libb) -target_link_libraries(RustMix libc) +add_library(c_static STATIC c_static.c) +add_library(c_shared SHARED c_shared.c) +add_library(c_obj OBJECT c_obj.c) + +add_library(cpp_shared SHARED cpp_shared.cpp) +# Note: trying to link a C++ object file or static library into the Rust +# executable currently fails with errors related to missing symbols from +# libstdc++, while libstdc++ is present on the linker command line. + +add_executable(RustMix main.rs) +target_link_libraries( + RustMix + rs_staticlib + rs_cdylib + rs_rlib + c_static + c_shared + c_obj + cpp_shared +)
diff --git a/Tests/RustMix/c_obj.c b/Tests/RustMix/c_obj.c new file mode 100644 index 0000000..e269d41 --- /dev/null +++ b/Tests/RustMix/c_obj.c
@@ -0,0 +1,6 @@ +#include <stdio.h> + +void c_obj_greet() +{ + printf("Hello from a C object file!"); +}
diff --git a/Tests/RustMix/c_shared.c b/Tests/RustMix/c_shared.c new file mode 100644 index 0000000..42255a3 --- /dev/null +++ b/Tests/RustMix/c_shared.c
@@ -0,0 +1,6 @@ +#include <stdio.h> + +void c_shared_greet() +{ + printf("Hello from a C shared library!"); +}
diff --git a/Tests/RustMix/c_static.c b/Tests/RustMix/c_static.c new file mode 100644 index 0000000..6af1de2 --- /dev/null +++ b/Tests/RustMix/c_static.c
@@ -0,0 +1,6 @@ +#include <stdio.h> + +void c_static_greet() +{ + printf("Hello from a C static library!"); +}
diff --git a/Tests/RustMix/cpp_shared.cpp b/Tests/RustMix/cpp_shared.cpp new file mode 100644 index 0000000..bee6784 --- /dev/null +++ b/Tests/RustMix/cpp_shared.cpp
@@ -0,0 +1,6 @@ +#include <iostream> + +extern "C" void cpp_shared_greet() +{ + std::cout << "Hello from a C++ shader library" << std::endl; +}
diff --git a/Tests/RustMix/liba.rs b/Tests/RustMix/liba.rs deleted file mode 100644 index 2958db3..0000000 --- a/Tests/RustMix/liba.rs +++ /dev/null
@@ -1,4 +0,0 @@ -#[no_mangle] -pub extern "C" fn liba_greet() { - println!("Hello from Rust liba"); -}
diff --git a/Tests/RustMix/libb.rs b/Tests/RustMix/libb.rs deleted file mode 100644 index e94f46f..0000000 --- a/Tests/RustMix/libb.rs +++ /dev/null
@@ -1,4 +0,0 @@ -#[no_mangle] -pub extern "C" fn libb_greet() { - println!("Hello from Rust libb"); -}
diff --git a/Tests/RustMix/libc.rs b/Tests/RustMix/libc.rs deleted file mode 100644 index 52bd67d..0000000 --- a/Tests/RustMix/libc.rs +++ /dev/null
@@ -1,4 +0,0 @@ -#[no_mangle] -pub extern "C" fn libc_greet() { - println!("Hello from Rust libc"); -}
diff --git a/Tests/RustMix/main.c b/Tests/RustMix/main.c deleted file mode 100644 index 39ceba1..0000000 --- a/Tests/RustMix/main.c +++ /dev/null
@@ -1,15 +0,0 @@ -#include <stdio.h> - -extern void liba_greet(); -extern void libb_greet(); -extern void libc_greet(); - -int main() -{ - printf("Hello from C main\n"); - liba_greet(); - libb_greet(); - libc_greet(); - - return 0; -}
diff --git a/Tests/RustMix/main.rs b/Tests/RustMix/main.rs new file mode 100644 index 0000000..fc153b0 --- /dev/null +++ b/Tests/RustMix/main.rs
@@ -0,0 +1,30 @@ +extern "C" { + // Functions defined in C + fn c_obj_greet(); + fn c_static_greet(); + fn c_shared_greet(); + + // Functions defined in C++ + fn cpp_shared_greet(); + + // Functions defined in Rust through a C-style ABI + fn rs_staticlib_greet(); + fn rs_cdylib_greet(); +} + +// Functions defined in Rust, using native Rust ABI +extern crate rs_rlib; +use rs_rlib::rs_rlib_greet; + +fn main() { + println!("Hello from main.rs"); + unsafe { + c_obj_greet(); + c_static_greet(); + c_shared_greet(); + cpp_shared_greet(); + rs_staticlib_greet(); + rs_cdylib_greet(); + } + rs_rlib_greet(); +}
diff --git a/Tests/RustMix/rs_cdylib.rs b/Tests/RustMix/rs_cdylib.rs new file mode 100644 index 0000000..ff8fd29 --- /dev/null +++ b/Tests/RustMix/rs_cdylib.rs
@@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn rs_cdylib_greet() { + println!("Hello from a Rust cdylib!"); +}
diff --git a/Tests/RustMix/rs_rlib.rs b/Tests/RustMix/rs_rlib.rs new file mode 100644 index 0000000..6456453 --- /dev/null +++ b/Tests/RustMix/rs_rlib.rs
@@ -0,0 +1,3 @@ +pub fn rs_rlib_greet() { + println!("Hello from a Rust rlib!"); +}
diff --git a/Tests/RustMix/rs_staticlib.rs b/Tests/RustMix/rs_staticlib.rs new file mode 100644 index 0000000..c25b65f --- /dev/null +++ b/Tests/RustMix/rs_staticlib.rs
@@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn rs_staticlib_greet() { + println!("Hello from a Rust staticlib!"); +}
diff --git a/Tests/RustOnly/CMakeLists.txt b/Tests/RustOnly/CMakeLists.txt index 8f889fb..72b6efa 100644 --- a/Tests/RustOnly/CMakeLists.txt +++ b/Tests/RustOnly/CMakeLists.txt
@@ -1,14 +1,18 @@ cmake_minimum_required(VERSION 4.2) -set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c") +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") project(RustOnly LANGUAGES Rust) -add_library(liba STATIC liba.rs) -add_library(libb SHARED libb.rs) -add_library(libc OBJECT libc.rs) +add_library(a STATIC a.rs) +add_library(b SHARED b.rs) +add_library(c OBJECT c.rs) -add_executable(RustOnly main.rs) -target_link_libraries(RustOnly liba) -target_link_libraries(RustOnly libb) -target_link_libraries(RustOnly libc) +add_executable(RustOnly d.rs main.rs e.rs) +# Ensure that Rust_MAIN_CRATE_ROOT property works. +set_target_properties(RustOnly PROPERTIES Rust_MAIN_CRATE_ROOT main.rs) +# Emit an object file instead of an rlib. +set_source_files_properties(e.rs PROPERTIES Rust_EMIT "obj") +target_link_libraries(RustOnly a) +target_link_libraries(RustOnly b) +target_link_libraries(RustOnly c)
diff --git a/Tests/RustOnly/liba.rs b/Tests/RustOnly/a.rs similarity index 100% rename from Tests/RustOnly/liba.rs rename to Tests/RustOnly/a.rs
diff --git a/Tests/RustOnly/libb.rs b/Tests/RustOnly/b.rs similarity index 100% rename from Tests/RustOnly/libb.rs rename to Tests/RustOnly/b.rs
diff --git a/Tests/RustOnly/c.rs b/Tests/RustOnly/c.rs new file mode 100644 index 0000000..6de15d7 --- /dev/null +++ b/Tests/RustOnly/c.rs
@@ -0,0 +1,4 @@ +// We can expose a Rust API, as this is compiled into a rlib. +pub fn libc_greet() { + println!("Hello from libc"); +}
diff --git a/Tests/RustOnly/d.rs b/Tests/RustOnly/d.rs new file mode 100644 index 0000000..52de74c --- /dev/null +++ b/Tests/RustOnly/d.rs
@@ -0,0 +1,4 @@ +// We can expose a Rust API, as this is compiled into a rlib. +pub fn libd_greet() { + println!("Hello from libd"); +}
diff --git a/Tests/RustOnly/e.rs b/Tests/RustOnly/e.rs new file mode 100644 index 0000000..4b3bd74 --- /dev/null +++ b/Tests/RustOnly/e.rs
@@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn libe_greet() { + println!("Hello from libe"); +}
diff --git a/Tests/RustOnly/libc.rs b/Tests/RustOnly/libc.rs deleted file mode 100644 index dd46cc5..0000000 --- a/Tests/RustOnly/libc.rs +++ /dev/null
@@ -1,4 +0,0 @@ -#[no_mangle] -pub extern "C" fn libc_greet() { - println!("Hello from libc"); -}
diff --git a/Tests/RustOnly/main.rs b/Tests/RustOnly/main.rs index 067fb24..af91fc3 100644 --- a/Tests/RustOnly/main.rs +++ b/Tests/RustOnly/main.rs
@@ -3,13 +3,20 @@ extern "C" { fn liba_greet(); fn libb_greet(); - fn libc_greet(); + fn libe_greet(); } +extern crate c; +extern crate d; +use c::libc_greet; +use d::libd_greet; + fn main() { println!("Hello from main.rs"); moda::moda_greet(); unsafe { liba_greet() }; unsafe { libb_greet() }; - unsafe { libc_greet() }; + libc_greet(); + libd_greet(); + unsafe { libe_greet() }; }
diff --git a/Tests/RustPie/CMakeLists.txt b/Tests/RustPie/CMakeLists.txt new file mode 100644 index 0000000..9adc23c --- /dev/null +++ b/Tests/RustPie/CMakeLists.txt
@@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 4.2) + +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") + +function(setup PREFIX) + add_library(${PREFIX}_static STATIC ../static.rs) + add_library(${PREFIX}_shared SHARED ../shared.rs) + add_library(${PREFIX}_object OBJECT ../object.rs) + + add_executable(${PREFIX}_RustPie ../main.rs) + target_link_libraries( + ${PREFIX}_RustPie PRIVATE + ${PREFIX}_static + ${PREFIX}_shared + ${PREFIX}_object + ) +endfunction() + +project(RustPie LANGUAGES Rust) + +add_subdirectory(Default) +add_subdirectory(Disabled) +add_subdirectory(Enabled) + +file( + GENERATE OUTPUT ${CMAKE_BINARY_DIR}/runner_$<CONFIG>.rs + INPUT runner.rs +) + +add_executable(RustPie ${CMAKE_BINARY_DIR}/runner_$<CONFIG>.rs) +add_dependencies(RustPie default_RustPie disabled_RustPie enabled_RustPie)
diff --git a/Tests/RustPie/Default/CMakeLists.txt b/Tests/RustPie/Default/CMakeLists.txt new file mode 100644 index 0000000..806bd62 --- /dev/null +++ b/Tests/RustPie/Default/CMakeLists.txt
@@ -0,0 +1,3 @@ + +project(DefaultRustPie LANGUAGES Rust) +setup(default)
diff --git a/Tests/RustPie/Disabled/CMakeLists.txt b/Tests/RustPie/Disabled/CMakeLists.txt new file mode 100644 index 0000000..b5161be --- /dev/null +++ b/Tests/RustPie/Disabled/CMakeLists.txt
@@ -0,0 +1,12 @@ + +project(DisabledRustPie LANGUAGES Rust) +setup(disabled) + +set_property( + TARGET + disabled_static + disabled_shared + disabled_object + disabled_RustPie + PROPERTY POSITION_INDEPENDENT_CODE OFF +)
diff --git a/Tests/RustPie/Enabled/CMakeLists.txt b/Tests/RustPie/Enabled/CMakeLists.txt new file mode 100644 index 0000000..c48a4e3 --- /dev/null +++ b/Tests/RustPie/Enabled/CMakeLists.txt
@@ -0,0 +1,12 @@ + +project(EnabledRustPie LANGUAGES Rust) +setup(enabled) + +set_property( + TARGET + enabled_static + enabled_shared + enabled_object + enabled_RustPie + PROPERTY POSITION_INDEPENDENT_CODE TRUE +)
diff --git a/Tests/RustPie/main.rs b/Tests/RustPie/main.rs new file mode 100644 index 0000000..fb92a96 --- /dev/null +++ b/Tests/RustPie/main.rs
@@ -0,0 +1,18 @@ + +extern "C" { + fn static_foo(); + fn shared_foo(); +} + + +extern crate object; +use object::object_foo; + + +fn main() { + unsafe { + static_foo(); + shared_foo(); + } + object_foo(); +}
diff --git a/Tests/RustPie/object.rs b/Tests/RustPie/object.rs new file mode 100644 index 0000000..b189f46 --- /dev/null +++ b/Tests/RustPie/object.rs
@@ -0,0 +1,3 @@ +pub fn object_foo() { + println!("object_foo"); +}
diff --git a/Tests/RustPie/runner.rs b/Tests/RustPie/runner.rs new file mode 100644 index 0000000..bc0a798 --- /dev/null +++ b/Tests/RustPie/runner.rs
@@ -0,0 +1,66 @@ + +use std::process::Command; + +#[cfg(target_os="macos")] +fn is_pie_executable(executable: &str) -> bool { + let output = Command::new("otool") + .args(["-hv", executable]) + .output() + .expect("Failed to check executable"); + if !output.status.success() { + panic!("otool exited with non-zero exit code"); + } + + output.stdout.windows(5).any(|w| { + (w[0] == b' ' || w[0] == b'\t') + && (w[1..=3] == b"PIE") + && (w[4] == b' ' || w[4] == b'\n') + }) +} + +#[cfg(all(target_family="unix", not(target_os="macos")))] +fn is_pie_executable(executable: &str) -> bool { + let output = Command::new("readelf") + .args(["-lW", executable]) + .env("LANG", "C") + .env("LC_ALL", "C") + .output() + .expect("Failed to check executable"); + if !output.status.success() { + panic!("readelf exited with non-zero exit code"); + } + let is_pie = output.stdout.windows(20).any(|w| w == b"Elf file type is DYN"); + let is_not_pie = output.stdout.windows(21).any(|w| w == b"Elf file type is EXEC"); + assert!(is_pie ^ is_not_pie, "Cannot determine type of ELF file"); + is_pie +} + +fn main() { + let commands = [ + // The actual path to the commands will be filled in by CMake. + r#"$<TARGET_FILE:default_RustPie>"#, + r#"$<TARGET_FILE:disabled_RustPie>"#, + r#"$<TARGET_FILE:enabled_RustPie>"#, + ]; + for command in commands { + println!("Start command: {command:?}"); + let result = Command::new(command) + .spawn() + .expect("Failed to launch command") + .wait() + .expect("Failure while waiting for process to finish") + .success(); + if !result { + panic!("Process exited with non-zero exit code."); + } + } + + assert!( + !is_pie_executable(commands[1]), + "ERROR: disabled_RustPie must not be a PIE executable, but it is not." + ); + assert!( + is_pie_executable(commands[2]), + "ERROR: enabled_RustPie must be a PIE executable, but it is not." + ); +}
diff --git a/Tests/RustPie/shared.rs b/Tests/RustPie/shared.rs new file mode 100644 index 0000000..40f89e6 --- /dev/null +++ b/Tests/RustPie/shared.rs
@@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn shared_foo() { + println!("shared_foo"); +}
diff --git a/Tests/RustPie/static.rs b/Tests/RustPie/static.rs new file mode 100644 index 0000000..99b3116 --- /dev/null +++ b/Tests/RustPie/static.rs
@@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn static_foo() { + println!("static_foo"); +}
diff --git a/Tests/StringFileTest/CMakeLists.txt b/Tests/StringFileTest/CMakeLists.txt index f72dafd..3c9570d 100644 --- a/Tests/StringFileTest/CMakeLists.txt +++ b/Tests/StringFileTest/CMakeLists.txt
@@ -55,6 +55,10 @@ "file(STRINGS) incorrectly read from srec file [${infile_strings}]") endif() +# this file is crafted to contain the maximum line length and trigger a buffer +# overflow +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/s1-max-length.srec" infile_strings) + #this file has utf-8 content file(STRINGS test.utf8 infile_strings ENCODING UTF-8) list(LENGTH infile_strings content_len)
diff --git a/Tests/StringFileTest/s1-max-length.srec b/Tests/StringFileTest/s1-max-length.srec new file mode 100644 index 0000000..e7dcee3 --- /dev/null +++ b/Tests/StringFileTest/s1-max-length.srec
@@ -0,0 +1,2 @@ +S107000001020304EE +S1FF000041414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414100
diff --git a/Tests/TestDriver/CMakeLists.txt b/Tests/TestDriver/CMakeLists.txt index 070a034..2c2e1ea 100644 --- a/Tests/TestDriver/CMakeLists.txt +++ b/Tests/TestDriver/CMakeLists.txt
@@ -9,6 +9,6 @@ test1.cxx test2.cxx subdir/test3.cxx - EXTRA_INCLUDE testArgs.h FUNCTION testProccessArgs) + EXTRA_INCLUDE testArgs.h FUNCTION testProcessArgs) add_executable(TestDriverTest ${testSrcs} ${Extra_SRCS})
diff --git a/Tests/TestDriver/testArgs.h b/Tests/TestDriver/testArgs.h index 0f1b879..051124e 100644 --- a/Tests/TestDriver/testArgs.h +++ b/Tests/TestDriver/testArgs.h
@@ -1,4 +1,4 @@ -void testProccessArgs(int* ac, char*** av) +void testProcessArgs(int* ac, char*** av) { char** argv = *av; if (*ac < 2) {
diff --git a/Tests/UseSWIG/LegacyConfiguration.cmake b/Tests/UseSWIG/LegacyConfiguration.cmake index 1acc05a..23e1a64 100644 --- a/Tests/UseSWIG/LegacyConfiguration.cmake +++ b/Tests/UseSWIG/LegacyConfiguration.cmake
@@ -1,6 +1,6 @@ # Prevent deprecated warnings from new UseSWIG module -set (CMAKE_WARN_DEPRECATED FALSE) +cmake_diagnostic(SET CMD_DEPRECATED IGNORE) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE})
diff --git a/Tests/VSResource/CMakeLists.txt b/Tests/VSResource/CMakeLists.txt index be6154c..62b4888 100644 --- a/Tests/VSResource/CMakeLists.txt +++ b/Tests/VSResource/CMakeLists.txt
@@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(VSResource) +project(VSResource LANGUAGES C) string(REPLACE "/INCREMENTAL:YES" "" CMAKE_EXE_LINKER_FLAGS_DEBUG @@ -13,9 +13,8 @@ # add_definitions is fine for simple definitions (with no spaces and no # quoting), but requires avoidance or work-arounds beyond that... -if(CMAKE_RC_COMPILER MATCHES windres) +if(CMAKE_RC_COMPILER MATCHES "(windres|porc)") # windres rc compiler does not properly define quoted /D values as strings - message(STATUS "CMAKE_RC_COMPILER MATCHES windres") add_definitions(/DCMAKE_RCDEFINE=test.txt) add_definitions(/DCMAKE_RCDEFINE_NO_QUOTED_STRINGS) if(CMAKE_CURRENT_BINARY_DIR MATCHES " ") @@ -45,10 +44,13 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) endif() -add_library(ResourceLib STATIC lib.cpp lib.rc) +add_executable(VSResource main.c test.rc) -add_executable(VSResource main.cpp test.rc) -target_link_libraries(VSResource ResourceLib) +if(NOT CMAKE_AR MATCHES "polib") + add_library(ResourceLib STATIC lib.c lib.rc) + target_link_libraries(VSResource PRIVATE ResourceLib) + target_compile_definitions(VSResource PRIVATE HAVE_RESOURCE_LIB) +endif() if(MSVC AND NOT MSVC_VERSION VERSION_LESS 1600) set_property(SOURCE test.rc PROPERTY COMPILE_FLAGS /nologo)
diff --git a/Tests/VSResource/lib.c b/Tests/VSResource/lib.c new file mode 100644 index 0000000..abe3b78 --- /dev/null +++ b/Tests/VSResource/lib.c
@@ -0,0 +1,4 @@ +int lib(void) +{ + return 0; +}
diff --git a/Tests/VSResource/lib.cpp b/Tests/VSResource/lib.cpp deleted file mode 100644 index c912397..0000000 --- a/Tests/VSResource/lib.cpp +++ /dev/null
@@ -1,4 +0,0 @@ -int lib() -{ - return 0; -}
diff --git a/Tests/VSResource/main.c b/Tests/VSResource/main.c new file mode 100644 index 0000000..5a58644 --- /dev/null +++ b/Tests/VSResource/main.c
@@ -0,0 +1,86 @@ +#include <windows.h> + +#include <stdio.h> + +#ifdef HAVE_RESOURCE_LIB +extern int lib(void); +#endif + +struct x +{ + char const* txt; +}; + +int main(int argc, char** argv) +{ + int ret = 1; + HRSRC hello = FindResource(NULL, MAKEINTRESOURCE(1025), "TEXTFILE"); + + fprintf(stdout, "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)\n"); + +#ifdef CMAKE_RCDEFINE + fprintf(stdout, "CMAKE_RCDEFINE defined\n"); +#endif + +#ifdef CMAKE_RCDEFINE_NO_QUOTED_STRINGS + { + // Expect CMAKE_RCDEFINE to preprocess to exactly test.txt + struct x test; + test.txt = "*exactly* test.txt"; + fprintf(stdout, "CMAKE_RCDEFINE_NO_QUOTED_STRINGS defined\n"); + fprintf(stdout, "CMAKE_RCDEFINE is %s, and is *not* a string constant\n", + CMAKE_RCDEFINE); + } +#else + // Expect CMAKE_RCDEFINE to be a string: + fprintf(stdout, "CMAKE_RCDEFINE='%s', and is a string constant\n", + CMAKE_RCDEFINE); +#endif + + if (hello) { + HGLOBAL hgbl = LoadResource(NULL, hello); + int datasize = (int)SizeofResource(NULL, hello); + fprintf(stdout, "FindResource worked\n"); + if (hgbl && datasize > 0) { + void* data = LockResource(hgbl); + fprintf(stdout, "LoadResource worked\n"); + fprintf(stdout, "SizeofResource returned datasize='%d'\n", datasize); + if (data) { + char* str = (char*)malloc(datasize + 4); + fprintf(stdout, "LockResource worked\n"); + if (str) { + memcpy(str, data, datasize); + str[datasize] = 'E'; + str[datasize + 1] = 'O'; + str[datasize + 2] = 'R'; + str[datasize + 3] = 0; + fprintf(stdout, "str='%s'\n", str); + free(str); + + ret = 0; + +#ifdef CMAKE_RCDEFINE_NO_QUOTED_STRINGS + fprintf(stdout, "LoadString skipped\n"); +#else + { + char buf[256]; + if (LoadString(NULL, 1026, buf, sizeof(buf)) > 0) { + fprintf(stdout, "LoadString worked\n"); + fprintf(stdout, "buf='%s'\n", buf); + } else { + fprintf(stdout, "LoadString failed\n"); + ret = 1; + } + } +#endif + } + } + } + } + + return ret +#ifdef HAVE_RESOURCE_LIB + + lib() +#endif + ; +}
diff --git a/Tests/VSResource/main.cpp b/Tests/VSResource/main.cpp deleted file mode 100644 index 65e33b8..0000000 --- a/Tests/VSResource/main.cpp +++ /dev/null
@@ -1,76 +0,0 @@ -#include <windows.h> - -#include <stdio.h> - -extern int lib(); - -struct x -{ - char const* txt; -}; - -int main(int argc, char** argv) -{ - int ret = 1; - - fprintf(stdout, "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)\n"); - -#ifdef CMAKE_RCDEFINE - fprintf(stdout, "CMAKE_RCDEFINE defined\n"); -#endif - -#ifdef CMAKE_RCDEFINE_NO_QUOTED_STRINGS - // Expect CMAKE_RCDEFINE to preprocess to exactly test.txt - x test; - test.txt = "*exactly* test.txt"; - fprintf(stdout, "CMAKE_RCDEFINE_NO_QUOTED_STRINGS defined\n"); - fprintf(stdout, "CMAKE_RCDEFINE is %s, and is *not* a string constant\n", - CMAKE_RCDEFINE); -#else - // Expect CMAKE_RCDEFINE to be a string: - fprintf(stdout, "CMAKE_RCDEFINE='%s', and is a string constant\n", - CMAKE_RCDEFINE); -#endif - - HRSRC hello = ::FindResource(NULL, MAKEINTRESOURCE(1025), "TEXTFILE"); - if (hello) { - fprintf(stdout, "FindResource worked\n"); - HGLOBAL hgbl = ::LoadResource(NULL, hello); - int datasize = (int)::SizeofResource(NULL, hello); - if (hgbl && datasize > 0) { - fprintf(stdout, "LoadResource worked\n"); - fprintf(stdout, "SizeofResource returned datasize='%d'\n", datasize); - void* data = ::LockResource(hgbl); - if (data) { - fprintf(stdout, "LockResource worked\n"); - char* str = (char*)malloc(datasize + 4); - if (str) { - memcpy(str, data, datasize); - str[datasize] = 'E'; - str[datasize + 1] = 'O'; - str[datasize + 2] = 'R'; - str[datasize + 3] = 0; - fprintf(stdout, "str='%s'\n", str); - free(str); - - ret = 0; - -#ifdef CMAKE_RCDEFINE_NO_QUOTED_STRINGS - fprintf(stdout, "LoadString skipped\n"); -#else - char buf[256]; - if (::LoadString(NULL, 1026, buf, sizeof(buf)) > 0) { - fprintf(stdout, "LoadString worked\n"); - fprintf(stdout, "buf='%s'\n", buf); - } else { - fprintf(stdout, "LoadString failed\n"); - ret = 1; - } -#endif - } - } - } - } - - return ret + lib(); -}
diff --git a/Tests/VSResourceNinjaForceRSP/CMakeLists.txt b/Tests/VSResourceNinjaForceRSP/CMakeLists.txt index d9d0bd0..909e0be 100644 --- a/Tests/VSResourceNinjaForceRSP/CMakeLists.txt +++ b/Tests/VSResourceNinjaForceRSP/CMakeLists.txt
@@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10) -project(VSResourceNinjaForceRSP) +project(VSResourceNinjaForceRSP LANGUAGES C) set(CMAKE_NINJA_FORCE_RESPONSE_FILE TRUE) -add_library(ResourceLib lib.cpp test.rc) -add_executable(VSResourceNinjaForceRSP main.cpp test.rc) +add_library(ResourceLib lib.c test.rc) +add_executable(VSResourceNinjaForceRSP main.c test.rc)
diff --git a/Tests/VSResourceNinjaForceRSP/lib.c b/Tests/VSResourceNinjaForceRSP/lib.c new file mode 100644 index 0000000..abe3b78 --- /dev/null +++ b/Tests/VSResourceNinjaForceRSP/lib.c
@@ -0,0 +1,4 @@ +int lib(void) +{ + return 0; +}
diff --git a/Tests/VSResourceNinjaForceRSP/lib.cpp b/Tests/VSResourceNinjaForceRSP/lib.cpp deleted file mode 100644 index c912397..0000000 --- a/Tests/VSResourceNinjaForceRSP/lib.cpp +++ /dev/null
@@ -1,4 +0,0 @@ -int lib() -{ - return 0; -}
diff --git a/Tests/VSResourceNinjaForceRSP/main.cpp b/Tests/VSResourceNinjaForceRSP/main.c similarity index 100% rename from Tests/VSResourceNinjaForceRSP/main.cpp rename to Tests/VSResourceNinjaForceRSP/main.c
diff --git a/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx b/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx index 8265e32..801c9f6 100644 --- a/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx +++ b/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx
@@ -18,13 +18,10 @@ void OstringstreamUseCmstrcatCheck::registerMatchers(MatchFinder* Finder) { - Finder->addMatcher( - typeLoc(unless(elaboratedTypeLoc()), - optionally(hasParent(elaboratedTypeLoc().bind("parentType"))), - loc(qualType( - hasDeclaration(namedDecl(hasName("::std::ostringstream")))))) - .bind("ostringstream"), - this); + Finder->addMatcher(typeLoc(loc(qualType(hasDeclaration( + namedDecl(hasName("::std::ostringstream")))))) + .bind("ostringstream"), + this); } void OstringstreamUseCmstrcatCheck::check(
diff --git a/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx b/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx index 5f28054..cf75bfc 100644 --- a/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx +++ b/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx
@@ -90,8 +90,7 @@ loc(usingType(throughUsingDecl(namedDecl(hasName(CmsysName))))); Finder->addMatcher( - typeLoc(IsStd, unless(IsCmsys), unless(elaboratedTypeLoc()), - optionally(hasParent(elaboratedTypeLoc().bind("parentType"))), + typeLoc(IsStd, unless(IsCmsys), optionally(hasParent(nestedNameSpecifierLoc().bind("parentName")))) .bind(Bind), this);
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt index e760924..0d29b1c 100644 --- a/Utilities/Doxygen/CMakeLists.txt +++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeDeveloperReference_STANDALONE 1) - cmake_minimum_required(VERSION 3.13...4.1 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...4.2 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp index a613bce..91f3014 100644 --- a/Utilities/IWYU/mapping.imp +++ b/Utilities/IWYU/mapping.imp
@@ -54,6 +54,10 @@ { symbol: [ "S_IWUSR", private, "\"cm_sys_stat.h\"", public ] }, { symbol: [ "S_IWGRP", private, "\"cm_sys_stat.h\"", public ] }, + { symbol: [ "std::filesystem::path", private, "<cm/filesystem>", public ] }, + { symbol: [ "std::optional", private, "<cm/optional>", public ] }, + { symbol: [ "std::string_view", private, "<cm/string_view>", 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/KWIML/include/kwiml/abi.h b/Utilities/KWIML/include/kwiml/abi.h index 9b23580..d979ce7 100644 --- a/Utilities/KWIML/include/kwiml/abi.h +++ b/Utilities/KWIML/include/kwiml/abi.h
@@ -155,7 +155,7 @@ #if !defined(KWIML_ABI_CHAR_IS_UNSIGNED) && !defined(KWIML_ABI_CHAR_IS_SIGNED) # if defined(__CHAR_UNSIGNED__) /* GNU, some IBM XL, others? */ # define KWIML_ABI_CHAR_IS_UNSIGNED 1 -# elif defined(_CHAR_UNSIGNED) /* Intel, IBM XL, MSVC, Borland, others? */ +# elif defined(_CHAR_UNSIGNED) /* Intel, IBM XL, MSVC, Borland, Pelles C, others? */ # define KWIML_ABI_CHAR_IS_UNSIGNED 1 # elif defined(_CHAR_SIGNED) /* IBM XL, others? */ # define KWIML_ABI_CHAR_IS_SIGNED 1 @@ -177,6 +177,8 @@ # define KWIML_ABI_CHAR_IS_UNSIGNED 1 # elif defined(__PGIC__) /* PGI default */ # define KWIML_ABI_CHAR_IS_SIGNED 1 +# elif defined(__POCC__) /* PellesC default */ +# define KWIML_ABI_CHAR_IS_SIGNED 1 # elif defined(_MSC_VER) /* MSVC default */ # define KWIML_ABI_CHAR_IS_SIGNED 1 # elif defined(__WATCOMC__) /* Watcom default */
diff --git a/Utilities/Scripts/regenerate-presets.py b/Utilities/Scripts/regenerate-presets.py new file mode 100755 index 0000000..665b712 --- /dev/null +++ b/Utilities/Scripts/regenerate-presets.py
@@ -0,0 +1,839 @@ +#!/usr/bin/env python3 +"""Generate CMake presets documentation (reST) and JSON schema. + +The documentation and JSON schema for CMake presets are both generated from +Help/manual/presets/schema.yaml, which describes the schema in a way that +allows both documentation forms to be combined in a single file, and also makes +it much easier to manage version revisions (compared to editing the JSON schema +'by hand'). + +All input and output files are expected to be in known locations relative to +this script. + +Usage: python3 regenerate-presets.py +""" + +import json +import re +from copy import deepcopy +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Self + +import yaml + +LATEST = 1 +TYPES = {} +REFS = {} + +PRESETS_REL = 'Help/manual/presets' +SCHEMA_YAML_FILENAME = 'schema.yaml' +SCHEMA_JSON_FILENAME = 'schema.json' + +WORKSPACE = Path(__file__).parent.parent.parent.absolute() +SCRIPT_REL = Path(__file__).relative_to(WORKSPACE) +PRESETS = WORKSPACE / PRESETS_REL +DIAGNOSTICS = WORKSPACE / 'Source/cmDiagnostics.h' + +RST_BANNER = f"""\ +.. This file was generated by {SCRIPT_REL.as_posix()} + from {PRESETS_REL}/{SCHEMA_YAML_FILENAME}. Do not edit. +""" + +DIAGNOSTIC_TABLE_MACRO = 'CM_FOR_EACH_DIAGNOSTIC_TABLE' +CONFIGURE_PRESET_PROPERTIES_PATH = ( + 'properties', 'configurePresets', 'items', 'properties' +) +WARNING_DESCRIPTION = ( + 'An optional boolean. ' + 'Equivalent to passing -W{c} or -Wno-{c} on the command line. ' + 'This may not be set to false if errors.{p} is set to true.' +) +WARNING_SPHINX_DESCRIPTION = """ +An optional boolean. Equivalent to passing :option:`-W{c} <cmake -W>` or +:option:`-Wno-{c} <cmake -Wno->` on the command line. +This may not be set to ``false`` if ``errors.{p}`` is set to ``true``. +""".strip() +ERROR_DESCRIPTION = ( + 'An optional boolean. ' + 'Equivalent to passing -Werror={c} or -Wno-error={c} on the command line. ' + 'This may not be set to true if warnings.{p} is set to false.' +) +ERROR_SPHINX_DESCRIPTION = """ +An optional boolean. Equivalent to passing :cmake-option:`-Werror={c}` or +:cmake-option:`-Wno-error={c}` on the command line. +This may not be set to ``true`` if ``warnings.{p}`` is set to ``false``. +""".strip() + + +# ============================================================================= +@dataclass +class Diagnostic: + presetName: str + cliName: str + since: int + + # ------------------------------------------------------------------------- + def format(self, template: str) -> str: + return template.format(c=self.cliName, p=self.presetName) + + +# ============================================================================= +class Value: + + # ------------------------------------------------------------------------- + def __init__(self, data: list[dict[str, Any]]): + self.data = data + self.spans = None + + # ------------------------------------------------------------------------- + @property + def objects(self) -> dict[str, 'Object']: + return {} + + # ------------------------------------------------------------------------- + def updateSpans(self, since: int, until: int) -> None: + self.spans = [(since, until)] + + # ------------------------------------------------------------------------- + def updateRefs(self, objects: dict[str, 'Object']) -> None: + pass + + # ------------------------------------------------------------------------- + def defs(self) -> dict[str, Any]: + return {} + + # ------------------------------------------------------------------------- + def schema(self, version: int) -> dict[str, Any]: + return deepcopy(self.data) + + +# ============================================================================= +class Object(Value): + + # ------------------------------------------------------------------------- + def __init__(self, data: dict[str, Any], name: str, + label: str | None = None): + # Prevent data sharing from modifying unrelated parts of the schema as + # we pick apart our section. + data = deepcopy(data) + + self.name = data.pop('id', name) + self.label = label + + self.properties = {} + for n, p in data.pop('properties').items(): + pl = '.'.join([label, n]) if label is not None else n + self.properties[n] = Property(p, n, pl) + if len(self.properties): + self.properties[CommentProperty.name] = CommentProperty() + + ap = data.pop('additionalProperties', None) + if ap is not None: + self.additionalProperties = buildValue(ap, self.name, self.label) + else: + self.additionalProperties = None + + super().__init__(data) + + # ------------------------------------------------------------------------- + @property + def objects(self) -> dict[str, Self]: + out = {self.name: self} + for p in self.properties.values(): + out.update(p.objects) + if self.additionalProperties is not None: + out.update(self.additionalProperties.objects) + return out + + # ------------------------------------------------------------------------- + def updateSpans(self, since: int, until: int) -> None: + spans = [[(since, until)]] + for p in self.properties.values(): + p.updateSpans(since, until) + spans.append(p.spans) + if self.additionalProperties is not None: + self.additionalProperties.updateSpans(since, until) + spans.append(self.additionalProperties.spans) + self.spans = combineSpans(*spans) + + # ------------------------------------------------------------------------- + def updateRefs(self, objects: dict[str, 'Object']) -> None: + for p in self.properties.values(): + p.updateRefs(objects) + if self.additionalProperties is not None: + self.additionalProperties.updateRefs(objects) + + # ------------------------------------------------------------------------- + def doc(self, since: int, until: int) -> str | None: + since = max(since, self.spans[0][0]) + until = min(until, self.spans[-1][1]) + + out = [] + for _, p in self.properties.items(): + content = p.doc(since, until) + if not content: + continue + out.append(content) + + return '\n\n'.join(out) if len(out) else None + + # ------------------------------------------------------------------------- + def defs(self, exclude: set[str] = {}) -> dict[str, Any]: + out = {} + for n, p in self.properties.items(): + if n in exclude: + continue + for s in p.spans: + r = f'{p.label}@{spanRef(*s)}' + out[r] = p.schema(s[0]) + out.update(p.defs()) + if self.additionalProperties is not None: + out.update(self.additionalProperties.defs()) + return out + + # ------------------------------------------------------------------------- + def schema(self, version: int, inline: set[str] = {}) -> dict[str, Any]: + out = deepcopy(self.data) + + props = {} + required = [] + for n, p in self.properties.items(): + if n in inline: + props[n] = p.schema(version) + else: + r = p.ref(version) + if r is not None: + props[n] = r + if p.required: + required.append(n) + + out['properties'] = props + if len(required): + out['required'] = required + + if self.additionalProperties is not None: + ap = self.additionalProperties.schema(version) + out['additionalProperties'] = ap + return out + + +# ============================================================================= +class Ref(Value): + + # ------------------------------------------------------------------------- + def __init__(self, data: list[dict[str, Any]]): + self.target = data.pop('target') + self.targetSpans = None + super().__init__(data) + + # ------------------------------------------------------------------------- + def updateSpans(self, since: int, until: int) -> None: + if self.spans is None: + super().updateSpans(since, until) + self.spans = trimSpans(self.spans, since, until) + + # ------------------------------------------------------------------------- + def updateRefs(self, objects: dict[str, 'Object']) -> None: + global REFS + + t = objects.get(self.target) + if t is not None: + since = self.spans[0][0] + until = self.spans[-1][1] + self.spans = trimSpans(t.spans, since, until) + self.targetSpans = t.spans + + rs = REFS.get(self.target, (since, until)) + REFS[self.target] = (min(since, rs[0]), max(until, rs[1])) + + # ------------------------------------------------------------------------- + def schema(self, version: int) -> dict[str, Any]: + ts = trimSpans(self.targetSpans, *REFS[self.target]) + s = findSpan(ts, version) + if s is not None: + return { + '$ref': f'#/definitions/{self.target}@{spanRef(*s)}', + } + return {} + + +# ============================================================================= +class Array(Value): + + # ------------------------------------------------------------------------- + def __init__(self, data: dict[str, Any], name: str, label: str): + item = data.pop('items') + self.value = buildValue(item, name, label) + + super().__init__(data) + + # ------------------------------------------------------------------------- + @property + def objects(self) -> dict[str, Object]: + return self.value.objects + + # ------------------------------------------------------------------------- + def updateSpans(self, since: int, until: int) -> None: + self.value.updateSpans(since, until) + self.spans = self.value.spans + + # ------------------------------------------------------------------------- + def updateRefs(self, objects: dict[str, 'Object']) -> None: + self.value.updateRefs(objects) + + # ------------------------------------------------------------------------- + def defs(self) -> dict[str, Any]: + return self.value.defs() + + # ------------------------------------------------------------------------- + def schema(self, version: int) -> dict[str, Any]: + out = deepcopy(self.data) + out['items'] = self.value.schema(version) + return out + + +# ============================================================================= +class Union(Value): + + # ------------------------------------------------------------------------- + def __init__(self, data: list[dict[str, Any]], name: str, label: str): + self.items = [] + for i, d in enumerate(data): + self.items.append(buildValue(d, name, self.itemLabel(label, i))) + + super().__init__({}) + + # ------------------------------------------------------------------------- + def itemLabel(self, label: str, index: int) -> str: + return label + + # ------------------------------------------------------------------------- + @property + def objects(self) -> dict[str, Object]: + out = {} + for v in self.items: + out.update(v.objects) + return out + + # ------------------------------------------------------------------------- + def updateSpans(self, since: int, until: int) -> None: + spans = [[(since, until)]] + for i in self.items: + i.updateSpans(since, until) + spans.append(i.spans) + self.spans = combineSpans(*spans) + + # ------------------------------------------------------------------------- + def updateRefs(self, objects: dict[str, 'Object']) -> None: + for i in self.items: + i.updateRefs(objects) + + # ------------------------------------------------------------------------- + def defs(self) -> dict[str, Any]: + out = {} + for v in self.items: + out.update(v.defs()) + return out + + # ------------------------------------------------------------------------- + def schema(self, version: int) -> dict[str, Any]: + out = [] + for i in self.items: + out.append(i.schema(version)) + return {'anyOf': out} + + +# ============================================================================= +class Variant(Union): + + # ------------------------------------------------------------------------- + def __init__(self, data: list[dict[str, Any]], name: str, label: str): + self.identifier = data.pop('id', None) + + if self.identifier is not None: + name = self.identifier + label = self.identifier + + super().__init__(data.pop('items'), name, label) + + # ------------------------------------------------------------------------- + def itemLabel(self, label: str, index: int) -> str: + return f'{label}[{index}]' + + # ------------------------------------------------------------------------- + @property + def objects(self) -> dict[str, Object]: + out = super().objects + if self.identifier is not None: + out[self.identifier] = self + return out + + # ------------------------------------------------------------------------- + def doc(self, since: int, until: int) -> None: + return None + + # ------------------------------------------------------------------------- + def defs(self) -> dict[str, Any]: + out = super().defs() + + if self.identifier is not None: + for s in self.spans: + n = f'{self.identifier}@{spanRef(*s)}' + out[n] = super().schema(s[0]) + + return out + + # ------------------------------------------------------------------------- + def schema(self, version: int) -> dict[str, Any]: + if self.identifier is not None: + s = findSpan(self.spans, version) + if s is not None: + return { + '$ref': f'#/definitions/{self.identifier}@{spanRef(*s)}', + } + return {} + + return super().schema(version) + + +# ============================================================================= +class Property: + + # ------------------------------------------------------------------------- + def __init__(self, data: dict[str, Any], name: str, label: str): + self.name = name + self.label = label + self.since = data.pop('since', 1) + self.until = data.pop('until', LATEST + 1) + self.required = data.pop('required', False) + self.content = data.pop('sphinxDescription', data.get('description')) + + note = data.pop('sphinxNote', None) + if note is not None: + self.content += f'\n\n.. note::\n\n{indent(note)}' + + self._revisions = [] + for rev in data.pop('revisions', []): + rd = deepcopy(data) + rd.update(rev) + self._revisions.append(Property(rd, self.name, self.label)) + + if len(self._revisions): + self.since = max(self.since, + min([r.since for r in self._revisions])) + self.until = min(self.until, + max([r.until for r in self._revisions])) + self.value = None + else: + self.value = buildValue(data, self.name, self.label) + + self.spans = None + + # ------------------------------------------------------------------------- + @property + def objects(self) -> dict[str, Object]: + if len(self._revisions): + out = {} + for r in self._revisions: + out.update(r.objects) + return out + + return self.value.objects + + # ------------------------------------------------------------------------- + def updateSpans(self, since: int, until: int) -> None: + since = max(since, self.since) + until = min(until, self.until) + + if len(self._revisions): + spans = [[(since, until)]] + for r in self._revisions: + r.updateSpans(since, until) + spans.append(r.spans) + self.spans = combineSpans(*spans) + else: + self.value.updateSpans(since, until) + self.spans = self.value.spans + + # ------------------------------------------------------------------------- + def updateRefs(self, objects: dict[str, 'Object']) -> None: + if len(self._revisions): + for r in self._revisions: + r.updateRefs(objects) + else: + self.value.updateRefs(objects) + + # ------------------------------------------------------------------------- + def ref(self, version: int) -> dict[str, str] | None: + s = findSpan(self.spans, version) + if s is not None: + return { + '$ref': f'#/definitions/{self.label}@{spanRef(*s)}', + } + return None + + # ------------------------------------------------------------------------- + def doc(self, since: int, until: int) -> str | None: + if self.content is None: + return None + + out = f'.. _`CMakePresets.{self.label}`:\n\n``{self.name}``\n' + if self.since > since: + out += f' .. presets-versionadded:: {self.since}\n\n' + if self.until < until: + out += f' .. presets-versionremoved:: {self.until}\n\n' + out += indent(self.content) + + return out + + # ------------------------------------------------------------------------- + def defs(self) -> dict[str, Any]: + if len(self._revisions): + out = {} + for r in self._revisions: + out.update(r.defs()) + return out + + return self.value.defs() + + # ------------------------------------------------------------------------- + def schema(self, version: int) -> dict[str, Any]: + for r in self._revisions: + if version >= r.since and version < r.until: + return r.value.schema(version) + return self.value.schema(version) + + +# ============================================================================= +class CommentProperty(Property): + name: str = '$comment' + since: int = 10 + + # ------------------------------------------------------------------------- + def __init__(self): + data = { + 'since': type(self).since, + 'id': 'comment', + 'type': 'variant', + 'items': [ + { + 'type': 'string', + 'description': 'A single-line comment.', + }, + { + 'type': 'array', + 'description': 'A multi-line comment.', + 'minItems': 1, + 'items': { + 'type': 'string', + 'description': 'One line of the multi-line comment.', + } + } + ], + } + super().__init__(data, type(self).name, type(self).name) + + # ------------------------------------------------------------------------- + def doc(self, since: int, until: int) -> None: + return None + + +# ----------------------------------------------------------------------------- +def buildValue( + data: Any, name: str, label: str | None = None +) -> Variant | Object | Array | Ref | Value: + if type(data) is dict: + if 'anyOf' in data: + return Union(data.pop('anyOf'), name, label) + elif data['type'] == 'variant': + return Variant(data, name, label) + elif data['type'] == 'array': + return Array(data, name, label) + elif data['type'] == 'object': + return Object(data, name, label) + elif data['type'] == 'ref': + return Ref(data) + + return Value(data) + + +# ----------------------------------------------------------------------------- +def extractDivisions(spans: list[tuple[int, int]]) -> set[int]: + return {n for s in spans for n in s} + + +# ----------------------------------------------------------------------------- +def trimSpans(spans: list[tuple[int, int]], + since: int, until: int) -> list[tuple[int, int]]: + divisions = extractDivisions(spans) + + last = since + out = [] + + for d in sorted(divisions): + if d > since: + out.append((last, min(d, until))) + last = min(d, until) + if d >= until: + break + + return out + + +# ----------------------------------------------------------------------------- +def combineSpans(*spans: list[tuple[int, int]]) -> list[tuple[int, int]]: + divisions = set() + for s in spans: + divisions.update(extractDivisions(s)) + + divisions = sorted(divisions) + last = divisions.pop(0) + out = [] + + for d in divisions: + out.append((last, d)) + last = d + + return out + + +# ----------------------------------------------------------------------------- +def findSpan(spans: list[tuple[int, int]], + version: int) -> tuple[int, int] | None: + for s in spans: + if version >= s[0] and version < s[1]: + return s + + return None + + +# ----------------------------------------------------------------------------- +def spanRef(since: int, until: int) -> str: + if since + 1 < until: + if until > LATEST: + return f'v{since}..' + else: + return f'v{since}..v{until - 1}' + return f'v{since}' + + +# ----------------------------------------------------------------------------- +def indent(text: str): + return '\n'.join([(' ' + line).rstrip() for line in text.split('\n')]) + + +# ----------------------------------------------------------------------------- +def trimDescriptions(data: Any): + if type(data) is dict: + for k, v in data.items(): + if type(v) is str and k in {'description', 'sphinxDescription'}: + data[k] = v.strip() + elif type(v) is dict: + data[k] = trimDescriptions(v) + elif type(v) is list: + data[k] = [trimDescriptions(item) for item in v] + + return data + + +# ----------------------------------------------------------------------------- +def getPath(data: Any, path: tuple[str]) -> Any: + if len(path) == 0: + return data + return getPath(data[path[0]], path[1:]) + + +# ----------------------------------------------------------------------------- +def diagnosticPresetName(symbol: str) -> str: + sep = False + out = '' + for c in symbol[4:]: + if c == '_': + sep = True + elif sep: + out += c + sep = False + else: + out += c.lower() + return out + + +# ----------------------------------------------------------------------------- +def buildDiagnosticsSchema( + diagnostics: list[Diagnostic], + descriptionTemplate: str, + sphinxDescriptionTemplate: str, +) -> dict[str, dict[str, Any]]: + out = {} + + for d in diagnostics: + out[d.presetName] = { + 'since': d.since, + 'type': 'boolean', + 'description': d.format(descriptionTemplate), + 'sphinxDescription': d.format(sphinxDescriptionTemplate), + } + + return out + + +# ----------------------------------------------------------------------------- +def mergeDiagnostics( + generated: dict[str, dict[str, Any]], + extra: dict[str, dict[str, Any]], +) -> dict[str, dict[str, Any]]: + # Get unsorted names. + tail = {} + for k in list(extra.keys()): + s = extra[k].pop('sort', True) + if not s: + tail[k] = extra.pop(k) + + # Combine inputs and sort. + out = {} + combined = deepcopy(generated) + combined.update(extra) + for k in sorted(combined.keys()): + out[k] = combined[k] + + # Add unsorted items and return result. + out.update(tail) + return out + + +# ----------------------------------------------------------------------------- +def readDiagnostics(path: Path | str) -> list[Diagnostic]: + content = '' + + # Extract diagnostics table from header. + with open(path, 'r') as f: + extracting = False + for line in f: + if DIAGNOSTIC_TABLE_MACRO in line: + extracting = True + continue + + if extracting: + content += line.replace('\\\n', ' ') + if not line.strip().endswith('\\'): + break + + out = [] + while True: + m = re.match(r'\s*SELECT[(]([^)]+)[)]', content) + if m is None: + break + + args = [a.strip() for a in m.group(1).split(',')] + p = diagnosticPresetName(args[3]) + c = args[3][4:].lower().replace('_', '-') + v = int(args[4]) + if v > 1: + out.append(Diagnostic(p, c, v)) + + content = content[m.span()[1]:] + + return out + + +# ----------------------------------------------------------------------------- +def main(): + # Read the schema definition. + with open(PRESETS / SCHEMA_YAML_FILENAME, 'r') as f: + schema = yaml.safe_load(f) + + # Extract the current (latest) version. + global LATEST + + del schema['definitions'] + LATEST = schema.pop('version') + + # Remove whitespace around descriptions. + schema = trimDescriptions(schema) + + # Read diagnostics and update schema. + diagnostics = readDiagnostics(DIAGNOSTICS) + configureSchema = getPath(schema, CONFIGURE_PRESET_PROPERTIES_PATH) + configureSchema['warnings']['properties'] = mergeDiagnostics( + buildDiagnosticsSchema(diagnostics, WARNING_DESCRIPTION, + WARNING_SPHINX_DESCRIPTION), + configureSchema['warnings']['properties']) + configureSchema['errors']['properties'] = mergeDiagnostics( + buildDiagnosticsSchema(diagnostics, ERROR_DESCRIPTION, + ERROR_SPHINX_DESCRIPTION), + configureSchema['errors']['properties']) + + # Extract global type definitions. + global TYPES, REFS + + for t in schema.pop('types', []): + name = t['id'] + value = buildValue(t, name) + value.updateSpans(1, LATEST + 1) + TYPES[name] = value + + for t in TYPES.values(): + t.updateRefs(TYPES) + + # Reset the spans for references, as these currently contain only spans for + # circular references of global types, which are likely broader than the + # actual usage spans of those types. We'll recalculate the correct spans + # when we update refs from the root object. + REFS = {} + + # Parse the root object. This will also recursively parse other objects. + root = Object(schema, 'root') + root.updateSpans(1, LATEST + 1) + + # Resolve references. + objects = root.objects + + types = objects + types.update(TYPES) + + root.updateRefs(types) + + for name, value in TYPES.items(): + span = REFS.get(name) + if span is not None: + value.updateSpans(*span) + + root.updateSpans(1, LATEST + 1) + + # Extract Sphinx documentation for each object's properties. + for n, o in objects.items(): + doc = o.doc(1, LATEST + 1) + if doc is not None: + print(f'- Generating reST documentation for {n} properties') + with open(PRESETS / f'{o.name}-properties.rst', 'w') as f: + print(RST_BANNER, file=f) + print(doc, file=f) + + # Generate schema for each version. + versions = [] + for v in range(1, LATEST + 1): + vs = root.schema(v, inline={'version'}) + vs['properties']['version']['const'] = v + versions.append(vs) + + # Add definitions and write the schema. + types = root.defs(exclude={'version'}) + for t in TYPES.values(): + types.update(t.defs()) + + schema = { + '$schema': 'http://json-schema.org/draft/2020-12/schema#', + 'type': 'object', + 'oneOf': versions, + 'required': ['version'], + 'definitions': types + } + + with open(PRESETS / SCHEMA_JSON_FILENAME, 'w') as f: + print(json.dumps(schema, indent=2), file=f) + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +main()
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index 6227b16..213b297 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash
@@ -8,7 +8,7 @@ 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_18_0" +readonly tag="curl-8_20_0" readonly shortlog=false readonly exact_tree_match=false readonly paths="
diff --git a/Utilities/Scripts/update-libarchive.bash b/Utilities/Scripts/update-libarchive.bash index 73ee07e..564f346 100755 --- a/Utilities/Scripts/update-libarchive.bash +++ b/Utilities/Scripts/update-libarchive.bash
@@ -8,7 +8,7 @@ readonly ownership="LibArchive Upstream <libarchive-discuss@googlegroups.com>" readonly subtree="Utilities/cmlibarchive" readonly repo="https://github.com/libarchive/libarchive.git" -readonly tag="v3.8.5" +readonly tag="v3.8.6" readonly shortlog=false readonly exact_tree_match=false readonly paths="
diff --git a/Utilities/Scripts/update-libuv.bash b/Utilities/Scripts/update-libuv.bash index b9f664e..19153d0 100755 --- a/Utilities/Scripts/update-libuv.bash +++ b/Utilities/Scripts/update-libuv.bash
@@ -8,11 +8,7 @@ readonly ownership="libuv upstream <libuv@googlegroups.com>" readonly subtree="Utilities/cmlibuv" readonly repo="https://github.com/libuv/libuv.git" -# We cannot import libuv 1.45 or higher because it has higher -# minimum system requirements than we do: -# - It requires C11 atomics from GCC 4.9+. We support GCC 4.8. -# - It requires Windows 8, we support Windows 7. -readonly tag="v1.44.2" +readonly tag="v1.52.0" readonly shortlog=false readonly exact_tree_match=false readonly paths="
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt index 3debbd0..ffb46e9 100644 --- a/Utilities/Sphinx/CMakeLists.txt +++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeHelp_STANDALONE 1) - cmake_minimum_required(VERSION 3.13...4.1 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...4.2 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py index dd8c2f38..2743c14 100644 --- a/Utilities/Sphinx/cmake.py +++ b/Utilities/Sphinx/cmake.py
@@ -13,6 +13,7 @@ # The following imports may fail if we don't have Sphinx 2.x or later. if sphinx.version_info >= (2,): from docutils import io, nodes + from docutils.statemachine import ViewList from docutils.nodes import Element, Node, TextElement, system_message from docutils.parsers.rst import Directive, directives from docutils.transforms import Transform @@ -21,6 +22,9 @@ from sphinx import addnodes from sphinx.directives import ObjectDescription, nl_escape_re from sphinx.domains import Domain, ObjType + from sphinx.domains.changeset import VersionChange + from sphinx.domains.changeset import versionlabels, versionlabel_classes + from sphinx.domains.std import OptionXRefRole from sphinx.roles import XRefRole from sphinx.util import logging, ws_re from sphinx.util.docutils import ReferenceRole @@ -33,6 +37,21 @@ # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# BEGIN sphinx tweaks + +# Adjust the 'std' domain regex used to parse options so that it recognizes +# e.g. `-W<name>` as the option `-W` with a value `<name>`, rather than +# treating the entire string as the option name. +# +# See also https://github.com/sphinx-doc/sphinx/issues/14323. + +sphinx.domains.std.option_desc_re = ( + re.compile(r'((?:/|--|-|\+)?[^\s=<]+)(=?\s*.*)')) + +# END sphinx tweaks + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + # BEGIN pygments tweaks # Override much of pygments' CMakeLexer. @@ -127,6 +146,12 @@ sig_end_re = re.compile(r'(?<=[)])\n') +def _parse_rst(state, text: str) -> list[Node]: + node = nodes.Element() + state.nested_parse(ViewList([text], source=''), 0, node) + return node.children + + @dataclass class ObjectEntry: docname: str @@ -216,6 +241,7 @@ _cmake_index_objs = { 'command': _cmake_index_entry('command'), 'cpack_gen': _cmake_index_entry('cpack generator'), + 'diagnostic': _cmake_index_entry('diagnostic'), 'envvar': _cmake_index_entry('envvar'), 'generator': _cmake_index_entry('generator'), 'genex': _cmake_index_entry('genex'), @@ -225,6 +251,7 @@ 'policy': _cmake_index_entry('policy'), 'prop_cache': _cmake_index_entry('cache property'), 'prop_dir': _cmake_index_entry('directory property'), + 'prop_fs': _cmake_index_entry('file set property'), 'prop_gbl': _cmake_index_entry('global property'), 'prop_inst': _cmake_index_entry('installed file property'), 'prop_sf': _cmake_index_entry('source file property'), @@ -460,6 +487,8 @@ sigargs = self.targetnames[sig] else: def extract_keywords(params): + if params[-1].endswith(')'): + params[-1] = params[-1][:-1] for p in params: if p[0].isalpha(): yield p @@ -502,6 +531,80 @@ return super().run() +class CMakeDiagnosticObject(CMakeObject): + object_type = 'diagnostic' + required_arguments = 0 + optional_arguments = 0 + + DEFAULT_CHOICES = {'ignore', 'warn', 'error'} + + def default_option(argument): + return directives.choice( + argument, CMakeDiagnosticObject.DEFAULT_CHOICES) + + option_spec = { + 'default': default_option, + 'parent': directives.unchanged, + } + + def __init__(self, *args, **kwargs): + self.targetname = None + super().__init__(*args, **kwargs) + + def _preset_name(self) -> str: + sep = False + out = '' + for c in self.targetname[4:]: + if c == '_': + sep = True + elif sep: + out += c + sep = False + else: + out += c.lower() + return out + + def _build_field(self, name: str, content: str | list[Node]) -> Node: + if type(content) is not list: + content = _parse_rst(self.state,content) + + name_node = nodes.field_name(text=name) + body_node = nodes.field_body('', *content) + return nodes.field('', name_node, body_node) + + def _build_cli(self) -> list[Node]: + cname = self.targetname[4:].lower().replace('_', '-') + ctext = f'-W[no-][error=]{cname}' + return _parse_rst(self.state,f':option:`{ctext} <cmake -W>`') + + def _build_preset_refs(self) -> list[Node]: + p = self._preset_name() + w = f':preset:`warnings.{p} <configurePresets.warnings.{p}>`' + e = f':preset:`errors.{p} <configurePresets.errors.{p}>`' + return _parse_rst(self.state,f'{w}, {e}') + + def run(self) -> list[Node]: + self.domain, self.objtype = self.name.split(':', 1) + doc = self.state.document + self.targetname = doc.next_node(nodes.title).astext() + + default = self.options['default'].capitalize() + parent = self.options.get('parent') + + headers = nodes.field_list() + headers += self._build_field('Command Line', self._build_cli()) + headers += self._build_field('Presets', self._build_preset_refs()) + headers += self._build_field('Default', default) + + if parent: + parentRef = _parse_rst(self.state,f':diagnostic:`{parent}`') + headers += self._build_field('Parent', parentRef) + + node = nodes.Element() + self.state.nested_parse(self.content, self.content_offset, node) + return [headers] + node.children + + class CMakeReferenceRole: # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'. _re = re.compile(r'^(.+?)(\s*)(?<!\x00)<(.*?)>$', re.DOTALL) @@ -549,6 +652,7 @@ _re_sub = re.compile(r'^([^()\s]+)\s*\(([^()]*)\)$', re.DOTALL) _re_genex = re.compile(r'^\$<([^<>:]+)(:[^<>]+)?>$', re.DOTALL) _re_guide = re.compile(r'^([^<>/]+)/([^<>]*)$', re.DOTALL) + _re_explicit = re.compile(r'^([^<>]+)\s+<([^<>]+)>$', re.DOTALL) def __call__(self, typ, rawtext, text, *args, **kwargs): if typ == 'cmake:command': @@ -568,6 +672,13 @@ m = CMakeXRefRole._re_guide.match(text) if m: text = f'{m.group(2)} <{text}>' + elif typ == 'cmake:preset': + m = CMakeXRefRole._re_explicit.match(text) + if m: + rawtext = f'{m.group(1)} <CMakePresets.{m.group(2)}>' + text = f'{m.group(1)} <CMakePresets.{m.group(2)}>' + else: + text = f'CMakePresets.{text}' return super().__call__(typ, rawtext, text, *args, **kwargs) # We cannot insert index nodes using the result_nodes method @@ -582,6 +693,16 @@ # pass +class CMakeOptionXRefRole(OptionXRefRole): + def __init__(self, command: str) -> None: + self.command = command + super().__init__() + + def __call__(self, typ, rawtext, text, *args, **kwargs): + content = f'{text} <{self.command} {re.split(r"[ =]", text)[0]}>' + return super().__call__('std:option', text, content, *args, **kwargs) + + class CMakeXRefTransform(Transform): # Run this transform early since we insert nodes we want @@ -641,6 +762,7 @@ object_types = { 'command': ObjType('command', 'command'), 'cpack_gen': ObjType('cpack_gen', 'cpack_gen'), + 'diagnostic': ObjType('diagnostic', 'diagnostic'), 'envvar': ObjType('envvar', 'envvar'), 'generator': ObjType('generator', 'generator'), 'genex': ObjType('genex', 'genex'), @@ -650,6 +772,7 @@ 'policy': ObjType('policy', 'policy'), 'prop_cache': ObjType('prop_cache', 'prop_cache'), 'prop_dir': ObjType('prop_dir', 'prop_dir'), + 'prop_fs': ObjType('prop_fs', 'prop_fs'), 'prop_gbl': ObjType('prop_gbl', 'prop_gbl'), 'prop_inst': ObjType('prop_inst', 'prop_inst'), 'prop_sf': ObjType('prop_sf', 'prop_sf'), @@ -660,15 +783,18 @@ directives = { 'command': CMakeObject, 'envvar': CMakeObject, + 'diagnostic': CMakeDiagnosticObject, 'genex': CMakeGenexObject, 'signature': CMakeSignatureObject, 'variable': CMakeObject, # Other `object_types` cannot be created except by the `CMakeTransform` } roles = { + # General CMake reference roles. 'cref': CMakeCRefRole(), 'command': CMakeXRefRole(fix_parens=True, lowercase=True), 'cpack_gen': CMakeXRefRole(), + 'diagnostic': CMakeXRefRole(), 'envvar': CMakeXRefRole(), 'generator': CMakeXRefRole(), 'genex': CMakeXRefRole(), @@ -678,12 +804,23 @@ 'policy': CMakeXRefRole(), 'prop_cache': CMakeXRefRole(), 'prop_dir': CMakeXRefRole(), + 'prop_fs': CMakeXRefRole(), 'prop_gbl': CMakeXRefRole(), 'prop_inst': CMakeXRefRole(), 'prop_sf': CMakeXRefRole(), 'prop_test': CMakeXRefRole(), 'prop_tgt': CMakeXRefRole(), 'manual': CMakeXRefRole(), + 'preset': CMakeXRefRole(lowercase=True, warn_dangling=True), + # Roles for program-specific command-line options without the program + # name (which add the name to form the ref). + 'cmake-option': CMakeOptionXRefRole('cmake'), + 'cmake-build-option': CMakeOptionXRefRole('cmake--build'), + 'cmake-install-option': CMakeOptionXRefRole('cmake--install'), + 'cmake-workflow-option': CMakeOptionXRefRole('cmake--workflow'), + 'cpack-option': CMakeOptionXRefRole('cpack'), + 'ctest-dashboard-option': CMakeOptionXRefRole('ctest-dashboard'), + 'ctest-option': CMakeOptionXRefRole('ctest'), } initial_data = { 'objects': {}, # fullname -> ObjectEntry @@ -716,13 +853,28 @@ targetid = f'{typ}:{target}' obj = self.data['objects'].get(targetid) - if obj is None and typ == 'command': - # If 'command(args)' wasn't found, try just 'command'. - # TODO: remove this fallback? warn? - # logger.warning(f'no match for {targetid}') - command = target.split('(')[0] - targetid = f'{typ}:{command}' - obj = self.data['objects'].get(targetid) + if obj is None: + if typ == 'command': + # If 'command(args)' wasn't found, try just 'command'. + # TODO: remove this fallback? warn? + # logger.warning(f'no match for {targetid}') + command = target.split('(')[0] + targetid = f'{typ}:{command}' + obj = self.data['objects'].get(targetid) + elif typ == 'preset': + # Preset references are really just references to plain old + # explicit targets. + labels = env.get_domain('std').labels + docname, labelid, sectname = labels.get(target, ('', '', '')) + + if not docname: + return None + + if node['refexplicit']: + sectname = node.astext() + + return make_refnode(builder, fromdocname, docname, labelid, + nodes.literal('', sectname), target) if obj is None: # TODO: warn somehow? @@ -752,4 +904,26 @@ app.add_transform(CMakeTransform) app.add_transform(CMakeXRefTransform) app.add_domain(CMakeDomain) + + version_directives = { + 'cmakefiles': 'cmakeFiles', + 'codemodel': 'codemodel', + 'presets': 'presets', + 'toolchains': 'toolchains', + } + for directive, name in version_directives.items(): + versionlabels.update({ + f'{directive}-versionadded': f'Added in {name} version %s', + f'{directive}-versionchanged': f'Changed in {name} version %s', + f'{directive}-versionremoved': f'Removed in {name} version %s', + }) + versionlabel_classes.update({ + f'{directive}-versionadded': 'added', + f'{directive}-versionchanged': 'changed', + f'{directive}-versionremoved': 'removed', + }) + app.add_directive(f'{directive}-versionadded', VersionChange) + app.add_directive(f'{directive}-versionchanged', VersionChange) + app.add_directive(f'{directive}-versionremoved', VersionChange) + return {"parallel_read_safe": True}
diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in index 54ebc8e..67450e6 100644 --- a/Utilities/Sphinx/conf.py.in +++ b/Utilities/Sphinx/conf.py.in
@@ -26,7 +26,8 @@ 'dev', # NOTE Ignore shared RST files used in `.. include::` directives 'include/*.rst', - '**/include/*.rst' + '**/include/*.rst', + 'manual/presets/*.rst', ] extensions = ['cmake'] @@ -99,6 +100,7 @@ r'about:', r'https://gitlab\.kitware\.com/cmake/community/-/wikis/doc/cpack', r'https://web\.archive\.org/', + r'https://en\.cppreference\.com/', r'https://www\.freedesktop\.org/', r'https://www\.gnu\.org/', r'https://www\.intel\.com/',
diff --git a/Utilities/Sphinx/create_identifiers.py b/Utilities/Sphinx/create_identifiers.py index 61dd819..4283992 100755 --- a/Utilities/Sphinx/create_identifiers.py +++ b/Utilities/Sphinx/create_identifiers.py
@@ -28,6 +28,7 @@ ("genex", "genex"), ("guide", "guide"), ("target property", "prop_tgt"), + ("file set property", "prop_fs"), ("test property", "prop_test"), ("source file property", "prop_sf"), ("global property", "prop_gbl"),
diff --git a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake index 2576a1a..51a2d94 100644 --- a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake +++ b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
@@ -24,7 +24,7 @@ option(CURL_HIDDEN_SYMBOLS "Hide libcurl internal symbols (=hide all symbols that are not officially external)" ON) mark_as_advanced(CURL_HIDDEN_SYMBOLS) -if(WIN32 AND (ENABLE_DEBUG OR ENABLE_CURLDEBUG)) +if(WIN32 AND ENABLE_DEBUG) # We need to export internal debug functions, # e.g. curl_easy_perform_ev() or curl_dbg_*(), # so disable symbol hiding for debug builds and for memory tracking.
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index a06c770..2cf306b 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -27,7 +27,7 @@ #include <sys/types.h> #include <unistd.h> #include <fcntl.h> -/* */ + #if defined(sun) || defined(__sun__) || \ defined(__SUNPRO_C) || defined(__SUNPRO_CC) # if defined(__SVR4) || defined(__srv4__) @@ -39,7 +39,7 @@ #if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX41) # define PLATFORM_AIX_V3 #endif -/* */ + #if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) #error "O_NONBLOCK does not work on this platform" #endif @@ -128,7 +128,7 @@ #ifdef HAVE_FILE_OFFSET_BITS #include <sys/types.h> /* Check that off_t can represent 2**63 - 1 correctly. - We cannot simply define LARGE_OFF_T to be 9223372036854775807, + We cannot define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t)1 << 62) - 1 + ((off_t)1 << 62))
diff --git a/Utilities/cmcurl/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake index 981b30c..bd3363c 100644 --- a/Utilities/cmcurl/CMake/FindBrotli.cmake +++ b/Utilities/cmcurl/CMake/FindBrotli.cmake
@@ -25,15 +25,16 @@ # # Input variables: # -# - `BROTLI_INCLUDE_DIR`: Absolute path to brotli include directory. -# - `BROTLICOMMON_LIBRARY`: Absolute path to `brotlicommon` library. -# - `BROTLIDEC_LIBRARY`: Absolute path to `brotlidec` library. +# - `BROTLI_INCLUDE_DIR`: Absolute path to brotli include directory. +# - `BROTLICOMMON_LIBRARY`: Absolute path to `brotlicommon` library. +# - `BROTLIDEC_LIBRARY`: Absolute path to `brotlidec` library. +# - `BROTLI_USE_STATIC_LIBS`: Configure for static brotli libraries. # # Defines: # -# - `BROTLI_FOUND`: System has brotli. -# - `BROTLI_VERSION`: Version of brotli. -# - `CURL::brotli`: brotli library target. +# - `BROTLI_FOUND`: System has brotli. +# - `BROTLI_VERSION`: Version of brotli. +# - `CURL::brotli`: brotli library target. set(_brotli_pc_requires "libbrotlidec" "libbrotlicommon") # order is significant: brotlidec then brotlicommon @@ -49,11 +50,22 @@ set(Brotli_FOUND TRUE) set(BROTLI_FOUND TRUE) set(BROTLI_VERSION ${_brotli_libbrotlicommon_VERSION}) + if(BROTLI_USE_STATIC_LIBS) + set(_brotli_CFLAGS "${_brotli_STATIC_CFLAGS}") + set(_brotli_INCLUDE_DIRS "${_brotli_STATIC_INCLUDE_DIRS}") + set(_brotli_LIBRARY_DIRS "${_brotli_STATIC_LIBRARY_DIRS}") + set(_brotli_LIBRARIES "${_brotli_STATIC_LIBRARIES}") + endif() message(STATUS "Found Brotli (via pkg-config): ${_brotli_INCLUDE_DIRS} (found version \"${BROTLI_VERSION}\")") else() find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") - find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon") - find_library(BROTLIDEC_LIBRARY NAMES "brotlidec") + if(BROTLI_USE_STATIC_LIBS) + find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon-static" "brotlicommon") + find_library(BROTLIDEC_LIBRARY NAMES "brotlidec-static" "brotlidec") + else() + find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon") + find_library(BROTLIDEC_LIBRARY NAMES "brotlidec") + endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Brotli @@ -72,10 +84,6 @@ endif() if(BROTLI_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_brotli_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::brotli) add_library(CURL::brotli INTERFACE IMPORTED) set_target_properties(CURL::brotli PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindCares.cmake b/Utilities/cmcurl/CMake/FindCares.cmake index 4a20bc0..26efdf8 100644 --- a/Utilities/cmcurl/CMake/FindCares.cmake +++ b/Utilities/cmcurl/CMake/FindCares.cmake
@@ -25,32 +25,58 @@ # # Input variables: # -# - `CARES_INCLUDE_DIR`: Absolute path to c-ares include directory. -# - `CARES_LIBRARY`: Absolute path to `cares` library. +# - `CARES_INCLUDE_DIR`: Absolute path to c-ares include directory. +# - `CARES_LIBRARY`: Absolute path to `cares` library. +# - `CARES_USE_STATIC_LIBS`: Configure for static c-ares libraries. # # Defines: # -# - `CARES_FOUND`: System has c-ares. -# - `CARES_VERSION`: Version of c-ares. -# - `CURL::cares`: c-ares library target. +# - `CARES_FOUND`: System has c-ares. +# - `CARES_VERSION`: Version of c-ares. +# - `CURL::cares`: c-ares library target. set(_cares_pc_requires "libcares") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED CARES_INCLUDE_DIR AND +if(NOT DEFINED CARES_INCLUDE_DIR AND NOT DEFINED CARES_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_cares ${_cares_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_cares ${_cares_pc_requires}) + endif() + if(NOT _cares_FOUND AND CURL_USE_CMAKECONFIG) + find_package(c-ares CONFIG QUIET) + endif() endif() if(_cares_FOUND) set(Cares_FOUND TRUE) set(CARES_FOUND TRUE) set(CARES_VERSION ${_cares_VERSION}) + if(CARES_USE_STATIC_LIBS) + set(_cares_CFLAGS "${_cares_STATIC_CFLAGS}") + set(_cares_INCLUDE_DIRS "${_cares_STATIC_INCLUDE_DIRS}") + set(_cares_LIBRARY_DIRS "${_cares_STATIC_LIBRARY_DIRS}") + set(_cares_LIBRARIES "${_cares_STATIC_LIBRARIES}") + endif() message(STATUS "Found Cares (via pkg-config): ${_cares_INCLUDE_DIRS} (found version \"${CARES_VERSION}\")") +elseif(c-ares_CONFIG) + set(Cares_FOUND TRUE) + set(CARES_FOUND TRUE) + set(CARES_VERSION ${c-ares_VERSION}) + if(CARES_USE_STATIC_LIBS) + set(_cares_LIBRARIES c-ares::cares_static) + else() + set(_cares_LIBRARIES c-ares::cares) + endif() + message(STATUS "Found Cares (via CMake Config): ${c-ares_CONFIG} (found version \"${CARES_VERSION}\")") else() find_path(CARES_INCLUDE_DIR NAMES "ares.h") - find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares") + if(CARES_USE_STATIC_LIBS) + set(_cares_CFLAGS "-DCARES_STATICLIB") + find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares_static" "cares") + else() + find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares") + endif() unset(CARES_VERSION CACHE) if(CARES_INCLUDE_DIR AND EXISTS "${CARES_INCLUDE_DIR}/ares_version.h") @@ -94,10 +120,6 @@ list(APPEND _cares_LIBRARIES "iphlpapi") # for if_indextoname and others endif() - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_cares_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::cares) add_library(CURL::cares INTERFACE IMPORTED) set_target_properties(CURL::cares PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake index 106c4c4..9237fb3 100644 --- a/Utilities/cmcurl/CMake/FindGSS.cmake +++ b/Utilities/cmcurl/CMake/FindGSS.cmake
@@ -25,15 +25,15 @@ # # Input variables: # -# - `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment) +# - `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment) # # Defines: # -# - `GSS_FOUND`: System has a GSS library. -# - `GSS_VERSION`: This is set to version advertised by pkg-config or read from manifest. -# In case the library is found but no version info available it is set to "unknown" -# - `CURL::gss`: GSS library target. -# - CURL_GSS_FLAVOUR`: Custom property. "GNU" or "MIT" if detected. +# - `GSS_FOUND`: System has a GSS library. +# - `GSS_VERSION`: This is set to version advertised by pkg-config or read from manifest. +# In case the library is found but no version info available it is set to "unknown" +# - `CURL::gss`: GSS library target. +# - `CURL_GSS_FLAVOUR`: Custom property. "GNU" or "MIT" if detected. set(_gnu_modname "gss") set(_mit_modname "mit-krb5-gssapi") @@ -258,10 +258,6 @@ ) if(GSS_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_gss_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::gss) add_library(CURL::gss INTERFACE IMPORTED) set_target_properties(CURL::gss PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindGnuTLS.cmake b/Utilities/cmcurl/CMake/FindGnuTLS.cmake index fff57b2..7f5b227 100644 --- a/Utilities/cmcurl/CMake/FindGnuTLS.cmake +++ b/Utilities/cmcurl/CMake/FindGnuTLS.cmake
@@ -80,10 +80,6 @@ endif() if(GNUTLS_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_gnutls_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::gnutls) add_library(CURL::gnutls INTERFACE IMPORTED) set_target_properties(CURL::gnutls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLDAP.cmake b/Utilities/cmcurl/CMake/FindLDAP.cmake index 2f5cc71..8902b23 100644 --- a/Utilities/cmcurl/CMake/FindLDAP.cmake +++ b/Utilities/cmcurl/CMake/FindLDAP.cmake
@@ -103,10 +103,6 @@ endif() if(LDAP_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_ldap_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::ldap) add_library(CURL::ldap INTERFACE IMPORTED) set_target_properties(CURL::ldap PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibbacktrace.cmake b/Utilities/cmcurl/CMake/FindLibbacktrace.cmake index c6f7b70..59da7fd 100644 --- a/Utilities/cmcurl/CMake/FindLibbacktrace.cmake +++ b/Utilities/cmcurl/CMake/FindLibbacktrace.cmake
@@ -47,10 +47,6 @@ set(_libbacktrace_INCLUDE_DIRS ${LIBBACKTRACE_INCLUDE_DIR}) set(_libbacktrace_LIBRARIES ${LIBBACKTRACE_LIBRARY}) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libbacktrace_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libbacktrace) add_library(CURL::libbacktrace INTERFACE IMPORTED) set_target_properties(CURL::libbacktrace PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibgsasl.cmake b/Utilities/cmcurl/CMake/FindLibgsasl.cmake index 5ddf957..7fbaa71 100644 --- a/Utilities/cmcurl/CMake/FindLibgsasl.cmake +++ b/Utilities/cmcurl/CMake/FindLibgsasl.cmake
@@ -79,10 +79,6 @@ endif() if(LIBGSASL_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libgsasl_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libgsasl) add_library(CURL::libgsasl INTERFACE IMPORTED) set_target_properties(CURL::libgsasl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibidn2.cmake b/Utilities/cmcurl/CMake/FindLibidn2.cmake index 336a7f7..dc78734 100644 --- a/Utilities/cmcurl/CMake/FindLibidn2.cmake +++ b/Utilities/cmcurl/CMake/FindLibidn2.cmake
@@ -80,10 +80,6 @@ endif() if(LIBIDN2_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libidn2_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libidn2) add_library(CURL::libidn2 INTERFACE IMPORTED) set_target_properties(CURL::libidn2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibpsl.cmake b/Utilities/cmcurl/CMake/FindLibpsl.cmake index 9b1a0cd..2287e67 100644 --- a/Utilities/cmcurl/CMake/FindLibpsl.cmake +++ b/Utilities/cmcurl/CMake/FindLibpsl.cmake
@@ -80,10 +80,6 @@ endif() if(LIBPSL_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libpsl_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libpsl) add_library(CURL::libpsl INTERFACE IMPORTED) set_target_properties(CURL::libpsl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibrtmp.cmake b/Utilities/cmcurl/CMake/FindLibrtmp.cmake deleted file mode 100644 index 853ba63..0000000 --- a/Utilities/cmcurl/CMake/FindLibrtmp.cmake +++ /dev/null
@@ -1,116 +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 -# -########################################################################### -# Find the librtmp library -# -# Input variables: -# -# - `LIBRTMP_INCLUDE_DIR`: Absolute path to librtmp include directory. -# - `LIBRTMP_LIBRARY`: Absolute path to `librtmp` library. -# -# Defines: -# -# - `LIBRTMP_FOUND`: System has librtmp. -# - `LIBRTMP_VERSION`: Version of librtmp. -# - `CURL::librtmp`: librtmp library target. - -set(_librtmp_pc_requires "librtmp") - -if(CURL_USE_PKGCONFIG AND - NOT DEFINED LIBRTMP_INCLUDE_DIR AND - NOT DEFINED LIBRTMP_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_librtmp ${_librtmp_pc_requires}) -endif() - -if(_librtmp_FOUND AND _librtmp_INCLUDE_DIRS) - set(Librtmp_FOUND TRUE) - set(LIBRTMP_FOUND TRUE) - set(LIBRTMP_VERSION ${_librtmp_VERSION}) - message(STATUS "Found Librtmp (via pkg-config): ${_librtmp_INCLUDE_DIRS} (found version \"${LIBRTMP_VERSION}\")") -else() - find_path(LIBRTMP_INCLUDE_DIR NAMES "librtmp/rtmp.h") - find_library(LIBRTMP_LIBRARY NAMES "rtmp") - - unset(LIBRTMP_VERSION CACHE) - if(LIBRTMP_INCLUDE_DIR AND EXISTS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h") - set(_version_regex "#[\t ]*define[\t ]+RTMP_LIB_VERSION[\t ]+0x([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F]).*") - file(STRINGS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str1 "${_version_str}") - string(REGEX REPLACE "${_version_regex}" "\\2" _version_str2 "${_version_str}") - if(CMAKE_VERSION VERSION_LESS 3.13) - # No support for hex version numbers, just strip leading zeroes - string(REGEX REPLACE "^0" "" _version_str1 "${_version_str1}") - string(REGEX REPLACE "^0" "" _version_str2 "${_version_str2}") - else() - math(EXPR _version_str1 "0x${_version_str1}" OUTPUT_FORMAT DECIMAL) - math(EXPR _version_str2 "0x${_version_str2}" OUTPUT_FORMAT DECIMAL) - endif() - set(LIBRTMP_VERSION "${_version_str1}.${_version_str2}") - unset(_version_regex) - unset(_version_str1) - unset(_version_str2) - endif() - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Librtmp - REQUIRED_VARS - LIBRTMP_INCLUDE_DIR - LIBRTMP_LIBRARY - VERSION_VAR - LIBRTMP_VERSION - ) - - if(LIBRTMP_FOUND) - set(_librtmp_INCLUDE_DIRS ${LIBRTMP_INCLUDE_DIR}) - set(_librtmp_LIBRARIES ${LIBRTMP_LIBRARY}) - endif() - - mark_as_advanced(LIBRTMP_INCLUDE_DIR LIBRTMP_LIBRARY) - - # Necessary when linking a static librtmp - find_package(OpenSSL) - if(OpenSSL_FOUND) - list(APPEND _librtmp_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) - endif() -endif() - -if(LIBRTMP_FOUND) - if(WIN32) - list(APPEND _librtmp_LIBRARIES "winmm") - endif() - - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_librtmp_LIBRARY_DIRS}) - endif() - - if(NOT TARGET CURL::librtmp) - add_library(CURL::librtmp INTERFACE IMPORTED) - set_target_properties(CURL::librtmp PROPERTIES - INTERFACE_LIBCURL_PC_MODULES "${_librtmp_pc_requires}" - INTERFACE_COMPILE_OPTIONS "${_librtmp_CFLAGS}" - INTERFACE_INCLUDE_DIRECTORIES "${_librtmp_INCLUDE_DIRS}" - INTERFACE_LINK_DIRECTORIES "${_librtmp_LIBRARY_DIRS}" - INTERFACE_LINK_LIBRARIES "${_librtmp_LIBRARIES}") - endif() -endif()
diff --git a/Utilities/cmcurl/CMake/FindLibssh.cmake b/Utilities/cmcurl/CMake/FindLibssh.cmake index ad1248f..3837d4d 100644 --- a/Utilities/cmcurl/CMake/FindLibssh.cmake +++ b/Utilities/cmcurl/CMake/FindLibssh.cmake
@@ -25,14 +25,15 @@ # # Input variables: # -# - `LIBSSH_INCLUDE_DIR`: Absolute path to libssh include directory. -# - `LIBSSH_LIBRARY`: Absolute path to `libssh` library. +# - `LIBSSH_INCLUDE_DIR`: Absolute path to libssh include directory. +# - `LIBSSH_LIBRARY`: Absolute path to `libssh` library. +# - `LIBSSH_USE_STATIC_LIBS`: Configure for static libssh libraries. # # Defines: # -# - `LIBSSH_FOUND`: System has libssh. -# - `LIBSSH_VERSION`: Version of libssh. -# - `CURL::libssh`: libssh library target. +# - `LIBSSH_FOUND`: System has libssh. +# - `LIBSSH_VERSION`: Version of libssh. +# - `CURL::libssh`: libssh library target. set(_libssh_pc_requires "libssh") @@ -47,10 +48,21 @@ set(Libssh_FOUND TRUE) set(LIBSSH_FOUND TRUE) set(LIBSSH_VERSION ${_libssh_VERSION}) + if(LIBSSH_USE_STATIC_LIBS) + set(_libssh_CFLAGS "${_libssh_STATIC_CFLAGS}") + set(_libssh_INCLUDE_DIRS "${_libssh_STATIC_INCLUDE_DIRS}") + set(_libssh_LIBRARY_DIRS "${_libssh_STATIC_LIBRARY_DIRS}") + set(_libssh_LIBRARIES "${_libssh_STATIC_LIBRARIES}") + endif() message(STATUS "Found Libssh (via pkg-config): ${_libssh_INCLUDE_DIRS} (found version \"${LIBSSH_VERSION}\")") else() find_path(LIBSSH_INCLUDE_DIR NAMES "libssh/libssh.h") - find_library(LIBSSH_LIBRARY NAMES "ssh" "libssh") + if(LIBSSH_USE_STATIC_LIBS) + set(_libssh_CFLAGS "-DLIBSSH_STATIC") + find_library(LIBSSH_LIBRARY NAMES "ssh_static" "libssh_static" "ssh" "libssh") + else() + find_library(LIBSSH_LIBRARY NAMES "ssh" "libssh") + endif() unset(LIBSSH_VERSION CACHE) if(LIBSSH_INCLUDE_DIR AND EXISTS "${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h") @@ -94,10 +106,6 @@ list(APPEND _libssh_LIBRARIES "iphlpapi") # for if_nametoindex endif() - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libssh_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libssh) add_library(CURL::libssh INTERFACE IMPORTED) set_target_properties(CURL::libssh PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibssh2.cmake b/Utilities/cmcurl/CMake/FindLibssh2.cmake index 330611b..22481d6 100644 --- a/Utilities/cmcurl/CMake/FindLibssh2.cmake +++ b/Utilities/cmcurl/CMake/FindLibssh2.cmake
@@ -25,32 +25,57 @@ # # Input variables: # -# - `LIBSSH2_INCLUDE_DIR`: Absolute path to libssh2 include directory. -# - `LIBSSH2_LIBRARY`: Absolute path to `libssh2` library. +# - `LIBSSH2_INCLUDE_DIR`: Absolute path to libssh2 include directory. +# - `LIBSSH2_LIBRARY`: Absolute path to `libssh2` library. +# - `LIBSSH2_USE_STATIC_LIBS`: Configure for static libssh2 libraries. # # Defines: # -# - `LIBSSH2_FOUND`: System has libssh2. -# - `LIBSSH2_VERSION`: Version of libssh2. -# - `CURL::libssh2`: libssh2 library target. +# - `LIBSSH2_FOUND`: System has libssh2. +# - `LIBSSH2_VERSION`: Version of libssh2. +# - `CURL::libssh2`: libssh2 library target. set(_libssh2_pc_requires "libssh2") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED LIBSSH2_INCLUDE_DIR AND +if(NOT DEFINED LIBSSH2_INCLUDE_DIR AND NOT DEFINED LIBSSH2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_libssh2 ${_libssh2_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_libssh2 ${_libssh2_pc_requires}) + endif() + if(NOT _libssh2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(libssh2 CONFIG QUIET) + endif() endif() if(_libssh2_FOUND AND _libssh2_INCLUDE_DIRS) set(Libssh2_FOUND TRUE) set(LIBSSH2_FOUND TRUE) set(LIBSSH2_VERSION ${_libssh2_VERSION}) + if(LIBSSH2_USE_STATIC_LIBS) + set(_libssh2_CFLAGS "${_libssh2_STATIC_CFLAGS}") + set(_libssh2_INCLUDE_DIRS "${_libssh2_STATIC_INCLUDE_DIRS}") + set(_libssh2_LIBRARY_DIRS "${_libssh2_STATIC_LIBRARY_DIRS}") + set(_libssh2_LIBRARIES "${_libssh2_STATIC_LIBRARIES}") + endif() message(STATUS "Found Libssh2 (via pkg-config): ${_libssh2_INCLUDE_DIRS} (found version \"${LIBSSH2_VERSION}\")") +elseif(libssh2_CONFIG) + set(Libssh2_FOUND TRUE) + set(LIBSSH2_FOUND TRUE) + set(LIBSSH2_VERSION ${libssh2_VERSION}) + if(LIBSSH2_USE_STATIC_LIBS) + set(_libssh2_LIBRARIES libssh2::libssh2_static) + else() + set(_libssh2_LIBRARIES libssh2::libssh2) + endif() + message(STATUS "Found Libssh2 (via CMake Config): ${libssh2_CONFIG} (found version \"${LIBSSH2_VERSION}\")") else() find_path(LIBSSH2_INCLUDE_DIR NAMES "libssh2.h") - find_library(LIBSSH2_LIBRARY NAMES "ssh2" "libssh2") + if(LIBSSH2_USE_STATIC_LIBS) + find_library(LIBSSH2_LIBRARY NAMES "ssh2_static" "libssh2_static" "ssh2" "libssh2") + else() + find_library(LIBSSH2_LIBRARY NAMES "ssh2" "libssh2") + endif() unset(LIBSSH2_VERSION CACHE) if(LIBSSH2_INCLUDE_DIR AND EXISTS "${LIBSSH2_INCLUDE_DIR}/libssh2.h") @@ -80,10 +105,6 @@ endif() if(LIBSSH2_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libssh2_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libssh2) add_library(CURL::libssh2 INTERFACE IMPORTED) set_target_properties(CURL::libssh2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibuv.cmake b/Utilities/cmcurl/CMake/FindLibuv.cmake index f9d614b..47d043c 100644 --- a/Utilities/cmcurl/CMake/FindLibuv.cmake +++ b/Utilities/cmcurl/CMake/FindLibuv.cmake
@@ -90,10 +90,6 @@ endif() if(LIBUV_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libuv_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libuv) add_library(CURL::libuv INTERFACE IMPORTED) set_target_properties(CURL::libuv PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake index 97201ab..21a5f4a 100644 --- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake +++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
@@ -25,16 +25,17 @@ # # Input variables: # -# - `MBEDTLS_INCLUDE_DIR`: Absolute path to mbedTLS include directory. -# - `MBEDTLS_LIBRARY`: Absolute path to `mbedtls` library. -# - `MBEDX509_LIBRARY`: Absolute path to `mbedx509` library. -# - `MBEDCRYPTO_LIBRARY`: Absolute path to `mbedcrypto` library. +# - `MBEDTLS_INCLUDE_DIR`: Absolute path to mbedTLS include directory. +# - `MBEDTLS_LIBRARY`: Absolute path to `mbedtls` library. +# - `MBEDX509_LIBRARY`: Absolute path to `mbedx509` library. +# - `MBEDCRYPTO_LIBRARY`: Absolute path to `mbedcrypto` library. +# - `MBEDTLS_USE_STATIC_LIBS`: Configure for static mbedTLS libraries. # # Defines: # -# - `MBEDTLS_FOUND`: System has mbedTLS. -# - `MBEDTLS_VERSION`: Version of mbedTLS. -# - `CURL::mbedtls`: mbedTLS library target. +# - `MBEDTLS_FOUND`: System has mbedTLS. +# - `MBEDTLS_VERSION`: Version of mbedTLS. +# - `CURL::mbedtls`: mbedTLS library target. if(DEFINED MBEDTLS_INCLUDE_DIRS AND NOT DEFINED MBEDTLS_INCLUDE_DIR) message(WARNING "MBEDTLS_INCLUDE_DIRS is deprecated, use MBEDTLS_INCLUDE_DIR instead.") @@ -44,27 +45,55 @@ set(_mbedtls_pc_requires "mbedtls" "mbedx509" "mbedcrypto") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED MBEDTLS_INCLUDE_DIR AND +if(NOT DEFINED MBEDTLS_INCLUDE_DIR AND NOT DEFINED MBEDTLS_LIBRARY AND NOT DEFINED MBEDX509_LIBRARY AND NOT DEFINED MBEDCRYPTO_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_mbedtls ${_mbedtls_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_mbedtls ${_mbedtls_pc_requires}) + endif() + if(NOT _mbedtls_FOUND AND CURL_USE_CMAKECONFIG) + find_package(MbedTLS CONFIG QUIET) + endif() endif() if(_mbedtls_FOUND) set(MbedTLS_FOUND TRUE) set(MBEDTLS_FOUND TRUE) set(MBEDTLS_VERSION ${_mbedtls_mbedtls_VERSION}) + if(MBEDTLS_USE_STATIC_LIBS) + set(_mbedtls_CFLAGS "${_mbedtls_STATIC_CFLAGS}") + set(_mbedtls_INCLUDE_DIRS "${_mbedtls_STATIC_INCLUDE_DIRS}") + set(_mbedtls_LIBRARY_DIRS "${_mbedtls_STATIC_LIBRARY_DIRS}") + set(_mbedtls_LIBRARIES "${_mbedtls_STATIC_LIBRARIES}") + endif() message(STATUS "Found MbedTLS (via pkg-config): ${_mbedtls_INCLUDE_DIRS} (found version \"${MBEDTLS_VERSION}\")") +elseif(MbedTLS_CONFIG) + set(MbedTLS_FOUND TRUE) + set(MBEDTLS_FOUND TRUE) + set(MBEDTLS_VERSION ${MbedTLS_VERSION}) + if(MBEDTLS_VERSION GREATER_EQUAL 4.0.0) + set(_mbedtls_LIBRARIES MbedTLS::tfpsacrypto) + else() + set(_mbedtls_LIBRARIES MbedTLS::mbedcrypto) + endif() + list(APPEND _mbedtls_LIBRARIES MbedTLS::mbedx509 MbedTLS::mbedtls) + message(STATUS "Found MbedTLS (via CMake Config): ${MbedTLS_CONFIG} (found version \"${MBEDTLS_VERSION}\")") else() set(_mbedtls_pc_requires "") # Depend on pkg-config only when found via pkg-config find_path(MBEDTLS_INCLUDE_DIR NAMES "mbedtls/ssl.h") - find_library(MBEDTLS_LIBRARY NAMES "mbedtls" "libmbedtls") - find_library(MBEDX509_LIBRARY NAMES "mbedx509" "libmbedx509") - find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" "libmbedcrypto") + if(MBEDTLS_USE_STATIC_LIBS) + find_library(MBEDTLS_LIBRARY NAMES "mbedtls_static" "libmbedtls_static" "mbedtls" "libmbedtls") + find_library(MBEDX509_LIBRARY NAMES "mbedx509_static" "libmbedx509_static" "mbedx509" "libmbedx509") + find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto_static" "libmbedcrypto_static" "mbedcrypto" "libmbedcrypto" + "tfpsacrypto_static" "libtfpsacrypto_static" "tfpsacrypto" "libtfpsacrypto") + else() + find_library(MBEDTLS_LIBRARY NAMES "mbedtls" "libmbedtls") + find_library(MBEDX509_LIBRARY NAMES "mbedx509" "libmbedx509") + find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" "libmbedcrypto" "tfpsacrypto" "libtfpsacrypto") + endif() unset(MBEDTLS_VERSION CACHE) if(MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") @@ -96,10 +125,6 @@ endif() if(MBEDTLS_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_mbedtls_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::mbedtls) add_library(CURL::mbedtls INTERFACE IMPORTED) set_target_properties(CURL::mbedtls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake index 8304345..f93113f 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
@@ -25,31 +25,56 @@ # # Input variables: # -# - `NGHTTP2_INCLUDE_DIR`: Absolute path to nghttp2 include directory. -# - `NGHTTP2_LIBRARY`: Absolute path to `nghttp2` library. +# - `NGHTTP2_INCLUDE_DIR`: Absolute path to nghttp2 include directory. +# - `NGHTTP2_LIBRARY`: Absolute path to `nghttp2` library. +# - `NGHTTP2_USE_STATIC_LIBS`: Configure for static nghttp2 libraries. # # Defines: # -# - `NGHTTP2_FOUND`: System has nghttp2. -# - `NGHTTP2_VERSION`: Version of nghttp2. -# - `CURL::nghttp2`: nghttp2 library target. +# - `NGHTTP2_FOUND`: System has nghttp2. +# - `NGHTTP2_VERSION`: Version of nghttp2. +# - `CURL::nghttp2`: nghttp2 library target. set(_nghttp2_pc_requires "libnghttp2") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGHTTP2_INCLUDE_DIR AND +if(NOT DEFINED NGHTTP2_INCLUDE_DIR AND NOT DEFINED NGHTTP2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_nghttp2 ${_nghttp2_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_nghttp2 ${_nghttp2_pc_requires}) + endif() + if(NOT _nghttp2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(nghttp2 CONFIG QUIET) + endif() endif() if(_nghttp2_FOUND) set(NGHTTP2_FOUND TRUE) set(NGHTTP2_VERSION ${_nghttp2_VERSION}) + if(NGHTTP2_USE_STATIC_LIBS) + set(_nghttp2_CFLAGS "${_nghttp2_STATIC_CFLAGS}") + set(_nghttp2_INCLUDE_DIRS "${_nghttp2_STATIC_INCLUDE_DIRS}") + set(_nghttp2_LIBRARY_DIRS "${_nghttp2_STATIC_LIBRARY_DIRS}") + set(_nghttp2_LIBRARIES "${_nghttp2_STATIC_LIBRARIES}") + endif() message(STATUS "Found NGHTTP2 (via pkg-config): ${_nghttp2_INCLUDE_DIRS} (found version \"${NGHTTP2_VERSION}\")") +elseif(nghttp2_CONFIG) + set(NGHTTP2_FOUND TRUE) + set(NGHTTP2_VERSION ${nghttp2_VERSION}) + if(NGHTTP2_USE_STATIC_LIBS) + set(_nghttp2_LIBRARIES nghttp2::nghttp2_static) + else() + set(_nghttp2_LIBRARIES nghttp2::nghttp2) + endif() + message(STATUS "Found NGHTTP2 (via CMake Config): ${nghttp2_CONFIG} (found version \"${NGHTTP2_VERSION}\")") else() find_path(NGHTTP2_INCLUDE_DIR NAMES "nghttp2/nghttp2.h") - find_library(NGHTTP2_LIBRARY NAMES "nghttp2" "nghttp2_static") + if(NGHTTP2_USE_STATIC_LIBS) + set(_nghttp2_CFLAGS "-DNGHTTP2_STATICLIB") + find_library(NGHTTP2_LIBRARY NAMES "nghttp2_static" "nghttp2") + else() + find_library(NGHTTP2_LIBRARY NAMES "nghttp2" "nghttp2_static") + endif() unset(NGHTTP2_VERSION CACHE) if(NGHTTP2_INCLUDE_DIR AND EXISTS "${NGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2ver.h") @@ -79,10 +104,6 @@ endif() if(NGHTTP2_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_nghttp2_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::nghttp2) add_library(CURL::nghttp2 INTERFACE IMPORTED) set_target_properties(CURL::nghttp2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake index 37ebfe1..427c139 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
@@ -25,31 +25,56 @@ # # Input variables: # -# - `NGHTTP3_INCLUDE_DIR`: Absolute path to nghttp3 include directory. -# - `NGHTTP3_LIBRARY`: Absolute path to `nghttp3` library. +# - `NGHTTP3_INCLUDE_DIR`: Absolute path to nghttp3 include directory. +# - `NGHTTP3_LIBRARY`: Absolute path to `nghttp3` library. +# - `NGHTTP3_USE_STATIC_LIBS`: Configure for static nghttp3 libraries. # # Defines: # -# - `NGHTTP3_FOUND`: System has nghttp3. -# - `NGHTTP3_VERSION`: Version of nghttp3. -# - `CURL::nghttp3`: nghttp3 library target. +# - `NGHTTP3_FOUND`: System has nghttp3. +# - `NGHTTP3_VERSION`: Version of nghttp3. +# - `CURL::nghttp3`: nghttp3 library target. set(_nghttp3_pc_requires "libnghttp3") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGHTTP3_INCLUDE_DIR AND +if(NOT DEFINED NGHTTP3_INCLUDE_DIR AND NOT DEFINED NGHTTP3_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_nghttp3 ${_nghttp3_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_nghttp3 ${_nghttp3_pc_requires}) + endif() + if(NOT _nghttp3_FOUND AND CURL_USE_CMAKECONFIG) + find_package(nghttp3 CONFIG QUIET) + endif() endif() if(_nghttp3_FOUND) set(NGHTTP3_FOUND TRUE) set(NGHTTP3_VERSION ${_nghttp3_VERSION}) + if(NGHTTP3_USE_STATIC_LIBS) + set(_nghttp3_CFLAGS "${_nghttp3_STATIC_CFLAGS}") + set(_nghttp3_INCLUDE_DIRS "${_nghttp3_STATIC_INCLUDE_DIRS}") + set(_nghttp3_LIBRARY_DIRS "${_nghttp3_STATIC_LIBRARY_DIRS}") + set(_nghttp3_LIBRARIES "${_nghttp3_STATIC_LIBRARIES}") + endif() message(STATUS "Found NGHTTP3 (via pkg-config): ${_nghttp3_INCLUDE_DIRS} (found version \"${NGHTTP3_VERSION}\")") +elseif(nghttp3_CONFIG) + set(NGHTTP3_FOUND TRUE) + set(NGHTTP3_VERSION ${nghttp3_VERSION}) + if(NGHTTP3_USE_STATIC_LIBS) + set(_nghttp3_LIBRARIES nghttp3::nghttp3_static) + else() + set(_nghttp3_LIBRARIES nghttp3::nghttp3) + endif() + message(STATUS "Found NGHTTP3 (via CMake Config): ${nghttp3_CONFIG} (found version \"${NGHTTP3_VERSION}\")") else() find_path(NGHTTP3_INCLUDE_DIR NAMES "nghttp3/nghttp3.h") - find_library(NGHTTP3_LIBRARY NAMES "nghttp3") + if(NGHTTP3_USE_STATIC_LIBS) + set(_nghttp3_CFLAGS "-DNGHTTP3_STATICLIB") + find_library(NGHTTP3_LIBRARY NAMES "nghttp3_static" "nghttp3") + else() + find_library(NGHTTP3_LIBRARY NAMES "nghttp3") + endif() unset(NGHTTP3_VERSION CACHE) if(NGHTTP3_INCLUDE_DIR AND EXISTS "${NGHTTP3_INCLUDE_DIR}/nghttp3/version.h") @@ -79,10 +104,6 @@ endif() if(NGHTTP3_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_nghttp3_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::nghttp3) add_library(CURL::nghttp3 INTERFACE IMPORTED) set_target_properties(CURL::nghttp3 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake index 416ea45..e492916 100644 --- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
@@ -43,6 +43,7 @@ # - `NGTCP2_CRYPTO_OSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_ossl` library. # - `NGTCP2_CRYPTO_QUICTLS_LIBRARY`: Absolute path to `ngtcp2_crypto_quictls` library. # - `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_wolfssl` library. +# - `NGTCP2_USE_STATIC_LIBS`: Configure for static ngtcp2 libraries. # # Defines: # @@ -73,21 +74,51 @@ endif() set(_tried_pkgconfig FALSE) -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGTCP2_INCLUDE_DIR AND +if(NOT DEFINED NGTCP2_INCLUDE_DIR AND NOT DEFINED NGTCP2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires}) - set(_tried_pkgconfig TRUE) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires}) + set(_tried_pkgconfig TRUE) + endif() + if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(ngtcp2 CONFIG QUIET) + # Skip using it if the crypto library target is not available + if(ngtcp2_CONFIG AND + NOT TARGET ngtcp2::${_crypto_library_lower}_static AND + NOT TARGET ngtcp2::${_crypto_library_lower}) + unset(ngtcp2_CONFIG) + endif() + endif() endif() if(_ngtcp2_FOUND) set(NGTCP2_FOUND TRUE) set(NGTCP2_VERSION ${_ngtcp2_libngtcp2_VERSION}) + if(NGTCP2_USE_STATIC_LIBS) + set(_ngtcp2_CFLAGS "${_ngtcp2_STATIC_CFLAGS}") + set(_ngtcp2_INCLUDE_DIRS "${_ngtcp2_STATIC_INCLUDE_DIRS}") + set(_ngtcp2_LIBRARY_DIRS "${_ngtcp2_STATIC_LIBRARY_DIRS}") + set(_ngtcp2_LIBRARIES "${_ngtcp2_STATIC_LIBRARIES}") + endif() message(STATUS "Found NGTCP2 (via pkg-config): ${_ngtcp2_INCLUDE_DIRS} (found version \"${NGTCP2_VERSION}\")") +elseif(ngtcp2_CONFIG) + set(NGTCP2_FOUND TRUE) + set(NGTCP2_VERSION ${ngtcp2_VERSION}) + if(NGTCP2_USE_STATIC_LIBS) + set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2_static ngtcp2::${_crypto_library_lower}_static) + else() + set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2 ngtcp2::${_crypto_library_lower}) + endif() + message(STATUS "Found NGTCP2 (via CMake Config): ${ngtcp2_CONFIG} (found version \"${NGTCP2_VERSION}\")") else() find_path(NGTCP2_INCLUDE_DIR NAMES "ngtcp2/ngtcp2.h") - find_library(NGTCP2_LIBRARY NAMES "ngtcp2") + if(NGTCP2_USE_STATIC_LIBS) + set(_ngtcp2_CFLAGS "-DNGTCP2_STATICLIB") + find_library(NGTCP2_LIBRARY NAMES "ngtcp2_static" "ngtcp2") + else() + find_library(NGTCP2_LIBRARY NAMES "ngtcp2") + endif() unset(NGTCP2_VERSION CACHE) if(NGTCP2_INCLUDE_DIR AND EXISTS "${NGTCP2_INCLUDE_DIR}/ngtcp2/version.h") @@ -105,7 +136,13 @@ else() get_filename_component(_ngtcp2_library_dir "${NGTCP2_LIBRARY}" DIRECTORY) endif() - find_library(${_crypto_library_upper}_LIBRARY NAMES ${_crypto_library_lower} HINTS ${_ngtcp2_library_dir}) + if(NGTCP2_USE_STATIC_LIBS) + find_library(${_crypto_library_upper}_LIBRARY NAMES ${_crypto_library_lower}_static ${_crypto_library_lower} + HINTS ${_ngtcp2_library_dir}) + else() + find_library(${_crypto_library_upper}_LIBRARY NAMES ${_crypto_library_lower} + HINTS ${_ngtcp2_library_dir}) + endif() if(${_crypto_library_upper}_LIBRARY) set(NGTCP2_${_ngtcp2_crypto_backend}_FOUND TRUE) @@ -137,10 +174,6 @@ endif() if(NGTCP2_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_ngtcp2_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::ngtcp2) add_library(CURL::ngtcp2 INTERFACE IMPORTED) set_target_properties(CURL::ngtcp2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNettle.cmake b/Utilities/cmcurl/CMake/FindNettle.cmake index d22865f..c963180 100644 --- a/Utilities/cmcurl/CMake/FindNettle.cmake +++ b/Utilities/cmcurl/CMake/FindNettle.cmake
@@ -85,10 +85,6 @@ endif() if(NETTLE_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_nettle_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::nettle) add_library(CURL::nettle INTERFACE IMPORTED) set_target_properties(CURL::nettle PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindQuiche.cmake b/Utilities/cmcurl/CMake/FindQuiche.cmake index 22482d7..137679b 100644 --- a/Utilities/cmcurl/CMake/FindQuiche.cmake +++ b/Utilities/cmcurl/CMake/FindQuiche.cmake
@@ -68,10 +68,6 @@ endif() if(QUICHE_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_quiche_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::quiche) add_library(CURL::quiche INTERFACE IMPORTED) set_target_properties(CURL::quiche PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindRustls.cmake b/Utilities/cmcurl/CMake/FindRustls.cmake index 270e99c..1e07565 100644 --- a/Utilities/cmcurl/CMake/FindRustls.cmake +++ b/Utilities/cmcurl/CMake/FindRustls.cmake
@@ -104,10 +104,6 @@ mark_as_advanced(MATH_LIBRARY) endif() - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_rustls_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::rustls) add_library(CURL::rustls INTERFACE IMPORTED) set_target_properties(CURL::rustls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake index 3a3c05f..964320a 100644 --- a/Utilities/cmcurl/CMake/FindWolfSSL.cmake +++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
@@ -45,11 +45,15 @@ set(_wolfssl_pc_requires "wolfssl") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED WOLFSSL_INCLUDE_DIR AND +if(NOT DEFINED WOLFSSL_INCLUDE_DIR AND NOT DEFINED WOLFSSL_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_wolfssl ${_wolfssl_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_wolfssl ${_wolfssl_pc_requires}) + endif() + if(NOT _wolfssl_FOUND AND CURL_USE_CMAKECONFIG) + find_package(wolfssl CONFIG QUIET) + endif() endif() if(_wolfssl_FOUND) @@ -57,6 +61,12 @@ set(WOLFSSL_FOUND TRUE) set(WOLFSSL_VERSION ${_wolfssl_VERSION}) message(STATUS "Found WolfSSL (via pkg-config): ${_wolfssl_INCLUDE_DIRS} (found version \"${WOLFSSL_VERSION}\")") +elseif(wolfssl_CONFIG) + set(WolfSSL_FOUND TRUE) + set(WOLFSSL_FOUND TRUE) + set(WOLFSSL_VERSION ${wolfssl_VERSION}) + set(_wolfssl_LIBRARIES wolfssl::wolfssl) + message(STATUS "Found WolfSSL (via CMake Config): ${wolfssl_CONFIG} (found version \"${WOLFSSL_VERSION}\")") else() find_path(WOLFSSL_INCLUDE_DIR NAMES "wolfssl/ssl.h") find_library(WOLFSSL_LIBRARY NAMES "wolfssl") @@ -113,10 +123,6 @@ mark_as_advanced(MATH_LIBRARY) endif() - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_wolfssl_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::wolfssl) add_library(CURL::wolfssl INTERFACE IMPORTED) set_target_properties(CURL::wolfssl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake index 954a827..8dc620a 100644 --- a/Utilities/cmcurl/CMake/FindZstd.cmake +++ b/Utilities/cmcurl/CMake/FindZstd.cmake
@@ -25,14 +25,15 @@ # # Input variables: # -# - `ZSTD_INCLUDE_DIR`: Absolute path to zstd include directory. -# - `ZSTD_LIBRARY`: Absolute path to `zstd` library. +# - `ZSTD_INCLUDE_DIR`: Absolute path to zstd include directory. +# - `ZSTD_LIBRARY`: Absolute path to `zstd` library. +# - `ZSTD_USE_STATIC_LIBS`: Configure for static zstd libraries. # # Defines: # -# - `ZSTD_FOUND`: System has zstd. -# - `ZSTD_VERSION`: Version of zstd. -# - `CURL::zstd`: zstd library target. +# - `ZSTD_FOUND`: System has zstd. +# - `ZSTD_VERSION`: Version of zstd. +# - `CURL::zstd`: zstd library target. if(DEFINED Zstd_INCLUDE_DIR AND NOT DEFINED ZSTD_INCLUDE_DIR) message(WARNING "Zstd_INCLUDE_DIR is deprecated, use ZSTD_INCLUDE_DIR instead.") @@ -45,21 +46,52 @@ set(_zstd_pc_requires "libzstd") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED ZSTD_INCLUDE_DIR AND +if(NOT DEFINED ZSTD_INCLUDE_DIR AND NOT DEFINED ZSTD_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_zstd ${_zstd_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_zstd ${_zstd_pc_requires}) + endif() + if(NOT _zstd_FOUND AND CURL_USE_CMAKECONFIG) + find_package(Zstd CONFIG QUIET) + # Skip using if older than v1.4.5 + if(Zstd_CONFIG AND + NOT TARGET zstd::libzstd_static AND + NOT TARGET zstd::libzstd_shared) + unset(Zstd_CONFIG) + endif() + endif() endif() if(_zstd_FOUND) set(Zstd_FOUND TRUE) set(ZSTD_FOUND TRUE) set(ZSTD_VERSION ${_zstd_VERSION}) + if(ZSTD_USE_STATIC_LIBS) + set(_zstd_CFLAGS "${_zstd_STATIC_CFLAGS}") + set(_zstd_INCLUDE_DIRS "${_zstd_STATIC_INCLUDE_DIRS}") + set(_zstd_LIBRARY_DIRS "${_zstd_STATIC_LIBRARY_DIRS}") + set(_zstd_LIBRARIES "${_zstd_STATIC_LIBRARIES}") + endif() message(STATUS "Found Zstd (via pkg-config): ${_zstd_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")") +elseif(Zstd_CONFIG) + set(ZSTD_FOUND TRUE) + set(ZSTD_VERSION ${Zstd_VERSION}) + if(ZSTD_USE_STATIC_LIBS) + set(_zstd_LIBRARIES zstd::libzstd_static) + elseif(TARGET zstd::libzstd) + set(_zstd_LIBRARIES zstd::libzstd) # v1.5.6+ + else() + set(_zstd_LIBRARIES zstd::libzstd_shared) + endif() + message(STATUS "Found Zstd (via CMake Config): ${Zstd_CONFIG} (found version \"${ZSTD_VERSION}\")") else() find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h") - find_library(ZSTD_LIBRARY NAMES "zstd") + if(ZSTD_USE_STATIC_LIBS) + find_library(ZSTD_LIBRARY NAMES "zstd_static" "zstd") + else() + find_library(ZSTD_LIBRARY NAMES "zstd") + endif() unset(ZSTD_VERSION CACHE) if(ZSTD_INCLUDE_DIR AND EXISTS "${ZSTD_INCLUDE_DIR}/zstd.h") @@ -99,10 +131,6 @@ endif() if(ZSTD_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_zstd_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::zstd) add_library(CURL::zstd INTERFACE IMPORTED) set_target_properties(CURL::zstd PROPERTIES
diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake index ca63301..5e26c38 100644 --- a/Utilities/cmcurl/CMake/Macros.cmake +++ b/Utilities/cmcurl/CMake/Macros.cmake
@@ -40,20 +40,12 @@ # Return result in variable: CURL_TEST_OUTPUT macro(curl_internal_test _curl_test) if(NOT DEFINED "${_curl_test}") - string(REPLACE ";" " " _cmake_required_definitions "${CMAKE_REQUIRED_DEFINITIONS}") - set(_curl_test_add_libraries "") - if(CMAKE_REQUIRED_LIBRARIES) - set(_curl_test_add_libraries - "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") - endif() - message(STATUS "Performing Test ${_curl_test}") try_compile(${_curl_test} ${PROJECT_BINARY_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c" - CMAKE_FLAGS - "-DCOMPILE_DEFINITIONS:STRING=-D${_curl_test} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${_cmake_required_definitions}" - "${_curl_test_add_libraries}" + COMPILE_DEFINITIONS "-D${_curl_test}" ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}" OUTPUT_VARIABLE CURL_TEST_OUTPUT) if(${_curl_test}) set(${_curl_test} 1 CACHE INTERNAL "curl test") @@ -71,9 +63,17 @@ set_property(CACHE ${_option_name} PROPERTY STRINGS "AUTO" "ON" "OFF") if(${_option_name} STREQUAL "AUTO") - find_package(${_find_name}) + if(_find_name STREQUAL "ZLIB") + find_package(${_find_name}) + else() + find_package(${_find_name} MODULE) + endif() elseif(${_option_name}) - find_package(${_find_name} REQUIRED) + if(_find_name STREQUAL "ZLIB") + find_package(${_find_name} REQUIRED) + else() + find_package(${_find_name} MODULE REQUIRED) + endif() else() string(TOUPPER "${_find_name}" _find_name_upper) set(${_find_name}_FOUND OFF) # cmake-lint: disable=C0103 @@ -102,24 +102,24 @@ # Internal: Recurse into target libraries and collect their include directories # and macro definitions. -macro(curl_collect_target_options _target) - get_target_property(_val ${_target} INTERFACE_INCLUDE_DIRECTORIES) - if(_val) - list(APPEND _includes ${_val}) - endif() - get_target_property(_val ${_target} INCLUDE_DIRECTORIES) - if(_val) - list(APPEND _includes ${_val}) - endif() - get_target_property(_val ${_target} COMPILE_DEFINITIONS) +macro(curl_collect_target_compile_options _target) + get_target_property(_val ${_target} INTERFACE_COMPILE_DEFINITIONS) if(_val) list(APPEND _definitions ${_val}) endif() + get_target_property(_val ${_target} INTERFACE_INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _incsys ${_val}) + endif() + get_target_property(_val ${_target} INTERFACE_COMPILE_OPTIONS) + if(_val) + list(APPEND _options ${_val}) + endif() get_target_property(_val ${_target} LINK_LIBRARIES) if(_val) foreach(_lib IN LISTS _val) if(TARGET "${_lib}") - curl_collect_target_options(${_lib}) + curl_collect_target_compile_options(${_lib}) endif() endforeach() endif() @@ -127,35 +127,94 @@ endmacro() # Create a clang-tidy target for test targets -macro(curl_add_clang_tidy_test_target _target_clang_tidy _target) +function(curl_add_clang_tidy_test_target _target_clang_tidy _target) if(CURL_CLANG_TIDY) - set(_includes "") set(_definitions "") + set(_includes "") + set(_incsys "") + set(_options "") - # Collect header directories and macro definitions applying to the directory - get_directory_property(_val INCLUDE_DIRECTORIES) - if(_val) - list(APPEND _includes ${_val}) - endif() + # Make a list of known system include directories + set(_sys_incdirs "${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}") + foreach(_inc IN LISTS CMAKE_SYSTEM_PREFIX_PATH) + if(NOT _inc MATCHES "/$") + string(APPEND _inc "/") + endif() + string(APPEND _inc "include") + if(NOT _inc IN_LIST _sys_incdirs AND IS_DIRECTORY "${_inc}") + list(APPEND _sys_incdirs "${_inc}") + endif() + endforeach() + + # Collect options applying to the directory get_directory_property(_val COMPILE_DEFINITIONS) if(_val) list(APPEND _definitions ${_val}) endif() - unset(_val) + get_directory_property(_val INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _includes ${_val}) + endif() + get_directory_property(_val COMPILE_OPTIONS) + if(_val) + list(APPEND _options ${_val}) + endif() + + # Collect options applying to the target + get_target_property(_val ${_target} COMPILE_DEFINITIONS) + if(_val) + list(APPEND _definitions ${_val}) + endif() + get_target_property(_val ${_target} INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _includes ${_val}) + endif() + get_target_property(_val ${_target} COMPILE_OPTIONS) + if(_val) + list(APPEND _options ${_val}) + endif() # Collect header directories and macro definitions from lib dependencies - curl_collect_target_options(${_target}) - - list(REMOVE_ITEM _includes "") - string(REPLACE ";" ";-I" _includes ";${_includes}") - list(REMOVE_DUPLICATES _includes) + curl_collect_target_compile_options(${_target}) list(REMOVE_ITEM _definitions "") string(REPLACE ";" ";-D" _definitions ";${_definitions}") list(REMOVE_DUPLICATES _definitions) list(SORT _definitions) # Sort like CMake does + list(REMOVE_ITEM _includes "") + string(REPLACE ";" ";-I" _includes ";${_includes}") + list(REMOVE_DUPLICATES _includes) + + set(_incsys_tmp ${_incsys}) + list(REMOVE_DUPLICATES _incsys_tmp) + set(_incsys "") + set(_incsystop "") + foreach(_inc IN LISTS _incsys_tmp) + if(_inc IN_LIST _sys_incdirs) + list(APPEND _incsystop "${_inc}") # Save system prefixes to re-add them later to the end of list + continue() + endif() + # Avoid empty and '$<INSTALL_INTERFACE:include>' items. The latter + # evaluates to an empty path in this context. Also skip + # '$<BUILD_INTERFACE:curl-include>', as already present in '_includes'. + if(_inc AND + NOT _inc MATCHES "INSTALL_INTERFACE:" AND + NOT _inc MATCHES "BUILD_INTERFACE:") + list(APPEND _incsys "-isystem" "${_inc}") + endif() + endforeach() + foreach(_inc IN LISTS _incsystop) + list(APPEND _incsys "-isystem" "${_inc}") + endforeach() + + if(CMAKE_C_COMPILER_ID MATCHES "Clang") + list(REMOVE_DUPLICATES _options) # Keep the first of duplicates to imitate CMake + else() + set(_options) + endif() + # Assemble source list set(_sources "") foreach(_source IN ITEMS ${ARGN}) @@ -165,14 +224,55 @@ list(APPEND _sources "${_source}") endforeach() + set(_cc "${CMAKE_C_COMPILER}") + if(CMAKE_C_COMPILER_TARGET AND CMAKE_C_COMPILE_OPTIONS_TARGET) + list(APPEND _cc "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}") + endif() + if(APPLE AND CMAKE_OSX_SYSROOT) + list(APPEND _cc "-isysroot" "${CMAKE_OSX_SYSROOT}") + elseif(CMAKE_SYSROOT AND CMAKE_C_COMPILE_OPTIONS_SYSROOT) + list(APPEND _cc "${CMAKE_C_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") + endif() + + # Pass -clang-diagnostic-unused-function to disable -Wunused-function implied by -Wunused add_custom_target(${_target_clang_tidy} USES_TERMINAL WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND ${CMAKE_C_CLANG_TIDY} ${_sources} -- ${_includes} ${_definitions} + COMMAND ${CMAKE_C_CLANG_TIDY} + "--checks=-clang-diagnostic-unused-function" + ${_sources} -- ${_cc} ${_definitions} ${_includes} ${_incsys} ${_options} DEPENDS ${_sources}) add_dependencies(tests-clang-tidy ${_target_clang_tidy}) - - unset(_includes) - unset(_definitions) - unset(_sources) endif() +endfunction() + +# Internal: Recurse into interface targets and collect their libraries +# and library paths. +macro(curl_collect_target_link_options _target) + get_target_property(_val ${_target} INTERFACE_LINK_DIRECTORIES) + if(_val) + list(APPEND _libdirs ${_val}) + endif() + get_target_property(_val ${_target} IMPORTED) + if(_val) + # LOCATION is empty for interface library targets and safe to ignore. + # Explicitly skip this query to avoid CMake v3.18 and older erroring out. + get_target_property(_val ${_target} TYPE) + if(NOT "${_val}" STREQUAL "INTERFACE_LIBRARY") + get_target_property(_val ${_target} LOCATION) + if(_val) + list(APPEND _libs ${_val}) + endif() + endif() + endif() + get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES) + if(_val) + foreach(_lib IN LISTS _val) + if(TARGET "${_lib}") + curl_collect_target_link_options(${_lib}) + else() + list(APPEND _libs ${_lib}) + endif() + endforeach() + endif() + unset(_val) endmacro()
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake index 9f59e0d..6619f3a 100644 --- a/Utilities/cmcurl/CMake/OtherTests.cmake +++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -50,19 +50,6 @@ cmake_pop_check_state() endif() -if(NOT WIN32) - set(_source_epilogue "#undef inline") - curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h") - check_c_source_compiles("${_source_epilogue} - #include <sys/socket.h> - int main(void) - { - int flag = MSG_NOSIGNAL; - (void)flag; - return 0; - }" HAVE_MSG_NOSIGNAL) -endif() - set(_source_epilogue "#undef inline") check_c_source_compiles("${_source_epilogue} #ifdef _MSC_VER @@ -91,7 +78,7 @@ elseif(NOT HAVE_GETADDRINFO) set(HAVE_GETADDRINFO_THREADSAFE FALSE) elseif(APPLE OR - CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "HP-UX" OR CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
diff --git a/Utilities/cmcurl/CMake/PickyWarnings.cmake b/Utilities/cmcurl/CMake/PickyWarnings.cmake index 5e643c5..aca79ec 100644 --- a/Utilities/cmcurl/CMake/PickyWarnings.cmake +++ b/Utilities/cmcurl/CMake/PickyWarnings.cmake
@@ -60,7 +60,9 @@ endif() if(PICKY_COMPILER) - if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + # Leave disabled for GCC <4.6, because they lack #pragma features to silence locally. + if((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.6) OR + CMAKE_C_COMPILER_ID MATCHES "Clang") # https://clang.llvm.org/docs/DiagnosticsReference.html # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html @@ -117,7 +119,7 @@ -Waddress # clang 2.7 gcc 4.3 -Wattributes # clang 2.7 gcc 4.1 -Wcast-align # clang 1.0 gcc 4.2 - -Wcast-qual # clang 3.0 gcc 3.4.6 + -Wcast-qual # clang 2.7 gcc 3.4.6 -Wdeclaration-after-statement # clang 1.0 gcc 3.4 -Wdiv-by-zero # clang 2.7 gcc 4.1 -Wempty-body # clang 2.7 gcc 4.3 @@ -127,7 +129,6 @@ -Wignored-qualifiers # clang 2.8 gcc 4.3 -Wmissing-field-initializers # clang 2.7 gcc 4.1 -Wmissing-noreturn # clang 2.7 gcc 4.1 - -Wno-format-nonliteral # clang 1.0 gcc 2.96 (3.0) -Wno-padded # clang 2.9 gcc 4.1 # Not used: We cannot change public structs -Wno-sign-conversion # clang 2.9 gcc 4.3 -Wno-switch-default # clang 2.7 gcc 4.1 # Not used: Annoying to fix or silence @@ -148,10 +149,10 @@ list(APPEND _picky_enable ${_picky_common_old} -Wconditional-uninitialized # clang 3.0 - -Wno-used-but-marked-unused # clang 3.0 # Triggered by typecheck-gcc.h (with clang 14+) + -Wno-used-but-marked-unused # clang 2.9 # for typecheck-gcc.h with clang 14+, dependency headers -Wshift-sign-overflow # clang 2.9 -Wshorten-64-to-32 # clang 1.0 - -Wformat=2 # clang 3.0 gcc 4.8 + -Wformat=2 # clang 2.7 gcc 4.8 ) if(NOT MSVC) list(APPEND _picky_enable @@ -162,7 +163,7 @@ if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.1) list(APPEND _picky_enable -Wno-covered-switch-default # clang 3.1 appleclang 3.1 # Annoying to fix or silence - -Wno-disabled-macro-expansion # clang 3.1 appleclang 3.1 # Triggered by typecheck-gcc.h (with clang 14+) + -Wno-disabled-macro-expansion # clang 3.1 appleclang 3.1 # for std headers, and curl/curl.h (rare combos) ) if(MSVC) list(APPEND _picky_enable @@ -226,6 +227,14 @@ -Wno-reserved-macro-identifier # clang 13.0 appleclang 13.1 # External macros have to be set sometimes ) endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0.3)) + if(CMAKE_GENERATOR STREQUAL "FASTBuild") + list(APPEND _picky_enable + -Wno-gnu-line-marker # clang 15.0 appleclang 14.0.3 + ) + endif() + endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0)) list(APPEND _picky_enable @@ -244,20 +253,23 @@ -Wno-format-signedness # clang 19.1 gcc 5.1 appleclang 17.0 # In clang-cl enums are signed ints by default ) endif() - if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 21.1) + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 21.1) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 26.4)) list(APPEND _picky_enable - -Warray-compare # clang 20.1 gcc 12.0 appleclang ? - -Wc++-hidden-decl # clang 21.1 appleclang ? - -Wno-implicit-void-ptr-cast # clang 21.1 appleclang ? - -Wtentative-definition-compat # clang 21.1 appleclang ? + -Warray-compare # clang 20.1 gcc 12.0 appleclang 26.4 + -Wc++-hidden-decl # clang 21.1 appleclang 26.4 + -Wimplicit-int-enum-cast # clang 21.1 + -Wjump-misses-init # clang 21.1 gcc 4.5 appleclang 26.4 + -Wno-implicit-void-ptr-cast # clang 21.1 appleclang 26.4 + -Wtentative-definition-compat # clang 21.1 appleclang 26.4 ) if(WIN32) list(APPEND _picky_enable - -Wno-c++-keyword # clang 21.1 appleclang ? # `wchar_t` triggers it on Windows + -Wno-c++-keyword # clang 21.1 appleclang 26.4 # `wchar_t` triggers it on Windows ) else() list(APPEND _picky_enable - -Wc++-keyword # clang 21.1 appleclang ? + -Wc++-keyword # clang 21.1 appleclang 26.4 ) endif() endif() @@ -276,7 +288,7 @@ endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.5) list(APPEND _picky_enable - -Wjump-misses-init # gcc 4.5 + -Wjump-misses-init # clang 21.1 gcc 4.5 appleclang 26.4 ) if(MINGW) list(APPEND _picky_enable @@ -287,23 +299,23 @@ if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) list(APPEND _picky_enable -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.1 - -Wformat=2 # clang 3.0 gcc 4.8 + -Wformat=2 # clang 2.7 gcc 4.8 -Wtrampolines # gcc 4.6 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0) list(APPEND _picky_enable - -Warray-bounds=2 # clang 3.0 gcc 5.0 (clang default: -Warray-bounds) + -Warray-bounds=2 # clang 2.9 gcc 5.0 (clang default: -Warray-bounds) -Wno-format-signedness # clang 19.1 gcc 5.1 appleclang 17.0 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) list(APPEND _picky_enable -Wduplicated-cond # gcc 6.0 - -Wnull-dereference # clang 3.0 gcc 6.0 (clang default) + -Wnull-dereference # clang 2.9 gcc 6.0 (clang default) -fdelete-null-pointer-checks -Wshift-negative-value # clang 3.7 gcc 6.0 (clang default) - -Wshift-overflow=2 # clang 3.0 gcc 6.0 (clang default: -Wshift-overflow) + -Wshift-overflow=2 # clang 2.9 gcc 6.0 (clang default: -Wshift-overflow) -Wunused-const-variable # clang 3.4 gcc 6.0 appleclang 5.1 ) endif() @@ -324,7 +336,7 @@ endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) list(APPEND _picky_enable - -Warray-compare # clang 20.0 gcc 12.0 appleclang ? + -Warray-compare # clang 20.1 gcc 12.0 appleclang 26.4 -Wenum-int-mismatch # gcc 13.0 -Wxor-used-as-pow # clang 10.0 gcc 13.0 appleclang 12.0 ) @@ -338,7 +350,7 @@ endif() endif() - # + # Assemble list of flags set(_picky_skipped "") foreach(_ccopt IN LISTS _picky_enable) @@ -369,15 +381,6 @@ endforeach() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") - if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5) - # Avoid false positives - list(APPEND _picky "-Wno-shadow") - list(APPEND _picky "-Wno-unreachable-code") - endif() - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6) - # GCC <4.6 do not support #pragma to suppress warnings locally. Disable them globally instead. - list(APPEND _picky "-Wno-overlength-strings") - endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.0 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7) list(APPEND _picky "-Wno-missing-field-initializers") # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750 endif() @@ -388,7 +391,7 @@ list(APPEND _picky "-Wno-conversion") # Avoid false positives endif() endif() - elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1944) # Skip for untested/unreleased newer versions + elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1950) # Skip for untested/unreleased newer versions list(APPEND _picky "-Wall") list(APPEND _picky "-wd4061") # enumerator 'A' in switch of enum 'B' is not explicitly handled by a case label list(APPEND _picky "-wd4191") # 'type cast': unsafe conversion from 'FARPROC' to 'void (__cdecl *)(void)' @@ -397,12 +400,11 @@ list(APPEND _picky "-wd4548") # expression before comma has no effect; expected expression with side-effect (in FD_SET()) list(APPEND _picky "-wd4574") # 'M' is defined to be '0': did you mean to use '#if M'? (in ws2tcpip.h) list(APPEND _picky "-wd4668") # 'M' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' (in winbase.h) - list(APPEND _picky "-wd4710") # 'snprintf': function not inlined + list(APPEND _picky "-wd4710") # 'fprintf'/'printf'/'sscanf': function not inlined (in tests, with VS2022+ Release) list(APPEND _picky "-wd4711") # function 'A' selected for automatic inline expansion # volatile access of '<expression>' is subject to /volatile:<iso|ms> setting; # consider using __iso_volatile_load/store intrinsic functions (ARM64) list(APPEND _picky "-wd4746") - list(APPEND _picky "-wd4774") # 'snprintf': format string expected in argument 3 is not a string literal list(APPEND _picky "-wd4820") # 'A': 'N' bytes padding added after data member 'B' if(MSVC_VERSION GREATER_EQUAL 1900) list(APPEND _picky "-wd5045") # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified @@ -428,6 +430,21 @@ endforeach() endif() +if(CMAKE_C_STANDARD STREQUAL 90) + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2)) + list(APPEND _picky "-Wno-c99-extensions") # Avoid: warning: '_Bool' is a C99 extension + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1)) + list(APPEND _picky "-Wno-comma") # Just silly + endif() +endif() + +if(DOS AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) + list(APPEND _picky "-Wno-arith-conversion") # Avoid warnings in DJGPP's built-in FD_SET() macro +endif() + if(_picky_nocheck OR _picky) set(_picky_tmp "${_picky_nocheck}" "${_picky}") string(REPLACE ";" " " _picky_tmp "${_picky_tmp}")
diff --git a/Utilities/cmcurl/CMake/Utilities.cmake b/Utilities/cmcurl/CMake/Utilities.cmake index efa28b7..f86a6aa 100644 --- a/Utilities/cmcurl/CMake/Utilities.cmake +++ b/Utilities/cmcurl/CMake/Utilities.cmake
@@ -60,6 +60,10 @@ list(REMOVE_DUPLICATES _cmake_property_list) list(REMOVE_ITEM _cmake_property_list "") list(APPEND _cmake_property_list "INTERFACE_LIBCURL_PC_MODULES") + get_target_property(_target_imported ${_target} IMPORTED) + if(NOT _target_imported) + list(REMOVE_ITEM _cmake_property_list "LOCATION" "LOCATION_<CONFIG>" "MACOSX_PACKAGE_LOCATION" "VS_DEPLOYMENT_LOCATION") + endif() foreach(_prop IN LISTS _cmake_property_list) if(_prop MATCHES "<CONFIG>") foreach(_config IN ITEMS "DEBUG" "RELEASE" "MINSIZEREL" "RELWITHDEBINFO")
diff --git a/Utilities/cmcurl/CMake/curl-config.in.cmake b/Utilities/cmcurl/CMake/curl-config.in.cmake index 7f346c8..4279062 100644 --- a/Utilities/cmcurl/CMake/curl-config.in.cmake +++ b/Utilities/cmcurl/CMake/curl-config.in.cmake
@@ -23,6 +23,8 @@ ########################################################################### @PACKAGE_INIT@ +option(CURL_USE_CMAKECONFIG "Enable detecting @PROJECT_NAME@ dependencies via CMake Config. Default: @CURL_USE_CMAKECONFIG@" + "@CURL_USE_CMAKECONFIG@") option(CURL_USE_PKGCONFIG "Enable pkg-config to detect @PROJECT_NAME@ dependencies. Default: @CURL_USE_PKGCONFIG@" "@CURL_USE_PKGCONFIG@") @@ -33,112 +35,123 @@ include(CMakeFindDependencyMacro) +if("@HAVE_THREADS_POSIX@" OR "@HAVE_THREADS_POSIX_BORINGSSL@") + find_dependency(Threads) # for Threads::Threads +endif() + if("@USE_OPENSSL@") if("@OPENSSL_VERSION_MAJOR@") find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@") else() find_dependency(OpenSSL) endif() + # Define lib duplicate to fixup lib order for GCC binutils ld in static builds + if(TARGET OpenSSL::Crypto AND NOT TARGET CURL::OpenSSL_Crypto) + add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED) + set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) + endif() endif() if("@HAVE_LIBZ@") find_dependency(ZLIB "@ZLIB_VERSION_MAJOR@") + # Define lib duplicate to fixup lib order for GCC binutils ld in static builds + if(TARGET ZLIB::ZLIB AND NOT TARGET CURL::ZLIB) + add_library(CURL::ZLIB INTERFACE IMPORTED) + set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB) + endif() endif() set(_curl_cmake_module_path_save ${CMAKE_MODULE_PATH}) -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH}) +list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_LIST_DIR}) set(_curl_libs "") if("@HAVE_BROTLI@") - find_dependency(Brotli) + find_dependency(Brotli MODULE) list(APPEND _curl_libs CURL::brotli) endif() if("@USE_ARES@") - find_dependency(Cares) + find_dependency(Cares MODULE) list(APPEND _curl_libs CURL::cares) endif() if("@HAVE_GSSAPI@") - find_dependency(GSS) + find_dependency(GSS MODULE) list(APPEND _curl_libs CURL::gss) endif() if("@USE_BACKTRACE@") - find_dependency(Libbacktrace) + find_dependency(Libbacktrace MODULE) list(APPEND _curl_libs CURL::libbacktrace) endif() if("@USE_GSASL@") - find_dependency(Libgsasl) + find_dependency(Libgsasl MODULE) list(APPEND _curl_libs CURL::libgsasl) endif() if(NOT "@USE_WIN32_LDAP@" AND NOT "@CURL_DISABLE_LDAP@") - find_dependency(LDAP) + find_dependency(LDAP MODULE) list(APPEND _curl_libs CURL::ldap) endif() if("@HAVE_LIBIDN2@") - find_dependency(Libidn2) + find_dependency(Libidn2 MODULE) list(APPEND _curl_libs CURL::libidn2) endif() if("@USE_LIBPSL@") - find_dependency(Libpsl) + find_dependency(Libpsl MODULE) list(APPEND _curl_libs CURL::libpsl) endif() -if("@USE_LIBRTMP@") - find_dependency(Librtmp) - list(APPEND _curl_libs CURL::librtmp) -endif() if("@USE_LIBSSH@") - find_dependency(Libssh) + find_dependency(Libssh MODULE) list(APPEND _curl_libs CURL::libssh) endif() if("@USE_LIBSSH2@") - find_dependency(Libssh2) + find_dependency(Libssh2 MODULE) list(APPEND _curl_libs CURL::libssh2) endif() if("@USE_LIBUV@") - find_dependency(Libuv) + find_dependency(Libuv MODULE) list(APPEND _curl_libs CURL::libuv) endif() if("@USE_MBEDTLS@") - find_dependency(MbedTLS) + find_dependency(MbedTLS MODULE) list(APPEND _curl_libs CURL::mbedtls) endif() if("@USE_NGHTTP2@") - find_dependency(NGHTTP2) + find_dependency(NGHTTP2 MODULE) list(APPEND _curl_libs CURL::nghttp2) endif() if("@USE_NGHTTP3@") - find_dependency(NGHTTP3) + find_dependency(NGHTTP3 MODULE) list(APPEND _curl_libs CURL::nghttp3) endif() if("@USE_NGTCP2@") - find_dependency(NGTCP2) + find_dependency(NGTCP2 MODULE) list(APPEND _curl_libs CURL::ngtcp2) endif() if("@USE_GNUTLS@") - find_dependency(GnuTLS) + find_dependency(GnuTLS MODULE) list(APPEND _curl_libs CURL::gnutls) - find_dependency(Nettle) + find_dependency(Nettle MODULE) list(APPEND _curl_libs CURL::nettle) endif() if("@USE_QUICHE@") - find_dependency(Quiche) + find_dependency(Quiche MODULE) list(APPEND _curl_libs CURL::quiche) endif() if("@USE_RUSTLS@") - find_dependency(Rustls) + find_dependency(Rustls MODULE) list(APPEND _curl_libs CURL::rustls) endif() if("@USE_WOLFSSL@") - find_dependency(WolfSSL) + find_dependency(WolfSSL MODULE) list(APPEND _curl_libs CURL::wolfssl) endif() if("@HAVE_ZSTD@") - find_dependency(Zstd) + find_dependency(Zstd MODULE) list(APPEND _curl_libs CURL::zstd) endif() set(CMAKE_MODULE_PATH ${_curl_cmake_module_path_save}) -if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND WIN32 AND NOT TARGET CURL::win32_winsock) +# Define lib duplicate to fixup lib order for GCC binutils ld in static builds +if(WIN32 AND NOT TARGET CURL::win32_winsock) add_library(CURL::win32_winsock INTERFACE IMPORTED) set_target_properties(CURL::win32_winsock PROPERTIES INTERFACE_LINK_LIBRARIES "ws2_32") endif() @@ -147,33 +160,9 @@ # Alias for either shared or static library if(NOT TARGET @PROJECT_NAME@::@LIB_NAME@) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.11 AND CMAKE_VERSION VERSION_LESS 3.18) - set_target_properties(@PROJECT_NAME@::@LIB_SELECTED@ PROPERTIES IMPORTED_GLOBAL TRUE) - endif() add_library(@PROJECT_NAME@::@LIB_NAME@ ALIAS @PROJECT_NAME@::@LIB_SELECTED@) endif() -if(TARGET @PROJECT_NAME@::@LIB_STATIC@) - # CMake before CMP0099 (CMake 3.17 2020-03-20) did not propagate libdirs to - # targets. It expected libs to have an absolute filename. As a workaround, - # manually apply dependency libdirs, for CMake consumers without this policy. - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) - cmake_policy(GET CMP0099 _has_CMP0099) # https://cmake.org/cmake/help/latest/policy/CMP0099.html - endif() - if(NOT _has_CMP0099 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND _curl_libs) - set(_curl_libdirs "") - foreach(_curl_lib IN LISTS _curl_libs) - get_target_property(_curl_libdir "${_curl_lib}" INTERFACE_LINK_DIRECTORIES) - if(_curl_libdir) - list(APPEND _curl_libdirs "${_curl_libdir}") - endif() - endforeach() - if(_curl_libdirs) - target_link_directories(@PROJECT_NAME@::@LIB_STATIC@ INTERFACE ${_curl_libdirs}) - endif() - endif() -endif() - # For compatibility with CMake's FindCURL.cmake set(CURL_VERSION_STRING "@CURLVERSION@") set(CURL_LIBRARIES @PROJECT_NAME@::@LIB_NAME@)
diff --git a/Utilities/cmcurl/CMake/unix-cache.cmake b/Utilities/cmcurl/CMake/unix-cache.cmake index eded7d9..f027f66 100644 --- a/Utilities/cmcurl/CMake/unix-cache.cmake +++ b/Utilities/cmcurl/CMake/unix-cache.cmake
@@ -90,7 +90,6 @@ set(HAVE_FSETXATTR_5 1) set(HAVE_FSETXATTR_6 0) endif() -set(HAVE_FTRUNCATE 1) set(HAVE_GETADDRINFO 1) if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETADDRINFO_THREADSAFE 0) @@ -172,7 +171,6 @@ endif() set(HAVE_LOCALE_H 1) set(HAVE_LOCALTIME_R 1) -set(HAVE_LONGLONG 1) if(APPLE) set(HAVE_MACH_ABSOLUTE_TIME 1) endif() @@ -182,7 +180,6 @@ else() set(HAVE_MEMRCHR 1) endif() -set(HAVE_MSG_NOSIGNAL 1) set(HAVE_NETDB_H 1) if(ANDROID) set(HAVE_NETINET_IN6_H 1) @@ -201,9 +198,12 @@ CYGWIN) set(HAVE_PIPE2 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + BSD OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "SunOS") set(HAVE_PIPE2 1) endif() set(HAVE_POLL 1) @@ -228,30 +228,22 @@ endif() set(HAVE_SENDMSG 1) set(HAVE_SETLOCALE 1) -if(CYGWIN OR - CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(HAVE_SETMODE 0) -else() - set(HAVE_SETMODE 1) -endif() set(HAVE_SETRLIMIT 1) set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) set(HAVE_SIGACTION 1) set(HAVE_SIGINTERRUPT 1) set(HAVE_SIGNAL 1) set(HAVE_SIGSETJMP 1) -set(HAVE_SNPRINTF 1) set(HAVE_SOCKADDR_IN6_SIN6_ADDR 1) set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) set(HAVE_SOCKET 1) set(HAVE_SOCKETPAIR 1) set(HAVE_STDATOMIC_H 1) set(HAVE_STDBOOL_H 1) -set(HAVE_STDDEF_H 1) -set(HAVE_STDINT_H 1) +set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() +set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() set(HAVE_STRCASECMP 1) set(HAVE_STRCMPI 0) -set(HAVE_STRDUP 1) set(HAVE_STRERROR_R 1) set(HAVE_STRICMP 0) set(HAVE_STRINGS_H 1) @@ -311,8 +303,5 @@ set(HAVE_UTIMES 1) set(HAVE_UTIME_H 1) set(HAVE_WRITABLE_ARGV 1) -if(CYGWIN) - set(HAVE__SETMODE 1) -endif() set(STDC_HEADERS 1) set(USE_UNIX_SOCKETS 1)
diff --git a/Utilities/cmcurl/CMake/win32-cache.cmake b/Utilities/cmcurl/CMake/win32-cache.cmake index e704d21..bd9bb6e 100644 --- a/Utilities/cmcurl/CMake/win32-cache.cmake +++ b/Utilities/cmcurl/CMake/win32-cache.cmake
@@ -29,11 +29,9 @@ set(HAVE_BASENAME 1) set(HAVE_BOOL_T 1) # = HAVE_STDBOOL_H set(HAVE_DIRENT_H 1) - set(HAVE_FTRUNCATE 1) set(HAVE_GETTIMEOFDAY 1) set(HAVE_LIBGEN_H 1) set(HAVE_OPENDIR 1) - set(HAVE_SNPRINTF 1) set(HAVE_STDBOOL_H 1) set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() @@ -43,7 +41,6 @@ set(HAVE_UTIME_H 1) # wrapper to sys/utime.h else() set(HAVE_DIRENT_H 0) - set(HAVE_FTRUNCATE 0) set(HAVE_GETTIMEOFDAY 0) set(HAVE_LIBGEN_H 0) set(HAVE_OPENDIR 0) @@ -60,11 +57,6 @@ set(HAVE_STDBOOL_H 0) endif() set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") - if(MSVC_VERSION GREATER_EQUAL 1900) - set(HAVE_SNPRINTF 1) - else() - set(HAVE_SNPRINTF 0) - endif() set(HAVE_BASENAME 0) endif() endif() @@ -125,7 +117,6 @@ set(HAVE_LOCALE_H 1) set(HAVE_LOCALTIME_R 0) set(HAVE_MEMRCHR 0) -set(HAVE_MSG_NOSIGNAL 0) set(HAVE_NETDB_H 0) set(HAVE_NETINET_IN6_H 0) set(HAVE_NETINET_IN_H 0) @@ -144,7 +135,6 @@ set(HAVE_SENDMMSG 0) set(HAVE_SENDMSG 0) set(HAVE_SETLOCALE 1) -set(HAVE_SETMODE 1) set(HAVE_SETRLIMIT 0) set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) set(HAVE_SIGACTION 0) @@ -154,7 +144,6 @@ set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) set(HAVE_SOCKET 1) set(HAVE_SOCKETPAIR 0) -set(HAVE_STRDUP 1) set(HAVE_STRERROR_R 0) set(HAVE_STROPTS_H 0) set(HAVE_STRUCT_SOCKADDR_STORAGE 1) @@ -174,7 +163,6 @@ set(HAVE_TIME_T_UNSIGNED 0) set(HAVE_UTIME 1) set(HAVE_UTIMES 0) -set(HAVE__SETMODE 1) set(STDC_HEADERS 1) # Types and sizes @@ -185,7 +173,6 @@ if(MINGW OR MSVC) curl_prefill_type_size("INT" 4) curl_prefill_type_size("LONG" 4) - curl_prefill_type_size("LONG_LONG" 8) curl_prefill_type_size("__INT64" 8) curl_prefill_type_size("CURL_OFF_T" 8) curl_prefill_type_size("CURL_SOCKET_T" ${CMAKE_SIZEOF_VOID_P})
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index 2c6a6e6..5b9cdcb 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt
@@ -8,6 +8,7 @@ set(BUILD_SHARED_LIBS OFF) set(BUILD_STATIC_LIBS ON) set(BUILD_STATIC_CURL OFF) +set(CURL_BUILD_EVERYTHING OFF) set(CURL_CA_SEARCH_SAFE OFF) set(CURL_USE_GSASL OFF) set(CURL_USE_GSSAPI OFF) @@ -22,6 +23,9 @@ set(CURL_USE_SCHANNEL OFF) set(CURL_USE_WOLFSSL OFF) set(CURL_BROTLI "OFF" CACHE INTERNAL "Build curl with BROTLI support (AUTO, ON or OFF)") +set(CURL_DEBUG_GLOBAL_MEM OFF) +set(CURL_ENABLE_NTLM OFF) +set(CURL_ENABLE_SMB OFF) set(CURL_DISABLE_ALTSVC ON) set(CURL_DISABLE_AWS OFF) set(CURL_DISABLE_BASIC_AUTH OFF) @@ -52,7 +56,6 @@ 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) set(CURL_DISABLE_PARSEDATE OFF) set(CURL_DISABLE_POP3 ON CACHE INTERNAL "Disable curl pop3 protocol?") @@ -61,7 +64,6 @@ set(CURL_DISABLE_RTSP ON CACHE INTERNAL "Disable curl rtsp protocol?") set(CURL_DISABLE_SHA512_256 OFF) set(CURL_DISABLE_SHUFFLE_DNS OFF) -set(CURL_DISABLE_SMB OFF) set(CURL_DISABLE_SMTP ON CACHE INTERNAL "Disable curl smtp protocol?") set(CURL_DISABLE_SOCKETPAIR OFF) set(CURL_DISABLE_SRP OFF) @@ -74,6 +76,7 @@ set(CURL_HIDDEN_SYMBOLS OFF CACHE INTERNAL "No curl hidden symbols") set(CURL_LTO OFF CACHE INTERNAL "Turn on compiler Link Time Optimizations") set(CURL_STATIC_CRT OFF CACHE INTERNAL "Set to ON to build libcurl with static CRT on Windows (/MT).") +set(CURL_USE_CMAKECONFIG OFF) set(CURL_WERROR OFF CACHE INTERNAL "Turn compiler warnings into errors") set(CURL_ZLIB "AUTO" CACHE INTERNAL "Build curl with ZLIB support (AUTO, ON or OFF)") set(CURL_ZSTD "OFF" CACHE INTERNAL "Build curl with ZSTD support (AUTO, ON or OFF)") @@ -178,7 +181,7 @@ ########################################################################### if(0) # XXX(cmake): not needed for build within cmake -cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR) +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) message(STATUS "Using CMake version ${CMAKE_VERSION}") # Collect command-line arguments for buildinfo.txt. @@ -192,7 +195,7 @@ get_property(_cache_var_type CACHE ${_cache_var} PROPERTY TYPE) get_property(_cache_var_value CACHE ${_cache_var} PROPERTY VALUE) if(_cache_var_type STREQUAL "UNINITIALIZED") - set(_cache_var_type) + set(_cache_var_type "") else() set(_cache_var_type ":${_cache_var_type}") endif() @@ -202,7 +205,7 @@ endif() endif() # XXX(cmake): end -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH}) +list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/CMake") include(Utilities) include(Macros) include(CMakeDependentOption) @@ -298,6 +301,9 @@ if(CMAKE_CROSSCOMPILING) string(APPEND _target_flags " CROSS") endif() +if(CMAKE_C_STANDARD) + string(APPEND _target_flags " C${CMAKE_C_STANDARD}") +endif() if(0) # XXX(cmake): not needed for build within cmake message(STATUS "CMake platform flags:${_target_flags}") endif() # XXX(cmake): end @@ -314,6 +320,10 @@ set(CURL_OS "\"${CMAKE_SYSTEM_NAME}\"") endif() +if(CURL_PATCHSTAMP) + set(CURL_PATCHSTAMP "\"${CURL_PATCHSTAMP}\"") +endif() + set(LIB_NAME "libcurl") set(EXE_NAME "curl") @@ -343,6 +353,7 @@ option(BUILD_STATIC_CURL "Build curl executable with static libcurl" OFF) option(ENABLE_ARES "Enable c-ares support" OFF) option(CURL_DISABLE_INSTALL "Disable installation targets" OFF) +option(CURL_BUILD_EVERYTHING "Build optional build targets (examples, tests) by default" OFF) if(WIN32) option(ENABLE_UNICODE "Use the Unicode version of the Windows API functions" OFF) @@ -359,14 +370,15 @@ if(0) # XXX(cmake): not needed for build within cmake # Apply to all feature checks list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") - if(MSVC) - list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_CRT_NONSTDC_NO_DEPRECATE") # for strdup() detection - endif() set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string") if(CURL_TARGET_WINDOWS_VERSION) - set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") - list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") # Apply to all feature checks + if(CURL_TARGET_WINDOWS_VERSION MATCHES "^0x[0-9a-fA-F]+$") + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") # Apply to all feature checks + else() + message(WARNING "CURL_TARGET_WINDOWS_VERSION value '${CURL_TARGET_WINDOWS_VERSION}' is not a valid hex string.") + endif() endif() endif() # XXX(cmake): end @@ -375,7 +387,7 @@ if(HAVE_WIN32_WINNT) string(REGEX MATCH "_WIN32_WINNT=0x[0-9a-fA-F]+" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") string(REGEX REPLACE "_WIN32_WINNT=" "" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") - string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") # pad to 4 digits + string(REGEX REPLACE "0x([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])$" "0x0\\1" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") # pad to 4 digits string(TOLOWER "${CURL_TEST_OUTPUT}" HAVE_WIN32_WINNT) message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}") endif() @@ -412,8 +424,6 @@ endif() option(ENABLE_THREADED_RESOLVER "Enable threaded DNS lookup" ${_enable_threaded_resolver_default}) endif() - -include(PickyWarnings) endif() # XXX(cmake): end if(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "GNU") @@ -425,38 +435,44 @@ if(ENABLE_DEBUG) message(WARNING "This curl build is Debug-enabled and insecure, do not use in production.") endif() -option(ENABLE_CURLDEBUG "Enable TrackMemory debug feature" ${ENABLE_DEBUG}) set(CURL_DEBUG_MACROS "") if(ENABLE_DEBUG) list(APPEND CURL_DEBUG_MACROS "DEBUGBUILD") endif() -if(ENABLE_CURLDEBUG) - list(APPEND CURL_DEBUG_MACROS "CURLDEBUG") -endif() if(0) # XXX(cmake): not needed for build within cmake option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF) if(CURL_CLANG_TIDY) - set(CMAKE_UNITY_BUILD OFF) # clang-tidy is not looking into #included sources, thus not compatible with unity builds. - set(CURL_DISABLE_TYPECHECK ON) # to improve performance and avoid potential interference. - set(_tidy_checks "") - list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.bzero") # for FD_ZERO() (seen on macOS) - list(APPEND _tidy_checks "-clang-analyzer-optin.performance.Padding") - list(APPEND _tidy_checks "-clang-analyzer-security.ArrayBound") # false positives with clang-tidy v21.1.0 - list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling") - string(REPLACE ";" "," _tidy_checks "${_tidy_checks}") find_program(CLANG_TIDY NAMES "clang-tidy" REQUIRED) - set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY}" "-checks=${_tidy_checks}" "-quiet") - unset(_tidy_checks) + if(NOT CMAKE_C_COMPILER_ID MATCHES "Clang") + set(PICKY_COMPILER OFF) # Do a best effort and skip passing non-clang warning options to clang-tidy. + # This lets through warning options enabled via CURL_WERROR=ON, affecting lib and src. + endif() + set(CURL_DISABLE_TYPECHECK ON) # to improve performance (1.4x), avoid potential interference and bugprone-macro-parentheses. + set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY}") + list(APPEND CMAKE_C_CLANG_TIDY "--config-file=${PROJECT_SOURCE_DIR}/.clang-tidy.yml") if(CURL_WERROR) list(APPEND CMAKE_C_CLANG_TIDY "--warnings-as-errors=*") endif() if(CURL_CLANG_TIDYFLAGS) - list(APPEND CMAKE_C_CLANG_TIDY ${CURL_CLANG_TIDYFLAGS}) + string(REPLACE " " ";" _tidy_flags_list "${CURL_CLANG_TIDYFLAGS}") + list(APPEND CMAKE_C_CLANG_TIDY ${_tidy_flags_list}) endif() endif() +option(CURL_GCC_ANALYZER "Enable GCC --analyzer option" OFF) +if(CURL_GCC_ANALYZER AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0) + set(CURL_DISABLE_TYPECHECK ON) # to improve performance (1.1x). + # https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html + set(CURL_ANALYZER_CFLAGS "-fanalyzer") + # disable checks causing false positives only + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-fd-leak" "-Wno-analyzer-fd-use-without-check" "-Wno-analyzer-file-leak") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-infinite-loop") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-malloc-leak") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-out-of-bounds") +endif() + option(CURL_CODE_COVERAGE "Enable code coverage build options" OFF) if(CURL_CODE_COVERAGE) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") @@ -477,6 +493,30 @@ endif() endif() +include(PickyWarnings) + +set(CURL_CFLAGS "") # C flags set for libcurl and curl tool (aka public binaries) only + +option(CURL_DROP_UNUSED "Drop unused code and data from built binaries" OFF) +if(CURL_DROP_UNUSED) + if(APPLE) + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,-dead_strip") + elseif(MSVC) # Options below are toolchain defaults in Release configurations. + # This option does not seem to have an effect with VS2010: + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-OPT:REF") + # Optional, but reduces binary size further, with the cost of larger objects/static libraries: + list(APPEND CURL_CFLAGS "-Gy") + elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + if(WIN32) + # To make -Wl,--gc-sections work on Windows: https://sourceware.org/bugzilla/show_bug.cgi?id=11539 + set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-fno-asynchronous-unwind-tables") + endif() + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,--gc-sections") + # Optional, but reduces binary size further, with the cost of larger objects/static libraries: + list(APPEND CURL_CFLAGS "-ffunction-sections" "-fdata-sections") + endif() +endif() + # For debug libs and exes, add "-d" postfix if(NOT DEFINED CMAKE_DEBUG_POSTFIX) set(CMAKE_DEBUG_POSTFIX "-d") @@ -522,6 +562,14 @@ endif() endif() +# Override to force-disable or force-enable the use of CMake Configs. +if(MSVC AND NOT VCPKG_TOOLCHAIN AND NOT CMAKE_CROSSCOMPILING) + set(_curl_use_cmakeconfig_default ON) +else() + set(_curl_use_cmakeconfig_default OFF) +endif() +option(CURL_USE_CMAKECONFIG "Enable detecting dependencies via CMake Config" ${_curl_use_cmakeconfig_default}) + # Override to force-disable or force-enable the use of pkg-config. if((UNIX AND NOT ANDROID AND (NOT APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")) OR VCPKG_TOOLCHAIN OR @@ -538,7 +586,7 @@ if(ENABLE_ARES) set(USE_ARES 1) - find_package(Cares REQUIRED) + find_package(Cares MODULE REQUIRED) list(APPEND CURL_LIBS CURL::cares) endif() @@ -609,8 +657,8 @@ mark_as_advanced(CURL_DISABLE_BINDLOCAL) option(CURL_DISABLE_NETRC "Disable netrc parser" OFF) mark_as_advanced(CURL_DISABLE_NETRC) -option(CURL_DISABLE_NTLM "Disable NTLM support" OFF) -mark_as_advanced(CURL_DISABLE_NTLM) +option(CURL_ENABLE_NTLM "Enable NTLM support" OFF) +mark_as_advanced(CURL_ENABLE_NTLM) option(CURL_DISABLE_PARSEDATE "Disable date parsing" OFF) mark_as_advanced(CURL_DISABLE_PARSEDATE) option(CURL_DISABLE_POP3 "Disable POP3" OFF) @@ -627,8 +675,8 @@ mark_as_advanced(CURL_DISABLE_SHA512_256) option(CURL_DISABLE_SHUFFLE_DNS "Disable shuffle DNS feature" OFF) mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS) -option(CURL_DISABLE_SMB "Disable SMB" OFF) -mark_as_advanced(CURL_DISABLE_SMB) +option(CURL_ENABLE_SMB "Enable SMB" OFF) +mark_as_advanced(CURL_ENABLE_SMB) option(CURL_DISABLE_SMTP "Disable SMTP" OFF) mark_as_advanced(CURL_DISABLE_SMTP) option(CURL_DISABLE_SOCKETPAIR "Disable use of socketpair for curl_multi_poll()" OFF) @@ -643,6 +691,17 @@ mark_as_advanced(CURL_DISABLE_TYPECHECK) option(CURL_DISABLE_VERBOSE_STRINGS "Disable verbose strings" OFF) mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS) +option(CURL_DEBUG_GLOBAL_MEM "Debug curl_global_init_mem" OFF) +mark_as_advanced(CURL_DEBUG_GLOBAL_MEM) + +if(CURL_DEBUG_GLOBAL_MEM) + # Set it via the command-line to make it apply to the entire directory. + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DEBUG_GLOBAL_MEM") +endif() +if(CURL_DISABLE_TYPECHECK) + # Set it via the command-line to make it apply to examples also. + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DISABLE_TYPECHECK") +endif() if(CURL_DISABLE_HTTP) set(CURL_DISABLE_ALTSVC ON) @@ -668,10 +727,10 @@ set(CURL_DISABLE_MQTT ON) set(CURL_DISABLE_POP3 ON) set(CURL_DISABLE_RTSP ON) - set(CURL_DISABLE_SMB ON) set(CURL_DISABLE_SMTP ON) set(CURL_DISABLE_TELNET ON) set(CURL_DISABLE_TFTP ON) + set(CURL_DISABLE_WEBSOCKETS ON) endif() if(WINDOWS_STORE) @@ -679,6 +738,8 @@ endif() if(0) # XXX(cmake): not needed for build within cmake +option(CURL_LINT "Run lint checks while building" OFF) + find_package(Perl) if(PERL_EXECUTABLE) @@ -692,6 +753,20 @@ COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" "lib/ca-bundle.crt" DEPENDS "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" ) + add_custom_target(curl-lint + COMMENT "Running lint checks" VERBATIM USES_TERMINAL + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/badwords-all" + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/checksrc-all.pl" + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/spacecheck.pl" + DEPENDS + "${PROJECT_SOURCE_DIR}/scripts/badwords-all" "${PROJECT_SOURCE_DIR}/scripts/badwords" + "${PROJECT_SOURCE_DIR}/scripts/checksrc-all.pl" "${PROJECT_SOURCE_DIR}/scripts/checksrc.pl" + "${PROJECT_SOURCE_DIR}/scripts/spacecheck.pl" + ) + if(CURL_LINT) + set_target_properties(curl-lint PROPERTIES EXCLUDE_FROM_ALL FALSE) + endif() endif() option(BUILD_LIBCURL_DOCS "Build libcurl man pages" ON) @@ -704,7 +779,7 @@ endif() # XXX(cmake): end # If we are on AIX, do the _ALL_SOURCE magic -if(CMAKE_SYSTEM_NAME STREQUAL "AIX") +if(AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX") set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_ALL_SOURCE") endif() @@ -727,6 +802,7 @@ include(CheckCSourceCompiles) option(_CURL_PREFILL "Fast-track known feature detection results (Windows, some Apple)" "${WIN32}") +mark_as_advanced(_CURL_PREFILL) if(_CURL_PREFILL) if(WIN32) include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/win32-cache.cmake") @@ -754,17 +830,23 @@ set(HAVE_TIME_T_UNSIGNED 1) endif() -if(ENABLE_THREADED_RESOLVER) - if(WIN32) - set(USE_THREADS_WIN32 ON) - else() - find_package(Threads REQUIRED) - set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT}) - set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT}) - list(APPEND CURL_NETWORK_AND_TIME_LIBS ${CMAKE_THREAD_LIBS_INIT}) +if(NOT WIN32) + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + set(HAVE_THREADS_POSIX 1) + list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads) endif() endif() +if(ENABLE_THREADED_RESOLVER) + if(NOT WIN32 AND NOT HAVE_THREADS_POSIX) + message(FATAL_ERROR "Threaded resolver requires POSIX Threads.") + endif() + set(USE_RESOLV_THREADED ON) +elseif(USE_ARES) + set(USE_RESOLV_ARES ON) +endif() + # Check for all needed libraries if(DOS) if(WATT_ROOT) @@ -790,7 +872,7 @@ elseif(NOT WIN32 AND NOT APPLE) check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) if(HAVE_LIBSOCKET) - set(CURL_NETWORK_AND_TIME_LIBS "socket" ${CURL_NETWORK_AND_TIME_LIBS}) + list(INSERT CURL_NETWORK_AND_TIME_LIBS 0 "socket") endif() endif() @@ -857,11 +939,6 @@ set(_openssl_default ON) endif() cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${_openssl_default} CURL_ENABLE_SSL OFF) -option(USE_OPENSSL_QUIC "Use OpenSSL and nghttp3 libraries for HTTP/3 support" OFF) -if(USE_OPENSSL_QUIC AND NOT CURL_USE_OPENSSL) - message(WARNING "OpenSSL QUIC has been requested, but without enabling OpenSSL. Will not enable QUIC.") - set(USE_OPENSSL_QUIC OFF) -endif() option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF) endif() # XXX(cmake): end @@ -901,9 +978,9 @@ if(NOT CURL_USE_OPENSSL AND NOT CURL_USE_GNUTLS) message(FATAL_ERROR "Apple SecTrust is only supported with Openssl/GnuTLS") endif() - find_library(COREFOUNDATION_FRAMEWORK NAMES "Security") - mark_as_advanced(COREFOUNDATION_FRAMEWORK) - if(NOT COREFOUNDATION_FRAMEWORK) + find_library(SECURITY_FRAMEWORK NAMES "Security") + mark_as_advanced(SECURITY_FRAMEWORK) + if(NOT SECURITY_FRAMEWORK) message(FATAL_ERROR "Security framework not found") endif() list(APPEND CURL_LIBS "-framework Security") @@ -944,7 +1021,6 @@ # Depend on OpenSSL via imported targets. This allows our dependents to # get our dependencies transitively. list(APPEND CURL_LIBS OpenSSL::SSL OpenSSL::Crypto) - set_target_properties(OpenSSL::SSL PROPERTIES INTERFACE_LIBCURL_PC_MODULES "openssl") if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl") set(_valid_default_ssl_backend TRUE) @@ -974,6 +1050,16 @@ endif() if(HAVE_BORINGSSL OR HAVE_AWSLC) + if(NOT MSVC AND NOT ANDROID) # BoringSSL/AWS-LC MSVC builds use native Windows threads + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + set(HAVE_THREADS_POSIX_BORINGSSL 1) + list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads) + list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads) + elseif(OPENSSL_USE_STATIC_LIBS) + message(WARNING "BoringSSL/AWS-LC requires POSIX Threads.") + endif() + endif() if(OPENSSL_USE_STATIC_LIBS AND CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND CURL_LIBS "stdc++") list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++") @@ -981,6 +1067,9 @@ endif() if(HAVE_BORINGSSL) + if(BORINGSSL_VERSION) + set(CURL_BORINGSSL_VERSION "\"${BORINGSSL_VERSION}\"") + endif() set(_openssl "BoringSSL") elseif(HAVE_AWSLC) set(_openssl "AWS-LC") @@ -994,7 +1083,7 @@ endif() if(CURL_USE_MBEDTLS) - find_package(MbedTLS REQUIRED) + find_package(MbedTLS MODULE REQUIRED) if(MBEDTLS_VERSION VERSION_LESS 3.2.0) message(FATAL_ERROR "mbedTLS v3.2.0 or newer is required.") endif() @@ -1019,7 +1108,7 @@ endif() if(CURL_USE_WOLFSSL) - find_package(WolfSSL REQUIRED) + find_package(WolfSSL MODULE REQUIRED) set(_ssl_enabled ON) set(USE_WOLFSSL ON) list(APPEND CURL_LIBS CURL::wolfssl) @@ -1028,12 +1117,18 @@ set(_valid_default_ssl_backend TRUE) endif() set(_curl_ca_bundle_supported TRUE) + + if(USE_OPENSSL AND WOLFSSL_VERSION VERSION_LESS 5.7.6) + message(FATAL_ERROR "wolfSSL 5.7.6 or newer is required to coexist with OpenSSL.") + endif() + + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "WOLFSSL_OPTIONS_IGNORE_SYS") endif() if(CURL_USE_GNUTLS) - find_package(GnuTLS REQUIRED) + find_package(GnuTLS MODULE REQUIRED) list(APPEND CURL_LIBS CURL::gnutls) - find_package(Nettle REQUIRED) + find_package(Nettle MODULE REQUIRED) list(APPEND CURL_LIBS CURL::nettle) set(_ssl_enabled ON) set(USE_GNUTLS ON) @@ -1054,7 +1149,7 @@ endif() if(CURL_USE_RUSTLS) - find_package(Rustls REQUIRED) + find_package(Rustls MODULE REQUIRED) set(_ssl_enabled ON) set(USE_RUSTLS ON) list(APPEND CURL_LIBS CURL::rustls) @@ -1094,7 +1189,6 @@ # get our dependencies transitively. if(CMAKE_USE_SYSTEM_ZLIB) list(APPEND CURL_LIBS ZLIB::ZLIB) - set_target_properties(ZLIB::ZLIB PROPERTIES INTERFACE_LIBCURL_PC_MODULES "zlib") else() list(APPEND CURL_LIBS cmzlib) endif() @@ -1132,11 +1226,12 @@ endif() endif() if(WIN32) - list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL + list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL BCryptGenRandom() endif() endif() if(USE_WOLFSSL) list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::wolfssl) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWOLFSSL_OPTIONS_IGNORE_SYS") if(HAVE_LIBZ) list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB) # Public wolfSSL headers also require zlib headers endif() @@ -1155,7 +1250,7 @@ # Ensure that OpenSSL (or fork) or wolfSSL actually supports QUICTLS API. macro(curl_openssl_check_quic) - if(USE_OPENSSL AND NOT USE_OPENSSL_QUIC) + if(USE_OPENSSL) if(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0) if(NOT DEFINED HAVE_SSL_SET_QUIC_TLS_CBS) curl_openssl_check_exists("SSL_set_quic_tls_cbs" HAVE_SSL_SET_QUIC_TLS_CBS) @@ -1179,9 +1274,9 @@ if(USE_WOLFSSL) curl_openssl_check_exists("wolfSSL_get_peer_certificate" HAVE_WOLFSSL_GET_PEER_CERTIFICATE) curl_openssl_check_exists("wolfSSL_UseALPN" HAVE_WOLFSSL_USEALPN) - curl_openssl_check_exists("wolfSSL_DES_ecb_encrypt" HAVE_WOLFSSL_DES_ECB_ENCRYPT) curl_openssl_check_exists("wolfSSL_BIO_new" HAVE_WOLFSSL_BIO_NEW) curl_openssl_check_exists("wolfSSL_BIO_set_shutdown" HAVE_WOLFSSL_BIO_SET_SHUTDOWN) + curl_openssl_check_exists("wc_Des_EcbEncrypt" HAVE_WC_DES_ECBENCRYPT) endif() if(USE_OPENSSL) @@ -1236,7 +1331,7 @@ option(USE_NGHTTP2 "Use nghttp2 library" ON) if(USE_NGHTTP2) - find_package(NGHTTP2) + find_package(NGHTTP2 MODULE) if(NGHTTP2_FOUND) list(APPEND CURL_LIBS CURL::nghttp2) else() @@ -1250,33 +1345,33 @@ message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") elseif(USE_OPENSSL OR USE_WOLFSSL) if(USE_WOLFSSL) - find_package(NGTCP2 REQUIRED COMPONENTS "wolfSSL") + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "wolfSSL") elseif(HAVE_BORINGSSL OR HAVE_AWSLC) - find_package(NGTCP2 REQUIRED COMPONENTS "BoringSSL") - elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0 AND NOT USE_OPENSSL_QUIC) - find_package(NGTCP2 REQUIRED COMPONENTS "ossl") + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "BoringSSL") + elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0) + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "ossl") if(NGTCP2_VERSION VERSION_LESS 1.12.0) message(FATAL_ERROR "ngtcp2 1.12.0 or upper required for OpenSSL") endif() set(OPENSSL_QUIC_API2 1) elseif(HAVE_LIBRESSL) - find_package(NGTCP2 COMPONENTS "LibreSSL") + find_package(NGTCP2 MODULE COMPONENTS "LibreSSL") if(NOT NGTCP2_FOUND) - find_package(NGTCP2 REQUIRED COMPONENTS "quictls") # for ngtcp2 <1.15.0 + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "quictls") # for ngtcp2 <1.15.0 endif() else() - find_package(NGTCP2 REQUIRED COMPONENTS "quictls") + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "quictls") set(_openssl "quictls") endif() curl_openssl_check_quic() elseif(USE_GNUTLS) - find_package(NGTCP2 REQUIRED "GnuTLS") + find_package(NGTCP2 MODULE REQUIRED "GnuTLS") else() message(FATAL_ERROR "ngtcp2 requires a supported TLS-backend") endif() list(APPEND CURL_LIBS CURL::ngtcp2) - find_package(NGHTTP3 REQUIRED) + find_package(NGHTTP3 MODULE REQUIRED) set(USE_NGHTTP3 ON) list(APPEND CURL_LIBS CURL::nghttp3) endif() @@ -1288,7 +1383,7 @@ elseif(CURL_WITH_MULTI_SSL) message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") endif() - find_package(Quiche REQUIRED) + find_package(Quiche MODULE REQUIRED) if(NOT HAVE_BORINGSSL) message(FATAL_ERROR "quiche requires BoringSSL") endif() @@ -1302,19 +1397,6 @@ endif() endif() -if(USE_OPENSSL_QUIC) - if(USE_NGTCP2 OR USE_QUICHE) - message(FATAL_ERROR "Only one HTTP/3 backend can be selected") - elseif(CURL_WITH_MULTI_SSL) - message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") - endif() - find_package(OpenSSL 3.3.0 REQUIRED) - - find_package(NGHTTP3 REQUIRED) - set(USE_NGHTTP3 ON) - list(APPEND CURL_LIBS CURL::nghttp3) -endif() - if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP)) set(USE_TLS_SRP 1) endif() @@ -1337,10 +1419,10 @@ if(USE_OPENSSL) list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) endif() - find_package(LDAP) + find_package(LDAP MODULE) if(LDAP_FOUND) set(HAVE_LBER_H 1) - set(CURL_LIBS CURL::ldap ${CURL_LIBS}) + list(INSERT CURL_LIBS 0 CURL::ldap) # LDAP feature checks @@ -1377,7 +1459,7 @@ if(WIN32) option(USE_WIN32_IDN "Use WinIDN for IDN support" OFF) if(USE_WIN32_IDN) - list(APPEND CURL_LIBS "normaliz") + list(APPEND CURL_LIBS "normaliz") # for IdnToAscii(), IdnToUnicode() endif() else() set(USE_WIN32_IDN OFF) @@ -1405,9 +1487,9 @@ set(HAVE_IDN2_H OFF) set(HAVE_LIBIDN2 OFF) if(USE_LIBIDN2 AND NOT USE_APPLE_IDN AND NOT USE_WIN32_IDN) - find_package(Libidn2 QUIET) + find_package(Libidn2 MODULE QUIET) if(LIBIDN2_FOUND) - set(CURL_LIBS CURL::libidn2 ${CURL_LIBS}) + list(INSERT CURL_LIBS 0 CURL::libidn2) set(HAVE_IDN2_H 1) set(HAVE_LIBIDN2 1) endif() @@ -1418,7 +1500,7 @@ mark_as_advanced(CURL_USE_LIBPSL) set(USE_LIBPSL OFF) if(CURL_USE_LIBPSL) - find_package(Libpsl REQUIRED) + find_package(Libpsl MODULE REQUIRED) list(APPEND CURL_LIBS CURL::libpsl) set(USE_LIBPSL ON) endif() @@ -1428,9 +1510,9 @@ mark_as_advanced(CURL_USE_LIBSSH2) set(USE_LIBSSH2 OFF) if(CURL_USE_LIBSSH2) - find_package(Libssh2) + find_package(Libssh2 MODULE) if(LIBSSH2_FOUND) - set(CURL_LIBS CURL::libssh2 ${CURL_LIBS}) # keep it before TLS-crypto, compression + list(INSERT CURL_LIBS 0 CURL::libssh2) # keep it before TLS-crypto, compression set(USE_LIBSSH2 ON) endif() endif() @@ -1439,15 +1521,15 @@ option(CURL_USE_LIBSSH "Use libssh" OFF) mark_as_advanced(CURL_USE_LIBSSH) if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH) - find_package(Libssh REQUIRED) - set(CURL_LIBS CURL::libssh ${CURL_LIBS}) # keep it before TLS-crypto, compression + find_package(Libssh MODULE REQUIRED) + list(INSERT CURL_LIBS 0 CURL::libssh) # keep it before TLS-crypto, compression set(USE_LIBSSH ON) endif() option(CURL_USE_GSASL "Use libgsasl" OFF) mark_as_advanced(CURL_USE_GSASL) if(CURL_USE_GSASL) - find_package(Libgsasl REQUIRED) + find_package(Libgsasl MODULE REQUIRED) list(APPEND CURL_LIBS CURL::libgsasl) set(USE_GSASL ON) endif() @@ -1456,7 +1538,7 @@ mark_as_advanced(CURL_USE_GSSAPI) if(CURL_USE_GSSAPI) - find_package(GSS) + find_package(GSS MODULE) set(HAVE_GSSAPI ${GSS_FOUND}) if(GSS_FOUND) @@ -1474,15 +1556,15 @@ endif() # libbacktrace -option(CURL_USE_LIBBACKTRACE "Use libbacktrace. Requires build with TrackMemory and DWARF debug information." OFF) +option(CURL_USE_LIBBACKTRACE "Use libbacktrace. Requires debug-enabled build and DWARF debug information." OFF) if(CURL_USE_LIBBACKTRACE) - if(NOT ENABLE_CURLDEBUG) - message(FATAL_ERROR "libbacktrace requires TrackMemory enabled") + if(NOT ENABLE_DEBUG) + message(FATAL_ERROR "libbacktrace requires debug-enabled build for TrackMemory") endif() if(NOT CMAKE_BUILD_TYPE MATCHES "(Debug|RelWithDebInfo)") message(FATAL_ERROR "libbacktrace requires debug information") endif() - find_package(Libbacktrace REQUIRED) + find_package(Libbacktrace MODULE REQUIRED) list(APPEND CURL_LIBS CURL::libbacktrace) set(USE_BACKTRACE ON) endif() @@ -1493,18 +1575,12 @@ if(NOT ENABLE_DEBUG) message(FATAL_ERROR "Using libuv without debug support enabled is useless") endif() - find_package(Libuv REQUIRED) + find_package(Libuv MODULE REQUIRED) list(APPEND CURL_LIBS CURL::libuv) set(USE_LIBUV ON) set(HAVE_UV_H ON) endif() -option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF) -if(USE_LIBRTMP) - find_package(Librtmp REQUIRED) - list(APPEND CURL_LIBS CURL::librtmp) -endif() - option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON) if(ENABLE_UNIX_SOCKETS) if(WIN32 OR DOS) @@ -1522,6 +1598,13 @@ # # CA handling # +option(CURL_CA_NATIVE "Use native CA store" OFF) +if(CURL_CA_NATIVE) + set(_curl_disable_ca_search_default ON) +else() + set(_curl_disable_ca_search_default OFF) +endif() + if(_curl_ca_bundle_supported) set(_ca_opt_desc "Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") @@ -1544,7 +1627,7 @@ unset(CURL_CA_BUNDLE CACHE) elseif(CURL_CA_BUNDLE STREQUAL "auto") unset(CURL_CA_BUNDLE CACHE) - if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32 AND NOT USE_APPLE_SECTRUST) + if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32 AND NOT USE_APPLE_SECTRUST AND NOT CURL_CA_NATIVE) set(_curl_ca_bundle_autodetect TRUE) endif() else() @@ -1558,7 +1641,7 @@ unset(CURL_CA_PATH CACHE) elseif(CURL_CA_PATH STREQUAL "auto") unset(CURL_CA_PATH CACHE) - if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32 AND NOT USE_APPLE_SECTRUST) + if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32 AND NOT USE_APPLE_SECTRUST AND NOT CURL_CA_NATIVE) set(_curl_ca_path_autodetect TRUE) endif() else() @@ -1616,30 +1699,20 @@ endif() # XXX(cmake): end if(WIN32) - option(CURL_DISABLE_CA_SEARCH "Disable unsafe CA bundle search in PATH on Windows" OFF) + option(CURL_DISABLE_CA_SEARCH "Disable unsafe CA bundle search in PATH on Windows" ${_curl_disable_ca_search_default}) option(CURL_CA_SEARCH_SAFE "Enable safe CA bundle search (within the curl tool directory) on Windows" OFF) endif() +set(CURL_INCLUDES "") + # Check for header files if(WIN32) list(APPEND CURL_INCLUDES "winsock2.h") list(APPEND CURL_INCLUDES "ws2tcpip.h") - if(HAVE_WIN32_WINNT AND HAVE_WIN32_WINNT LESS 0x0501) - # Windows XP is required for freeaddrinfo, getaddrinfo - message(FATAL_ERROR "Building for Windows XP or newer is required.") - endif() - - # Pre-fill detection results based on target OS version - if(_CURL_PREFILL) - if(NOT HAVE_WIN32_WINNT OR HAVE_WIN32_WINNT LESS 0x0600 OR # older than Windows Vista - WINDOWS_STORE) - set(HAVE_IF_NAMETOINDEX 0) - unset(HAVE_IF_NAMETOINDEX CACHE) - elseif(MSVC OR MINGW) - set(HAVE_IF_NAMETOINDEX 1) - unset(HAVE_IF_NAMETOINDEX CACHE) - endif() + if(HAVE_WIN32_WINNT AND HAVE_WIN32_WINNT LESS 0x0600) + # Windows Vista is required for freeaddrinfo, getaddrinfo, if_nametoindex + message(FATAL_ERROR "Building for Windows Vista or newer is required.") endif() endif() @@ -1685,7 +1758,6 @@ check_include_file("pwd.h" HAVE_PWD_H) check_include_file("stdatomic.h" HAVE_STDATOMIC_H) check_include_file("stdbool.h" HAVE_STDBOOL_H) -check_include_file("stdint.h" HAVE_STDINT_H) check_include_file("strings.h" HAVE_STRINGS_H) check_include_file("stropts.h" HAVE_STROPTS_H) check_include_file("termio.h" HAVE_TERMIO_H) @@ -1708,7 +1780,7 @@ HAVE_UNISTD_H ) if(${_variable}) - string(APPEND CURL_TEST_DEFINES " -D${_variable}") + list(APPEND CURL_TEST_DEFINES "-D${_variable}") endif() endforeach() @@ -1749,7 +1821,6 @@ check_function_exists("sendmsg" HAVE_SENDMSG) check_function_exists("sendmmsg" HAVE_SENDMMSG) check_symbol_exists("select" "${CURL_INCLUDES}" HAVE_SELECT) # proto/bsdsocket.h sys/select.h sys/socket.h -check_symbol_exists("strdup" "string.h" HAVE_STRDUP) check_symbol_exists("memrchr" "string.h" HAVE_MEMRCHR) check_symbol_exists("alarm" "unistd.h" HAVE_ALARM) check_symbol_exists("fcntl" "fcntl.h" HAVE_FCNTL) @@ -1778,19 +1849,21 @@ check_symbol_exists("getifaddrs" "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) # ifaddrs.h check_symbol_exists("freeaddrinfo" "${CURL_INCLUDES}" HAVE_FREEADDRINFO) # ws2tcpip.h sys/socket.h netdb.h check_function_exists("pipe" HAVE_PIPE) -check_function_exists("pipe2" HAVE_PIPE2) check_function_exists("eventfd" HAVE_EVENTFD) -check_symbol_exists("ftruncate" "unistd.h" HAVE_FTRUNCATE) check_symbol_exists("getpeername" "${CURL_INCLUDES}" HAVE_GETPEERNAME) # winsock2.h unistd.h proto/bsdsocket.h check_symbol_exists("getsockname" "${CURL_INCLUDES}" HAVE_GETSOCKNAME) # winsock2.h unistd.h proto/bsdsocket.h check_function_exists("getrlimit" HAVE_GETRLIMIT) check_function_exists("setlocale" HAVE_SETLOCALE) check_function_exists("setrlimit" HAVE_SETRLIMIT) -if(WIN32) - # include wincrypt.h as a workaround for mingw-w64 __MINGW64_VERSION_MAJOR <= 5 header bug */ - check_symbol_exists("if_nametoindex" "winsock2.h;wincrypt.h;iphlpapi.h" HAVE_IF_NAMETOINDEX) # Windows Vista+ non-UWP */ -else() +if(NOT APPLE) + # Apple platforms do not offer pipe2(), but the iPhone Simulator-specific + # /usr/lib/system/libsystem_sim_kernel.dylib exports it. To avoid false + # detection, omit this feature check for Apple targets. + check_function_exists("pipe2" HAVE_PIPE2) +endif() + +if(NOT WIN32) check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # net/if.h check_function_exists("realpath" HAVE_REALPATH) check_function_exists("sched_yield" HAVE_SCHED_YIELD) @@ -1799,11 +1872,6 @@ check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) endif() -check_function_exists("setmode" HAVE_SETMODE) -if(WIN32 OR CYGWIN) - check_function_exists("_setmode" HAVE__SETMODE) -endif() - if(AMIGA) check_symbol_exists("CloseSocket" "${CURL_INCLUDES}" HAVE_CLOSESOCKET_CAMEL) # sys/socket.h proto/bsdsocket.h endif() @@ -1812,11 +1880,6 @@ check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM) endif() -if(NOT MSVC) - check_function_exists("snprintf" HAVE_SNPRINTF) # to match detection method in ./configure -elseif(MSVC_VERSION GREATER_EQUAL 1900) # Earlier MSVC compilers had faulty snprintf implementations - check_symbol_exists("snprintf" "stdio.h" HAVE_SNPRINTF) # snprintf may be a compatibility macro, not an exported function -endif() if(APPLE) check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) endif() @@ -1927,7 +1990,7 @@ { (void)argc; argv[0][0] = ' '; - return (argv[0][0] == ' ')?0:1; + return (argv[0][0] == ' ') ? 0 : 1; }" HAVE_WRITABLE_ARGV) endif() endif() # XXX(cmake): end @@ -1967,9 +2030,9 @@ if(WIN32) list(APPEND CURL_NETWORK_AND_TIME_LIBS "ws2_32") if(NOT WINDOWS_STORE) - list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi") + list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi") # for if_nametoindex() endif() - list(APPEND CURL_LIBS "bcrypt") + list(APPEND CURL_LIBS "bcrypt") # for BCryptGenRandom() # We use crypto functions that are not available for UWP apps if(NOT WINDOWS_STORE) @@ -1978,10 +2041,16 @@ # Link required libraries for USE_WIN32_CRYPTO or USE_SCHANNEL if(USE_WIN32_CRYPTO OR USE_SCHANNEL) - list(APPEND CURL_LIBS "advapi32" "crypt32") + # for CryptAcquireContext(), CryptCreateHash(), CryptDestroyHash(), CryptGetHashParam(), CryptHashData(), + # CryptReleaseContext() in NTLM, md4, md5, sha256, Schannel + # for CryptDestroyKey(), CryptEncrypt(), CryptImportKey() in NTLM + list(APPEND CURL_LIBS "advapi32") + # for Cert*() in openssl.c Native CA, Schannel + # for CryptDecodeObjectEx(), CryptQueryObject(), CryptStringToBinary(), PFXImportCertStore() in Schannel + list(APPEND CURL_LIBS "crypt32") endif() if(USE_WINDOWS_SSPI) - list(APPEND CURL_LIBS "secur32") + list(APPEND CURL_LIBS "secur32") # for InitSecurityInterface() endif() endif() @@ -1997,15 +2066,19 @@ # list on the linker command-line for some reason. This makes them appear # before dependencies detected via curl's custom Find modules, and breaks # linkers sensitive to lib order. There must be a better solution to this. +# Enable the workaround for all compilers, to make it available when using GCC +# to consume libcurl, regardless of the compiler used to build libcurl itself. if(CMAKE_C_COMPILER_ID STREQUAL "GNU") - foreach(_lib IN ITEMS OpenSSL::Crypto ZLIB::ZLIB) - if(TARGET "${_lib}") - add_library(CURL::${_lib} INTERFACE IMPORTED) - get_target_property(_libname "${_lib}" LOCATION) - set_target_properties(${_lib} PROPERTIES INTERFACE_LINK_LIBRARIES "${_libname}") - list(APPEND CURL_LIBS ${_lib}) - endif() - endforeach() + if(USE_OPENSSL AND TARGET OpenSSL::Crypto) + add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED) + set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) + list(APPEND CURL_LIBS CURL::OpenSSL_Crypto) + endif() + if(HAVE_LIBZ AND TARGET ZLIB::ZLIB) + add_library(CURL::ZLIB INTERFACE IMPORTED) + set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB) + list(APPEND CURL_LIBS CURL::ZLIB) + endif() if(WIN32) add_library(CURL::win32_winsock INTERFACE IMPORTED) set_target_properties(CURL::win32_winsock PROPERTIES INTERFACE_LINK_LIBRARIES "ws2_32") @@ -2019,12 +2092,6 @@ endif() if(CURL_LTO) - if(CMAKE_VERSION VERSION_LESS 3.9) - message(FATAL_ERROR "LTO has been requested, but your cmake version ${CMAKE_VERSION} is to old. You need at least 3.9") - endif() - - cmake_policy(SET CMP0069 NEW) - include(CheckIPOSupported) check_ipo_supported(RESULT CURL_HAS_LTO OUTPUT _lto_error LANGUAGES C) if(CURL_HAS_LTO) @@ -2114,18 +2181,18 @@ macro(curl_add_if _label) # Needs to be a macro to allow this indirection if(${ARGN}) - set(_items ${_items} "${_label}") + list(APPEND _items "${_label}") endif() endmacro() # NTLM support requires crypto functions from various SSL libs. # These conditions must match those in lib/curl_setup.h. -if(NOT CURL_DISABLE_NTLM AND +if(CURL_ENABLE_NTLM AND ((USE_OPENSSL AND HAVE_DES_ECB_ENCRYPT) OR (USE_MBEDTLS AND HAVE_MBEDTLS_DES_CRYPT_ECB) OR USE_GNUTLS OR USE_WIN32_CRYPTO OR - (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT))) + (USE_WOLFSSL AND HAVE_WC_DES_ECBENCRYPT))) set(_use_curl_ntlm_core ON) endif() @@ -2150,9 +2217,9 @@ curl_add_if("POP3S" NOT CURL_DISABLE_POP3 AND _ssl_enabled) curl_add_if("IMAP" NOT CURL_DISABLE_IMAP) curl_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND _ssl_enabled) -curl_add_if("SMB" NOT CURL_DISABLE_SMB AND +curl_add_if("SMB" CURL_ENABLE_SMB AND _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) -curl_add_if("SMBS" NOT CURL_DISABLE_SMB AND _ssl_enabled AND +curl_add_if("SMBS" CURL_ENABLE_SMB AND _ssl_enabled AND _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) curl_add_if("SMTP" NOT CURL_DISABLE_SMTP) curl_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND _ssl_enabled) @@ -2161,8 +2228,8 @@ curl_add_if("IPFS" NOT CURL_DISABLE_IPFS) curl_add_if("IPNS" NOT CURL_DISABLE_IPFS) curl_add_if("RTSP" NOT CURL_DISABLE_RTSP) -curl_add_if("RTMP" USE_LIBRTMP) curl_add_if("MQTT" NOT CURL_DISABLE_MQTT) +curl_add_if("MQTTS" NOT CURL_DISABLE_MQTT AND _ssl_enabled) curl_add_if("WS" NOT CURL_DISABLE_WEBSOCKETS) curl_add_if("WSS" NOT CURL_DISABLE_WEBSOCKETS AND _ssl_enabled) if(_items) @@ -2182,8 +2249,8 @@ curl_add_if("brotli" HAVE_BROTLI) curl_add_if("gsasl" USE_GSASL) curl_add_if("zstd" HAVE_ZSTD) -curl_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) -curl_add_if("asyn-rr" USE_ARES AND ENABLE_THREADED_RESOLVER AND USE_HTTPSRR) +curl_add_if("AsynchDNS" USE_RESOLV_ARES OR USE_RESOLV_THREADED) +curl_add_if("asyn-rr" USE_ARES AND USE_RESOLV_THREADED AND USE_HTTPSRR) curl_add_if("IDN" (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR USE_WIN32_IDN OR USE_APPLE_IDN) @@ -2196,33 +2263,27 @@ (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) curl_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) -curl_add_if("NTLM" NOT CURL_DISABLE_NTLM AND +curl_add_if("NTLM" CURL_ENABLE_NTLM AND (_use_curl_ntlm_core OR USE_WINDOWS_SSPI)) curl_add_if("TLS-SRP" USE_TLS_SRP) curl_add_if("HTTP2" USE_NGHTTP2) -curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC) +curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) curl_add_if("MultiSSL" CURL_WITH_MULTI_SSL) curl_add_if("HTTPS-proxy" NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW))) curl_add_if("Unicode" ENABLE_UNICODE) -curl_add_if("threadsafe" HAVE_ATOMIC OR - (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR - (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x0600)) +curl_add_if("threadsafe" HAVE_ATOMIC OR HAVE_THREADS_POSIX OR WIN32) curl_add_if("Debug" ENABLE_DEBUG) -curl_add_if("TrackMemory" ENABLE_CURLDEBUG) curl_add_if("ECH" _ssl_enabled AND HAVE_ECH) curl_add_if("HTTPSRR" _ssl_enabled AND USE_HTTPSRR) curl_add_if("PSL" USE_LIBPSL) curl_add_if("CAcert" CURL_CA_EMBED_SET) curl_add_if("SSLS-EXPORT" _ssl_enabled AND USE_SSLS_EXPORT) curl_add_if("AppleSecTrust" USE_APPLE_SECTRUST AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS)) +curl_add_if("NativeCA" NOT USE_APPLE_SECTRUST AND _ssl_enabled AND CURL_CA_NATIVE) if(_items) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - list(SORT _items CASE INSENSITIVE) - else() - list(SORT _items) - endif() + list(SORT _items CASE INSENSITIVE) endif() set(CURL_SUPPORTED_FEATURES_LIST "${_items}") string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") @@ -2238,11 +2299,7 @@ curl_add_if("Rustls" _ssl_enabled AND USE_RUSTLS) if(_items) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - list(SORT _items CASE INSENSITIVE) - else() - list(SORT _items) - endif() + list(SORT _items CASE INSENSITIVE) endif() string(REPLACE ";" " " SSL_BACKENDS "${_items}") message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}") @@ -2332,29 +2389,21 @@ # Assume the user will not need this information in the .pc file. continue() endif() - if(_lib MATCHES "CURL::") - # This is empty for 'CURL::*' targets and safe to ignore. - # Explicitly skip this query to avoid CMake v3.18 and older erroring out. - set(_libname "") - else() - get_target_property(_libname "${_lib}" LOCATION) - endif() - if(_libname) - list(APPEND _explicit_libs "${_libname}") - else() - get_target_property(_libs "${_lib}" INTERFACE_LINK_LIBRARIES) - if(_libs) - list(APPEND _explicit_libs "${_libs}") - endif() - get_target_property(_libdirs "${_lib}" INTERFACE_LINK_DIRECTORIES) - if(_libdirs) - list(APPEND _explicit_libdirs "${_libdirs}") - endif() - endif() - if(NOT _libname AND NOT _libs AND NOT _libdirs) + set(_libdirs "") + set(_libs "") + curl_collect_target_link_options("${_lib}") # look into the target recursively + list(APPEND _explicit_libdirs ${_libdirs}) + list(APPEND _explicit_libs ${_libs}) + if(NOT _libs AND NOT _libdirs AND NOT _lib STREQUAL Threads::Threads) message(WARNING "Bad lib in library list: ${_lib}") endif() - get_target_property(_modules "${_lib}" INTERFACE_LIBCURL_PC_MODULES) + if(_lib STREQUAL OpenSSL::SSL AND NOT HAVE_BORINGSSL) # BoringSSL does not provide openssl.pc + set(_modules "openssl") + elseif(_lib STREQUAL ZLIB::ZLIB) + set(_modules "zlib") + else() + get_target_property(_modules "${_lib}" INTERFACE_LIBCURL_PC_MODULES) + endif() if(_modules) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "${_modules}") endif() @@ -2374,8 +2423,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) cmake_path(SET _libdir NORMALIZE "${_libdir}") endif() - list(FIND _sys_libdirs "${_libdir}" _libdir_index) - if(_libdir_index LESS 0) + if(NOT _libdir IN_LIST _sys_libdirs) list(APPEND _ldflags "-L${_libdir}") endif() string(REGEX REPLACE "^lib" "" _libname "${_libname}") @@ -2403,8 +2451,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) cmake_path(SET _libdir NORMALIZE "${_libdir}") endif() - list(FIND _sys_libdirs "${_libdir}" _libdir_index) - if(_libdir_index LESS 0) + if(NOT _libdir IN_LIST _sys_libdirs) list(APPEND _ldflags "-L${_libdir}") endif() endforeach() @@ -2509,8 +2556,7 @@ FILES_MATCHING PATTERN "*.h") include(CMakePackageConfigHelpers) - write_basic_package_version_file( - "${_version_config}" + write_basic_package_version_file("${_version_config}" VERSION ${_curl_version} COMPATIBILITY SameMajorVersion) file(READ "${_version_config}" _generated_version_config) @@ -2532,17 +2578,20 @@ # TARGETS_EXPORT_NAME # CURL_SUPPORTED_FEATURES_LIST # CURL_SUPPORTED_PROTOCOLS_LIST + # CURL_USE_CMAKECONFIG + # CURL_USE_PKGCONFIG # HAVE_BROTLI # HAVE_GSSAPI # HAVE_LIBIDN2 # HAVE_LIBZ ZLIB_VERSION_MAJOR + # HAVE_THREADS_POSIX + # HAVE_THREADS_POSIX_BORINGSSL # HAVE_ZSTD # USE_ARES # USE_BACKTRACE # USE_GNUTLS # USE_GSASL # USE_LIBPSL - # USE_LIBRTMP # USE_LIBSSH # USE_LIBSSH2 # USE_LIBUV @@ -2579,7 +2628,6 @@ "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibgsasl.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibidn2.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibpsl.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibrtmp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh2.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibuv.cmake" @@ -2604,12 +2652,14 @@ COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake") endif() - install(FILES "${PROJECT_SOURCE_DIR}/scripts/wcurl" - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS - OWNER_READ OWNER_WRITE OWNER_EXECUTE - GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE) + if(BUILD_CURL_EXE) + install(FILES "${PROJECT_SOURCE_DIR}/scripts/wcurl" + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + endif() # The `-DEV` part is important string(REGEX REPLACE "([0-9]+\.[0-9]+)\.([0-9]+.*)" "\\2" CPACK_PACKAGE_VERSION_PATCH "${_curl_version}")
diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING index 3fa85eb..2f71d999 100644 --- a/Utilities/cmcurl/COPYING +++ b/Utilities/cmcurl/COPYING
@@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2025, Daniel Stenberg, <daniel@haxx.se>, and many +Copyright (c) 1996 - 2026, Daniel Stenberg, <daniel@haxx.se>, and many contributors, see the THANKS file. All rights reserved.
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index c73b93d..3b0af7a 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h
@@ -59,7 +59,7 @@ #define CURL_IGNORE_DEPRECATION(statements) statements #endif -#include "curlver.h" /* libcurl version defines */ +#include "curlver.h" /* libcurl version defines */ #include "system.h" /* determine things runtime */ #include <stdio.h> @@ -142,7 +142,7 @@ #define CURL_SOCKET_BAD INVALID_SOCKET #else typedef int curl_socket_t; -#define CURL_SOCKET_BAD -1 +#define CURL_SOCKET_BAD (-1) #endif #define curl_socket_typedef #endif /* curl_socket_typedef */ @@ -220,8 +220,8 @@ #define CURL_HTTPPOST_LARGE (1 << 7) char *showfilename; /* The filename to show. If not set, the - actual filename will be used (if this - is a file part) */ + actual filename is used (if this is + a file part) */ void *userp; /* custom pointer used for HTTPPOST_CALLBACK posts */ curl_off_t contentlen; /* alternative length of contents @@ -229,8 +229,8 @@ set. Added in 7.46.0 */ }; -/* This is a return code for the progress callback that, when returned, will - signal libcurl to continue executing the default progress function */ +/* This is a return code for the progress callback that, when returned, + signals libcurl to continue executing the default progress function */ #define CURL_PROGRESSFUNC_CONTINUE 0x10000001 /* This is the CURLOPT_PROGRESSFUNCTION callback prototype. It is now @@ -267,17 +267,17 @@ #ifndef CURL_MAX_HTTP_HEADER /* The only reason to have a max limit for this is to avoid the risk of a bad - server feeding libcurl with a never-ending header that will cause reallocs + server feeding libcurl with a never-ending header that causes reallocs infinitely */ #define CURL_MAX_HTTP_HEADER (100*1024) #endif /* This is a magic return code for the write callback that, when returned, - will signal libcurl to pause receiving on the current transfer. */ + signals libcurl to pause receiving on the current transfer. */ #define CURL_WRITEFUNC_PAUSE 0x10000001 /* This is a magic return code for the write callback that, when returned, - will signal an error from the callback. */ + signals an error from the callback. */ #define CURL_WRITEFUNC_ERROR 0xFFFFFFFF typedef size_t (*curl_write_callback)(char *buffer, @@ -285,7 +285,7 @@ size_t nitems, void *outstream); -/* This callback will be called when a new resolver request is made */ +/* This callback is called when a new resolver request is made */ typedef int (*curl_resolver_start_callback)(void *resolver_state, void *reserved, void *userdata); @@ -385,11 +385,11 @@ curl_off_t offset, int origin); /* 'whence' */ -/* This is a return code for the read callback that, when returned, will - signal libcurl to immediately abort the current transfer. */ +/* This is a return code for the read callback that, when returned, + signals libcurl to immediately abort the current transfer. */ #define CURL_READFUNC_ABORT 0x10000000 -/* This is a return code for the read callback that, when returned, will - signal libcurl to pause sending data on the current transfer. */ +/* This is a return code for the read callback that, when returned, + signals libcurl to pause sending data on the current transfer. */ #define CURL_READFUNC_PAUSE 0x10000001 /* Return code for when the trailing headers' callback has terminated @@ -609,7 +609,7 @@ or wrong format */ CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ CURLE_SSH, /* 79 - error from the SSH layer, somewhat - generic so the error message will be of + generic so the error message is of interest when this has happened */ CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL @@ -627,7 +627,7 @@ CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the - session will be queued */ + session is queued */ CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not match */ CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ @@ -820,7 +820,7 @@ * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper * CURLAUTH_BEARER - HTTP Bearer token authentication * CURLAUTH_ONLY - Use together with a single other type to force no - * authentication or just that single type + * authentication or that single type * CURLAUTH_ANY - All fine types set * CURLAUTH_ANYSAFE - All fine types except Basic */ @@ -842,10 +842,13 @@ #define CURLAUTH_BEARER (((unsigned long)1) << 6) #define CURLAUTH_AWS_SIGV4 (((unsigned long)1) << 7) #define CURLAUTH_ONLY (((unsigned long)1) << 31) -#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) -#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE)) +#define CURLAUTH_ANY ((~CURLAUTH_DIGEST_IE) & \ + ((unsigned long)0xffffffff)) +#define CURLAUTH_ANYSAFE ((~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE)) & \ + ((unsigned long)0xffffffff)) -#define CURLSSH_AUTH_ANY ~0L /* all types supported by server */ +/* all types supported by server */ +#define CURLSSH_AUTH_ANY ((unsigned long)0xffffffff) #define CURLSSH_AUTH_NONE 0L /* none allowed, silly but complete */ #define CURLSSH_AUTH_PUBLICKEY (1L << 0) /* public/private key files */ #define CURLSSH_AUTH_PASSWORD (1L << 1) /* password */ @@ -885,7 +888,7 @@ CURLKHSTAT_REJECT, /* reject the connection, return an error */ CURLKHSTAT_DEFER, /* do not accept it, but we cannot answer right now. Causes a CURLE_PEER_FAILED_VERIFICATION error but the - connection will be left intact etc */ + connection is left intact etc */ CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key */ CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ }; @@ -1100,7 +1103,8 @@ #define CURLPROTO_SMBS (1L << 27) #define CURLPROTO_MQTT (1L << 28) #define CURLPROTO_GOPHERS (1L << 29) -#define CURLPROTO_ALL (~0L) /* enable everything */ +#define CURLPROTO_ALL ((unsigned long)0xffffffff) /* old-style enable + "everything" */ /* long may be 32 or 64 bits, but we should never depend on anything else but 32 */ @@ -1113,8 +1117,9 @@ /* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the string options from the header file */ -#define CURLOPT(na,t,nu) na = t + nu -#define CURLOPTDEPRECATED(na,t,nu,v,m) na CURL_DEPRECATED(v,m) = t + nu +#define CURLOPT(na, t, nu) na = ((t) + (nu)) +#define CURLOPTDEPRECATED(na, t, nu, v, m) na CURL_DEPRECATED(v, m) \ + = ((t) + (nu)) /* CURLOPT aliases that make no runtime difference */ @@ -1165,12 +1170,12 @@ * bytes big. */ CURLOPT(CURLOPT_ERRORBUFFER, CURLOPTTYPE_OBJECTPOINT, 10), - /* Function that will be called to store the output (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ + /* Function that is called to store the output (instead of fwrite). The + * parameters use fwrite() syntax, make sure to follow them. */ CURLOPT(CURLOPT_WRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 11), - /* Function that will be called to read the input (instead of fread). The - * parameters will use fread() syntax, make sure to follow them. */ + /* Function that is called to read the input (instead of fread). The + * parameters use fread() syntax, make sure to follow them. */ CURLOPT(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12), /* Time-out the read operation after this amount of seconds */ @@ -1244,7 +1249,7 @@ CURLOPT(CURLOPT_QUOTE, CURLOPTTYPE_SLISTPOINT, 28), /* send FILE * or void * to store headers to, if you use a callback it - is simply passed to the callback unmodified */ + is passed to the callback unmodified */ CURLOPT(CURLOPT_HEADERDATA, CURLOPTTYPE_CBPOINT, 29), /* point to a file to read the initial cookies from, also enables @@ -1324,7 +1329,7 @@ /* 55 = OBSOLETE */ /* DEPRECATED - * Function that will be called instead of the internal progress display + * Function that is called instead of the internal progress display * function. This function should be defined as the curl_progress_callback * prototype defines. */ CURLOPTDEPRECATED(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56, @@ -1353,7 +1358,7 @@ /* Set the krb4/5 security level, this also enables krb4/5 awareness. This * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string - * is set but does not match one of these, 'private' will be used. */ + * is set but does not match one of these, 'private' is used. */ CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63, 8.17.0, "removed"), @@ -1406,8 +1411,8 @@ OK within this time, then fine... This only aborts the connect phase. */ CURLOPT(CURLOPT_CONNECTTIMEOUT, CURLOPTTYPE_LONG, 78), - /* Function that will be called to store headers (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ + /* Function that is called to store headers (instead of fwrite). The + * parameters use fwrite() syntax, make sure to follow them. */ CURLOPT(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79), /* Set this to force the HTTP request to get back to GET. Only really usable @@ -1432,7 +1437,7 @@ CURLOPT(CURLOPT_HTTP_VERSION, CURLOPTTYPE_VALUES, 84), /* Specifically switch on or off the FTP engine's use of the EPSV command. By - default, that one will always be attempted before the more traditional + default, that one is always attempted before the more traditional PASV command. */ CURLOPT(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85), @@ -1510,7 +1515,7 @@ CURLOPT(CURLOPT_UNRESTRICTED_AUTH, CURLOPTTYPE_LONG, 105), /* Specifically switch on or off the FTP engine's use of the EPRT command ( - it also disables the LPRT attempt). By default, those ones will always be + it also disables the LPRT attempt). By default, those ones are always attempted before the good old traditional PORT command. */ CURLOPT(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106), @@ -1550,7 +1555,7 @@ systems with support for more than one, i.e IPv4 _and_ IPv6. */ CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_VALUES, 113), - /* Set this option to limit the size of a file that will be downloaded from + /* Set this option to limit the size of a file that is to be downloaded from an HTTP or FTP server. Note there is also _LARGE version which adds large file support for @@ -1567,13 +1572,13 @@ */ CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116), - /* Sets the maximum size of data that will be downloaded from + /* Sets the maximum size of data that is to be downloaded from * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. */ CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117), /* Set this option to the filename of your .netrc file you want libcurl - to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + to parse (using the CURLOPT_NETRC option). If not set, libcurl does a poor attempt to find the user's home directory and check for a .netrc file in there. */ CURLOPT(CURLOPT_NETRC_FILE, CURLOPTTYPE_STRINGPOINT, 118), @@ -1631,7 +1636,7 @@ /* Set to non-zero to skip the IP address received in a 227 PASV FTP server response. Typically used for FTP-SSL purposes but is not restricted to - that. libcurl will then instead use the same IP address it used for the + that. libcurl is then instead using the same IP address it used for the control connection. */ CURLOPT(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137), @@ -1643,7 +1648,7 @@ CURLOPT(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139), /* Number of ports to try, including the first one set with LOCALPORT. - Thus, setting it to 1 will make no additional attempts but the first. + Thus, setting it to 1 makes no additional attempts but the first. */ CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140), @@ -1651,19 +1656,19 @@ extracting it with CURLINFO_LASTSOCKET */ CURLOPT(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141), - /* Function that will be called to convert from the + /* Function that is called to convert from the network encoding (instead of using the iconv calls in libcurl) */ CURLOPTDEPRECATED(CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 142, 7.82.0, "Serves no purpose anymore"), - /* Function that will be called to convert to the + /* Function that is called to convert to the network encoding (instead of using the iconv calls in libcurl) */ CURLOPTDEPRECATED(CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 143, 7.82.0, "Serves no purpose anymore"), - /* Function that will be called to convert from UTF8 + /* Function that is called to convert from UTF8 (instead of using the iconv calls in libcurl) Note that this is used only for SSL certificate processing */ CURLOPTDEPRECATED(CURLOPT_CONV_FROM_UTF8_FUNCTION, @@ -1757,9 +1762,9 @@ /* Comma separated list of hostnames defining no-proxy zones. These should match both hostnames directly, and hostnames within a domain. For - example, local.com will match local.com and www.local.com, but NOT + example, local.com matches local.com and www.local.com, but NOT notlocal.com or www.notlocal.com. For compatibility with other - implementations of this, .local.com will be considered to be the same as + implementations of this, .local.com is considered to be the same as local.com. A single * is the only valid wildcard, and effectively disables the use of proxy. */ CURLOPT(CURLOPT_NOPROXY, CURLOPTTYPE_STRINGPOINT, 177), @@ -1869,8 +1874,8 @@ in outgoing requests. The current default is 0, but it might change in a future libcurl release. - libcurl will ask for the compressed methods it knows of, and if that - is not any, it will not ask for transfer-encoding at all even if this + libcurl asks for the compressed methods it knows of, and if that + is not any, it does not ask for transfer-encoding at all even if this option is set to 1. */ CURLOPT(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207), @@ -1906,7 +1911,7 @@ /* Enable/disable SASL initial response */ CURLOPT(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218), - /* Function that will be called instead of the internal progress display + /* Function that is called instead of the internal progress display * function. This function should be defined as the curl_xferinfo_callback * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ CURLOPT(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219), @@ -2088,7 +2093,7 @@ /* Head start in milliseconds to give happy eyeballs. */ CURLOPT(CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOPTTYPE_LONG, 271), - /* Function that will be called before a resolver request is made */ + /* Function that is called before a resolver request is made */ CURLOPT(CURLOPT_RESOLVER_START_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 272), /* User data to pass to the resolver start callback. */ @@ -2119,7 +2124,7 @@ /* Specify URL using CURL URL API. */ CURLOPT(CURLOPT_CURLU, CURLOPTTYPE_OBJECTPOINT, 282), - /* add trailing data just after no more data is available */ + /* add trailing data after no more data is available */ CURLOPT(CURLOPT_TRAILERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 283), /* pointer to be passed to HTTP_TRAILER_FUNCTION */ @@ -2197,8 +2202,8 @@ /* used by scp/sftp to verify the host's public key */ CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311), - /* Function that will be called immediately before the initial request - is made on a connection (after any protocol negotiation step). */ + /* Function that is called immediately before the initial request + is made on a connection (after any protocol negotiation step). */ CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312), /* Data passed to the CURLOPT_PREREQFUNCTION callback */ @@ -2278,7 +2283,6 @@ #define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD #define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL -/* */ #define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT /* Added in 8.2.0 */ @@ -2344,16 +2348,16 @@ #define CURL_RTSPREQ_LAST 12L /* not used */ /* These enums are for use with the CURLOPT_NETRC option. */ -#define CURL_NETRC_IGNORED 0L /* The .netrc will never be read. +#define CURL_NETRC_IGNORED 0L /* The .netrc is never read. This is the default. */ -#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL will be preferred +#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL is preferred to one in the .netrc. */ -#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL will be ignored. +#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL is ignored. Unless one is set programmatically, the - .netrc will be queried. */ + .netrc is queried. */ enum CURL_NETRC_OPTION { - /* we set a single member here, just to make sure we still provide the enum, - but the values to use are defined above with L suffixes */ + /* we set a single member here, to make sure we still provide the enum, but + the values to use are defined above with L suffixes */ CURL_NETRC_LAST = 3 }; @@ -2382,7 +2386,7 @@ #define CURL_TLSAUTH_SRP 1L enum CURL_TLSAUTH { - /* we set a single member here, just to make sure we still provide the enum, + /* we set a single member here, to make sure we still provide the enum, but the values to use are defined above with L suffixes */ CURL_TLSAUTH_LAST = 2 }; @@ -2405,7 +2409,7 @@ #define CURL_TIMECOND_LASTMOD 3L typedef enum { - /* we set a single member here, just to make sure we still provide + /* we set a single member here, to make sure we still provide the enum typedef, but the values to use are defined above with L suffixes */ CURL_TIMECOND_LAST = 4 @@ -2631,7 +2635,7 @@ /* * callback function for curl_formget() - * The void *arg pointer will be the one passed as second argument to + * The void *arg pointer is the one passed as second argument to * curl_formget(). * The character buffer passed to it must not be freed. * Should return the buffer length passed to it as the argument "len" on @@ -2646,7 +2650,7 @@ * DESCRIPTION * * Serialize a curl_httppost struct built with curl_formadd(). - * Accepts a void pointer as second argument which will be passed to + * Accepts a void pointer as second argument which is passed to * the curl_formget_callback function. * Returns 0 on success. */ @@ -2691,7 +2695,7 @@ * %XX versions). This function returns a new allocated string or NULL if an * error occurred. */ -CURL_EXTERN char *curl_easy_escape(CURL *handle, +CURL_EXTERN char *curl_easy_escape(CURL *curl, const char *string, int length); @@ -2704,15 +2708,15 @@ * * DESCRIPTION * - * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * Unescapes URL encoding in strings (converts all %XX codes to their 8-bit * versions). This function returns a new allocated string or NULL if an error * occurred. * Conversion Note: On non-ASCII platforms the ASCII %XX codes are * converted into the host encoding. */ -CURL_EXTERN char *curl_easy_unescape(CURL *handle, +CURL_EXTERN char *curl_easy_unescape(CURL *curl, const char *string, - int length, + int inlength, int *outlength); /* the previous version */ @@ -2752,7 +2756,7 @@ * initialize libcurl and set user defined memory management callback * functions. Users can implement memory management routines to check for * memory leaks, check for misuse of the curl library etc. User registered - * callback routines will be invoked by this library instead of the system + * callback routines is invoked by this library instead of the system * memory management routines like malloc, free etc. */ CURL_EXTERN CURLcode curl_global_init_mem(long flags, @@ -2802,8 +2806,8 @@ * * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The * backend can also be specified via the name parameter (passing -1 as id). If - * both id and name are specified, the name will be ignored. If neither id nor - * name are specified, the function will fail with CURLSSLSET_UNKNOWN_BACKEND + * both id and name are specified, the name is ignored. If neither id nor + * name are specified, the function fails with CURLSSLSET_UNKNOWN_BACKEND * and set the "avail" pointer to the NULL-terminated list of available * backends. * @@ -2814,7 +2818,7 @@ * NULL-terminated list of available SSL backends. * * The SSL backend can be set only once. If it has already been set, a - * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. + * subsequent attempt to change it results in a CURLSSLSET_TOO_LATE. */ struct curl_ssl_backend { @@ -2838,8 +2842,8 @@ * * DESCRIPTION * - * Appends a string to a linked list. If no list exists, it will be created - * first. Returns the new list, after appending. + * Appends a string to a linked list. If no list exists, it is created first. + * Returns the new list, after appending. */ CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *list, const char *data); @@ -2987,7 +2991,8 @@ CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68, CURLINFO_HTTPAUTH_USED = CURLINFO_LONG + 69, CURLINFO_PROXYAUTH_USED = CURLINFO_LONG + 70, - CURLINFO_LASTONE = 70 + CURLINFO_SIZE_DELIVERED = CURLINFO_OFF_T + 71, + CURLINFO_LASTONE = 71 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -3020,9 +3025,8 @@ /* Different data locks for a single share */ typedef enum { CURL_LOCK_DATA_NONE = 0, - /* CURL_LOCK_DATA_SHARE is used internally to say that - * the locking is just made to change the internal state of the share - * itself. + /* CURL_LOCK_DATA_SHARE is used internally to say that the locking is made + * to change the internal state of the share itself. */ CURL_LOCK_DATA_SHARE, CURL_LOCK_DATA_COOKIE, @@ -3072,9 +3076,8 @@ } CURLSHoption; CURL_EXTERN CURLSH *curl_share_init(void); -CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option, - ...); -CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *share); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *sh); /**************************************************************************** * Structures for querying information about the curl library at runtime. @@ -3096,11 +3099,10 @@ CURLVERSION_LAST /* never actually use this */ } CURLversion; -/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by - basically all programs ever that want to get version information. It is - meant to be a built-in version number for what kind of struct the caller - expects. If the struct ever changes, we redefine the NOW to another enum - from above. */ +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by programs + that want to get version information. It is meant to be a built-in + version number for what kind of struct the caller expects. If the struct + ever changes, we redefine the NOW to another enum from above. */ #define CURLVERSION_NOW CURLVERSION_TWELFTH struct curl_version_info_data { @@ -3183,7 +3185,8 @@ supported */ #define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ #define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ -#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported + (deprecated) */ #define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ #define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper is supported */ @@ -3212,7 +3215,7 @@ * This function returns a pointer to a static copy of the version info * struct. See above. */ -CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion stamp); /* * NAME curl_easy_strerror() @@ -3223,7 +3226,7 @@ * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ -CURL_EXTERN const char *curl_easy_strerror(CURLcode); +CURL_EXTERN const char *curl_easy_strerror(CURLcode error); /* * NAME curl_share_strerror() @@ -3234,7 +3237,7 @@ * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ -CURL_EXTERN const char *curl_share_strerror(CURLSHcode); +CURL_EXTERN const char *curl_share_strerror(CURLSHcode error); /* * NAME curl_easy_pause() @@ -3242,16 +3245,16 @@ * DESCRIPTION * * The curl_easy_pause function pauses or unpauses transfers. Select the new - * state by setting the bitmask, use the convenience defines below. + * state by setting the action bitmask, use the convenience defines below. * */ -CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); +CURL_EXTERN CURLcode curl_easy_pause(CURL *curl, int action); #define CURLPAUSE_RECV (1 << 0) -#define CURLPAUSE_RECV_CONT (0) +#define CURLPAUSE_RECV_CONT 0 #define CURLPAUSE_SEND (1 << 2) -#define CURLPAUSE_SEND_CONT (0) +#define CURLPAUSE_SEND_CONT 0 #define CURLPAUSE_ALL (CURLPAUSE_RECV | CURLPAUSE_SEND) #define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT | CURLPAUSE_SEND_CONT) @@ -3264,7 +3267,7 @@ * The curl_easy_ssls_import function adds a previously exported SSL session * to the SSL session cache of the easy handle (or the underlying share). */ -CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *handle, +CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, const unsigned char *shmac, size_t shmac_len, @@ -3273,7 +3276,7 @@ /* This is the curl_ssls_export_cb callback prototype. It * is passed to curl_easy_ssls_export() to extract SSL sessions/tickets. */ -typedef CURLcode curl_ssls_export_cb(CURL *handle, +typedef CURLcode curl_ssls_export_cb(CURL *curl, void *userptr, const char *session_key, const unsigned char *shmac, @@ -3295,7 +3298,7 @@ * callback. * */ -CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle, +CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *curl, curl_ssls_export_cb *export_fn, void *userptr); @@ -3304,7 +3307,7 @@ #endif /* unfortunately, the easy.h and multi.h include files need options and info - stuff before they can be included! */ + stuff before they can be included! */ #include "easy.h" /* nothing in curl is fun without the easy stuff */ #include "multi.h" #include "urlapi.h" @@ -3322,13 +3325,19 @@ #else #if defined(__STDC__) && (__STDC__ >= 1) #if 0 /* Triggers clang -Wdisabled-macro-expansion, skip for CMake. */ -/* This preprocessor magic that replaces a call with the exact same call is - only done to make sure application authors pass exactly three arguments - to these functions. */ -#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) -#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) -#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) -#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +/* This preprocessor magic ensures that application authors pass exactly three + arguments to these functions. For compatibility with C++ global namespace + '::' and reusing these symbols as method names, while also avoiding + recursive macros, use a two-stage solution. */ +#define curl_exactly_three_arguments(a, b, c) (a, b, c) +#define curl_easy_setopt(handle, opt, param) \ + curl_easy_setopt curl_exactly_three_arguments(handle, opt, param) +#define curl_easy_getinfo(handle, info, arg) \ + curl_easy_getinfo curl_exactly_three_arguments(handle, info, arg) +#define curl_share_setopt(share, opt, param) \ + curl_share_setopt curl_exactly_three_arguments(share, opt, param) +#define curl_multi_setopt(handle, opt, param) \ + curl_multi_setopt curl_exactly_three_arguments(handle, opt, param) #endif #endif /* __STDC__ >= 1 */ #endif /* gcc >= 4.3 && !__cplusplus && !CURL_DISABLE_TYPECHECK */
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 999e312..8ae3fbc 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -32,16 +32,16 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.18.0" +#define LIBCURL_VERSION "8.20.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 18 +#define LIBCURL_VERSION_MINOR 20 #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 - always follow this syntax: + parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always + follows this syntax: 0xXXYYZZ @@ -58,7 +58,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 0x081200 +#define LIBCURL_VERSION_NUM 0x081400 /* * This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h index 5b3cdbd..197e6e7 100644 --- a/Utilities/cmcurl/include/curl/easy.h +++ b/Utilities/cmcurl/include/curl/easy.h
@@ -51,7 +51,7 @@ * Request internal information from the curl session with this function. * The third argument MUST be pointing to the specific type of the used option * which is documented in each man page of the option. The data pointed to - * will be filled in accordingly and can be relied upon only if the function + * is filled in accordingly and can be relied upon only if the function * returns CURLE_OK. This function is intended to get used *AFTER* a performed * transfer, all results from this function are undefined until the transfer * is completed. @@ -78,7 +78,7 @@ * DESCRIPTION * * Re-initializes a curl handle to the default values. This puts back the - * handle to the same state as it was in when it was just created. + * handle to the same state as it was in when it was created. * * It does keep: live connections, the Session ID cache, the DNS cache and the * cookies.
diff --git a/Utilities/cmcurl/include/curl/header.h b/Utilities/cmcurl/include/curl/header.h index e7334b5..5c3281a 100644 --- a/Utilities/cmcurl/include/curl/header.h +++ b/Utilities/cmcurl/include/curl/header.h
@@ -31,7 +31,7 @@ struct curl_header { char *name; /* this might not use the same case */ char *value; - size_t amount; /* number of headers using this name */ + size_t amount; /* number of headers using this name */ size_t index; /* ... of this instance, 0 or higher */ unsigned int origin; /* see bits below */ void *anchor; /* handle privately used by libcurl */ @@ -55,14 +55,14 @@ CURLHE_NOT_BUILT_IN /* if API was disabled in the build */ } CURLHcode; -CURL_EXTERN CURLHcode curl_easy_header(CURL *easy, +CURL_EXTERN CURLHcode curl_easy_header(CURL *curl, const char *name, - size_t index, + size_t nameindex, unsigned int origin, int request, struct curl_header **hout); -CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *easy, +CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *curl, unsigned int origin, int request, struct curl_header *prev);
diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h index 531c1a9..060b73e 100644 --- a/Utilities/cmcurl/include/curl/multi.h +++ b/Utilities/cmcurl/include/curl/multi.h
@@ -77,9 +77,8 @@ CURLM_LAST } CURLMcode; -/* just to make code nicer when using curl_multi_socket() you can now check - for CURLM_CALL_MULTI_SOCKET too in the same style it works for - curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +/* You can check for CURLM_CALL_MULTI_SOCKET too in the same style it works + for curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ #define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM /* bitmask bits for CURLMOPT_PIPELINING */ @@ -133,8 +132,8 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, - CURL *curl_handle); +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *m, + CURL *curl); /* * Name: curl_multi_remove_handle() @@ -143,8 +142,8 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, - CURL *curl_handle); +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *m, + CURL *curl); /* * Name: curl_multi_fdset() @@ -155,7 +154,7 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *m, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, @@ -169,7 +168,7 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -183,7 +182,7 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_poll(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -196,25 +195,25 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); +CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *m); /* * Name: curl_multi_perform() * - * Desc: When the app thinks there is data available for curl it calls this + * Desc: When the app thinks there is data available for curl it calls this * function to read/write whatever there is right now. This returns * as soon as the reads and writes are done. This function does not * require that there actually is data available for reading or that - * data can be written, it can be called just in case. It returns - * the number of handles that still transfer data in the second - * argument's integer-pointer. + * data can be written, it can be called. It returns the number of + * handles that still transfer data in the second argument's + * integer-pointer. * * Returns: CURLMcode type, general multi error code. *NOTE* that this only * returns errors etc regarding the whole multi stack. There might * still have occurred problems on individual transfers even when * this returns OK. */ -CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *m, int *running_handles); /* @@ -222,34 +221,34 @@ * * Desc: Cleans up and removes a whole multi stack. It does not free or * touch any individual easy handles in any way. We need to define - * in what state those handles will be if this function is called - * in the middle of a transfer. + * in what state those handles are going to be if this function is + * called in the middle of a transfer. * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *m); /* * Name: curl_multi_info_read() * * Desc: Ask the multi handle if there is any messages/informationals from * the individual transfers. Messages include informationals such as - * error code from the transfer or just the fact that a transfer is + * error code from the transfer or the fact that a transfer is * completed. More details on these should be written down as well. * - * Repeated calls to this function will return a new struct each - * time, until a special "end of msgs" struct is returned as a signal - * that there is no more to get at this point. + * Repeated calls to this function return a new struct each time, + * until a special "end of msgs" struct is returned as a signal that + * there is no more to get at this point. * - * The data the returned pointer points to will not survive calling + * The data the returned pointer points to does not survive calling * curl_multi_cleanup(). * * The 'CURLMsg' struct is meant to be simple and only contain basic - * information. If more involved information is wanted, we will - * provide the particular "transfer handle" in that struct and that + * information. If more involved information is wanted, we provide + * the particular "transfer handle" in that struct and that * should/could/would be used in subsequent curl_easy_getinfo() calls * (or similar). The point being that we must never expose complex - * structs to applications, as then we will undoubtably get backwards + * structs to applications, as then we undoubtably get backwards * compatibility problems in the future. * * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out @@ -257,7 +256,7 @@ * queue (after this read) in the integer the second argument points * to. */ -CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue); /* @@ -269,7 +268,7 @@ * * Returns: A pointer to a null-terminated error message. */ -CURL_EXTERN const char *curl_multi_strerror(CURLMcode); +CURL_EXTERN const char *curl_multi_strerror(CURLMcode error); /* * Name: curl_multi_socket() and @@ -309,27 +308,27 @@ * * Returns: The callback should return zero. */ -typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ +typedef int (*curl_multi_timer_callback)(CURLM *m, /* multi handle */ long timeout_ms, /* see above */ void *userp); /* private callback pointer */ CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); +curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles); -CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s, int ev_bitmask, int *running_handles); CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket_all(CURLM *multi_handle, int *running_handles); +curl_multi_socket_all(CURLM *m, int *running_handles); #ifndef CURL_ALLOW_OLD_MULTI_SOCKET /* This macro below was added in 7.16.3 to push users who recompile to use * the new curl_multi_socket_action() instead of the old curl_multi_socket() */ -#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) +#define curl_multi_socket(x, y, z) curl_multi_socket_action(x, y, 0, z) #endif /* @@ -341,8 +340,8 @@ * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, - long *milliseconds); +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *m, + long *timeout_ms); typedef enum { /* This is the socket callback function pointer */ @@ -370,11 +369,11 @@ CURLOPT(CURLMOPT_MAX_PIPELINE_LENGTH, CURLOPTTYPE_LONG, 8), /* a connection with a content-length longer than this - will not be considered for pipelining */ + is not considered for pipelining */ CURLOPT(CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 9), /* a connection with a chunk length longer than this - will not be considered for pipelining */ + is not considered for pipelining */ CURLOPT(CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 10), /* a list of site names(+port) that are blocked from pipelining */ @@ -404,20 +403,30 @@ /* This is the argument passed to the notify callback */ CURLOPT(CURLMOPT_NOTIFYDATA, CURLOPTTYPE_OBJECTPOINT, 19), + /* maximum number of threads used with threaded DNS resolver */ + CURLOPT(CURLMOPT_RESOLVE_THREADS_MAX, CURLOPTTYPE_LONG, 20), + + /* set to 1L for not joining threads when multi is cleaned up */ + CURLOPT(CURLMOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 21), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; /* Definition of bits for the CURLMOPT_NETWORK_CHANGED argument: */ -/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing - connections. Connections that are idle will be closed. Ongoing transfers - will continue with the connection they have. */ -#define CURLMNWC_CLEAR_CONNS (1L << 0) +/* - CURLMNWC_CLEAR_ALL tells libcurl to clear "everything" that could be + associated with this network, including both connections and DNS data. */ +#define CURLMNWC_CLEAR_ALL (1L << 0) -/* - CURLMNWC_CLEAR_DNS tells libcurl to prevent further reuse of existing - connections. Connections that are idle will be closed. Ongoing transfers - will continue with the connection they have. */ -#define CURLMNWC_CLEAR_DNS (1L << 0) +/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing + connections. Connections that are idle are closed. Ongoing transfers do + continue with the connection they have. */ +#define CURLMNWC_CLEAR_CONNS (1L << 1) + +/* - CURLMNWC_CLEAR_DNS tells libcurl to clear the DNS cache associated with + this multi handle. Ongoing transfers keep using their already resolved + addresses, but future name resolutions are performed again. */ +#define CURLMNWC_CLEAR_DNS (1L << 2) /* * Name: curl_multi_setopt() @@ -426,7 +435,7 @@ * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...); /* @@ -438,7 +447,7 @@ * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, void *sockp); /* @@ -451,7 +460,7 @@ * * Returns: NULL on failure, otherwise a CURL **array pointer */ -CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle); +CURL_EXTERN CURL **curl_multi_get_handles(CURLM *m); typedef enum { CURLMINFO_NONE, /* first, never use this */ @@ -480,7 +489,7 @@ * * Returns: CULRM_OK or error when value could not be obtained. */ -CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *m, CURLMinfo_offt info, curl_off_t *pvalue); @@ -515,11 +524,11 @@ * * Desc: Ask curl for fds for polling. The app can use these to poll on. * We want curl_multi_perform() called as soon as one of them are - * ready. Passing zero size allows to get just a number of fds. + * ready. Passing zero size allows to get a number of fds. * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi, +CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *m, struct curl_waitfd *ufds, unsigned int size, unsigned int *fd_count); @@ -533,15 +542,15 @@ /* * Callback to install via CURLMOPT_NOTIFYFUNCTION. */ -typedef void (*curl_notify_callback)(CURLM *multi, +typedef void (*curl_notify_callback)(CURLM *m, unsigned int notification, CURL *easy, void *user_data); -CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *multi, +CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification); -CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *multi, +CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification); #ifdef __cplusplus
diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index a5b3e9e..c2dbab5 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h
@@ -45,7 +45,7 @@ * size of off_t is independent of large file support settings. Keep your * build on the safe side avoiding an off_t gating. If you have a 64-bit * off_t then take for sure that another 64-bit data type exists, dig deeper - * and you will find it. + * to find it. * */ @@ -367,20 +367,20 @@ #define CURL_PULL_SYS_POLL_H #endif -/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ -/* sys/types.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file + sys/types.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_TYPES_H # include <sys/types.h> #endif -/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ -/* sys/socket.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file + sys/socket.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_SOCKET_H # include <sys/socket.h> #endif -/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file */ -/* sys/poll.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file + sys/poll.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_POLL_H # include <sys/poll.h> #endif
diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h index 0642afd..d600c73 100644 --- a/Utilities/cmcurl/include/curl/typecheck-gcc.h +++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h
@@ -37,192 +37,192 @@ * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x * when compiling with -Wlogical-op. * - * To add an option that uses the same type as an existing option, you will - * just need to extend the appropriate _curl_*_option macro + * To add an option that uses the same type as an existing option, you need + * to extend the appropriate _curl_*_option macro */ #define curl_easy_setopt(handle, option, value) \ __extension__({ \ - if(__builtin_constant_p(option)) { \ - CURL_IGNORE_DEPRECATION( \ - if(curlcheck_long_option(option)) \ - if(!curlcheck_long(value)) \ - Wcurl_easy_setopt_err_long(); \ - if(curlcheck_off_t_option(option)) \ - if(!curlcheck_off_t(value)) \ - Wcurl_easy_setopt_err_curl_off_t(); \ - if(curlcheck_string_option(option)) \ - if(!curlcheck_string(value)) \ - Wcurl_easy_setopt_err_string(); \ - if((option) == CURLOPT_PRIVATE) { } \ - if(curlcheck_write_cb_option(option)) \ - if(!curlcheck_write_cb(value)) \ - Wcurl_easy_setopt_err_write_callback(); \ - if(curlcheck_curl_option(option)) \ - if(!curlcheck_curl(value)) \ - Wcurl_easy_setopt_err_curl(); \ - if((option) == CURLOPT_RESOLVER_START_FUNCTION) \ - if(!curlcheck_resolver_start_callback(value)) \ - Wcurl_easy_setopt_err_resolver_start_callback(); \ - if((option) == CURLOPT_READFUNCTION) \ - if(!curlcheck_read_cb(value)) \ - Wcurl_easy_setopt_err_read_cb(); \ - if((option) == CURLOPT_IOCTLFUNCTION) \ - if(!curlcheck_ioctl_cb(value)) \ - Wcurl_easy_setopt_err_ioctl_cb(); \ - if((option) == CURLOPT_SOCKOPTFUNCTION) \ - if(!curlcheck_sockopt_cb(value)) \ - Wcurl_easy_setopt_err_sockopt_cb(); \ - if((option) == CURLOPT_OPENSOCKETFUNCTION) \ - if(!curlcheck_opensocket_cb(value)) \ - Wcurl_easy_setopt_err_opensocket_cb(); \ - if((option) == CURLOPT_PROGRESSFUNCTION) \ - if(!curlcheck_progress_cb(value)) \ - Wcurl_easy_setopt_err_progress_cb(); \ - if((option) == CURLOPT_XFERINFOFUNCTION) \ - if(!curlcheck_xferinfo_cb(value)) \ - Wcurl_easy_setopt_err_xferinfo_cb(); \ - if((option) == CURLOPT_DEBUGFUNCTION) \ - if(!curlcheck_debug_cb(value)) \ - Wcurl_easy_setopt_err_debug_cb(); \ - if((option) == CURLOPT_SSL_CTX_FUNCTION) \ - if(!curlcheck_ssl_ctx_cb(value)) \ - Wcurl_easy_setopt_err_ssl_ctx_cb(); \ - if(curlcheck_conv_cb_option(option)) \ - if(!curlcheck_conv_cb(value)) \ - Wcurl_easy_setopt_err_conv_cb(); \ - if((option) == CURLOPT_SEEKFUNCTION) \ - if(!curlcheck_seek_cb(value)) \ - Wcurl_easy_setopt_err_seek_cb(); \ - if((option) == CURLOPT_CHUNK_BGN_FUNCTION) \ - if(!curlcheck_chunk_bgn_cb(value)) \ - Wcurl_easy_setopt_err_chunk_bgn_cb(); \ - if((option) == CURLOPT_CHUNK_END_FUNCTION) \ - if(!curlcheck_chunk_end_cb(value)) \ - Wcurl_easy_setopt_err_chunk_end_cb(); \ - if((option) == CURLOPT_CLOSESOCKETFUNCTION) \ - if(!curlcheck_close_socket_cb(value)) \ - Wcurl_easy_setopt_err_close_socket_cb(); \ - if((option) == CURLOPT_FNMATCH_FUNCTION) \ - if(!curlcheck_fnmatch_cb(value)) \ - Wcurl_easy_setopt_err_fnmatch_cb(); \ - if((option) == CURLOPT_HSTSREADFUNCTION) \ - if(!curlcheck_hstsread_cb(value)) \ - Wcurl_easy_setopt_err_hstsread_cb(); \ - if((option) == CURLOPT_HSTSWRITEFUNCTION) \ - if(!curlcheck_hstswrite_cb(value)) \ - Wcurl_easy_setopt_err_hstswrite_cb(); \ - if((option) == CURLOPT_SSH_HOSTKEYFUNCTION) \ - if(!curlcheck_ssh_hostkey_cb(value)) \ - Wcurl_easy_setopt_err_ssh_hostkey_cb(); \ - if((option) == CURLOPT_SSH_KEYFUNCTION) \ - if(!curlcheck_ssh_key_cb(value)) \ - Wcurl_easy_setopt_err_ssh_key_cb(); \ - if((option) == CURLOPT_INTERLEAVEFUNCTION) \ - if(!curlcheck_interleave_cb(value)) \ - Wcurl_easy_setopt_err_interleave_cb(); \ - if((option) == CURLOPT_PREREQFUNCTION) \ - if(!curlcheck_prereq_cb(value)) \ - Wcurl_easy_setopt_err_prereq_cb(); \ - if((option) == CURLOPT_TRAILERFUNCTION) \ - if(!curlcheck_trailer_cb(value)) \ - Wcurl_easy_setopt_err_trailer_cb(); \ - if(curlcheck_cb_data_option(option)) \ - if(!curlcheck_cb_data(value)) \ - Wcurl_easy_setopt_err_cb_data(); \ - if((option) == CURLOPT_ERRORBUFFER) \ - if(!curlcheck_error_buffer(value)) \ - Wcurl_easy_setopt_err_error_buffer(); \ - if((option) == CURLOPT_CURLU) \ - if(!curlcheck_ptr((value), CURLU)) \ - Wcurl_easy_setopt_err_curlu(); \ - if((option) == CURLOPT_STDERR) \ - if(!curlcheck_FILE(value)) \ - Wcurl_easy_setopt_err_FILE(); \ - if(curlcheck_postfields_option(option)) \ - if(!curlcheck_postfields(value)) \ - Wcurl_easy_setopt_err_postfields(); \ - if((option) == CURLOPT_HTTPPOST) \ - if(!curlcheck_arr((value), struct curl_httppost)) \ - Wcurl_easy_setopt_err_curl_httpost(); \ - if((option) == CURLOPT_MIMEPOST) \ - if(!curlcheck_ptr((value), curl_mime)) \ - Wcurl_easy_setopt_err_curl_mimepost(); \ - if(curlcheck_slist_option(option)) \ - if(!curlcheck_arr((value), struct curl_slist)) \ - Wcurl_easy_setopt_err_curl_slist(); \ - if((option) == CURLOPT_SHARE) \ - if(!curlcheck_ptr((value), CURLSH)) \ - Wcurl_easy_setopt_err_CURLSH(); \ - ) \ - } \ - curl_easy_setopt(handle, option, value); \ - }) + if(__builtin_constant_p(option)) { \ + CURL_IGNORE_DEPRECATION( \ + if(curlcheck_long_option(option)) \ + if(!curlcheck_long(value)) \ + Wcurl_easy_setopt_err_long(); \ + if(curlcheck_off_t_option(option)) \ + if(!curlcheck_off_t(value)) \ + Wcurl_easy_setopt_err_curl_off_t(); \ + if(curlcheck_string_option(option)) \ + if(!curlcheck_string(value)) \ + Wcurl_easy_setopt_err_string(); \ + if((option) == CURLOPT_PRIVATE) { } \ + if(curlcheck_write_cb_option(option)) \ + if(!curlcheck_write_cb(value)) \ + Wcurl_easy_setopt_err_write_callback(); \ + if(curlcheck_curl_option(option)) \ + if(!curlcheck_curl(value)) \ + Wcurl_easy_setopt_err_curl(); \ + if((option) == CURLOPT_RESOLVER_START_FUNCTION) \ + if(!curlcheck_resolver_start_callback(value)) \ + Wcurl_easy_setopt_err_resolver_start_callback(); \ + if((option) == CURLOPT_READFUNCTION) \ + if(!curlcheck_read_cb(value)) \ + Wcurl_easy_setopt_err_read_cb(); \ + if((option) == CURLOPT_IOCTLFUNCTION) \ + if(!curlcheck_ioctl_cb(value)) \ + Wcurl_easy_setopt_err_ioctl_cb(); \ + if((option) == CURLOPT_SOCKOPTFUNCTION) \ + if(!curlcheck_sockopt_cb(value)) \ + Wcurl_easy_setopt_err_sockopt_cb(); \ + if((option) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!curlcheck_opensocket_cb(value)) \ + Wcurl_easy_setopt_err_opensocket_cb(); \ + if((option) == CURLOPT_PROGRESSFUNCTION) \ + if(!curlcheck_progress_cb(value)) \ + Wcurl_easy_setopt_err_progress_cb(); \ + if((option) == CURLOPT_XFERINFOFUNCTION) \ + if(!curlcheck_xferinfo_cb(value)) \ + Wcurl_easy_setopt_err_xferinfo_cb(); \ + if((option) == CURLOPT_DEBUGFUNCTION) \ + if(!curlcheck_debug_cb(value)) \ + Wcurl_easy_setopt_err_debug_cb(); \ + if((option) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!curlcheck_ssl_ctx_cb(value)) \ + Wcurl_easy_setopt_err_ssl_ctx_cb(); \ + if(curlcheck_conv_cb_option(option)) \ + if(!curlcheck_conv_cb(value)) \ + Wcurl_easy_setopt_err_conv_cb(); \ + if((option) == CURLOPT_SEEKFUNCTION) \ + if(!curlcheck_seek_cb(value)) \ + Wcurl_easy_setopt_err_seek_cb(); \ + if((option) == CURLOPT_CHUNK_BGN_FUNCTION) \ + if(!curlcheck_chunk_bgn_cb(value)) \ + Wcurl_easy_setopt_err_chunk_bgn_cb(); \ + if((option) == CURLOPT_CHUNK_END_FUNCTION) \ + if(!curlcheck_chunk_end_cb(value)) \ + Wcurl_easy_setopt_err_chunk_end_cb(); \ + if((option) == CURLOPT_CLOSESOCKETFUNCTION) \ + if(!curlcheck_close_socket_cb(value)) \ + Wcurl_easy_setopt_err_close_socket_cb(); \ + if((option) == CURLOPT_FNMATCH_FUNCTION) \ + if(!curlcheck_fnmatch_cb(value)) \ + Wcurl_easy_setopt_err_fnmatch_cb(); \ + if((option) == CURLOPT_HSTSREADFUNCTION) \ + if(!curlcheck_hstsread_cb(value)) \ + Wcurl_easy_setopt_err_hstsread_cb(); \ + if((option) == CURLOPT_HSTSWRITEFUNCTION) \ + if(!curlcheck_hstswrite_cb(value)) \ + Wcurl_easy_setopt_err_hstswrite_cb(); \ + if((option) == CURLOPT_SSH_HOSTKEYFUNCTION) \ + if(!curlcheck_ssh_hostkey_cb(value)) \ + Wcurl_easy_setopt_err_ssh_hostkey_cb(); \ + if((option) == CURLOPT_SSH_KEYFUNCTION) \ + if(!curlcheck_ssh_key_cb(value)) \ + Wcurl_easy_setopt_err_ssh_key_cb(); \ + if((option) == CURLOPT_INTERLEAVEFUNCTION) \ + if(!curlcheck_interleave_cb(value)) \ + Wcurl_easy_setopt_err_interleave_cb(); \ + if((option) == CURLOPT_PREREQFUNCTION) \ + if(!curlcheck_prereq_cb(value)) \ + Wcurl_easy_setopt_err_prereq_cb(); \ + if((option) == CURLOPT_TRAILERFUNCTION) \ + if(!curlcheck_trailer_cb(value)) \ + Wcurl_easy_setopt_err_trailer_cb(); \ + if(curlcheck_cb_data_option(option)) \ + if(!curlcheck_cb_data(value)) \ + Wcurl_easy_setopt_err_cb_data(); \ + if((option) == CURLOPT_ERRORBUFFER) \ + if(!curlcheck_error_buffer(value)) \ + Wcurl_easy_setopt_err_error_buffer(); \ + if((option) == CURLOPT_CURLU) \ + if(!curlcheck_ptr((value), CURLU)) \ + Wcurl_easy_setopt_err_curlu(); \ + if((option) == CURLOPT_STDERR) \ + if(!curlcheck_FILE(value)) \ + Wcurl_easy_setopt_err_FILE(); \ + if(curlcheck_postfields_option(option)) \ + if(!curlcheck_postfields(value)) \ + Wcurl_easy_setopt_err_postfields(); \ + if((option) == CURLOPT_HTTPPOST) \ + if(!curlcheck_arr((value), struct curl_httppost)) \ + Wcurl_easy_setopt_err_curl_httpost(); \ + if((option) == CURLOPT_MIMEPOST) \ + if(!curlcheck_ptr((value), curl_mime)) \ + Wcurl_easy_setopt_err_curl_mimepost(); \ + if(curlcheck_slist_option(option)) \ + if(!curlcheck_arr((value), struct curl_slist)) \ + Wcurl_easy_setopt_err_curl_slist(); \ + if((option) == CURLOPT_SHARE) \ + if(!curlcheck_ptr((value), CURLSH)) \ + Wcurl_easy_setopt_err_CURLSH(); \ + ) \ + } \ + (curl_easy_setopt)(handle, option, value); \ + }) /* wraps curl_easy_getinfo() with type checking */ #define curl_easy_getinfo(handle, info, arg) \ __extension__({ \ - if(__builtin_constant_p(info)) { \ - CURL_IGNORE_DEPRECATION( \ - if(curlcheck_string_info(info)) \ - if(!curlcheck_arr((arg), char *)) \ - Wcurl_easy_getinfo_err_string(); \ - if(curlcheck_long_info(info)) \ - if(!curlcheck_arr((arg), long)) \ - Wcurl_easy_getinfo_err_long(); \ - if(curlcheck_double_info(info)) \ - if(!curlcheck_arr((arg), double)) \ - Wcurl_easy_getinfo_err_double(); \ - if(curlcheck_slist_info(info)) \ - if(!curlcheck_arr((arg), struct curl_slist *)) \ - Wcurl_easy_getinfo_err_curl_slist(); \ - if(curlcheck_tlssessioninfo_info(info)) \ - if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ - Wcurl_easy_getinfo_err_curl_tlssessioninfo(); \ - if(curlcheck_certinfo_info(info)) \ - if(!curlcheck_arr((arg), struct curl_certinfo *)) \ - Wcurl_easy_getinfo_err_curl_certinfo(); \ - if(curlcheck_socket_info(info)) \ - if(!curlcheck_arr((arg), curl_socket_t)) \ - Wcurl_easy_getinfo_err_curl_socket(); \ - if(curlcheck_off_t_info(info)) \ - if(!curlcheck_arr((arg), curl_off_t)) \ - Wcurl_easy_getinfo_err_curl_off_t(); \ - ) \ - } \ - curl_easy_getinfo(handle, info, arg); \ - }) + if(__builtin_constant_p(info)) { \ + CURL_IGNORE_DEPRECATION( \ + if(curlcheck_string_info(info)) \ + if(!curlcheck_arr((arg), char *)) \ + Wcurl_easy_getinfo_err_string(); \ + if(curlcheck_long_info(info)) \ + if(!curlcheck_arr((arg), long)) \ + Wcurl_easy_getinfo_err_long(); \ + if(curlcheck_double_info(info)) \ + if(!curlcheck_arr((arg), double)) \ + Wcurl_easy_getinfo_err_double(); \ + if(curlcheck_slist_info(info)) \ + if(!curlcheck_arr((arg), struct curl_slist *)) \ + Wcurl_easy_getinfo_err_curl_slist(); \ + if(curlcheck_tlssessioninfo_info(info)) \ + if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ + Wcurl_easy_getinfo_err_curl_tlssessioninfo(); \ + if(curlcheck_certinfo_info(info)) \ + if(!curlcheck_arr((arg), struct curl_certinfo *)) \ + Wcurl_easy_getinfo_err_curl_certinfo(); \ + if(curlcheck_socket_info(info)) \ + if(!curlcheck_arr((arg), curl_socket_t)) \ + Wcurl_easy_getinfo_err_curl_socket(); \ + if(curlcheck_off_t_info(info)) \ + if(!curlcheck_arr((arg), curl_off_t)) \ + Wcurl_easy_getinfo_err_curl_off_t(); \ + ) \ + } \ + (curl_easy_getinfo)(handle, info, arg); \ + }) #define curl_multi_setopt(handle, option, value) \ __extension__({ \ - if(__builtin_constant_p(option)) { \ - if(curlcheck_long_option(option)) \ - if(!curlcheck_long(value)) \ - Wcurl_multi_setopt_err_long(); \ - if(curlcheck_off_t_option(option)) \ - if(!curlcheck_off_t(value)) \ - Wcurl_multi_setopt_err_curl_off_t(); \ - if(curlcheck_multicb_data_option(option)) \ - if(!curlcheck_cb_data(value)) \ - Wcurl_multi_setopt_err_cb_data(); \ - if(curlcheck_charpp_option(option)) \ - if(!curlcheck_ptrptr(value, char)) \ - Wcurl_multi_setopt_err_charpp(); \ - if((option) == CURLMOPT_NOTIFYFUNCTION) \ - if(!curlcheck_multinotify_cb(value)) \ - Wcurl_multi_setopt_err_notifycb(); \ - if((option) == CURLMOPT_PUSHFUNCTION) \ - if(!curlcheck_multipush_cb(value)) \ - Wcurl_multi_setopt_err_pushcb(); \ - if((option) == CURLMOPT_SOCKETFUNCTION) \ - if(!curlcheck_multisocket_cb(value)) \ - Wcurl_multi_setopt_err_socketcb(); \ - if((option) == CURLMOPT_TIMERFUNCTION) \ - if(!curlcheck_multitimer_cb(value)) \ - Wcurl_multi_setopt_err_timercb(); \ - } \ - curl_multi_setopt(handle, option, value); \ - }) + if(__builtin_constant_p(option)) { \ + if(curlcheck_long_option(option)) \ + if(!curlcheck_long(value)) \ + Wcurl_multi_setopt_err_long(); \ + if(curlcheck_off_t_option(option)) \ + if(!curlcheck_off_t(value)) \ + Wcurl_multi_setopt_err_curl_off_t(); \ + if(curlcheck_multicb_data_option(option)) \ + if(!curlcheck_cb_data(value)) \ + Wcurl_multi_setopt_err_cb_data(); \ + if(curlcheck_charpp_option(option)) \ + if(!curlcheck_ptrptr(value, char)) \ + Wcurl_multi_setopt_err_charpp(); \ + if((option) == CURLMOPT_NOTIFYFUNCTION) \ + if(!curlcheck_multinotify_cb(value)) \ + Wcurl_multi_setopt_err_notifycb(); \ + if((option) == CURLMOPT_PUSHFUNCTION) \ + if(!curlcheck_multipush_cb(value)) \ + Wcurl_multi_setopt_err_pushcb(); \ + if((option) == CURLMOPT_SOCKETFUNCTION) \ + if(!curlcheck_multisocket_cb(value)) \ + Wcurl_multi_setopt_err_socketcb(); \ + if((option) == CURLMOPT_TIMERFUNCTION) \ + if(!curlcheck_multitimer_cb(value)) \ + Wcurl_multi_setopt_err_timercb(); \ + } \ + (curl_multi_setopt)(handle, option, value); \ + }) /* evaluates to true if the option takes a data argument to pass to a callback */ @@ -260,9 +260,10 @@ curlcheck_cb_compatible((expr), curl_notify_callback)) /* - * For now, just make sure that the functions are called with three arguments + * Make sure that the functions are called with three arguments */ -#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_share_setopt(share, opt, param) \ + (curl_share_setopt)(share, opt, param) /* the actual warnings, triggered by calling the Wcurl_easy_setopt_err* * functions */ @@ -511,82 +512,84 @@ (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) /* evaluates to true if option takes a data argument to pass to a callback */ -#define curlcheck_cb_data_option(option) \ - ((option) == CURLOPT_CHUNK_DATA || \ - (option) == CURLOPT_CLOSESOCKETDATA || \ - (option) == CURLOPT_DEBUGDATA || \ - (option) == CURLOPT_FNMATCH_DATA || \ - (option) == CURLOPT_HEADERDATA || \ - (option) == CURLOPT_HSTSREADDATA || \ - (option) == CURLOPT_HSTSWRITEDATA || \ - (option) == CURLOPT_INTERLEAVEDATA || \ - (option) == CURLOPT_IOCTLDATA || \ - (option) == CURLOPT_OPENSOCKETDATA || \ - (option) == CURLOPT_PREREQDATA || \ - (option) == CURLOPT_XFERINFODATA || \ - (option) == CURLOPT_READDATA || \ - (option) == CURLOPT_SEEKDATA || \ - (option) == CURLOPT_SOCKOPTDATA || \ - (option) == CURLOPT_SSH_KEYDATA || \ - (option) == CURLOPT_SSL_CTX_DATA || \ - (option) == CURLOPT_WRITEDATA || \ - (option) == CURLOPT_RESOLVER_START_DATA || \ - (option) == CURLOPT_TRAILERDATA || \ - (option) == CURLOPT_SSH_HOSTKEYDATA || \ +#define curlcheck_cb_data_option(option) \ + ((option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_CLOSESOCKETDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_HSTSREADDATA || \ + (option) == CURLOPT_HSTSWRITEDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PREREQDATA || \ + (option) == CURLOPT_XFERINFODATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_WRITEDATA || \ + (option) == CURLOPT_RESOLVER_START_DATA || \ + (option) == CURLOPT_TRAILERDATA || \ + (option) == CURLOPT_SSH_HOSTKEYDATA || \ 0) /* evaluates to true if option takes a POST data argument (void* or char*) */ -#define curlcheck_postfields_option(option) \ - ((option) == CURLOPT_POSTFIELDS || \ - (option) == CURLOPT_COPYPOSTFIELDS || \ +#define curlcheck_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ 0) /* evaluates to true if option takes a struct curl_slist * argument */ -#define curlcheck_slist_option(option) \ - ((option) == CURLOPT_HTTP200ALIASES || \ - (option) == CURLOPT_HTTPHEADER || \ - (option) == CURLOPT_MAIL_RCPT || \ - (option) == CURLOPT_POSTQUOTE || \ - (option) == CURLOPT_PREQUOTE || \ - (option) == CURLOPT_PROXYHEADER || \ - (option) == CURLOPT_QUOTE || \ - (option) == CURLOPT_RESOLVE || \ - (option) == CURLOPT_TELNETOPTIONS || \ - (option) == CURLOPT_CONNECT_TO || \ +#define curlcheck_slist_option(option) \ + ((option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_MAIL_RCPT || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_PROXYHEADER || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_RESOLVE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + (option) == CURLOPT_CONNECT_TO || \ 0) /* groups of curl_easy_getinfo infos that take the same type of argument */ /* evaluates to true if info expects a pointer to char * argument */ -#define curlcheck_string_info(info) \ - (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG && \ +#define curlcheck_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG && \ (info) != CURLINFO_PRIVATE) /* evaluates to true if info expects a pointer to long argument */ -#define curlcheck_long_info(info) \ +#define curlcheck_long_info(info) \ (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) /* evaluates to true if info expects a pointer to double argument */ -#define curlcheck_double_info(info) \ +#define curlcheck_double_info(info) \ (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) /* true if info expects a pointer to struct curl_slist * argument */ #define curlcheck_slist_info(info) \ - (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST)) + (((info) == CURLINFO_SSL_ENGINES) || \ + ((info) == CURLINFO_COOKIELIST)) /* true if info expects a pointer to struct curl_tlssessioninfo * argument */ -#define curlcheck_tlssessioninfo_info(info) \ - (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION)) +#define curlcheck_tlssessioninfo_info(info) \ + (((info) == CURLINFO_TLS_SSL_PTR) || \ + ((info) == CURLINFO_TLS_SESSION)) /* true if info expects a pointer to struct curl_certinfo * argument */ #define curlcheck_certinfo_info(info) ((info) == CURLINFO_CERTINFO) /* true if info expects a pointer to struct curl_socket_t argument */ -#define curlcheck_socket_info(info) \ +#define curlcheck_socket_info(info) \ (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T) /* true if info expects a pointer to curl_off_t argument */ -#define curlcheck_off_t_info(info) \ +#define curlcheck_off_t_info(info) \ (CURLINFO_OFF_T < (info)) /* @@ -603,7 +606,7 @@ */ /* XXX: should evaluate to true if expr is a pointer */ -#define curlcheck_any_ptr(expr) \ +#define curlcheck_any_ptr(expr) \ (sizeof(expr) == sizeof(void *)) /* evaluates to true if expr is NULL */ @@ -667,7 +670,7 @@ ) /* evaluates to true if expr is of type curl_off_t */ -#define curlcheck_off_t(expr) \ +#define curlcheck_off_t(expr) \ (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) /* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ @@ -705,8 +708,8 @@ __builtin_types_compatible_p(__typeof__(func) *, type)) /* evaluates to true if expr is of type curl_resolver_start_callback */ -#define curlcheck_resolver_start_callback(expr) \ - (curlcheck_NULL(expr) || \ +#define curlcheck_resolver_start_callback(expr) \ + (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_resolver_start_callback)) /* evaluates to true if expr is of type curl_read_callback or "similar" */ @@ -740,11 +743,11 @@ curlcheck_cb_compatible((expr), Wcurl_write_callback6)) typedef size_t (*Wcurl_write_callback1)(const char *, size_t, size_t, void *); typedef size_t (*Wcurl_write_callback2)(const char *, size_t, size_t, - const void *); + const void *); typedef size_t (*Wcurl_write_callback3)(const char *, size_t, size_t, FILE *); typedef size_t (*Wcurl_write_callback4)(const void *, size_t, size_t, void *); typedef size_t (*Wcurl_write_callback5)(const void *, size_t, size_t, - const void *); + const void *); typedef size_t (*Wcurl_write_callback6)(const void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ @@ -768,7 +771,7 @@ curlcheck_cb_compatible((expr), Wcurl_sockopt_callback2)) typedef int (*Wcurl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); typedef int (*Wcurl_sockopt_callback2)(const void *, curl_socket_t, - curlsocktype); + curlsocktype); /* evaluates to true if expr is of type curl_opensocket_callback or "similar" */ @@ -816,21 +819,21 @@ curlcheck_cb_compatible((expr), Wcurl_debug_callback6) || \ curlcheck_cb_compatible((expr), Wcurl_debug_callback7) || \ curlcheck_cb_compatible((expr), Wcurl_debug_callback8)) -typedef int (*Wcurl_debug_callback1) (CURL *, +typedef int (*Wcurl_debug_callback1)(CURL *, curl_infotype, char *, size_t, void *); -typedef int (*Wcurl_debug_callback2) (CURL *, +typedef int (*Wcurl_debug_callback2)(CURL *, curl_infotype, char *, size_t, const void *); -typedef int (*Wcurl_debug_callback3) (CURL *, +typedef int (*Wcurl_debug_callback3)(CURL *, curl_infotype, const char *, size_t, void *); -typedef int (*Wcurl_debug_callback4) (CURL *, +typedef int (*Wcurl_debug_callback4)(CURL *, curl_infotype, const char *, size_t, const void *); -typedef int (*Wcurl_debug_callback5) (CURL *, +typedef int (*Wcurl_debug_callback5)(CURL *, curl_infotype, unsigned char *, size_t, void *); -typedef int (*Wcurl_debug_callback6) (CURL *, +typedef int (*Wcurl_debug_callback6)(CURL *, curl_infotype, unsigned char *, size_t, const void *); -typedef int (*Wcurl_debug_callback7) (CURL *, +typedef int (*Wcurl_debug_callback7)(CURL *, curl_infotype, const unsigned char *, size_t, void *); -typedef int (*Wcurl_debug_callback8) (CURL *, +typedef int (*Wcurl_debug_callback8)(CURL *, curl_infotype, const unsigned char *, size_t, const void *); /* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ @@ -853,7 +856,7 @@ const void *); #ifdef HEADER_SSL_H /* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX - * this will of course break if we are included before OpenSSL headers... + * this of course breaks if we are included before OpenSSL headers... */ typedef CURLcode (*Wcurl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *); typedef CURLcode (*Wcurl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *);
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index dabe44a..b1f3a23 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -114,10 +114,10 @@ /* * curl_url_cleanup() frees the CURLU handle and related resources used for - * the URL parsing. It will not free strings previously returned with the URL + * the URL parsing. It does not free strings previously returned with the URL * API. */ -CURL_EXTERN void curl_url_cleanup(CURLU *handle); +CURL_EXTERN void curl_url_cleanup(CURLU *u); /* * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new @@ -130,15 +130,15 @@ * handle. Returns error code. The returned pointer MUST be freed with * curl_free() afterwards. */ -CURL_EXTERN CURLUcode curl_url_get(const CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char **part, unsigned int flags); /* * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns - * error code. The passed in string will be copied. Passing a NULL instead of + * error code. The passed in string is copied. Passing a NULL instead of * a part string, clears that part. */ -CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags); /* @@ -146,7 +146,7 @@ * readable error string. This is useful for printing meaningful error * messages. */ -CURL_EXTERN const char *curl_url_strerror(CURLUcode); +CURL_EXTERN const char *curl_url_strerror(CURLUcode error); #ifdef __cplusplus } /* end of extern "C" */
diff --git a/Utilities/cmcurl/include/curl/websockets.h b/Utilities/cmcurl/include/curl/websockets.h index 402a2ca..bf93715 100644 --- a/Utilities/cmcurl/include/curl/websockets.h +++ b/Utilities/cmcurl/include/curl/websockets.h
@@ -67,7 +67,7 @@ * Sends data over the websocket connection. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, +CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags);
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 5eef8df..a11224c 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -38,6 +38,7 @@ set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}/lib" # for "curl_config.h" + "${PROJECT_SOURCE_DIR}/lib" ) #----------------------------------------------------------------------------- @@ -80,13 +81,13 @@ #----------------------------------------------------------------------------- if(CURL_BUILD_TESTING) - # special libcurlu library just for unittests + # special libcurlu library for unittests add_library(curlu STATIC EXCLUDE_FROM_ALL ${HHEADERS} ${CSOURCES}) target_compile_definitions(curlu PUBLIC "CURL_STATICLIB" "UNITTESTS") target_link_libraries(curlu PUBLIC ${CURL_LIBS}) # There is plenty of parallelism when building the testdeps target. # Override the curlu batch size with the maximum to optimize performance. - set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0 C_CLANG_TIDY "") + set_target_properties(curlu PROPERTIES UNITY_BUILD ON UNITY_BUILD_BATCH_SIZE 0 C_CLANG_TIDY "") add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" @@ -137,9 +138,9 @@ endif() endif() -if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) +if(SHARE_LIB_OBJECT) set(LIB_OBJECT "libcurl_object") - add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) # Requires CMake 3.12 + add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) if(WIN32) # Define CURL_STATICLIB always, to disable __declspec(dllexport) for # exported libcurl symbols. We handle exports via libcurl.def instead. @@ -150,6 +151,7 @@ target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) set_target_properties(${LIB_OBJECT} PROPERTIES POSITION_INDEPENDENT_CODE ON) + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") @@ -163,6 +165,13 @@ set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() endif() + if(CURL_CLANG_TIDY) + set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF) + endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() if(CURL_CODE_COVERAGE) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) @@ -191,6 +200,7 @@ PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}" INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB") + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") @@ -204,6 +214,17 @@ set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() endif() + if(CURL_CLANG_TIDY) + if(BUILD_SHARED_LIBS) # disable clang-tidy for static, and limit to the shared library, if both enabled + set_target_properties(${LIB_STATIC} PROPERTIES C_CLANG_TIDY "") + else() + set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF) + endif() + endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() if(CURL_CODE_COVERAGE) set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) @@ -212,27 +233,6 @@ target_include_directories(${LIB_STATIC} INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>") - - # CMake before CMP0099 (CMake 3.17 2020-03-20) did not propagate libdirs to - # targets. It expected libs to have an absolute filename. As a workaround, - # manually apply dependency libdirs, for CMake consumers without this policy. - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) - cmake_policy(GET CMP0099 _has_CMP0099) # https://cmake.org/cmake/help/latest/policy/CMP0099.html - endif() - if(NOT _has_CMP0099 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CURL_LIBS) - set(_curl_libdirs "") - foreach(_curl_lib IN LISTS CURL_LIBS) - if(TARGET "${_curl_lib}") - get_target_property(_curl_libdir "${_curl_lib}" INTERFACE_LINK_DIRECTORIES) - if(_curl_libdir) - list(APPEND _curl_libdirs "${_curl_libdir}") - endif() - endif() - endforeach() - if(_curl_libdirs) - target_link_directories(${LIB_STATIC} INTERFACE ${_curl_libdirs}) - endif() - endif() endif() if(BUILD_SHARED_LIBS) @@ -252,6 +252,7 @@ PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" POSITION_INDEPENDENT_CODE ON) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") @@ -265,14 +266,17 @@ set_target_properties(${LIB_SHARED} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() endif() + if(CURL_CLANG_TIDY) + set_target_properties(${LIB_SHARED} PROPERTIES UNITY_BUILD OFF) + endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_SHARED} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() if(CURL_CODE_COVERAGE) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS}) - else() - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS ${CURL_COVERAGE_LDFLAGS}) - endif() + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS}) endif() target_include_directories(${LIB_SHARED} INTERFACE @@ -282,7 +286,7 @@ if(CMAKE_DLL_NAME_WITH_SOVERSION OR CYGWIN OR APPLE OR - CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR @@ -343,11 +347,7 @@ set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers") check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS) if(HAVE_VERSIONED_SYMBOLS) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}") - else() - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}") - endif() + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}") else() message(WARNING "Versioned symbols requested, but not supported by the toolchain.") endif() @@ -385,7 +385,7 @@ if(PERL_EXECUTABLE) add_custom_target(curl-optiontable COMMENT "Generating lib/easyoptions.c" VERBATIM USES_TERMINAL - COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" < "${PROJECT_SOURCE_DIR}/include/curl/curl.h" + COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" "${PROJECT_SOURCE_DIR}/include/curl/curl.h" > "${PROJECT_SOURCE_DIR}/lib/easyoptions.c" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" "${PROJECT_SOURCE_DIR}/include/curl/curl.h" )
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index 402a5be..d762f72 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -23,43 +23,46 @@ ########################################################################### # Shared between CMakeLists.txt and Makefile.am -LIB_CURLX_CFILES = \ - curlx/base64.c \ - curlx/dynbuf.c \ - curlx/fopen.c \ - curlx/inet_ntop.c \ - curlx/inet_pton.c \ - curlx/multibyte.c \ - curlx/nonblock.c \ - curlx/strcopy.c \ - curlx/strerr.c \ - curlx/strparse.c \ - curlx/timediff.c \ - curlx/timeval.c \ +LIB_CURLX_CFILES = \ + curlx/base64.c \ + curlx/basename.c \ + curlx/dynbuf.c \ + curlx/fopen.c \ + curlx/inet_ntop.c \ + curlx/inet_pton.c \ + curlx/multibyte.c \ + curlx/nonblock.c \ + curlx/snprintf.c \ + curlx/strcopy.c \ + curlx/strdup.c \ + curlx/strerr.c \ + curlx/strparse.c \ + curlx/timediff.c \ + curlx/timeval.c \ curlx/version_win32.c \ - curlx/wait.c \ - curlx/warnless.c \ + curlx/wait.c \ + curlx/warnless.c \ curlx/winapi.c -LIB_CURLX_HFILES = \ - curlx/binmode.h \ - curlx/base64.h \ - curlx/curlx.h \ - curlx/dynbuf.h \ - curlx/fopen.h \ - curlx/inet_ntop.h \ - curlx/inet_pton.h \ - curlx/multibyte.h \ - curlx/nonblock.h \ - curlx/snprintf.h \ - curlx/strcopy.h \ - curlx/strerr.h \ - curlx/strparse.h \ - curlx/timediff.h \ - curlx/timeval.h \ +LIB_CURLX_HFILES = \ + curlx/base64.h \ + curlx/basename.h \ + curlx/dynbuf.h \ + curlx/fopen.h \ + curlx/inet_ntop.h \ + curlx/inet_pton.h \ + curlx/multibyte.h \ + curlx/nonblock.h \ + curlx/snprintf.h \ + curlx/strcopy.h \ + curlx/strdup.h \ + curlx/strerr.h \ + curlx/strparse.h \ + curlx/timediff.h \ + curlx/timeval.h \ curlx/version_win32.h \ - curlx/wait.h \ - curlx/warnless.h \ + curlx/wait.h \ + curlx/warnless.h \ curlx/winapi.h LIB_VAUTH_CFILES = \ @@ -116,24 +119,22 @@ vtls/wolfssl.h \ vtls/x509asn1.h -LIB_VQUIC_CFILES = \ +LIB_VQUIC_CFILES = \ vquic/curl_ngtcp2.c \ - vquic/curl_osslq.c \ vquic/curl_quiche.c \ - vquic/vquic.c \ + vquic/vquic.c \ vquic/vquic-tls.c -LIB_VQUIC_HFILES = \ +LIB_VQUIC_HFILES = \ vquic/curl_ngtcp2.h \ - vquic/curl_osslq.h \ vquic/curl_quiche.h \ - vquic/vquic.h \ - vquic/vquic_int.h \ + vquic/vquic.h \ + vquic/vquic_int.h \ vquic/vquic-tls.h -LIB_VSSH_CFILES = \ - vssh/libssh.c \ - vssh/libssh2.c \ +LIB_VSSH_CFILES = \ + vssh/libssh.c \ + vssh/libssh2.c \ vssh/vssh.c LIB_VSSH_HFILES = \ @@ -148,6 +149,7 @@ asyn-thrdd.c \ bufq.c \ bufref.c \ + cf-dns.c \ cf-h1-proxy.c \ cf-h2-proxy.c \ cf-haproxy.c \ @@ -170,7 +172,6 @@ curl_memrchr.c \ curl_ntlm_core.c \ curl_range.c \ - curl_rtmp.c \ curl_sasl.c \ curl_sha512_256.c \ curl_share.c \ @@ -180,6 +181,7 @@ cw-out.c \ cw-pause.c \ dict.c \ + dnscache.c \ doh.c \ dynhds.c \ easy.c \ @@ -234,6 +236,7 @@ pingpong.c \ pop3.c \ progress.c \ + protocol.c \ psl.c \ rand.c \ ratelimit.c \ @@ -252,12 +255,13 @@ socks_sspi.c \ splay.c \ strcase.c \ - strdup.c \ strequal.c \ strerror.c \ system_win32.c \ telnet.c \ tftp.c \ + thrdpool.c \ + thrdqueue.c \ transfer.c \ uint-bset.c \ uint-hash.c \ @@ -275,6 +279,7 @@ asyn.h \ bufq.h \ bufref.h \ + cf-dns.h \ cf-h1-proxy.h \ cf-h2-proxy.h \ cf-haproxy.h \ @@ -303,10 +308,8 @@ curl_ntlm_core.h \ curl_printf.h \ curl_range.h \ - curl_rtmp.h \ curl_sasl.h \ curl_setup.h \ - curl_setup_once.h \ curl_sha256.h \ curl_sha512_256.h \ curl_share.h \ @@ -316,6 +319,7 @@ cw-out.h \ cw-pause.h \ dict.h \ + dnscache.h \ doh.h \ dynhds.h \ easy_lock.h \ @@ -327,6 +331,7 @@ fileinfo.h \ formdata.h \ ftp.h \ + ftp-int.h \ ftplistparser.h \ functypes.h \ getinfo.h \ @@ -362,6 +367,7 @@ pingpong.h \ pop3.h \ progress.h \ + protocol.h \ psl.h \ rand.h \ ratelimit.h \ @@ -382,11 +388,12 @@ socks.h \ splay.h \ strcase.h \ - strdup.h \ strerror.h \ system_win32.h \ telnet.h \ tftp.h \ + thrdpool.h \ + thrdqueue.h \ transfer.h \ uint-bset.h \ uint-hash.h \
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index 7006333..81b5379 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c
@@ -39,12 +39,30 @@ #include "connect.h" #define MAX_ALTSVC_LINE 4095 -#define MAX_ALTSVC_DATELEN 256 +#define MAX_ALTSVC_DATELEN 17 #define MAX_ALTSVC_HOSTLEN 2048 #define MAX_ALTSVC_ALPNLEN 10 #define H3VERSION "h3" +#if defined(DEBUGBUILD) || defined(UNITTESTS) +/* to play well with debug builds, we can *set* a fixed time this will + return */ +static time_t altsvc_debugtime(void *unused) +{ + const char *timestr = getenv("CURL_TIME"); + (void)unused; + if(timestr) { + curl_off_t val; + curlx_str_number(×tr, &val, TIME_T_MAX); + return (time_t)val; + } + return time(NULL); +} +#undef time +#define time(x) altsvc_debugtime(x) +#endif + /* Given the ALPN ID, return the name */ const char *Curl_alpnid2str(enum alpnid id) { @@ -126,6 +144,20 @@ srcport, dstport); } +/* append the new entry to the list after possibly removing an old entry + first */ +static void altsvc_append(struct altsvcinfo *asi, struct altsvc *as) +{ + while(Curl_llist_count(&asi->list) >= MAX_ALTSVC_ENTRIES) { + /* It's full. Remove the first entry in the list */ + struct Curl_llist_node *e = Curl_llist_head(&asi->list); + struct altsvc *oldas = Curl_node_elem(e); + Curl_node_remove(e); + altsvc_free(oldas); + } + Curl_llist_append(&asi->list, as, &as->node); +} + /* only returns SERIOUS errors */ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line) { @@ -162,25 +194,27 @@ curlx_str_newline(&line)) ; else { - struct altsvc *as; char dbuf[MAX_ALTSVC_DATELEN + 1]; time_t expires = 0; + time_t now = time(NULL); /* The date parser works on a null-terminated string. The maximum length is upheld by curlx_str_quotedword(). */ memcpy(dbuf, curlx_str(&date), curlx_strlen(&date)); dbuf[curlx_strlen(&date)] = 0; Curl_getdate_capped(dbuf, &expires); - as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, - (size_t)srcport, (size_t)dstport); - if(as) { - as->expires = expires; - as->prio = 0; /* not supported to just set zero */ - as->persist = persist ? 1 : 0; - Curl_llist_append(&asi->list, as, &as->node); + + if(now < expires) { + struct altsvc *as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, + (size_t)srcport, (size_t)dstport); + if(as) { + as->expires = expires; + as->persist = persist ? 1 : 0; + altsvc_append(asi, as); + } + else + return CURLE_OUT_OF_MEMORY; } - else - return CURLE_OUT_OF_MEMORY; } return CURLE_OK; @@ -208,19 +242,22 @@ fp = curlx_fopen(file, FOPEN_READTEXT); if(fp) { - bool eof = FALSE; - struct dynbuf buf; - curlx_dyn_init(&buf, MAX_ALTSVC_LINE); - do { - result = Curl_get_line(&buf, fp, &eof); - if(!result) { - const char *lineptr = curlx_dyn_ptr(&buf); - curlx_str_passblanks(&lineptr); - if(curlx_str_single(&lineptr, '#')) - altsvc_add(asi, lineptr); - } - } while(!result && !eof); - curlx_dyn_free(&buf); /* free the line buffer */ + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + bool eof = FALSE; + struct dynbuf buf; + curlx_dyn_init(&buf, MAX_ALTSVC_LINE); + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + curlx_str_passblanks(&lineptr); + if(curlx_str_single(&lineptr, '#')) + altsvc_add(asi, lineptr); + } + } while(!result && !eof); + curlx_dyn_free(&buf); /* free the line buffer */ + } curlx_fclose(fp); } return result; @@ -229,7 +266,6 @@ /* * Write this single altsvc entry to a single output line */ - static CURLcode altsvc_out(struct altsvc *as, FILE *fp) { struct tm stamp; @@ -258,7 +294,7 @@ "%s %s%s%s %u " "\"%d%02d%02d " "%02d:%02d:%02d\" " - "%u %u\n", + "%d 0\n", /* prio still always zero */ Curl_alpnid2str(as->src.alpnid), src6_pre, as->src.host, src6_post, as->src.port, @@ -269,7 +305,7 @@ stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, stamp.tm_hour, stamp.tm_min, stamp.tm_sec, - as->persist, as->prio); + as->persist); return CURLE_OK; } @@ -310,10 +346,18 @@ /* * Curl_altsvc_ctrl() passes on the external bitmask. */ -CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl) +CURLcode Curl_altsvc_ctrl(struct Curl_easy *data, const long ctrl) { - DEBUGASSERT(asi); - asi->flags = ctrl; + DEBUGASSERT(data); + if(!ctrl) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(!data->asi) { + data->asi = Curl_altsvc_init(); + if(!data->asi) + return CURLE_OUT_OF_MEMORY; + } + data->asi->flags = ctrl; return CURLE_OK; } @@ -321,12 +365,12 @@ * Curl_altsvc_cleanup() frees an altsvc cache instance and all associated * resources. */ -void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp) +void Curl_altsvc_cleanup(struct altsvcinfo **asi) { - if(*altsvcp) { + if(*asi) { struct Curl_llist_node *e; struct Curl_llist_node *n; - struct altsvcinfo *altsvc = *altsvcp; + struct altsvcinfo *altsvc = *asi; for(e = Curl_llist_head(&altsvc->list); e; e = n) { struct altsvc *as = Curl_node_elem(e); n = Curl_node_next(e); @@ -334,7 +378,7 @@ } curlx_free(altsvc->filename); curlx_free(altsvc); - *altsvcp = NULL; /* clear the pointer */ + *asi = NULL; /* clear the pointer */ } } @@ -342,21 +386,21 @@ * Curl_altsvc_save() writes the altsvc cache to a file. */ CURLcode Curl_altsvc_save(struct Curl_easy *data, - struct altsvcinfo *altsvc, const char *file) + struct altsvcinfo *asi, const char *file) { CURLcode result = CURLE_OK; FILE *out; char *tempstore = NULL; - if(!altsvc) + if(!asi) /* no cache activated */ return CURLE_OK; /* if not new name is given, use the one we stored from the load */ - if(!file && altsvc->filename) - file = altsvc->filename; + if(!file && asi->filename) + file = asi->filename; - if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) + if((asi->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) /* marked as read-only, no file or zero length filename */ return CURLE_OK; @@ -367,7 +411,7 @@ fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n" "# This file was generated by libcurl! Edit at your own risk.\n", out); - for(e = Curl_llist_head(&altsvc->list); e; e = n) { + for(e = Curl_llist_head(&asi->list); e; e = n) { struct altsvc *as = Curl_node_elem(e); n = Curl_node_next(e); result = altsvc_out(as, out); @@ -420,24 +464,6 @@ } } -#if defined(DEBUGBUILD) || defined(UNITTESTS) -/* to play well with debug builds, we can *set* a fixed time this will - return */ -static time_t altsvc_debugtime(void *unused) -{ - const char *timestr = getenv("CURL_TIME"); - (void)unused; - if(timestr) { - curl_off_t val; - curlx_str_number(×tr, &val, TIME_T_MAX); - return (time_t)val; - } - return time(NULL); -} -#undef time -#define time(x) altsvc_debugtime(x) -#endif - /* * Curl_altsvc_parse() takes an incoming alt-svc response header and stores * the data correctly in the cache. @@ -459,9 +485,6 @@ unsigned short dstport = srcport; /* the same by default */ size_t entries = 0; struct Curl_str alpn; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif DEBUGASSERT(asi); @@ -584,7 +607,7 @@ else as->expires = maxage + secs; as->persist = persist; - Curl_llist_append(&asi->list, as, &as->node); + altsvc_append(asi, as); infof(data, "Added alt-svc: %.*s:%d over %s", (int)curlx_strlen(&dsthost), curlx_str(&dsthost), dstport, Curl_alpnid2str(dstalpnid));
diff --git a/Utilities/cmcurl/lib/altsvc.h b/Utilities/cmcurl/lib/altsvc.h index b82eaa8..dc1740b 100644 --- a/Utilities/cmcurl/lib/altsvc.h +++ b/Utilities/cmcurl/lib/altsvc.h
@@ -28,9 +28,12 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC) #include "llist.h" +/* the maximum number of alt-svc entries kept in a single cache */ +#define MAX_ALTSVC_ENTRIES 5000 + struct althost { char *host; - unsigned short port; + uint16_t port; enum alpnid alpnid; }; @@ -39,7 +42,6 @@ struct althost dst; time_t expires; struct Curl_llist_node node; - unsigned int prio; BIT(persist); }; @@ -54,11 +56,11 @@ CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file); CURLcode Curl_altsvc_save(struct Curl_easy *data, struct altsvcinfo *asi, const char *file); -CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl); -void Curl_altsvc_cleanup(struct altsvcinfo **altsvc); +CURLcode Curl_altsvc_ctrl(struct Curl_easy *data, const long ctrl); +void Curl_altsvc_cleanup(struct altsvcinfo **asi); CURLcode Curl_altsvc_parse(struct Curl_easy *data, - struct altsvcinfo *altsvc, const char *value, - enum alpnid srcalpn, const char *srchost, + struct altsvcinfo *asi, const char *value, + enum alpnid srcalpnid, const char *srchost, unsigned short srcport); bool Curl_altsvc_lookup(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost,
diff --git a/Utilities/cmcurl/lib/amigaos.c b/Utilities/cmcurl/lib/amigaos.c index 7ce5045..e4f3bfb 100644 --- a/Utilities/cmcurl/lib/amigaos.c +++ b/Utilities/cmcurl/lib/amigaos.c
@@ -26,6 +26,7 @@ #ifdef __AMIGA__ #include "hostip.h" +#include "curl_addrinfo.h" #include "amigaos.h" #ifdef HAVE_PROTO_BSDSOCKET_H @@ -117,7 +118,7 @@ * allocates memory also. */ -struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port) +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port) { struct Curl_addrinfo *ai = NULL; struct hostent *h; @@ -140,7 +141,7 @@ } } else { -#ifdef CURLRES_THREADED +#ifdef USE_RESOLV_THREADED /* gethostbyname() is not thread-safe, so we need to reopen bsdsocket * on the thread's context */
diff --git a/Utilities/cmcurl/lib/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h index daf2487..b5faab4 100644 --- a/Utilities/cmcurl/lib/arpa_telnet.h +++ b/Utilities/cmcurl/lib/arpa_telnet.h
@@ -27,19 +27,18 @@ /* * Telnet option defines. Add more here if in need. */ -#define CURL_TELOPT_BINARY 0 /* binary 8bit data */ -#define CURL_TELOPT_ECHO 1 /* just echo! */ -#define CURL_TELOPT_SGA 3 /* Suppress Go Ahead */ -#define CURL_TELOPT_EXOPL 255 /* EXtended OPtions List */ -#define CURL_TELOPT_TTYPE 24 /* Terminal TYPE */ -#define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */ -#define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */ - +#define CURL_TELOPT_BINARY 0 /* binary 8-bit data */ +#define CURL_TELOPT_ECHO 1 /* echo */ +#define CURL_TELOPT_SGA 3 /* Suppress Go Ahead */ +#define CURL_TELOPT_EXOPL 255 /* EXtended OPtions List */ +#define CURL_TELOPT_TTYPE 24 /* Terminal TYPE */ +#define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */ +#define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */ #define CURL_TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */ #define CURL_NEW_ENV_VAR 0 #define CURL_NEW_ENV_VALUE 1 -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE /* * The telnet options represented as strings */ @@ -55,9 +54,9 @@ "TERM SPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON" }; -#define CURL_TELOPT(x) telnetoptions[x] +#define CURL_TELOPT(x) telnetoptions[x] #else -#define CURL_TELOPT(x) "" +#define CURL_TELOPT(x) "" #endif #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON @@ -81,7 +80,7 @@ #define CURL_DONT 254 /* DO NOT use this option! */ #define CURL_IAC 255 /* Interpret As Command */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE /* * Then those numbers represented as strings: */ @@ -101,13 +100,14 @@ #define CURL_TELQUAL_INFO 2 #define CURL_TELQUAL_NAME 3 -#define CURL_TELCMD_OK(x) (((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \ - ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM)) +#define CURL_TELCMD_OK(x) \ + (((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \ + ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM)) -#ifndef CURL_DISABLE_VERBOSE_STRINGS -#define CURL_TELCMD(x) telnetcmds[(x) - CURL_TELCMD_MINIMUM] +#ifdef CURLVERBOSE +#define CURL_TELCMD(x) telnetcmds[(x) - CURL_TELCMD_MINIMUM] #else -#define CURL_TELCMD(x) "" +#define CURL_TELCMD(x) "" #endif #endif /* CURL_DISABLE_TELNET */
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 7544140..a4a7464 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -23,12 +23,12 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES /*********************************************************************** - * Only for ares-enabled builds - * And only for functions that fulfill the asynch resolver backend API - * as defined in asyn.h, nothing else belongs in this file! + * Only for ares-enabled builds and only for functions that fulfill + * the asynch resolver backend API as defined in asyn.h, + * nothing else belongs in this file! **********************************************************************/ #ifdef HAVE_NETINET_IN_H @@ -47,6 +47,7 @@ #include "urldata.h" #include "cfilters.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" #include "url.h" @@ -57,40 +58,10 @@ #include "progress.h" #include "curlx/timediff.h" #include "httpsrr.h" - #include <ares.h> -#include <ares_version.h> /* really old c-ares did not include this by - itself */ -#if ARES_VERSION >= 0x010601 -/* IPv6 supported since 1.6.1 */ -#define HAVE_CARES_IPV6 1 -#endif - -#if ARES_VERSION >= 0x010704 -#define HAVE_CARES_SERVERS_CSV 1 -#define HAVE_CARES_LOCAL_DEV 1 -#define HAVE_CARES_SET_LOCAL 1 -#endif - -#if ARES_VERSION >= 0x010b00 -#define HAVE_CARES_PORTS_CSV 1 -#endif - -#if ARES_VERSION >= 0x011000 -/* 1.16.0 or later has ares_getaddrinfo */ -#define HAVE_CARES_GETADDRINFO 1 -#else -/* How long we are willing to wait for additional parallel responses after - obtaining a "definitive" one. For old c-ares without getaddrinfo. - - This is intended to equal the c-ares default timeout. curl always uses that - default value. Unfortunately, c-ares does not expose its default timeout in - its API, but it is officially documented as 5 seconds. - - See query_completed_cb() for an explanation of how this is used. - */ -#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000 +#if ARES_VERSION < 0x011000 +#error "requires c-ares 1.16.0 or newer" #endif #ifdef USE_HTTPSRR @@ -105,7 +76,13 @@ static int ares_ver = 0; static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, - bool reset_on_null); + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, + struct Curl_resolv_async *async); /* * Curl_async_global_init() - the generic low-level asynchronous name @@ -146,13 +123,18 @@ } } -static CURLcode async_ares_init(struct Curl_easy *data) +static CURLcode async_ares_init(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; int status; struct ares_options options; int optmask = ARES_OPT_SOCK_STATE_CB; - CURLcode rc = CURLE_OK; + CURLcode result = CURLE_OK; + + /* initial status - failed */ + ares->ares_status = ARES_ENOTFOUND; + async->queries_ongoing = 0; options.sock_state_cb = sock_state_cb; options.sock_state_cb_data = data; @@ -176,422 +158,330 @@ status = ares_init_options(&ares->channel, &options, optmask); if(status != ARES_SUCCESS) { ares->channel = NULL; - rc = (status == ARES_ENOMEM) ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; + result = (status == ARES_ENOMEM) ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; goto out; } - rc = async_ares_set_dns_servers(data, FALSE); - if(rc && rc != CURLE_NOT_BUILT_IN) + result = async_ares_set_dns_servers(data, async); + if(result && result != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_interface(data); - if(rc && rc != CURLE_NOT_BUILT_IN) + result = async_ares_set_dns_interface(data, async); + if(result && result != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_local_ip4(data); - if(rc && rc != CURLE_NOT_BUILT_IN) + result = async_ares_set_dns_local_ip4(data, async); + if(result && result != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_local_ip6(data); - if(rc && rc != CURLE_NOT_BUILT_IN) + result = async_ares_set_dns_local_ip6(data, async); + if(result && result != CURLE_NOT_BUILT_IN) goto out; - rc = CURLE_OK; + result = CURLE_OK; out: - if(rc && ares->channel) { + if(result && ares->channel) { ares_destroy(ares->channel); ares->channel = NULL; } - return rc; -} - -static CURLcode async_ares_init_lazy(struct Curl_easy *data) -{ - struct async_ares_ctx *ares = &data->state.async.ares; - if(!ares->channel) - return async_ares_init(data); - return CURLE_OK; -} - -CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) -{ - struct async_ares_ctx *ares = &data->state.async.ares; - CURLcode result = CURLE_OK; - if(!ares->channel) { - result = async_ares_init(data); - } - *impl = ares->channel; return result; } /* * async_ares_cleanup() cleans up async resolver data. */ -static void async_ares_cleanup(struct Curl_easy *data) +static void async_ares_cleanup(struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; - if(ares->temp_ai) { - Curl_freeaddrinfo(ares->temp_ai); - ares->temp_ai = NULL; + struct async_ares_ctx *ares = &async->ares; + if(ares->res_A) { + Curl_freeaddrinfo(ares->res_A); + ares->res_A = NULL; + } + if(ares->res_AAAA) { + Curl_freeaddrinfo(ares->res_AAAA); + ares->res_AAAA = NULL; } #ifdef USE_HTTPSRR Curl_httpsrr_cleanup(&ares->hinfo); #endif } -void Curl_async_ares_shutdown(struct Curl_easy *data) +void Curl_async_ares_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { /* c-ares has a method to "cancel" operations on a channel, but * as reported in #18216, this does not totally reset the channel * and ares may get stuck. * We need to destroy the channel and on demand create a new * one to avoid that. */ - Curl_async_ares_destroy(data); + Curl_async_ares_destroy(data, async); } -void Curl_async_ares_destroy(struct Curl_easy *data) +void Curl_async_ares_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; + (void)data; if(ares->channel) { ares_destroy(ares->channel); ares->channel = NULL; } - async_ares_cleanup(data); + async_ares_cleanup(async); } -/* - * Curl_async_pollset() is called when someone from the outside world - * (using curl_multi_fdset()) wants to get our fd_set setup. - */ - -CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps) { - struct async_ares_ctx *ares = &data->state.async.ares; - if(ares->channel) - return Curl_ares_pollset(data, ares->channel, ps); - return CURLE_OK; + struct async_ares_ctx *ares = &async->ares; + CURLcode result = CURLE_OK; + + if(ares->channel) { + result = Curl_ares_pollset(data, ares->channel, ps); + if(!result) { + timediff_t ms = Curl_ares_timeout_ms(data, async, ares->channel); + Curl_expire(data, ms, EXPIRE_ASYNC_NAME); + } + } + return result; } /* - * Curl_async_is_resolved() is called repeatedly to check if a previous + * Curl_async_take_result() is called repeatedly to check if a previous * name resolve request has completed. It should also make sure to time-out if * the operation seems to take too long. * * Returns normal CURLcode errors. */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns) +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; CURLcode result = CURLE_OK; - DEBUGASSERT(dns); - *dns = NULL; - - if(data->state.async.done) { - *dns = data->state.async.dns; - return ares->result; - } + DEBUGASSERT(pdns); + *pdns = NULL; + if(!ares) + return CURLE_FAILED_INIT; if(Curl_ares_perform(ares->channel, 0) < 0) { result = CURLE_UNRECOVERABLE_POLL; goto out; } -#ifndef HAVE_CARES_GETADDRINFO - /* Now that we have checked for any last minute results above, see if there - are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer - expires. */ - if(ares->num_pending && - /* This is only set to non-zero if the timer was started. */ - (ares->happy_eyeballs_dns_time.tv_sec || - ares->happy_eyeballs_dns_time.tv_usec) && - (curlx_ptimediff_ms(Curl_pgrs_now(data), - &ares->happy_eyeballs_dns_time) >= - HAPPY_EYEBALLS_DNS_TIMEOUT)) { - /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer - running. */ - memset(&ares->happy_eyeballs_dns_time, 0, - sizeof(ares->happy_eyeballs_dns_time)); - - /* Cancel the raw c-ares request, which will fire query_completed_cb() with - ARES_ECANCELLED synchronously for all pending responses. This will - leave us with res->num_pending == 0, which is perfect for the next - block. */ - ares_cancel(ares->channel); - DEBUGASSERT(ares->num_pending == 0); + if(async->queries_ongoing) { + result = CURLE_AGAIN; + goto out; } -#endif - if(!ares->num_pending) { - /* all c-ares operations done, what is the result to report? */ - Curl_resolv_unlink(data, &data->state.async.dns); - data->state.async.done = TRUE; - result = ares->result; - if(ares->ares_status == ARES_SUCCESS && !result) { - data->state.async.dns = - Curl_dnscache_mk_entry(data, ares->temp_ai, - data->state.async.hostname, 0, - data->state.async.port, FALSE); - if(data->state.async.dns) - ares->temp_ai = NULL; /* temp_ai now owned by entry */ + /* all c-ares operations done, what is the result to report? */ + result = ares->result; + if(ares->ares_status == ARES_SUCCESS && !result) { + struct Curl_dns_entry *dns = + Curl_dnscache_mk_entry2(data, async->dns_queries, + &ares->res_AAAA, &ares->res_A, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } #ifdef HTTPSRR_WORKS - if(data->state.async.dns) { + if(async->dns_queries & CURL_DNSQ_HTTPS) { + if(ares->hinfo.complete) { struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo); if(!lhrr) result = CURLE_OUT_OF_MEMORY; else - data->state.async.dns->hinfo = lhrr; + Curl_dns_entry_set_https_rr(dns, lhrr); } + else + Curl_dns_entry_set_https_rr(dns, NULL); + } #endif - if(!result && data->state.async.dns) - result = Curl_dnscache_add(data, data->state.async.dns); + if(!result) { + *pdns = dns; } - /* if we have not found anything, report the proper - * CURLE_COULDNT_RESOLVE_* code */ - if(!result && !data->state.async.dns) { - const char *msg = NULL; - if(ares->ares_status != ARES_SUCCESS) - msg = ares_strerror(ares->ares_status); - result = Curl_resolver_error(data, msg); - } - - if(result) - Curl_resolv_unlink(data, &data->state.async.dns); - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound", - result, *dns ? "" : "not "); - async_ares_cleanup(data); + } + /* if we have not found anything, report the proper + * CURLE_COULDNT_RESOLVE_* code */ + if(!result && !*pdns) { + const char *msg = NULL; + if(ares->ares_status != ARES_SUCCESS) + msg = ares_strerror(ares->ares_status); + result = Curl_async_failed(data, async, msg); } + CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound", + result, *pdns ? "" : "not "); + async_ares_cleanup(async); + out: - ares->result = result; + if(result != CURLE_AGAIN) + ares->result = result; return result; } +static timediff_t async_ares_poll_timeout(struct async_ares_ctx *ares, + timediff_t timeout_ms) +{ + struct timeval *ares_calced, time_buf, max_timeout; + int itimeout_ms; + +#if TIMEDIFF_T_MAX > INT_MAX + itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : + ((timeout_ms < 0) ? -1 : (int)timeout_ms); +#else + itimeout_ms = (int)timeout_ms; +#endif + max_timeout.tv_sec = itimeout_ms / 1000; + max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; + + /* c-ares tells us the shortest timeout of any operation on channel */ + ares_calced = ares_timeout(ares->channel, &max_timeout, &time_buf); + /* use the timeout period ares returned to us above if less than one + second is left, otherwise use 1000ms to make sure the progress callback + gets called frequent enough */ + if(!ares_calced->tv_sec) + return (timediff_t)(ares_calced->tv_usec / 1000); + else + return 1000; +} + +static const struct Curl_addrinfo *async_ares_get_ai( + const struct Curl_addrinfo *ai, + int ai_family, + unsigned int index) +{ + unsigned int i = 0; + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } + } + return NULL; +} + +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index) +{ + struct async_ares_ctx *ares = &async->ares; + + (void)data; + switch(ai_family) { + case AF_INET: + if(ares->res_A) + return async_ares_get_ai(ares->res_A, ai_family, index); + break; + case AF_INET6: + if(ares->res_AAAA) + return async_ares_get_ai(ares->res_AAAA, ai_family, index); + break; + default: + break; + } + return NULL; +} + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + if(Curl_async_knows_https(data, async)) + return &async->ares.hinfo; + return NULL; +} + +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->dns_queries & CURL_DNSQ_HTTPS) + return ((async->dns_responses & CURL_DNSQ_HTTPS) || + !async->queries_ongoing); + return TRUE; /* we know it will never come */ +} + +#endif /* USE_HTTPSRR */ + /* * Curl_async_await() * * Waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". * - * 'entry' MUST be non-NULL. + * 'pdns' MUST be non-NULL. * * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **entry) +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + struct async_ares_ctx *ares = async ? &async->ares : NULL; + struct curltime start = *Curl_pgrs_now(data); CURLcode result = CURLE_OK; - timediff_t timeout_ms; - DEBUGASSERT(entry); - *entry = NULL; /* clear on entry */ + DEBUGASSERT(pdns); + *pdns = NULL; /* clear on entry */ - timeout_ms = Curl_timeleft_ms(data, TRUE); - if(timeout_ms < 0) { - /* already expired! */ - connclose(data->conn, "Timed out before name resolve started"); - return CURLE_OPERATION_TIMEDOUT; - } - if(!timeout_ms) - timeout_ms = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve */ + if(!ares) + return CURLE_FAILED_INIT; - /* Wait for the name resolve query to complete. */ + /* Wait for the name resolve query to complete or time out. */ while(!result) { - struct timeval *real_timeout, time_buf, max_timeout; - int itimeout_ms; - timediff_t call_timeout_ms; + timediff_t timeout_ms; -#if TIMEDIFF_T_MAX > INT_MAX - itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : (int)timeout_ms; -#else - itimeout_ms = (int)timeout_ms; -#endif + timeout_ms = Curl_timeleft_ms(data); + if(!timeout_ms) { /* no applicable timeout from `data`*/ + timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &start); + if(elapsed_ms < CURL_TIMEOUT_RESOLVE_MS) + timeout_ms = CURL_TIMEOUT_RESOLVE_MS - elapsed_ms; + else + timeout_ms = -1; + } - max_timeout.tv_sec = itimeout_ms / 1000; - max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } - real_timeout = ares_timeout(ares->channel, &max_timeout, &time_buf); + if(Curl_ares_perform(ares->channel, + async_ares_poll_timeout(ares, timeout_ms)) < 0) { + result = CURLE_UNRECOVERABLE_POLL; + break; + } - /* use the timeout period ares returned to us above if less than one - second is left, otherwise just use 1000ms to make sure the progress - callback gets called frequent enough */ - if(!real_timeout->tv_sec) - call_timeout_ms = (timediff_t)(real_timeout->tv_usec / 1000); - else - call_timeout_ms = 1000; - - if(Curl_ares_perform(ares->channel, call_timeout_ms) < 0) - return CURLE_UNRECOVERABLE_POLL; - - result = Curl_async_is_resolved(data, entry); - if(result || data->state.async.done) + result = Curl_async_take_result(data, async, pdns); + if(result == CURLE_AGAIN) + result = CURLE_OK; + else if(result || *pdns) break; - if(Curl_pgrsUpdate(data)) + if(Curl_pgrsUpdate(data)) { result = CURLE_ABORTED_BY_CALLBACK; - else { - struct curltime now = curlx_now(); /* update in loop */ - timediff_t elapsed_ms = curlx_ptimediff_ms(&now, Curl_pgrs_now(data)); - if(elapsed_ms <= 0) - timeout_ms -= 1; /* always deduct at least 1 */ - else if(elapsed_ms > timeout_ms) - timeout_ms = -1; - else - timeout_ms -= elapsed_ms; + break; } - if(timeout_ms < 0) - result = CURLE_OPERATION_TIMEDOUT; } - /* Operation complete, if the lookup was successful we now have the entry - in the cache. */ - data->state.async.done = TRUE; - *entry = data->state.async.dns; - if(result) ares_cancel(ares->channel); return result; } -#ifndef HAVE_CARES_GETADDRINFO - -/* Connects results to the list */ -static void async_addr_concat(struct Curl_addrinfo **pbase, - struct Curl_addrinfo *ai) -{ - if(!ai) - return; - - /* When adding `ai` to an existing address list, we prefer ipv6 - * to be in front. */ -#ifdef USE_IPV6 /* CURLRES_IPV6 */ - if(*pbase && (*pbase)->ai_family == PF_INET6) { - /* ipv6 already in front, append `ai` */ - struct Curl_addrinfo *tail = *pbase; - while(tail->ai_next) - tail = tail->ai_next; - tail->ai_next = ai; - } - else -#endif /* CURLRES_IPV6 */ - { - /* prepend to the (possibly) existing list. */ - struct Curl_addrinfo *tail = ai; - while(tail->ai_next) - tail = tail->ai_next; - tail->ai_next = *pbase; - *pbase = ai; - } -} - -/* - * ares_query_completed_cb() is the callback that ares will call when - * the host query initiated by ares_gethostbyname() from - * Curl_async_getaddrinfo(), when using ares, is completed either - * successfully or with failure. - */ -static void async_ares_hostbyname_cb(void *user_data, - int status, - int timeouts, - struct hostent *hostent) -{ - struct Curl_easy *data = (struct Curl_easy *)user_data; - struct async_ares_ctx *ares = &data->state.async.ares; - - (void)timeouts; /* ignored */ - - if(ARES_EDESTRUCTION == status) - return; - - if(ARES_SUCCESS == status) { - ares->ares_status = status; /* one success overrules any error */ - async_addr_concat(&ares->temp_ai, - Curl_he2ai(hostent, data->state.async.port)); - } - else if(ares->ares_status != ARES_SUCCESS) { - /* no success so far, remember last error */ - ares->ares_status = status; - } - - ares->num_pending--; - - CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, " - "addr=%sfound", - status, ares->num_pending, ares->temp_ai ? "" : "not "); - /* If there are responses still pending, we presume they must be the - complementary IPv4 or IPv6 lookups that we started in parallel in - Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a - "definitive" response from one of a set of parallel queries, we need to - think about how long we are willing to wait for more responses. */ - if(ares->num_pending - /* Only these c-ares status values count as "definitive" for these - purposes. For example, ARES_ENODATA is what we expect when there is - no IPv6 entry for a domain name, and that is not a reason to get more - aggressive in our timeouts for the other response. Other errors are - either a result of bad input (which should affect all parallel - requests), local or network conditions, non-definitive server - responses, or us cancelling the request. */ - && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) { - /* Right now, there can only be up to two parallel queries, so do not - bother handling any other cases. */ - DEBUGASSERT(ares->num_pending == 1); - - /* it is possible that one of these parallel queries could succeed - quickly, but the other could always fail or timeout (when we are - talking to a pool of DNS servers that can only successfully resolve - IPv4 address, for example). - - it is also possible that the other request could always just take - longer because it needs more time or only the second DNS server can - fulfill it successfully. But, to align with the philosophy of Happy - Eyeballs, we do not want to wait _too_ long or users will think - requests are slow when IPv6 lookups do not actually work (but IPv4 - ones do). - - So, now that we have a usable answer (some IPv4 addresses, some IPv6 - addresses, or "no such domain"), we start a timeout for the remaining - pending responses. Even though it is typical that this resolved - request came back quickly, that need not be the case. It might be that - this completing request did not get a result from the first DNS - server or even the first round of the whole DNS server pool. So it - could already be quite some time after we issued the DNS queries in - the first place. Without modifying c-ares, we cannot know exactly - where in its retry cycle we are. We could guess based on how much - time has gone by, but it does not really matter. Happy Eyeballs tells - us that, given usable information in hand, we simply do not want to - wait "too much longer" after we get a result. - - We simply wait an additional amount of time equal to the default - c-ares query timeout. That is enough time for a typical parallel - response to arrive without being "too long". Even on a network - where one of the two types of queries is failing or timing out - constantly, this will usually mean we wait a total of the default - c-ares timeout (5 seconds) plus the round trip time for the successful - request, which seems bearable. The downside is that c-ares might race - with us to issue one more retry just before we give up, but it seems - better to "waste" that request instead of trying to guess the perfect - timeout to prevent it. After all, we do not even know where in the - c-ares retry cycle each request is. - */ - ares->happy_eyeballs_dns_time = *Curl_pgrs_now(data); - Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS); - } -} - -#else -/* c-ares 1.16.0 or later */ - /* * async_ares_node2addr() converts an address list provided by c-ares * to an internal libcurl compatible list. */ -static struct Curl_addrinfo * -async_ares_node2addr(struct ares_addrinfo_node *node) +static struct Curl_addrinfo *async_ares_node2addr( + struct ares_addrinfo_node *node) { /* traverse the ares_addrinfo_node list */ struct ares_addrinfo_node *ai; @@ -602,8 +492,8 @@ for(ai = node; ai != NULL; ai = ai->ai_next) { size_t ss_size; struct Curl_addrinfo *ca; - /* ignore elements with unsupported address family, */ - /* settle family-specific sockaddr structure size. */ + /* ignore elements with unsupported address family, + settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); #ifdef USE_IPV6 @@ -627,8 +517,8 @@ break; } - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ + /* copy each structure member individually, member ordering, + size, or padding might be different for each platform. */ ca->ai_flags = ai->ai_flags; ca->ai_family = ai->ai_family; @@ -661,46 +551,70 @@ return cafirst; } -static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts, - struct ares_addrinfo *ares_ai) +static void async_ares_A_cb(void *user_data, int status, int timeouts, + struct ares_addrinfo *ares_ai) { - struct Curl_easy *data = (struct Curl_easy *)user_data; - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + (void)timeouts; - if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ - ares->ares_status = status; + if(!async) + return; + + async->dns_responses |= CURL_DNSQ_A; + async->queries_ongoing--; + async->done = !async->queries_ongoing; if(status == ARES_SUCCESS) { - ares->temp_ai = async_ares_node2addr(ares_ai->nodes); + ares->ares_status = ARES_SUCCESS; + ares->res_A = async_ares_node2addr(ares_ai->nodes); ares_freeaddrinfo(ares_ai); } - ares->num_pending--; - CURL_TRC_DNS(data, "ares: addrinfo done, query status=%d, " - "overall status=%d, pending=%d, addr=%sfound", - status, ares->ares_status, ares->num_pending, - ares->temp_ai ? "" : "not "); + else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ + ares->ares_status = status; } -#endif +#ifdef CURLRES_IPV6 +static void async_ares_AAAA_cb(void *user_data, int status, int timeouts, + struct ares_addrinfo *ares_ai) +{ + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + + (void)timeouts; + if(!async) + return; + + async->dns_responses |= CURL_DNSQ_AAAA; + async->queries_ongoing--; + async->done = !async->queries_ongoing; + if(status == ARES_SUCCESS) { + ares->ares_status = ARES_SUCCESS; + ares->res_AAAA = async_ares_node2addr(ares_ai->nodes); + ares_freeaddrinfo(ares_ai); + } + else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ + ares->ares_status = status; +} +#endif /* CURLRES_IPV6 */ #ifdef USE_HTTPSRR static void async_ares_rr_done(void *user_data, ares_status_t status, size_t timeouts, const ares_dns_record_t *dnsrec) { - struct Curl_easy *data = user_data; - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + + if(!async) + return; (void)timeouts; - --ares->num_pending; - CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, " - "dnsres=%sfound", - status, ares->num_pending, - (dnsrec && - ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ? - "" : "not "); + async->dns_responses |= CURL_DNSQ_HTTPS; + async->queries_ongoing--; + async->done = !async->queries_ongoing; if((ARES_SUCCESS != status) || !dnsrec) return; - ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo); + ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo); } #endif /* USE_HTTPSRR */ @@ -709,117 +623,92 @@ * * Starts a name resolve for the given hostname and port number. */ -CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, - int port, int ip_version) +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; -#ifdef USE_HTTPSRR - char *rrname = NULL; -#endif + struct async_ares_ctx *ares = &async->ares; + char service[12]; + int socktype; + CURLcode result = CURLE_OK; - if(async_ares_init_lazy(data)) - return CURLE_FAILED_INIT; - - data->state.async.done = FALSE; /* not done */ - data->state.async.dns = NULL; /* clear */ - data->state.async.port = port; - data->state.async.ip_version = ip_version; - data->state.async.hostname = curlx_strdup(hostname); - if(!data->state.async.hostname) - return CURLE_OUT_OF_MEMORY; -#ifdef USE_HTTPSRR - if(port != 443) { - rrname = curl_maprintf("_%d_.https.%s", port, hostname); - if(!rrname) - return CURLE_OUT_OF_MEMORY; + if(ares->channel) { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + goto out; } -#endif - /* initial status - failed */ - ares->ares_status = ARES_ENOTFOUND; - ares->result = CURLE_OK; + result = async_ares_init(data, async); + if(result) + goto out; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) && \ - ARES_VERSION >= 0x011800 /* >= v1.24.0 */ + result = Curl_resolv_announce_start(data, ares->channel); + if(result) + goto out; + +#if defined(CURLVERBOSE) && ARES_VERSION >= 0x011800 /* >= v1.24.0 */ if(CURL_TRC_DNS_is_verbose(data)) { char *csv = ares_get_servers_csv(ares->channel); - CURL_TRC_DNS(data, "asyn-ares: servers=%s", csv); + CURL_TRC_DNS(data, "ares: servers=%s", csv); ares_free_string(csv); } #endif -#ifdef HAVE_CARES_GETADDRINFO - { - struct ares_addrinfo_hints hints; - char service[12]; - int pf = PF_INET; - memset(&hints, 0, sizeof(hints)); + curl_msnprintf(service, sizeof(service), "%d", async->port); + socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? + SOCK_STREAM : SOCK_DGRAM; + #ifdef CURLRES_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && - Curl_ipv6works(data)) { - /* The stack seems to be IPv6-enabled */ - if(ip_version == CURL_IPRESOLVE_V6) - pf = PF_INET6; - else - pf = PF_UNSPEC; - } -#endif /* CURLRES_IPV6 */ - CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s", - (pf == PF_UNSPEC) ? "A+AAAA" : - ((pf == PF_INET) ? "A" : "AAAA")); - hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? - SOCK_STREAM : SOCK_DGRAM; - /* Since the service is a numerical one, set the hint flags - * accordingly to save a call to getservbyname in inside C-Ares - */ + if(async->dns_queries & CURL_DNSQ_AAAA) { + struct ares_addrinfo_hints hints; + + memset(&hints, 0, sizeof(hints)); + CURL_TRC_DNS(data, "ares: query AAAA records for %s", async->hostname); + hints.ai_family = PF_INET6; + hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; - curl_msnprintf(service, sizeof(service), "%d", port); - ares->num_pending = 1; - ares_getaddrinfo(ares->channel, data->state.async.hostname, - service, &hints, async_ares_addrinfo_cb, data); + async->queries_ongoing++; + ares_getaddrinfo(ares->channel, async->hostname, + service, &hints, async_ares_AAAA_cb, async); } -#else +#endif /* CURLRES_IPV6 */ -#ifdef HAVE_CARES_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { - /* The stack seems to be IPv6-enabled */ - /* areschannel is already setup in the Curl_open() function */ - CURL_TRC_DNS(data, "asyn-ares: fire off query for A"); - ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET, - async_ares_hostbyname_cb, data); - CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA"); - ares->num_pending = 2; - ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6, - async_ares_hostbyname_cb, data); + if(async->dns_queries & CURL_DNSQ_A) { + struct ares_addrinfo_hints hints; + + memset(&hints, 0, sizeof(hints)); + CURL_TRC_DNS(data, "ares: query A records for %s", async->hostname); + hints.ai_family = PF_INET; + hints.ai_socktype = socktype; + hints.ai_flags = ARES_AI_NUMERICSERV; + async->queries_ongoing++; + ares_getaddrinfo(ares->channel, async->hostname, + service, &hints, async_ares_A_cb, async); } - else -#endif - { - /* areschannel is already setup in the Curl_open() function */ - CURL_TRC_DNS(data, "asyn-ares: fire off query for A"); - ares->num_pending = 1; - ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET, - async_ares_hostbyname_cb, data); - } -#endif + #ifdef USE_HTTPSRR - { - CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR: %s", - rrname ? rrname : data->state.async.hostname); - memset(&ares->hinfo, 0, sizeof(ares->hinfo)); - ares->hinfo.port = -1; + memset(&ares->hinfo, 0, sizeof(ares->hinfo)); + if(async->dns_queries & CURL_DNSQ_HTTPS) { + char *rrname = NULL; + if(async->port != 443) { + rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname); + if(!rrname) + return CURLE_OUT_OF_MEMORY; + } + CURL_TRC_DNS(data, "ares: query HTTPS records for %s", + rrname ? rrname : async->hostname); ares->hinfo.rrname = rrname; - ares->num_pending++; /* one more */ + async->queries_ongoing++; ares_query_dnsrec(ares->channel, - rrname ? rrname : data->state.async.hostname, + rrname ? rrname : async->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, - async_ares_rr_done, data, NULL); + async_ares_rr_done, async, NULL); } -#endif +#endif /* USE_HTTPSRR */ - return CURLE_OK; +out: + ares->result = result; + return result ? result : (async->queries_ongoing ? CURLE_AGAIN : CURLE_OK); } /* Set what DNS server are is to use. This is called in 2 situations: @@ -829,33 +718,24 @@ * 2. When we lazy init the ares channel and NULL means that there * are no preferences and we do not reset any existing channel. */ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, - bool reset_on_null) + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; CURLcode result = CURLE_NOT_BUILT_IN; const char *servers = data->set.str[STRING_DNS_SERVERS]; int ares_result = ARES_SUCCESS; -#if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV) +#ifdef DEBUGBUILD if(getenv("CURL_DNS_SERVER")) servers = getenv("CURL_DNS_SERVER"); #endif - if(!servers) { - if(reset_on_null) { - Curl_async_destroy(data); - } + if(!servers) return CURLE_OK; - } -#ifdef HAVE_CARES_SERVERS_CSV - /* if channel is not there, this is just a parameter check */ - if(ares->channel) -#ifdef HAVE_CARES_PORTS_CSV + /* if channel is not there, this is a parameter check */ + if(ares && ares->channel) ares_result = ares_set_servers_ports_csv(ares->channel, servers); -#else - ares_result = ares_set_servers_csv(ares->channel, servers); -#endif switch(ares_result) { case ARES_SUCCESS: result = CURLE_OK; @@ -871,47 +751,33 @@ result = CURLE_BAD_FUNCTION_ARGUMENT; break; } -#else /* c-ares version too old! */ - (void)data; - (void)(ares_result); -#endif return result; } -CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data) +static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, + struct Curl_resolv_async *async) { - return async_ares_set_dns_servers(data, TRUE); -} - -CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data) -{ -#ifdef HAVE_CARES_LOCAL_DEV - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; const char *interf = data->set.str[STRING_DNS_INTERFACE]; if(!interf) interf = ""; - /* if channel is not there, this is just a parameter check */ - if(ares->channel) + /* if channel is not there, this is a parameter check */ + if(ares && ares->channel) ares_set_local_dev(ares->channel, interf); return CURLE_OK; -#else /* c-ares version too old! */ - (void)data; - (void)interf; - return CURLE_NOT_BUILT_IN; -#endif } -CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data) +static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef HAVE_CARES_SET_LOCAL - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; struct in_addr a4; const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4]; - if((!local_ip4) || (local_ip4[0] == 0)) { + if(!local_ip4 || (local_ip4[0] == 0)) { a4.s_addr = 0; /* disabled: do not bind to a specific address */ } else { @@ -921,26 +787,22 @@ } } - /* if channel is not there yet, this is just a parameter check */ - if(ares->channel) + /* if channel is not there yet, this is a parameter check */ + if(ares && ares->channel) ares_set_local_ip4(ares->channel, ntohl(a4.s_addr)); return CURLE_OK; -#else /* c-ares version too old! */ - (void)data; - (void)local_ip4; - return CURLE_NOT_BUILT_IN; -#endif } -CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) +static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6) - struct async_ares_ctx *ares = &data->state.async.ares; +#ifdef USE_IPV6 + struct async_ares_ctx *ares = async ? &async->ares : NULL; unsigned char a6[INET6_ADDRSTRLEN]; const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6]; - if((!local_ip6) || (local_ip6[0] == 0)) { + if(!local_ip6 || (local_ip6[0] == 0)) { /* disabled: do not bind to a specific address */ memset(a6, 0, sizeof(a6)); } @@ -951,15 +813,16 @@ } } - /* if channel is not there, this is just a parameter check */ - if(ares->channel) + /* if channel is not there, this is a parameter check */ + if(ares && ares->channel) ares_set_local_ip6(ares->channel, a6); return CURLE_OK; -#else /* c-ares version too old! */ +#else /* no IPv6 support */ (void)data; + (void)async; return CURLE_NOT_BUILT_IN; #endif } -#endif /* CURLRES_ARES */ +#endif /* USE_RESOLV_ARES */
diff --git a/Utilities/cmcurl/lib/asyn-base.c b/Utilities/cmcurl/lib/asyn-base.c index 02797f1..62cb0ef 100644 --- a/Utilities/cmcurl/lib/asyn-base.c +++ b/Utilities/cmcurl/lib/asyn-base.c
@@ -39,13 +39,14 @@ #ifdef USE_ARES #include <ares.h> -#include <ares_version.h> /* really old c-ares did not include it by itself */ #endif #include "urldata.h" -#include "asyn.h" +#include "connect.h" +#include "curl_trc.h" #include "hostip.h" #include "multiif.h" +#include "progress.h" #include "select.h" #include "url.h" @@ -54,10 +55,21 @@ **********************************************************************/ #ifdef CURLRES_ASYNCH +timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + if(async->timeout_ms) { + timediff_t elapsed_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + return async->timeout_ms - elapsed_ms; + } + return Curl_timeleft_ms(data); +} + #ifdef USE_ARES -#if ARES_VERSION < 0x010600 -#error "requires c-ares 1.6.0 or newer" +#if ARES_VERSION < 0x011000 +#error "requires c-ares 1.16.0 or newer" #endif /* @@ -72,12 +84,8 @@ ares_channel channel, struct easy_pollset *ps) { - struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; - struct timeval timebuf; curl_socket_t sockets[16]; /* ARES documented limit */ unsigned int bitmap, i; - struct timeval *timeout; - timediff_t milli; CURLcode result = CURLE_OK; DEBUGASSERT(channel); @@ -98,15 +106,33 @@ if(result) return result; } - - timeout = ares_timeout(channel, &maxtime, &timebuf); - if(!timeout) - timeout = &maxtime; - milli = curlx_tvtoms(timeout); - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); return result; } +timediff_t Curl_ares_timeout_ms(struct Curl_easy *data, + struct Curl_resolv_async *async, + ares_channel channel) +{ + timediff_t async_timeout_ms; + + DEBUGASSERT(channel); + if(!channel) + return -1; + + async_timeout_ms = Curl_async_timeleft_ms(data, async); + if((async_timeout_ms > 0) && (async_timeout_ms < INT_MAX)) { + struct timeval timebuf; + struct timeval *timeout; + struct timeval end = { (int)async_timeout_ms / 1000, + ((int)async_timeout_ms % 1000) * 1000 }; + + timeout = ares_timeout(channel, &end, &timebuf); + if(timeout) + return curlx_tvtoms(timeout); + } + return async_timeout_ms; +} + /* * Curl_ares_perform() * @@ -156,7 +182,7 @@ nfds = 0; if(!nfds) - /* Call ares_process() unconditionally here, even if we simply timed out + /* Call ares_process() unconditionally here, even if we timed out above, as otherwise the ares name resolve will not timeout! */ ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); else { @@ -179,32 +205,61 @@ #include "doh.h" -void Curl_async_shutdown(struct Curl_easy *data) +void Curl_async_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef CURLRES_ARES - Curl_async_ares_shutdown(data); + if(async) { + CURL_TRC_DNS(data, "[%u] shutdown async", async->id); + async->shutdown = TRUE; +#ifdef USE_RESOLV_ARES + Curl_async_ares_shutdown(data, async); #endif -#ifdef CURLRES_THREADED - Curl_async_thrdd_shutdown(data); +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_shutdown(data, async); #endif #ifndef CURL_DISABLE_DOH - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); #endif - Curl_safefree(data->state.async.hostname); + } } -void Curl_async_destroy(struct Curl_easy *data) +void Curl_async_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef CURLRES_ARES - Curl_async_ares_destroy(data); + if(async) { + CURL_TRC_DNS(data, "[%u] destroy async", async->id); + async->shutdown = TRUE; +#ifdef USE_RESOLV_ARES + Curl_async_ares_destroy(data, async); #endif -#ifdef CURLRES_THREADED - Curl_async_thrdd_destroy(data); +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_destroy(data, async); #endif #ifndef CURL_DISABLE_DOH - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); #endif - Curl_safefree(data->state.async.hostname); + curlx_safefree(async); + } +} + +CURLcode Curl_async_failed(struct Curl_easy *data, + struct Curl_resolv_async *async, + const char *detail) +{ + const char *host_or_proxy = "host"; + CURLcode result = CURLE_COULDNT_RESOLVE_HOST; + +#ifndef CURL_DISABLE_PROXY + if(async->for_proxy) { + host_or_proxy = "proxy"; + result = CURLE_COULDNT_RESOLVE_PROXY; + } +#endif + + failf(data, "Could not resolve %s: %s%s%s%s", + host_or_proxy, async->hostname, + detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); + return result; } #endif /* USE_CURL_ASYNC */
diff --git a/Utilities/cmcurl/lib/asyn-thrdd.c b/Utilities/cmcurl/lib/asyn-thrdd.c index 690e813..90f055c 100644 --- a/Utilities/cmcurl/lib/asyn-thrdd.c +++ b/Utilities/cmcurl/lib/asyn-thrdd.c
@@ -26,9 +26,7 @@ /*********************************************************************** * Only for threaded name resolves builds **********************************************************************/ -#ifdef CURLRES_THREADED - -#include "socketpair.h" +#ifdef USE_RESOLV_THREADED #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> @@ -44,10 +42,6 @@ #include <inet.h> #endif -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) -#include <pthread.h> -#endif - #ifdef HAVE_GETADDRINFO #define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */ #else @@ -56,13 +50,19 @@ #include "urldata.h" #include "cfilters.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" +#include "httpsrr.h" #include "url.h" #include "multiif.h" #include "curl_threads.h" #include "progress.h" +#include "rand.h" #include "select.h" +#include "thrdqueue.h" +#include "curlx/strparse.h" +#include "curlx/wait.h" #ifdef USE_ARES #include <ares.h> @@ -99,244 +99,93 @@ #endif } -static void async_thrdd_destroy(struct Curl_easy *); -static void async_thrdd_shutdown(struct Curl_easy *); +#ifdef CURLVERBOSE +#define CURL_ASYN_ITEM_DESC_LEN 64 +#define async_item_description(x) (x)->description +#else +#define async_item_description(x) NULL +#endif -CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) -{ - (void)data; - *impl = NULL; - return CURLE_OK; -} +struct async_thrdd_item { + struct Curl_addrinfo *res; +#ifdef CURLVERBOSE + char description[CURL_ASYN_ITEM_DESC_LEN]; +#endif + int sock_error; + uint32_t mid; + uint32_t resolv_id; + uint16_t port; + uint8_t transport; + uint8_t dns_queries; +#ifdef DEBUGBUILD + uint32_t delay_ms; + uint32_t delay_fail_ms; +#endif + char hostname[1]; +}; /* Give up reference to add_ctx */ -static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx, - struct Curl_easy *data) +static void async_thrdd_item_destroy(struct async_thrdd_item *item) { - struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx; - bool destroy; - - (void)data; - if(!addr_ctx) - return; - - Curl_mutex_acquire(&addr_ctx->mutx); - if(!data) /* called by resolving thread */ - addr_ctx->thrd_done = TRUE; - - DEBUGASSERT(addr_ctx->ref_count); - --addr_ctx->ref_count; - destroy = !addr_ctx->ref_count; - Curl_mutex_release(&addr_ctx->mutx); - - if(destroy) { - Curl_mutex_destroy(&addr_ctx->mutx); - curlx_free(addr_ctx->hostname); - if(addr_ctx->res) - Curl_freeaddrinfo(addr_ctx->res); -#ifndef CURL_DISABLE_SOCKETPAIR -#ifndef USE_EVENTFD - wakeup_close(addr_ctx->sock_pair[1]); -#endif - wakeup_close(addr_ctx->sock_pair[0]); -#endif - curlx_free(addr_ctx); + if(item) { + if(item->res) + Curl_freeaddrinfo(item->res); + curlx_free(item); } - *paddr_ctx = NULL; } /* Initialize context for threaded resolver */ -static struct async_thrdd_addr_ctx * -addr_ctx_create(struct Curl_easy *data, - const char *hostname, int port, - const struct addrinfo *hints) +static struct async_thrdd_item *async_thrdd_item_create( + struct Curl_easy *data, + uint32_t resolv_id, uint8_t dns_queries, + const char *hostname, uint16_t port, + uint8_t transport) { - struct async_thrdd_addr_ctx *addr_ctx = curlx_calloc(1, sizeof(*addr_ctx)); - if(!addr_ctx) + size_t hostlen = strlen(hostname); + struct async_thrdd_item *item; + + item = curlx_calloc(1, sizeof(*item) + hostlen); + if(!item) return NULL; - addr_ctx->thread_hnd = curl_thread_t_null; - addr_ctx->port = port; - addr_ctx->ref_count = 1; + if(hostlen) /* NUL byte of name already in struct size */ + memcpy(item->hostname, hostname, hostlen); + item->mid = data->mid; + item->resolv_id = resolv_id; + item->dns_queries = dns_queries; + item->port = port; + item->transport = transport; -#ifdef HAVE_GETADDRINFO - DEBUGASSERT(hints); - addr_ctx->hints = *hints; -#else - (void)hints; +#ifdef CURLVERBOSE + curl_msnprintf(item->description, sizeof(item->description), + "[%" FMT_OFF_T "/%u] %s %s:%u", + data->id, item->resolv_id, + Curl_resolv_query_str(dns_queries), + item->hostname, item->port); #endif - Curl_mutex_init(&addr_ctx->mutx); - -#ifndef CURL_DISABLE_SOCKETPAIR - /* create socket pair or pipe */ - if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) { - addr_ctx->sock_pair[0] = CURL_SOCKET_BAD; - addr_ctx->sock_pair[1] = CURL_SOCKET_BAD; - goto err_exit; - } -#endif - addr_ctx->sock_error = 0; - - /* Copying hostname string because original can be destroyed by parent - * thread during gethostbyname execution. - */ - addr_ctx->hostname = curlx_strdup(hostname); - if(!addr_ctx->hostname) - goto err_exit; - - return addr_ctx; - -err_exit: - addr_ctx_unlink(&addr_ctx, data); - return NULL; -} - -#ifdef HAVE_GETADDRINFO - -/* - * getaddrinfo_thread() resolves a name and then exits. - * - * For builds without ARES, but with USE_IPV6, create a resolver thread - * and wait on it. - */ -static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg) -{ - struct async_thrdd_addr_ctx *addr_ctx = arg; - bool do_abort; - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); - - if(!do_abort) { - char service[12]; - int rc; - - curl_msnprintf(service, sizeof(service), "%d", addr_ctx->port); - - rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service, - &addr_ctx->hints, &addr_ctx->res); - - if(rc) { - addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc; - if(addr_ctx->sock_error == 0) - addr_ctx->sock_error = RESOLVER_ENOMEM; - } - else { - Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port); - } - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!do_abort) { -#ifdef USE_EVENTFD - const uint64_t buf[1] = { 1 }; -#else - const char buf[1] = { 1 }; -#endif - /* Thread is done, notify transfer */ - if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) { - /* update sock_error to errno */ - addr_ctx->sock_error = SOCKERRNO; +#ifdef DEBUGBUILD + { + const char *p = getenv("CURL_DBG_RESOLV_DELAY"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + item->delay_ms = (uint32_t)l; } } -#endif - } - - addr_ctx_unlink(&addr_ctx, NULL); - return 0; -} - -#else /* HAVE_GETADDRINFO */ - -/* - * gethostbyname_thread() resolves a name and then exits. - */ -static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg) -{ - struct async_thrdd_addr_ctx *addr_ctx = arg; - bool do_abort; - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); - - if(!do_abort) { - addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port); - if(!addr_ctx->res) { - addr_ctx->sock_error = SOCKERRNO; - if(addr_ctx->sock_error == 0) - addr_ctx->sock_error = RESOLVER_ENOMEM; - } - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!do_abort) { -#ifdef USE_EVENTFD - const uint64_t buf[1] = { 1 }; -#else - const char buf[1] = { 1 }; -#endif - /* Thread is done, notify transfer */ - if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) { - /* update sock_error to errno */ - addr_ctx->sock_error = SOCKERRNO; + p = getenv("CURL_DBG_RESOLV_FAIL_DELAY"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + unsigned char c = 0; + Curl_rand_bytes(data, FALSE, &c, 1); + item->delay_fail_ms = (uint32_t)l + c; } } -#endif } - - addr_ctx_unlink(&addr_ctx, NULL); - return 0; -} - -#endif /* HAVE_GETADDRINFO */ - -/* - * async_thrdd_destroy() cleans up async resolver data and thread handle. - */ -static void async_thrdd_destroy(struct Curl_easy *data) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr = thrdd->addr; - -#ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - ares_destroy(thrdd->rr.channel); - thrdd->rr.channel = NULL; - } - Curl_httpsrr_cleanup(&thrdd->rr.hinfo); #endif - if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) { - bool done; - - Curl_mutex_acquire(&addr->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!addr->do_abort) - Curl_multi_will_close(data, addr->sock_pair[0]); -#endif - addr->do_abort = TRUE; - done = addr->thrd_done; - Curl_mutex_release(&addr->mutx); - - if(done) { - Curl_thread_join(&addr->thread_hnd); - CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined"); - } - else { - /* thread is still running. Detach it. */ - Curl_thread_destroy(&addr->thread_hnd); - CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached"); - } - } - /* release our reference to the shared context */ - addr_ctx_unlink(&thrdd->addr, data); + return item; } #ifdef USE_HTTPSRR_ARES @@ -345,25 +194,30 @@ size_t timeouts, const ares_dns_record_t *dnsrec) { - struct Curl_easy *data = user_data; - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; + struct Curl_resolv_async *async = user_data; + struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; (void)timeouts; - thrdd->rr.done = TRUE; - if((ARES_SUCCESS != status) || !dnsrec) + if(!thrdd) return; - thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo); + + async->dns_responses |= CURL_DNSQ_HTTPS; + async->queries_ongoing--; + async->done = !async->queries_ongoing; + if((ARES_SUCCESS == status) && dnsrec) + async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo); } -static CURLcode async_rr_start(struct Curl_easy *data, int port) +static CURLcode async_rr_start(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; + struct async_thrdd_ctx *thrdd = &async->thrdd; int status; char *rrname = NULL; DEBUGASSERT(!thrdd->rr.channel); - if(port != 443) { - rrname = curl_maprintf("_%d_.https.%s", port, data->conn->host.name); + if(async->port != 443) { + rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname); if(!rrname) return CURLE_OUT_OF_MEMORY; } @@ -373,7 +227,7 @@ curlx_free(rrname); return CURLE_FAILED_INIT; } -#ifdef CURLDEBUG +#ifdef DEBUGBUILD if(getenv("CURL_DNS_SERVER")) { const char *servers = getenv("CURL_DNS_SERVER"); status = ares_set_servers_ports_csv(thrdd->rr.channel, servers); @@ -385,328 +239,82 @@ #endif memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo)); - thrdd->rr.hinfo.port = -1; thrdd->rr.hinfo.rrname = rrname; + async->queries_ongoing++; ares_query_dnsrec(thrdd->rr.channel, - rrname ? rrname : data->conn->host.name, ARES_CLASS_IN, + rrname ? rrname : async->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, - async_thrdd_rr_done, data, NULL); - CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name); + async_thrdd_rr_done, async, NULL); + CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s", + rrname ? rrname : async->hostname); return CURLE_OK; } #endif -/* - * async_thrdd_init() starts a new thread that performs the actual - * resolve. This function returns before the resolve is done. - * - * Returns FALSE in case of failure, otherwise TRUE. - */ -static bool async_thrdd_init(struct Curl_easy *data, - const char *hostname, int port, int ip_version, - const struct addrinfo *hints) +void Curl_async_thrdd_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr_ctx; + Curl_async_thrdd_destroy(data, async); +} - /* !checksrc! disable ERRNOVAR 1 */ - int err = ENOMEM; +struct async_thrdd_match_ctx { + uint32_t mid; + uint32_t resolv_id; +}; - if(thrdd->addr +static bool async_thrdd_match_item(void *qitem, void *match_data) +{ + const struct async_thrdd_match_ctx *ctx = match_data; + struct async_thrdd_item *item = qitem; + return (item->mid == ctx->mid) && (item->resolv_id == ctx->resolv_id); +} + +void Curl_async_thrdd_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->queries_ongoing && !async->done && + data->multi && data->multi->resolv_thrdq) { + /* Remove any resolve items still queued */ + struct async_thrdd_match_ctx mctx; + mctx.mid = data->mid; + mctx.resolv_id = async->id; + Curl_thrdq_clear(data->multi->resolv_thrdq, + async_thrdd_match_item, &mctx); + } #ifdef USE_HTTPSRR_ARES - || thrdd->rr.channel -#endif - ) { - CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up"); - async_thrdd_destroy(data); - DEBUGASSERT(!thrdd->addr); -#ifdef USE_HTTPSRR_ARES - DEBUGASSERT(!thrdd->rr.channel); -#endif + if(async->thrdd.rr.channel) { + ares_destroy(async->thrdd.rr.channel); + async->thrdd.rr.channel = NULL; } - - data->state.async.dns = NULL; - data->state.async.done = FALSE; - data->state.async.port = port; - data->state.async.ip_version = ip_version; - curlx_free(data->state.async.hostname); - data->state.async.hostname = curlx_strdup(hostname); - if(!data->state.async.hostname) - goto err_exit; - - addr_ctx = addr_ctx_create(data, hostname, port, hints); - if(!addr_ctx) - goto err_exit; - thrdd->addr = addr_ctx; - - /* passing addr_ctx to the thread adds a reference */ - addr_ctx->ref_count = 2; - addr_ctx->start = *Curl_pgrs_now(data); - -#ifdef HAVE_GETADDRINFO - addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx); -#else - addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx); + Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo); #endif - - if(addr_ctx->thread_hnd == curl_thread_t_null) { - /* The thread never started */ - addr_ctx->ref_count = 1; - addr_ctx->thrd_done = TRUE; - err = errno; - goto err_exit; - } - -#ifdef USE_HTTPSRR_ARES - if(async_rr_start(data, port)) - infof(data, "Failed HTTPS RR operation"); -#endif - CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port); - return TRUE; - -err_exit: - CURL_TRC_DNS(data, "resolve thread failed init: %d", err); - async_thrdd_destroy(data); - errno = err; - return FALSE; -} - -static void async_thrdd_shutdown(struct Curl_easy *data) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr; - bool done; - - if(!addr_ctx) - return; - if(addr_ctx->thread_hnd == curl_thread_t_null) - return; - - Curl_mutex_acquire(&addr_ctx->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!addr_ctx->do_abort) - Curl_multi_will_close(data, addr_ctx->sock_pair[0]); -#endif - addr_ctx->do_abort = TRUE; - done = addr_ctx->thrd_done; - Curl_mutex_release(&addr_ctx->mutx); - - /* Wait for the thread to terminate if it is already marked done. If it is - not done yet we cannot do anything here. We had tried pthread_cancel but - it caused hanging and resource leaks (#18532). */ - if(done && (addr_ctx->thread_hnd != curl_thread_t_null)) { - Curl_thread_join(&addr_ctx->thread_hnd); - CURL_TRC_DNS(data, "async_thrdd_shutdown, thread joined"); - } + async_thrdd_item_destroy(async->thrdd.res_A); + async->thrdd.res_A = NULL; + async_thrdd_item_destroy(async->thrdd.res_AAAA); + async->thrdd.res_AAAA = NULL; } /* - * 'entry' may be NULL and then no data is returned - */ -static CURLcode asyn_thrdd_await(struct Curl_easy *data, - struct async_thrdd_addr_ctx *addr_ctx, - struct Curl_dns_entry **entry) -{ - CURLcode result = CURLE_OK; - - if(addr_ctx->thread_hnd != curl_thread_t_null) { - /* not interested in result? cancel, if still running... */ - if(!entry) - async_thrdd_shutdown(data); - - if(addr_ctx->thread_hnd != curl_thread_t_null) { - CURL_TRC_DNS(data, "resolve, wait for thread to finish"); - if(!Curl_thread_join(&addr_ctx->thread_hnd)) { - DEBUGASSERT(0); - } - } - - if(entry) - result = Curl_async_is_resolved(data, entry); - } - - data->state.async.done = TRUE; - if(entry) - *entry = data->state.async.dns; - - return result; -} - -/* - * Until we gain a way to signal the resolver threads to stop early, we must - * simply wait for them and ignore their results. - */ -void Curl_async_thrdd_shutdown(struct Curl_easy *data) -{ - async_thrdd_shutdown(data); -} - -void Curl_async_thrdd_destroy(struct Curl_easy *data) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - - if(thrdd->addr && !data->set.quick_exit) { - (void)asyn_thrdd_await(data, thrdd->addr, NULL); - } - async_thrdd_destroy(data); -} - -/* - * Curl_async_await() - * * Waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". - * - * If 'entry' is non-NULL, make it point to the resolved dns entry - * - * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, - * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. - * - * This is the version for resolves-in-a-thread. */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **entry) +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - if(thrdd->addr) - return asyn_thrdd_await(data, thrdd->addr, entry); - return CURLE_FAILED_INIT; -} + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; + timediff_t milli, ms; -/* - * Curl_async_is_resolved() is called repeatedly to check if a previous - * name resolve request has completed. It should also make sure to time-out if - * the operation seems to take too long. - */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - bool done = FALSE; - - DEBUGASSERT(dns); - *dns = NULL; - - if(data->state.async.done) { - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound", - *dns ? "" : "not "); - return CURLE_OK; - } - -#ifdef USE_HTTPSRR_ARES - /* best effort, ignore errors */ - if(thrdd->rr.channel) - (void)Curl_ares_perform(thrdd->rr.channel, 0); -#endif - - DEBUGASSERT(thrdd->addr); - if(!thrdd->addr) + if(!thrdd) return CURLE_FAILED_INIT; - Curl_mutex_acquire(&thrdd->addr->mutx); - done = thrdd->addr->thrd_done; - Curl_mutex_release(&thrdd->addr->mutx); + while(async->queries_ongoing && !async->done) { + Curl_async_thrdd_multi_process(data->multi); + if(async->done) + break; - if(done) { - CURLcode result = CURLE_OK; - - data->state.async.done = TRUE; - Curl_resolv_unlink(data, &data->state.async.dns); - Curl_expire_done(data, EXPIRE_ASYNC_NAME); - - if(thrdd->addr->res) { - data->state.async.dns = - Curl_dnscache_mk_entry(data, thrdd->addr->res, - data->state.async.hostname, 0, - data->state.async.port, FALSE); - thrdd->addr->res = NULL; - if(!data->state.async.dns) - result = CURLE_OUT_OF_MEMORY; - -#ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - result = thrdd->rr.result; - if(!result) { - struct Curl_https_rrinfo *lhrr; - lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); - if(!lhrr) - result = CURLE_OUT_OF_MEMORY; - else - data->state.async.dns->hinfo = lhrr; - } - } -#endif - if(!result && data->state.async.dns) - result = Curl_dnscache_add(data, data->state.async.dns); - } - - if(!result && !data->state.async.dns) - result = Curl_resolver_error(data, NULL); - if(result) - Curl_resolv_unlink(data, &data->state.async.dns); - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound", - result, *dns ? "" : "not "); - async_thrdd_shutdown(data); - return result; - } - else { - /* poll for name lookup done with exponential backoff up to 250ms */ - /* should be fine even if this converts to 32-bit */ - timediff_t elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle); - if(elapsed < 0) - elapsed = 0; - - if(thrdd->addr->poll_interval == 0) - /* Start at 1ms poll interval */ - thrdd->addr->poll_interval = 1; - else if(elapsed >= thrdd->addr->interval_end) - /* Back-off exponentially if last interval expired */ - thrdd->addr->poll_interval *= 2; - - if(thrdd->addr->poll_interval > 250) - thrdd->addr->poll_interval = 250; - - thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval; - Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME); - return CURLE_OK; - } -} - -CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - CURLcode result = CURLE_OK; - bool thrd_done; - -#if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR) - (void)ps; -#endif - -#ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - result = Curl_ares_pollset(data, thrdd->rr.channel, ps); - if(result) - return result; - } -#endif - if(!thrdd->addr) - return result; - - Curl_mutex_acquire(&thrdd->addr->mutx); - thrd_done = thrdd->addr->thrd_done; - Curl_mutex_release(&thrdd->addr->mutx); - - if(!thrd_done) { -#ifndef CURL_DISABLE_SOCKETPAIR - /* return read fd to client for polling the DNS resolution status */ - result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]); -#else - timediff_t milli; - timediff_t ms = - curlx_ptimediff_ms(Curl_pgrs_now(data), &thrdd->addr->start); + ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); if(ms < 3) milli = 0; else if(ms <= 50) @@ -715,68 +323,506 @@ milli = 50; else milli = 200; - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); + CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli); + curlx_wait_ms(milli); + } + return Curl_async_take_result(data, async, pdns); +} + +#ifdef HAVE_GETADDRINFO + +/* Process the item, using Curl_getaddrinfo_ex() */ +static void async_thrdd_item_process(void *arg) +{ + struct async_thrdd_item *item = arg; + struct addrinfo hints; + char service[12]; + int pf = PF_INET; + int rc; + +#ifdef DEBUGBUILD + if(item->delay_ms) { + curlx_wait_ms(item->delay_ms); + } + if(item->delay_fail_ms) { + curlx_wait_ms(item->delay_fail_ms); + return; + } #endif + + memset(&hints, 0, sizeof(hints)); +#ifdef CURLRES_IPV6 + if(item->dns_queries & CURL_DNSQ_AAAA) { + pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6; + } +#endif + hints.ai_family = pf; + hints.ai_socktype = Curl_socktype_for_transport(item->transport); + hints.ai_protocol = Curl_protocol_for_transport(item->transport); +#ifdef __APPLE__ + /* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED + * when doing AAAA queries. We do not want this "help". */ + hints.ai_flags = AI_ADDRCONFIG; +#endif + + curl_msnprintf(service, sizeof(service), "%u", item->port); +#ifdef AI_NUMERICSERV + hints.ai_flags |= AI_NUMERICSERV; +#endif + + rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res); + if(rc) { + item->sock_error = SOCKERRNO ? SOCKERRNO : rc; + if(item->sock_error == 0) + item->sock_error = RESOLVER_ENOMEM; + } + else { + Curl_addrinfo_set_port(item->res, item->port); + } +} + +#else /* HAVE_GETADDRINFO */ + +/* Process the item, using Curl_ipv4_resolve_r() */ +static void async_thrdd_item_process(void *arg) +{ + struct async_thrdd_item *item = arg; + +#ifdef DEBUGBUILD + if(item->delay_ms) { + curlx_wait_ms(item->delay_ms); + } + if(item->delay_fail_ms) { + curlx_wait_ms(item->delay_fail_ms); + return; + } +#endif + item->res = Curl_ipv4_resolve_r(item->hostname, item->port); + if(!item->res) { + item->sock_error = SOCKERRNO; + if(item->sock_error == 0) + item->sock_error = RESOLVER_ENOMEM; + } +} + +#endif /* HAVE_GETADDRINFO */ + +#ifdef ENABLE_WAKEUP +static void async_thrdd_event(const struct curl_thrdq *tqueue, + Curl_thrdq_event ev, + void *user_data) +{ + struct Curl_multi *multi = user_data; + (void)tqueue; + switch(ev) { + case CURL_THRDQ_EV_ITEM_DONE: + (void)curl_multi_wakeup(multi); + break; + default: + break; + } +} +#else +#define async_thrdd_event NULL +#endif + +static void async_thrdd_item_free(void *item) +{ + async_thrdd_item_destroy(item); +} + +/* Create a thread queue for processing resolv items */ +CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + CURLcode result; + DEBUGASSERT(!multi->resolv_thrdq); + result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0, + min_threads, max_threads, idle_time_ms, + async_thrdd_item_free, + async_thrdd_item_process, + async_thrdd_event, + multi); +#ifdef DEBUGBUILD + if(!result) { + const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + result = Curl_async_thrdd_multi_set_props( + multi, min_threads, (uint32_t)l, idle_time_ms); + } + } + } +#endif + return result; +} + +/* Tear down the thread queue, joining active threads or detaching them */ +void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join) +{ + if(multi->resolv_thrdq) { +#ifdef CURLVERBOSE + CURL_TRC_DNS(multi->admin, "destroy thread queue+pool, join=%d", join); + Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); +#endif + Curl_thrdq_destroy(multi->resolv_thrdq, join); + multi->resolv_thrdq = NULL; + } +} + +#ifdef CURLVERBOSE +static void async_thrdd_report_item(struct Curl_easy *data, + struct async_thrdd_item *item) +{ + char buf[MAX_IPADR_LEN]; + struct dynbuf tmp; + const char *sep = ""; + const struct Curl_addrinfo *ai = item->res; + int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; + CURLcode result; + + if(!CURL_TRC_DNS_is_verbose(data)) + return; + + curlx_dyn_init(&tmp, 1024); + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + Curl_printable_address(ai, buf, sizeof(buf)); + result = curlx_dyn_addf(&tmp, "%s%s", sep, buf); + if(result) { + CURL_TRC_DNS(data, "too many IP, cannot show"); + goto out; + } + sep = ", "; + } + } + + CURL_TRC_DNS(data, "Host %s:%u resolved IPv%c: %s", + item->hostname, item->port, + (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4', + (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)")); +out: + curlx_dyn_free(&tmp); +} +#endif /* CURLVERBOSE */ + +/* Process the receiving end of the thread queue, dispatching + * processed items to their transfer when it can still be found + * and has an `async` state present. Otherwise, destroy the item. */ +void Curl_async_thrdd_multi_process(struct Curl_multi *multi) +{ + struct Curl_easy *data; + void *qitem; + + while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) { + /* dispatch resolve result */ + struct async_thrdd_item *item = qitem; + struct Curl_resolv_async *async = NULL; + + data = Curl_multi_get_easy(multi, item->mid); + if(data) + async = Curl_async_get(data, item->resolv_id); + if(async) { + struct async_thrdd_item **pdest = &async->thrdd.res_A; + + async->dns_responses |= item->dns_queries; + --async->queries_ongoing; + async->done = !async->queries_ongoing; + +#ifdef CURLRES_IPV6 + if(item->dns_queries & CURL_DNSQ_AAAA) + pdest = &async->thrdd.res_AAAA; +#endif + if(!*pdest) { + VERBOSE(async_thrdd_report_item(data, item)); + *pdest = item; + item = NULL; + } + else + DEBUGASSERT(0); /* should not receive duplicates here */ + Curl_multi_mark_dirty(data); + } + async_thrdd_item_free(item); + } +#ifdef CURLVERBOSE + Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); +#endif +} + +CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + return Curl_thrdq_set_props(multi->resolv_thrdq, 0, + min_threads, max_threads, idle_time_ms); +} + +static CURLcode async_thrdd_query(struct Curl_easy *data, + struct Curl_resolv_async *async, + uint8_t dns_queries) +{ + struct async_thrdd_item *item; + CURLcode result; + + item = async_thrdd_item_create(data, async->id, dns_queries, + async->hostname, async->port, + async->transport); + if(!item) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + CURL_TRC_DNS(data, "queueing query %s", item->description); + result = Curl_thrdq_send(data->multi->resolv_thrdq, item, + async_item_description(item), async->timeout_ms); + if(result) + goto out; + item = NULL; + async->queries_ongoing++; + +out: + if(item) + async_thrdd_item_free(item); + return result; +} + +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + CURLcode result = CURLE_FAILED_INIT; + void *resolver = NULL; + + if(async->queries_ongoing || async->done) + return CURLE_FAILED_INIT; + +#ifdef USE_HTTPSRR_ARES + DEBUGASSERT(!async->thrdd.rr.channel); + if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) { + result = async_rr_start(data, async); + if(result) + goto out; + resolver = async->thrdd.rr.channel; + } +#endif + + result = Curl_resolv_announce_start(data, resolver); + if(result) + return result; + +#ifdef CURLRES_IPV6 + /* Do not start an AAAA query for an ipv4 address when + * we will start an A query for it. */ + if((async->dns_queries & CURL_DNSQ_AAAA) && + !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) { + result = async_thrdd_query(data, async, CURL_DNSQ_AAAA); + if(result) + goto out; + } +#endif + if(async->dns_queries & CURL_DNSQ_A) { + result = async_thrdd_query(data, async, CURL_DNSQ_A); + if(result) + goto out; + } + +#ifdef CURLVERBOSE + Curl_thrdq_trace(data->multi->resolv_thrdq, data); +#endif + +out: + if(result) + CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", + async->hostname, async->port, result); + return result; +} + +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps) +{ + timediff_t timeout_ms; + + timeout_ms = Curl_async_timeleft_ms(data, async); +#ifdef USE_HTTPSRR_ARES + if(async->thrdd.rr.channel) { + CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps); + if(result) + return result; + timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel); + } +#else + (void)ps; +#endif + + if(!async->done) { +#ifndef ENABLE_WAKEUP + timediff_t stutter_ms, elapsed_ms; + elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + if(elapsed_ms < 3) + stutter_ms = 1; + else if(elapsed_ms <= 50) + stutter_ms = elapsed_ms / 3; + else if(elapsed_ms <= 250) + stutter_ms = 50; + else + stutter_ms = 200; + timeout_ms = CURLMIN(stutter_ms, timeout_ms); +#endif + Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME); + } + return CURLE_OK; +} + +/* + * Curl_async_take_result() is called repeatedly to check if a previous + * name resolve request has completed. It should also make sure to time-out if + * the operation seems to take too long. + */ +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) +{ + struct async_thrdd_ctx *thrdd = &async->thrdd; + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; + + DEBUGASSERT(pdns); + *pdns = NULL; + if(!async->queries_ongoing && !async->done) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + +#ifdef USE_HTTPSRR_ARES + /* best effort, ignore errors */ + if(thrdd->rr.channel) + (void)Curl_ares_perform(thrdd->rr.channel, 0); +#endif + + if(!async->done) + return CURLE_AGAIN; + + Curl_expire_done(data, EXPIRE_ASYNC_NAME); + if(async->result) { + result = async->result; + goto out; + } + + if((thrdd->res_A && thrdd->res_A->res) || + (thrdd->res_AAAA && thrdd->res_AAAA->res)) { + dns = Curl_dnscache_mk_entry2( + data, async->dns_queries, + thrdd->res_A ? &thrdd->res_A->res : NULL, + thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + +#ifdef USE_HTTPSRR_ARES + if(thrdd->rr.channel) { + struct Curl_https_rrinfo *lhrr = NULL; + if(thrdd->rr.hinfo.complete) { + lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); + if(!lhrr) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + Curl_httpsrr_trace(data, lhrr); + Curl_dns_entry_set_https_rr(dns, lhrr); + } +#endif + } + + if(dns) { + *pdns = dns; + dns = NULL; + } +#ifdef CURLVERBOSE + Curl_thrdq_trace(data->multi->resolv_thrdq, data); +#endif + +out: + Curl_dns_entry_unlink(data, &dns); + Curl_async_thrdd_shutdown(data, async); + if(!result && !*pdns) + result = Curl_async_failed(data, async, NULL); + if(result && + (result != CURLE_COULDNT_RESOLVE_HOST) && + (result != CURLE_COULDNT_RESOLVE_PROXY)) { + CURL_TRC_DNS(data, "Error %d resolving %s:%d", + result, async->hostname, async->port); } return result; } -#ifndef HAVE_GETADDRINFO -/* - * Curl_async_getaddrinfo() - for platforms without getaddrinfo - */ -CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, - int port, int ip_version) +static const struct Curl_addrinfo *async_thrdd_get_ai( + const struct Curl_addrinfo *ai, + int ai_family, unsigned int index) { - (void)ip_version; - - /* fire up a new resolver thread! */ - if(async_thrdd_init(data, hostname, port, ip_version, NULL)) { - return CURLE_OK; + unsigned int i = 0; + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } } - - failf(data, "getaddrinfo() thread failed"); - return CURLE_FAILED_INIT; + return NULL; } -#else /* !HAVE_GETADDRINFO */ - -/* - * Curl_async_getaddrinfo() - for getaddrinfo - */ -CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, - int port, int ip_version) +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index) { - struct addrinfo hints; - int pf = PF_INET; + struct async_thrdd_ctx *thrdd = &async->thrdd; - CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port); -#ifdef CURLRES_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { - /* The stack seems to be IPv6-enabled */ - if(ip_version == CURL_IPRESOLVE_V6) - pf = PF_INET6; - else - pf = PF_UNSPEC; + (void)data; + switch(ai_family) { + case AF_INET: + if(thrdd->res_A) + return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index); + break; + case AF_INET6: + if(thrdd->res_AAAA) + return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index); + break; + default: + break; } + return NULL; +} + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async) +{ +#ifdef USE_HTTPSRR_ARES + if(Curl_async_knows_https(data, async)) + return &async->thrdd.rr.hinfo; #else - (void)ip_version; -#endif /* CURLRES_IPV6 */ - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? - SOCK_STREAM : SOCK_DGRAM; - - /* fire up a new resolver thread! */ - if(async_thrdd_init(data, hostname, port, ip_version, &hints)) - return CURLE_OK; - - failf(data, "getaddrinfo() thread failed to start"); - return CURLE_FAILED_INIT; + (void)data; + (void)async; +#endif + return NULL; } -#endif /* !HAVE_GETADDRINFO */ +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->dns_queries & CURL_DNSQ_HTTPS) + return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done); + return TRUE; /* we know it will never come */ +} -#endif /* CURLRES_THREADED */ +#endif /* USE_HTTPSRR */ + +#endif /* USE_RESOLV_THREADED */
diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h index b961081..ed50933 100644 --- a/Utilities/cmcurl/lib/asyn.h +++ b/Utilities/cmcurl/lib/asyn.h
@@ -25,21 +25,26 @@ ***************************************************************************/ #include "curl_setup.h" +#if defined(USE_HTTPSRR) && defined(USE_ARES) +#include "httpsrr.h" +#endif + struct Curl_easy; struct Curl_dns_entry; +struct Curl_resolv_async; +struct Curl_multi; +struct easy_pollset; #ifdef CURLRES_ASYNCH #include "curl_addrinfo.h" -#include "httpsrr.h" -struct addrinfo; struct hostent; struct connectdata; struct easy_pollset; -#if defined(CURLRES_ARES) && defined(CURLRES_THREADED) -#error cannot have both CURLRES_ARES and CURLRES_THREADED defined +#if defined(USE_RESOLV_ARES) && defined(USE_RESOLV_THREADED) +#error cannot have both USE_RESOLV_ARES and USE_RESOLV_THREADED defined #endif /* @@ -64,49 +69,6 @@ void Curl_async_global_cleanup(void); /* - * Curl_async_get_impl() - * Get the resolver implementation instance (c-ares channel) or NULL - * for passing to application callback. - */ -CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl); - -/* Curl_async_pollset() - * - * This function is called from the Curl_multi_pollset() function. 'sock' is a - * pointer to an array to hold the file descriptors, with 'numsock' being the - * size of that array (in number of entries). This function is supposed to - * return bitmask indicating what file descriptors (referring to array indexes - * in the 'sock' array) to wait for, read/write. - */ -CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps); - -/* - * Curl_async_is_resolved() - * - * Called repeatedly to check if a previous name resolve request has - * completed. It should also make sure to time-out if the operation seems to - * take too long. - * - * Returns normal CURLcode errors. - */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns); - -/* - * Curl_async_await() - * - * Waits for a resolve to finish. This function should be avoided since using - * this risk getting the multi interface to "hang". - * - * On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise. - * - * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, - * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. - */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **dnsentry); - -/* * Curl_async_getaddrinfo() - when using this resolver * * Returns name information about the given hostname and port number. If @@ -117,8 +79,21 @@ * Each resolver backend must of course make sure to return data in the * correct format to comply with this. */ -CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, - int port, int ip_version); +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async); + +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index); + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async); +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async); +#endif /* USE_HTTPSRR */ #ifdef USE_ARES /* common functions for c-ares and threaded resolver with HTTPSRR */ @@ -128,105 +103,119 @@ ares_channel channel, struct easy_pollset *ps); +timediff_t Curl_ares_timeout_ms(struct Curl_easy *data, + struct Curl_resolv_async *async, + ares_channel channel); + int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms); #endif -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES /* async resolving implementation using c-ares alone */ struct async_ares_ctx { ares_channel channel; - int num_pending; /* number of outstanding c-ares requests */ - struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares - parts */ + struct Curl_addrinfo *res_A; + struct Curl_addrinfo *res_AAAA; int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */ CURLcode result; /* CURLE_OK or error handling response */ -#ifndef HAVE_CARES_GETADDRINFO struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ -#endif #ifdef USE_HTTPSRR struct Curl_https_rrinfo hinfo; #endif }; -void Curl_async_ares_shutdown(struct Curl_easy *data); -void Curl_async_ares_destroy(struct Curl_easy *data); +void Curl_async_ares_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); +void Curl_async_ares_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); -/* Set the DNS server to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data); +#endif /* USE_RESOLV_ARES */ -/* Set the DNS interfacer to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data); +#ifdef USE_RESOLV_THREADED -/* Set the local ipv4 address to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data); - -/* Set the local ipv6 address to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data); - -#endif /* CURLRES_ARES */ - -#ifdef CURLRES_THREADED -/* async resolving implementation using POSIX threads */ -#include "curl_threads.h" - -/* Context for threaded address resolver */ -struct async_thrdd_addr_ctx { - curl_thread_t thread_hnd; - char *hostname; /* hostname to resolve, Curl_async.hostname - duplicate */ - curl_mutex_t mutx; -#ifndef CURL_DISABLE_SOCKETPAIR - curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */ -#endif - struct Curl_addrinfo *res; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints; -#endif - struct curltime start; - timediff_t interval_end; - unsigned int poll_interval; - int port; - int sock_error; - int ref_count; - BIT(thrd_done); - BIT(do_abort); -}; +struct async_thrdd_item; /* Context for threaded resolver */ struct async_thrdd_ctx { - /* `addr` is a pointer since this memory is shared with a started - * thread. Since threads cannot be killed, we use reference counting - * so that we can "release" our pointer to this memory while the - * thread is still running. */ - struct async_thrdd_addr_ctx *addr; + struct async_thrdd_item *res_A; /* ipv4 result */ + struct async_thrdd_item *res_AAAA; /* ipv6 result */ #if defined(USE_HTTPSRR) && defined(USE_ARES) struct { ares_channel channel; struct Curl_https_rrinfo hinfo; - CURLcode result; - BIT(done); } rr; #endif }; -void Curl_async_thrdd_shutdown(struct Curl_easy *data); -void Curl_async_thrdd_destroy(struct Curl_easy *data); +void Curl_async_thrdd_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); +void Curl_async_thrdd_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); -#endif /* CURLRES_THREADED */ +CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); +void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join); +void Curl_async_thrdd_multi_process(struct Curl_multi *multi); + +CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#endif /* USE_RESOLV_THREADED */ #ifndef CURL_DISABLE_DOH struct doh_probes; #endif +/* + * Curl_async_await() + * + * Waits for a resolve to finish. This function should be avoided since using + * this risk getting the multi interface to "hang". + * + * On return 'dns' is assigned the resolved dns (CURLE_OK or NULL otherwise. + * + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. + */ +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns); + +/* + * Take the result of an async resolve operation. + * Returns CURLE_OK with `*pdns` != NULL, CURLE_AGAIN while still + * ongoing or an error code for a failed resolve. + */ +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns); + +/* Curl_async_pollset() + * + * This function is called from the Curl_multi_pollset() function. 'sock' is a + * pointer to an array to hold the file descriptors, with 'numsock' being the + * size of that array (in number of entries). This function is supposed to + * return bitmask indicating what file descriptors (referring to array indexes + * in the 'sock' array) to wait for, read/write. + */ +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps); + #else /* CURLRES_ASYNCH */ /* convert these functions if an asynch resolver is not used */ -#define Curl_async_get_impl(x, y) (*(y) = NULL, CURLE_OK) -#define Curl_async_is_resolved(x, y) CURLE_COULDNT_RESOLVE_HOST -#define Curl_async_await(x, y) CURLE_COULDNT_RESOLVE_HOST -#define Curl_async_global_init() CURLE_OK -#define Curl_async_global_cleanup() Curl_nop_stmt - +#define Curl_async_global_init() CURLE_OK +#define Curl_async_global_cleanup() Curl_nop_stmt +#define Curl_async_get_ai(a, b, c, d) NULL +#define Curl_async_await(a, b, c) CURLE_COULDNT_RESOLVE_HOST +#define Curl_async_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST +#define Curl_async_pollset(x, y, z) CURLE_OK +#define Curl_async_get_https(x, y) NULL +#define Curl_async_knows_https(x, y) TRUE #endif /* !CURLRES_ASYNCH */ #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) @@ -234,38 +223,54 @@ #endif #ifdef USE_CURL_ASYNC -struct Curl_async { -#ifdef CURLRES_ARES + +struct Curl_resolv_async { + struct Curl_resolv_async *next; +#ifdef USE_RESOLV_ARES struct async_ares_ctx ares; -#elif defined(CURLRES_THREADED) +#elif defined(USE_RESOLV_THREADED) struct async_thrdd_ctx thrdd; #endif #ifndef CURL_DISABLE_DOH struct doh_probes *doh; /* DoH specific data for this request */ #endif - struct Curl_dns_entry *dns; /* result of resolving on success */ - char *hostname; /* copy of the params resolv started with */ - int port; - int ip_version; + struct curltime start; + timediff_t interval_end; + timediff_t timeout_ms; + CURLcode result; + uint32_t poll_interval; + uint32_t id; /* unique id per easy handle of the resolve operation */ + /* what is being resolved */ + uint16_t port; + uint8_t dns_queries; /* what queries are being performed */ + uint8_t dns_responses; /* what queries had responses so far. */ + uint8_t transport; + uint8_t queries_ongoing; + BIT(is_ipaddr); + BIT(is_ipv4addr); + BIT(for_proxy); BIT(done); + BIT(shutdown); + char hostname[1]; }; -/* - * Curl_async_shutdown(). - * - * This shuts down all ongoing operations. - */ -void Curl_async_shutdown(struct Curl_easy *data); +timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, + struct Curl_resolv_async *async); -/* - * Curl_async_destroy(). - * - * This frees the resources of any async resolve. - */ -void Curl_async_destroy(struct Curl_easy *data); +/* Shut down the given async resolve. */ +void Curl_async_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); + +/* Frees the resources of the given async resolve and the struct itself. */ +void Curl_async_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); + +CURLcode Curl_async_failed(struct Curl_easy *data, + struct Curl_resolv_async *async, + const char *detail); + #else /* !USE_CURL_ASYNC */ -#define Curl_async_shutdown(x) Curl_nop_stmt -#define Curl_async_destroy(x) Curl_nop_stmt +#define Curl_async_shutdown(x, y) Curl_nop_stmt #endif /* USE_CURL_ASYNC */ /********** end of generic resolver interface functions *****************/
diff --git a/Utilities/cmcurl/lib/bufq.h b/Utilities/cmcurl/lib/bufq.h index bcd7df2..da411b5 100644 --- a/Utilities/cmcurl/lib/bufq.h +++ b/Utilities/cmcurl/lib/bufq.h
@@ -44,9 +44,8 @@ /** * A pool for providing/keeping a number of chunks of the same size * - * The same pool can be shared by many `bufq` instances. However, a pool - * is not thread safe. All bufqs using it are supposed to operate in the - * same thread. + * The same pool can be shared by many `bufq` instances. A pool is not thread + * safe. All bufqs using it are supposed to operate in the same thread. */ struct bufc_pool { struct buf_chunk *spare; /* list of available spare chunks */ @@ -77,10 +76,10 @@ * * By default, writing to a full bufq will return (-1, CURLE_AGAIN). Same * as reading from an empty bufq. - * With `BUFQ_OPT_SOFT_LIMIT` set, a bufq will allow writing becond this - * limit and use more than `max_chunks`. However it will report that it - * is full nevertheless. This is provided for situation where writes - * preferably never fail (except for memory exhaustion). + * With `BUFQ_OPT_SOFT_LIMIT` set, a bufq will allow writing beyond this limit + * and use more than `max_chunks`. It will report that it is full + * nevertheless. This is provided for situation where writes preferably never + * fail (except for memory exhaustion). * * By default and without a pool, a bufq will keep chunks that read * empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will @@ -104,7 +103,7 @@ * Default behaviour: chunk limit is "hard", meaning attempts to write * more bytes than can be hold in `max_chunks` is refused and will return * -1, CURLE_AGAIN. */ -#define BUFQ_OPT_NONE (0) +#define BUFQ_OPT_NONE 0 /** * Make `max_chunks` a "soft" limit. A bufq will report that it is "full" * when `max_chunks` are used, but allows writing beyond this limit. @@ -133,8 +132,8 @@ size_t max_chunks, int opts); /** - * Reset the buffer queue to be empty. Will keep any allocated buffer - * chunks around. + * Reset the buffer queue to be empty. Keep any allocated buffer chunks + * around. */ void Curl_bufq_reset(struct bufq *q); @@ -197,9 +196,8 @@ const uint8_t **pbuf, size_t *plen); /** - * Tell the buffer queue to discard `amount` buf bytes at the head - * of the queue. Skipping more buf than is currently buffered will - * just empty the queue. + * Tell the buffer queue to discard `amount` buf bytes at the head of the + * queue. Skipping more buf than is currently buffered will empty the queue. */ void Curl_bufq_skip(struct bufq *q, size_t amount); @@ -245,7 +243,7 @@ /** * Write buf to the end of the buffer queue. - * Will write bufq content or passed `buf` directly using the `writer` + * Write bufq content or passed `buf` directly using the `writer` * callback when it sees fit. 'buf' might get passed directly * on or is placed into the buffer, depending on `len` and current * amount buffered, chunk size, etc.
diff --git a/Utilities/cmcurl/lib/bufref.c b/Utilities/cmcurl/lib/bufref.c index b1272d5..50fafda 100644 --- a/Utilities/cmcurl/lib/bufref.c +++ b/Utilities/cmcurl/lib/bufref.c
@@ -25,7 +25,7 @@ #include "urldata.h" #include "bufref.h" -#include "strdup.h" +#include "curlx/strdup.h" #ifdef DEBUGBUILD #define SIGNATURE 0x5c48e9b2 /* Random pattern. */ @@ -128,7 +128,7 @@ DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH); if(ptr) { - cpy = Curl_memdup0(ptr, len); + cpy = curlx_memdup0(ptr, len); if(!cpy) return CURLE_OUT_OF_MEMORY; }
diff --git a/Utilities/cmcurl/lib/cf-dns.c b/Utilities/cmcurl/lib/cf-dns.c new file mode 100644 index 0000000..e763b8e --- /dev/null +++ b/Utilities/cmcurl/lib/cf-dns.c
@@ -0,0 +1,667 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "urldata.h" +#include "curl_addrinfo.h" +#include "cfilters.h" +#include "connect.h" +#include "dnscache.h" +#include "httpsrr.h" +#include "curl_trc.h" +#include "progress.h" +#include "url.h" +#include "cf-dns.h" + + +struct cf_dns_ctx { + struct Curl_dns_entry *dns; + CURLcode resolv_result; + uint32_t resolv_id; + uint16_t port; + uint8_t dns_queries; + uint8_t transport; + BIT(started); + BIT(announced); + BIT(abstract_unix_socket); + BIT(complete_resolve); + BIT(for_proxy); + char hostname[1]; +}; + +static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, uint8_t transport, + bool abstract_unix_socket, + bool for_proxy, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct cf_dns_ctx *ctx; + size_t hlen = strlen(hostname); + + ctx = curlx_calloc(1, sizeof(*ctx) + hlen); + if(!ctx) + return NULL; + + ctx->port = port; + ctx->dns_queries = dns_queries; + ctx->transport = transport; + ctx->abstract_unix_socket = abstract_unix_socket; + ctx->for_proxy = for_proxy; + ctx->complete_resolve = complete_resolve; + ctx->dns = Curl_dns_entry_link(data, dns); + ctx->started = !!ctx->dns; + if(hlen) + memcpy(ctx->hostname, hostname, hlen); + + CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", + ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries); + return ctx; +} + +static void cf_dns_ctx_destroy(struct Curl_easy *data, + struct cf_dns_ctx *ctx) +{ + if(ctx) { + Curl_dns_entry_unlink(data, &ctx->dns); + curlx_free(ctx); + } +} + +#ifdef CURLVERBOSE +static void cf_dns_report_addr(struct Curl_easy *data, + struct dynbuf *tmp, + const char *label, + int ai_family, + const struct Curl_addrinfo *ai) +{ + char buf[MAX_IPADR_LEN]; + const char *sep = ""; + CURLcode result; + + curlx_dyn_reset(tmp); + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + Curl_printable_address(ai, buf, sizeof(buf)); + result = curlx_dyn_addf(tmp, "%s%s", sep, buf); + if(result) { + infof(data, "too many IP, cannot show"); + return; + } + sep = ", "; + } + } + + infof(data, "%s%s", label, + (curlx_dyn_len(tmp) ? curlx_dyn_ptr(tmp) : "(none)")); +} + +static void cf_dns_report(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + struct cf_dns_ctx *ctx = cf->ctx; + struct dynbuf tmp; + + if(!Curl_trc_is_verbose(data) || + /* ignore no name or numerical IP addresses */ + !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) + return; + + switch(ctx->transport) { + case TRNSPRT_UNIX: +#ifdef USE_UNIX_SOCKETS + CURL_TRC_CF(data, cf, "resolved unix domain %s", + Curl_conn_get_unix_path(data->conn)); +#else + DEBUGASSERT(0); +#endif + break; + default: + curlx_dyn_init(&tmp, 1024); + infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); +#ifdef CURLRES_IPV6 + cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr); +#endif + cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr); +#ifdef USE_HTTPSRR + if(!dns->hinfo) + infof(data, "HTTPS-RR: -"); + else if(!Curl_httpsrr_applicable(data, dns->hinfo)) + infof(data, "HTTPS-RR: not applicable"); + else { + CURLcode result = Curl_httpsrr_print(&tmp, dns->hinfo); + if(!result) + infof(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); + else + infof(data, "Error printing HTTPS-RR information"); + } +#endif + curlx_dyn_free(&tmp); + break; + } +} +#else +#define cf_dns_report(x, y, z) Curl_nop_stmt +#endif + +/************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ +static CURLcode cf_dns_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_dns_entry **pdns) +{ + struct cf_dns_ctx *ctx = cf->ctx; + timediff_t timeout_ms = Curl_timeleft_ms(data); + CURLcode result; + + *pdns = NULL; + +#ifdef USE_UNIX_SOCKETS + if(ctx->transport == TRNSPRT_UNIX) { + CURL_TRC_CF(data, cf, "resolve unix socket %s", ctx->hostname); + return Curl_resolv_unix(data, ctx->hostname, + (bool)cf->conn->bits.abstract_unix_socket, pdns); + } +#endif + + /* Resolve target host right on */ + CURL_TRC_CF(data, cf, "cf_dns_start host %s:%u", ctx->hostname, ctx->port); + if(Curl_is_ipv4addr(ctx->hostname)) + ctx->dns_queries |= CURL_DNSQ_A; +#ifdef USE_IPV6 + else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */ + ctx->dns_queries |= CURL_DNSQ_AAAA; +#endif + result = Curl_resolv(data, ctx->dns_queries, + ctx->hostname, ctx->port, ctx->transport, + (bool)ctx->for_proxy, timeout_ms, + &ctx->resolv_id, pdns); + DEBUGASSERT(!result || !*pdns); + if(!result) { /* resolved right away, either sync or from dnscache */ + DEBUGASSERT(*pdns); + return CURLE_OK; + } + else if(result == CURLE_AGAIN) { /* async resolv in progress */ + return CURLE_OK; + } + else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ + failf(data, "Failed to resolve '%s' with timeout after %" + FMT_TIMEDIFF_T " ms", ctx->hostname, + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle)); + return CURLE_OPERATION_TIMEDOUT; + } + else { + DEBUGASSERT(result); + failf(data, "Could not resolve: %s", ctx->hostname); + return result; + } +} + +#define CURL_HEV3_RESOLVE_DELAY_MS 50 + +static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_dns_ctx *ctx = cf->ctx; + + if(ctx->resolv_result) + return TRUE; + else if(ctx->dns) + return TRUE; +#ifdef USE_CURL_ASYNC + else { + /* We want AAAA answer as we prefer ipv6. If a sub-filter desires + * HTTPS-RR, we check for that query as well. */ + uint8_t wanted_answers = CURL_DNSQ_AAAA; + if(Curl_conn_cf_wants_httpsrr(cf, data)) + wanted_answers |= CURL_DNSQ_HTTPS; + + /* Note: if a query was never started, it is considered to have + * an answer (e.g. a negative one). */ + if(Curl_resolv_has_answers(data, ctx->resolv_id, wanted_answers)) + return TRUE; + /* If the wanted answers are not available after a delay, + * we let the connect attempts start anyway. */ + return Curl_resolv_elapsed_ms(data, ctx->resolv_id) >= + CURL_HEV3_RESOLVE_DELAY_MS; + } +#else + (void)data; + DEBUGASSERT(0); /* We should not come here */ + return FALSE; +#endif /* USE_CURL_ASYNC */ +} + +static CURLcode cf_dns_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_dns_ctx *ctx = cf->ctx; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + if(!ctx->started) { + ctx->started = TRUE; + ctx->resolv_result = cf_dns_start(cf, data, &ctx->dns); + } + + if(!ctx->dns && !ctx->resolv_result) { + ctx->resolv_result = + Curl_resolv_take_result(data, ctx->resolv_id, &ctx->dns); + } + + if(ctx->resolv_result) { + CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result); + return ctx->resolv_result; + } + + if(ctx->dns && !ctx->announced) { + ctx->announced = TRUE; + if(cf->sockindex == FIRSTSOCKET) { + cf->conn->bits.dns_resolved = TRUE; + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + } + cf_dns_report(cf, data, ctx->dns); + } + + if(!cf_dns_ready_to_connect(cf, data)) { + return CURLE_OK; + } + + if(cf->next && !cf->next->connected) { + bool sub_done; + CURLcode result = Curl_conn_cf_connect(cf->next, data, &sub_done); + if(result || !sub_done) + return result; + DEBUGASSERT(sub_done); + } + + /* sub filter chain is connected */ + CURL_TRC_CF(data, cf, "connected filter chain below"); + if(ctx->complete_resolve && !ctx->dns && !ctx->resolv_result) { + /* This filter only connects when it has resolved everything. */ + CURL_TRC_CF(data, cf, "delay connect until resolve complete"); + return CURLE_OK; + } + *done = TRUE; + cf->connected = TRUE; + Curl_resolv_destroy(data, ctx->resolv_id); + return CURLE_OK; +} + +static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_dns_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "destroy"); + cf_dns_ctx_destroy(data, ctx); +} + +static void cf_dns_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + cf->connected = FALSE; + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + +static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ +#ifdef USE_CURL_ASYNC + if(!cf->connected) + return Curl_resolv_pollset(data, ps); +#else + (void)cf; + (void)data; + (void)ps; +#endif + return CURLE_OK; +} + +static CURLcode cf_dns_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_dns_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_DONE: + if(ctx->dns) { + /* Should only come here when the connect attempt failed and + * `data` is giving up on it. On a successful connect, we already + * unlinked the DNS entry. */ + Curl_dns_entry_unlink(data, &ctx->dns); + } + break; + default: + break; + } + return result; +} + +struct Curl_cftype Curl_cft_dns = { + "DNS", + CF_TYPE_SETUP, + CURL_LOG_LVL_NONE, + cf_dns_destroy, + cf_dns_connect, + cf_dns_close, + Curl_cf_def_shutdown, + cf_dns_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + cf_dns_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_dns_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool abstract_unix_socket, + bool for_proxy, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct Curl_cfilter *cf = NULL; + struct cf_dns_ctx *ctx; + CURLcode result = CURLE_OK; + + (void)data; + ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport, + abstract_unix_socket, for_proxy, + complete_resolve, dns); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_dns, ctx); + +out: + *pcf = result ? NULL : cf; + if(result) + cf_dns_ctx_destroy(data, ctx); + return result; +} + +/* Create a "resolv" filter for the transfer's connection. Figures + * out the hostname/path and port where to connect to. */ +static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + uint8_t dns_queries, + uint8_t transport, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct connectdata *conn = data->conn; + const char *hostname = NULL; + uint16_t port = 0; + bool abstract_unix_socket = FALSE, for_proxy = FALSE; + +#ifdef USE_UNIX_SOCKETS + { + const char *unix_path = Curl_conn_get_unix_path(conn); + if(unix_path) { + DEBUGASSERT(transport == TRNSPRT_UNIX); + hostname = unix_path; + abstract_unix_socket = (bool)conn->bits.abstract_unix_socket; + } + } +#endif + +#ifndef CURL_DISABLE_PROXY + if(!hostname && conn->bits.proxy) { + for_proxy = TRUE; + hostname = conn->bits.socksproxy ? + conn->socks_proxy.host.name : conn->http_proxy.host.name; + port = conn->bits.socksproxy ? + conn->socks_proxy.port : conn->http_proxy.port; + } +#endif + if(!hostname) { + struct hostname *ehost; + ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; + /* If not connecting via a proxy, extract the port from the URL, if it is + * there, thus overriding any defaults that might have been set above. */ + hostname = ehost->name; + port = conn->bits.conn_to_port ? + conn->conn_to_port : (uint16_t)conn->remote_port; + } + + if(!hostname) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + return cf_dns_create(pcf, data, dns_queries, + hostname, port, transport, + abstract_unix_socket, for_proxy, + complete_resolve, dns); +} + +/* Adds a "resolv" filter at the top of the connection's filter chain. + * For FIRSTSOCKET, the `dns` parameter may be NULL. The filter will + * figure out hostname and port to connect to and start the DNS resolve + * on the first connect attempt. + * For SECONDARYSOCKET, the `dns` parameter must be given. + */ +CURLcode Curl_cf_dns_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t dns_queries, + uint8_t transport, + struct Curl_dns_entry *dns) +{ + struct Curl_cfilter *cf = NULL; + CURLcode result; + + DEBUGASSERT(data); + if(sockindex == FIRSTSOCKET) + result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns); + else if(dns) { + result = cf_dns_create(&cf, data, dns_queries, + dns->hostname, dns->port, transport, + FALSE, FALSE, FALSE, dns); + } + else { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + } + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +/* Insert a new "resolv" filter directly after `cf`. It will + * start a DNS resolve for the given hostnmae and port on the + * first connect attempt. + * See socks.c on how this is used to make a non-blocking DNS + * resolve during connect. + */ +CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool complete_resolve) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_dns_create(&cf, data, dns_queries, + hostname, port, transport, + FALSE, FALSE, complete_resolve, NULL); + if(result) + return result; + + Curl_conn_cf_insert_after(cf_at, cf); + return CURLE_OK; +} + +/* Return the resolv result from the first "resolv" filter, starting + * the given filter `cf` downwards. + */ +static CURLcode cf_dns_result(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns || ctx->resolv_result) + return ctx->resolv_result; + return CURLE_AGAIN; + } + } + return CURLE_FAILED_INIT; +} + +/* Return the result of the DNS resolution. Searches for a "resolv" + * filter from the top of the filter chain down. Returns + * - CURLE_AGAIN when not done yet + * - CURLE_OK when DNS was successfully resolved + * - CURLR_FAILED_INIT when no resolv filter was found + * - error returned by the DNS resolv + */ +CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex) +{ + return cf_dns_result(conn->cfilter[sockindex]); +} + +static const struct Curl_addrinfo *cf_dns_get_nth_ai( + struct Curl_cfilter *cf, + const struct Curl_addrinfo *ai, + int ai_family, unsigned int index) +{ + struct cf_dns_ctx *ctx = cf->ctx; + unsigned int i = 0; + + if((ai_family == AF_INET) && !(ctx->dns_queries & CURL_DNSQ_A)) + return NULL; +#ifdef USE_IPV6 + if((ai_family == AF_INET6) && !(ctx->dns_queries & CURL_DNSQ_AAAA)) + return NULL; +#endif + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } + } + return NULL; +} + +/* Return the addrinfo at `index` for the given `family` from the + * first "resolve" filter underneath `cf`. If the DNS resolving is + * not done yet or if no address for the family exists, returns NULL. + */ +const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, + struct Curl_easy *data, + int ai_family, + unsigned int index) +{ + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->resolv_result) + return NULL; + else if(ctx->dns) + return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index); + else + return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); + } + } + return NULL; +} + +/* Return the addrinfo at `index` for the given `family` from the + * first "resolve" filter at the connection. If the DNS resolving is + * not done yet or if no address for the family exists, returns NULL. + */ +const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + int sockindex, int ai_family, + unsigned int index) +{ + struct connectdata *conn = data->conn; + return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, ai_family, index); +} + +#ifdef USE_HTTPSRR +/* Return the HTTPS-RR info from the first "resolve" filter at the + * connection. If the DNS resolving is not done yet or if there + * is no HTTPS-RR info, returns NULL. + */ +const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex) +{ + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns) + return ctx->dns->hinfo; + else + return Curl_resolv_get_https(data, ctx->resolv_id); + } + } + return NULL; +} + +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns) + return TRUE; + else + return Curl_resolv_knows_https(data, ctx->resolv_id); + } + } + return FALSE; +} + +#endif /* USE_HTTPSRR */
diff --git a/Utilities/cmcurl/lib/cf-dns.h b/Utilities/cmcurl/lib/cf-dns.h new file mode 100644 index 0000000..3c46b1b --- /dev/null +++ b/Utilities/cmcurl/lib/cf-dns.h
@@ -0,0 +1,71 @@ +#ifndef HEADER_CURL_CF_DNS_H +#define HEADER_CURL_CF_DNS_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" + +struct Curl_easy; +struct connectdata; +struct Curl_dns_entry; +struct Curl_addrinfo; + +CURLcode Curl_cf_dns_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t dns_queries, + uint8_t transport, + struct Curl_dns_entry *dns); + +CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool complete_resolve); + +CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); + +const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + int sockindex, + int ai_family, + unsigned int index); + +const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, + struct Curl_easy *data, + int ai_family, + unsigned int index); + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex); +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex); +#else +#define Curl_conn_dns_get_https(a, b) NULL +#define Curl_conn_dns_resolved_https(a, b) TRUE +#endif + +extern struct Curl_cftype Curl_cft_dns; + +#endif /* HEADER_CURL_CF_DNS_H */
diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c index c41a68d..3c2c837 100644 --- a/Utilities/cmcurl/lib/cf-h1-proxy.c +++ b/Utilities/cmcurl/lib/cf-h1-proxy.c
@@ -31,7 +31,6 @@ #include "http.h" #include "http1.h" #include "http_proxy.h" -#include "url.h" #include "select.h" #include "progress.h" #include "cfilters.h" @@ -39,7 +38,6 @@ #include "connect.h" #include "curl_trc.h" #include "strcase.h" -#include "transfer.h" #include "curlx/strparse.h" @@ -106,8 +104,8 @@ { struct h1_tunnel_state *ts; - if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) { - failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme); + if(cf->conn->scheme->flags & PROTOPT_NOTCPPROXY) { + failf(data, "%s cannot be done over CONNECT", cf->conn->scheme->name); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -173,8 +171,8 @@ proxy */ /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request - after we have connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); + after we have connected. Let's thus free and clear it here. */ + curlx_safefree(data->req.proxyuserpwd); break; } } @@ -208,10 +206,9 @@ int http_minor; CURLcode result; - /* This only happens if we have looped here due to authentication - reasons, and we do not really use the newly cloned URL here - then. Just free it. */ - Curl_safefree(data->req.newurl); + /* This only happens if we have looped here due to authentication reasons, + and we do not really use the newly cloned URL here then. Free it. */ + curlx_safefree(data->req.newurl); result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); if(result) @@ -241,7 +238,7 @@ struct h1_tunnel_state *ts, bool *done) { - uint8_t *buf = curlx_dyn_uptr(&ts->request_data); + const uint8_t *buf = curlx_dyn_uptr(&ts->request_data); size_t request_len = curlx_dyn_len(&ts->request_data); size_t blen = request_len; CURLcode result = CURLE_OK; @@ -262,7 +259,7 @@ DEBUGASSERT(blen >= nwritten); ts->nsent += nwritten; - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf, nwritten); + Curl_debug(data, CURLINFO_HEADER_OUT, (const char *)buf, nwritten); out: if(result) @@ -342,8 +339,8 @@ ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && !ISDIGIT(header[12])) { /* store the HTTP code from the proxy */ - data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 + - (header[10] - '0') * 10 + (header[11] - '0'); + data->info.httpproxycode = k->httpcode = ((header[9] - '0') * 100) + + ((header[10] - '0') * 10) + (header[11] - '0'); } return result; } @@ -353,9 +350,9 @@ struct h1_tunnel_state *ts) { CURLcode result = CURLE_OK; - char *linep = curlx_dyn_ptr(&ts->rcvbuf); + const char *linep = curlx_dyn_ptr(&ts->rcvbuf); size_t line_len = curlx_dyn_len(&ts->rcvbuf); /* bytes in this line */ - struct SingleRequest *k = &data->req; + const struct SingleRequest *k = &data->req; int writetype; ts->headerlines++; @@ -452,7 +449,7 @@ if(!nread) { if(data->set.proxyauth && data->state.authproxy.avail && - data->state.aptr.proxyuserpwd) { + data->req.proxyuserpwd) { /* proxy auth was requested and there was proxy auth available, then deem this as "mere" proxy disconnect */ ts->close_connection = TRUE; @@ -468,17 +465,7 @@ if(ts->keepon == KEEPON_IGNORE) { /* This means we are currently ignoring a response-body */ - - if(ts->cl) { - /* A Content-Length based body: simply count down the counter - and make sure to break out of the loop when we are done! */ - ts->cl--; - if(ts->cl <= 0) { - ts->keepon = KEEPON_DONE; - break; - } - } - else if(ts->chunked_encoding) { + if(ts->chunked_encoding) { /* chunked-encoded body, so we need to do the chunked dance properly to know when the end of the body is reached */ size_t consumed = 0; @@ -494,6 +481,15 @@ ts->keepon = KEEPON_DONE; } } + else if(ts->cl) { + /* A Content-Length based body: count down the counter + and make sure to break out of the loop when we are done! */ + ts->cl--; + if(ts->cl <= 0) { + ts->keepon = KEEPON_DONE; + break; + } + } continue; } @@ -531,7 +527,7 @@ if(byte != 0x0a) continue; else { - char *linep = curlx_dyn_ptr(&ts->rcvbuf); + const char *linep = curlx_dyn_ptr(&ts->rcvbuf); size_t hlen = curlx_dyn_len(&ts->rcvbuf); if(hlen && ISNEWLINE(linep[0])) { /* end of headers */ @@ -571,7 +567,7 @@ do { - if(Curl_timeleft_ms(data, TRUE) < 0) { + if(Curl_timeleft_ms(data) < 0) { failf(data, "Proxy CONNECT aborted due to timeout"); result = CURLE_OPERATION_TIMEDOUT; goto out; @@ -600,6 +596,8 @@ /* read what is there */ CURL_TRC_CF(data, cf, "CONNECT receive"); result = recv_CONNECT_resp(cf, data, ts, &done); + if(result) + CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result); if(!result) result = Curl_pgrsUpdate(data); /* error or not complete yet. return for more multi-multi */ @@ -645,10 +643,10 @@ DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); if(data->info.httpproxycode / 100 != 2) { /* a non-2xx response and we have no next URL to try. */ - Curl_safefree(data->req.newurl); + curlx_safefree(data->req.newurl); h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); - return CURLE_RECV_ERROR; + return CURLE_COULDNT_CONNECT; } /* 2xx response, SUCCESS! */ h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); @@ -692,7 +690,7 @@ result = H1_CONNECT(cf, data, ts); if(result) goto out; - Curl_safefree(data->state.aptr.proxyuserpwd); + curlx_safefree(data->req.proxyuserpwd); out: *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.h b/Utilities/cmcurl/lib/cf-h1-proxy.h index e48d827..6544ec5 100644 --- a/Utilities/cmcurl/lib/cf-h1-proxy.h +++ b/Utilities/cmcurl/lib/cf-h1-proxy.h
@@ -27,7 +27,7 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) -CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf, +CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data); extern struct Curl_cftype Curl_cft_h1_proxy;
diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c index 4f101a0..2f8cc41 100644 --- a/Utilities/cmcurl/lib/cf-h2-proxy.c +++ b/Utilities/cmcurl/lib/cf-h2-proxy.c
@@ -80,7 +80,7 @@ struct tunnel_stream *ts) { const char *hostname; - int port; + uint16_t port; bool ipv6_ip; ts->state = H2_TUNNEL_INIT; @@ -92,7 +92,7 @@ Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); /* host:port with IPv6 support */ - ts->authority = curl_maprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname, + ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, ipv6_ip ? "]" : "", port); if(!ts->authority) return CURLE_OUT_OF_MEMORY; @@ -105,7 +105,7 @@ Curl_http_resp_free(ts->resp); Curl_bufq_free(&ts->recvbuf); Curl_bufq_free(&ts->sendbuf); - Curl_safefree(ts->authority); + curlx_safefree(ts->authority); memset(ts, 0, sizeof(*ts)); ts->state = H2_TUNNEL_INIT; } @@ -157,8 +157,8 @@ ts->state = new_state; /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request - after we have connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); + after we have connected. Let's thus free and clear it here. */ + curlx_safefree(data->req.proxyuserpwd); break; } } @@ -226,8 +226,7 @@ if(cf) { struct Curl_easy *data = CF_DATA_CURRENT(cf); CURLcode result; - result = Curl_conn_cf_send(cf->next, data, buf, buflen, - FALSE, pnwritten); + result = Curl_conn_cf_send(cf->next, data, buf, buflen, FALSE, pnwritten); CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %d, %zu", buflen, result, *pnwritten); return result; @@ -259,113 +258,6 @@ return rc; } -static ssize_t on_session_send(nghttp2_session *h2, - const uint8_t *buf, size_t blen, - int flags, void *userp); -static int proxy_h2_on_frame_recv(nghttp2_session *session, - const nghttp2_frame *frame, - void *userp); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int proxy_h2_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); -static int proxy_h2_on_header(nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp); -static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *mem, size_t len, void *userp); - -/* - * Initialize the cfilter context - */ -static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OUT_OF_MEMORY; - nghttp2_session_callbacks *cbs = NULL; - int rc; - - DEBUGASSERT(!ctx->h2); - memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); - - 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; - - rc = nghttp2_session_callbacks_new(&cbs); - if(rc) { - failf(data, "Could not initialize nghttp2 callbacks"); - goto out; - } - - 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, - proxy_h2_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( - cbs, proxy_h2_on_stream_close); - nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); - - /* The nghttp2 session is not yet setup, do it */ - rc = proxy_h2_client_new(cf, cbs); - if(rc) { - failf(data, "Could not initialize nghttp2"); - goto out; - } - - { - nghttp2_settings_entry iv[3]; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = Curl_multi_max_concurrent_streams(data->multi); - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = H2_TUNNEL_WINDOW_SIZE; - iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = 0; - rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); - if(rc) { - failf(data, "nghttp2_submit_settings() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - } - - rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, - PROXY_HTTP2_HUGE_WINDOW_SIZE); - if(rc) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - - /* all set, traffic will be send on connect */ - result = CURLE_OK; - -out: - if(cbs) - nghttp2_session_callbacks_del(cbs); - CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); - return result; -} - static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx) { return !nghttp2_session_want_read(ctx->h2) && @@ -379,7 +271,6 @@ size_t nwritten; CURLcode result; - (void)data; if(Curl_bufq_is_empty(&ctx->outbufq)) return CURLE_OK; @@ -530,82 +421,11 @@ if(!nwritten) return NGHTTP2_ERR_WOULDBLOCK; - return (nwritten > SSIZE_MAX) ? + return (nwritten > SSIZE_MAX) ? NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int proxy_h2_fr_print(const nghttp2_frame *frame, - char *buffer, size_t blen) -{ - switch(frame->hd.type) { - case NGHTTP2_DATA: { - return curl_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 curl_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 curl_msnprintf(buffer, blen, - "FRAME[PRIORITY, len=%d, flags=%d]", - (int)frame->hd.length, frame->hd.flags); - } - case NGHTTP2_RST_STREAM: { - return curl_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 curl_msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); - } - return curl_msnprintf(buffer, blen, - "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); - } - case NGHTTP2_PUSH_PROMISE: - return curl_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 curl_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 = CURL_ARRAYSIZE(scratch); - 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 curl_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 curl_msnprintf(buffer, blen, - "FRAME[WINDOW_UPDATE, incr=%d]", - frame->window_update.window_size_increment); - } - default: - return curl_msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", - frame->hd.type, (int)frame->hd.length, - frame->hd.flags); - } -} - +#ifdef CURLVERBOSE static int proxy_h2_on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, void *userp) @@ -615,16 +435,16 @@ (void)session; DEBUGASSERT(data); - if(data && Curl_trc_cf_is_verbose(cf, data)) { + if(Curl_trc_cf_is_verbose(cf, data)) { char buffer[256]; int len; - len = proxy_h2_fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_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 */ +#endif /* CURLVERBOSE */ static int proxy_h2_on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, @@ -637,15 +457,15 @@ (void)session; DEBUGASSERT(data); -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_cf_is_verbose(cf, data)) { char buffer[256]; int len; - len = proxy_h2_fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_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 */ +#endif /* CURLVERBOSE */ if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ @@ -657,7 +477,7 @@ * 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(CURL_WANT_SEND(data)) { + if(CURL_REQ_WANT_SEND(data)) { drain_tunnel(cf, data, &ctx->tunnel); } break; @@ -692,10 +512,14 @@ } break; case NGHTTP2_WINDOW_UPDATE: - if(CURL_WANT_SEND(data)) { + if(CURL_REQ_WANT_SEND(data)) { drain_tunnel(cf, data, &ctx->tunnel); } break; + case NGHTTP2_RST_STREAM: + if(frame->rst_stream.error_code) + ctx->tunnel.reset = TRUE; + break; default: break; } @@ -716,7 +540,6 @@ CURLcode result; (void)flags; - (void)data; (void)session; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ if(stream_id != ctx->tunnel.stream_id) { @@ -785,7 +608,6 @@ size_t nread; (void)source; - (void)data; (void)ctx; if(!stream_id) @@ -805,7 +627,7 @@ if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) *data_flags = NGHTTP2_DATA_FLAG_EOF; - CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd", + CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zu", ts->stream_id, nread); return (nread > SSIZE_MAX) ? NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nread; @@ -849,28 +671,30 @@ struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)session; - (void)data; if(stream_id != ctx->tunnel.stream_id) return 0; - CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)", + CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %u)", stream_id, nghttp2_http2_strerror(error_code), error_code); ctx->tunnel.closed = TRUE; ctx->tunnel.error = error_code; + if(error_code) + ctx->tunnel.reset = TRUE; return 0; } -static CURLcode proxy_h2_submit(int32_t *pstream_id, - struct Curl_cfilter *cf, - struct Curl_easy *data, - nghttp2_session *h2, - struct httpreq *req, - const nghttp2_priority_spec *pri_spec, - void *stream_user_data, - nghttp2_data_source_read_callback read_callback, - void *read_ctx) +static CURLcode proxy_h2_submit( + int32_t *pstream_id, + struct Curl_cfilter *cf, + struct Curl_easy *data, + nghttp2_session *h2, + struct httpreq *req, + const nghttp2_priority_spec *pri_spec, + void *stream_user_data, + nghttp2_data_source_read_callback read_callback, + void *read_ctx) { struct dynhds h2_headers; nghttp2_nv *nva = NULL; @@ -981,14 +805,14 @@ return result; if(data->req.newurl) { /* Indicator that we should try again */ - Curl_safefree(data->req.newurl); + curlx_safefree(data->req.newurl); h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); return CURLE_OK; } } /* Seems to have failed */ - return CURLE_RECV_ERROR; + return CURLE_COULDNT_CONNECT; } static CURLcode H2_CONNECT(struct Curl_cfilter *cf, @@ -1055,6 +879,89 @@ return result; } +/* + * Initialize the cfilter context + */ +static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + nghttp2_session_callbacks *cbs = NULL; + int rc; + + DEBUGASSERT(!ctx->h2); + memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); + + 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; + + rc = nghttp2_session_callbacks_new(&cbs); + if(rc) { + failf(data, "Could not initialize nghttp2 callbacks"); + goto out; + } + + nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); + nghttp2_session_callbacks_set_on_frame_recv_callback( + cbs, proxy_h2_on_frame_recv); +#ifdef CURLVERBOSE + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, + proxy_h2_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( + cbs, proxy_h2_on_stream_close); + nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); + + /* The nghttp2 session is not yet setup, do it */ + rc = proxy_h2_client_new(cf, cbs); + if(rc) { + failf(data, "Could not initialize nghttp2"); + goto out; + } + + { + nghttp2_settings_entry iv[3]; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = Curl_multi_max_concurrent_streams(data->multi); + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = H2_TUNNEL_WINDOW_SIZE; + iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[2].value = 0; + rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); + if(rc) { + failf(data, "nghttp2_submit_settings() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + } + + rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, + PROXY_HTTP2_HUGE_WINDOW_SIZE); + if(rc) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + /* all set, traffic will be send on connect */ + result = CURLE_OK; + +out: + if(cbs) + nghttp2_session_callbacks_del(cbs); + CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); + return result; +} + static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1086,7 +993,7 @@ } DEBUGASSERT(ts->authority); - if(Curl_timeleft_ms(data, TRUE) < 0) { + if(Curl_timeleft_ms(data) < 0) { failf(data, "Proxy CONNECT aborted due to timeout"); result = CURLE_OPERATION_TIMEDOUT; goto out; @@ -1251,20 +1158,10 @@ struct cf_h2_proxy_ctx *ctx = cf->ctx; *pnread = 0; - if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { - CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " - "connection", ctx->tunnel.stream_id); - failf(data, "proxy server refused HTTP/2 stream"); - return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - } - else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { - failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", - ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), - ctx->tunnel.error); - return CURLE_HTTP2_STREAM; - } - else if(ctx->tunnel.reset) { - failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); + if(ctx->tunnel.error) { + failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)", + ctx->tunnel.stream_id, ctx->tunnel.reset ? "server" : "curl", + ctx->tunnel.error, nghttp2_http2_strerror(ctx->tunnel.error)); return CURLE_RECV_ERROR; } @@ -1372,7 +1269,7 @@ } result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zd", result, *pnwritten); + CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", result, *pnwritten); if(result && (result != CURLE_AGAIN)) goto out;
diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c index 6c9bfdd..9ee5e79 100644 --- a/Utilities/cmcurl/lib/cf-haproxy.c +++ b/Utilities/cmcurl/lib/cf-haproxy.c
@@ -28,6 +28,7 @@ #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "select.h" @@ -61,9 +62,16 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, struct Curl_easy *data) { + /* We fake a client connection report to the upstream server + * with the HAProxy protocol, reporting the client's source + * and destination IP addresses and ports. + * addresses: either the ones used to talk to the upstream + * OR the value supplied by the user + * ports: the ports used in the upstream connection */ + const char *client_source_ip; + const char *client_dest_ip; struct cf_haproxy_ctx *ctx = cf->ctx; CURLcode result; - const char *client_ip; struct ip_quadruple ipquad; bool is_ipv6; @@ -79,15 +87,19 @@ if(result) return result; - /* Emit the correct prefix for IPv6 */ - if(data->set.str[STRING_HAPROXY_CLIENT_IP]) - client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; - else - client_ip = ipquad.local_ip; + if(data->set.str[STRING_HAPROXY_CLIENT_IP]) { + client_source_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; + client_dest_ip = client_source_ip; + is_ipv6 = !Curl_is_ipv4addr(client_source_ip); + } + else { + client_source_ip = ipquad.local_ip; + client_dest_ip = ipquad.remote_ip; + } result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", is_ipv6 ? "TCP6" : "TCP4", - client_ip, ipquad.remote_ip, + client_source_ip, client_dest_ip, ipquad.local_port, ipquad.remote_port); #ifdef USE_UNIX_SOCKETS @@ -156,7 +168,6 @@ static void cf_haproxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - (void)data; CURL_TRC_CF(data, cf, "destroy"); cf_haproxy_ctx_free(cf->ctx); } @@ -186,7 +197,7 @@ struct Curl_cftype Curl_cft_haproxy = { "HAPROXY", - CF_TYPE_PROXY, + CF_TYPE_PROXY | CF_TYPE_SETUP, 0, cf_haproxy_destroy, cf_haproxy_connect,
diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c index d2a08d1..d1d8f51 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.c +++ b/Utilities/cmcurl/lib/cf-https-connect.c
@@ -28,8 +28,10 @@ #include "urldata.h" #include "curl_trc.h" #include "cfilters.h" +#include "cf-dns.h" #include "connect.h" #include "hostip.h" +#include "httpsrr.h" #include "multiif.h" #include "cf-https-connect.h" #include "http2.h" @@ -38,6 +40,7 @@ #include "vquic/vquic.h" typedef enum { + CF_HC_RESOLV, CF_HC_INIT, CF_HC_CONNECT, CF_HC_SUCCESS, @@ -55,19 +58,17 @@ BIT(shutdown); }; -static void cf_hc_baller_reset(struct cf_hc_baller *b, - struct Curl_easy *data) +static void cf_hc_baller_discard(struct cf_hc_baller *b, + struct Curl_easy *data) { if(b->cf) { Curl_conn_cf_close(b->cf, data); Curl_conn_cf_discard_chain(&b->cf, data); b->cf = NULL; } - b->result = CURLE_OK; - b->reply_ms = -1; } -static bool cf_hc_baller_is_active(struct cf_hc_baller *b) +static bool cf_hc_baller_is_connecting(struct cf_hc_baller *b) { return b->cf && !b->result; } @@ -111,18 +112,46 @@ cf_hc_state state; struct curltime started; /* when connect started */ CURLcode result; /* overall result */ + CURLcode check_h3_result; struct cf_hc_baller ballers[2]; size_t baller_count; timediff_t soft_eyeballs_timeout_ms; timediff_t hard_eyeballs_timeout_ms; + uint8_t def_transport; + BIT(httpsrr_resolved); + BIT(checked_h3); + BIT(ballers_complete); }; +static void cf_hc_ctx_close(struct Curl_easy *data, + struct cf_hc_ctx *ctx) +{ + if(ctx) { + size_t i; + for(i = 0; i < ctx->baller_count; ++i) + cf_hc_baller_discard(&ctx->ballers[i], data); + } +} + +static void cf_hc_ctx_destroy(struct Curl_easy *data, + struct cf_hc_ctx *ctx) +{ + if(ctx) { + cf_hc_ctx_close(data, ctx); + curlx_free(ctx); + } +} + static void cf_hc_baller_assign(struct cf_hc_baller *b, enum alpnid alpn_id, uint8_t def_transport) { b->alpn_id = alpn_id; b->transport = def_transport; + b->cf = NULL; + b->result = CURLE_OK; + b->reply_ms = -1; + b->shutdown = FALSE; switch(b->alpn_id) { case ALPN_h3: b->name = "h3"; @@ -134,6 +163,9 @@ case ALPN_h1: b->name = "h1"; break; + case ALPN_none: + b->name = "no-alpn"; + break; default: b->result = CURLE_FAILED_INIT; break; @@ -142,24 +174,14 @@ static void cf_hc_baller_init(struct cf_hc_baller *b, struct Curl_cfilter *cf, - struct Curl_easy *data, - uint8_t transport) + struct Curl_easy *data) { struct Curl_cfilter *save = cf->next; cf->next = NULL; b->started = *Curl_pgrs_now(data); - switch(b->alpn_id) { - case ALPN_h3: - transport = TRNSPRT_QUIC; - break; - default: - break; - } - - if(!b->result) - b->result = Curl_cf_setup_insert_after(cf, data, transport, - CURL_CF_SSL_ENABLE); + b->result = Curl_cf_setup_insert_after(cf, data, b->transport, + CURL_CF_SSL_ENABLE); b->cf = cf->next; cf->next = save; } @@ -178,57 +200,39 @@ return b->result; } -static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_hc_ctx *ctx = cf->ctx; - size_t i; - - if(ctx) { - for(i = 0; i < ctx->baller_count; ++i) - cf_hc_baller_reset(&ctx->ballers[i], data); - ctx->state = CF_HC_INIT; - ctx->result = CURLE_OK; - ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; - ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4; - } -} - static CURLcode baller_connected(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_hc_baller *winner) { struct cf_hc_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - int reply_ms; - size_t i; - DEBUGASSERT(winner->cf); - for(i = 0; i < ctx->baller_count; ++i) - if(winner != &ctx->ballers[i]) - cf_hc_baller_reset(&ctx->ballers[i], data); + /* Make the winner's connection filter out own sub-filter, check, move, + * close all remaining. */ + if(cf->next) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + if(!winner->cf) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } - reply_ms = cf_hc_baller_reply_ms(winner, data); - if(reply_ms >= 0) - CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", - winner->name, - (int)curlx_ptimediff_ms(Curl_pgrs_now(data), - &winner->started), reply_ms); - else - CURL_TRC_CF(data, cf, "deferred handshake %s: %dms", - winner->name, (int)curlx_ptimediff_ms(Curl_pgrs_now(data), - &winner->started)); - - /* install the winning filter below this one. */ cf->next = winner->cf; winner->cf = NULL; + ctx->state = CF_HC_SUCCESS; + cf->connected = TRUE; + + cf_hc_ctx_close(data, ctx); + /* ballers may have failf()'d, the winner resets it, so our + * errorbuf is clean again. */ + Curl_reset_fail(data); #ifdef USE_NGHTTP2 { - /* Using nghttp2, we add the filter "below" us, so when the conn - * closes, we tear it down for a fresh reconnect */ + /* For a negotiated HTTP/2 connection insert the h2 filter. */ const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); if(alpn && !strcmp("h2", alpn)) { - result = Curl_http2_switch_at(cf, data); + CURLcode result = Curl_http2_switch_at(cf, data); if(result) { ctx->state = CF_HC_FAILURE; ctx->result = result; @@ -237,63 +241,249 @@ } } #endif - - ctx->state = CF_HC_SUCCESS; - cf->connected = TRUE; - return result; + return CURLE_OK; } -static bool time_to_start_next(struct Curl_cfilter *cf, - struct Curl_easy *data, - size_t idx, struct curltime now) +static bool time_to_start_baller2(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; timediff_t elapsed_ms; - size_t i; - if(idx >= ctx->baller_count) + if(ctx->baller_count < 2) return FALSE; - if(cf_hc_baller_has_started(&ctx->ballers[idx])) + else if(cf_hc_baller_has_started(&ctx->ballers[1])) return FALSE; - for(i = 0; i < idx; i++) { - if(!ctx->ballers[i].result) - break; - } - if(i == idx) { - CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s", - ctx->ballers[idx].name); + else if(ctx->ballers[0].result) { + CURL_TRC_CF(data, cf, "%s baller failed, starting %s", + ctx->ballers[0].name, ctx->ballers[1].name); return TRUE; } - elapsed_ms = curlx_ptimediff_ms(&now, &ctx->started); + + elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - CURL_TRC_CF(data, cf, "hard timeout of %" FMT_TIMEDIFF_T "ms reached, " - "starting %s", - ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name); + CURL_TRC_CF(data, cf, "%s inconclusive after %" FMT_TIMEDIFF_T ", " + "starting %s", ctx->ballers[0].name, + ctx->hard_eyeballs_timeout_ms, ctx->ballers[1].name); return TRUE; } - - if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) { - if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) { - CURL_TRC_CF(data, cf, "soft timeout of %" FMT_TIMEDIFF_T "ms reached, " - "%s has not seen any data, starting %s", - ctx->soft_eyeballs_timeout_ms, - ctx->ballers[idx - 1].name, ctx->ballers[idx].name); + else if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { + if(cf_hc_baller_reply_ms(&ctx->ballers[0], data) < 0) { + CURL_TRC_CF(data, cf, "%s has not seen any data after %" + FMT_TIMEDIFF_T "ms, starting %s", + ctx->ballers[0].name, ctx->soft_eyeballs_timeout_ms, + ctx->ballers[1].name); return TRUE; } - /* set the effective hard timeout again */ - Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms, - EXPIRE_ALPN_EYEBALLS); } return FALSE; } +static bool cf_hc_may_h3(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + if(!ctx->checked_h3) { + ctx->check_h3_result = + Curl_conn_may_http3(data, cf->conn, ctx->def_transport); + ctx->checked_h3 = TRUE; + } + return !ctx->check_h3_result; +} + +static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + enum alpnid not_this_one) +{ +#ifdef USE_HTTPSRR + /* Is there an HTTPSRR use its ALPNs here. + * We are here after having selected a connection to a host+port and + * can no longer change that. Any HTTPSRR advice for other hosts and ports + * we need to ignore. */ + const struct Curl_https_rrinfo *rr; + size_t i; + + /* Do we have HTTPS-RR information? */ + rr = Curl_conn_dns_get_https(data, cf->sockindex); + + /* We do not support `rr->no_def_alpn`. */ + if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) { + for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) { + enum alpnid alpn_rr = (enum alpnid)rr->alpns[i]; + if(alpn_rr == not_this_one) /* don't want this one */ + continue; + switch(alpn_rr) { + case ALPN_h3: + if((data->state.http_neg.allowed & CURL_HTTP_V3x) && + cf_hc_may_h3(cf, data)) { + return alpn_rr; + } + break; + case ALPN_h2: + if(data->state.http_neg.allowed & CURL_HTTP_V2x) { + return alpn_rr; + } + break; + case ALPN_h1: + if(data->state.http_neg.allowed & CURL_HTTP_V1x) { + return alpn_rr; + } + break; + default: /* ignore */ + break; + } + } + } +#else + (void)cf; + (void)data; + (void)not_this_one; +#endif + return ALPN_none; +} + +static enum alpnid cf_hc_get_pref_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + enum alpnid not_this_one) +{ + if((data->state.http_neg.preferred & data->state.http_neg.allowed)) { + switch(data->state.http_neg.preferred) { + case CURL_HTTP_V3x: + if(cf_hc_may_h3(cf, data) && (ALPN_h3 != not_this_one)) + return ALPN_h3; + break; + case CURL_HTTP_V2x: + if(ALPN_h2 != not_this_one) + return ALPN_h2; + break; + case CURL_HTTP_V1x: + /* If we are trying h2 already, h1 is already used as fallback */ + if((ALPN_h1 != not_this_one) && (ALPN_h2 != not_this_one)) + return ALPN_h1; + break; + default: + break; + } + } + return ALPN_none; +} + +static enum alpnid cf_hc_get_first_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + http_majors choices, + enum alpnid not_this_one) +{ + /* When told to not try h2, we also do not try h1 and vice versa */ + bool allow_h1_or_h2 = (not_this_one != ALPN_h1) && + (not_this_one != ALPN_h2); + if((ALPN_h3 != not_this_one) && (choices & CURL_HTTP_V3x) && + cf_hc_may_h3(cf, data)) { + return ALPN_h3; + } + if(allow_h1_or_h2 && (choices & CURL_HTTP_V2x)) { + return ALPN_h2; + } + if(allow_h1_or_h2 && (choices & CURL_HTTP_V1x)) { + return ALPN_h1; + } + return ALPN_none; +} + +static CURLcode cf_hc_set_baller1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + enum alpnid alpn1 = ALPN_none; + VERBOSE(const char *source = "HTTPS-RR"); + + DEBUGASSERT(cf->conn->bits.tls_enable_alpn); + + alpn1 = cf_hc_get_httpsrr_alpn(cf, data, ALPN_none); + if(alpn1 == ALPN_none) { + /* preference is configured and allowed, can we use it? */ + VERBOSE(source = "preferred version"); + alpn1 = cf_hc_get_pref_alpn(cf, data, ALPN_none); + } + if(alpn1 == ALPN_none) { + VERBOSE(source = "wanted versions"); + alpn1 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.wanted, + ALPN_none); + } + if(alpn1 == ALPN_none) { + VERBOSE(source = "allowed versions"); + alpn1 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.allowed, + ALPN_none); + } + + if(alpn1 == ALPN_none) { + /* None of the wanted/allowed HTTP versions could be chosen */ + if(ctx->check_h3_result) { + CURL_TRC_CF(data, cf, "unable to use HTTP/3"); + return ctx->check_h3_result; + } + CURL_TRC_CF(data, cf, "unable to select HTTP version"); + return CURLE_FAILED_INIT; + } + + cf_hc_baller_assign(&ctx->ballers[0], alpn1, ctx->def_transport); + ctx->baller_count = 1; + CURL_TRC_CF(data, cf, "1st attempt uses %s from %s", + ctx->ballers[0].name, source); + + switch(alpn1) { + case ALPN_h1: + /* We really want h1, switch off h2 to make it disappear in ALPN */ + data->state.http_neg.wanted &= (uint8_t)~CURL_HTTP_V2x; + break; + default: + break; + } + + return CURLE_OK; +} + +static void cf_hc_set_baller2(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + enum alpnid alpn2 = ALPN_none, alpn1 = ctx->ballers[0].alpn_id; + VERBOSE(const char *source = "HTTPS-RR"); + + if(ctx->ballers_complete) + return; /* already done */ + if(!ctx->httpsrr_resolved) + return; /* HTTPS-RR pending */ + + alpn2 = cf_hc_get_httpsrr_alpn(cf, data, alpn1); + if(alpn2 == ALPN_none) { + /* preference is configured and allowed, can we use it? */ + VERBOSE(source = "preferred version"); + alpn2 = cf_hc_get_pref_alpn(cf, data, alpn1); + } + if(alpn2 == ALPN_none) { + VERBOSE(source = "wanted versions"); + alpn2 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.wanted, + alpn1); + } + + if(alpn2 != ALPN_none) { + cf_hc_baller_assign(&ctx->ballers[1], alpn2, ctx->def_transport); + ctx->baller_count = 2; + CURL_TRC_CF(data, cf, "2nd attempt uses %s from %s", + ctx->ballers[1].name, source); + } + ctx->ballers_complete = TRUE; +} + static CURLcode cf_hc_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct cf_hc_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - size_t i, failed_ballers; if(cf->connected) { *done = TRUE; @@ -301,24 +491,45 @@ } *done = FALSE; + + if(!ctx->httpsrr_resolved) { + ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex); +#ifdef DEBUGBUILD + if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) { + CURL_TRC_CF(data, cf, "awaiting HTTPS-RR"); + return CURLE_OK; + } +#endif + } + switch(ctx->state) { + case CF_HC_RESOLV: + ctx->state = CF_HC_INIT; + FALLTHROUGH(); + case CF_HC_INIT: DEBUGASSERT(!cf->next); - for(i = 0; i < ctx->baller_count; i++) - DEBUGASSERT(!ctx->ballers[i].cf); CURL_TRC_CF(data, cf, "connect, init"); + result = cf_hc_set_baller1(cf, data); + if(result) { + ctx->result = result; + ctx->state = CF_HC_FAILURE; + goto out; + } + cf_hc_set_baller2(cf, data); ctx->started = *Curl_pgrs_now(data); - cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport); - if(ctx->baller_count > 1) { + cf_hc_baller_init(&ctx->ballers[0], cf, data); + if((ctx->baller_count > 1) || !ctx->ballers_complete) { Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); - CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T - "ms", ctx->soft_eyeballs_timeout_ms); } ctx->state = CF_HC_CONNECT; FALLTHROUGH(); case CF_HC_CONNECT: - if(cf_hc_baller_is_active(&ctx->ballers[0])) { + if(!ctx->ballers_complete) + cf_hc_set_baller2(cf, data); + + if(cf_hc_baller_is_connecting(&ctx->ballers[0])) { result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->ballers[0]); @@ -326,12 +537,11 @@ } } - if(time_to_start_next(cf, data, 1, *Curl_pgrs_now(data))) { - cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport); + if(time_to_start_baller2(cf, data)) { + cf_hc_baller_init(&ctx->ballers[1], cf, data); } - if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) { - CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name); + if(cf_hc_baller_is_connecting(&ctx->ballers[1])) { result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->ballers[1]); @@ -339,21 +549,12 @@ } } - failed_ballers = 0; - for(i = 0; i < ctx->baller_count; i++) { - if(ctx->ballers[i].result) - ++failed_ballers; - } - - if(failed_ballers == ctx->baller_count) { + if(ctx->ballers[0].result && + (ctx->ballers[1].result || + (ctx->ballers_complete && (ctx->baller_count < 2)))) { /* all have failed. we give up */ CURL_TRC_CF(data, cf, "connect, all attempts failed"); - for(i = 0; i < ctx->baller_count; i++) { - if(ctx->ballers[i].result) { - result = ctx->ballers[i].result; - break; - } - } + ctx->result = result = ctx->ballers[0].result; ctx->state = CF_HC_FAILURE; goto out; } @@ -397,7 +598,7 @@ for(i = 0; i < ctx->baller_count; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; bool bdone = FALSE; - if(!cf_hc_baller_is_active(b) || b->shutdown) + if(!cf_hc_baller_is_connecting(b) || b->shutdown) continue; b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone); if(b->result || bdone) @@ -430,11 +631,11 @@ for(i = 0; (i < ctx->baller_count) && !result; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; - if(!cf_hc_baller_is_active(b)) + if(!cf_hc_baller_is_connecting(b)) continue; result = Curl_conn_cf_adjust_pollset(b->cf, data, ps); } - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); } return result; } @@ -533,7 +734,7 @@ static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "close"); - cf_hc_reset(cf, data); + cf_hc_ctx_close(data, cf->ctx); cf->connected = FALSE; if(cf->next) { @@ -546,15 +747,13 @@ { struct cf_hc_ctx *ctx = cf->ctx; - (void)data; CURL_TRC_CF(data, cf, "destroy"); - cf_hc_reset(cf, data); - Curl_safefree(ctx); + cf_hc_ctx_destroy(data, ctx); } struct Curl_cftype Curl_cft_http_connect = { "HTTPS-CONNECT", - 0, + CF_TYPE_SETUP | CF_TYPE_HTTPSRR, CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, @@ -572,59 +771,42 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, struct Curl_easy *data, - enum alpnid *alpnids, size_t alpn_count, uint8_t def_transport) { struct Curl_cfilter *cf = NULL; struct cf_hc_ctx *ctx; CURLcode result = CURLE_OK; - size_t i; ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - - DEBUGASSERT(alpnids); - DEBUGASSERT(alpn_count); - DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers)); - if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) { - failf(data, "https-connect filter create with unsupported %zu ALPN ids", - alpn_count); - result = CURLE_FAILED_INIT; - goto out; - } - - for(i = 0; i < alpn_count; ++i) - cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport); - for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i) - ctx->ballers[i].alpn_id = ALPN_none; - ctx->baller_count = alpn_count; + ctx->def_transport = def_transport; + ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; + ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2; result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx); if(result) goto out; ctx = NULL; - cf_hc_reset(cf, data); out: *pcf = result ? NULL : cf; - curlx_free(ctx); + cf_hc_ctx_destroy(data, ctx); return result; } -static CURLcode cf_http_connect_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - enum alpnid *alpn_ids, size_t alpn_count, - uint8_t def_transport) +static CURLcode cf_hc_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t def_transport) { struct Curl_cfilter *cf; CURLcode result = CURLE_OK; DEBUGASSERT(data); - result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport); + result = cf_hc_create(&cf, data, def_transport); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -632,138 +814,19 @@ return result; } -static bool cf_https_alpns_contain(enum alpnid id, - enum alpnid *list, size_t len) -{ - size_t i; - for(i = 0; i < len; ++i) { - if(id == list[i]) - return TRUE; - } - return FALSE; -} - CURLcode Curl_cf_https_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex) { - enum alpnid alpn_ids[2]; - size_t alpn_count = 0; CURLcode result = CURLE_OK; - struct Curl_cfilter cf_fake, *cf = NULL; - (void)sockindex; - /* we want to log for the filter before we create it, fake it. */ - memset(&cf_fake, 0, sizeof(cf_fake)); - cf_fake.cft = &Curl_cft_http_connect; - cf = &cf_fake; + DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS); - if(conn->bits.tls_enable_alpn) { -#ifdef USE_HTTPSRR - /* Is there an HTTPSRR use its ALPNs here. - * We are here after having selected a connection to a host+port and - * can no longer change that. Any HTTPSRR advice for other hosts and ports - * we need to ignore. */ - struct Curl_dns_entry *dns = data->state.dns[sockindex]; - struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL; - if(rr && !rr->no_def_alpn && /* ALPNs are defaults */ - (!rr->target || /* for same host */ - !rr->target[0] || - (rr->target[0] == '.' && - !rr->target[1])) && - (rr->port < 0 || /* for same port */ - rr->port == conn->remote_port)) { - size_t i; - for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) && - alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) { - enum alpnid alpn = rr->alpns[i]; - if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count)) - continue; - switch(alpn) { - case ALPN_h3: - if(Curl_conn_may_http3(data, conn, conn->transport_wanted)) - break; /* not possible */ - if(data->state.http_neg.allowed & CURL_HTTP_V3x) { - CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - case ALPN_h2: - if(data->state.http_neg.allowed & CURL_HTTP_V2x) { - CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - case ALPN_h1: - if(data->state.http_neg.allowed & CURL_HTTP_V1x) { - CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - default: /* ignore */ - break; - } - } - } -#endif + if((conn->scheme->protocol != CURLPROTO_HTTPS) || + !conn->bits.tls_enable_alpn) + goto out; - /* Add preferred HTTP version ALPN first */ - if(data->state.http_neg.preferred && - (alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.preferred & data->state.http_neg.allowed)) { - enum alpnid alpn_pref = ALPN_none; - switch(data->state.http_neg.preferred) { - case CURL_HTTP_V3x: - if(!Curl_conn_may_http3(data, conn, conn->transport_wanted)) - alpn_pref = ALPN_h3; - break; - case CURL_HTTP_V2x: - alpn_pref = ALPN_h2; - break; - case CURL_HTTP_V1x: - alpn_pref = ALPN_h1; - break; - default: - break; - } - if(alpn_pref && - !cf_https_alpns_contain(alpn_pref, alpn_ids, alpn_count)) { - alpn_ids[alpn_count++] = alpn_pref; - } - } - - if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V3x) && - !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) { - result = Curl_conn_may_http3(data, conn, conn->transport_wanted); - if(!result) { - CURL_TRC_CF(data, cf, "adding wanted h3"); - alpn_ids[alpn_count++] = ALPN_h3; - } - else if(data->state.http_neg.wanted == CURL_HTTP_V3x) - goto out; /* only h3 allowed, not possible, error out */ - } - if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V2x) && - !cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) { - CURL_TRC_CF(data, cf, "adding wanted h2"); - alpn_ids[alpn_count++] = ALPN_h2; - } - else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V1x) && - !cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) { - CURL_TRC_CF(data, cf, "adding wanted h1"); - alpn_ids[alpn_count++] = ALPN_h1; - } - } - - /* If we identified ALPNs to use, install our filter. Otherwise, - * install nothing, so our call will use a default connect setup. */ - if(alpn_count) { - result = cf_http_connect_add(data, conn, sockindex, - alpn_ids, alpn_count, - conn->transport_wanted); - } + result = cf_hc_add(data, conn, sockindex, conn->transport_wanted); out: return result;
diff --git a/Utilities/cmcurl/lib/cf-https-connect.h b/Utilities/cmcurl/lib/cf-https-connect.h index df29c8d..3160c03 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.h +++ b/Utilities/cmcurl/lib/cf-https-connect.h
@@ -31,15 +31,9 @@ struct Curl_easy; struct connectdata; struct Curl_cftype; -struct Curl_dns_entry; extern struct Curl_cftype Curl_cft_http_connect; -CURLcode Curl_cf_http_connect_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool try_h3, bool try_h21); - CURLcode Curl_cf_https_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex);
diff --git a/Utilities/cmcurl/lib/cf-ip-happy.c b/Utilities/cmcurl/lib/cf-ip-happy.c index 05e9505..f67273e 100644 --- a/Utilities/cmcurl/lib/cf-ip-happy.c +++ b/Utilities/cmcurl/lib/cf-ip-happy.c
@@ -49,7 +49,9 @@ #include "urldata.h" #include "connect.h" #include "cfilters.h" +#include "cf-dns.h" #include "cf-ip-happy.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "multiif.h" #include "progress.h" @@ -90,9 +92,11 @@ } #ifdef UNITTESTS -/* used by unit2600.c */ -void Curl_debug_set_transport_provider(uint8_t transport, - cf_ip_connect_create *cf_create) +/* @unittest 2600 */ +UNITTEST void debug_set_transport_provider( + uint8_t transport, cf_ip_connect_create *cf_create); +UNITTEST void debug_set_transport_provider( + uint8_t transport, cf_ip_connect_create *cf_create) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { @@ -105,59 +109,46 @@ #endif /* UNITTESTS */ struct cf_ai_iter { - const struct Curl_addrinfo *head; - const struct Curl_addrinfo *last; + struct Curl_cfilter *cf; int ai_family; - int n; + unsigned int n; }; static void cf_ai_iter_init(struct cf_ai_iter *iter, - const struct Curl_addrinfo *list, + struct Curl_cfilter *cf, int ai_family) { - iter->head = list; + iter->cf = cf; iter->ai_family = ai_family; - iter->last = NULL; - iter->n = -1; + iter->n = 0; } -static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter) +static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter, + struct Curl_easy *data) { const struct Curl_addrinfo *addr; - if(iter->n < 0) { + + if(!iter->cf) + return NULL; + + addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex, + iter->ai_family, iter->n); + if(addr) iter->n++; - for(addr = iter->head; addr; addr = addr->ai_next) { - if(addr->ai_family == iter->ai_family) - break; - } - iter->last = addr; - } - else if(iter->last) { - iter->n++; - for(addr = iter->last->ai_next; addr; addr = addr->ai_next) { - if(addr->ai_family == iter->ai_family) - break; - } - iter->last = addr; - } - return iter->last; + return addr; } -static bool cf_ai_iter_has_more(struct cf_ai_iter *iter) +static bool cf_ai_iter_has_more(struct cf_ai_iter *iter, + struct Curl_easy *data) { - const struct Curl_addrinfo *addr = iter->last ? iter->last->ai_next : - ((iter->n < 0) ? iter->head : NULL); - while(addr) { - if(addr->ai_family == iter->ai_family) - return TRUE; - addr = addr->ai_next; - } - return FALSE; + return (iter->cf && + !!Curl_conn_dns_get_ai(data, iter->cf->sockindex, + iter->ai_family, iter->n)); } struct cf_ip_attempt { struct cf_ip_attempt *next; - const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */ + struct Curl_sockaddr_ex addr; struct Curl_cfilter *cf; /* current sub-cfilter connecting */ cf_ip_connect_create *cf_create; struct curltime started; /* start of current attempt */ @@ -184,7 +175,7 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, struct Curl_cfilter *cf, struct Curl_easy *data, - const struct Curl_addrinfo *addr, + struct Curl_sockaddr_ex *addr, int ai_family, uint8_t transport, cf_ip_connect_create *cf_create) @@ -198,14 +189,14 @@ if(!a) return CURLE_OUT_OF_MEMORY; - a->addr = addr; + a->addr = *addr; a->ai_family = ai_family; a->transport = transport; a->result = CURLE_OK; a->cf_create = cf_create; *pa = a; - result = a->cf_create(&a->cf, data, cf->conn, a->addr, transport); + result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport); if(result) goto out; @@ -227,7 +218,7 @@ struct Curl_easy *data, bool *connected) { - *connected = a->connected; + *connected = (bool)a->connected; if(!a->result && !*connected) { /* evaluate again */ a->result = Curl_conn_cf_connect(a->cf, data, connected); @@ -237,8 +228,12 @@ a->connected = TRUE; } } - else if(a->result == CURLE_WEIRD_SERVER_REPLY) - a->inconclusive = TRUE; + else { + if(a->result == CURLE_WEIRD_SERVER_REPLY) + a->inconclusive = TRUE; + if(a->cf) + Curl_conn_cf_discard_chain(&a->cf, data); + } } return a->result; } @@ -255,6 +250,7 @@ struct curltime last_attempt_started; timediff_t attempt_delay_ms; int last_attempt_ai_family; + uint32_t max_concurrent; uint8_t transport; }; @@ -262,19 +258,18 @@ struct Curl_cfilter *cf, struct Curl_easy *data) { - struct Curl_cfilter *cf_prev = a->cf; struct Curl_cfilter *wcf; CURLcode result; - /* When restarting, we tear down and existing filter *after* we - * started up the new one. This gives us a new socket number and - * probably a new local port. Which may prevent confusion. */ + if(a->cf) + Curl_conn_cf_discard_chain(&a->cf, data); + a->result = CURLE_OK; a->connected = FALSE; a->inconclusive = FALSE; a->cf = NULL; - result = a->cf_create(&a->cf, data, cf->conn, a->addr, a->transport); + result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport); if(!result) { bool dummy; /* the new filter might have sub-filters */ @@ -284,8 +279,6 @@ } a->result = cf_ip_attempt_connect(a, data, &dummy); } - if(cf_prev) - Curl_conn_cf_discard_chain(&cf_prev, data); return result; } @@ -303,54 +296,77 @@ bs->winner = NULL; } -static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version, - const struct Curl_addrinfo *addr_list, +static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, + struct Curl_cfilter *cf, cf_ip_connect_create *cf_create, uint8_t transport, - timediff_t attempt_delay_ms) + timediff_t attempt_delay_ms, + uint32_t max_concurrent) { memset(bs, 0, sizeof(*bs)); bs->cf_create = cf_create; bs->transport = transport; bs->attempt_delay_ms = attempt_delay_ms; + bs->max_concurrent = max_concurrent; bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */ if(transport == TRNSPRT_UNIX) { #ifdef USE_UNIX_SOCKETS - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_UNIX); + cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX); #else return CURLE_UNSUPPORTED_PROTOCOL; #endif } else { /* TCP/UDP/QUIC */ #ifdef USE_IPV6 - if(ip_version == CURL_IPRESOLVE_V6) - cf_ai_iter_init(&bs->addr_iter, NULL, AF_INET); - else - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET); - - if(ip_version == CURL_IPRESOLVE_V4) - cf_ai_iter_init(&bs->ipv6_iter, NULL, AF_INET6); - else - cf_ai_iter_init(&bs->ipv6_iter, addr_list, AF_INET6); -#else - (void)ip_version; - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET); + cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6); #endif + cf_ai_iter_init(&bs->addr_iter, cf, AF_INET); } return CURLE_OK; } +static void cf_ip_ballers_prune(struct cf_ip_ballers *bs, + struct Curl_cfilter *cf, + struct Curl_easy *data, + uint32_t max_concurrent) +{ + struct cf_ip_attempt *a = NULL, **panchor; + uint32_t ongoing = 0; + + for(a = bs->running; a; a = a->next) { + if(!a->result && !a->connected) + ++ongoing; + } + + panchor = &bs->running; + while(*panchor && (ongoing > max_concurrent)) { + a = *panchor; + if(!a->result && !a->connected) { + *panchor = a->next; + a->next = NULL; + cf_ip_attempt_free(a, data); + --ongoing; + CURL_TRC_CF(data, cf, "discarding oldest attempt to keep limit"); + } + else { + panchor = &a->next; + } + } +} + static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, struct Curl_cfilter *cf, struct Curl_easy *data, + bool dns_resolved, bool *connected) { CURLcode result = CURLE_OK; struct cf_ip_attempt *a = NULL, **panchor; bool do_more; timediff_t next_expire_ms; - int i, inconclusive, ongoing; + uint32_t inconclusive, ongoing; + VERBOSE(int i); if(bs->winner) return CURLE_OK; @@ -359,9 +375,9 @@ ongoing = inconclusive = 0; /* check if a running baller connects now */ - i = -1; + VERBOSE(i = -1); for(panchor = &bs->running; *panchor; panchor = &((*panchor)->next)) { - ++i; + VERBOSE(++i); a = *panchor; a->result = cf_ip_attempt_connect(a, data, connected); if(!a->result) { @@ -387,7 +403,7 @@ } if(bs->running) CURL_TRC_CF(data, cf, "checked connect attempts: " - "%d ongoing, %d inconclusive", ongoing, inconclusive); + "%u ongoing, %u inconclusive", ongoing, inconclusive); /* no attempt connected yet, start another one? */ if(!ongoing) { @@ -396,10 +412,10 @@ do_more = TRUE; } else { - bool more_possible = cf_ai_iter_has_more(&bs->addr_iter); + bool more_possible = cf_ai_iter_has_more(&bs->addr_iter, data); #ifdef USE_IPV6 if(!more_possible) - more_possible = cf_ai_iter_has_more(&bs->ipv6_iter); + more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data); #endif do_more = more_possible && (curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started) >= @@ -412,23 +428,42 @@ if(do_more) { /* start the next attempt if there is another ip address to try. * Alternate between address families when possible. */ - const struct Curl_addrinfo *addr = NULL; + const struct Curl_addrinfo *ai = NULL; int ai_family = 0; + CURL_TRC_CF(data, cf, "want to do more"); #ifdef USE_IPV6 if((bs->last_attempt_ai_family == AF_INET) || - !cf_ai_iter_has_more(&bs->addr_iter)) { - addr = cf_ai_iter_next(&bs->ipv6_iter); + !cf_ai_iter_has_more(&bs->addr_iter, data)) { + ai = cf_ai_iter_next(&bs->ipv6_iter, data); ai_family = bs->ipv6_iter.ai_family; + CURL_TRC_CF(data, cf, "check for next AAAA address: %s", + ai ? "found" : "none"); } #endif - if(!addr) { - addr = cf_ai_iter_next(&bs->addr_iter); + if(!ai) { + ai = cf_ai_iter_next(&bs->addr_iter, data); ai_family = bs->addr_iter.ai_family; + CURL_TRC_CF(data, cf, "check for next A address: %s", + ai ? "found" : "none"); } + /* We are (re-)starting attempts. We are not interested in + * keeping old failure information. The new attempt will either + * succeed or persist new failure. */ + Curl_reset_fail(data); - if(addr) { /* try another address */ - result = cf_ip_attempt_new(&a, cf, data, addr, ai_family, - bs->transport, bs->cf_create); + if(ai) { /* try another address */ + struct Curl_sockaddr_ex addr; + + /* Discard oldest to make room for new attempt */ + if(bs->max_concurrent) + cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1); + + result = Curl_socket_addr_from_ai(&addr, ai, bs->transport); + if(result) + goto out; + + result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family, + bs->transport, bs->cf_create); CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d", bs->running ? "next" : "first", (ai_family == AF_INET) ? "4" : "6", result); @@ -454,9 +489,9 @@ timediff_t delay_ms = bs->attempt_delay_ms - since_ms; if(delay_ms <= 0) { CURL_TRC_CF(data, cf, "all attempts inconclusive, restarting one"); - i = -1; + VERBOSE(i = -1); for(a = bs->running; a; a = a->next) { - ++i; + VERBOSE(++i); if(!a->inconclusive) continue; result = cf_ip_attempt_restart(a, cf, data); @@ -477,11 +512,11 @@ /* attempt timeout for restart has not expired yet */ goto out; } - else if(!ongoing) { + else if(!ongoing && dns_resolved) { /* no more addresses, no inconclusive attempts */ CURL_TRC_CF(data, cf, "no more attempts to try"); result = CURLE_COULDNT_CONNECT; - i = 0; + VERBOSE(i = 0); for(a = bs->running; a; a = a->next) { CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result); if(a->result) @@ -495,18 +530,18 @@ bool more_possible; /* when do we need to be called again? */ - next_expire_ms = Curl_timeleft_ms(data, TRUE); - if(next_expire_ms <= 0) { + next_expire_ms = Curl_timeleft_ms(data); + if(next_expire_ms < 0) { failf(data, "Connection timeout after %" FMT_OFF_T " ms", - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle)); + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle)); return CURLE_OPERATION_TIMEDOUT; } - more_possible = cf_ai_iter_has_more(&bs->addr_iter); + more_possible = cf_ai_iter_has_more(&bs->addr_iter, data); #ifdef USE_IPV6 if(!more_possible) - more_possible = cf_ai_iter_has_more(&bs->ipv6_iter); + more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data); #endif if(more_possible) { timediff_t expire_ms, elapsed_ms; @@ -537,7 +572,7 @@ *done = TRUE; for(a = bs->running; a; a = a->next) { bool bdone = FALSE; - if(a->shutdown) + if(a->shutdown || !a->cf) continue; a->result = a->cf->cft->do_shutdown(a->cf, data, &bdone); if(a->result || bdone) @@ -570,7 +605,7 @@ for(a = bs->running; a; a = a->next) { if(a->result) continue; - if(a->cf->cft->has_data_pending(a->cf, data)) + if(a->cf && a->cf->cft->has_data_pending(a->cf, data)) return TRUE; } return FALSE; @@ -586,7 +621,7 @@ memset(&tmax, 0, sizeof(tmax)); for(a = bs->running; a; a = a->next) { memset(&t, 0, sizeof(t)); - if(!a->cf->cft->query(a->cf, data, query, NULL, &t)) { + if(a->cf && !a->cf->cft->query(a->cf, data, query, NULL, &t)) { if((t.tv_sec || t.tv_usec) && curlx_ptimediff_us(&t, &tmax) > 0) tmax = t; } @@ -601,8 +636,8 @@ struct cf_ip_attempt *a; for(a = bs->running; a; a = a->next) { - if(!a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS, - &breply_ms, NULL)) { + if(a->cf && !a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS, + &breply_ms, NULL)) { if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms)) reply_ms = breply_ms; } @@ -622,6 +657,7 @@ cf_connect_state state; struct cf_ip_ballers ballers; struct curltime started; + BIT(dns_resolved); }; static CURLcode is_connected(struct Curl_cfilter *cf, @@ -632,12 +668,12 @@ struct connectdata *conn = cf->conn; CURLcode result; - result = cf_ip_ballers_run(&ctx->ballers, cf, data, connected); + result = cf_ip_ballers_run(&ctx->ballers, cf, data, + (bool)ctx->dns_resolved, connected); if(!result) return CURLE_OK; - - { + else { const char *hostname, *proxy_name = NULL; char viamsg[160]; #ifndef CURL_DISABLE_PROXY @@ -656,14 +692,14 @@ else #endif { - int port; + uint16_t port; if(cf->sockindex == SECONDARYSOCKET) port = conn->secondary_port; else if(cf->conn->bits.conn_to_port) port = conn->conn_to_port; else port = conn->remote_port; - curl_msnprintf(viamsg, sizeof(viamsg), "port %u", port); + curl_msnprintf(viamsg, sizeof(viamsg), "port %d", port); } failf(data, "Failed to connect to %s %s %s%s%safter " @@ -675,30 +711,24 @@ curlx_ptimediff_ms(Curl_pgrs_now(data), &data->progress.t_startsingle), curl_easy_strerror(result)); - } #ifdef SOCKETIMEDOUT - if(SOCKETIMEDOUT == data->state.os_errno) - result = CURLE_OPERATION_TIMEDOUT; + if(SOCKETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; #endif - return result; + return result; + } } -/* - * Connect to the given host with timeout, proxy or remote does not matter. - * There might be more than one IP address to try out. - */ -static CURLcode start_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) +#define IP_HE_MAX_CONCURRENT_ATTEMPTS 6 + +static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_ip_happy_ctx *ctx = cf->ctx; - struct Curl_dns_entry *dns = data->state.dns[cf->sockindex]; - if(!dns) - return CURLE_FAILED_INIT; - - if(Curl_timeleft_ms(data, TRUE) < 0) { + if(Curl_timeleft_ms(data) < 0) { /* a precaution, no need to continue if time already is up */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; @@ -706,9 +736,10 @@ CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport); ctx->started = *Curl_pgrs_now(data); - return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version, - dns->addr, ctx->cf_create, ctx->transport, - data->set.happy_eyeballs_timeout); + return cf_ip_ballers_init(&ctx->ballers, cf, + ctx->cf_create, ctx->transport, + data->set.happy_eyeballs_timeout, + IP_HE_MAX_CONCURRENT_ATTEMPTS); } static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf, @@ -721,6 +752,12 @@ cf_ip_ballers_clear(cf, data, &ctx->ballers); } +static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx) +{ + if(ctx) + curlx_free(ctx); +} + static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -748,7 +785,7 @@ if(!cf->connected) { result = cf_ip_ballers_pollset(&ctx->ballers, data, ps); - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); } return result; } @@ -760,6 +797,10 @@ struct cf_ip_happy_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; + /* -Werror=null-dereference finds false positives suddenly. */ + if(!data) + return CURLE_FAILED_INIT; + if(cf->connected) { *done = TRUE; return CURLE_OK; @@ -768,13 +809,24 @@ DEBUGASSERT(ctx); *done = FALSE; + if(!ctx->dns_resolved) { + result = Curl_conn_dns_result(cf->conn, cf->sockindex); + if(!result) + ctx->dns_resolved = TRUE; + else if(result == CURLE_AGAIN) { + result = CURLE_OK; + } + else /* real error */ + goto out; + } + switch(ctx->state) { case SCFST_INIT: DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data)); DEBUGASSERT(!cf->connected); - result = start_connect(cf, data); + result = cf_ip_happy_init(cf, data); if(result) - return result; + goto out; ctx->state = SCFST_WAITING; FALLTHROUGH(); case SCFST_WAITING: @@ -791,17 +843,18 @@ ctx->ballers.winner->cf = NULL; cf_ip_happy_ctx_clear(cf, data); Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); + /* whatever errors where reported by ballers, clear our errorbuf */ + Curl_reset_fail(data); - if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) + if(cf->conn->scheme->protocol & PROTO_FAMILY_SSH) Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_cf_is_verbose(cf, data)) { struct ip_quadruple ipquad; bool is_ipv6; if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) { const char *host; - int port; - Curl_conn_get_current_host(data, cf->sockindex, &host, &port); + Curl_conn_get_current_host(data, cf->sockindex, &host, NULL); CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u", host, ipquad.remote_ip, ipquad.remote_port); } @@ -814,6 +867,7 @@ *done = TRUE; break; } +out: return result; } @@ -887,14 +941,13 @@ CURL_TRC_CF(data, cf, "destroy"); if(ctx) { cf_ip_happy_ctx_clear(cf, data); + cf_ip_happy_ctx_destroy(ctx); } - /* release any resources held in state */ - Curl_safefree(ctx); } struct Curl_cftype Curl_cft_ip_happy = { "HAPPY-EYEBALLS", - 0, + CF_TYPE_SETUP, CURL_LOG_LVL_NONE, cf_ip_happy_destroy, cf_ip_happy_connect, @@ -944,8 +997,8 @@ out: if(result) { - Curl_safefree(*pcf); - curlx_free(ctx); + curlx_safefree(*pcf); + cf_ip_happy_ctx_destroy(ctx); } return result; }
diff --git a/Utilities/cmcurl/lib/cf-ip-happy.h b/Utilities/cmcurl/lib/cf-ip-happy.h index 71f2122..547ee4b 100644 --- a/Utilities/cmcurl/lib/cf-ip-happy.h +++ b/Utilities/cmcurl/lib/cf-ip-happy.h
@@ -25,6 +25,12 @@ ***************************************************************************/ #include "curl_setup.h" +struct connectdata; +struct Curl_addrinfo; +struct Curl_cfilter; +struct Curl_easy; +struct Curl_sockaddr_ex; + /** * Create a cfilter for making an "ip" connection to the * given address, using parameters from `conn`. The "ip" connection @@ -39,7 +45,7 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, @@ -48,9 +54,4 @@ extern struct Curl_cftype Curl_cft_ip_happy; -#ifdef UNITTESTS -void Curl_debug_set_transport_provider(uint8_t transport, - cf_ip_connect_create *cf_create); -#endif - #endif /* HEADER_CURL_IP_HAPPY_H */
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c index ce728f5..b99bcde 100644 --- a/Utilities/cmcurl/lib/cf-socket.c +++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -55,12 +55,12 @@ #endif #include "urldata.h" -#include "bufq.h" #include "curl_trc.h" #include "if2ip.h" #include "cfilters.h" #include "cf-socket.h" #include "connect.h" +#include "curl_addrinfo.h" #include "select.h" #include "multiif.h" #include "curlx/inet_pton.h" @@ -68,9 +68,10 @@ #include "conncache.h" #include "multihandle.h" #include "rand.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "system_win32.h" #include "curlx/nonblock.h" +#include "curlx/strcopy.h" #include "curlx/version_win32.h" #include "curlx/strerr.h" #include "curlx/strparse.h" @@ -83,7 +84,7 @@ #if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED) curl_socklen_t onoff = (curl_socklen_t)1; int level = IPPROTO_TCP; - char buffer[STRERROR_LEN]; + VERBOSE(char buffer[STRERROR_LEN]); if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) @@ -96,42 +97,20 @@ #endif } -#ifdef SO_NOSIGPIPE -/* The preferred method on macOS (10.2 and later) to prevent SIGPIPEs when - sending data to a dead peer (instead of relying on the 4th argument to send - being MSG_NOSIGNAL). Possibly also existing and in use on other BSD - systems? */ -static void nosigpipe(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t sockfd) -{ - int onoff = 1; - if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, - (void *)&onoff, sizeof(onoff)) < 0) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; - CURL_TRC_CF(data, cf, "Could not set SO_NOSIGPIPE: %s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#else - (void)cf; - (void)data; -#endif - } -} -#else -#define nosigpipe(x, y, z) Curl_nop_stmt -#endif - +#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \ + defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \ + defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD) #if defined(USE_WINSOCK) || \ - (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ - (defined(__DragonFly__) && __DragonFly_version < 500702) || \ - (defined(_WIN32) && !defined(TCP_KEEPIDLE)) + (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ + (defined(__DragonFly__) && __DragonFly_version < 500702) || \ + (defined(_WIN32) && !defined(TCP_KEEPIDLE)) /* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299 * use millisecond units. */ -#define KEEPALIVE_FACTOR(x) (x *= 1000) +#define KEEPALIVE_FACTOR(x) ((x) *= 1000) #else #define KEEPALIVE_FACTOR(x) #endif +#endif static void tcpkeepalive(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -258,8 +237,15 @@ * Note that the consequent probes will not be sent * at equal intervals on Solaris, but will be sent * using the exponential backoff algorithm. */ - optval = curlx_sltosi(data->set.tcp_keepcnt) * - curlx_sltosi(data->set.tcp_keepintvl); + { + int keepcnt = curlx_sltosi(data->set.tcp_keepcnt); + int keepintvl = curlx_sltosi(data->set.tcp_keepintvl); + + if(keepcnt > 0 && keepintvl > (INT_MAX / keepcnt)) + optval = INT_MAX; + else + optval = keepcnt * keepintvl; + } KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, (void *)&optval, sizeof(optval)) < 0) { @@ -280,51 +266,52 @@ } /** - * Assign the address `ai` to the Curl_sockaddr_ex `dest` and - * set the transport used. + * Assign the addrinfo `ai` to the Curl_sockaddr_ex `addr` with + * transport determining socktype and protocol. */ -static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest, - const struct Curl_addrinfo *ai, - uint8_t transport) +CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr, + const struct Curl_addrinfo *ai, + uint8_t transport) { /* - * The Curl_sockaddr_ex structure is basically libcurl's external API + * The Curl_sockaddr_ex structure is libcurl's external API * curl_sockaddr structure with enough space available to directly hold * any protocol-specific address structures. The variable declared here * will be used to pass / receive data to/from the fopensocket callback * if this has been set, before that, it is initialized from parameters. */ - dest->family = ai->ai_family; - switch(transport) { - case TRNSPRT_TCP: - dest->socktype = SOCK_STREAM; - dest->protocol = IPPROTO_TCP; - break; - case TRNSPRT_UNIX: - dest->socktype = SOCK_STREAM; - dest->protocol = IPPROTO_IP; - break; - default: /* UDP and QUIC */ - dest->socktype = SOCK_DGRAM; - dest->protocol = IPPROTO_UDP; - break; - } - dest->addrlen = (unsigned int)ai->ai_addrlen; + addr->family = ai->ai_family; + addr->socktype = Curl_socktype_for_transport(transport); + addr->protocol = Curl_protocol_for_transport(transport); + addr->addrlen = (unsigned int)ai->ai_addrlen; - DEBUGASSERT(dest->addrlen <= sizeof(dest->curl_sa_addrbuf)); - if(dest->addrlen > sizeof(dest->curl_sa_addrbuf)) + DEBUGASSERT(addr->addrlen <= sizeof(addr->curl_sa_addrbuf)); + if(addr->addrlen > sizeof(addr->curl_sa_addrbuf)) return CURLE_TOO_LARGE; - memcpy(&dest->curl_sa_addrbuf, ai->ai_addr, dest->addrlen); + memcpy(&addr->curl_sa_addrbuf, ai->ai_addr, addr->addrlen); return CURLE_OK; } +#ifdef USE_SO_NOSIGPIPE +int Curl_sock_nosigpipe(curl_socket_t sockfd) +{ + int onoff = 1; + return setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, + (void *)&onoff, sizeof(onoff)); +} +#endif /* USE_SO_NOSIGPIPE */ + static CURLcode socket_open(struct Curl_easy *data, struct Curl_sockaddr_ex *addr, curl_socket_t *sockfd) { char errbuf[STRERROR_LEN]; +#ifdef SOCK_CLOEXEC + addr->socktype |= SOCK_CLOEXEC; +#endif + DEBUGASSERT(data); DEBUGASSERT(data->conn); if(data->set.fopensocket) { @@ -344,7 +331,13 @@ Curl_set_in_callback(data, FALSE); } else { - /* opensocket callback not set, so simply create the socket now */ + /* opensocket callback not set, so create the socket now */ +#ifdef DEBUGBUILD + if((addr->family == AF_INET6) && getenv("CURL_DBG_SOCK_FAIL_IPV6")) { + failf(data, "CURL_DBG_SOCK_FAIL_IPV6: failed to open socket"); + return CURLE_COULDNT_CONNECT; + } +#endif *sockfd = CURL_SOCKET(addr->family, addr->socktype, addr->protocol); if((*sockfd == CURL_SOCKET_BAD) && (SOCKERRNO == SOCKENOMEM)) return CURLE_OUT_OF_MEMORY; @@ -357,11 +350,21 @@ return CURLE_COULDNT_CONNECT; } -#ifdef HAVE_FCNTL +#ifdef USE_SO_NOSIGPIPE + if(Curl_sock_nosigpipe(*sockfd) < 0) { + failf(data, "setsockopt enable SO_NOSIGPIPE: %s", + curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); + sclose(*sockfd); + *sockfd = CURL_SOCKET_BAD; + return CURLE_COULDNT_CONNECT; + } +#endif /* USE_SO_NOSIGPIPE */ + +#if defined(HAVE_FCNTL) && !defined(SOCK_CLOEXEC) if(fcntl(*sockfd, F_SETFD, FD_CLOEXEC) < 0) { failf(data, "fcntl set CLOEXEC: %s", curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); - close(*sockfd); + sclose(*sockfd); *sockfd = CURL_SOCKET_BAD; return CURLE_COULDNT_CONNECT; } @@ -398,7 +401,7 @@ /* if the caller does not want info back, use a local temp copy */ addr = &dummy; - result = sock_assign_addr(addr, ai, transport); + result = Curl_socket_addr_from_ai(addr, ai, transport); if(result) return result; @@ -408,7 +411,7 @@ static int socket_close(struct Curl_easy *data, struct connectdata *conn, int use_callback, curl_socket_t sock) { - if(CURL_SOCKET_BAD == sock) + if(sock == CURL_SOCKET_BAD) return 0; if(use_callback && conn && conn->fclosesocket) { @@ -440,37 +443,6 @@ return socket_close(data, conn, FALSE, sock); } -#ifdef USE_WINSOCK -/* When you run a program that uses the Windows Sockets API, you may - experience slow performance when you copy data to a TCP server. - - https://learn.microsoft.com/troubleshoot/windows-server/networking/slow-performance-copy-data-tcp-server-sockets-api - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - - The problem described in this knowledge-base is applied only to pre-Vista - Windows. Following function trying to detect OS version and skips - SO_SNDBUF adjustment for Windows Vista and above. -*/ - -void Curl_sndbuf_init(curl_socket_t sockfd) -{ - int val = CURL_MAX_WRITE_SIZE + 32; - int curval = 0; - int curlen = sizeof(curval); - - if(Curl_isVistaOrGreater) - return; - - if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) - if(curval > val) - return; - - setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); -} -#endif /* USE_WINSOCK */ - /* * Curl_parse_interface() * @@ -516,14 +488,14 @@ input += strlen(if_prefix); if(!*input) return CURLE_BAD_FUNCTION_ARGUMENT; - *iface = Curl_memdup0(input, len - strlen(if_prefix)); + *iface = curlx_memdup0(input, len - strlen(if_prefix)); return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY; } else if(!strncmp(host_prefix, input, strlen(host_prefix))) { input += strlen(host_prefix); if(!*input) return CURLE_BAD_FUNCTION_ARGUMENT; - *host = Curl_memdup0(input, len - strlen(host_prefix)); + *host = curlx_memdup0(input, len - strlen(host_prefix)); return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY; } else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) { @@ -533,11 +505,11 @@ host_part = memchr(input, '!', len); if(!host_part || !*(host_part + 1)) return CURLE_BAD_FUNCTION_ARGUMENT; - *iface = Curl_memdup0(input, host_part - input); + *iface = curlx_memdup0(input, host_part - input); if(!*iface) return CURLE_OUT_OF_MEMORY; ++host_part; - *host = Curl_memdup0(host_part, len - (host_part - input)); + *host = curlx_memdup0(host_part, len - (host_part - input)); if(!*host) { curlx_free(*iface); *iface = NULL; @@ -548,13 +520,14 @@ if(!*input) return CURLE_BAD_FUNCTION_ARGUMENT; - *dev = Curl_memdup0(input, len); + *dev = curlx_memdup0(input, len); return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY; } #ifndef CURL_DISABLE_BINDLOCAL static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd, int af, unsigned int scope) + curl_socket_t sockfd, int af, unsigned int scope, + uint8_t transport) { struct Curl_sockaddr_storage sa; struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ @@ -604,10 +577,10 @@ * 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. + * not as a hostname or ip address. * * The interface might be a VRF, eg: vrf-blue, which means it cannot be - * converted to an IP address and would fail Curl_if2ip. Simply try to + * converted to an IP address and would fail Curl_if2ip. Try to * use it straight away. */ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, @@ -665,21 +638,21 @@ * of the connection. The resolve functions should really be changed * to take a type parameter instead. */ - int ip_version = (af == AF_INET) ? - CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER; + uint8_t dns_queries = (af == AF_INET) ? + CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA); #ifdef USE_IPV6 if(af == AF_INET6) - ip_version = CURL_IPRESOLVE_V6; + dns_queries = CURL_DNSQ_AAAA; #endif - (void)Curl_resolv_blocking(data, host, 80, ip_version, &h); + (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h); if(h) { int h_af = h->addr->ai_family; /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_printable_address(h->addr, myhost, sizeof(myhost)); infof(data, "Name '%s' family %i resolved to '%s' family %i", host, af, myhost, h_af); - Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */ + Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */ if(af != h_af) { /* bad IP version combo, signal the caller to try another address family if available */ @@ -712,8 +685,8 @@ if(scope_ptr) { /* The "myhost" string either comes from Curl_if2ip or from Curl_printable_address. The latter returns only numeric scope - IDs and the former returns none at all. So the scope ID, if - present, is known to be numeric */ + IDs and the former returns none at all. Making the scope ID, + if present, known to be numeric */ curl_off_t scope_id; if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr), &scope_id, UINT_MAX)) @@ -822,7 +795,7 @@ * * "I do not have Rational Quantify, but the hint from his post was * ntdll::NtRemoveIoCompletion(). I would assume the SleepEx (or maybe - * just Sleep(0) would be enough?) would release whatever + * Sleep(0) would be enough?) would release whatever * mutex/critical-section the ntdll call is waiting on. * * Someone got to verify this on Win-NT 4.0, 2000." @@ -877,15 +850,12 @@ default: /* unknown error, fallthrough and try another address! */ -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)ipaddress; -#else { - char buffer[STRERROR_LEN]; + VERBOSE(char buffer[STRERROR_LEN]); infof(data, "Immediate connect fail for %s: %s", ipaddress, curlx_strerror(error, buffer, sizeof(buffer))); + NOVERBOSE((void)ipaddress); } -#endif data->state.os_errno = error; /* connect failed */ return CURLE_COULDNT_CONNECT; @@ -919,18 +889,13 @@ }; static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { - CURLcode result; - memset(ctx, 0, sizeof(*ctx)); ctx->sock = CURL_SOCKET_BAD; ctx->transport = transport; - - result = sock_assign_addr(&ctx->addr, ai, transport); - if(result) - return result; + ctx->addr = *addr; #ifdef DEBUGBUILD { @@ -961,14 +926,14 @@ } #endif - return result; + return CURLE_OK; } static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; - if(ctx && CURL_SOCKET_BAD != ctx->sock) { + if(ctx && ctx->sock != CURL_SOCKET_BAD) { CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock); if(ctx->sock == cf->conn->sock[cf->sockindex]) cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; @@ -1023,16 +988,15 @@ #ifdef HAVE_GETSOCKNAME if((ctx->sock != CURL_SOCKET_BAD) && - !(data->conn->handler->protocol & CURLPROTO_TFTP)) { + !(data->conn->scheme->protocol & CURLPROTO_TFTP)) { /* TFTP does not connect, so it cannot get the IP like this */ - - char buffer[STRERROR_LEN]; struct Curl_sockaddr_storage ssloc; curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); + VERBOSE(char buffer[STRERROR_LEN]); memset(&ssloc, 0, sizeof(ssloc)); if(getsockname(ctx->sock, (struct sockaddr *)&ssloc, &slen)) { - int error = SOCKERRNO; + VERBOSE(int error = SOCKERRNO); infof(data, "getsockname() failed with errno %d: %s", error, curlx_strerror(error, buffer, sizeof(buffer))); } @@ -1068,6 +1032,19 @@ return CURLE_OK; } +/* to figure out the type of the socket safely, remove the possibly ORed + bits before comparing */ +static int cf_socktype(int x) +{ +#ifdef SOCK_CLOEXEC + x &= ~SOCK_CLOEXEC; +#endif +#ifdef SOCK_NONBLOCK + x &= ~SOCK_NONBLOCK; +#endif + return x; +} + static CURLcode cf_socket_open(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1077,7 +1054,6 @@ CURLcode result = CURLE_COULDNT_CONNECT; bool is_tcp; - (void)data; DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); ctx->started_at = *Curl_pgrs_now(data); #ifdef SOCK_NONBLOCK @@ -1124,18 +1100,14 @@ #ifdef USE_IPV6 is_tcp = (ctx->addr.family == AF_INET || ctx->addr.family == AF_INET6) && - ctx->addr.socktype == SOCK_STREAM; + cf_socktype(ctx->addr.socktype) == SOCK_STREAM; #else is_tcp = (ctx->addr.family == AF_INET) && - ctx->addr.socktype == SOCK_STREAM; + cf_socktype(ctx->addr.socktype) == SOCK_STREAM; #endif if(is_tcp && data->set.tcp_nodelay) tcpnodelay(cf, data, ctx->sock); - nosigpipe(cf, data, ctx->sock); - - Curl_sndbuf_init(ctx->sock); - if(is_tcp && data->set.tcp_keepalive) tcpkeepalive(cf, data, ctx->sock); @@ -1163,7 +1135,8 @@ #endif ) { result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family, - Curl_ipv6_scope(&ctx->addr.curl_sa_addr)); + Curl_ipv6_scope(&ctx->addr.curl_sa_addr), + ctx->transport); if(result) { if(result == CURLE_UNSUPPORTED_PROTOCOL) { /* The address family is not supported on this interface. @@ -1196,7 +1169,7 @@ } } #endif - ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM); + ctx->sock_connected = (cf_socktype(ctx->addr.socktype) != SOCK_DGRAM); out: if(result) { if(ctx->sock != CURL_SOCKET_BAD) { @@ -1277,7 +1250,6 @@ CURLcode result = CURLE_COULDNT_CONNECT; int rc = 0; - (void)data; if(cf->connected) { *done = TRUE; return CURLE_OK; @@ -1297,12 +1269,12 @@ } /* Connect TCP socket */ - rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); + rc = do_connect(cf, data, (bool)cf->conn->bits.tcp_fastopen); error = SOCKERRNO; set_local_ip(cf, data); CURL_TRC_CF(data, cf, "local address %s port %d...", ctx->ip.local_ip, ctx->ip.local_port); - if(-1 == rc) { + if(rc == -1) { result = socket_connect_result(data, ctx->ip.remote_ip, error); goto out; } @@ -1334,30 +1306,31 @@ } } else if(rc & CURL_CSELECT_ERR) { + CURL_TRC_CF(data, cf, "poll/select error on fd=%" FMT_SOCKET_T, ctx->sock); (void)verifyconnect(ctx->sock, &ctx->error); result = CURLE_COULDNT_CONNECT; } out: if(result) { + VERBOSE(char buffer[STRERROR_LEN]); + set_local_ip(cf, data); if(ctx->error) { - set_local_ip(cf, data); data->state.os_errno = ctx->error; SET_SOCKERRNO(ctx->error); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - { - char buffer[STRERROR_LEN]; - infof(data, "connect to %s port %u from %s port %d failed: %s", - ctx->ip.remote_ip, ctx->ip.remote_port, - ctx->ip.local_ip, ctx->ip.local_port, - curlx_strerror(ctx->error, buffer, sizeof(buffer))); - } -#endif + VERBOSE(curlx_strerror(ctx->error, buffer, sizeof(buffer))); + } + else { + VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed"))); } if(ctx->sock != CURL_SOCKET_BAD) { socket_close(data, cf->conn, TRUE, ctx->sock); ctx->sock = CURL_SOCKET_BAD; } + infof(data, "connect to %s port %u from %s port %d failed: %s", + ctx->ip.remote_ip, ctx->ip.remote_port, + ctx->ip.local_ip, ctx->ip.local_port, + curlx_strerror(ctx->error, buffer, sizeof(buffer))); *done = FALSE; } return result; @@ -1429,8 +1402,8 @@ struct cf_socket_ctx *ctx = cf->ctx; curl_socket_t fdsave; ssize_t rv; - size_t orig_len = len; CURLcode result = CURLE_OK; + VERBOSE(size_t orig_len = len); (void)eos; *pnwritten = 0; @@ -1483,7 +1456,7 @@ (SOCKEINPROGRESS == sockerr) #endif ) { - /* this is just a case of EWOULDBLOCK */ + /* EWOULDBLOCK */ result = CURLE_AGAIN; } else { @@ -1525,10 +1498,9 @@ } } if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) { - size_t orig_len = len; - len = ctx->recv_max; CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes", - orig_len, len); + len, ctx->recv_max); + len = ctx->recv_max; } #endif @@ -1549,7 +1521,7 @@ (EAGAIN == sockerr) || (SOCKEINTR == sockerr) #endif ) { - /* this is just a case of EWOULDBLOCK */ + /* EWOULDBLOCK */ result = CURLE_AGAIN; } else { @@ -1627,7 +1599,7 @@ int r; *input_pending = FALSE; - (void)data; + if(!ctx || ctx->sock == CURL_SOCKET_BAD) return FALSE; @@ -1739,7 +1711,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { struct cf_socket_ctx *ctx = NULL; @@ -1749,7 +1721,7 @@ (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_TCP); - if(!ai) { + if(!addr) { result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } @@ -1760,7 +1732,7 @@ goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1769,8 +1741,8 @@ out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; @@ -1827,7 +1799,7 @@ NOLINTNEXTLINE(clang-analyzer-unix.StdCLibraryFunctions) */ rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, (curl_socklen_t)ctx->addr.addrlen); - if(-1 == rc) { + if(rc == -1) { return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO); } ctx->sock_connected = TRUE; @@ -1905,7 +1877,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { struct cf_socket_ctx *ctx = NULL; @@ -1921,7 +1893,7 @@ goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1930,8 +1902,8 @@ out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; @@ -1959,7 +1931,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { struct cf_socket_ctx *ctx = NULL; @@ -1975,7 +1947,7 @@ goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1984,8 +1956,8 @@ out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; @@ -2004,7 +1976,7 @@ #endif /* check if the generic timeout possibly is set shorter */ - other_ms = Curl_timeleft_ms(data, FALSE); + other_ms = Curl_timeleft_ms(data); if(other_ms && (other_ms < timeout_ms)) /* note that this also works fine for when other_ms happens to be negative due to it already having elapsed */ @@ -2113,7 +2085,7 @@ s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *)&add, &size); #endif - if(CURL_SOCKET_BAD == s_accepted) { + if(s_accepted == CURL_SOCKET_BAD) { failf(data, "Error accept()ing server connect: %s", curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); return CURLE_FTP_ACCEPT_FAILED; @@ -2221,8 +2193,8 @@ out: if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; }
diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h index 9db492e..40c001c 100644 --- a/Utilities/cmcurl/lib/cf-socket.h +++ b/Utilities/cmcurl/lib/cf-socket.h
@@ -35,8 +35,8 @@ struct ip_quadruple; /* - * The Curl_sockaddr_ex structure is basically libcurl's external API - * curl_sockaddr structure with enough space available to directly hold any + * The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr + * structure with enough space available to directly hold any * protocol-specific address structures. The variable declared here will be * used to pass / receive data to/from the fopensocket callback if this has * been set, before that, it is initialized from parameters. @@ -60,6 +60,10 @@ CURLcode Curl_parse_interface(const char *input, char **dev, char **iface, char **host); +CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr, + const struct Curl_addrinfo *ai, + uint8_t transport); + /* * Create a socket based on info from 'conn' and 'ai'. * @@ -73,24 +77,14 @@ uint8_t transport, curl_socket_t *sockfd); +#ifdef USE_SO_NOSIGPIPE +/* Set SO_NOSIGPIPE on socket, return < 0 on error. */ +int Curl_sock_nosigpipe(curl_socket_t sockfd); +#endif + int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sock); -#ifdef USE_WINSOCK -/* When you run a program that uses the Windows Sockets API, you may - experience slow performance when you copy data to a TCP server. - - https://learn.microsoft.com/troubleshoot/windows-server/networking/slow-performance-copy-data-tcp-server-sockets-api - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - -*/ -void Curl_sndbuf_init(curl_socket_t sockfd); -#else -#define Curl_sndbuf_init(y) Curl_nop_stmt -#endif - /** * Creates a cfilter that opens a TCP socket to the given address * when calling its `connect` implementation. @@ -101,7 +95,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); /** @@ -114,7 +108,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); /** @@ -127,7 +121,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); /**
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index b91d30c..6d7d8ef 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c
@@ -33,12 +33,10 @@ #include "select.h" #include "curlx/strparse.h" -static void cf_cntrl_update_info(struct Curl_easy *data, - struct connectdata *conn); - #ifdef UNITTESTS -/* used by unit2600.c */ -void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) +/* @unittest 2600 */ +UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); +UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) { cf->connected = FALSE; if(cf->next) @@ -55,9 +53,6 @@ return CURLE_OK; } -static void conn_report_connect_stats(struct Curl_cfilter *cf, - struct Curl_easy *data); - CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -121,6 +116,37 @@ CURLE_UNKNOWN_OPTION; } +#ifdef CURLVERBOSE +static void conn_trc_filters(struct Curl_easy *data, + int sockindex, + const char *info) +{ + if(CURL_TRC_M_is_verbose(data) && data->conn) { + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + + if(cf) { + struct dynbuf msg; + CURLcode result = CURLE_OK; + + curlx_dyn_init(&msg, 1024); + result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex); + for(; cf && !result; cf = cf->next) { + result = curlx_dyn_addf(&msg, "[%s%s]", + cf->connected ? "" : "!", cf->cft->name); + } + if(!result) + CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg)); + else + CURL_TRC_M(data, "%s [%d] error %d tracing chain", + info, sockindex, result); + curlx_dyn_free(&msg); + } + else + CURL_TRC_M(data, "%s [%d][-]", info, sockindex); + } +} +#endif /* CURLVERBOSE */ + void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, struct Curl_easy *data) { @@ -142,22 +168,22 @@ } void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, int index) + struct connectdata *conn, int sockindex) { - Curl_conn_cf_discard_chain(&conn->cfilter[index], data); + Curl_conn_cf_discard_chain(&conn->cfilter[sockindex], data); } -void Curl_conn_close(struct Curl_easy *data, int index) +void Curl_conn_close(struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf; DEBUGASSERT(data->conn); /* it is valid to call that without filters being present */ - cf = data->conn->cfilter[index]; + cf = data->conn->cfilter[sockindex]; if(cf) { cf->cft->do_close(cf, data); } - Curl_shutdown_clear(data, index); + Curl_shutdown_clear(data, sockindex); } CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) @@ -183,8 +209,6 @@ *done = FALSE; if(!Curl_shutdown_started(data, sockindex)) { - CURL_TRC_M(data, "shutdown start on%s connection", - sockindex ? " secondary" : ""); Curl_shutdown_start(data, sockindex, 0); } else { @@ -217,14 +241,14 @@ return result; } -CURLcode Curl_cf_recv(struct Curl_easy *data, int num, char *buf, +CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, size_t len, size_t *pnread) { struct Curl_cfilter *cf; DEBUGASSERT(data); DEBUGASSERT(data->conn); - cf = data->conn->cfilter[num]; + cf = data->conn->cfilter[sockindex]; while(cf && !cf->connected) cf = cf->next; if(cf) @@ -235,19 +259,19 @@ return CURLE_FAILED_INIT; } -CURLcode Curl_cf_send(struct Curl_easy *data, int num, - const uint8_t *mem, size_t len, bool eos, +CURLcode Curl_cf_send(struct Curl_easy *data, int sockindex, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct Curl_cfilter *cf; DEBUGASSERT(data); DEBUGASSERT(data->conn); - cf = data->conn->cfilter[num]; + cf = data->conn->cfilter[sockindex]; while(cf && !cf->connected) cf = cf->next; if(cf) { - return cf->cft->do_send(cf, data, mem, len, eos, pnwritten); + return cf->cft->do_send(cf, data, buf, len, eos, pnwritten); } failf(data, "send: no filter connected"); DEBUGASSERT(0); @@ -336,18 +360,17 @@ void Curl_conn_cf_add(struct Curl_easy *data, struct connectdata *conn, - int index, + int sockindex, struct Curl_cfilter *cf) { - (void)data; DEBUGASSERT(conn); DEBUGASSERT(!cf->conn); DEBUGASSERT(!cf->next); - cf->next = conn->cfilter[index]; + cf->next = conn->cfilter[sockindex]; cf->conn = conn; - cf->sockindex = index; - conn->cfilter[index] = cf; + cf->sockindex = sockindex; + conn->cfilter[sockindex] = cf; CURL_TRC_CF(data, cf, "added"); } @@ -429,7 +452,7 @@ return CURLE_RECV_ERROR; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static CURLcode cf_verboseconnect(struct Curl_easy *data, struct Curl_cfilter *cf) { @@ -452,6 +475,69 @@ } #endif +static CURLcode cf_cntrl_all(struct connectdata *conn, + struct Curl_easy *data, + bool ignore_result, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + size_t i; + + for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) { + result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result, + event, arg1, arg2); + if(!ignore_result && result) + break; + } + return result; +} + +static void cf_cntrl_update_info(struct Curl_easy *data, + struct connectdata *conn) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); +} + +/** + * Update connection statistics + */ +static void conn_report_connect_stats(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + if(cf) { + struct curltime connected; + struct curltime appconnected; + + memset(&connected, 0, sizeof(connected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); + if(connected.tv_sec || connected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); + + memset(&appconnected, 0, sizeof(appconnected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); + if(appconnected.tv_sec || appconnected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); + } +} + +static void conn_remove_setup_filters(struct Curl_easy *data, + int sockindex) +{ + struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex]; + while(*anchor) { + struct Curl_cfilter *cf = *anchor; + if(cf->connected && (cf->cft->flags & CF_TYPE_SETUP)) { + *anchor = cf->next; + cf->next = NULL; + CURL_TRC_CF(data, cf, "removing connected setup filter"); + cf->cft->destroy(cf, data); + curlx_free(cf); + } + else + anchor = &cf->next; + } +} + CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, bool blocking, @@ -469,13 +555,18 @@ if(!CONN_SOCK_IDX_VALID(sockindex)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(data->conn->scheme->flags & PROTOPT_NONETWORK) { + *done = TRUE; + return CURLE_OK; + } + cf = data->conn->cfilter[sockindex]; if(!cf) { *done = FALSE; return CURLE_FAILED_INIT; } - *done = cf->connected; + *done = (bool)cf->connected; if(*done) return CURLE_OK; @@ -499,13 +590,15 @@ cf_cntrl_update_info(data, data->conn); conn_report_connect_stats(cf, data); data->conn->keepalive = *Curl_pgrs_now(data); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - result = cf_verboseconnect(data, cf); -#endif + VERBOSE(result = cf_verboseconnect(data, cf)); + VERBOSE(conn_trc_filters(data, sockindex, "connected")); + conn_remove_setup_filters(data, sockindex); + VERBOSE(conn_trc_filters(data, sockindex, "reduced to")); goto out; } else if(result) { CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result); + VERBOSE(conn_trc_filters(data, sockindex, "failed to connect")); conn_report_connect_stats(cf, data); goto out; } @@ -514,7 +607,7 @@ goto out; else { /* check allowed time left */ - const timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE); + const timediff_t timeout_ms = Curl_timeleft_ms(data); curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); int rc; @@ -572,8 +665,8 @@ return FALSE; cf = conn->cfilter[sockindex]; if(cf) - return cf->connected; - else if(conn->handler->flags & PROTOPT_NONETWORK) + return (bool)cf->connected; + else if(conn->scheme->flags & PROTOPT_NONETWORK) return TRUE; return FALSE; } @@ -615,14 +708,16 @@ bool Curl_conn_get_ssl_info(struct Curl_easy *data, struct connectdata *conn, int sockindex, + int query, struct curl_tlssessioninfo *info) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; if(Curl_conn_is_ssl(conn, sockindex)) { struct Curl_cfilter *cf = conn->cfilter[sockindex]; - CURLcode result = cf ? cf->cft->query(cf, data, CF_QUERY_SSL_INFO, - NULL, (void *)info) : CURLE_UNKNOWN_OPTION; + CURLcode result = cf ? + cf->cft->query(cf, data, query, NULL, (void *)info) : + CURLE_UNKNOWN_OPTION; return !result; } return FALSE; @@ -663,6 +758,41 @@ return Curl_conn_cf_get_transport(cf, data); } +int Curl_socktype_for_transport(uint8_t transport) +{ + switch(transport) { + case TRNSPRT_TCP: + return SOCK_STREAM; + case TRNSPRT_UNIX: + return SOCK_STREAM; + default: /* UDP and QUIC */ + return SOCK_DGRAM; + } +} + +int Curl_protocol_for_transport(uint8_t transport) +{ + switch(transport) { + case TRNSPRT_TCP: + return IPPROTO_TCP; + case TRNSPRT_UNIX: + return IPPROTO_IP; + default: /* UDP and QUIC */ + return IPPROTO_UDP; + } +} + +bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft->flags & CF_TYPE_HTTPSRR) + return TRUE; + } + return FALSE; +} + const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, struct connectdata *conn) { @@ -766,12 +896,19 @@ return result; } +/* + * Return values: + * -1 = error + * 0 = timeout + * N = number of structures with non zero revent fields + */ int Curl_conn_cf_poll(struct Curl_cfilter *cf, struct Curl_easy *data, timediff_t timeout_ms) { struct easy_pollset ps; - int result; + int rc; + CURLcode result; DEBUGASSERT(cf); DEBUGASSERT(data); @@ -780,20 +917,24 @@ result = Curl_conn_cf_adjust_pollset(cf, data, &ps); if(!result) - result = Curl_pollset_poll(data, &ps, timeout_ms); + rc = Curl_pollset_poll(data, &ps, timeout_ms); + else + rc = -1; Curl_pollset_cleanup(&ps); - return result; + return rc; } void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, const char **phost, int *pport) { struct Curl_cfilter *cf, *cf_proxy = NULL; + int portarg = -1; if(!data->conn) { DEBUGASSERT(0); *phost = ""; - *pport = -1; + if(pport) + *pport = -1; return; } @@ -809,12 +950,14 @@ * to an interim host and any authentication or other things apply * to this interim host and port. */ if(!cf_proxy || cf_proxy->cft->query(cf_proxy, data, CF_QUERY_HOST_PORT, - pport, CURL_UNCONST(phost))) { + &portarg, CURL_UNCONST(phost))) { /* Everything connected or query unsuccessful, the overall * connection's destination is the answer */ *phost = data->conn->host.name; - *pport = data->conn->remote_port; + portarg = data->conn->remote_port; } + if(pport) + *pport = portarg; } CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, @@ -837,7 +980,7 @@ CURLcode result = CURLE_OK; for(; cf; cf = cf->next) { - if(Curl_cf_def_cntrl == cf->cft->cntrl) + if(cf->cft->cntrl == Curl_cf_def_cntrl) continue; result = cf->cft->cntrl(cf, data, event, arg1, arg2); if(!ignore_result && result) @@ -923,23 +1066,6 @@ return cf ? cf_get_remote_addr(cf, data) : NULL; } -static CURLcode cf_cntrl_all(struct connectdata *conn, - struct Curl_easy *data, - bool ignore_result, - int event, int arg1, void *arg2) -{ - CURLcode result = CURLE_OK; - size_t i; - - for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) { - result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result, - event, arg1, arg2); - if(!ignore_result && result) - break; - } - return result; -} - CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data) { return cf_cntrl_all(data->conn, data, FALSE, CF_CTRL_DATA_SETUP, 0, NULL); @@ -977,34 +1103,6 @@ CF_CTRL_DATA_PAUSE, do_pause, NULL); } -static void cf_cntrl_update_info(struct Curl_easy *data, - struct connectdata *conn) -{ - cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); -} - -/** - * Update connection statistics - */ -static void conn_report_connect_stats(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - if(cf) { - struct curltime connected; - struct curltime appconnected; - - memset(&connected, 0, sizeof(connected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); - if(connected.tv_sec || connected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); - - memset(&appconnected, 0, sizeof(appconnected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); - if(appconnected.tv_sec || appconnected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); - } -} - bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, bool *input_pending) { @@ -1040,7 +1138,7 @@ result = cf ? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT, &n, NULL) : CURLE_UNKNOWN_OPTION; /* If no filter answered the query, the default is a non-multiplexed - * connection with limit 1. Otherwise, the the query may return 0 + * connection with limit 1. Otherwise, the query may return 0 * for connections that are in shutdown, e.g. server HTTP/2 GOAWAY. */ return (result || n < 0) ? 1 : (size_t)n; } @@ -1071,23 +1169,23 @@ } CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, - char *buf, size_t blen, size_t *pnread) + char *buf, size_t len, size_t *pnread) { DEBUGASSERT(data); DEBUGASSERT(data->conn); if(!CONN_SOCK_IDX_VALID(sockindex)) return CURLE_BAD_FUNCTION_ARGUMENT; if(data && data->conn && data->conn->recv[sockindex]) - return data->conn->recv[sockindex](data, sockindex, buf, blen, pnread); + return data->conn->recv[sockindex](data, sockindex, buf, len, pnread); *pnread = 0; return CURLE_FAILED_INIT; } CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t blen, bool eos, + const void *buf, size_t len, bool eos, size_t *pnwritten) { - size_t write_len = blen; + size_t write_len = len; DEBUGASSERT(data); DEBUGASSERT(data->conn); @@ -1100,13 +1198,14 @@ const char *p = getenv("CURL_SMALLSENDS"); if(p) { curl_off_t altsize; - if(!curlx_str_number(&p, &altsize, write_len)) + if(!curlx_str_number(&p, &altsize, write_len)) { write_len = (size_t)altsize; + if(write_len != len) + eos = FALSE; + } } } #endif - if(write_len != blen) - eos = FALSE; if(data && data->conn && data->conn->send[sockindex]) return data->conn->send[sockindex](data, sockindex, buf, write_len, eos, pnwritten);
diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h index 9faf01d..ac56737 100644 --- a/Utilities/cmcurl/lib/cfilters.h +++ b/Utilities/cmcurl/lib/cfilters.h
@@ -117,7 +117,7 @@ */ /* data event arg1 arg2 return */ #define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */ -/* unused now 5 */ +/* unused now 5 */ #define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */ #define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */ #define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */ @@ -170,7 +170,7 @@ #define CF_QUERY_STREAM_ERROR 6 /* error code - */ #define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */ #define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */ -#define CF_QUERY_HTTP_VERSION 9 /* number (10/11/20/30) - */ +#define CF_QUERY_HTTP_VERSION 9 /* number (10/11/20/30) - */ /* pass in a `const struct Curl_sockaddr_ex **` as `pres2`. Gets set * to NULL when not connected. */ #define CF_QUERY_REMOTE_ADDR 10 /* - `Curl_sockaddr_ex *` */ @@ -199,12 +199,17 @@ * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles * CF_TYPE_PROXY provides proxying * CF_TYPE_HTTP implement a version of the HTTP protocol + * CF_TYPE_SETUP filter is only needed for connection setup and + * can be removed once connected + * CF_TYPE_HTTPSRR filter that wants HTTPS-RR information */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) #define CF_TYPE_MULTIPLEX (1 << 2) #define CF_TYPE_PROXY (1 << 3) #define CF_TYPE_HTTP (1 << 4) +#define CF_TYPE_SETUP (1 << 5) +#define CF_TYPE_HTTPSRR (1 << 6) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { @@ -312,8 +317,7 @@ * Remove and destroy all filters at chain `sockindex` on connection `conn`. */ void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); + struct connectdata *conn, int sockindex); CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -346,10 +350,17 @@ unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, struct Curl_easy *data); +int Curl_socktype_for_transport(uint8_t transport); +int Curl_protocol_for_transport(uint8_t transport); + const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data); -#define CURL_CF_SSL_DEFAULT -1 +/* The filter (or one of its sub-filters) wants HTTPS-RR information. */ +bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, + struct Curl_easy *data); + +#define CURL_CF_SSL_DEFAULT (-1) #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 @@ -389,12 +400,13 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); /* - * Fill `info` with information about the TLS instance securing - * the connection when available, otherwise e.g. when - * Curl_conn_is_ssl() is FALSE, return FALSE. + * Fill `info` with information about the TLS instance securing the connection + * when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return + * FALSE. 'query' should be CF_QUERY_SSL_INFO or CF_QUERY_SSL_CTX_INFO. */ bool Curl_conn_get_ssl_info(struct Curl_easy *data, struct connectdata *conn, int sockindex, + int query, struct curl_tlssessioninfo *info); CURLcode Curl_conn_get_ip_info(struct Curl_easy *data, @@ -430,7 +442,7 @@ /** * Shutdown the connection at `sockindex` non-blocking, using timeout * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS. - * Will return CURLE_OK and *done == FALSE if not finished. + * Return CURLE_OK and *done == FALSE if not finished. */ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done); @@ -566,10 +578,6 @@ struct connectdata *conn, int sockindex); -#ifdef UNITTESTS -void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); -#endif - /** * Get the remote hostname and port that the connection is currently * talking to (or will talk to). @@ -605,18 +613,17 @@ /* * Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. - * Will return CURLE_AGAIN iff blocked on receiving. + * Return CURLE_AGAIN iff blocked on receiving. */ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, - char *buf, size_t buffersize, - size_t *pnread); + char *buf, size_t len, size_t *pnread); /* * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. - * Will return CURLE_AGAIN iff blocked on sending. + * Return CURLE_AGAIN iff blocked on sending. */ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t blen, bool eos, + const void *buf, size_t len, bool eos, size_t *pnwritten); /**
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index 1a3dba7..33fb68b 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c
@@ -34,9 +34,6 @@ #include "conncache.h" #include "curl_share.h" #include "sigpipe.h" -#include "connect.h" -#include "select.h" -#include "curlx/strparse.h" #define CPOOL_IS_LOCKED(c) ((c) && (c)->locked) @@ -52,7 +49,7 @@ } \ } while(0) -#define CPOOL_UNLOCK(c,d) \ +#define CPOOL_UNLOCK(c, d) \ do { \ if(c) { \ DEBUGASSERT((c)->locked); \ @@ -69,11 +66,6 @@ char dest[1]; /* destination of bundle, allocated to keep dest_len bytes */ }; -static void cpool_discard_conn(struct cpool *cpool, - struct Curl_easy *data, - struct connectdata *conn, - bool aborted); - static struct cpool_bundle *cpool_bundle_create(const char *dest) { struct cpool_bundle *bundle; @@ -189,26 +181,74 @@ } } +static void cpool_discard_conn(struct cpool *cpool, + struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + bool done = FALSE; + + DEBUGASSERT(data); + DEBUGASSERT(!data->conn); + DEBUGASSERT(cpool); + DEBUGASSERT(!conn->bits.in_cpool); + + /* + * If this connection is not marked to force-close, leave it open if there + * are other users of it + */ + if(CONN_INUSE(conn) && !aborted) { + CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T + " still in use by %u transfers", conn->connection_id, + conn->attached_xfers); + return; + } + + /* treat the connection as aborted in CONNECT_ONLY situations, we do + * not know what the APP did with it. */ + if(conn->bits.connect_only) + aborted = TRUE; + conn->bits.aborted = aborted; + + /* We do not shutdown dead connections. The term 'dead' can be misleading + * here, as we also mark errored connections/transfers as 'dead'. + * If we do a shutdown for an aborted transfer, the server might think + * it was successful otherwise (for example an ftps: upload). This is + * not what we want. */ + if(aborted) + done = TRUE; + if(!done) { + /* Attempt to shutdown the connection right away. */ + Curl_cshutdn_run_once(cpool->idata, conn, &done); + } + + if(done || !data->multi) + Curl_cshutdn_terminate(cpool->idata, conn, FALSE); + else + Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn); +} + void Curl_cpool_destroy(struct cpool *cpool) { if(cpool && cpool->initialised && cpool->idata) { struct connectdata *conn; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx pipe_ctx; CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections", cpool->share ? "[SHARE] " : "", cpool->num_conn); /* Move all connections to the shutdown list */ - sigpipe_init(&pipe_st); + sigpipe_init(&pipe_ctx); CPOOL_LOCK(cpool, cpool->idata); conn = cpool_get_first(cpool); + if(conn) + sigpipe_apply(cpool->idata, &pipe_ctx); while(conn) { cpool_remove_conn(cpool, conn); - sigpipe_apply(cpool->idata, &pipe_st); cpool_discard_conn(cpool, cpool->idata, conn, FALSE); conn = cpool_get_first(cpool); } CPOOL_UNLOCK(cpool, cpool->idata); - sigpipe_restore(&pipe_st); + sigpipe_restore(&pipe_ctx); Curl_hash_destroy(&cpool->dest2bundle); } } @@ -314,7 +354,7 @@ for(curr = Curl_llist_head(&bundle->conns); curr; curr = Curl_node_next(curr)) { conn = Curl_node_elem(curr); - if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only) + if(CONN_INUSE(conn) || conn->bits.close || conn->bits.connect_only) continue; /* Set higher score for the age passed since the connection was used */ score = curlx_ptimediff_ms(pnow, &conn->lastused); @@ -335,7 +375,7 @@ size_t dest_limit = 0; size_t total_limit = 0; size_t shutdowns; - int result = CPOOL_LIMIT_OK; + int res = CPOOL_LIMIT_OK; if(!cpool) return CPOOL_LIMIT_OK; @@ -385,7 +425,7 @@ shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination); } if((live + shutdowns) >= dest_limit) { - result = CPOOL_LIMIT_DEST; + res = CPOOL_LIMIT_DEST; goto out; } } @@ -413,14 +453,14 @@ shutdowns = Curl_cshutdn_count(cpool->idata); } if((cpool->num_conn + shutdowns) >= total_limit) { - result = CPOOL_LIMIT_TOTAL; + res = CPOOL_LIMIT_TOTAL; goto out; } } out: CPOOL_UNLOCK(cpool, cpool->idata); - return result; + return res; } CURLcode Curl_cpool_add(struct Curl_easy *data, @@ -518,7 +558,7 @@ struct cpool *cpool = cpool_get_instance(data); bool kept = TRUE; - if(!data) + if(!data || !data->multi) return kept; if(!data->multi->maxconnects) { @@ -560,7 +600,7 @@ { struct cpool *cpool = cpool_get_instance(data); struct cpool_bundle *bundle; - bool result = FALSE; + bool found = FALSE; DEBUGASSERT(cpool); DEBUGASSERT(conn_cb); @@ -579,64 +619,17 @@ curr = Curl_node_next(curr); if(conn_cb(conn, userdata)) { - result = TRUE; + found = TRUE; break; } } } if(done_cb) { - result = done_cb(result, userdata); + found = done_cb(userdata); } CPOOL_UNLOCK(cpool, data); - return result; -} - -static void cpool_discard_conn(struct cpool *cpool, - struct Curl_easy *data, - struct connectdata *conn, - bool aborted) -{ - bool done = FALSE; - - DEBUGASSERT(data); - DEBUGASSERT(!data->conn); - DEBUGASSERT(cpool); - DEBUGASSERT(!conn->bits.in_cpool); - - /* - * If this connection is not marked to force-close, leave it open if there - * are other users of it - */ - if(CONN_INUSE(conn) && !aborted) { - CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T - " still in use by %u transfers", conn->connection_id, - conn->attached_xfers); - return; - } - - /* treat the connection as aborted in CONNECT_ONLY situations, we do - * not know what the APP did with it. */ - if(conn->connect_only) - aborted = TRUE; - conn->bits.aborted = aborted; - - /* We do not shutdown dead connections. The term 'dead' can be misleading - * here, as we also mark errored connections/transfers as 'dead'. - * If we do a shutdown for an aborted transfer, the server might think - * it was successful otherwise (for example an ftps: upload). This is - * not what we want. */ - if(aborted) - done = TRUE; - if(!done) { - /* Attempt to shutdown the connection right away. */ - Curl_cshutdn_run_once(cpool->idata, conn, &done); - } - - if(done || !data->multi) - Curl_cshutdn_terminate(cpool->idata, conn, FALSE); - else - Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn); + return found; } void Curl_conn_terminate(struct Curl_easy *data, @@ -672,7 +665,7 @@ /* treat the connection as aborted in CONNECT_ONLY situations, * so no graceful shutdown is attempted. */ - if(conn->connect_only) + if(conn->bits.connect_only) aborted = TRUE; if(data->multi) { @@ -808,7 +801,7 @@ struct connectdata *conn, void *param) { struct cpool_do_conn_ctx *dctx = param; - (void)data; + if(conn->connection_id == dctx->id) { dctx->cb(conn, data, dctx->cbdata); return 1; @@ -858,7 +851,6 @@ static int cpool_reap_no_reuse(struct Curl_easy *data, struct connectdata *conn, void *param) { - (void)data; (void)param; if(!CONN_INUSE(conn) && conn->bits.no_reuse) { Curl_conn_terminate(data, conn, FALSE);
diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h index 9b392af..7cee4d4 100644 --- a/Utilities/cmcurl/lib/conncache.h +++ b/Utilities/cmcurl/lib/conncache.h
@@ -68,7 +68,7 @@ size_t size); /* Destroy all connections and free all members */ -void Curl_cpool_destroy(struct cpool *connc); +void Curl_cpool_destroy(struct cpool *cpool); /* Init the transfer to be used within its connection pool. * Assigns `data->id`. */ @@ -84,8 +84,8 @@ /** * Return if the pool has reached its configured limits for adding - * the given connection. Will try to discard the oldest, idle - * connections to make space. + * the given connection. Try to discard the oldest, idle connections + * to make space. */ #define CPOOL_LIMIT_OK 0 #define CPOOL_LIMIT_DEST 1 @@ -98,7 +98,7 @@ void *userdata); /* Act on the result of the find, may override it. */ -typedef bool Curl_cpool_done_match_cb(bool result, void *userdata); +typedef bool Curl_cpool_done_match_cb(void *userdata); /** * Find a connection in the pool matching `destination`.
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index 6e17834..b13d496 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c
@@ -54,6 +54,7 @@ #include "strerror.h" #include "cfilters.h" #include "connect.h" +#include "cf-dns.h" #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" @@ -61,7 +62,7 @@ #include "multiif.h" #include "curlx/inet_ntop.h" #include "curlx/strparse.h" -#include "vtls/vtls.h" /* for vtsl cfilters */ +#include "vtls/vtls.h" /* for vtls cfilters */ #include "progress.h" #include "conncache.h" #include "multihandle.h" @@ -96,29 +97,34 @@ #endif /* - * Curl_timeleft_ms() returns the amount of milliseconds left allowed for the + * timeleft_now_ms() returns the amount of milliseconds left allowed for the * transfer/connection. If the value is 0, there is no timeout (ie there is * infinite time left). If the value is negative, the timeout time has already * elapsed. - * @param data the transfer to check on - * @param duringconnect TRUE iff connect timeout is also taken into account. - * @unittest: 1303 + * + * @unittest 1303 */ -timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, - const struct curltime *pnow, - bool duringconnect) +UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data, + const struct curltime *pnow); +UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data, + const struct curltime *pnow) { timediff_t timeleft_ms = 0; timediff_t ctimeleft_ms = 0; - timediff_t ctimeout_ms; - /* The duration of a connect and the total transfer are calculated from two - different time-stamps. It can end up with the total timeout being reached - before the connect timeout expires and we must acknowledge whichever - timeout that is reached first. The total timeout is set per entire - operation, while the connect timeout is set per connect. */ - if((!data->set.timeout || data->set.connect_only) && !duringconnect) + if(Curl_shutdown_started(data, FIRSTSOCKET)) + return Curl_shutdown_timeleft(data, data->conn, FIRSTSOCKET); + else if(Curl_is_connecting(data)) { + timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ? + data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT; + ctimeleft_ms = ctimeout_ms - + curlx_ptimediff_ms(pnow, &data->progress.t_startsingle); + if(!ctimeleft_ms) + ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ + } + else if(!data->set.timeout || data->set.connect_only) { return 0; /* no timeout in place or checked, return "no limit" */ + } if(data->set.timeout) { timeleft_ms = data->set.timeout - @@ -127,25 +133,16 @@ timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ } - if(!duringconnect) - return timeleft_ms; /* no connect check, this is it */ - ctimeout_ms = (data->set.connecttimeout > 0) ? - data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT; - ctimeleft_ms = ctimeout_ms - - curlx_ptimediff_ms(pnow, &data->progress.t_startsingle); if(!ctimeleft_ms) - ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ - if(!timeleft_ms) - return ctimeleft_ms; /* no general timeout, this is it */ - - /* return minimal time left or max amount already expired */ - return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms; + return timeleft_ms; + else if(!timeleft_ms) + return ctimeleft_ms; + return CURLMIN(ctimeleft_ms, timeleft_ms); } -timediff_t Curl_timeleft_ms(struct Curl_easy *data, - bool duringconnect) +timediff_t Curl_timeleft_ms(struct Curl_easy *data) { - return Curl_timeleft_now_ms(data, Curl_pgrs_now(data), duringconnect); + return timeleft_now_ms(data, Curl_pgrs_now(data)); } void Curl_shutdown_start(struct Curl_easy *data, int sockindex, @@ -162,6 +159,8 @@ /* Set a timer, unless we operate on the admin handle */ if(data->mid) Curl_expire_ex(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN); + CURL_TRC_M(data, "shutdown start on%s connection", + sockindex ? " secondary" : ""); } timediff_t Curl_shutdown_timeleft(struct Curl_easy *data, @@ -204,8 +203,11 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex) { - struct curltime *pt = &data->conn->shutdown.start[sockindex]; - return (pt->tv_sec > 0) || (pt->tv_usec > 0); + if(data->conn) { + struct curltime *pt = &data->conn->shutdown.start[sockindex]; + return (pt->tv_sec > 0) || (pt->tv_usec > 0); + } + return FALSE; } /* retrieves ip address and port from a sockaddr structure. note it calls @@ -299,7 +301,7 @@ */ void Curl_conncontrol(struct connectdata *conn, int ctrl /* see defines in header */ -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) , const char *reason #endif ) @@ -309,7 +311,7 @@ associated with a transfer. */ bool closeit, is_multiplex; DEBUGASSERT(conn); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) (void)reason; /* useful for debugging */ #endif is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); @@ -317,7 +319,7 @@ ((ctrl == CONNCTRL_STREAM) && !is_multiplex); if((ctrl == CONNCTRL_STREAM) && is_multiplex) ; /* stream signal on multiplex conn never affects close state */ - else if((bit)closeit != conn->bits.close) { + else if((curl_bit)closeit != conn->bits.close) { conn->bits.close = closeit; /* the only place in the source code that should assign this bit */ } @@ -345,7 +347,6 @@ { struct cf_setup_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - struct Curl_dns_entry *dns = data->state.dns[cf->sockindex]; if(cf->connected) { *done = TRUE; @@ -354,8 +355,6 @@ /* connect current sub-chain */ connect_sub_chain: - if(!dns) - return CURLE_FAILED_INIT; if(cf->next && !cf->next->connected) { result = Curl_conn_cf_connect(cf->next, data, done); @@ -375,7 +374,27 @@ /* sub-chain connected, do we need to add more? */ #ifndef CURL_DISABLE_PROXY if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - result = Curl_cf_socks_proxy_insert_after(cf, data); + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + const char *hostname = + cf->conn->bits.httpproxy ? + cf->conn->http_proxy.host.name : + cf->conn->bits.conn_to_host ? + cf->conn->conn_to_host.name : + cf->sockindex == SECONDARYSOCKET ? + cf->conn->secondaryhostname : cf->conn->host.name; + uint16_t port = + cf->conn->bits.httpproxy ? cf->conn->http_proxy.port : + cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port : + cf->conn->bits.conn_to_port ? cf->conn->conn_to_port : + cf->conn->remote_port; + const char *user = cf->conn->socks_proxy.user; + const char *passwd = cf->conn->socks_proxy.passwd; + + result = Curl_cf_socks_proxy_insert_after( + cf, data, hostname, port, cf->conn->ip_version, + cf->conn->socks_proxy.proxytype, user, passwd); if(result) return result; ctx->state = CF_SETUP_CNNCT_SOCKS; @@ -410,7 +429,7 @@ #ifndef CURL_DISABLE_PROXY if(data->set.haproxyprotocol) { if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - failf(data, "haproxy protocol not support with SSL " + failf(data, "haproxy protocol not supported with SSL " "encryption in place (QUIC?)"); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -428,8 +447,8 @@ #ifdef USE_SSL if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || (ctx->ssl_mode != CURL_CF_SSL_DISABLE && - cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */ - && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ + cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ result = Curl_cf_ssl_insert_after(cf, data); if(result) return result; @@ -465,14 +484,13 @@ { struct cf_setup_ctx *ctx = cf->ctx; - (void)data; CURL_TRC_CF(data, cf, "destroy"); - Curl_safefree(ctx); + curlx_safefree(ctx); } struct Curl_cftype Curl_cft_setup = { "SETUP", - 0, + CF_TYPE_SETUP, CURL_LOG_LVL_NONE, cf_setup_destroy, cf_setup_connect, @@ -562,17 +580,15 @@ int ssl_mode) { CURLcode result = CURLE_OK; + uint8_t dns_queries; DEBUGASSERT(data); - DEBUGASSERT(conn->handler); - DEBUGASSERT(dns); - - Curl_resolv_unlink(data, &data->state.dns[sockindex]); - data->state.dns[sockindex] = dns; + DEBUGASSERT(conn->scheme); + DEBUGASSERT(!conn->cfilter[sockindex]); #ifndef CURL_DISABLE_HTTP if(!conn->cfilter[sockindex] && - conn->handler->protocol == CURLPROTO_HTTPS) { + conn->scheme->protocol == CURLPROTO_HTTPS) { DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); result = Curl_cf_https_setup(data, conn, sockindex); if(result) @@ -588,13 +604,34 @@ goto out; } + dns_queries = Curl_resolv_dns_queries(data, conn->ip_version); +#ifdef USE_HTTPSRR + if(sockindex == FIRSTSOCKET) + dns_queries |= CURL_DNSQ_HTTPS; +#endif + result = Curl_cf_dns_add(data, conn, sockindex, dns_queries, + conn->transport_wanted, dns); DEBUGASSERT(conn->cfilter[sockindex]); out: - if(result) - Curl_resolv_unlink(data, &data->state.dns[sockindex]); return result; } +#ifdef USE_UNIX_SOCKETS +const char *Curl_conn_get_unix_path(struct connectdata *conn) +{ + const char *unix_path = conn->unix_domain_socket; + +#ifndef CURL_DISABLE_PROXY + if(!unix_path && conn->bits.proxy && conn->socks_proxy.host.name && + !strncmp(UNIX_SOCKET_PREFIX "/", + conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) + unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; +#endif + + return unix_path; +} +#endif /* USE_UNIX_SOCKETS */ + void Curl_conn_set_multiplex(struct connectdata *conn) { if(!conn->bits.multiplex) {
diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h index 01e9dfc..40f1c9c 100644 --- a/Utilities/cmcurl/lib/connect.h +++ b/Utilities/cmcurl/lib/connect.h
@@ -25,6 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" +#include "hostip.h" #include "curlx/timeval.h" struct Curl_dns_entry; @@ -32,15 +33,11 @@ struct Curl_str; enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); -enum alpnid Curl_str2alpnid(const struct Curl_str *str); +enum alpnid Curl_str2alpnid(const struct Curl_str *cstr); /* generic function that returns how much time there is left to run, according to the timeouts set */ -timediff_t Curl_timeleft_ms(struct Curl_easy *data, - bool duringconnect); -timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, - const struct curltime *pnow, - bool duringconnect); +timediff_t Curl_timeleft_ms(struct Curl_easy *data); #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ @@ -78,7 +75,7 @@ char *addr, uint16_t *port); /* - * Curl_conncontrol() marks the end of a connection/stream. The 'closeit' + * Curl_conncontrol() marks the end of a connection/stream. The 'ctrl' * argument specifies if it is the end of a connection or a stream. * * For stream-based protocols (such as HTTP/2), a stream close will not cause @@ -94,17 +91,17 @@ #define CONNCTRL_STREAM 2 void Curl_conncontrol(struct connectdata *conn, - int closeit -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + int ctrl +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) , const char *reason #endif ); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) #define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM, y) #define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y) #define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y) -#else /* if !DEBUGBUILD || CURL_DISABLE_VERBOSE_STRINGS */ +#else /* !DEBUGBUILD || !CURLVERBOSE */ #define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM) #define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION) #define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP) @@ -129,6 +126,15 @@ /* Set conn to allow multiplexing. */ void Curl_conn_set_multiplex(struct connectdata *conn); +#ifdef USE_UNIX_SOCKETS +#ifndef CURL_DISABLE_PROXY +#define UNIX_SOCKET_PREFIX "localhost" +#endif +const char *Curl_conn_get_unix_path(struct connectdata *conn); +#else +#define Curl_conn_get_unix_path(c) NULL +#endif + extern struct Curl_cftype Curl_cft_setup; #endif /* HEADER_CURL_CONNECT_H */
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index 9cd3e24..dea9a0f 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -31,13 +31,13 @@ #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG /* Ignore -Wvla warnings in brotli headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvla" #endif #include <brotli/decode.h> -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif #endif @@ -193,7 +193,7 @@ done = FALSE; break; case Z_BUF_ERROR: - /* No more data to flush: just exit loop. */ + /* No more data to flush: exit loop. */ break; case Z_STREAM_END: result = process_trailer(data, zp); @@ -600,9 +600,7 @@ /* supported content decoders only for transfer encodings */ static const struct Curl_cwtype * const transfer_unencoders[] = { -#ifndef CURL_DISABLE_HTTP &Curl_httpchunk_unencoder, -#endif NULL }; @@ -624,6 +622,9 @@ result = curlx_dyn_add(&enc, ce->name); } } + if(!result && !curlx_dyn_len(&enc)) + result = curlx_dyn_add(&enc, CONTENT_ENCODING_DEFAULT); + if(!result) return curlx_dyn_ptr(&enc); return NULL; @@ -679,8 +680,8 @@ for(cep = transfer_unencoders; *cep; cep++) { const struct Curl_cwtype *ce = *cep; if((curl_strnequal(name, ce->name, len) && !ce->name[len]) || - (ce->alias && curl_strnequal(name, ce->alias, len) - && !ce->alias[len])) + (ce->alias && curl_strnequal(name, ce->alias, len) && + !ce->alias[len])) return ce; } } @@ -750,7 +751,7 @@ } if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to more than %u content encodings", + failf(data, "Reject response due to more than %d content encodings", MAX_ENCODE_STACK); return CURLE_BAD_CONTENT_ENCODING; }
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index c807222..0f822ea 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c
@@ -36,9 +36,8 @@ #include "curl_get_line.h" #include "curl_memrchr.h" #include "parsedate.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "llist.h" -#include "bufref.h" #include "curlx/strparse.h" /* number of seconds in 400 days */ @@ -245,7 +244,7 @@ if(len > 1 && cookie_path[len - 1] == '/') len--; - return Curl_memdup0(cookie_path, len); + return curlx_memdup0(cookie_path, len); } /* @@ -264,7 +263,7 @@ len++; newstr = ""; } - *str = Curl_memdup0(newstr, len); + *str = curlx_memdup0(newstr, len); if(!*str) return CURLE_OUT_OF_MEMORY; return CURLE_OK; @@ -288,10 +287,10 @@ /* * If the earliest expiration timestamp in the jar is in the future we can * skip scanning the whole jar and instead exit early as there will not be - * any cookies to evict. If we need to evict however, reset the - * next_expiration counter in order to track the next one. In case the - * recorded first expiration is the max offset, then perform the safe - * fallback of checking all cookies. + * any cookies to evict. If we need to evict, reset the next_expiration + * counter in order to track the next one. In case the recorded first + * expiration is the max offset, then perform the safe fallback of checking + * all cookies. */ if(now < ci->next_expiration && ci->next_expiration != CURL_OFF_T_MAX) @@ -331,7 +330,7 @@ return FALSE; else { /* there must be a dot present, but that dot must not be a trailing dot */ - char *dot = memchr(domain, '.', len); + const char *dot = memchr(domain, '.', len); if(dot) { size_t i = dot - domain; if((len - i) > 1) @@ -348,7 +347,7 @@ cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E - But Firefox and Chrome as of June 2022 accept space, comma and double-quotes + Yet, Firefox and Chrome as of June 2022 accept space, comma and double-quotes fine. The prime reason for filtering out control bytes is that some HTTP servers return 400 for requests that contain such. */ @@ -367,15 +366,14 @@ /* The maximum length we accept a date string for the 'expire' keyword. The standard date formats are within the 30 bytes range. This adds an extra - margin just to make sure it realistically works with what is used out - there. + margin to make sure it realistically works with what is used out there. */ #define MAX_DATE_LENGTH 80 -#define COOKIE_NAME 0 -#define COOKIE_VALUE 1 +#define COOKIE_NAME 0 +#define COOKIE_VALUE 1 #define COOKIE_DOMAIN 2 -#define COOKIE_PATH 3 +#define COOKIE_PATH 3 #define COOKIE_PIECES 4 /* the list above */ @@ -398,7 +396,7 @@ /* No path was given in the header line, set the default */ const char *endslash = strrchr(path, '/'); if(endslash) - plen = (endslash - path + 1); /* include end slash */ + plen = endslash - path + 1; /* include end slash */ else plen = strlen(path); } @@ -425,18 +423,17 @@ /* this function return errors on OOM etc, not on plain cookie format problems */ -static CURLcode -parse_cookie_header(struct Curl_easy *data, - struct Cookie *co, - struct CookieInfo *ci, - bool *okay, /* if the cookie was fine */ - const char *ptr, - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is - set, used to get default path for - the cookie unless set */ - bool secure) /* TRUE if connection is over secure - origin */ +static CURLcode parse_cookie_header( + struct Curl_easy *data, + struct Cookie *co, + struct CookieInfo *ci, + bool *okay, /* if the cookie was fine */ + const char *ptr, + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is + set, used to get default path for + the cookie unless set */ + bool secure) /* TRUE if connection is over secure origin */ { /* This line was read off an HTTP-header */ time_t now = 0; @@ -463,6 +460,13 @@ sep = TRUE; /* a '=' was used */ if(!curlx_str_cspn(&ptr, &val, ";\r\n")) curlx_str_trimblanks(&val); + + /* Reject cookies with a TAB inside the value */ + if(curlx_strlen(&val) && + memchr(curlx_str(&val), '\t', curlx_strlen(&val))) { + infof(data, "cookie contains TAB, dropping"); + return CURLE_OK; + } } else curlx_str_init(&val); @@ -491,13 +495,6 @@ return CURLE_OK; } - /* Reject cookies with a TAB inside the value */ - if(curlx_strlen(&val) && - memchr(curlx_str(&val), '\t', curlx_strlen(&val))) { - infof(data, "cookie contains TAB, dropping"); - return CURLE_OK; - } - /* Check if we have a reserved prefix set. */ if(!strncmp("__Secure-", curlx_str(&name), 9)) co->prefix_secure = TRUE; @@ -694,7 +691,7 @@ ptr++; len--; } - co->domain = Curl_memdup0(ptr, len); + co->domain = curlx_memdup0(ptr, len); if(!co->domain) return CURLE_OUT_OF_MEMORY; break; @@ -737,7 +734,7 @@ return CURLE_OK; break; case 5: - co->name = Curl_memdup0(ptr, len); + co->name = curlx_memdup0(ptr, len); if(!co->name) return CURLE_OUT_OF_MEMORY; else { @@ -749,7 +746,7 @@ } break; case 6: - co->value = Curl_memdup0(ptr, len); + co->value = curlx_memdup0(ptr, len); if(!co->value) return CURLE_OUT_OF_MEMORY; break; @@ -783,7 +780,7 @@ * dereference it. */ DEBUGF(infof(data, "PSL check set-cookie '%s' for domain=%s in %s", - co->name, co->domain, domain)); + co->name, co->domain, domain)); if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { bool acceptable = FALSE; char lcase[256]; @@ -814,7 +811,7 @@ (void)co; (void)domain; DEBUGF(infof(data, "NO PSL to check set-cookie '%s' for domain=%s in %s", - co->name, co->domain, domain)); + co->name, co->domain, domain)); #endif return FALSE; } @@ -932,17 +929,16 @@ * IPv6 address. * */ -CURLcode -Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *ci, - bool httpheader, /* TRUE if HTTP header-style line */ - bool noexpire, /* if TRUE, skip remove_expired() */ - const char *lineptr, /* first character of the line */ - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is set, - used to get default path for the cookie - unless set */ - bool secure) /* TRUE if connection is over secure origin */ +CURLcode Curl_cookie_add( + struct Curl_easy *data, + struct CookieInfo *ci, + bool httpheader, /* TRUE if HTTP header-style line */ + bool noexpire, /* if TRUE, skip remove_expired() */ + const char *lineptr, /* first character of the line */ + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is set, used + to get default path for the cookie unless set */ + bool secure) /* TRUE if connection is over secure origin */ { struct Cookie comem; struct Cookie *co; @@ -1008,7 +1004,7 @@ goto fail; /* clone the stack struct into heap */ - co = Curl_memdup(&comem, sizeof(comem)); + co = curlx_memdup(&comem, sizeof(comem)); if(!co) { co = &comem; result = CURLE_OUT_OF_MEMORY; @@ -1108,8 +1104,17 @@ fp = curlx_fopen(file, "rb"); if(!fp) infof(data, "WARNING: failed to open cookie file \"%s\"", file); - else - handle = fp; + else { + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) != -1) && S_ISDIR(stat.st_mode)) { + curlx_fclose(fp); + fp = NULL; + infof(data, "WARNING: cookie filename points to a directory: \"%s\"", + file); + } + else + handle = fp; + } } } @@ -1169,7 +1174,7 @@ data->state.cookie_engine = TRUE; while(list) { result = cookie_load(data, list->data, data->cookies, - data->set.cookiesession); + (bool)data->set.cookiesession); if(result) break; list = list->next; @@ -1234,7 +1239,7 @@ bool Curl_secure_context(struct connectdata *conn, const char *host) { - return conn->handler->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS) || + return conn->scheme->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS) || curl_strequal("localhost", host) || !strcmp(host, "127.0.0.1") || !strcmp(host, "::1"); @@ -1315,8 +1320,8 @@ if(matches) { /* * Now we need to make sure that if there is a name appearing more than - * once, the longest specified path version comes first. To make this - * the swiftest way, we just sort them all based on path length. + * once, the longest specified path version comes first. To make this the + * swiftest way, we sort them all based on path length. */ struct Cookie **array; size_t i; @@ -1466,7 +1471,7 @@ FILE *out = NULL; bool use_stdout = FALSE; char *tempstore = NULL; - CURLcode error = CURLE_OK; + CURLcode result = CURLE_OK; if(!ci) /* no cookie engine alive */ @@ -1481,8 +1486,8 @@ use_stdout = TRUE; } else { - error = Curl_fopen(data, filename, &out, &tempstore); - if(error) + result = Curl_fopen(data, filename, &out, &tempstore); + if(result) goto error; } @@ -1499,7 +1504,7 @@ array = curlx_calloc(1, sizeof(struct Cookie *) * ci->numcookies); if(!array) { - error = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -1519,7 +1524,7 @@ char *format_ptr = get_netscape_format(array[i]); if(!format_ptr) { curlx_free(array); - error = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto error; } curl_mfprintf(out, "%s\n", format_ptr); @@ -1533,7 +1538,7 @@ curlx_fclose(out); out = NULL; if(tempstore && curlx_rename(tempstore, filename)) { - error = CURLE_WRITE_ERROR; + result = CURLE_WRITE_ERROR; goto error; } } @@ -1553,7 +1558,7 @@ unlink(tempstore); curlx_free(tempstore); } - return error; + return result; } static struct curl_slist *cookie_list(struct Curl_easy *data)
diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h index cbc7b74..f66e0ef 100644 --- a/Utilities/cmcurl/lib/cookie.h +++ b/Utilities/cmcurl/lib/cookie.h
@@ -111,26 +111,29 @@ bool Curl_secure_context(struct connectdata *conn, const char *host); CURLcode Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *c, bool header, - bool noexpiry, const char *lineptr, - const char *domain, const char *path, + struct CookieInfo *ci, + bool httpheader, + bool noexpire, + const char *lineptr, + const char *domain, + const char *path, bool secure) WARN_UNUSED_RESULT; CURLcode Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn, bool *okay, const char *host, struct Curl_llist *list) WARN_UNUSED_RESULT; -void Curl_cookie_clearall(struct CookieInfo *cookies); -void Curl_cookie_clearsess(struct CookieInfo *cookies); +void Curl_cookie_clearall(struct CookieInfo *ci); +void Curl_cookie_clearsess(struct CookieInfo *ci); #if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES) -#define Curl_cookie_list(x) NULL +#define Curl_cookie_list(x) NULL #define Curl_cookie_loadfiles(x) CURLE_OK -#define Curl_cookie_init() NULL -#define Curl_cookie_run(x) Curl_nop_stmt -#define Curl_cookie_cleanup(x) Curl_nop_stmt +#define Curl_cookie_init() NULL +#define Curl_cookie_run(x) Curl_nop_stmt +#define Curl_cookie_cleanup(x) Curl_nop_stmt #define Curl_flush_cookies(x, y) Curl_nop_stmt #else void Curl_flush_cookies(struct Curl_easy *data, bool cleanup); -void Curl_cookie_cleanup(struct CookieInfo *c); +void Curl_cookie_cleanup(struct CookieInfo *ci); struct CookieInfo *Curl_cookie_init(void); struct curl_slist *Curl_cookie_list(struct Curl_easy *data); CURLcode Curl_cookie_loadfiles(struct Curl_easy *data) WARN_UNUSED_RESULT;
diff --git a/Utilities/cmcurl/lib/cshutdn.c b/Utilities/cmcurl/lib/cshutdn.c index 84e5b2a..27b4a9f 100644 --- a/Utilities/cmcurl/lib/cshutdn.c +++ b/Utilities/cmcurl/lib/cshutdn.c
@@ -43,14 +43,14 @@ { if(!conn->bits.shutdown_handler) { - if(conn->handler && conn->handler->disconnect) { + if(conn->scheme && conn->scheme->run->disconnect) { /* Some disconnect handlers do a blocking wait on server responses. * FTP/IMAP/SMTP and SFTP are among them. When using the internal * handle, set an overall short timeout so we do not hang for the * default 120 seconds. */ if(data->state.internal) { data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS; - (void)Curl_pgrsTime(data, TIMER_STARTOP); + Curl_pgrsTime(data, TIMER_STARTOP); } /* This is set if protocol-specific cleanups should be made */ @@ -59,7 +59,7 @@ conn->connection_id, conn->bits.aborted)); /* There are protocol handlers that block on retrieving * server responses here (FTP). Set a short timeout. */ - conn->handler->disconnect(data, conn, conn->bits.aborted); + conn->scheme->run->disconnect(data, conn, (bool)conn->bits.aborted); } conn->bits.shutdown_handler = TRUE; @@ -76,6 +76,10 @@ /* We expect to be attached when called */ DEBUGASSERT(data->conn == conn); + if(!Curl_shutdown_started(data, FIRSTSOCKET)) { + Curl_shutdown_start(data, FIRSTSOCKET, 0); + } + cshutdn_run_conn_handler(data, conn); if(conn->bits.shutdown_filters) { @@ -83,14 +87,14 @@ return; } - if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) + if(!conn->bits.connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1); else { r1 = CURLE_OK; done1 = TRUE; } - if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) + if(!conn->bits.connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2); else { r2 = CURLE_OK; @@ -176,13 +180,13 @@ } if(e) { - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; conn = Curl_node_elem(e); Curl_node_remove(e); - sigpipe_init(&pipe_st); - sigpipe_apply(data, &pipe_st); + sigpipe_init(&sigpipe_ctx); + sigpipe_apply(data, &sigpipe_ctx); Curl_cshutdn_terminate(data, conn, FALSE); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); return TRUE; } return FALSE; @@ -222,7 +226,8 @@ } static void cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_sigpipe_ctx *sigpipe_ctx) { struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list); struct Curl_llist_node *enext; @@ -235,6 +240,7 @@ CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections", Curl_llist_count(&cshutdn->list)); + sigpipe_apply(data, sigpipe_ctx); while(e) { enext = Curl_node_next(e); conn = Curl_node_elem(e); @@ -263,20 +269,19 @@ { struct curltime started = *Curl_pgrs_now(data); struct Curl_llist_node *e; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; DEBUGASSERT(cshutdn); DEBUGASSERT(data); CURL_TRC_M(data, "[SHUTDOWN] shutdown all"); - sigpipe_init(&pipe_st); - sigpipe_apply(data, &pipe_st); + sigpipe_init(&sigpipe_ctx); while(Curl_llist_head(&cshutdn->list)) { timediff_t spent_ms; int remain_ms; - cshutdn_perform(cshutdn, data); + cshutdn_perform(cshutdn, data, &sigpipe_ctx); if(!Curl_llist_head(&cshutdn->list)) { CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly"); @@ -308,7 +313,7 @@ } DEBUGASSERT(!Curl_llist_count(&cshutdn->list)); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); } int Curl_cshutdn_init(struct cshutdn *cshutdn, @@ -326,7 +331,7 @@ { if(cshutdn->initialised && data) { int timeout_ms = 0; - /* Just for testing, run graceful shutdown */ + /* for testing, run graceful shutdown */ #ifdef DEBUGBUILD { const char *p = getenv("CURL_GRACEFUL_SHUTDOWN"); @@ -396,8 +401,8 @@ /* Add the connection to our shutdown list for non-blocking shutdown * during multi processing. */ - if(max_total > 0 && (max_total <= - (conns_in_pool + Curl_llist_count(&cshutdn->list)))) { + if(max_total > 0 && + (max_total <= (conns_in_pool + Curl_llist_count(&cshutdn->list)))) { CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection " "due to connection limit of %zu", max_total); cshutdn_destroy_oldest(cshutdn, data, NULL); @@ -418,38 +423,11 @@ conn->connection_id, Curl_llist_count(&cshutdn->list)); } -static void cshutdn_multi_socket(struct cshutdn *cshutdn, - struct Curl_easy *data, - curl_socket_t s) -{ - struct Curl_llist_node *e; - struct connectdata *conn; - bool done; - - DEBUGASSERT(cshutdn->multi->socket_cb); - e = Curl_llist_head(&cshutdn->list); - while(e) { - conn = Curl_node_elem(e); - if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) { - Curl_cshutdn_run_once(data, conn, &done); - if(done || cshutdn_update_ev(cshutdn, data, conn)) { - Curl_node_remove(e); - Curl_cshutdn_terminate(data, conn, FALSE); - } - break; - } - e = Curl_node_next(e); - } -} - void Curl_cshutdn_perform(struct cshutdn *cshutdn, struct Curl_easy *data, - curl_socket_t s) + struct Curl_sigpipe_ctx *sigpipe_ctx) { - if((s == CURL_SOCKET_TIMEOUT) || (!cshutdn->multi->socket_cb)) - cshutdn_perform(cshutdn, data); - else - cshutdn_multi_socket(cshutdn, data, s); + cshutdn_perform(cshutdn, data, sigpipe_ctx); } /* return fd_set info about the shutdown connections */ @@ -480,17 +458,10 @@ curl_socket_t sock = ps.sockets[i]; if(!FDSET_SOCK(sock)) continue; -#ifdef __DJGPP__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warith-conversion" -#endif if(ps.actions[i] & CURL_POLL_IN) FD_SET(sock, read_fd_set); if(ps.actions[i] & CURL_POLL_OUT) FD_SET(sock, write_fd_set); -#ifdef __DJGPP__ -#pragma GCC diagnostic pop -#endif if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) && ((int)sock > *maxfd)) *maxfd = (int)sock;
diff --git a/Utilities/cmcurl/lib/cshutdn.h b/Utilities/cmcurl/lib/cshutdn.h index 0a15d39..b2e83f3 100644 --- a/Utilities/cmcurl/lib/cshutdn.h +++ b/Utilities/cmcurl/lib/cshutdn.h
@@ -30,9 +30,10 @@ struct Curl_waitfds; struct Curl_multi; struct Curl_share; +struct Curl_sigpipe_ctx; /* Run the shutdown of the connection once. - * Will shortly attach/detach `data` to `conn` while doing so. + * Shortly attach/detach `data` to `conn` while doing so. * `done` will be set TRUE if any error was encountered or if * the connection was shut down completely. */ void Curl_cshutdn_run_once(struct Curl_easy *data, @@ -40,12 +41,12 @@ bool *done); /* Terminates the connection, e.g. closes and destroys it. - * If `run_shutdown` is TRUE, the shutdown will be run once before + * If `do_shutdown` is TRUE, the shutdown will be run once before * terminating it. * Takes ownership of `conn`. */ void Curl_cshutdn_terminate(struct Curl_easy *data, struct connectdata *conn, - bool run_shutdown); + bool do_shutdown); /* A `cshutdown` is always owned by a multi handle to maintain * the connections to be shut down. It registers timers and @@ -77,7 +78,7 @@ bool Curl_cshutdn_close_oldest(struct Curl_easy *data, const char *destination); -/* Add a connection to have it shut down. Will terminate the oldest +/* Add a connection to have it shut down. Terminate the oldest * connection when total connection limit of multi is being reached. */ void Curl_cshutdn_add(struct cshutdn *cshutdn, struct connectdata *conn, @@ -97,10 +98,9 @@ fd_set *read_fd_set, fd_set *write_fd_set, int *maxfd); -/* Run shut down connections using socket. If socket is CURL_SOCKET_TIMEOUT, - * run maintenance on all connections. */ +/* Run maintenance on all connections. */ void Curl_cshutdn_perform(struct cshutdn *cshutdn, struct Curl_easy *data, - curl_socket_t s); + struct Curl_sigpipe_ctx *sigpipe_ctx); #endif /* HEADER_CURL_CSHUTDN_H */
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c index 61c8b18..fd26c5f 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.c +++ b/Utilities/cmcurl/lib/curl_addrinfo.c
@@ -62,9 +62,9 @@ #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) /* workaround icc 9.1 optimizer issue */ -# define vqualifier volatile +# define vqualifier volatile #else -# define vqualifier +# define vqualifier #endif void Curl_freeaddrinfo(struct Curl_addrinfo *cahead) @@ -115,8 +115,8 @@ for(ai = aihead; ai != NULL; ai = ai->ai_next) { size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0; - /* ignore elements with unsupported address family, */ - /* settle family-specific sockaddr structure size. */ + /* ignore elements with unsupported address family, + settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); #ifdef USE_IPV6 @@ -140,8 +140,8 @@ break; } - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ + /* copy each structure member individually, member ordering, + size, or padding might be different for each platform. */ ca->ai_flags = ai->ai_flags; ca->ai_family = ai->ai_family; @@ -404,28 +404,33 @@ * Given an IPv4 or IPv6 dotted string address, this converts it to a proper * allocated Curl_addrinfo struct and returns it. */ -CURLcode Curl_str2addr(const char *address, int port, +CURLcode Curl_str2addr(const char *dotted, uint16_t port, struct Curl_addrinfo **addrp) { struct in_addr in; - if(curlx_inet_pton(AF_INET, address, &in) > 0) + if(curlx_inet_pton(AF_INET, dotted, &in) > 0) /* This is a dotted IP address 123.123.123.123-style */ - return ip2addr(addrp, AF_INET, &in, address, port); + return ip2addr(addrp, AF_INET, &in, dotted, port); #ifdef USE_IPV6 { struct in6_addr in6; - if(curlx_inet_pton(AF_INET6, address, &in6) > 0) + if(curlx_inet_pton(AF_INET6, dotted, &in6) > 0) /* This is a dotted IPv6 address ::1-style */ - return ip2addr(addrp, AF_INET6, &in6, address, port); + return ip2addr(addrp, AF_INET6, &in6, dotted, port); } #endif return CURLE_BAD_FUNCTION_ARGUMENT; /* bad input format */ } -bool Curl_is_ipaddr(const char *address) +bool Curl_is_ipv4addr(const char *address) { struct in_addr in; - if(curlx_inet_pton(AF_INET, address, &in) > 0) + return (curlx_inet_pton(AF_INET, address, &in) > 0); +} + +bool Curl_is_ipaddr(const char *address) +{ + if(Curl_is_ipv4addr(address)) return TRUE; #ifdef USE_IPV6 { @@ -442,34 +447,31 @@ /** * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo * struct initialized with this path. - * Set '*longpath' to TRUE if the error is a too long path. + * Returns CURLE_TOO_LARGE when path is too long. */ -struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, - bool abstract) +CURLcode Curl_unix2addr(const char *path, bool abstract, + struct Curl_addrinfo **paddr) { struct Curl_addrinfo *ai; struct sockaddr_un *sa_un; size_t path_len; - *longpath = FALSE; + *paddr = NULL; + + /* sun_path must be able to store the null-terminated path */ + path_len = strlen(path) + 1; + if(path_len > sizeof(sa_un->sun_path)) + return CURLE_TOO_LARGE; ai = curlx_calloc(1, sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un)); if(!ai) - return NULL; - ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + return CURLE_OUT_OF_MEMORY; + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); sa_un = (void *)ai->ai_addr; sa_un->sun_family = AF_UNIX; - /* sun_path must be able to store the null-terminated path */ - path_len = strlen(path) + 1; - if(path_len > sizeof(sa_un->sun_path)) { - curlx_free(ai); - *longpath = TRUE; - return NULL; - } - ai->ai_family = AF_UNIX; ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */ ai->ai_addrlen = (curl_socklen_t) @@ -481,11 +483,12 @@ else memcpy(sa_un->sun_path, path, path_len); /* copy NUL byte */ - return ai; + *paddr = ai; + return CURLE_OK; } #endif -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \ defined(HAVE_FREEADDRINFO) /* * curl_dbg_freeaddrinfo() @@ -515,9 +518,9 @@ freeaddrinfo(freethis); #endif } -#endif /* CURLDEBUG && HAVE_FREEADDRINFO */ +#endif /* CURL_MEMDEBUG && HAVE_FREEADDRINFO */ -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) /* * curl_dbg_getaddrinfo() * @@ -553,7 +556,7 @@ curl_dbg_log("ADDR %s:%d getaddrinfo() failed\n", source, line); return res; } -#endif /* CURLDEBUG && HAVE_GETADDRINFO */ +#endif /* CURL_MEMDEBUG && HAVE_GETADDRINFO */ #if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS) /*
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h index ca80f3a..da2da87 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.h +++ b/Utilities/cmcurl/lib/curl_addrinfo.h
@@ -38,7 +38,6 @@ #ifdef __VMS # include <in.h> # include <inet.h> -# include <stdlib.h> #endif /* @@ -72,21 +71,23 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port); #endif +bool Curl_is_ipv4addr(const char *address); bool Curl_is_ipaddr(const char *address); -CURLcode Curl_str2addr(const char *dotted, int port, struct Curl_addrinfo **); +CURLcode Curl_str2addr(const char *dotted, uint16_t port, + struct Curl_addrinfo **addrp); #ifdef USE_UNIX_SOCKETS -struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, - bool abstract); +CURLcode Curl_unix2addr(const char *path, bool abstract, + struct Curl_addrinfo **paddr); #endif -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \ defined(HAVE_FREEADDRINFO) void curl_dbg_freeaddrinfo(struct addrinfo *freethis, int line, const char *source); #endif -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) int curl_dbg_getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result, int line,
diff --git a/Utilities/cmcurl/lib/curl_config-cmake.h.in b/Utilities/cmcurl/lib/curl_config-cmake.h.in index df06ae7..37c401f 100644 --- a/Utilities/cmcurl/lib/curl_config-cmake.h.in +++ b/Utilities/cmcurl/lib/curl_config-cmake.h.in
@@ -27,6 +27,9 @@ /* Default SSL backend */ #cmakedefine CURL_DEFAULT_SSL_BACKEND "${CURL_DEFAULT_SSL_BACKEND}" +/* Use native CA store */ +#cmakedefine CURL_CA_NATIVE 1 + /* disables alt-svc */ #cmakedefine CURL_DISABLE_ALTSVC 1 @@ -108,8 +111,8 @@ /* disables netrc parser */ #cmakedefine CURL_DISABLE_NETRC 1 -/* disables NTLM support */ -#cmakedefine CURL_DISABLE_NTLM 1 +/* enables NTLM support */ +#cmakedefine CURL_ENABLE_NTLM 1 /* disables date parsing */ #cmakedefine CURL_DISABLE_PARSEDATE 1 @@ -136,7 +139,7 @@ #cmakedefine CURL_DISABLE_SHUFFLE_DNS 1 /* disables SMB */ -#cmakedefine CURL_DISABLE_SMB 1 +#cmakedefine CURL_ENABLE_SMB 1 /* disables SMTP */ #cmakedefine CURL_DISABLE_SMTP 1 @@ -153,9 +156,6 @@ /* disables TFTP */ #cmakedefine CURL_DISABLE_TFTP 1 -/* disables curl_easy_setopt()/curl_easy_getinfo() type checking */ -#cmakedefine CURL_DISABLE_TYPECHECK 1 - /* disables verbose strings */ #cmakedefine CURL_DISABLE_VERBOSE_STRINGS 1 @@ -245,9 +245,6 @@ /* Define to 1 if you have the fseeko declaration. */ #cmakedefine HAVE_DECL_FSEEKO 1 -/* Define to 1 if you have the ftruncate function. */ -#cmakedefine HAVE_FTRUNCATE 1 - /* Define to 1 if you have a working getaddrinfo function. */ #cmakedefine HAVE_GETADDRINFO 1 @@ -317,6 +314,12 @@ /* MIT Kerberos version */ #cmakedefine CURL_KRB5_VERSION ${CURL_KRB5_VERSION} +/* BoringSSL version */ +#cmakedefine CURL_BORINGSSL_VERSION ${CURL_BORINGSSL_VERSION} + +/* Patch stamp */ +#cmakedefine CURL_PATCHSTAMP ${CURL_PATCHSTAMP} + /* Define to 1 if you have the <ifaddrs.h> header file. */ #cmakedefine HAVE_IFADDRS_H 1 @@ -387,17 +390,9 @@ /* Define to 1 if you have a working localtime_r function. */ #cmakedefine HAVE_LOCALTIME_R 1 -/* Define to 1 if the compiler supports the 'long long' data type. */ -#if KWIML_ABI_SIZEOF_LONG_LONG -# define HAVE_LONGLONG 1 -#endif - /* Define to 1 if you have the 'suseconds_t' data type. */ #cmakedefine HAVE_SUSECONDS_T 1 -/* Define to 1 if you have the MSG_NOSIGNAL flag. */ -#cmakedefine HAVE_MSG_NOSIGNAL 1 - /* Define to 1 if you have the <netdb.h> header file. */ #cmakedefine HAVE_NETDB_H 1 @@ -440,9 +435,6 @@ /* Define to 1 if you have a working POSIX-style strerror_r function. */ #cmakedefine HAVE_POSIX_STRERROR_R 1 -/* Define to 1 if you have the <pthread.h> header file */ -#cmakedefine HAVE_PTHREAD_H 1 - /* Define to 1 if you have the <pwd.h> header file. */ #cmakedefine HAVE_PWD_H 1 @@ -467,9 +459,6 @@ /* Define to 1 if you have the sendmmsg function. */ #cmakedefine HAVE_SENDMMSG 1 -/* Define to 1 if you have the <stdint.h> header file. */ -#cmakedefine HAVE_STDINT_H 1 - /* Define to 1 if you have the 'fsetxattr' function. */ #cmakedefine HAVE_FSETXATTR 1 @@ -482,12 +471,6 @@ /* Define to 1 if you have the `setlocale' function. */ #cmakedefine HAVE_SETLOCALE 1 -/* Define to 1 if you have the `setmode' function. */ -#cmakedefine HAVE_SETMODE 1 - -/* Define to 1 if you have the `_setmode' function. */ -#cmakedefine HAVE__SETMODE 1 - /* Define to 1 if you have the `setrlimit' function. */ #cmakedefine HAVE_SETRLIMIT 1 @@ -506,9 +489,6 @@ /* Define to 1 if you have the sigsetjmp function or macro. */ #cmakedefine HAVE_SIGSETJMP 1 -/* Define to 1 if you have the `snprintf' function. */ -#cmakedefine HAVE_SNPRINTF 1 - /* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */ #cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 @@ -533,9 +513,6 @@ /* Define to 1 if you have the strcmpi function. */ #cmakedefine HAVE_STRCMPI 1 -/* Define to 1 if you have the strdup function. */ -#cmakedefine HAVE_STRDUP 1 - /* Define to 1 if you have the strerror_r function. */ #cmakedefine HAVE_STRERROR_R 1 @@ -621,12 +598,12 @@ #cmakedefine CURL_OS ${CURL_OS} /* - Note: SIZEOF_* variables are fetched with CMake through check_type_size(). - As per CMake documentation on CheckTypeSize, C preprocessor code is - generated by CMake into SIZEOF_*_CODE. This is what we use in the - following statements. + Note: SIZEOF_* variables are fetched with CMake through check_type_size(). + As per CMake documentation on CheckTypeSize, C preprocessor code is + generated by CMake into SIZEOF_*_CODE. This is what we use in the + following statements. - Reference: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html + Reference: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html */ /* The size of `int', as computed by sizeof. */ @@ -646,9 +623,6 @@ # 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} @@ -670,14 +644,17 @@ /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS 1 +/* Define if you have POSIX pthreads */ +#cmakedefine HAVE_THREADS_POSIX 1 + /* Define if you want to enable c-ares support */ #cmakedefine USE_ARES 1 -/* Define if you want to enable POSIX threaded DNS lookup */ -#cmakedefine USE_THREADS_POSIX 1 +/* Define if you want to enable c-ares DNS lookup */ +#cmakedefine USE_RESOLV_ARES 1 -/* Define if you want to enable Win32 threaded DNS lookup */ -#cmakedefine USE_THREADS_WIN32 1 +/* Define if you want to enable threaded DNS lookup */ +#cmakedefine USE_RESOLV_THREADED 1 /* if GnuTLS is enabled */ #cmakedefine USE_GNUTLS 1 @@ -703,15 +680,15 @@ /* if wolfSSL has the wolfSSL_UseALPN function. */ #cmakedefine HAVE_WOLFSSL_USEALPN 1 -/* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */ -#cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1 - /* if wolfSSL has the wolfSSL_BIO_new function. */ #cmakedefine HAVE_WOLFSSL_BIO_NEW 1 /* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */ #cmakedefine HAVE_WOLFSSL_BIO_SET_SHUTDOWN 1 +/* if wolfSSL has the wc_Des_EcbEncrypt function. */ +#cmakedefine HAVE_WC_DES_ECBENCRYPT 1 + /* if libssh is in use */ #cmakedefine USE_LIBSSH 1 @@ -730,9 +707,6 @@ /* if AmiSSL is in use */ #cmakedefine USE_AMISSL 1 -/* if librtmp/rtmpdump is in use */ -#cmakedefine USE_LIBRTMP 1 - /* if GSASL is in use */ #cmakedefine USE_GSASL 1 @@ -749,34 +723,31 @@ automatically */ #cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1 -/* to enable NGHTTP2 */ +/* to enable NGHTTP2 */ #cmakedefine USE_NGHTTP2 1 /* to enable NGTCP2 */ #cmakedefine USE_NGTCP2 1 -/* to enable NGHTTP3 */ +/* to enable NGHTTP3 */ #cmakedefine USE_NGHTTP3 1 /* to enable quiche */ #cmakedefine USE_QUICHE 1 -/* to enable openssl + nghttp3 */ -#cmakedefine USE_OPENSSL_QUIC 1 - /* to enable openssl + ngtcp2 + nghttp3 */ #cmakedefine OPENSSL_QUIC_API2 1 /* Define to 1 if you have the quiche_conn_set_qlog_fd function. */ #cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1 -/* if Unix domain sockets are enabled */ +/* if Unix domain sockets are enabled */ #cmakedefine USE_UNIX_SOCKETS 1 /* to enable SSPI support */ #cmakedefine USE_WINDOWS_SSPI 1 -/* to enable Windows SSL */ +/* to enable Windows SSL */ #cmakedefine USE_SCHANNEL 1 /* if Watt-32 is in use */
diff --git a/Utilities/cmcurl/lib/curl_ctype.h b/Utilities/cmcurl/lib/curl_ctype.h index 153a8f9..f3291ad 100644 --- a/Utilities/cmcurl/lib/curl_ctype.h +++ b/Utilities/cmcurl/lib/curl_ctype.h
@@ -24,8 +24,8 @@ * ***************************************************************************/ -#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f')) -#define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F')) +#define ISLOWHEXALPHA(x) (((x) >= 'a') && ((x) <= 'f')) +#define ISUPHEXALPHA(x) (((x) >= 'A') && ((x) <= 'F')) #define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f) #define IS7F(x) ((x) == 0x7f) @@ -36,7 +36,7 @@ #define ISGRAPH(x) (ISLOWPRINT(x) || (((x) > ' ') && ((x) <= 0x7e))) #define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x)) #define ISALPHA(x) (ISLOWER(x) || ISUPPER(x)) -#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x)) +#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALPHA(x) || ISUPHEXALPHA(x)) #define ISODIGIT(x) (((x) >= '0') && ((x) <= '7')) #define ISALNUM(x) (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x)) #define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z')) @@ -44,9 +44,9 @@ #define ISDIGIT(x) (((x) >= '0') && ((x) <= '9')) #define ISBLANK(x) (((x) == ' ') || ((x) == '\t')) #define ISSPACE(x) (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d))) -#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \ - ((x) == '~')) +#define ISURLPUNTCS(x) \ + (((x) == '-') || ((x) == '.') || ((x) == '_') || ((x) == '~')) #define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x)) -#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') +#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') #endif /* HEADER_CURL_CTYPE_H */
diff --git a/Utilities/cmcurl/lib/curl_fnmatch.c b/Utilities/cmcurl/lib/curl_fnmatch.c index 8e35ede..dde956c 100644 --- a/Utilities/cmcurl/lib/curl_fnmatch.c +++ b/Utilities/cmcurl/lib/curl_fnmatch.c
@@ -251,8 +251,8 @@ static int loop(const unsigned char *pattern, const unsigned char *string, int maxstars) { - const unsigned char *p = (const unsigned char *)pattern; - const unsigned char *s = (const unsigned char *)string; + const unsigned char *p = pattern; + const unsigned char *s = string; unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 }; for(;;) {
diff --git a/Utilities/cmcurl/lib/curl_fopen.c b/Utilities/cmcurl/lib/curl_fopen.c index 9559c0d..cc888f7 100644 --- a/Utilities/cmcurl/lib/curl_fopen.c +++ b/Utilities/cmcurl/lib/curl_fopen.c
@@ -23,7 +23,7 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ +#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ !defined(CURL_DISABLE_HSTS) #include "urldata.h" @@ -89,7 +89,7 @@ unsigned char randbuf[41]; char *tempstore = NULL; #ifndef _WIN32 - struct_stat sb; + curlx_struct_stat sb; #endif int fd = -1; char *dir = NULL; @@ -99,11 +99,18 @@ *fh = curlx_fopen(filename, FOPEN_WRITETEXT); if(!*fh) goto fail; - if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { + if(curlx_fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { return CURLE_OK; } curlx_fclose(*fh); +#ifdef HAVE_GETEUID + /* If the existing file is not owned by the user, do not inherit + * its permissions at the temp file created below. The permissions + * might be unsuitable for holding user private data. */ + if(sb.st_uid != geteuid()) + sb.st_mode = 0; #endif +#endif /* !_WIN32 */ *fh = NULL; result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); @@ -125,8 +132,8 @@ result = CURLE_WRITE_ERROR; #ifdef _WIN32 - fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL, - S_IREAD | S_IWRITE); + fd = curlx_open(tempstore, _O_WRONLY | _O_CREAT | _O_EXCL, + _S_IREAD | _S_IWRITE); #elif (defined(ANDROID) || defined(__ANDROID__)) && \ (defined(__i386__) || defined(__arm__)) fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL, @@ -147,7 +154,7 @@ fail: if(fd != -1) { - close(fd); + curlx_close(fd); unlink(tempstore); }
diff --git a/Utilities/cmcurl/lib/curl_get_line.c b/Utilities/cmcurl/lib/curl_get_line.c index 46c1287..6fcd043 100644 --- a/Utilities/cmcurl/lib/curl_get_line.c +++ b/Utilities/cmcurl/lib/curl_get_line.c
@@ -23,13 +23,11 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ +#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ !defined(CURL_DISABLE_HSTS) || !defined(CURL_DISABLE_NETRC) #include "curl_get_line.h" -#define appendnl(b) curlx_dyn_addn(buf, "\n", 1) - /* * Curl_get_line() returns only complete whole lines that end with newline. * When 'eof' is set TRUE, the last line has been read. @@ -41,7 +39,9 @@ curlx_dyn_reset(buf); while(1) { size_t rlen; - char *b = fgets(buffer, sizeof(buffer), input); + const char *b = fgets(buffer, sizeof(buffer), input); + if(!b && ferror(input)) + return CURLE_READ_ERROR; *eof = feof(input); @@ -60,7 +60,7 @@ return CURLE_OK; /* all good */ if(*eof) /* append a newline */ - return appendnl(buf); + return curlx_dyn_addn(buf, "\n", 1); /* otherwise get next line to append */ } /* UNREACHABLE */
diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c index 8c82f77..650d190 100644 --- a/Utilities/cmcurl/lib/curl_gssapi.c +++ b/Utilities/cmcurl/lib/curl_gssapi.c
@@ -55,7 +55,7 @@ #define CURL_ALIGN8 #endif -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -93,20 +93,20 @@ char creds[250]; }; -static OM_uint32 -stub_gss_init_sec_context(OM_uint32 *min, - gss_cred_id_t initiator_cred_handle, - struct stub_gss_ctx_id_t_desc **context, - gss_name_t target_name, - const gss_OID mech_type, - OM_uint32 req_flags, - OM_uint32 time_req, - const gss_channel_bindings_t input_chan_bindings, - gss_buffer_desc *input_token, - gss_OID *actual_mech_type, - gss_buffer_desc *output_token, - OM_uint32 *ret_flags, - OM_uint32 *time_rec) +static OM_uint32 stub_gss_init_sec_context( + OM_uint32 *min, + gss_cred_id_t initiator_cred_handle, + struct stub_gss_ctx_id_t_desc **context, + gss_name_t target_name, + const gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + const gss_channel_bindings_t input_chan_bindings, + gss_buffer_desc *input_token, + gss_OID *actual_mech_type, + gss_buffer_desc *output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) { struct stub_gss_ctx_id_t_desc *ctx = NULL; @@ -283,10 +283,10 @@ return GSS_S_CONTINUE_NEEDED; } -static OM_uint32 -stub_gss_delete_sec_context(OM_uint32 *min, - struct stub_gss_ctx_id_t_desc **context, - gss_buffer_t output_token) +static OM_uint32 stub_gss_delete_sec_context( + OM_uint32 *min, + struct stub_gss_ctx_id_t_desc **context, + gss_buffer_t output_token) { (void)output_token; @@ -384,6 +384,7 @@ return gss_delete_sec_context(min, context, output_token); } +#ifdef CURLVERBOSE #define GSS_LOG_BUFFER_LEN 1024 static size_t display_gss_error(OM_uint32 status, int type, char *buf, size_t len) @@ -437,13 +438,10 @@ display_gss_error(minor, GSS_C_MECH_CODE, buf, len); infof(data, "%s%s", prefix, buf); -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; - (void)prefix; -#endif } +#endif /* CURLVERBOSE */ -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/curl_gssapi.h b/Utilities/cmcurl/lib/curl_gssapi.h index 8c09c72..fc3759e 100644 --- a/Utilities/cmcurl/lib/curl_gssapi.h +++ b/Utilities/cmcurl/lib/curl_gssapi.h
@@ -44,12 +44,22 @@ OM_uint32 *ret_flags); OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, - gss_ctx_id_t *context_handle, + gss_ctx_id_t *context, gss_buffer_t output_token); +#ifdef CURLVERBOSE /* Helper to log a GSS-API error status */ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, OM_uint32 major, OM_uint32 minor); +#else +#define Curl_gss_log_error(data, prefix, major, minor) \ + do { \ + (void)(data); \ + (void)(prefix); \ + (void)(major); \ + (void)(minor); \ + } while(0) +#endif /* Define our privacy and integrity protection values */ #define GSSAUTH_P_NONE 1
diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h index 4759ff6..301d44f 100644 --- a/Utilities/cmcurl/lib/curl_hmac.h +++ b/Utilities/cmcurl/lib/curl_hmac.h
@@ -57,14 +57,14 @@ struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams, const unsigned char *key, unsigned int keylen); -int Curl_HMAC_update(struct HMAC_context *context, - const unsigned char *data, - unsigned int len); -int Curl_HMAC_final(struct HMAC_context *context, unsigned char *result); +void Curl_HMAC_update(struct HMAC_context *ctxt, + const unsigned char *data, + unsigned int len); +int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output); CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, - const unsigned char *data, const size_t datalen, + const unsigned char *data, size_t datalen, unsigned char *output); #endif
diff --git a/Utilities/cmcurl/lib/curl_ldap.h b/Utilities/cmcurl/lib/curl_ldap.h index 5ef32a5..0ccc128 100644 --- a/Utilities/cmcurl/lib/curl_ldap.h +++ b/Utilities/cmcurl/lib/curl_ldap.h
@@ -23,15 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef CURL_DISABLE_LDAP -extern const struct Curl_handler Curl_handler_ldap; - -#if !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) -extern const struct Curl_handler Curl_handler_ldaps; -#endif +extern const struct Curl_protocol Curl_protocol_ldap; void Curl_ldap_version(char *buf, size_t bufsz); -#endif + #endif /* HEADER_CURL_LDAP_H */
diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h index 16272c7..8a0cc26 100644 --- a/Utilities/cmcurl/lib/curl_md5.h +++ b/Utilities/cmcurl/lib/curl_md5.h
@@ -54,11 +54,11 @@ extern const struct HMAC_params Curl_HMAC_MD5; CURLcode Curl_md5it(unsigned char *output, const unsigned char *input, - const size_t len); + size_t len); struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params); CURLcode Curl_MD5_update(struct MD5_context *context, - const unsigned char *data, + const unsigned char *input, unsigned int len); CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result);
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index 2f4f7c3..4b2007b 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -49,63 +49,30 @@ in NTLM type-3 messages. */ -#ifdef USE_MBEDTLS -#include <mbedtls/version.h> -#if MBEDTLS_VERSION_NUMBER < 0x03020000 -#error "mbedTLS 3.2.0 or later required" -#endif -#endif - #if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT) - #define USE_OPENSSL_DES -#elif defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT) - #define USE_OPENSSL_DES -#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) - #define USE_MBEDTLS_DES -#endif -#ifdef USE_OPENSSL_DES - -#ifdef USE_OPENSSL # include <openssl/des.h> # ifdef OPENSSL_IS_AWSLC /* for versions 1.2.0 to 1.30.1 */ # define DES_set_key_unchecked (void)DES_set_key # endif -# define DESKEY(x) &x -#else +# define USE_OPENSSL_DES + +#elif defined(USE_WOLFSSL) && defined(HAVE_WC_DES_ECBENCRYPT) + # include <wolfssl/options.h> -# include <wolfssl/openssl/des.h> -# include <wolfssl/version.h> -# ifdef OPENSSL_COEXIST -# define DES_key_schedule WOLFSSL_DES_key_schedule -# define DES_cblock WOLFSSL_DES_cblock -# define DES_set_odd_parity wolfSSL_DES_set_odd_parity -# define DES_set_key wolfSSL_DES_set_key -# define DES_set_key_unchecked wolfSSL_DES_set_key_unchecked -# define DES_ecb_encrypt wolfSSL_DES_ecb_encrypt -# define DESKEY(x) ((WOLFSSL_DES_key_schedule *)(x)) - -# if defined(LIBWOLFSSL_VERSION_HEX) && \ - (LIBWOLFSSL_VERSION_HEX >= 0x05007006) -# define DES_ENCRYPT WC_DES_ENCRYPT -# define DES_DECRYPT WC_DES_DECRYPT -# endif - -# else -# define DESKEY(x) &x -# endif -#endif -#define DESKEYARG(x) *x +# include <wolfssl/wolfcrypt/des3.h> +# define USE_WOLFSSL_DES #elif defined(USE_GNUTLS) - # include <nettle/des.h> # define USE_CURL_DES_SET_ODD_PARITY - -#elif defined(USE_MBEDTLS_DES) - +#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) +# include <mbedtls/version.h> +# if MBEDTLS_VERSION_NUMBER < 0x03020000 +# error "mbedTLS 3.2.0 or later required" +# endif # include <mbedtls/des.h> - +# define USE_MBEDTLS_DES #elif defined(USE_OS400CRYPTO) # include "cipher.mih" /* mih/cipher */ # define USE_CURL_DES_SET_ODD_PARITY @@ -141,7 +108,7 @@ * * bytes [in/out] - The data whose parity bits are to be adjusted for * odd parity. - * len [out] - The length of the data. + * len [in] - The length of the data. */ static void curl_des_set_odd_parity(unsigned char *bytes, size_t len) { @@ -174,7 +141,7 @@ key[4] = (char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); key[5] = (char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); key[6] = (char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); - key[7] = (char) ((key_56[6] << 1) & 0xFF); + key[7] = (char)((key_56[6] << 1) & 0xFF); } #ifdef USE_OPENSSL_DES @@ -182,8 +149,7 @@ * Turns a 56-bit key into a 64-bit, odd parity key and sets the key. The * key schedule ks is also set. */ -static void setup_des_key(const unsigned char *key_56, - DES_key_schedule DESKEYARG(ks)) +static void setup_des_key(const unsigned char *key_56, DES_key_schedule *ks) { DES_cblock key; @@ -197,8 +163,19 @@ DES_set_key_unchecked(&key, ks); } -#elif defined(USE_GNUTLS) +#elif defined(USE_WOLFSSL_DES) +static void setup_des_key(const unsigned char *key_56, Des *des) +{ + byte key[8]; + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, (char *)key); + + /* Set the key */ + wc_Des_SetKey(des, key, NULL, 0); +} + +#elif defined(USE_GNUTLS) static void setup_des_key(const unsigned char *key_56, struct des_ctx *des) { char key[8]; @@ -214,7 +191,6 @@ } #elif defined(USE_MBEDTLS_DES) - static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -234,7 +210,6 @@ } #elif defined(USE_OS400CRYPTO) - static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -258,7 +233,6 @@ } #elif defined(USE_WIN32_CRYPTO) - static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -307,7 +281,7 @@ return TRUE; } -#endif /* USE_WIN32_CRYPTO */ +#endif /* crypto backends */ /* * takes a 21 byte array and treats it as 3 56-bit DES keys. The @@ -321,17 +295,25 @@ #ifdef USE_OPENSSL_DES DES_key_schedule ks; - setup_des_key(keys, DESKEY(ks)); + setup_des_key(keys, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), - (DES_cblock *)results, DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)results, &ks, DES_ENCRYPT); - setup_des_key(keys + 7, DESKEY(ks)); + setup_des_key(keys + 7, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), - (DES_cblock *)(results + 8), DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)(results + 8), &ks, DES_ENCRYPT); - setup_des_key(keys + 14, DESKEY(ks)); + setup_des_key(keys + 14, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), - (DES_cblock *)(results + 16), DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)(results + 16), &ks, DES_ENCRYPT); +#elif defined(USE_WOLFSSL_DES) + Des des; + setup_des_key(keys, &des); + wc_Des_EcbEncrypt(&des, results, plaintext, DES_KEY_SIZE); + setup_des_key(keys + 7, &des); + wc_Des_EcbEncrypt(&des, results + 8, plaintext, DES_KEY_SIZE); + setup_des_key(keys + 14, &des); + wc_Des_EcbEncrypt(&des, results + 16, plaintext, DES_KEY_SIZE); #elif defined(USE_GNUTLS) struct des_ctx des; setup_des_key(keys, &des); @@ -340,7 +322,7 @@ des_encrypt(&des, 8, results + 8, plaintext); setup_des_key(keys + 14, &des); des_encrypt(&des, 8, results + 16, plaintext); -#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \ +#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \ defined(USE_WIN32_CRYPTO) encrypt_des(plaintext, results, keys); encrypt_des(plaintext, results + 8, keys + 7); @@ -369,24 +351,29 @@ { /* Create LanManager hashed password. */ - #ifdef USE_OPENSSL_DES DES_key_schedule ks; - setup_des_key(pw, DESKEY(ks)); + setup_des_key(pw, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic), - (DES_cblock *)lmbuffer, DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)lmbuffer, &ks, DES_ENCRYPT); - setup_des_key(pw + 7, DESKEY(ks)); + setup_des_key(pw + 7, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic), - (DES_cblock *)(lmbuffer + 8), DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)(lmbuffer + 8), &ks, DES_ENCRYPT); +#elif defined(USE_WOLFSSL_DES) + Des des; + setup_des_key(pw, &des); + wc_Des_EcbEncrypt(&des, lmbuffer, magic, DES_KEY_SIZE); + setup_des_key(pw + 7, &des); + wc_Des_EcbEncrypt(&des, lmbuffer + 8, magic, DES_KEY_SIZE); #elif defined(USE_GNUTLS) struct des_ctx des; setup_des_key(pw, &des); des_encrypt(&des, 8, lmbuffer, magic); setup_des_key(pw + 7, &des); des_encrypt(&des, 8, lmbuffer + 8, magic); -#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \ +#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \ defined(USE_WIN32_CRYPTO) encrypt_des(magic, lmbuffer, pw); encrypt_des(magic, lmbuffer + 8, pw + 7); @@ -404,24 +391,10 @@ size_t i; for(i = 0; i < srclen; i++) { dest[2 * i] = (unsigned char)src[i]; - dest[2 * i + 1] = '\0'; + dest[(2 * i) + 1] = '\0'; } } -#ifndef USE_WINDOWS_SSPI - -static void ascii_uppercase_to_unicode_le(unsigned char *dest, - const char *src, size_t srclen) -{ - size_t i; - for(i = 0; i < srclen; i++) { - dest[2 * i] = (unsigned char)(Curl_raw_toupper(src[i])); - dest[2 * i + 1] = '\0'; - } -} - -#endif /* !USE_WINDOWS_SSPI */ - /* * Set up nt hashed passwords * @unittest: 1600 @@ -505,6 +478,16 @@ #endif } +static void ascii_uppercase_to_unicode_le(unsigned char *dest, + const char *src, size_t srclen) +{ + size_t i; + for(i = 0; i < srclen; i++) { + dest[2 * i] = (unsigned char)(Curl_raw_toupper(src[i])); + dest[(2 * i) + 1] = '\0'; + } +} + /* This creates the NTLMv2 hash by using NTLM hash as the key and Unicode * (uppercase UserName + Domain) as the data */ @@ -554,9 +537,9 @@ * * Returns CURLE_OK on success. */ -CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, - unsigned char *challenge_client, - struct ntlmdata *ntlm, +CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash, + const unsigned char *challenge_client, + const struct ntlmdata *ntlm, unsigned char **ntresp, unsigned int *ntresp_len) { @@ -647,9 +630,9 @@ * * Returns CURLE_OK on success. */ -CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, - unsigned char *challenge_client, - unsigned char *challenge_server, +CURLcode Curl_ntlm_core_mk_lmv2_resp(const unsigned char *ntlmv2hash, + const unsigned char *challenge_client, + const unsigned char *challenge_server, unsigned char *lmresp) { unsigned char data[16];
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h index 947b836..f96bf0a 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.h +++ b/Utilities/cmcurl/lib/curl_ntlm_core.h
@@ -55,15 +55,15 @@ unsigned char *ntlmhash, unsigned char *ntlmv2hash); -CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, - unsigned char *challenge_client, - struct ntlmdata *ntlm, +CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash, + const unsigned char *challenge_client, + const struct ntlmdata *ntlm, unsigned char **ntresp, unsigned int *ntresp_len); -CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, - unsigned char *challenge_client, - unsigned char *challenge_server, +CURLcode Curl_ntlm_core_mk_lmv2_resp(const unsigned char *ntlmv2hash, + const unsigned char *challenge_client, + const unsigned char *challenge_server, unsigned char *lmresp); #endif /* !USE_WINDOWS_SSPI */
diff --git a/Utilities/cmcurl/lib/curl_printf.h b/Utilities/cmcurl/lib/curl_printf.h index 207ba71..3b1a5af 100644 --- a/Utilities/cmcurl/lib/curl_printf.h +++ b/Utilities/cmcurl/lib/curl_printf.h
@@ -28,10 +28,10 @@ #define MERR_MEM 1 #define MERR_TOO_LARGE 2 -/* Lower-case digits. */ +/* Lower-case digits. */ extern const unsigned char Curl_ldigits[]; -/* Upper-case digits. */ +/* Upper-case digits. */ extern const unsigned char Curl_udigits[]; #endif /* HEADER_CURL_PRINTF_H */
diff --git a/Utilities/cmcurl/lib/curl_range.c b/Utilities/cmcurl/lib/curl_range.c index 14218f1..9bbafa4 100644 --- a/Utilities/cmcurl/lib/curl_range.c +++ b/Utilities/cmcurl/lib/curl_range.c
@@ -30,10 +30,8 @@ /* Only include this function if one or more of FTP, FILE are enabled. */ #if !defined(CURL_DISABLE_FTP) || !defined(CURL_DISABLE_FILE) -/* - Check if this is a range download, and if so, set the internal variables - properly. -*/ +/* Check if this is a range download, and if so, set the internal variables + properly. */ CURLcode Curl_range(struct Curl_easy *data) { if(data->state.use_range && data->state.range) { @@ -56,7 +54,7 @@ else if(!first_num) { /* -Y */ if(!to) - /* "-0" is just wrong */ + /* "-0" is wrong */ return CURLE_RANGE_ERROR; data->req.maxdownload = to;
diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c deleted file mode 100644 index 98816f8..0000000 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ /dev/null
@@ -1,386 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Howard Chu, <hyc@highlandsun.com> - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * 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_LIBRTMP - -#include "curl_rtmp.h" -#include "urldata.h" -#include "url.h" -#include "curlx/nonblock.h" -#include "progress.h" /* for Curl_pgrsSetUploadSize */ -#include "transfer.h" -#include "bufref.h" - -#include <librtmp/rtmp.h> - -#if defined(USE_WINSOCK) || defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD) -#define SET_RCVTIMEO(tv, s) int tv = s * 1000 -#else -#define SET_RCVTIMEO(tv, s) struct timeval tv = { s, 0 } -#endif - -#define DEF_BUFTIME (2 * 60 * 60 * 1000) /* 2 hours */ - -/* meta key for storing RTMP* at connection */ -#define CURL_META_RTMP_CONN "meta:proto:rtmp:conn" - - -static CURLcode rtmp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode rtmp_do(struct Curl_easy *data, bool *done); -static CURLcode rtmp_done(struct Curl_easy *data, CURLcode, bool premature); -static CURLcode rtmp_connect(struct Curl_easy *data, bool *done); -static CURLcode rtmp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); - -static Curl_recv rtmp_recv; -static Curl_send rtmp_send; - -/* - * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu/ - */ - -const struct Curl_handler Curl_handler_rtmp = { - "rtmp", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMP, /* defport */ - CURLPROTO_RTMP, /* protocol */ - CURLPROTO_RTMP, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmpt = { - "rtmpt", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMPT, /* defport */ - CURLPROTO_RTMPT, /* protocol */ - CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmpe = { - "rtmpe", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMP, /* defport */ - CURLPROTO_RTMPE, /* protocol */ - CURLPROTO_RTMPE, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmpte = { - "rtmpte", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMPT, /* defport */ - CURLPROTO_RTMPTE, /* protocol */ - CURLPROTO_RTMPTE, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmps = { - "rtmps", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMPS, /* defport */ - CURLPROTO_RTMPS, /* protocol */ - CURLPROTO_RTMP, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmpts = { - "rtmpts", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMPS, /* defport */ - CURLPROTO_RTMPTS, /* protocol */ - CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE /* flags */ -}; - -static void rtmp_conn_dtor(void *key, size_t klen, void *entry) -{ - RTMP *r = entry; - (void)key; - (void)klen; - RTMP_Close(r); - RTMP_Free(r); -} - -static CURLcode rtmp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - RTMP *r = RTMP_Alloc(); - if(!r || - Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor)) - return CURLE_OUT_OF_MEMORY; - - RTMP_Init(r); - RTMP_SetBufferMS(r, DEF_BUFTIME); - if(!RTMP_SetupURL(r, CURL_UNCONST(Curl_bufref_ptr(&data->state.url)))) - /* rtmp_conn_dtor() performs the cleanup */ - return CURLE_URL_MALFORMAT; - return CURLE_OK; -} - -static CURLcode rtmp_connect(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - SET_RCVTIMEO(tv, 10); - - if(!r) - return CURLE_FAILED_INIT; - - if(conn->sock[FIRSTSOCKET] > INT_MAX) { - /* The socket value is invalid for rtmp. */ - return CURLE_FAILED_INIT; - } - - r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET]; - - /* We have to know if it is a write before we send the - * connect request packet - */ - if(data->state.upload) - r->Link.protocol |= RTMP_FEATURE_WRITE; - - /* For plain streams, use the buffer toggle trick to keep data flowing */ - if(!(r->Link.lFlags & RTMP_LF_LIVE) && - !(r->Link.protocol & RTMP_FEATURE_HTTP)) - r->Link.lFlags |= RTMP_LF_BUFX; - - (void)curlx_nonblock(r->m_sb.sb_socket, FALSE); - setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, - (char *)&tv, sizeof(tv)); - - if(!RTMP_Connect1(r, NULL)) - return CURLE_FAILED_INIT; - - /* Clients must send a periodic BytesReceived report to the server */ - r->m_bSendCounter = TRUE; - - *done = TRUE; - conn->recv[FIRSTSOCKET] = rtmp_recv; - conn->send[FIRSTSOCKET] = rtmp_send; - return CURLE_OK; -} - -static CURLcode rtmp_do(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - - if(!r || !RTMP_ConnectStream(r, 0)) - return CURLE_FAILED_INIT; - - if(data->state.upload) { - Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_xfer_setup_send(data, FIRSTSOCKET); - } - else - Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); - *done = TRUE; - return CURLE_OK; -} - -static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - (void)data; - (void)status; - (void)premature; - - return CURLE_OK; -} - -static CURLcode rtmp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - (void)data; - (void)dead_connection; - if(r) - Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN); - return CURLE_OK; -} - -static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, size_t *pnread) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - CURLcode result = CURLE_OK; - ssize_t rv; - - (void)sockindex; - *pnread = 0; - if(!r) - return CURLE_FAILED_INIT; - - rv = RTMP_Read(r, buf, curlx_uztosi(len)); - if(!curlx_sztouz(rv, pnread)) { - if(r->m_read.status == RTMP_READ_COMPLETE || - r->m_read.status == RTMP_READ_EOF) { - data->req.size = data->req.bytecount; - } - else - result = CURLE_RECV_ERROR; - } - - return result; -} - -static CURLcode rtmp_send(struct Curl_easy *data, int sockindex, - const uint8_t *buf, size_t len, bool eos, - size_t *pnwritten) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - ssize_t rv; - - (void)sockindex; - (void)eos; - *pnwritten = 0; - if(!r) - return CURLE_FAILED_INIT; - - rv = RTMP_Write(r, (const char *)buf, curlx_uztosi(len)); - if(!curlx_sztouz(rv, pnwritten)) - return CURLE_SEND_ERROR; - - return CURLE_OK; -} - -void Curl_rtmp_version(char *version, size_t len) -{ - char suff[2]; - if(RTMP_LIB_VERSION & 0xff) { - suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1; - suff[1] = '\0'; - } - else - suff[0] = '\0'; - - curl_msnprintf(version, len, "librtmp/%d.%d%s", - RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff, - suff); -} - -#endif /* USE_LIBRTMP */
diff --git a/Utilities/cmcurl/lib/curl_rtmp.h b/Utilities/cmcurl/lib/curl_rtmp.h deleted file mode 100644 index 339d3a4..0000000 --- a/Utilities/cmcurl/lib/curl_rtmp.h +++ /dev/null
@@ -1,37 +0,0 @@ -#ifndef HEADER_CURL_RTMP_H -#define HEADER_CURL_RTMP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Howard Chu, <hyc@highlandsun.com> - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#ifdef USE_LIBRTMP -extern const struct Curl_handler Curl_handler_rtmp; -extern const struct Curl_handler Curl_handler_rtmpt; -extern const struct Curl_handler Curl_handler_rtmpe; -extern const struct Curl_handler Curl_handler_rtmpte; -extern const struct Curl_handler Curl_handler_rtmps; -extern const struct Curl_handler Curl_handler_rtmpts; - -void Curl_rtmp_version(char *version, size_t len); -#endif - -#endif /* HEADER_CURL_RTMP_H */
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index 123c1c1..60f0859 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -183,9 +183,9 @@ static void sasl_state(struct SASL *sasl, struct Curl_easy *data, saslstate newstate) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* for debug purposes */ - static const char * const names[]={ + static const char * const names[] = { "STOP", "PLAIN", "LOGIN", @@ -227,13 +227,14 @@ result = sasl->params->getmessage(data, out); if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) { - unsigned char *msg; - size_t msglen; const char *serverdata = Curl_bufref_ptr(out); if(!*serverdata || *serverdata == '=') Curl_bufref_set(out, NULL, 0, NULL); else { + unsigned char *msg; + size_t msglen; + result = curlx_base64_decode(serverdata, &msg, &msglen); if(!result) Curl_bufref_set(out, msg, msglen, curl_free); @@ -332,8 +333,8 @@ Curl_auth_create_gssapi_user_message(data, sctx->conn->user, sctx->conn->passwd, service, sctx->conn->host.name, - sctx->sasl->mutual_auth, NULL, - krb5, &sctx->resp); + (bool)sctx->sasl->mutual_auth, + NULL, krb5, &sctx->resp); } return TRUE; } @@ -414,9 +415,8 @@ data->set.str[STRING_SERVICE_NAME] : sctx->sasl->params->service; const char *hostname; - int port; - Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); + Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, NULL); sctx->mech = SASL_MECH_STRING_NTLM; sctx->state1 = SASL_NTLM; @@ -711,9 +711,9 @@ struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); result = !krb5 ? CURLE_OUT_OF_MEMORY : Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, - service, conn->host.name, - sasl->mutual_auth, NULL, - krb5, &resp); + service, conn->host.name, + (bool)sasl->mutual_auth, NULL, + krb5, &resp); newstate = SASL_GSSAPI_TOKEN; break; } @@ -728,7 +728,7 @@ message */ result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, NULL, NULL, - sasl->mutual_auth, + (bool)sasl->mutual_auth, &serverdata, krb5, &resp); newstate = SASL_GSSAPI_NO_DATA; @@ -801,7 +801,7 @@ sasl->curmech = NULL; /* Start an alternative SASL authentication */ - return Curl_sasl_start(sasl, data, sasl->force_ir, progress); + return Curl_sasl_start(sasl, data, (bool)sasl->force_ir, progress); default: failf(data, "Unsupported SASL authentication mechanism"); result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ @@ -834,7 +834,7 @@ return result; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static void sasl_unchosen(struct Curl_easy *data, unsigned short mech, unsigned short enabledmechs, bool built_in, bool platform, @@ -865,11 +865,11 @@ infof(data, "SASL: %s is missing username", mname); } } -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE #ifdef USE_KERBEROS5 #define CURL_SASL_KERBEROS5 TRUE #else @@ -924,7 +924,7 @@ data->set.str[STRING_BEARER] ? NULL : "CURLOPT_XOAUTH2_BEARER"); } -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ (void)sasl; (void)data; return CURLE_LOGIN_DENIED;
diff --git a/Utilities/cmcurl/lib/curl_sasl.h b/Utilities/cmcurl/lib/curl_sasl.h index 8432d26..8a97f52 100644 --- a/Utilities/cmcurl/lib/curl_sasl.h +++ b/Utilities/cmcurl/lib/curl_sasl.h
@@ -129,7 +129,7 @@ /* This is used to test whether the line starts with the given mechanism */ #define sasl_mech_equal(line, wordlen, mech) \ - (wordlen == (sizeof(mech) - 1) / sizeof(char) && \ + ((wordlen) == (sizeof(mech) - 1) / sizeof(char) && \ !memcmp(line, mech, wordlen)) /* Convert a mechanism name to a token */ @@ -147,11 +147,11 @@ /* Check if we have enough auth data and capabilities to authenticate */ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data); -/* Calculate the required login details for SASL authentication */ +/* Calculate the required login details for SASL authentication */ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, bool force_ir, saslprogress *progress); -/* Continue an SASL authentication */ +/* Continue an SASL authentication */ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, int code, saslprogress *progress);
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index b976838..2494152 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -89,12 +89,8 @@ #ifdef _MSC_VER /* Disable Visual Studio warnings: 4127 "conditional expression is constant" */ #pragma warning(disable:4127) -/* Avoid VS2005 and upper complaining about portable C functions. */ -#ifndef _CRT_NONSTDC_NO_DEPRECATE /* mingw-w64 v2+. MS SDK ~10+/~VS2017+. */ -#define _CRT_NONSTDC_NO_DEPRECATE /* for close(), fileno(), unlink(), etc. */ -#endif #ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS /* for getenv(), tests: sscanf() */ +#define _CRT_SECURE_NO_WARNINGS /* for getenv(), sscanf() */ #endif #endif /* _MSC_VER */ @@ -110,6 +106,7 @@ # ifndef NOGDI # define NOGDI # endif + /* Detect Windows App environment which has a restricted access * to the Win32 APIs. */ # if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \ @@ -120,6 +117,12 @@ # define CURL_WINDOWS_UWP # endif # endif + +/* Mandatory to define SECURITY_WIN32 or SECURITY_KERNEL to indicating who is + compiling the code. */ +#undef SECURITY_KERNEL +#undef SECURITY_WIN32 +#define SECURITY_WIN32 /* for <sspi.h> */ #endif /* Compatibility */ @@ -154,16 +157,24 @@ # include "config-os400.h" #endif -#ifdef __PLAN9__ -# include "config-plan9.h" -#endif - #endif /* HAVE_CONFIG_H */ #if defined(_MSC_VER) # pragma warning(push,1) #endif +#ifdef _WIN32 +# if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600) +# error The minimum build target is Windows Vista (0x0600) +# endif + +# if !defined(CURL_WINDOWS_UWP) && (defined(_MSC_VER) || defined(__MINGW32__)) +# ifndef HAVE_IF_NAMETOINDEX +# define HAVE_IF_NAMETOINDEX +# endif +# endif +#endif + /* ================================================================ */ /* Definition of preprocessor macros/symbols which modify compiler */ /* behavior or generated code characteristics must be done here, */ @@ -213,7 +224,7 @@ /* ================================================================ */ /* Give calloc a chance to be dragging in early, so we do not redefine */ -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) +#ifdef HAVE_THREADS_POSIX # include <pthread.h> #endif @@ -251,9 +262,6 @@ # ifndef CURL_DISABLE_RTSP # define CURL_DISABLE_RTSP # endif -# ifndef CURL_DISABLE_SMB -# define CURL_DISABLE_SMB -# endif # ifndef CURL_DISABLE_SMTP # define CURL_DISABLE_SMTP # endif @@ -263,6 +271,9 @@ # ifndef CURL_DISABLE_TFTP # define CURL_DISABLE_TFTP # endif +# ifndef CURL_DISABLE_WEBSOCKETS +# define CURL_DISABLE_WEBSOCKETS +# endif #endif /* @@ -276,16 +287,39 @@ * When HTTP is disabled, disable HTTP-only features */ #ifdef CURL_DISABLE_HTTP -# define CURL_DISABLE_ALTSVC 1 -# define CURL_DISABLE_COOKIES 1 -# define CURL_DISABLE_BASIC_AUTH 1 -# define CURL_DISABLE_BEARER_AUTH 1 -# define CURL_DISABLE_AWS 1 -# define CURL_DISABLE_DOH 1 -# define CURL_DISABLE_FORM_API 1 -# define CURL_DISABLE_HEADERS_API 1 -# define CURL_DISABLE_HSTS 1 -# define CURL_DISABLE_HTTP_AUTH 1 +# ifndef CURL_DISABLE_ALTSVC +# define CURL_DISABLE_ALTSVC +# endif +# ifndef CURL_DISABLE_COOKIES +# define CURL_DISABLE_COOKIES +# endif +# ifndef CURL_DISABLE_BASIC_AUTH +# define CURL_DISABLE_BASIC_AUTH +# endif +# ifndef CURL_DISABLE_BEARER_AUTH +# define CURL_DISABLE_BEARER_AUTH +# endif +# ifndef CURL_DISABLE_AWS +# define CURL_DISABLE_AWS +# endif +# ifndef CURL_DISABLE_DOH +# define CURL_DISABLE_DOH +# endif +# ifndef CURL_DISABLE_FORM_API +# define CURL_DISABLE_FORM_API +# endif +# ifndef CURL_DISABLE_HEADERS_API +# define CURL_DISABLE_HEADERS_API +# endif +# ifndef CURL_DISABLE_HSTS +# define CURL_DISABLE_HSTS +# endif +# ifndef CURL_DISABLE_HTTP_AUTH +# define CURL_DISABLE_HTTP_AUTH +# endif +# ifndef CURL_DISABLE_WEBSOCKETS +# define CURL_DISABLE_WEBSOCKETS /* no WebSockets without HTTP present */ +# endif #endif /* ================================================================ */ @@ -335,16 +369,14 @@ #endif /* based on logic in "curl/mprintf.h" */ -#if (defined(__GNUC__) || defined(__clang__) || \ - defined(__IAR_SYSTEMS_ICC__)) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ +#if (defined(__GNUC__) || defined(__clang__) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ !defined(CURL_NO_FMT_CHECKS) #if defined(__MINGW32__) && !defined(__clang__) -#define CURL_PRINTF(fmt, arg) \ - __attribute__((format(gnu_printf, fmt, arg))) +#define CURL_PRINTF(fmt, arg) __attribute__((format(gnu_printf, fmt, arg))) #else -#define CURL_PRINTF(fmt, arg) \ - __attribute__((format(__printf__, fmt, arg))) +#define CURL_PRINTF(fmt, arg) __attribute__((format(__printf__, fmt, arg))) #endif #else #define CURL_PRINTF(fmt, arg) @@ -362,7 +394,7 @@ (defined(__GNUC__) && __GNUC__ <= 14)) && \ defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ !defined(__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__) -#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \ +#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif @@ -409,7 +441,7 @@ # if !(defined(__NEWLIB__) || \ (defined(__CLIB2__) && defined(__THREAD_SAFE))) /* disable threaded resolver with clib2 - requires newlib or clib-ts */ -# undef USE_THREADS_POSIX +# undef USE_RESOLV_THREADED # endif # endif # include <exec/types.h> @@ -449,6 +481,10 @@ #define USE_EVENTFD #endif +#ifdef SO_NOSIGPIPE +#define USE_SO_NOSIGPIPE +#endif + #include <stdio.h> #include <assert.h> @@ -462,8 +498,19 @@ #include <curl/stdcheaders.h> #endif -#if defined(HAVE_STDINT_H) || defined(USE_WOLFSSL) #include <stdint.h> +#define HAVE_UINTPTR_T /* assume uintptr_t is provided by stdint.h */ + +#ifdef __DJGPP__ +/* By default, DJGPP provides this type as a version of 'unsigned long' which + forces us to use a define use it in printf() format strings without + warnings. long and int are both 32 bits for this platform. */ +#define uint32_t unsigned int +#endif + +/* Disable uintptr_t for targets known to miss it from stdint.h */ +#ifdef __OS400__ +#undef HAVE_UINTPTR_T #endif #include <limits.h> @@ -486,26 +533,19 @@ # include <sys/types.h> # include <sys/stat.h> /* Large file (>2Gb) support using Win32 functions. */ -# undef lseek -# define lseek(fdes, offset, whence) _lseeki64(fdes, offset, whence) -# undef fstat -# define fstat(fdes, stp) _fstati64(fdes, stp) -# define struct_stat struct _stati64 -# define LSEEK_ERROR (__int64)-1 +# define curl_lseek _lseeki64 +# define LSEEK_ERROR ((__int64)-1) #elif defined(__DJGPP__) /* Requires DJGPP 2.04 */ # include <unistd.h> -# undef lseek -# define lseek(fdes, offset, whence) llseek(fdes, offset, whence) -# define LSEEK_ERROR (offset_t)-1 -#endif - -#ifndef struct_stat -#define struct_stat struct stat -#endif - -#ifndef LSEEK_ERROR -#define LSEEK_ERROR (off_t)-1 +# define curl_lseek llseek +# define LSEEK_ERROR ((offset_t)-1) +#elif defined(__AMIGA__) +# define curl_lseek(fd, offset, whence) lseek(fd, (off_t)(offset), whence) +# define LSEEK_ERROR ((off_t)-1) +#else +# define curl_lseek lseek +# define LSEEK_ERROR ((off_t)-1) #endif #ifndef SIZEOF_TIME_T @@ -515,7 +555,7 @@ #ifndef SIZEOF_CURL_SOCKET_T /* configure and cmake check and set the define */ -# ifdef _WIN64 +# if defined(USE_WINSOCK) && defined(_WIN64) # define SIZEOF_CURL_SOCKET_T 8 # else /* default guess */ @@ -524,12 +564,12 @@ #endif #if SIZEOF_CURL_SOCKET_T < 8 -#ifdef _WIN32 +#ifdef USE_WINSOCK # define FMT_SOCKET_T "u" #else # define FMT_SOCKET_T "d" #endif -#elif defined(_WIN32) +#elif defined(USE_WINSOCK) # define FMT_SOCKET_T "zu" #else # define FMT_SOCKET_T "qd" @@ -655,6 +695,16 @@ #endif /* _WIN32 */ +/* We want to use mutex when available. */ +#if defined(HAVE_THREADS_POSIX) || defined(_WIN32) +#define USE_MUTEX +#endif + +/* threaded resolver is the only feature requiring threads. */ +#ifdef USE_RESOLV_THREADED +#define USE_THREADS +#endif + /* ---------------------------------------------------------------- */ /* resolver specialty compile-time defines */ /* CURLRES_* defines to use in the host*.c sources */ @@ -672,13 +722,11 @@ # define CURLRES_IPV4 #endif -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_RESOLV_THREADED # define CURLRES_ASYNCH -# define CURLRES_THREADED -#elif defined(USE_ARES) +#elif defined(USE_RESOLV_ARES) # define CURLRES_ASYNCH -# define CURLRES_ARES -/* now undef the stock libc functions just to avoid them being used */ +/* now undef the stock libc functions to avoid them being used */ # undef HAVE_GETADDRINFO # undef HAVE_FREEADDRINFO #else @@ -698,20 +746,14 @@ #endif #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ - defined(USE_RUSTLS) + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif #if defined(USE_OPENSSL) && defined(USE_WOLFSSL) -# include <wolfssl/version.h> -# if LIBWOLFSSL_VERSION_HEX >= 0x05007006 -# ifndef OPENSSL_COEXIST -# define OPENSSL_COEXIST -# endif -# else -# error "OpenSSL can only coexist with wolfSSL v5.7.6 or upper" -# endif +#ifndef OPENSSL_COEXIST +#define OPENSSL_COEXIST +#endif #endif #if defined(USE_WOLFSSL) && defined(USE_GNUTLS) @@ -720,24 +762,24 @@ #endif /* Single point where USE_SPNEGO definition might be defined */ -#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ - (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#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_KERBEROS_AUTH) && \ - (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#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 */ -#ifndef CURL_DISABLE_NTLM -# if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) || \ - defined(USE_GNUTLS) || \ - (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) || \ - defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ - (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) +#ifdef CURL_ENABLE_NTLM +# if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) || \ + defined(USE_GNUTLS) || \ + (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) || \ + defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ + (defined(USE_WOLFSSL) && defined(HAVE_WC_DES_ECBENCRYPT)) # define USE_CURL_NTLM_CORE # endif # if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI) @@ -749,6 +791,16 @@ #define USE_SSH #endif +/* GCC <4.6 does not support '#pragma GCC diagnostic push' and does not support + 'pragma GCC diagnostic' inside functions. + Use CURL_HAVE_DIAG to guard the above in the curl codebase, instead of + defined(__GNUC__) || defined(__clang__). + */ +#if defined(__clang__) || (defined(__GNUC__) && \ + ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)))) +#define CURL_HAVE_DIAG +#endif + /* * Provide a mechanism to silence picky compilers, such as gcc 4.6+. * Parameters should of course normally not be unused, but for example when @@ -779,31 +831,361 @@ /* fallthrough attribute */ #ifndef FALLTHROUGH -#if (defined(__GNUC__) && __GNUC__ >= 7) || \ - (defined(__clang__) && __clang_major__ >= 10) +#if (defined(__GNUC__) && __GNUC__ >= 7) || \ + (defined(__clang__) && __clang_major__ >= 10) # define FALLTHROUGH() __attribute__((fallthrough)) #else -# define FALLTHROUGH() do {} while (0) +# define FALLTHROUGH() do {} while(0) #endif #endif /* - * Include macros and defines that should only be processed once. + * Inclusion of common header files. */ -#ifndef HEADER_CURL_SETUP_ONCE_H -#include "curl_setup_once.h" + +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> +#include <errno.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> #endif +#include <sys/stat.h> + +#if !defined(_WIN32) || defined(__MINGW32__) +#include <sys/time.h> +#endif + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#if defined(HAVE_STDBOOL_H) && defined(HAVE_BOOL_T) +#include <stdbool.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/* Macro to strip 'const' without triggering a compiler warning. + Use it for APIs that do not or cannot support the const qualifier. */ +#ifdef HAVE_UINTPTR_T +#define CURL_UNCONST(p) ((void *)(uintptr_t)(const void *)(p)) +#else +#define CURL_UNCONST(p) ((void *)(p)) /* Fall back to simple cast */ +#endif + +#ifdef USE_SCHANNEL +/* Must set this before <schannel.h> is included directly or indirectly by + another Windows header. */ +# define SCHANNEL_USE_BLACKLISTS /* for SCH_CREDENTIALS */ +# include <subauth.h> /* for [P]UNICODE_STRING in SCH_CREDENTIALS */ +#endif + +#ifdef __hpux +# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) +# ifdef _APP32_64BIT_OFF_T +# define OLD_APP32_64BIT_OFF_T _APP32_64BIT_OFF_T +# undef _APP32_64BIT_OFF_T +# else +# undef OLD_APP32_64BIT_OFF_T +# endif +# endif +#endif + +#ifndef _WIN32 +#include <sys/socket.h> /* also for MSG_NOSIGNAL */ +#endif + +#include "functypes.h" + +#ifdef __hpux +# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) +# ifdef OLD_APP32_64BIT_OFF_T +# define _APP32_64BIT_OFF_T OLD_APP32_64BIT_OFF_T +# undef OLD_APP32_64BIT_OFF_T +# endif +# endif +#endif + +/* + * Definition of timeval struct for platforms that do not have it. + */ +#ifndef HAVE_STRUCT_TIMEVAL +struct timeval { + long tv_sec; + long tv_usec; +}; +#endif + +/* + * If we have the MSG_NOSIGNAL define, make sure we use + * it as the fourth argument of function send() + */ +#ifdef MSG_NOSIGNAL +#define SEND_4TH_ARG MSG_NOSIGNAL +#else +#define SEND_4TH_ARG 0 +#endif + +#ifdef __minix +/* Minix does not support recv on TCP sockets */ +#define sread(x, y, z) (ssize_t)read((RECV_TYPE_ARG1)(x), \ + (RECV_TYPE_ARG2)(y), \ + (RECV_TYPE_ARG3)(z)) + +#elif defined(HAVE_RECV) +/* + * The definitions for the return type and arguments types + * of functions recv() and send() belong and come from the + * configuration file. Do not define them in any other place. + * + * HAVE_RECV is defined if you have a function named recv() + * which is used to read incoming data from sockets. If your + * function has another name then do not define HAVE_RECV. + * + * If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2, + * RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also + * be defined. + * + * HAVE_SEND is defined if you have a function named send() + * which is used to write outgoing data on a connected socket. + * If yours has another name then do not define HAVE_SEND. + * + * If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_TYPE_ARG2, + * SEND_TYPE_ARG3, SEND_TYPE_ARG4 and SEND_TYPE_RETV must also + * be defined. SEND_NONCONST_ARG2 must also be defined if ARG2 + * does not accept const. + */ + +#define sread(x, y, z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \ + (RECV_TYPE_ARG2)(y), \ + (RECV_TYPE_ARG3)(z), \ + (RECV_TYPE_ARG4)(0)) +#else /* HAVE_RECV */ +#ifndef sread +#error "Missing definition of macro sread!" +#endif +#endif /* HAVE_RECV */ + +#ifdef __minix +/* Minix does not support send on TCP sockets */ +#define swrite(x, y, z) (ssize_t)write((SEND_TYPE_ARG1)(x), \ + (SEND_TYPE_ARG2)CURL_UNCONST(y), \ + (SEND_TYPE_ARG3)(z)) +#elif defined(HAVE_SEND) +#ifdef SEND_NONCONST_ARG2 +#define swrite(x, y, z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ + (SEND_TYPE_ARG2)CURL_UNCONST(y), \ + (SEND_TYPE_ARG3)(z), \ + (SEND_TYPE_ARG4)(SEND_4TH_ARG)) +#else +#define swrite(x, y, z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ + (const SEND_TYPE_ARG2)(y), \ + (SEND_TYPE_ARG3)(z), \ + (SEND_TYPE_ARG4)(SEND_4TH_ARG)) +#endif /* SEND_NONCONST_ARG2 */ +#else /* HAVE_SEND */ +#ifndef swrite +#error "Missing definition of macro swrite!" +#endif +#endif /* HAVE_SEND */ + +/* + * Function-like macro definition used to close a socket. + */ +#ifdef HAVE_CLOSESOCKET +# define CURL_SCLOSE(x) closesocket(x) +#elif defined(HAVE_CLOSESOCKET_CAMEL) +# define CURL_SCLOSE(x) CloseSocket(x) +#elif defined(MSDOS) /* Watt-32 */ +# define CURL_SCLOSE(x) close_s(x) +#elif defined(USE_LWIPSOCK) +# define CURL_SCLOSE(x) lwip_close(x) +#else +# define CURL_SCLOSE(x) close(x) +#endif + +/* + * Stack-independent version of fcntl() on sockets: + */ +#ifdef USE_LWIPSOCK +# define sfcntl lwip_fcntl +#else +# define sfcntl fcntl +#endif + +/* + * 'bool' stuff compatible with HP-UX headers. + */ +#if defined(__hpux) && !defined(HAVE_BOOL_T) +typedef int bool; +# define false 0 +# define true 1 +# define HAVE_BOOL_T +#endif + +/* + * 'bool' exists on platforms with <stdbool.h>, i.e. C99 platforms. + * On non-C99 platforms there is no bool, so define an enum for that. + * On C99 platforms 'false' and 'true' also exist. Enum uses a + * global namespace though, so use bool_false and bool_true. + */ +#ifndef HAVE_BOOL_T +typedef enum { + bool_false = 0, + bool_true = 1 +} bool; + +/* + * Use a define to let 'true' and 'false' use those enums. There + * are currently no use of true and false in libcurl proper, but + * there are some in the examples. This will cater for any later + * code happening to use true and false. + */ +# define false bool_false +# define true bool_true +# define HAVE_BOOL_T +#endif + +/* the type we use for storing a single boolean bit */ +typedef unsigned int curl_bit; +#define BIT(x) curl_bit x:1 + +/* + * Redefine TRUE and FALSE too, to catch current use. With this + * change, 'bool found = 1' will give a warning on MIPSPro, but + * 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro, + * AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too. + */ +#ifndef TRUE +#define TRUE true +#endif +#ifndef FALSE +#define FALSE false +#endif + +#include "curl_ctype.h" + +/* + * Macro used to include code only in debug builds. + */ +#ifdef DEBUGBUILD +#define DEBUGF(x) x +#else +#define DEBUGF(x) do {} while(0) +#endif + +/* + * Macro used to include assertion code only in debug builds. + */ +#undef DEBUGASSERT +#ifdef DEBUGBUILD +#ifdef CURL_DEBUGASSERT +/* External assertion handler for custom integrations */ +#define DEBUGASSERT(x) CURL_DEBUGASSERT(x) +#else +#define DEBUGASSERT(x) assert(x) +#endif +#else +#define DEBUGASSERT(x) do {} while(0) +#endif + +/* + * Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno + * (or equivalent) on this platform to hide platform details to code using it. + */ +#ifdef USE_WINSOCK +#define SOCKERRNO ((int)WSAGetLastError()) +#define SET_SOCKERRNO(x) WSASetLastError((int)(x)) +#else +#define SOCKERRNO errno +#define SET_SOCKERRNO(x) (errno = (x)) +#endif + +/* + * Portable error number symbolic names defined to Winsock error codes. + */ +#ifdef USE_WINSOCK +#define SOCKEACCES WSAEACCES +#define SOCKEADDRINUSE WSAEADDRINUSE +#define SOCKEADDRNOTAVAIL WSAEADDRNOTAVAIL +#define SOCKEAFNOSUPPORT WSAEAFNOSUPPORT +#define SOCKEBADF WSAEBADF +#define SOCKECONNREFUSED WSAECONNREFUSED +#define SOCKECONNRESET WSAECONNRESET +#define SOCKEINPROGRESS WSAEINPROGRESS +#define SOCKEINTR WSAEINTR +#define SOCKEINVAL WSAEINVAL +#define SOCKEISCONN WSAEISCONN +#define SOCKEMSGSIZE WSAEMSGSIZE +/* Use literal value to work around clang-tidy <=20 misreporting + 'readability-uppercase-literal-suffix' with mingw-w64 headers */ +#define SOCKENOMEM 8L /* WSA_NOT_ENOUGH_MEMORY */ +#define SOCKETIMEDOUT WSAETIMEDOUT +#define SOCKEWOULDBLOCK WSAEWOULDBLOCK +#else +#define SOCKEACCES EACCES +#define SOCKEADDRINUSE EADDRINUSE +#define SOCKEADDRNOTAVAIL EADDRNOTAVAIL +#define SOCKEAFNOSUPPORT EAFNOSUPPORT +#define SOCKEBADF EBADF +#define SOCKECONNREFUSED ECONNREFUSED +#define SOCKECONNRESET ECONNRESET +#define SOCKEINPROGRESS EINPROGRESS +#define SOCKEINTR EINTR +#define SOCKEINVAL EINVAL +#define SOCKEISCONN EISCONN +#define SOCKEMSGSIZE EMSGSIZE +#define SOCKENOMEM ENOMEM +#ifdef ETIMEDOUT +#define SOCKETIMEDOUT ETIMEDOUT +#endif +#define SOCKEWOULDBLOCK EWOULDBLOCK +#endif + +/* + * Macro argv_item_t hides platform details to code using it. + */ +#ifdef __VMS +#define argv_item_t __char_ptr32 +#elif defined(_UNICODE) +#define argv_item_t wchar_t * +#else +#define argv_item_t char * +#endif + +/* + * We use this ZERO_NULL to avoid picky compiler warnings, + * when assigning a NULL pointer to a function pointer var. + */ +#define ZERO_NULL 0 + /* * Macros and functions to safely suppress warnings */ #include "curlx/warnless.h" #ifdef _WIN32 -# undef read +# undef read # define read(fd, buf, count) (ssize_t)_read(fd, buf, curlx_uztoui(count)) -# undef write +# undef write # define write(fd, buf, count) (ssize_t)_write(fd, buf, curlx_uztoui(count)) +/* Avoid VS2005+ _CRT_NONSTDC_NO_DEPRECATE warnings about non-portable funcs */ +# undef fileno +# define fileno(fh) _fileno(fh) +# undef unlink +# define unlink(fn) _unlink(fn) +# undef isatty +# define isatty(fd) _isatty(fd) #endif /* @@ -860,12 +1242,23 @@ /* Since O_BINARY is used in bitmasks, setting it to zero makes it usable in source code but yet it does not ruin anything */ -#ifdef O_BINARY +#ifdef _O_BINARY /* for _WIN32 || MSDOS */ +#define CURL_O_BINARY _O_BINARY +#elif defined(O_BINARY) /* __CYGWIN__ */ #define CURL_O_BINARY O_BINARY #else #define CURL_O_BINARY 0 #endif +/* Requires io.h when available */ +#ifdef MSDOS +#define CURL_BINMODE(stream) (void)setmode(fileno(stream), CURL_O_BINARY) +#elif defined(_WIN32) || defined(__CYGWIN__) +#define CURL_BINMODE(stream) (void)_setmode(fileno(stream), CURL_O_BINARY) +#else +#define CURL_BINMODE(stream) (void)(stream) +#endif + /* In Windows the default file mode is text but an application can override it. Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 */ @@ -891,7 +1284,7 @@ /* for systems that do not detect this in configure */ #ifndef CURL_SA_FAMILY_T -# ifdef _WIN32 +# ifdef USE_WINSOCK # define CURL_SA_FAMILY_T ADDRESS_FAMILY # elif defined(HAVE_SA_FAMILY_T) # define CURL_SA_FAMILY_T sa_family_t @@ -941,19 +1334,23 @@ extern curl_calloc_callback Curl_ccalloc; /* - * Curl_safefree defined as a macro to allow MemoryTracking feature - * to log free() calls at same location where Curl_safefree is used. + * curlx_safefree() defined as a macro to allow MemoryTracking feature + * to log free() calls at same location where curlx_safefree() is used. * This macro also assigns NULL to given pointer when free'd. */ -#define Curl_safefree(ptr) \ - do { \ - curlx_free(ptr); \ - (ptr) = NULL; \ +#define curlx_safefree(ptr) \ + do { \ + curlx_free(ptr); \ + (ptr) = NULL; \ } while(0) #include <curl/curl.h> /* for CURL_EXTERN, curl_socket_t, mprintf.h */ -#ifdef CURLDEBUG +#ifdef DEBUGBUILD +#define CURL_MEMDEBUG +#endif + +#ifdef CURL_MEMDEBUG #ifdef __clang__ # define ALLOC_FUNC __attribute__((__malloc__)) # if __clang_major__ >= 4 @@ -1005,7 +1402,8 @@ int line, const char *source); CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source); -CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen, +CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, + void *saddr, void *saddrlen, int line, const char *source); #ifdef HAVE_ACCEPT4 CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, @@ -1036,10 +1434,10 @@ #define CURL_FREEADDRINFO(data) \ curl_dbg_freeaddrinfo(data, __LINE__, __FILE__) #define CURL_SOCKET(domain, type, protocol) \ - curl_dbg_socket((int)domain, type, protocol, __LINE__, __FILE__) + curl_dbg_socket((int)(domain), type, protocol, __LINE__, __FILE__) #ifdef HAVE_SOCKETPAIR #define CURL_SOCKETPAIR(domain, type, protocol, socket_vector) \ - curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \ + curl_dbg_socketpair((int)(domain), type, protocol, socket_vector, \ __LINE__, __FILE__) #endif #define CURL_ACCEPT(sock, addr, len) \ @@ -1049,7 +1447,7 @@ curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__) #endif -#else /* !CURLDEBUG */ +#else /* !CURL_MEMDEBUG */ #define sclose(x) CURL_SCLOSE(x) #define fake_sclose(x) Curl_nop_stmt @@ -1065,11 +1463,17 @@ #define CURL_ACCEPT4 accept4 #endif -#endif /* CURLDEBUG */ +#endif /* CURL_MEMDEBUG */ /* Allocator macros */ -#ifdef CURLDEBUG +#ifdef _WIN32 +#define CURLX_STRDUP_LOW _strdup +#else +#define CURLX_STRDUP_LOW strdup +#endif + +#ifdef CURL_MEMDEBUG #define curlx_strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) #define curlx_malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__) @@ -1083,39 +1487,35 @@ #ifdef UNICODE #define curlx_tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) #else -#define curlx_tcsdup(ptr) curlx_strdup(ptr) +#define curlx_tcsdup curlx_strdup #endif #endif /* _WIN32 */ -#else /* !CURLDEBUG */ +#else /* !CURL_MEMDEBUG */ #ifdef BUILDING_LIBCURL -#define curlx_strdup(ptr) Curl_cstrdup(ptr) -#define curlx_malloc(size) Curl_cmalloc(size) -#define curlx_calloc(nbelem, size) Curl_ccalloc(nbelem, size) -#define curlx_realloc(ptr, size) Curl_crealloc(ptr, size) -#define curlx_free(ptr) Curl_cfree(ptr) +#define curlx_strdup Curl_cstrdup +#define curlx_malloc Curl_cmalloc +#define curlx_calloc Curl_ccalloc +#define curlx_realloc Curl_crealloc +#define curlx_free Curl_cfree #else /* !BUILDING_LIBCURL */ -#ifdef _WIN32 -#define curlx_strdup(ptr) _strdup(ptr) -#else -#define curlx_strdup(ptr) strdup(ptr) -#endif -#define curlx_malloc(size) malloc(size) -#define curlx_calloc(nbelem, size) calloc(nbelem, size) -#define curlx_realloc(ptr, size) realloc(ptr, size) -#define curlx_free(ptr) free(ptr) +#define curlx_strdup CURLX_STRDUP_LOW +#define curlx_malloc malloc +#define curlx_calloc calloc +#define curlx_realloc realloc +#define curlx_free free #endif /* BUILDING_LIBCURL */ #ifdef _WIN32 #ifdef UNICODE -#define curlx_tcsdup(ptr) Curl_wcsdup(ptr) +#define curlx_tcsdup curlx_wcsdup #else -#define curlx_tcsdup(ptr) curlx_strdup(ptr) +#define curlx_tcsdup curlx_strdup #endif #endif /* _WIN32 */ -#endif /* CURLDEBUG */ +#endif /* CURL_MEMDEBUG */ /* Some versions of the Android NDK is missing the declaration */ #if defined(HAVE_GETPWUID_R) && \ @@ -1135,9 +1535,7 @@ #define USE_HTTP2 #endif -#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ - (defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \ - defined(USE_QUICHE) +#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE) #ifdef CURL_WITH_MULTI_SSL #error "MultiSSL combined with QUIC is not supported" @@ -1173,8 +1571,11 @@ #endif #ifdef USE_OPENSSL -/* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no - replacements (yet) so tell the compiler to not warn for them. */ +/* OpenSSL 3 marks these functions deprecated but we have no replacements (yet) + so tell the compiler to not warn for them: + - DES_* (for NTLM), SSL_CTX_set_srp_* (for TLS-SRP) + - EVP_PKEY_get1_RSA, MD5_*, RSA_flags, RSA_free (auto-skipped for OpenSSL + built with no-deprecated) */ # define OPENSSL_SUPPRESS_DEPRECATED # ifdef _WIN32 /* Silence LibreSSL warnings about wincrypt.h collision. Works in 3.8.2+ */ @@ -1206,4 +1607,20 @@ # define CURL_INLINE /* empty */ #endif +/* Detect if compiler supports C99 variadic macros */ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ + defined(_MSC_VER) +#define CURL_HAVE_MACRO_VARARG +#endif + +#if !defined(CURL_HAVE_MACRO_VARARG) || \ + (defined(CURL_HAVE_MACRO_VARARG) && !defined(CURL_DISABLE_VERBOSE_STRINGS)) +#define CURLVERBOSE +#define VERBOSE(x) x +#define NOVERBOSE(x) Curl_nop_stmt +#else +#define VERBOSE(x) Curl_nop_stmt +#define NOVERBOSE(x) x +#endif + #endif /* HEADER_CURL_SETUP_H */
diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h deleted file mode 100644 index 06ccc98..0000000 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ /dev/null
@@ -1,346 +0,0 @@ -#ifndef HEADER_CURL_SETUP_ONCE_H -#define HEADER_CURL_SETUP_ONCE_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 - * - ***************************************************************************/ - -/* - * Inclusion of common header files. - */ - -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <time.h> -#include <errno.h> - -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif - -#include <sys/stat.h> - -#if !defined(_WIN32) || defined(__MINGW32__) -#include <sys/time.h> -#endif - -#ifdef HAVE_IO_H -#include <io.h> -#endif - -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#if defined(HAVE_STDBOOL_H) && defined(HAVE_BOOL_T) -#include <stdbool.h> -#endif - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -/* Macro to strip 'const' without triggering a compiler warning. - Use it for APIs that do not or cannot support the const qualifier. */ -#ifdef HAVE_STDINT_H -# define CURL_UNCONST(p) ((void *)(uintptr_t)(const void *)(p)) -#else -# define CURL_UNCONST(p) ((void *)(p)) /* Fall back to simple cast */ -#endif - -#ifdef USE_SCHANNEL -/* Must set this before <schannel.h> is included directly or indirectly by - another Windows header. */ -# define SCHANNEL_USE_BLACKLISTS 1 -#endif - -#ifdef __hpux -# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) -# ifdef _APP32_64BIT_OFF_T -# define OLD_APP32_64BIT_OFF_T _APP32_64BIT_OFF_T -# undef _APP32_64BIT_OFF_T -# else -# undef OLD_APP32_64BIT_OFF_T -# endif -# endif -#endif - -#ifndef _WIN32 -#include <sys/socket.h> -#endif - -#include "functypes.h" - -#ifdef __hpux -# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) -# ifdef OLD_APP32_64BIT_OFF_T -# define _APP32_64BIT_OFF_T OLD_APP32_64BIT_OFF_T -# undef OLD_APP32_64BIT_OFF_T -# endif -# endif -#endif - -/* - * Definition of timeval struct for platforms that do not have it. - */ -#ifndef HAVE_STRUCT_TIMEVAL -struct timeval { - long tv_sec; - long tv_usec; -}; -#endif - -/* - * If we have the MSG_NOSIGNAL define, make sure we use - * it as the fourth argument of function send() - */ -#ifdef HAVE_MSG_NOSIGNAL -#define SEND_4TH_ARG MSG_NOSIGNAL -#else -#define SEND_4TH_ARG 0 -#endif - -#ifdef __minix -/* Minix does not support recv on TCP sockets */ -#define sread(x, y, z) (ssize_t)read((RECV_TYPE_ARG1)(x), \ - (RECV_TYPE_ARG2)(y), \ - (RECV_TYPE_ARG3)(z)) - -#elif defined(HAVE_RECV) -/* - * The definitions for the return type and arguments types - * of functions recv() and send() belong and come from the - * configuration file. Do not define them in any other place. - * - * HAVE_RECV is defined if you have a function named recv() - * which is used to read incoming data from sockets. If your - * function has another name then do not define HAVE_RECV. - * - * If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2, - * RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also - * be defined. - * - * HAVE_SEND is defined if you have a function named send() - * which is used to write outgoing data on a connected socket. - * If yours has another name then do not define HAVE_SEND. - * - * If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_QUAL_ARG2, - * SEND_TYPE_ARG2, SEND_TYPE_ARG3, SEND_TYPE_ARG4 and - * SEND_TYPE_RETV must also be defined. - */ - -#define sread(x, y, z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \ - (RECV_TYPE_ARG2)(y), \ - (RECV_TYPE_ARG3)(z), \ - (RECV_TYPE_ARG4)(0)) -#else /* HAVE_RECV */ -#ifndef sread -#error "Missing definition of macro sread!" -#endif -#endif /* HAVE_RECV */ - -#ifdef __minix -/* Minix does not support send on TCP sockets */ -#define swrite(x, y, z) (ssize_t)write((SEND_TYPE_ARG1)(x), \ - (SEND_TYPE_ARG2)CURL_UNCONST(y), \ - (SEND_TYPE_ARG3)(z)) -#elif defined(HAVE_SEND) -#define swrite(x, y, z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ - (SEND_QUAL_ARG2 SEND_TYPE_ARG2)CURL_UNCONST(y), \ - (SEND_TYPE_ARG3)(z), \ - (SEND_TYPE_ARG4)(SEND_4TH_ARG)) -#else /* HAVE_SEND */ -#ifndef swrite -#error "Missing definition of macro swrite!" -#endif -#endif /* HAVE_SEND */ - -/* - * Function-like macro definition used to close a socket. - */ -#ifdef HAVE_CLOSESOCKET -# define CURL_SCLOSE(x) closesocket(x) -#elif defined(HAVE_CLOSESOCKET_CAMEL) -# define CURL_SCLOSE(x) CloseSocket(x) -#elif defined(MSDOS) /* Watt-32 */ -# define CURL_SCLOSE(x) close_s(x) -#elif defined(USE_LWIPSOCK) -# define CURL_SCLOSE(x) lwip_close(x) -#else -# define CURL_SCLOSE(x) close(x) -#endif - -/* - * Stack-independent version of fcntl() on sockets: - */ -#ifdef USE_LWIPSOCK -# define sfcntl lwip_fcntl -#else -# define sfcntl fcntl -#endif - -/* - * 'bool' stuff compatible with HP-UX headers. - */ -#if defined(__hpux) && !defined(HAVE_BOOL_T) - typedef int bool; -# define false 0 -# define true 1 -# define HAVE_BOOL_T -#endif - -/* - * 'bool' exists on platforms with <stdbool.h>, i.e. C99 platforms. - * On non-C99 platforms there is no bool, so define an enum for that. - * On C99 platforms 'false' and 'true' also exist. Enum uses a - * global namespace though, so use bool_false and bool_true. - */ -#ifndef HAVE_BOOL_T - typedef enum { - bool_false = 0, - bool_true = 1 - } bool; - -/* - * Use a define to let 'true' and 'false' use those enums. There - * are currently no use of true and false in libcurl proper, but - * there are some in the examples. This will cater for any later - * code happening to use true and false. - */ -# define false bool_false -# define true bool_true -# define HAVE_BOOL_T -#endif - -/* the type we use for storing a single boolean bit */ -#ifdef _MSC_VER -typedef bool bit; -#define BIT(x) bool x -#else -typedef unsigned int bit; -#define BIT(x) bit x:1 -#endif - -/* - * Redefine TRUE and FALSE too, to catch current use. With this - * change, 'bool found = 1' will give a warning on MIPSPro, but - * 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro, - * AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too. - */ -#ifndef TRUE -#define TRUE true -#endif -#ifndef FALSE -#define FALSE false -#endif - -#include "curl_ctype.h" - -/* - * Macro used to include code only in debug builds. - */ -#ifdef DEBUGBUILD -#define DEBUGF(x) x -#else -#define DEBUGF(x) do {} while(0) -#endif - -/* - * Macro used to include assertion code only in debug builds. - */ -#undef DEBUGASSERT -#ifdef DEBUGBUILD -#define DEBUGASSERT(x) assert(x) -#else -#define DEBUGASSERT(x) do {} while(0) -#endif - -/* - * Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno - * (or equivalent) on this platform to hide platform details to code using it. - */ -#ifdef USE_WINSOCK -#define SOCKERRNO ((int)WSAGetLastError()) -#define SET_SOCKERRNO(x) (WSASetLastError((int)(x))) -#else -#define SOCKERRNO (errno) -#define SET_SOCKERRNO(x) (errno = (x)) -#endif - -/* - * Portable error number symbolic names defined to Winsock error codes. - */ -#ifdef USE_WINSOCK -#define SOCKEACCES WSAEACCES -#define SOCKEADDRINUSE WSAEADDRINUSE -#define SOCKEADDRNOTAVAIL WSAEADDRNOTAVAIL -#define SOCKEAFNOSUPPORT WSAEAFNOSUPPORT -#define SOCKEBADF WSAEBADF -#define SOCKECONNREFUSED WSAECONNREFUSED -#define SOCKECONNRESET WSAECONNRESET -#define SOCKEINPROGRESS WSAEINPROGRESS -#define SOCKEINTR WSAEINTR -#define SOCKEINVAL WSAEINVAL -#define SOCKEISCONN WSAEISCONN -#define SOCKEMSGSIZE WSAEMSGSIZE -#define SOCKENOMEM WSA_NOT_ENOUGH_MEMORY -#define SOCKETIMEDOUT WSAETIMEDOUT -#define SOCKEWOULDBLOCK WSAEWOULDBLOCK -#else -#define SOCKEACCES EACCES -#define SOCKEADDRINUSE EADDRINUSE -#define SOCKEADDRNOTAVAIL EADDRNOTAVAIL -#define SOCKEAFNOSUPPORT EAFNOSUPPORT -#define SOCKEBADF EBADF -#define SOCKECONNREFUSED ECONNREFUSED -#define SOCKECONNRESET ECONNRESET -#define SOCKEINPROGRESS EINPROGRESS -#define SOCKEINTR EINTR -#define SOCKEINVAL EINVAL -#define SOCKEISCONN EISCONN -#define SOCKEMSGSIZE EMSGSIZE -#define SOCKENOMEM ENOMEM -#ifdef ETIMEDOUT -#define SOCKETIMEDOUT ETIMEDOUT -#endif -#define SOCKEWOULDBLOCK EWOULDBLOCK -#endif - -/* - * Macro argv_item_t hides platform details to code using it. - */ -#ifdef __VMS -#define argv_item_t __char_ptr32 -#elif defined(_UNICODE) -#define argv_item_t wchar_t * -#else -#define argv_item_t char * -#endif - -/* - * We use this ZERO_NULL to avoid picky compiler warnings, - * when assigning a NULL pointer to a function pointer var. - */ -#define ZERO_NULL 0 - -#endif /* HEADER_CURL_SETUP_ONCE_H */
diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h index 0aaa412..6c48a8e 100644 --- a/Utilities/cmcurl/lib/curl_sha256.h +++ b/Utilities/cmcurl/lib/curl_sha256.h
@@ -37,8 +37,8 @@ #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ #endif -CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input, - const size_t len); +CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, + size_t len); #endif
diff --git a/Utilities/cmcurl/lib/curl_sha512_256.c b/Utilities/cmcurl/lib/curl_sha512_256.c index 44ba9be..73b959c 100644 --- a/Utilities/cmcurl/lib/curl_sha512_256.c +++ b/Utilities/cmcurl/lib/curl_sha512_256.c
@@ -28,12 +28,12 @@ #include "curl_sha512_256.h" /* The recommended order of the TLS backends: - * 1. OpenSSL - * 2. GnuTLS - * 3. wolfSSL - * 4. Schannel SSPI - * 5. mbedTLS - * 6. Rustls + * 1. USE_OPENSSL + * 2. USE_WOLFSSL + * 3. USE_GNUTLS + * 4. USE_MBEDTLS (TBD) + * 5. USE_RUSTLS (TBD) + * 6. USE_WIN32_CRYPTO (TBD) * Skip the backend if it does not support the required algorithm */ #ifdef USE_OPENSSL @@ -53,7 +53,7 @@ * The bug was fixed in NetBSD 9.4 release, NetBSD 10.0 release, * NetBSD 10.99.11 development. * It is safe to apply the workaround even if the bug is not present, as - * the workaround just reduces performance slightly. */ + * the workaround reduces performance slightly. */ # include <sys/param.h> # if __NetBSD_Version__ < 904000000 || \ (__NetBSD_Version__ >= 999000000 && \ @@ -66,6 +66,14 @@ # endif #endif /* USE_OPENSSL */ +#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_WOLFSSL) +# include <wolfssl/options.h> +# ifndef WOLFSSL_NOSHA512_256 +# define USE_WOLFSSL_SHA512_256 1 +# define HAS_SHA512_256_IMPLEMENTATION 1 +# endif +#endif + #if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS) # include <nettle/sha.h> # ifdef SHA512_256_DIGEST_SIZE @@ -147,32 +155,69 @@ * * @param context the calculation context * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE - # bytes + * bytes * @return CURLE_OK if succeed, * error code otherwise */ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { - CURLcode ret; + CURLcode result; Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; #ifdef NEED_NETBSD_SHA512_256_WORKAROUND /* Use a larger buffer to work around a bug in NetBSD: https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */ unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2]; - ret = EVP_DigestFinal_ex(*ctx, + result = EVP_DigestFinal_ex(*ctx, tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; - if(ret == CURLE_OK) + if(result == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); #else /* !NEED_NETBSD_SHA512_256_WORKAROUND */ - ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; + result = EVP_DigestFinal_ex(*ctx, digest, NULL) ? + CURLE_OK : CURLE_SSL_CIPHER; #endif /* NEED_NETBSD_SHA512_256_WORKAROUND */ EVP_MD_CTX_destroy(*ctx); *ctx = NULL; - return ret; + return result; +} + +#elif defined(USE_WOLFSSL_SHA512_256) +#include <wolfssl/wolfcrypt/sha512.h> + +#define CURL_SHA512_256_DIGEST_SIZE WC_SHA512_256_DIGEST_SIZE +#define CURL_SHA512_256_BLOCK_SIZE WC_SHA512_256_BLOCK_SIZE + +typedef struct wc_Sha512 Curl_sha512_256_ctx; + +static CURLcode Curl_sha512_256_init(void *ctx) +{ + if(wc_InitSha512_256(ctx)) + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static CURLcode Curl_sha512_256_update(void *ctx, + const unsigned char *data, + size_t length) +{ + do { + word32 ilen = (word32)CURLMIN(length, UINT_MAX); + if(wc_Sha512_256Update(ctx, data, ilen)) + return CURLE_SSL_CIPHER; + length -= ilen; + data += ilen; + } while(length); + return CURLE_OK; +} + +static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) +{ + if(wc_Sha512_256Final(ctx, digest)) + return CURLE_SSL_CIPHER; + return CURLE_OK; } #elif defined(USE_GNUTLS_SHA512_256) @@ -229,7 +274,7 @@ * * @param context the calculation context * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE - # bytes + * bytes * @return always CURLE_OK */ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) @@ -247,10 +292,7 @@ /* ** This implementation of SHA-512/256 hash calculation was originally ** * * ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd. ** * * ** The author ported the code to libcurl. The ported code is provided ** * - * ** under curl license. ** * - * ** This is a minimal version with minimal optimizations. Performance ** * - * ** can be significantly improved. Big-endian store and load macros ** * - * ** are obvious targets for optimization. ** */ + * ** under curl license. ** */ #ifdef __GNUC__ # if defined(__has_attribute) && defined(__STDC_VERSION__) @@ -452,14 +494,14 @@ /* Four 'Sigma' macro functions. See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ -#define SIG0(x) \ - (Curl_rotr64((x), 28) ^ Curl_rotr64((x), 34) ^ Curl_rotr64((x), 39)) -#define SIG1(x) \ - (Curl_rotr64((x), 14) ^ Curl_rotr64((x), 18) ^ Curl_rotr64((x), 41)) -#define sig0(x) \ - (Curl_rotr64((x), 1) ^ Curl_rotr64((x), 8) ^ ((x) >> 7)) -#define sig1(x) \ - (Curl_rotr64((x), 19) ^ Curl_rotr64((x), 61) ^ ((x) >> 6)) +#define SIG0(x) \ + (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39)) +#define SIG1(x) \ + (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41)) +#define sig0(x) \ + (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7)) +#define sig1(x) \ + (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6)) if(1) { unsigned int t; @@ -520,8 +562,8 @@ used. */ #define SHA2STEP64(vA, vB, vC, vD, vE, vF, vG, vH, kt, wt) \ do { \ - (vD) += ((vH) += SIG1((vE)) + Sha512_Ch((vE), (vF), (vG)) + (kt) + (wt)); \ - (vH) += SIG0((vA)) + Sha512_Maj((vA), (vB), (vC)); \ + (vD) += ((vH) += SIG1(vE) + Sha512_Ch(vE, vF, vG) + (kt) + (wt)); \ + (vH) += SIG0(vA) + Sha512_Maj(vA, vB, vC); \ } while(0) /* One step of SHA-512/256 computation with working variables rotation, @@ -530,7 +572,7 @@ #define SHA2STEP64RV(vA, vB, vC, vD, vE, vF, vG, vH, kt, wt) \ do { \ uint64_t tmp_h_ = (vH); \ - SHA2STEP64((vA), (vB), (vC), (vD), (vE), (vF), (vG), tmp_h_, (kt), (wt)); \ + SHA2STEP64(vA, vB, vC, vD, vE, vF, vG, tmp_h_, kt, wt); \ (vH) = (vG); \ (vG) = (vF); \ (vF) = (vE); \ @@ -546,7 +588,7 @@ Input data must be read in big-endian bytes order, see FIPS PUB 180-4 section 3.1.2. */ #define SHA512_GET_W_FROM_DATA(buf, t) \ - CURL_GET_64BIT_BE(((const uint8_t *)(buf)) + (t) * SHA512_256_BYTES_IN_WORD) + CURL_GET_64BIT_BE((const uint8_t *)(buf) + ((t) * SHA512_256_BYTES_IN_WORD)) /* During first 16 steps, before making any calculation on each step, the W element is read from the input data buffer as a big-endian value and @@ -663,7 +705,7 @@ * * @param context the calculation context * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE - # bytes + * bytes * @return always CURLE_OK */ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) @@ -727,10 +769,10 @@ /* Put in BE mode the leftmost part of the hash as the final digest. See FIPS PUB 180-4 section 6.7. */ - CURL_PUT_64BIT_BE(digest + 0 * SHA512_256_BYTES_IN_WORD, ctx->H[0]); - CURL_PUT_64BIT_BE(digest + 1 * SHA512_256_BYTES_IN_WORD, ctx->H[1]); - CURL_PUT_64BIT_BE(digest + 2 * SHA512_256_BYTES_IN_WORD, ctx->H[2]); - CURL_PUT_64BIT_BE(digest + 3 * SHA512_256_BYTES_IN_WORD, ctx->H[3]); + CURL_PUT_64BIT_BE(digest + (0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]); + CURL_PUT_64BIT_BE(digest + (1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]); + CURL_PUT_64BIT_BE(digest + (2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]); + CURL_PUT_64BIT_BE(digest + (3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]); /* Erase potentially sensitive data. */ memset(ctx, 0, sizeof(struct Curl_sha512_256ctx)); @@ -751,17 +793,17 @@ size_t input_size) { Curl_sha512_256_ctx ctx; - CURLcode res; + CURLcode result; - res = Curl_sha512_256_init(&ctx); - if(res != CURLE_OK) - return res; + result = Curl_sha512_256_init(&ctx); + if(result != CURLE_OK) + return result; - res = Curl_sha512_256_update(&ctx, (const void *)input, input_size); + result = Curl_sha512_256_update(&ctx, (const void *)input, input_size); - if(res != CURLE_OK) { + if(result != CURLE_OK) { (void)Curl_sha512_256_finish(output, &ctx); - return res; + return result; } return Curl_sha512_256_finish(output, &ctx);
diff --git a/Utilities/cmcurl/lib/curl_share.c b/Utilities/cmcurl/lib/curl_share.c index 3ff2755..386a2b5 100644 --- a/Utilities/cmcurl/lib/curl_share.c +++ b/Utilities/cmcurl/lib/curl_share.c
@@ -24,23 +24,61 @@ #include "curl_setup.h" #include "urldata.h" -#include "connect.h" +#include "multiif.h" +#include "curl_threads.h" #include "curl_share.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" #include "hsts.h" #include "url.h" +static void share_destroy(struct Curl_share *share) +{ + if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { + Curl_cpool_destroy(&share->cpool); + } + + Curl_dnscache_destroy(&share->dnscache); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + Curl_cookie_cleanup(share->cookies); +#endif + +#ifndef CURL_DISABLE_HSTS + Curl_hsts_cleanup(&share->hsts); +#endif + +#ifdef USE_SSL + if(share->ssl_scache) { + Curl_ssl_scache_destroy(share->ssl_scache); + share->ssl_scache = NULL; + } +#endif + + Curl_psl_destroy(&share->psl); + Curl_close(&share->admin); + +#ifdef USE_MUTEX + Curl_mutex_destroy(&share->lock); +#endif + share->magic = 0; + curlx_free(share); +} + CURLSH *curl_share_init(void) { struct Curl_share *share = curlx_calloc(1, sizeof(struct Curl_share)); if(share) { share->magic = CURL_GOOD_SHARE; share->specifier |= (1 << CURL_LOCK_DATA_SHARE); +#ifdef USE_MUTEX + Curl_mutex_init(&share->lock); +#endif + share->ref_count = 1; Curl_dnscache_init(&share->dnscache, 23); share->admin = curl_easy_init(); if(!share->admin) { - curlx_free(share); + share_destroy(share); return NULL; } /* admin handles have mid 0 */ @@ -51,10 +89,104 @@ share->admin->set.verbose = TRUE; #endif } - return share; } +static uint32_t share_ref_inc(struct Curl_share *share) +{ + uint32_t n; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + n = ++(share->ref_count); + share->has_been_shared = TRUE; + Curl_mutex_release(&share->lock); +#else + n = ++(share->ref_count); + share->has_been_shared = TRUE; +#endif + return n; +} + +static uint32_t share_ref_dec(struct Curl_share *share) +{ + uint32_t n; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + DEBUGASSERT(share->ref_count); + n = --(share->ref_count); + Curl_mutex_release(&share->lock); +#else + n = --(share->ref_count); +#endif + return n; +} + +static bool share_has_been_shared(struct Curl_share *share) +{ + bool was_shared; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + was_shared = share->has_been_shared; + Curl_mutex_release(&share->lock); +#else + was_shared = share->has_been_shared; +#endif + return was_shared; +} + +static bool share_lock_acquire(struct Curl_share *share, + struct Curl_easy *data) +{ + if(share->lockfunc && share->unlockfunc && + (data || share_has_been_shared(share))) { + share->lockfunc(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE, + share->clientdata); + return TRUE; + } + return FALSE; +} + +static void share_lock_release(struct Curl_share *share, + struct Curl_easy *data, + bool locked) +{ + if(locked) { + DEBUGASSERT(share->unlockfunc); + if(share->unlockfunc) + share->unlockfunc(data, CURL_LOCK_DATA_SHARE, share->clientdata); + } +} + +static bool share_in_use(struct Curl_share *share) +{ + bool in_use; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + in_use = (share->ref_count > 1); + Curl_mutex_release(&share->lock); +#else + bool locked = share_lock_acquire(share, NULL); + in_use = (share->ref_count > 1); + share_lock_release(share, NULL, locked); +#endif + return in_use; +} + +static void share_unlink(struct Curl_share **pshare, + struct Curl_easy *data, + bool locked) +{ + struct Curl_share *share = *pshare; + uint32_t n; + + *pshare = NULL; + n = share_ref_dec(share); + if(locked) + share_lock_release(share, data, locked); + if(!n) /* last reference gone */ + share_destroy(share); +} + #undef curl_share_setopt CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) { @@ -69,10 +201,11 @@ if(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; - if(share->dirty) + if(share_in_use(share)) { /* do not allow setting options while one or more handles are already using this share */ return CURLSHE_IN_USE; + } va_start(param, option); @@ -115,8 +248,8 @@ /* There is no way (yet) for the application to configure the * session cache size, shared between many transfers. As for curl * itself, a high session count will impact startup time. Also, the - * scache is not optimized for several hundreds of peers. So, - * keep it at a reasonable level. */ + * scache is not optimized for several hundreds of peers. + * Keep it at a reasonable level. */ if(Curl_ssl_scache_create(25, 2, &share->ssl_scache)) res = CURLSHE_NOMEM; } @@ -148,7 +281,6 @@ case CURLSHOPT_UNSHARE: /* this is a type this share will no longer share */ type = va_arg(param, int); - share->specifier &= ~(unsigned int)(1 << type); switch(type) { case CURL_LOCK_DATA_DNS: break; @@ -192,6 +324,8 @@ res = CURLSHE_BAD_OPTION; break; } + if(!res) + share->specifier &= ~(unsigned int)(1 << type); break; case CURLSHOPT_LOCKFUNC: @@ -222,48 +356,15 @@ CURLSHcode curl_share_cleanup(CURLSH *sh) { struct Curl_share *share = sh; + bool locked; if(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; - if(share->lockfunc) - share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE, - share->clientdata); - - if(share->dirty) { - if(share->unlockfunc) - share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); + if(share_in_use(share)) return CURLSHE_IN_USE; - } - if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { - Curl_cpool_destroy(&share->cpool); - } - - Curl_dnscache_destroy(&share->dnscache); - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - Curl_cookie_cleanup(share->cookies); -#endif - -#ifndef CURL_DISABLE_HSTS - Curl_hsts_cleanup(&share->hsts); -#endif - -#ifdef USE_SSL - if(share->ssl_scache) { - Curl_ssl_scache_destroy(share->ssl_scache); - share->ssl_scache = NULL; - } -#endif - - Curl_psl_destroy(&share->psl); - Curl_close(&share->admin); - - if(share->unlockfunc) - share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); - share->magic = 0; - curlx_free(share); - + locked = share_lock_acquire(share, NULL); + share_unlink(&share, NULL, locked); return CURLSHE_OK; } @@ -298,3 +399,74 @@ return CURLSHE_OK; } + +CURLcode Curl_share_easy_unlink(struct Curl_easy *data) +{ + struct Curl_share *share = data->share; + + if(share) { + bool locked = share_lock_acquire(share, data); + + /* If data has a connection from this share, detach it. */ + if(data->conn && (share->specifier & (1 << CURL_LOCK_DATA_CONNECT))) + Curl_detach_connection(data); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(share->cookies == data->cookies) + data->cookies = NULL; +#endif + +#ifndef CURL_DISABLE_HSTS + if(share->hsts == data->hsts) + data->hsts = NULL; +#endif +#ifdef USE_LIBPSL + if(&share->psl == data->psl) + data->psl = data->multi ? &data->multi->psl : NULL; +#endif + + share_unlink(&data->share, data, locked); + } + return CURLE_OK; +} + +CURLcode Curl_share_easy_link(struct Curl_easy *data, + struct Curl_share *share) +{ + if(data->share) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + + if(share) { + bool locked = share_lock_acquire(share, data); + + share_ref_inc(share); + data->share = share; + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(share->cookies) { + /* use shared cookie list, first free own one if any */ + Curl_cookie_cleanup(data->cookies); + /* enable cookies since we now use a share that uses cookies! */ + data->cookies = share->cookies; + } +#endif /* CURL_DISABLE_HTTP */ +#ifndef CURL_DISABLE_HSTS + if(share->hsts) { + /* first free the private one if any */ + Curl_hsts_cleanup(&data->hsts); + data->hsts = share->hsts; + } +#endif +#ifdef USE_LIBPSL + if(share->specifier & (1 << CURL_LOCK_DATA_PSL)) + data->psl = &share->psl; +#endif + + /* check for host cache not needed, + * it will be done by curl_easy_perform */ + share_lock_release(share, data, locked); + } + return CURLE_OK; +}
diff --git a/Utilities/cmcurl/lib/curl_share.h b/Utilities/cmcurl/lib/curl_share.h index fcd9e32..69001be 100644 --- a/Utilities/cmcurl/lib/curl_share.h +++ b/Utilities/cmcurl/lib/curl_share.h
@@ -25,6 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" +#include "curl_threads.h" #include "cookie.h" #include "psl.h" #include "urldata.h" @@ -36,19 +37,29 @@ #define CURL_GOOD_SHARE 0x7e117a1e #define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE) -#define CURL_SHARE_KEEP_CONNECT(s) \ - ((s) && ((s)->specifier & (1 << CURL_LOCK_DATA_CONNECT))) +#define CURL_SHARE_KEEP_CONNECT(s) \ + ((s) && ((s)->specifier & (1 << CURL_LOCK_DATA_CONNECT))) /* this struct is libcurl-private, do not export details */ struct Curl_share { unsigned int magic; /* CURL_GOOD_SHARE */ unsigned int specifier; - volatile unsigned int dirty; + uint32_t ref_count; +#ifdef USE_MUTEX + /* do `ref_count` and `has_been_shared` checks using this mutex. */ + curl_mutex_t lock; + int has_been_shared; +#else + /* this only ever goes from FALSE -> TRUE once. We need to check + * this without being able to use the `lockfunc`. */ + volatile int has_been_shared; +#endif curl_lock_function lockfunc; curl_unlock_function unlockfunc; void *clientdata; struct Curl_easy *admin; + struct cpool cpool; struct Curl_dnscache dnscache; /* DNS cache */ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) @@ -65,13 +76,17 @@ #endif }; -CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data, - curl_lock_access); -CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data); +CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, + curl_lock_access accesstype); +CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type); /* convenience macro to check if this handle is using a shared SSL spool */ -#define CURL_SHARE_ssl_scache(data) (data->share && \ - (data->share->specifier & \ +#define CURL_SHARE_ssl_scache(data) ((data)->share && \ + ((data)->share->specifier & \ (1 << CURL_LOCK_DATA_SSL_SESSION))) +CURLcode Curl_share_easy_unlink(struct Curl_easy *data); +CURLcode Curl_share_easy_link(struct Curl_easy *data, + struct Curl_share *share); + #endif /* HEADER_CURL_SHARE_H */
diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c index 6e27269..3ea1762 100644 --- a/Utilities/cmcurl/lib/curl_sspi.c +++ b/Utilities/cmcurl/lib/curl_sspi.c
@@ -26,7 +26,7 @@ #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "curlx/multibyte.h" /* Pointer to SSPI dispatch table */ @@ -198,9 +198,9 @@ void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity) { if(identity) { - Curl_safefree(identity->User); - Curl_safefree(identity->Password); - Curl_safefree(identity->Domain); + curlx_safefree(identity->User); + curlx_safefree(identity->Password); + curlx_safefree(identity->Domain); } }
diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h index 0fba510..3779d51 100644 --- a/Utilities/cmcurl/lib/curl_sspi.h +++ b/Utilities/cmcurl/lib/curl_sspi.h
@@ -27,17 +27,7 @@ #ifdef USE_WINDOWS_SSPI -/* - * When including the following three headers, it is mandatory to define either - * SECURITY_WIN32 or SECURITY_KERNEL, indicating who is compiling the code. - */ - -#undef SECURITY_WIN32 -#undef SECURITY_KERNEL -#define SECURITY_WIN32 1 -#include <security.h> #include <sspi.h> -#include <rpc.h> CURLcode Curl_sspi_global_init(void); void Curl_sspi_global_cleanup(void); @@ -56,7 +46,7 @@ /* Forward-declaration of global variables defined in curl_sspi.c */ extern PSecurityFunctionTable Curl_pSecFn; -/* Provide some definitions missing in old headers */ +/* Provide Service Principal names as macros */ #define SP_NAME_DIGEST "WDigest" #define SP_NAME_NTLM "NTLM" #define SP_NAME_NEGOTIATE "Negotiate"
diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c index f5fea98..01041f6 100644 --- a/Utilities/cmcurl/lib/curl_threads.c +++ b/Utilities/cmcurl/lib/curl_threads.c
@@ -22,14 +22,12 @@ * ***************************************************************************/ #include "curl_setup.h" - -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) -#include <pthread.h> -#endif - #include "curl_threads.h" +#include "curlx/timeval.h" -#ifdef USE_THREADS_POSIX +#ifdef USE_THREADS + +#ifdef HAVE_THREADS_POSIX struct Curl_actual_call { unsigned int (*func)(void *); @@ -49,8 +47,8 @@ return 0; } -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg) +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg) { curl_thread_t t = curlx_malloc(sizeof(pthread_t)); struct Curl_actual_call *ac = NULL; @@ -97,10 +95,10 @@ return ret; } -#elif defined(USE_THREADS_WIN32) +#elif defined(_WIN32) -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg) +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg) { curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL); if(!t) { @@ -131,4 +129,86 @@ return ret; } -#endif /* USE_THREADS_* */ +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined +#endif +#endif /* USE_THREADS */ + +#ifdef USE_MUTEX + +#ifdef HAVE_THREADS_POSIX + +void Curl_cond_signal(pthread_cond_t *c) +{ + /* return code defined as always 0 */ + (void)pthread_cond_signal(c); +} + +void Curl_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) +{ + /* return code defined as always 0 */ + (void)pthread_cond_wait(c, m); +} + +CURLcode Curl_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, + uint32_t timeout_ms) +{ + struct curltime now; + struct timespec ts; + timediff_t usec; + int rc; + + /* POSIX expects an "absolute" time until the condition wait ends. + * We cannot use `curlx_now()` here that may run on some monotonic clock + * that will be most likely in the past, as far as POSIX abstime is + * concerned. */ +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + (void)gettimeofday(&tv, NULL); + now.tv_sec = tv.tv_sec; + now.tv_usec = (int)tv.tv_usec; +#else + now.tv_sec = time(NULL); + now.tv_usec = 0; +#endif + + ts.tv_sec = now.tv_sec + (timeout_ms / 1000); + usec = now.tv_usec + ((timeout_ms % 1000) * 1000); + if(usec >= 1000000) { + ++ts.tv_sec; + usec %= 1000000; + } + ts.tv_nsec = (long)usec * 1000; + + rc = pthread_cond_timedwait(c, m, &ts); + if(rc == SOCKETIMEDOUT) + return CURLE_OPERATION_TIMEDOUT; + return rc ? CURLE_UNRECOVERABLE_POLL : CURLE_OK; +} + +#elif defined(_WIN32) + +void Curl_cond_signal(CONDITION_VARIABLE *c) +{ + WakeConditionVariable(c); +} + +void Curl_cond_wait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m) +{ + SleepConditionVariableCS(c, m, INFINITE); +} + +CURLcode Curl_cond_timedwait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m, + uint32_t timeout_ms) +{ + if(!SleepConditionVariableCS(c, m, (DWORD)timeout_ms)) { + DWORD err = GetLastError(); + return (err == ERROR_TIMEOUT) ? + CURLE_OPERATION_TIMEDOUT : CURLE_UNRECOVERABLE_POLL; + } + return CURLE_OK; +} +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined +#endif +#endif /* USE_MUTEX */
diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h index 241014c..081d155 100644 --- a/Utilities/cmcurl/lib/curl_threads.h +++ b/Utilities/cmcurl/lib/curl_threads.h
@@ -25,7 +25,8 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef USE_THREADS_POSIX +#ifdef USE_MUTEX +#ifdef HAVE_THREADS_POSIX # define CURL_THREAD_RETURN_T unsigned int # define CURL_STDCALL # define curl_mutex_t pthread_mutex_t @@ -35,31 +36,42 @@ # define Curl_mutex_acquire(m) pthread_mutex_lock(m) # define Curl_mutex_release(m) pthread_mutex_unlock(m) # define Curl_mutex_destroy(m) pthread_mutex_destroy(m) -#elif defined(USE_THREADS_WIN32) +# define curl_cond_t pthread_cond_t +# define Curl_cond_init(c) pthread_cond_init(c, NULL) +# define Curl_cond_destroy(c) pthread_cond_destroy(c) +#elif defined(_WIN32) # define CURL_THREAD_RETURN_T DWORD # define CURL_STDCALL WINAPI # define curl_mutex_t CRITICAL_SECTION # define curl_thread_t HANDLE # define curl_thread_t_null (HANDLE)0 -# if !defined(_WIN32_WINNT) || (_WIN32_WINNT < _WIN32_WINNT_VISTA) -# define Curl_mutex_init(m) InitializeCriticalSection(m) -# else -# define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1) -# endif +# define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1) # define Curl_mutex_acquire(m) EnterCriticalSection(m) # define Curl_mutex_release(m) LeaveCriticalSection(m) # define Curl_mutex_destroy(m) DeleteCriticalSection(m) +# define curl_cond_t CONDITION_VARIABLE +# define Curl_cond_init(c) InitializeConditionVariable(c) +# define Curl_cond_destroy(c) (void)(c) +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined #endif -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +void Curl_cond_signal(curl_cond_t *c); +void Curl_cond_wait(curl_cond_t *c, curl_mutex_t *m); +/* Returns CURLE_OPERATION_TIMEDOUT on timeout */ +CURLcode Curl_cond_timedwait(curl_cond_t *c, curl_mutex_t *m, + uint32_t timeout_ms); +#endif /* USE_MUTEX */ -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg); +#ifdef USE_THREADS + +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg); void Curl_thread_destroy(curl_thread_t *hnd); int Curl_thread_join(curl_thread_t *hnd); -#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */ +#endif /* USE_THREADS */ #endif /* HEADER_CURL_THREADS_H */
diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c index 565d1b4..c6115cf 100644 --- a/Utilities/cmcurl/lib/curl_trc.c +++ b/Utilities/cmcurl/lib/curl_trc.c
@@ -28,6 +28,7 @@ #include "cfilters.h" #include "multiif.h" +#include "cf-dns.h" #include "cf-socket.h" #include "connect.h" #include "http2.h" @@ -194,6 +195,42 @@ } } +void Curl_reset_fail(struct Curl_easy *data) +{ + if(data->set.errorbuffer) + data->set.errorbuffer[0] = 0; + data->state.errorbuf = FALSE; +} + +#ifdef CURLVERBOSE +struct curl_trc_feat Curl_trc_feat_multi = { + "MULTI", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_read = { + "READ", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_write = { + "WRITE", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_dns = { + "DNS", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_timer = { + "TIMER", + CURL_LOG_LVL_NONE, +}; +#ifdef USE_THREADS +struct curl_trc_feat Curl_trc_feat_threads = { + "THREADS", + CURL_LOG_LVL_NONE, +}; +#endif +#endif + #ifndef CURL_DISABLE_VERBOSE_STRINGS static void trc_infof(struct Curl_easy *data, @@ -248,26 +285,18 @@ } } -struct curl_trc_feat Curl_trc_feat_multi = { - "MULTI", - CURL_LOG_LVL_NONE, -}; -struct curl_trc_feat Curl_trc_feat_read = { - "READ", - CURL_LOG_LVL_NONE, -}; -struct curl_trc_feat Curl_trc_feat_write = { - "WRITE", - CURL_LOG_LVL_NONE, -}; -struct curl_trc_feat Curl_trc_feat_dns = { - "DNS", - CURL_LOG_LVL_NONE, -}; -struct curl_trc_feat Curl_trc_feat_timer = { - "TIMER", - CURL_LOG_LVL_NONE, -}; +void Curl_trc_feat_infof(struct Curl_easy *data, + struct curl_trc_feat *feat, + const char *fmt, ...) +{ + DEBUGASSERT(feat); + if(Curl_trc_ft_is_verbose(data, feat)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, feat, NULL, 0, fmt, ap); + va_end(ap); + } +} static const char * const Curl_trc_timer_names[] = { "100_TIMEOUT", @@ -327,7 +356,6 @@ "PENDING", "SETUP", "CONNECT", - "RESOLVING", "CONNECTING", "PROTOCONNECT", "PROTOCONNECTING", @@ -485,7 +513,7 @@ } #endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */ -#define TRC_CT_NONE (0) +#define TRC_CT_NONE 0 #define TRC_CT_PROTOCOL (1 << 0) #define TRC_CT_NETWORK (1 << 1) #define TRC_CT_PROXY (1 << 2) @@ -503,6 +531,9 @@ { &Curl_trc_feat_write, TRC_CT_NONE }, { &Curl_trc_feat_dns, TRC_CT_NETWORK }, { &Curl_trc_feat_timer, TRC_CT_NETWORK }, +#ifdef USE_THREADS + { &Curl_trc_feat_threads, TRC_CT_NONE }, +#endif #ifndef CURL_DISABLE_FTP { &Curl_trc_feat_ftp, TRC_CT_PROTOCOL }, #endif @@ -526,6 +557,7 @@ }; static struct trc_cft_def trc_cfts[] = { + { &Curl_cft_dns, TRC_CT_NETWORK }, { &Curl_cft_tcp, TRC_CT_NETWORK }, { &Curl_cft_udp, TRC_CT_NETWORK }, { &Curl_cft_unix, TRC_CT_NETWORK }, @@ -578,7 +610,7 @@ } } -static void trc_apply_level_by_category(int category, int lvl) +static void trc_apply_level_by_category(unsigned int category, int lvl) { size_t i;
diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h index ff085d6..b4ae8e5 100644 --- a/Utilities/cmcurl/lib/curl_trc.h +++ b/Utilities/cmcurl/lib/curl_trc.h
@@ -61,13 +61,14 @@ #define failf Curl_failf +/* In case failf() reported into the errorbuf, clear it again. + * This is used to clear information from happy eyeballing attempts + * when ultimately a successful attempt was made. */ +void Curl_reset_fail(struct Curl_easy *data); + #define CURL_LOG_LVL_NONE 0 #define CURL_LOG_LVL_INFO 1 -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#define CURL_HAVE_C99 -#endif - /** * Output an informational message when transfer's verbose logging is enabled. */ @@ -100,6 +101,10 @@ int log_level; }; +void Curl_trc_feat_infof(struct Curl_easy *data, + struct curl_trc_feat *feat, + const char *fmt, ...) CURL_PRINTF(3, 4); + #ifndef CURL_DISABLE_FTP extern struct curl_trc_feat Curl_trc_feat_ftp; void Curl_trc_ftp(struct Curl_easy *data, @@ -133,7 +138,7 @@ #define CURL_TRC_TIMER_is_verbose(data) \ Curl_trc_ft_is_verbose(data, &Curl_trc_feat_timer) -#if defined(CURL_HAVE_C99) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(CURL_HAVE_MACRO_VARARG) && !defined(CURL_DISABLE_VERBOSE_STRINGS) #define infof(data, ...) \ do { \ if(Curl_trc_is_verbose(data)) \ @@ -206,7 +211,70 @@ } while(0) #endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */ -#else /* CURL_HAVE_C99 */ +#elif defined(CURL_HAVE_MACRO_VARARG) && defined(CURL_DISABLE_VERBOSE_STRINGS) + +#define infof(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_M(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_CF(data, cf, ...) \ + do { \ + (void)(data); \ + (void)(cf); \ + } while(0) +#define CURL_TRC_WRITE(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_READ(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_DNS(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_TIMER(data, tid, ...) \ + do { \ + (void)(data); \ + (void)(tid); \ + } while(0) +#ifndef CURL_DISABLE_FTP +#define CURL_TRC_FTP(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif /* !CURL_DISABLE_FTP */ +#ifndef CURL_DISABLE_SMTP +#define CURL_TRC_SMTP(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif /* !CURL_DISABLE_SMTP */ +#ifdef USE_SSL +#define CURL_TRC_SSLS(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif /* USE_SSL */ +#ifdef USE_SSH +#define CURL_TRC_SSH(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif /* USE_SSH */ +#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +#define CURL_TRC_WS(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif + +#else /* !CURL_HAVE_MACRO_VARARG */ #define infof Curl_infof #define CURL_TRC_M Curl_trc_multi @@ -232,17 +300,21 @@ #define CURL_TRC_WS Curl_trc_ws #endif -#endif /* !CURL_HAVE_C99 */ +#endif /* CURL_HAVE_MACRO_VARARG */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS -/* informational messages enabled */ - +#ifdef CURLVERBOSE extern struct curl_trc_feat Curl_trc_feat_multi; extern struct curl_trc_feat Curl_trc_feat_read; extern struct curl_trc_feat Curl_trc_feat_write; extern struct curl_trc_feat Curl_trc_feat_dns; extern struct curl_trc_feat Curl_trc_feat_timer; +#ifdef USE_THREADS +extern struct curl_trc_feat Curl_trc_feat_threads; +#endif +#endif +#ifndef CURL_DISABLE_VERBOSE_STRINGS +/* informational messages enabled */ #define Curl_trc_is_verbose(data) \ ((data) && (data)->set.verbose && \ (!(data)->state.feat || \ @@ -262,13 +334,11 @@ #else /* CURL_DISABLE_VERBOSE_STRINGS */ /* All informational messages are not compiled in for size savings */ - -#define Curl_trc_is_verbose(d) (FALSE) -#define Curl_trc_cf_is_verbose(x, y) (FALSE) -#define Curl_trc_ft_is_verbose(x, y) (FALSE) +#define Curl_trc_is_verbose(d) FALSE +#define Curl_trc_cf_is_verbose(x, y) FALSE +#define Curl_trc_ft_is_verbose(x, y) FALSE #define CURL_MSTATE_NAME(x) ((void)(x), "-") #define CURL_TRC_EASY_TIMERS(x) Curl_nop_stmt - #endif /* !CURL_DISABLE_VERBOSE_STRINGS */ #endif /* HEADER_CURL_TRC_H */
diff --git a/Utilities/cmcurl/lib/curlx/base64.c b/Utilities/cmcurl/lib/curlx/base64.c index 0e44eae..7f51576 100644 --- a/Utilities/cmcurl/lib/curlx/base64.c +++ b/Utilities/cmcurl/lib/curlx/base64.c
@@ -24,9 +24,9 @@ /* Base64 encoding/decoding */ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "base64.h" +#include "curlx/base64.h" /* ---- Base64 Encoding/Decoding Table --- */ const char curlx_base64encdec[] = @@ -149,7 +149,7 @@ pos += 3 - padding; } - /* Zero terminate */ + /* Null-terminate */ *pos = '\0'; /* Return the decoded data */ @@ -182,7 +182,7 @@ if(insize > CURL_MAX_BASE64_INPUT) return CURLE_TOO_LARGE; - base64data = output = curlx_malloc((insize + 2) / 3 * 4 + 1); + base64data = output = curlx_malloc(((insize + 2) / 3 * 4) + 1); if(!output) return CURLE_OUT_OF_MEMORY; @@ -213,7 +213,7 @@ } } - /* Zero terminate */ + /* Null-terminate */ *output = '\0'; /* Return the pointer to the new data (allocated memory) */
diff --git a/Utilities/cmcurl/lib/curlx/basename.c b/Utilities/cmcurl/lib/curlx/basename.c new file mode 100644 index 0000000..d2fd160 --- /dev/null +++ b/Utilities/cmcurl/lib/curlx/basename.c
@@ -0,0 +1,74 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +#ifndef HAVE_BASENAME + +#include "curlx/basename.h" + +/* + (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 + Edition) + + The basename() function shall take the pathname pointed to by path and + return a pointer to the final component of the pathname, deleting any + trailing '/' characters. + + If the string pointed to by path consists entirely of the '/' character, + basename() shall return a pointer to the string "/". If the string pointed + to by path is exactly "//", it is implementation-defined whether '/' or "//" + is returned. + + If path is a null pointer or points to an empty string, basename() shall + return a pointer to the string ".". + + The basename() function may modify the string pointed to by path, and may + return a pointer to static storage that may then be overwritten by a + subsequent call to basename(). + + The basename() function need not be reentrant. A function that is not + required to be reentrant is not required to be thread-safe. + +*/ +char *curlx_basename(char *path) +{ + /* Ignore all the details above for now and make a quick and simple + implementation here */ + char *s1; + char *s2; + + s1 = strrchr(path, '/'); + s2 = strrchr(path, '\\'); + + if(s1 && s2) + path = ((s1 > s2) ? s1 : s2) + 1; + else if(s1) + path = s1 + 1; + else if(s2) + path = s2 + 1; + + return path; +} + +#endif /* !HAVE_BASENAME */
diff --git a/Utilities/cmcurl/lib/curlx/basename.h b/Utilities/cmcurl/lib/curlx/basename.h new file mode 100644 index 0000000..fb79fed --- /dev/null +++ b/Utilities/cmcurl/lib/curlx/basename.h
@@ -0,0 +1,39 @@ +#ifndef HEADER_CURLX_BASENAME_H +#define HEADER_CURLX_BASENAME_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" + +#ifndef HAVE_BASENAME +char *curlx_basename(char *path); +#else + +#ifdef HAVE_LIBGEN_H +#include <libgen.h> +#endif + +#define curlx_basename(x) basename(x) +#endif /* !HAVE_BASENAME */ + +#endif /* HEADER_CURLX_BASENAME_H */
diff --git a/Utilities/cmcurl/lib/curlx/binmode.h b/Utilities/cmcurl/lib/curlx/binmode.h deleted file mode 100644 index 991cc89..0000000 --- a/Utilities/cmcurl/lib/curlx/binmode.h +++ /dev/null
@@ -1,39 +0,0 @@ -#ifndef HEADER_CURL_TOOL_BINMODE_H -#define HEADER_CURL_TOOL_BINMODE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "../curl_setup.h" - -#if (defined(HAVE_SETMODE) || defined(HAVE__SETMODE)) && defined(O_BINARY) -/* Requires io.h and/or fcntl.h when available */ -#ifdef HAVE__SETMODE -# define CURLX_SET_BINMODE(stream) (void)_setmode(fileno(stream), O_BINARY) -#else -# define CURLX_SET_BINMODE(stream) (void)setmode(fileno(stream), O_BINARY) -#endif -#else -# define CURLX_SET_BINMODE(stream) (void)stream -#endif - -#endif /* HEADER_CURL_TOOL_BINMODE_H */
diff --git a/Utilities/cmcurl/lib/curlx/curlx.h b/Utilities/cmcurl/lib/curlx/curlx.h deleted file mode 100644 index 817acb0..0000000 --- a/Utilities/cmcurl/lib/curlx/curlx.h +++ /dev/null
@@ -1,83 +0,0 @@ -#ifndef HEADER_CURL_CURLX_H -#define HEADER_CURL_CURLX_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 - * - ***************************************************************************/ - -/* - * Defines protos and includes all header files that provide the curlx_* - * functions. The curlx_* functions are not part of the libcurl API, but are - * stand-alone functions whose sources can be built and linked by apps if need - * be. - */ - -#include "binmode.h" -/* "binmode.h" provides macro CURLX_SET_BINMODE() */ - -#include "nonblock.h" -/* "nonblock.h" provides curlx_nonblock() */ - -#include "multibyte.h" -/* "multibyte.h" provides these functions and macros: - - curlx_convert_UTF8_to_wchar() - curlx_convert_wchar_to_UTF8() - curlx_convert_UTF8_to_tchar() - curlx_convert_tchar_to_UTF8() -*/ - -#include "version_win32.h" -/* provides curlx_verify_windows_version() */ - -#include "strerr.h" -/* The curlx_strerror() function */ - -#include "strparse.h" -/* The curlx_str_* parsing functions */ - -#include "strcopy.h" -/* curlx_strcopy */ - -#include "dynbuf.h" -/* The curlx_dyn_* functions */ - -#include "fopen.h" -/* The curlx_f* functions */ - -#include "base64.h" -#include "timeval.h" -#include "timediff.h" - -#include "wait.h" -/* for curlx_wait_ms */ - -#include "winapi.h" -/* for curlx_winapi_strerror */ - -#include "inet_pton.h" -/* for curlx_inet_pton */ - -#include "inet_ntop.h" -/* for curlx_inet_ntop */ - -#endif /* HEADER_CURL_CURLX_H */
diff --git a/Utilities/cmcurl/lib/curlx/dynbuf.c b/Utilities/cmcurl/lib/curlx/dynbuf.c index 158d4d0..1e5ea51 100644 --- a/Utilities/cmcurl/lib/curlx/dynbuf.c +++ b/Utilities/cmcurl/lib/curlx/dynbuf.c
@@ -21,10 +21,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "dynbuf.h" -#include "../curl_printf.h" +#include "curlx/dynbuf.h" +#include "curl_printf.h" #define MIN_FIRST_ALLOC 32 @@ -57,7 +57,7 @@ { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); - Curl_safefree(s->bufr); + curlx_safefree(s->bufr); s->leng = s->allc = 0; } @@ -102,8 +102,6 @@ } if(a != s->allc) { - /* this logic is not using Curl_saferealloc() to make the tool not have to - include that as well when it uses this code */ void *p = curlx_realloc(s->bufr, a); if(!p) { curlx_dyn_free(s); @@ -136,7 +134,7 @@ /* * Specify the size of the tail to keep (number of bytes from the end of the - * buffer). The rest will be dropped. + * buffer). The rest is dropped. */ CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail) { @@ -207,7 +205,7 @@ if(str) { CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str)); - curlx_free(str); + curl_free(str); return result; } /* If we failed, we cleanup the whole buffer and return error */
diff --git a/Utilities/cmcurl/lib/curlx/dynbuf.h b/Utilities/cmcurl/lib/curlx/dynbuf.h index 6f09e13..0cf4a2c 100644 --- a/Utilities/cmcurl/lib/curlx/dynbuf.h +++ b/Utilities/cmcurl/lib/curlx/dynbuf.h
@@ -53,7 +53,7 @@ /* returns 0 on success, -1 on error */ /* The implementation of this function exists in mprintf.c */ -int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save); +int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args); /* Take the buffer out of the dynbuf. Caller has ownership and * dynbuf resets to initial state. */
diff --git a/Utilities/cmcurl/lib/curlx/fopen.c b/Utilities/cmcurl/lib/curlx/fopen.c index e9eb6df..6733010 100644 --- a/Utilities/cmcurl/lib/curlx/fopen.c +++ b/Utilities/cmcurl/lib/curlx/fopen.c
@@ -21,9 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "fopen.h" +#include "curlx/fopen.h" int curlx_fseek(void *stream, curl_off_t offset, int whence) { @@ -42,10 +42,10 @@ #include <share.h> /* for _SH_DENYNO */ -#include "multibyte.h" -#include "timeval.h" +#include "curlx/multibyte.h" +#include "curlx/timeval.h" -#ifdef CURLDEBUG +#ifdef CURL_MEMDEBUG /* * Use system allocators to avoid infinite recursion when called by curl's * memory tracker memdebug functions. @@ -94,7 +94,7 @@ * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path. * * For example 'in' filename255chars in current directory C:\foo\bar is - * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows + * fixed as \\?\C:\foo\bar\filename255chars for 'out' which tells Windows * it is ok to access that filename even though the actual full path is longer * than 260 chars. * @@ -296,7 +296,7 @@ int curlx_win32_open(const char *filename, int oflag, ...) { int pmode = 0; - int result = -1; + int res = -1; TCHAR *fixed = NULL; const TCHAR *target = NULL; @@ -316,7 +316,7 @@ target = fixed; else target = filename_w; - errno = _wsopen_s(&result, target, oflag, _SH_DENYNO, pmode); + errno = _wsopen_s(&res, target, oflag, _SH_DENYNO, pmode); CURLX_FREE(filename_w); } else @@ -327,16 +327,16 @@ target = fixed; else target = filename; - errno = _sopen_s(&result, target, oflag, _SH_DENYNO, pmode); + errno = _sopen_s(&res, target, oflag, _SH_DENYNO, pmode); #endif CURLX_FREE(fixed); - return result; + return res; } FILE *curlx_win32_fopen(const char *filename, const char *mode) { - FILE *result = NULL; + FILE *file = NULL; TCHAR *fixed = NULL; const TCHAR *target = NULL; @@ -348,7 +348,7 @@ target = fixed; else target = filename_w; - result = _wfsopen(target, mode_w, _SH_DENYNO); + file = _wfsopen(target, mode_w, _SH_DENYNO); } else /* !checksrc! disable ERRNOVAR 1 */ @@ -360,11 +360,11 @@ target = fixed; else target = filename; - result = _fsopen(target, mode, _SH_DENYNO); + file = _fsopen(target, mode, _SH_DENYNO); #endif CURLX_FREE(fixed); - return result; + return file; } #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR < 5) @@ -374,7 +374,7 @@ FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fp) { - FILE *result = NULL; + FILE *file = NULL; TCHAR *fixed = NULL; const TCHAR *target = NULL; @@ -386,7 +386,7 @@ target = fixed; else target = filename_w; - errno = _wfreopen_s(&result, target, mode_w, fp); + errno = _wfreopen_s(&file, target, mode_w, fp); } else /* !checksrc! disable ERRNOVAR 1 */ @@ -398,16 +398,16 @@ target = fixed; else target = filename; - errno = freopen_s(&result, target, mode, fp); + errno = freopen_s(&file, target, mode, fp); #endif CURLX_FREE(fixed); - return result; + return file; } -int curlx_win32_stat(const char *path, struct_stat *buffer) +int curlx_win32_stat(const char *path, curlx_struct_stat *buffer) { - int result = -1; + int res = -1; TCHAR *fixed = NULL; const TCHAR *target = NULL; @@ -418,7 +418,7 @@ target = fixed; else target = path_w; - result = _wstati64(target, buffer); + res = _wstati64(target, buffer); curlx_free(path_w); } else @@ -429,18 +429,18 @@ target = fixed; else target = path; - result = _stati64(target, buffer); + res = _stati64(target, buffer); #endif CURLX_FREE(fixed); - return result; + return res; } #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES) || \ !defined(CURL_DISABLE_ALTSVC) /* rename() on Windows does not overwrite, so we cannot use it here. - MoveFileEx() will overwrite and is usually atomic, however it fails - when there are open handles to the file. */ + MoveFileEx() does overwrite and is usually atomic but fails when there are + open handles to the file. */ int curlx_win32_rename(const char *oldpath, const char *newpath) { int res = -1; /* fail */
diff --git a/Utilities/cmcurl/lib/curlx/fopen.h b/Utilities/cmcurl/lib/curlx/fopen.h index 53c079e..b64fbf6 100644 --- a/Utilities/cmcurl/lib/curlx/fopen.h +++ b/Utilities/cmcurl/lib/curlx/fopen.h
@@ -23,9 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "multibyte.h" +#include "curlx/multibyte.h" #ifdef HAVE_FCNTL_H #include <fcntl.h> /* for open() and attributes */ @@ -34,6 +34,7 @@ int curlx_fseek(void *stream, curl_off_t offset, int whence); #ifdef _WIN32 +#include <sys/stat.h> /* for _fstati64(), struct _stati64 */ #ifndef CURL_WINDOWS_UWP HANDLE curlx_CreateFile(const char *filename, DWORD dwDesiredAccess, @@ -43,25 +44,33 @@ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); #endif /* !CURL_WINDOWS_UWP */ +#define curlx_fstat _fstati64 +#define curlx_struct_stat struct _stati64 FILE *curlx_win32_fopen(const char *filename, const char *mode); -FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fh); -int curlx_win32_stat(const char *path, struct_stat *buffer); +FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fp); +int curlx_win32_stat(const char *path, curlx_struct_stat *buffer); int curlx_win32_open(const char *filename, int oflag, ...); int curlx_win32_rename(const char *oldpath, const char *newpath); -#define CURLX_FOPEN_LOW(fname, mode) curlx_win32_fopen(fname, mode) -#define CURLX_FREOPEN_LOW(fname, mode, fh) curlx_win32_freopen(fname, mode, fh) -#define curlx_stat(fname, stp) curlx_win32_stat(fname, stp) -#define curlx_open curlx_win32_open -#define curlx_rename curlx_win32_rename +#define CURLX_FOPEN_LOW curlx_win32_fopen +#define CURLX_FREOPEN_LOW curlx_win32_freopen +#define CURLX_FDOPEN_LOW _fdopen +#define curlx_stat curlx_win32_stat +#define curlx_open curlx_win32_open +#define curlx_close _close +#define curlx_rename curlx_win32_rename #else -#define CURLX_FOPEN_LOW fopen -#define CURLX_FREOPEN_LOW freopen -#define curlx_stat(fname, stp) stat(fname, stp) -#define curlx_open open -#define curlx_rename rename +#define curlx_fstat fstat +#define curlx_struct_stat struct stat +#define CURLX_FOPEN_LOW fopen +#define CURLX_FREOPEN_LOW freopen +#define CURLX_FDOPEN_LOW fdopen +#define curlx_stat stat +#define curlx_open open +#define curlx_close close +#define curlx_rename rename #endif -#ifdef CURLDEBUG +#ifdef CURL_MEMDEBUG #define curlx_fopen(file, mode) curl_dbg_fopen(file, mode, __LINE__, __FILE__) #define curlx_freopen(file, mode, fh) \ curl_dbg_freopen(file, mode, fh, __LINE__, __FILE__) @@ -71,7 +80,7 @@ #else #define curlx_fopen CURLX_FOPEN_LOW #define curlx_freopen CURLX_FREOPEN_LOW -#define curlx_fdopen fdopen +#define curlx_fdopen CURLX_FDOPEN_LOW #define curlx_fclose fclose #endif
diff --git a/Utilities/cmcurl/lib/curlx/inet_ntop.c b/Utilities/cmcurl/lib/curlx/inet_ntop.c index 6bea5e1..803b988 100644 --- a/Utilities/cmcurl/lib/curlx/inet_ntop.c +++ b/Utilities/cmcurl/lib/curlx/inet_ntop.c
@@ -16,7 +16,7 @@ * * SPDX-License-Identifier: ISC */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef HAVE_INET_NTOP @@ -30,9 +30,9 @@ #include <arpa/inet.h> #endif -#include "inet_ntop.h" -#include "snprintf.h" -#include "strcopy.h" +#include "curlx/inet_ntop.h" +#include "curlx/snprintf.h" +#include "curlx/strcopy.h" #define IN6ADDRSZ 16 /* #define INADDRSZ 4 */ @@ -166,7 +166,7 @@ static const unsigned char ldigits[] = "0123456789abcdef"; unsigned int w = words[i]; - /* output lowercase 16bit hex number but ignore leading zeroes */ + /* output lowercase 16-bit hex number but ignore leading zeroes */ if(w & 0xf000) *tp++ = ldigits[(w & 0xf000) >> 12]; if(w & 0xff00)
diff --git a/Utilities/cmcurl/lib/curlx/inet_ntop.h b/Utilities/cmcurl/lib/curlx/inet_ntop.h index 45b63d9..ba8299f 100644 --- a/Utilities/cmcurl/lib/curlx/inet_ntop.h +++ b/Utilities/cmcurl/lib/curlx/inet_ntop.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_INET_NTOP #ifdef HAVE_NETINET_IN_H @@ -36,15 +36,15 @@ #include <arpa/inet.h> #endif #ifdef __AMIGA__ -#define curlx_inet_ntop(af, addr, buf, size) \ - (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)buf, \ +#define curlx_inet_ntop(af, src, buf, size) \ + (char *)inet_ntop(af, CURL_UNCONST(src), (unsigned char *)(buf), \ (curl_socklen_t)(size)) #else -#define curlx_inet_ntop(af, addr, buf, size) \ - inet_ntop(af, addr, buf, (curl_socklen_t)(size)) +#define curlx_inet_ntop(af, src, buf, size) \ + inet_ntop(af, src, buf, (curl_socklen_t)(size)) #endif #else -char *curlx_inet_ntop(int af, const void *addr, char *buf, size_t size); +char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size); #endif /* HAVE_INET_NTOP */ #endif /* HEADER_CURL_INET_NTOP_H */
diff --git a/Utilities/cmcurl/lib/curlx/inet_pton.c b/Utilities/cmcurl/lib/curlx/inet_pton.c index 703a7b9..7994f25 100644 --- a/Utilities/cmcurl/lib/curlx/inet_pton.c +++ b/Utilities/cmcurl/lib/curlx/inet_pton.c
@@ -17,9 +17,7 @@ * * SPDX-License-Identifier: ISC */ -#include "../curl_setup.h" - -#include "strparse.h" +#include "curl_setup.h" #ifndef HAVE_INET_PTON @@ -33,7 +31,8 @@ #include <arpa/inet.h> #endif -#include "inet_pton.h" +#include "curlx/inet_pton.h" +#include "curlx/strparse.h" #define IN6ADDRSZ 16 #define INADDRSZ 4 @@ -53,41 +52,7 @@ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ -static int inet_pton4(const char *src, unsigned char *dst); -static int inet_pton6(const char *src, unsigned char *dst); - -/* int - * inet_pton(af, src, dst) - * convert from presentation format (which usually means ASCII printable) - * to network format (which is usually some kind of binary format). - * return: - * 1 if the address was valid for the specified address family - * 0 if the address was not valid (`dst' is untouched in this case) - * -1 if some other error occurred (`dst' is untouched in this case, too) - * notice: - * On Windows we store the error in the thread errno, not - * in the Winsock error code. This is to avoid losing the - * actual last Winsock error. When this function returns - * -1, check errno not SOCKERRNO. - * author: - * Paul Vixie, 1996. - */ -int curlx_inet_pton(int af, const char *src, void *dst) -{ - switch(af) { - case AF_INET: - return inet_pton4(src, (unsigned char *)dst); - case AF_INET6: - return inet_pton6(src, (unsigned char *)dst); - default: - errno = SOCKEAFNOSUPPORT; - return -1; - } - /* NOTREACHED */ -} - -/* int - * inet_pton4(src, dst) +/* int inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. @@ -105,7 +70,7 @@ octets = 0; tp = tmp; *tp = 0; - while((ch = *src++) != '\0') { + while((ch = (unsigned char)*src++) != '\0') { if(ISDIGIT(ch)) { unsigned int val = (*tp * 10) + (ch - '0'); @@ -135,8 +100,7 @@ return 1; } -/* int - * inet_pton6(src, dst) +/* int inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. @@ -165,7 +129,7 @@ curtok = src; saw_xdigit = 0; val = 0; - while((ch = *src++) != '\0') { + while((ch = (unsigned char)*src++) != '\0') { if(ISXDIGIT(ch)) { val <<= 4; val |= curlx_hexval(ch); @@ -206,7 +170,7 @@ if(colonp) { /* * Since some memmove()'s erroneously fail to handle - * overlapping regions, we will do the shift by hand. + * overlapping regions, we do the shift by hand. */ const ssize_t n = tp - colonp; ssize_t i; @@ -225,4 +189,33 @@ return 1; } +/* int inet_pton(af, src, dst) + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * return: + * 1 if the address was valid for the specified address family + * 0 if the address was not valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * notice: + * On Windows we store the error in the thread errno, not + * in the Winsock error code. This is to avoid losing the + * actual last Winsock error. When this function returns + * -1, check errno not SOCKERRNO. + * author: + * Paul Vixie, 1996. + */ +int curlx_inet_pton(int af, const char *src, void *dst) +{ + switch(af) { + case AF_INET: + return inet_pton4(src, (unsigned char *)dst); + case AF_INET6: + return inet_pton6(src, (unsigned char *)dst); + default: + errno = SOCKEAFNOSUPPORT; + return -1; + } + /* NOTREACHED */ +} + #endif /* HAVE_INET_PTON */
diff --git a/Utilities/cmcurl/lib/curlx/inet_pton.h b/Utilities/cmcurl/lib/curlx/inet_pton.h index fc3ae3d..02ae7f2 100644 --- a/Utilities/cmcurl/lib/curlx/inet_pton.h +++ b/Utilities/cmcurl/lib/curlx/inet_pton.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_INET_PTON #ifdef HAVE_NETINET_IN_H @@ -43,7 +43,7 @@ inet_pton(x, y, z) #endif #else -int curlx_inet_pton(int, const char *, void *); +int curlx_inet_pton(int af, const char *src, void *dst); #endif /* HAVE_INET_PTON */ #endif /* HEADER_CURL_INET_PTON_H */
diff --git a/Utilities/cmcurl/lib/curlx/multibyte.c b/Utilities/cmcurl/lib/curlx/multibyte.c index 529091a..715d2b8 100644 --- a/Utilities/cmcurl/lib/curlx/multibyte.c +++ b/Utilities/cmcurl/lib/curlx/multibyte.c
@@ -21,11 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(_WIN32) && defined(UNICODE) -#include "multibyte.h" +#include "curlx/multibyte.h" /* * MultiByte conversions using Windows kernel32 library.
diff --git a/Utilities/cmcurl/lib/curlx/multibyte.h b/Utilities/cmcurl/lib/curlx/multibyte.h index 26da0f0..8863fb0 100644 --- a/Utilities/cmcurl/lib/curlx/multibyte.h +++ b/Utilities/cmcurl/lib/curlx/multibyte.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32
diff --git a/Utilities/cmcurl/lib/curlx/nonblock.c b/Utilities/cmcurl/lib/curlx/nonblock.c index 96bf3db..6f6458f 100644 --- a/Utilities/cmcurl/lib/curlx/nonblock.c +++ b/Utilities/cmcurl/lib/curlx/nonblock.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> @@ -35,7 +35,7 @@ #include <inet.h> #endif -#include "nonblock.h" +#include "curlx/nonblock.h" /* * curlx_nonblock() set the given socket to either blocking or non-blocking
diff --git a/Utilities/cmcurl/lib/curlx/snprintf.c b/Utilities/cmcurl/lib/curlx/snprintf.c new file mode 100644 index 0000000..911c42e --- /dev/null +++ b/Utilities/cmcurl/lib/curlx/snprintf.c
@@ -0,0 +1,49 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "curlx/snprintf.h" + +#ifdef _WIN32 +#include <stdarg.h> + +/* Simplified wrapper for the Windows platform to use the correct symbol and + ensuring null-termination. Omit returning a length to keep it simple. */ +void curlx_win32_snprintf(char *buf, size_t maxlen, const char *fmt, ...) +{ + va_list ap; + if(!maxlen) + return; + va_start(ap, fmt); +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + /* !checksrc! disable BANNEDFUNC 1 */ + (void)vsnprintf(buf, maxlen, fmt, ap); +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop +#endif + buf[maxlen - 1] = 0; + va_end(ap); +} +#endif /* _WIN32 */
diff --git a/Utilities/cmcurl/lib/curlx/snprintf.h b/Utilities/cmcurl/lib/curlx/snprintf.h index 266ea13..d6260e4 100644 --- a/Utilities/cmcurl/lib/curlx/snprintf.h +++ b/Utilities/cmcurl/lib/curlx/snprintf.h
@@ -1,3 +1,5 @@ +#ifndef HEADER_CURLX_SNPRINTF_H +#define HEADER_CURLX_SNPRINTF_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -21,12 +23,18 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" /* Raw snprintf() for curlx */ +#ifdef _WIN32 +void curlx_win32_snprintf(char *buf, size_t maxlen, const char *fmt, ...) + CURL_PRINTF(3, 4); +#endif + #ifdef WITHOUT_LIBCURL /* when built for the test servers */ -#if defined(_MSC_VER) && (_MSC_VER < 1900) /* adjust for old MSVC */ -#define SNPRINTF _snprintf +#ifdef _WIN32 +#define SNPRINTF curlx_win32_snprintf #else #define SNPRINTF snprintf #endif @@ -34,3 +42,4 @@ #include <curl/mprintf.h> #define SNPRINTF curl_msnprintf #endif /* WITHOUT_LIBCURL */ +#endif /* HEADER_CURLX_SNPRINTF_H */
diff --git a/Utilities/cmcurl/lib/curlx/strcopy.c b/Utilities/cmcurl/lib/curlx/strcopy.c index d6bff15..3e58ea2 100644 --- a/Utilities/cmcurl/lib/curlx/strcopy.c +++ b/Utilities/cmcurl/lib/curlx/strcopy.c
@@ -21,19 +21,19 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "strcopy.h" +#include "curlx/strcopy.h" /* * curlx_strcopy() is a replacement for strcpy. * * Provide the target buffer @dest and size of the target buffer @dsize, If * the source string @src with its *string length* @slen fits in the target - * buffer it will be copied there - including storing a null terminator. + * buffer it is copied there - including storing a null terminator. * * If the target buffer is too small, the copy is not performed but if the - * target buffer has a non-zero size it will get a null terminator stored. + * target buffer has a non-zero size it gets a null terminator stored. */ void curlx_strcopy(char *dest, /* destination buffer */ size_t dsize, /* size of target buffer */
diff --git a/Utilities/cmcurl/lib/curlx/strdup.c b/Utilities/cmcurl/lib/curlx/strdup.c new file mode 100644 index 0000000..3c967db --- /dev/null +++ b/Utilities/cmcurl/lib/curlx/strdup.c
@@ -0,0 +1,96 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 _WIN32 +#include <wchar.h> +#endif + +#include "curlx/strdup.h" + +#ifdef _WIN32 +/*************************************************************************** + * + * curlx_wcsdup(source) + * + * Copies the 'source' wchar string to a newly allocated buffer (that is + * returned). Used by macro curlx_tcsdup(). + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +wchar_t *curlx_wcsdup(const wchar_t *src) +{ + size_t length = wcslen(src); + + if(length > (SIZE_MAX / sizeof(wchar_t)) - 1) + return (wchar_t *)NULL; /* integer overflow */ + + return (wchar_t *)curlx_memdup(src, (length + 1) * sizeof(wchar_t)); +} +#endif + +/*************************************************************************** + * + * curlx_memdup(source, length) + * + * Copies the 'source' data to a newly allocated buffer (that is + * returned). Copies 'length' bytes. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +void *curlx_memdup(const void *src, size_t length) +{ + void *buffer = curlx_malloc(length); + if(!buffer) + return NULL; /* fail */ + + memcpy(buffer, src, length); + + return buffer; +} + +/*************************************************************************** + * + * curlx_memdup0(source, length) + * + * Copies the 'source' string to a newly allocated buffer (that is returned). + * Copies 'length' bytes then adds a null-terminator. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +void *curlx_memdup0(const char *src, size_t length) +{ + char *buf = (length < SIZE_MAX) ? curlx_malloc(length + 1) : NULL; + if(!buf) + return NULL; + if(length) { + DEBUGASSERT(src); /* must never be NULL */ + memcpy(buf, src, length); + } + buf[length] = 0; + return buf; +}
diff --git a/Utilities/cmcurl/lib/curlx/strdup.h b/Utilities/cmcurl/lib/curlx/strdup.h new file mode 100644 index 0000000..0c4b141 --- /dev/null +++ b/Utilities/cmcurl/lib/curlx/strdup.h
@@ -0,0 +1,33 @@ +#ifndef HEADER_CURLX_STRDUP_H +#define HEADER_CURLX_STRDUP_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 _WIN32 +wchar_t *curlx_wcsdup(const wchar_t *src); /* for curlx_tcsdup() */ +#endif +void *curlx_memdup(const void *src, size_t length); +void *curlx_memdup0(const char *src, size_t length); +#endif /* HEADER_CURLX_STRDUP_H */
diff --git a/Utilities/cmcurl/lib/curlx/strerr.c b/Utilities/cmcurl/lib/curlx/strerr.c index 3f1b1f5..b53173c 100644 --- a/Utilities/cmcurl/lib/curlx/strerr.c +++ b/Utilities/cmcurl/lib/curlx/strerr.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_STRERROR_R # if (!defined(HAVE_POSIX_STRERROR_R) && \ @@ -31,10 +31,10 @@ # endif #endif -#include "winapi.h" -#include "snprintf.h" -#include "strerr.h" -#include "strcopy.h" +#include "curlx/winapi.h" +#include "curlx/snprintf.h" +#include "curlx/strerr.h" +#include "curlx/strcopy.h" #ifdef USE_WINSOCK /* This is a helper function for curlx_strerror that converts Winsock error @@ -43,17 +43,14 @@ */ static const char *get_winsock_error(int err, char *buf, size_t len) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *p; - size_t alen; -#endif + VERBOSE(const char *p); if(!len) return NULL; *buf = '\0'; -#ifdef CURL_DISABLE_VERBOSE_STRINGS +#ifndef CURLVERBOSE (void)err; return NULL; #else @@ -223,8 +220,7 @@ default: return NULL; } - alen = strlen(p); - curlx_strcopy(buf, len, p, alen); + curlx_strcopy(buf, len, p, strlen(p)); return buf; #endif } @@ -267,7 +263,7 @@ *buf = '\0'; #ifdef _WIN32 - if((!strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) && + if((strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) && #ifdef USE_WINSOCK !get_winsock_error(err, buf, buflen) && #endif @@ -294,7 +290,7 @@ */ { char buffer[256]; - char *msg = strerror_r(err, buffer, sizeof(buffer)); + const char *msg = strerror_r(err, buffer, sizeof(buffer)); if(msg && buflen > 1) SNPRINTF(buf, buflen, "%s", msg); else if(buflen > sizeof("Unknown error ") + 20)
diff --git a/Utilities/cmcurl/lib/curlx/strparse.c b/Utilities/cmcurl/lib/curlx/strparse.c index f11b58b..7866b20 100644 --- a/Utilities/cmcurl/lib/curlx/strparse.c +++ b/Utilities/cmcurl/lib/curlx/strparse.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "strparse.h" +#include "curlx/strparse.h" void curlx_str_init(struct Curl_str *out) { @@ -35,6 +35,15 @@ out->len = len; } +/* remove bytes from the end of the string, never remove more bytes than what + the string holds! */ +void curlx_str_trim(struct Curl_str *out, size_t len) +{ + DEBUGASSERT(out); + DEBUGASSERT(out->len >= len); + out->len -= len; +} + /* Get a word until the first DELIM or end of string. At least one byte long. return non-zero on error */ int curlx_str_until(const char **linep, struct Curl_str *out, @@ -140,7 +149,7 @@ /* given an ASCII character and max ascii, return TRUE if valid */ #define valid_digit(x, m) \ - (((x) >= '0') && ((x) <= m) && curlx_hexasciitable[(x) - '0']) + (((x) >= '0') && ((x) <= (m)) && curlx_hexasciitable[(x) - '0']) /* We use 16 for the zero index (and the necessary bitwise AND in the loop) to be able to have a non-zero value there to make valid_digit() able to @@ -172,7 +181,7 @@ /* special-case low max scenario because check needs to be different */ do { int n = curlx_hexval(*p++); - num = num * base + n; + num = (num * base) + n; if(num > max) return STRE_OVERFLOW; } while(valid_digit(*p, m)); @@ -182,7 +191,7 @@ int n = curlx_hexval(*p++); if(num > ((max - n) / base)) return STRE_OVERFLOW; - num = num * base + n; + num = (num * base) + n; } while(valid_digit(*p, m)); } *nump = num;
diff --git a/Utilities/cmcurl/lib/curlx/strparse.h b/Utilities/cmcurl/lib/curlx/strparse.h index ae8fd19..c7801b2 100644 --- a/Utilities/cmcurl/lib/curlx/strparse.h +++ b/Utilities/cmcurl/lib/curlx/strparse.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #define STRE_OK 0 #define STRE_BIG 1 @@ -44,6 +44,7 @@ void curlx_str_init(struct Curl_str *out); void curlx_str_assign(struct Curl_str *out, const char *str, size_t len); +void curlx_str_trim(struct Curl_str *out, size_t len); #define curlx_str(x) ((x)->str) #define curlx_strlen(x) ((x)->len) @@ -98,7 +99,8 @@ int curlx_str_nudge(struct Curl_str *str, size_t num); -int curlx_str_cspn(const char **linep, struct Curl_str *out, const char *cspn); +int curlx_str_cspn(const char **linep, struct Curl_str *out, + const char *reject); void curlx_str_trimblanks(struct Curl_str *out); void curlx_str_passblanks(const char **linep);
diff --git a/Utilities/cmcurl/lib/curlx/timediff.c b/Utilities/cmcurl/lib/curlx/timediff.c index 694c8d8..2bf7869 100644 --- a/Utilities/cmcurl/lib/curlx/timediff.c +++ b/Utilities/cmcurl/lib/curlx/timediff.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "timediff.h" +#include "curlx/timediff.h" /* * Converts number of milliseconds into a timeval structure.
diff --git a/Utilities/cmcurl/lib/curlx/timediff.h b/Utilities/cmcurl/lib/curlx/timediff.h index e19b0f4..1081c75 100644 --- a/Utilities/cmcurl/lib/curlx/timediff.h +++ b/Utilities/cmcurl/lib/curlx/timediff.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" /* Use a larger type even for 32-bit time_t systems so that we can keep microsecond accuracy in it */
diff --git a/Utilities/cmcurl/lib/curlx/timeval.c b/Utilities/cmcurl/lib/curlx/timeval.c index fa7e528..2363e60 100644 --- a/Utilities/cmcurl/lib/curlx/timeval.c +++ b/Utilities/cmcurl/lib/curlx/timeval.c
@@ -21,58 +21,30 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "timeval.h" +#include "curlx/timeval.h" #ifdef _WIN32 -#include "version_win32.h" -#include "../system_win32.h" +#include "system_win32.h" LARGE_INTEGER Curl_freq; -bool Curl_isVistaOrGreater; /* For tool or tests, we must initialize before calling curlx_now(). Providing this function here is wrong. */ void curlx_now_init(void) { - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) - Curl_isVistaOrGreater = true; - else - Curl_isVistaOrGreater = false; - QueryPerformanceFrequency(&Curl_freq); } /* In case of bug fix this function has a counterpart in tool_util.c */ void curlx_pnow(struct curltime *pnow) { - bool isVistaOrGreater; - isVistaOrGreater = Curl_isVistaOrGreater; - if(isVistaOrGreater) { /* QPC timer might have issues pre-Vista */ - LARGE_INTEGER count; - LARGE_INTEGER freq; - freq = Curl_freq; - DEBUGASSERT(freq.QuadPart); - QueryPerformanceCounter(&count); - pnow->tv_sec = (time_t)(count.QuadPart / freq.QuadPart); - pnow->tv_usec = (int)((count.QuadPart % freq.QuadPart) * 1000000 / - freq.QuadPart); - } - else { - /* Disable /analyze warning that GetTickCount64 is preferred */ -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:28159) -#endif - DWORD milliseconds = GetTickCount(); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - pnow->tv_sec = (time_t)(milliseconds / 1000); - pnow->tv_usec = (int)((milliseconds % 1000) * 1000); - } + LARGE_INTEGER count; + DEBUGASSERT(Curl_freq.QuadPart); + QueryPerformanceCounter(&count); + pnow->tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart); + pnow->tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 / + Curl_freq.QuadPart); } #elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \ @@ -81,19 +53,19 @@ void curlx_pnow(struct curltime *pnow) { /* - ** clock_gettime() is granted to be increased monotonically when the - ** monotonic clock is queried. Time starting point is unspecified, it - ** could be the system start-up time, the Epoch, or something else, - ** in any case the time starting point does not change once that the - ** system has started up. - */ + * clock_gettime() is granted to be increased monotonically when the + * monotonic clock is queried. Time starting point is unspecified, it + * could be the system start-up time, the Epoch, or something else, + * in any case the time starting point does not change once that the + * system has started up. + */ struct timespec tsnow; /* - ** clock_gettime() may be defined by Apple's SDK as weak symbol thus - ** code compiles but fails during runtime if clock_gettime() is - ** called on unsupported OS version. - */ + * clock_gettime() may be defined by Apple's SDK as weak symbol thus + * code compiles but fails during runtime if clock_gettime() is + * called on unsupported OS version. + */ #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ (HAVE_BUILTIN_AVAILABLE == 1) bool have_clock_gettime = FALSE; @@ -124,10 +96,10 @@ pnow->tv_usec = (int)(tsnow.tv_nsec / 1000); } /* - ** Even when the configure process has truly detected monotonic clock - ** availability, it might happen that it is not actually available at - ** runtime. When this occurs simply fallback to other time source. - */ + * Even when the configure process has truly detected monotonic clock + * availability, it might happen that it is not actually available at + * runtime. When this occurs, fallback to other time source. + */ #ifdef HAVE_GETTIMEOFDAY else { struct timeval now; @@ -145,17 +117,16 @@ #elif defined(HAVE_MACH_ABSOLUTE_TIME) -#include <stdint.h> #include <mach/mach_time.h> void curlx_pnow(struct curltime *pnow) { /* - ** Monotonic timer on macOS is provided by mach_absolute_time(), which - ** returns time in Mach "absolute time units," which are platform-dependent. - ** To convert to nanoseconds, one must use conversion factors specified by - ** mach_timebase_info(). - */ + * Monotonic timer on macOS is provided by mach_absolute_time(), which + * returns time in Mach "absolute time units," which are platform-dependent. + * To convert to nanoseconds, one must use conversion factors specified by + * mach_timebase_info(). + */ static mach_timebase_info_data_t timebase; uint64_t usecs; @@ -176,10 +147,10 @@ void curlx_pnow(struct curltime *pnow) { /* - ** gettimeofday() is not granted to be increased monotonically, due to - ** clock drifting and external source time synchronization it can jump - ** forward or backward in time. - */ + * gettimeofday() is not granted to be increased monotonically, due to + * clock drifting and external source time synchronization it can jump + * forward or backward in time. + */ struct timeval now; (void)gettimeofday(&now, NULL); pnow->tv_sec = now.tv_sec; @@ -191,10 +162,12 @@ void curlx_pnow(struct curltime *pnow) { /* - ** time() returns the value of time in seconds since the Epoch. - */ + * time() returns the value of time in seconds since the Epoch. + */ pnow->tv_sec = time(NULL); pnow->tv_usec = 0; + if(!pnow->tv_sec) /* avoid a `now` fully zero */ + pnow->tv_usec = 1; } #endif @@ -220,10 +193,9 @@ return TIMEDIFF_T_MAX; else if(diff <= (TIMEDIFF_T_MIN / 1000)) return TIMEDIFF_T_MIN; - return diff * 1000 + (newer->tv_usec - older->tv_usec) / 1000; + return (diff * 1000) + ((newer->tv_usec - older->tv_usec) / 1000); } - timediff_t curlx_timediff_ms(struct curltime newer, struct curltime older) { return curlx_ptimediff_ms(&newer, &older); @@ -241,7 +213,7 @@ return TIMEDIFF_T_MAX; else if(diff <= (TIMEDIFF_T_MIN / 1000)) return TIMEDIFF_T_MIN; - return diff * 1000 + (newer.tv_usec - older.tv_usec + 999) / 1000; + return (diff * 1000) + ((newer.tv_usec - older.tv_usec + 999) / 1000); } /* @@ -256,7 +228,7 @@ return TIMEDIFF_T_MAX; else if(diff <= (TIMEDIFF_T_MIN / 1000000)) return TIMEDIFF_T_MIN; - return diff * 1000000 + newer->tv_usec - older->tv_usec; + return (diff * 1000000) + newer->tv_usec - older->tv_usec; } timediff_t curlx_timediff_us(struct curltime newer, struct curltime older)
diff --git a/Utilities/cmcurl/lib/curlx/timeval.h b/Utilities/cmcurl/lib/curlx/timeval.h index 4aab175..c01f95d 100644 --- a/Utilities/cmcurl/lib/curlx/timeval.h +++ b/Utilities/cmcurl/lib/curlx/timeval.h
@@ -23,9 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "timediff.h" +#include "curlx/timediff.h" struct curltime { time_t tv_sec; /* seconds */
diff --git a/Utilities/cmcurl/lib/curlx/version_win32.c b/Utilities/cmcurl/lib/curlx/version_win32.c index a9a4ee3..296a38d 100644 --- a/Utilities/cmcurl/lib/curlx/version_win32.c +++ b/Utilities/cmcurl/lib/curlx/version_win32.c
@@ -21,12 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32 -#include "version_win32.h" +#include "curlx/version_win32.h" +#ifndef CURL_WINDOWS_UWP /* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW) and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */ struct OUR_OSVERSIONINFOEXW { @@ -43,6 +44,24 @@ UCHAR wReserved; }; +typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN) + (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG); +static RTLVERIFYVERSIONINFO_FN s_pRtlVerifyVersionInfo; + +void curlx_verify_windows_init(void) +{ +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif + s_pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN, + GetProcAddress(GetModuleHandle(TEXT("ntdll")), "RtlVerifyVersionInfo")); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif +} +#endif /* !CURL_WINDOWS_UWP */ + /* * curlx_verify_windows_version() * @@ -115,24 +134,6 @@ DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; - typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN) - (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG); - static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo; - static bool onetime = TRUE; /* safe because first call is during init */ - - if(onetime) { -#if defined(__clang__) && __clang_major__ >= 16 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcast-function-type-strict" -#endif - pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN, - GetProcAddress(GetModuleHandle(TEXT("ntdll")), "RtlVerifyVersionInfo")); -#if defined(__clang__) && __clang_major__ >= 16 -#pragma clang diagnostic pop -#endif - onetime = FALSE; - } - switch(condition) { case VERSION_LESS_THAN: majorCondition = VER_LESS; @@ -203,8 +204,8 @@ the real version always, so we use the Rtl variant of the function when possible. Note though the function signatures have underlying fundamental types that are the same, the return values are different. */ - if(pRtlVerifyVersionInfo) - matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); + if(s_pRtlVerifyVersionInfo) + matched = !s_pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); else matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm); @@ -222,11 +223,11 @@ cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition); dwTypeMask = VER_BUILDNUMBER; - if(pRtlVerifyVersionInfo) - matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); + if(s_pRtlVerifyVersionInfo) + matched = !s_pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); else matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, - dwTypeMask, cm); + dwTypeMask, cm); } #endif
diff --git a/Utilities/cmcurl/lib/curlx/version_win32.h b/Utilities/cmcurl/lib/curlx/version_win32.h index a5fb8ff..c4a1c0f 100644 --- a/Utilities/cmcurl/lib/curlx/version_win32.h +++ b/Utilities/cmcurl/lib/curlx/version_win32.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32 @@ -43,6 +43,11 @@ PLATFORM_WINNT } PlatformIdentifier; +#ifdef CURL_WINDOWS_UWP +#define curlx_verify_windows_init() Curl_nop_stmt +#else +void curlx_verify_windows_init(void); +#endif /* This is used to verify if we are running on a specific Windows version */ bool curlx_verify_windows_version(const unsigned int majorVersion, const unsigned int minorVersion,
diff --git a/Utilities/cmcurl/lib/curlx/wait.c b/Utilities/cmcurl/lib/curlx/wait.c index 2a9f54b..e50a0f0 100644 --- a/Utilities/cmcurl/lib/curlx/wait.c +++ b/Utilities/cmcurl/lib/curlx/wait.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef HAVE_SELECT #error "We cannot compile without select() support." @@ -37,19 +37,19 @@ #include <dos.h> /* delay() */ #endif -#include "timediff.h" -#include "wait.h" +#include "curlx/timediff.h" +#include "curlx/wait.h" /* * Internal function used for waiting a specific amount of ms in * Curl_socket_check() and Curl_poll() when no file descriptor is provided to - * wait on, just being used to delay execution. Winsock select() and poll() - * timeout mechanisms need a valid socket descriptor in a not null file - * descriptor set to work. Waiting indefinitely with this function is not - * allowed, a zero or negative timeout value will return immediately. Timeout - * resolution, accuracy, as well as maximum supported value is system - * dependent, neither factor is a critical issue for the intended use of this - * function in the library. + * wait on, being used to delay execution. Winsock select() and poll() timeout + * mechanisms need a valid socket descriptor in a not null file descriptor set + * to work. Waiting indefinitely with this function is not allowed, a zero or + * negative timeout value is returned immediately. Timeout resolution, + * accuracy, as well as maximum supported value is system dependent, neither + * factor is a critical issue for the intended use of this function in the + * library. * * Return values: * -1 = system call error, or invalid timeout value
diff --git a/Utilities/cmcurl/lib/curlx/wait.h b/Utilities/cmcurl/lib/curlx/wait.h index c140194..9b9c273 100644 --- a/Utilities/cmcurl/lib/curlx/wait.h +++ b/Utilities/cmcurl/lib/curlx/wait.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" int curlx_wait_ms(timediff_t timeout_ms);
diff --git a/Utilities/cmcurl/lib/curlx/warnless.c b/Utilities/cmcurl/lib/curlx/warnless.c index 8c1be2d..5fc36ac 100644 --- a/Utilities/cmcurl/lib/curlx/warnless.c +++ b/Utilities/cmcurl/lib/curlx/warnless.c
@@ -21,9 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "warnless.h" +#include "curlx/warnless.h" #if defined(__INTEL_COMPILER) && defined(__unix__) @@ -49,9 +49,8 @@ #define CURL_MASK_SSIZE_T (CURL_MASK_USIZE_T >> 1) /* -** unsigned long to unsigned char -*/ - + * unsigned long to unsigned char + */ unsigned char curlx_ultouc(unsigned long ulnum) { #ifdef __INTEL_COMPILER @@ -68,9 +67,8 @@ } /* -** unsigned size_t to signed int -*/ - + * unsigned size_t to signed int + */ int curlx_uztosi(size_t uznum) { #ifdef __INTEL_COMPILER @@ -87,9 +85,8 @@ } /* -** unsigned size_t to unsigned long -*/ - + * unsigned size_t to unsigned long + */ unsigned long curlx_uztoul(size_t uznum) { #ifdef __INTEL_COMPILER @@ -108,9 +105,8 @@ } /* -** unsigned size_t to unsigned int -*/ - + * unsigned size_t to unsigned int + */ unsigned int curlx_uztoui(size_t uznum) { #ifdef __INTEL_COMPILER @@ -129,9 +125,8 @@ } /* -** signed long to signed int -*/ - + * signed long to signed int + */ int curlx_sltosi(long slnum) { #ifdef __INTEL_COMPILER @@ -151,9 +146,8 @@ } /* -** signed long to unsigned int -*/ - + * signed long to unsigned int + */ unsigned int curlx_sltoui(long slnum) { #ifdef __INTEL_COMPILER @@ -173,9 +167,8 @@ } /* -** signed long to unsigned short -*/ - + * signed long to unsigned short + */ unsigned short curlx_sltous(long slnum) { #ifdef __INTEL_COMPILER @@ -193,9 +186,8 @@ } /* -** unsigned size_t to signed ssize_t -*/ - + * unsigned size_t to signed ssize_t + */ ssize_t curlx_uztosz(size_t uznum) { #ifdef __INTEL_COMPILER @@ -212,9 +204,8 @@ } /* -** signed curl_off_t to unsigned size_t -*/ - + * signed curl_off_t to unsigned size_t + */ size_t curlx_sotouz(curl_off_t sonum) { #ifdef __INTEL_COMPILER @@ -231,9 +222,8 @@ } /* -** signed ssize_t to signed int -*/ - + * signed ssize_t to signed int + */ int curlx_sztosi(ssize_t sznum) { #ifdef __INTEL_COMPILER @@ -253,9 +243,8 @@ } /* -** unsigned int to unsigned short -*/ - + * unsigned int to unsigned short + */ unsigned short curlx_uitous(unsigned int uinum) { #ifdef __INTEL_COMPILER @@ -272,9 +261,8 @@ } /* -** signed int to unsigned size_t -*/ - + * signed int to unsigned size_t + */ size_t curlx_sitouz(int sinum) { #ifdef __INTEL_COMPILER
diff --git a/Utilities/cmcurl/lib/curlx/warnless.h b/Utilities/cmcurl/lib/curlx/warnless.h index 8a62ccf..3d0341e 100644 --- a/Utilities/cmcurl/lib/curlx/warnless.h +++ b/Utilities/cmcurl/lib/curlx/warnless.h
@@ -69,6 +69,6 @@ /* Convert a long to size_t, return FALSE if negative or too large * and set 0 */ -bool curlx_sltouz(long sznum, size_t *puznum); +bool curlx_sltouz(long slnum, size_t *puznum); #endif /* HEADER_CURL_WARNLESS_H */
diff --git a/Utilities/cmcurl/lib/curlx/winapi.c b/Utilities/cmcurl/lib/curlx/winapi.c index b46a5bf..f025ca4 100644 --- a/Utilities/cmcurl/lib/curlx/winapi.c +++ b/Utilities/cmcurl/lib/curlx/winapi.c
@@ -21,16 +21,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" /* * curlx_winapi_strerror: * Variant of curlx_strerror if the error code is definitely Windows API. */ #ifdef _WIN32 -#include "winapi.h" -#include "snprintf.h" -#include "strcopy.h" +#include "curlx/winapi.h" +#include "curlx/snprintf.h" +#include "curlx/strcopy.h" /* This is a helper function for curlx_strerror that converts Windows API error * codes (GetLastError) to error messages. @@ -39,30 +39,27 @@ const char *curlx_get_winapi_error(DWORD err, char *buf, size_t buflen) { char *p; - wchar_t wbuf[256]; - DWORD wlen; if(!buflen) return NULL; - *buf = '\0'; - *wbuf = L'\0'; - /* We return the local codepage version of the error string because if it is - output to the user's terminal it will likely be with functions which + output to the user's terminal, it is likely done with functions which expect the local codepage (eg fprintf, failf, infof). */ - wlen = FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err, - LANG_NEUTRAL, wbuf, CURL_ARRAYSIZE(wbuf), NULL); - if(wlen && !wcstombs_s(NULL, buf, buflen, wbuf, wlen)) { - /* Truncate multiple lines */ - p = strchr(buf, '\n'); - if(p) { - if(p > buf && *(p - 1) == '\r') - *(p - 1) = '\0'; - else - *p = '\0'; - } + if(!FormatMessageA((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err, + LANG_NEUTRAL, buf, (DWORD)buflen, NULL)) { + *buf = '\0'; + return NULL; + } + + /* Truncate multiple lines */ + p = strchr(buf, '\n'); + if(p) { + if(p > buf && *(p - 1) == '\r') + *(p - 1) = '\0'; + else + *p = '\0'; } return *buf ? buf : NULL; @@ -78,7 +75,7 @@ *buf = '\0'; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(!curlx_get_winapi_error(err, buf, buflen)) { #if defined(__GNUC__) && __GNUC__ >= 7 #pragma GCC diagnostic push
diff --git a/Utilities/cmcurl/lib/cw-out.c b/Utilities/cmcurl/lib/cw-out.c index 69fb344..2af074e 100644 --- a/Utilities/cmcurl/lib/cw-out.c +++ b/Utilities/cmcurl/lib/cw-out.c
@@ -31,7 +31,7 @@ #include "transfer.h" #include "cw-out.h" #include "cw-pause.h" - +#include "progress.h" /** * OVERALL DESIGN of this client writer @@ -101,22 +101,6 @@ BIT(errored); }; -static CURLcode cw_out_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes); -static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer); -static CURLcode cw_out_init(struct Curl_easy *data, - struct Curl_cwriter *writer); - -const struct Curl_cwtype Curl_cwt_out = { - "cw-out", - NULL, - cw_out_init, - cw_out_write, - cw_out_close, - sizeof(struct cw_out_ctx) -}; - static CURLcode cw_out_init(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -198,6 +182,8 @@ size_t nwritten; CURLcode result; + NOVERBOSE((void)otype); + DEBUGASSERT(data->conn); *pnwritten = 0; Curl_set_in_callback(data, TRUE); @@ -206,11 +192,11 @@ CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu", blen, (otype == CW_OUT_HDS) ? "header" : "body", nwritten); - if(CURL_WRITEFUNC_PAUSE == nwritten) { - if(data->conn->handler->flags & PROTOPT_NONETWORK) { + if(nwritten == CURL_WRITEFUNC_PAUSE) { + if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* Protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it cannot pause since the - transfer is not done using the "normal" procedure. */ + actually only FILE:// now, and it cannot pause since the transfer is + not done using the "normal" procedure. */ failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; } @@ -219,13 +205,13 @@ result = Curl_xfer_pause_recv(data, TRUE); return result ? result : CURLE_AGAIN; } - else if(CURL_WRITEFUNC_ERROR == nwritten) { + else if(nwritten == CURL_WRITEFUNC_ERROR) { failf(data, "client returned ERROR on write of %zu bytes", blen); return CURLE_WRITE_ERROR; } else if(nwritten != blen) { failf(data, "Failure writing output to destination, " - "passed %zu returned %zd", blen, nwritten); + "passed %zu returned %zu", blen, nwritten); return CURLE_WRITE_ERROR; } *pnwritten = nwritten; @@ -242,8 +228,8 @@ curl_write_callback wcb = NULL; void *wcb_data; size_t max_write, min_write; - size_t wlen, nwritten; - CURLcode result; + size_t wlen, nwritten = 0; + CURLcode result = CURLE_OK; /* If we errored once, we do not invoke the client callback again */ if(ctx->errored) @@ -267,10 +253,15 @@ if(!flush_all && blen < min_write) break; wlen = max_write ? CURLMIN(blen, max_write) : blen; - result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype, - buf, wlen, &nwritten); + if(otype == CW_OUT_BODY) + result = Curl_pgrs_deliver_check(data, wlen); + if(!result) + result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype, + buf, wlen, &nwritten); if(result) return result; + if(otype == CW_OUT_BODY) + Curl_pgrs_deliver_inc(data, nwritten); *pconsumed += nwritten; blen -= nwritten; buf += nwritten; @@ -364,8 +355,8 @@ } /* if we do not have a buffer, or it is of another type, make a new one. - * And for CW_OUT_HDS always make a new one, so we "replay" headers - * exactly as they came in */ + * For CW_OUT_HDS always make a new one, so we "replay" headers exactly + * as they came in */ if(!ctx->buf || (ctx->buf->type != otype) || (otype == CW_OUT_HDS)) { struct cw_out_buf *cwbuf = cw_out_buf_create(otype); if(!cwbuf) @@ -455,6 +446,15 @@ return CURLE_OK; } +const struct Curl_cwtype Curl_cwt_out = { + "cw-out", + NULL, + cw_out_init, + cw_out_write, + cw_out_close, + sizeof(struct cw_out_ctx) +}; + bool Curl_cw_out_is_paused(struct Curl_easy *data) { struct Curl_cwriter *cw_out; @@ -465,7 +465,7 @@ return FALSE; ctx = (struct cw_out_ctx *)cw_out; - return ctx->paused; + return (bool)ctx->paused; } static CURLcode cw_out_flush(struct Curl_easy *data,
diff --git a/Utilities/cmcurl/lib/cw-pause.c b/Utilities/cmcurl/lib/cw-pause.c index 481b9fb..ec61187 100644 --- a/Utilities/cmcurl/lib/cw-pause.c +++ b/Utilities/cmcurl/lib/cw-pause.c
@@ -34,7 +34,7 @@ /* body dynbuf sizes */ #define CW_PAUSE_BUF_CHUNK (16 * 1024) /* when content decoding, write data in chunks */ -#define CW_PAUSE_DEC_WRITE_CHUNK (4096) +#define CW_PAUSE_DEC_WRITE_CHUNK 4096 struct cw_pause_buf { struct cw_pause_buf *next; @@ -70,23 +70,6 @@ size_t buf_total; }; -static CURLcode cw_pause_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes); -static void cw_pause_close(struct Curl_easy *data, - struct Curl_cwriter *writer); -static CURLcode cw_pause_init(struct Curl_easy *data, - struct Curl_cwriter *writer); - -const struct Curl_cwtype Curl_cwt_pause = { - "cw-pause", - NULL, - cw_pause_init, - cw_pause_write, - cw_pause_close, - sizeof(struct cw_pause_ctx) -}; - static CURLcode cw_pause_init(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -220,6 +203,15 @@ return result; } +const struct Curl_cwtype Curl_cwt_pause = { + "cw-pause", + NULL, + cw_pause_init, + cw_pause_write, + cw_pause_close, + sizeof(struct cw_pause_ctx) +}; + CURLcode Curl_cw_pause_flush(struct Curl_easy *data) { struct Curl_cwriter *cw_pause;
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c index 5839e80..7b83c6c 100644 --- a/Utilities/cmcurl/lib/dict.c +++ b/Utilities/cmcurl/lib/dict.c
@@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "dict.h" #ifndef CURL_DISABLE_DICT @@ -51,11 +53,9 @@ #include <unistd.h> #endif -#include "urldata.h" #include "transfer.h" #include "curl_trc.h" #include "escape.h" -#include "dict.h" #define DICT_MATCH "/MATCH:" #define DICT_MATCH2 "/M:" @@ -64,42 +64,6 @@ #define DICT_DEFINE2 "/D:" #define DICT_DEFINE3 "/LOOKUP:" - -/* - * Forward declarations. - */ - -static CURLcode dict_do(struct Curl_easy *data, bool *done); - -/* - * DICT protocol handler. - */ - -const struct Curl_handler Curl_handler_dict = { - "dict", /* scheme */ - ZERO_NULL, /* setup_connection */ - dict_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_DICT, /* defport */ - CURLPROTO_DICT, /* protocol */ - CURLPROTO_DICT, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ -}; - #define DYN_DICT_WORD 10000 static char *unescape_word(const char *input) { @@ -306,4 +270,28 @@ curlx_free(path); return result; } + +/* + * DICT protocol + */ +const struct Curl_protocol Curl_protocol_dict = { + ZERO_NULL, /* setup_connection */ + dict_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_DICT */
diff --git a/Utilities/cmcurl/lib/dict.h b/Utilities/cmcurl/lib/dict.h index 4025c8b..5bdfcff 100644 --- a/Utilities/cmcurl/lib/dict.h +++ b/Utilities/cmcurl/lib/dict.h
@@ -24,7 +24,7 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_DICT -extern const struct Curl_handler Curl_handler_dict; +extern const struct Curl_protocol Curl_protocol_dict; #endif #endif /* HEADER_CURL_DICT_H */
diff --git a/Utilities/cmcurl/lib/dnscache.c b/Utilities/cmcurl/lib/dnscache.c new file mode 100644 index 0000000..20f6b21 --- /dev/null +++ b/Utilities/cmcurl/lib/dnscache.c
@@ -0,0 +1,873 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef __VMS +#include <in.h> +#include <inet.h> +#endif + +#include "urldata.h" +#include "curl_addrinfo.h" +#include "curl_share.h" +#include "curl_trc.h" +#include "dnscache.h" +#include "hash.h" +#include "hostip.h" +#include "httpsrr.h" +#include "progress.h" +#include "rand.h" +#include "strcase.h" +#include "curlx/inet_ntop.h" +#include "curlx/inet_pton.h" +#include "curlx/strcopy.h" +#include "curlx/strparse.h" + +#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ + +#define MAX_DNS_CACHE_SIZE 29999 + +static void dnscache_entry_free(struct Curl_dns_entry *dns) +{ + Curl_freeaddrinfo(dns->addr); +#ifdef USE_HTTPSRR + if(dns->hinfo) { + Curl_httpsrr_cleanup(dns->hinfo); + curlx_free(dns->hinfo); + } +#endif + curlx_free(dns); +} + +/* + * Create a hostcache id string for the provided host + port, to be used by + * the DNS caching. Without alloc. Return length of the id string. + */ +static size_t create_dnscache_id(const char *name, + size_t nlen, /* 0 or actual name length */ + uint16_t port, char *ptr, size_t buflen) +{ + size_t len = nlen ? nlen : strlen(name); + DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); + if(len > (buflen - 7)) + len = buflen - 7; + /* store and lower case the name */ + Curl_strntolower(ptr, name, len); + return curl_msnprintf(&ptr[len], 7, ":%u", port) + len; +} + +struct dnscache_prune_data { + struct curltime now; + timediff_t oldest_ms; /* oldest time in cache not pruned. */ + timediff_t max_age_ms; +}; + +/* + * This function is set as a callback to be called for every entry in the DNS + * cache when we want to prune old unused entries. + * + * Returning non-zero means remove the entry, return 0 to keep it in the + * cache. + */ +static int dnscache_entry_is_stale(void *datap, void *hc) +{ + struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap; + struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc; + + if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) { + /* get age in milliseconds */ + timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp); + if(!dns->addr) + age *= 2; /* negative entries age twice as fast */ + if(age >= prune->max_age_ms) + return TRUE; + if(age > prune->oldest_ms) + prune->oldest_ms = age; + } + return FALSE; +} + +/* + * Prune the DNS cache. This assumes that a lock has already been taken. + * Returns the 'age' of the oldest still kept entry - in milliseconds. + */ +static timediff_t dnscache_prune(struct Curl_hash *hostcache, + timediff_t cache_timeout_ms, + struct curltime now) +{ + struct dnscache_prune_data user; + + user.max_age_ms = cache_timeout_ms; + user.now = now; + user.oldest_ms = 0; + + Curl_hash_clean_with_criterium(hostcache, + (void *)&user, + dnscache_entry_is_stale); + + return user.oldest_ms; +} + +static struct Curl_dnscache *dnscache_get(struct Curl_easy *data) +{ + if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) + return &data->share->dnscache; + if(data->multi) + return &data->multi->dnscache; + return NULL; +} + +static void dnscache_lock(struct Curl_easy *data, + struct Curl_dnscache *dnscache) +{ + if(data->share && dnscache == &data->share->dnscache) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); +} + +static void dnscache_unlock(struct Curl_easy *data, + struct Curl_dnscache *dnscache) +{ + if(data->share && dnscache == &data->share->dnscache) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); +} + +/* + * Library-wide function for pruning the DNS cache. This function takes and + * returns the appropriate locks. + */ +void Curl_dnscache_prune(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + /* the timeout may be set -1 (forever) */ + timediff_t timeout_ms = data->set.dns_cache_timeout_ms; + + if(!dnscache || (timeout_ms == -1)) + /* NULL hostcache means we cannot do it */ + return; + + dnscache_lock(data, dnscache); + + do { + /* Remove outdated and unused entries from the hostcache */ + timediff_t oldest_ms = + dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data)); + + if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE) + /* prune the ones over half this age */ + timeout_ms = oldest_ms / 2; + else + break; + + /* if the cache size is still too big, use the oldest age as new prune + limit */ + } while(timeout_ms); + + dnscache_unlock(data, dnscache); +} + +void Curl_dnscache_clear(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + if(dnscache) { + dnscache_lock(data, dnscache); + Curl_hash_clean(&dnscache->entries); + dnscache_unlock(data, dnscache); + } +} + +/* lookup address, returns entry if found and not stale */ +static CURLcode fetch_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pdns) +{ + struct Curl_dns_entry *dns = NULL; + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + CURLcode result = CURLE_OK; + + *pdns = NULL; + if(!dnscache) + return CURLE_OK; + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(hostname, 0, port, + entry_id, sizeof(entry_id)); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + + /* No entry found in cache, check if we might have a wildcard entry */ + if(!dns && data->state.wildcard_resolve) { + entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id)); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + } + + if(dns && (data->set.dns_cache_timeout_ms != -1)) { + /* See whether the returned entry is stale. Done before we release lock */ + struct dnscache_prune_data user; + + user.now = *Curl_pgrs_now(data); + user.max_age_ms = data->set.dns_cache_timeout_ms; + user.oldest_ms = 0; + + if(dnscache_entry_is_stale(&user, dns)) { + infof(data, "Hostname in DNS cache was stale, zapped"); + dns = NULL; /* the memory deallocation is being handled by the hash */ + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + } + } + + if(dns) { + if((dns->dns_queries & dns_queries) != dns_queries) { + /* The entry does not cover all wanted DNS queries, a miss. */ + dns = NULL; + } + else if(!(dns->dns_responses & dns_queries)) { + /* The entry has no responses for the wanted DNS queries. */ + CURL_TRC_DNS(data, "cache entry does not have type=%s addresses", + Curl_resolv_query_str(dns_queries)); + dns = NULL; + result = CURLE_COULDNT_RESOLVE_HOST; + } + } + + if(dns && !dns->addr) { /* negative entry */ + dns = NULL; + result = CURLE_COULDNT_RESOLVE_HOST; + } + *pdns = dns; + return result; +} + +/* + * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. + * + * Curl_resolv() checks initially and multi_runsingle() checks each time + * it discovers the handle in the state WAITRESOLVE whether the hostname + * has already been resolved and the address has already been stored in + * the DNS cache. This short circuits waiting for a lot of pending + * lookups for the same hostname requested by different handles. + * + * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after + * use, or we will leak memory! + */ +CURLcode Curl_dnscache_get(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pentry) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; + + dnscache_lock(data, dnscache); + result = fetch_addr(data, dnscache, dns_queries, hostname, port, &dns); + if(!result && dns) + dns->refcount++; /* we pass out a reference */ + else if(result) { + DEBUGASSERT(!dns); + dns = NULL; + } + dnscache_unlock(data, dnscache); + + *pentry = dns; + return result; +} + +#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; +} + +/* + * dns_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' + * struct by re-linking its linked list. + * + * The addr argument should be the address of a pointer to the head node of a + * `Curl_addrinfo` list and it will be modified to point to the new head after + * shuffling. + * + * Not declared static only to make it easy to use in a unit test! + * + * @unittest 1608 + */ +UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data, + struct Curl_addrinfo **addr); +UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data, + struct Curl_addrinfo **addr) +{ + CURLcode result = CURLE_OK; + const int num_addrs = num_addresses(*addr); + + if(num_addrs > 1) { + struct Curl_addrinfo **nodes; + CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs); + + nodes = curlx_malloc(num_addrs * sizeof(*nodes)); + if(nodes) { + int i; + unsigned int *rnd; + const size_t rnd_size = num_addrs * sizeof(*rnd); + + /* build a plain array of Curl_addrinfo pointers */ + nodes[0] = *addr; + for(i = 1; i < num_addrs; i++) { + nodes[i] = nodes[i - 1]->ai_next; + } + + rnd = curlx_malloc(rnd_size); + if(rnd) { + /* Fisher-Yates shuffle */ + if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { + struct Curl_addrinfo *swap_tmp; + for(i = num_addrs - 1; i > 0; i--) { + swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)]; + nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i]; + nodes[i] = swap_tmp; + } + + /* relink list in the new order */ + for(i = 1; i < num_addrs; i++) { + nodes[i - 1]->ai_next = nodes[i]; + } + + nodes[num_addrs - 1]->ai_next = NULL; + *addr = nodes[0]; + } + curlx_free(rnd); + } + else + result = CURLE_OUT_OF_MEMORY; + curlx_free(nodes); + } + else + result = CURLE_OUT_OF_MEMORY; + } + return result; +} +#endif + +static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, + int ai_family) +{ + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) + return TRUE; + } + return FALSE; +} + +static struct Curl_dns_entry *dnscache_entry_create( + struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + size_t hostlen, + uint16_t port, + bool permanent) +{ + struct Curl_dns_entry *dns = NULL; + + /* Create a new cache entry, struct already has the hostname NUL */ + dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen); + if(!dns) + goto out; + + dns->refcount = 1; /* the cache has the first reference */ + dns->dns_queries = dns_queries; + dns->port = port; + if(hostlen) + memcpy(dns->hostname, hostname, hostlen); + + if(permanent) { + dns->timestamp.tv_sec = 0; /* an entry that never goes stale */ + dns->timestamp.tv_usec = 0; /* an entry that never goes stale */ + } + else { + dns->timestamp = *Curl_pgrs_now(data); + } + + /* Take the given address lists into the entry */ + if(paddr1 && *paddr1) { + dns->addr = *paddr1; + *paddr1 = NULL; + } + if(paddr2 && *paddr2) { + struct Curl_addrinfo **phead = &dns->addr; + while(*phead) + phead = &(*phead)->ai_next; + *phead = *paddr2; + *paddr2 = NULL; + } + + if((dns_queries & CURL_DNSQ_A) && + dnscache_ai_has_family(dns->addr, PF_INET)) + dns->dns_responses |= CURL_DNSQ_A; + +#ifdef USE_IPV6 + if((dns_queries & CURL_DNSQ_AAAA) && + dnscache_ai_has_family(dns->addr, PF_INET6)) + dns->dns_responses |= CURL_DNSQ_AAAA; +#endif /* USE_IPV6 */ + +#ifndef CURL_DISABLE_SHUFFLE_DNS + /* shuffle addresses if requested */ + if(data->set.dns_shuffle_addresses && dns->addr) { + CURLcode result = dns_shuffle_addr(data, &dns->addr); + if(result) { + /* free without lock, we are the sole owner */ + dnscache_entry_free(dns); + dns = NULL; + goto out; + } + } +#else + (void)data; +#endif + +out: + if(paddr1 && *paddr1) { + Curl_freeaddrinfo(*paddr1); + *paddr1 = NULL; + } + if(paddr2 && *paddr2) { + Curl_freeaddrinfo(*paddr2); + *paddr2 = NULL; + } + return dns; +} + +struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + uint16_t port) +{ + return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname, + hostname ? strlen(hostname) : 0, + port, FALSE); +} + +struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + uint16_t port) +{ + return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname, + hostname ? strlen(hostname) : 0, + port, FALSE); +} + +#ifdef USE_HTTPSRR +void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, + struct Curl_https_rrinfo *hinfo) +{ + /* only do this when this is the only reference */ + DEBUGASSERT(dns->refcount == 1); + /* it should have been in the queries */ + DEBUGASSERT(dns->dns_queries & CURL_DNSQ_HTTPS); + if(dns->hinfo) { + Curl_httpsrr_cleanup(dns->hinfo); + curlx_free(dns->hinfo); + } + dns->hinfo = hinfo; + dns->dns_responses |= CURL_DNSQ_HTTPS; +} +#endif /* USE_HTTPSRR */ + +static struct Curl_dns_entry *dnscache_add_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + size_t hlen, + uint16_t port, + bool permanent) +{ + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns2; + + dns = dnscache_entry_create(data, dns_queries, paddr, NULL, + hostname, hlen, port, permanent); + if(!dns) + return NULL; + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(hostname, hlen, port, + entry_id, sizeof(entry_id)); + + /* Store the resolved data in our DNS cache. */ + dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, + (void *)dns); + if(!dns2) { + dnscache_entry_free(dns); + return NULL; + } + + dns = dns2; + dns->refcount++; /* mark entry as in-use */ + return dns; +} + +CURLcode Curl_dnscache_add(struct Curl_easy *data, + struct Curl_dns_entry *entry) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + char id[MAX_HOSTCACHE_LEN]; + size_t idlen; + + if(!dnscache) + return CURLE_FAILED_INIT; + /* Create an entry id, based upon the hostname and port */ + idlen = create_dnscache_id(entry->hostname, 0, entry->port, id, sizeof(id)); + + /* Store the resolved data in our DNS cache and up ref count */ + dnscache_lock(data, dnscache); + if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) { + dnscache_unlock(data, dnscache); + return CURLE_OUT_OF_MEMORY; + } + entry->refcount++; + dnscache_unlock(data, dnscache); + return CURLE_OK; +} + +CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, + uint8_t dns_queries, + const char *host, + uint16_t port) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns; + DEBUGASSERT(dnscache); + if(!dnscache) + return CURLE_FAILED_INIT; + + dnscache_lock(data, dnscache); + + /* put this new host in the cache */ + dns = dnscache_add_addr(data, dnscache, dns_queries, NULL, + host, strlen(host), port, FALSE); + if(dns) { + /* release the returned reference; the cache itself will keep the + * entry alive: */ + dns->refcount--; + dnscache_unlock(data, dnscache); + CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s", + host, port, Curl_resolv_query_str(dns_queries)); + return CURLE_OK; + } + dnscache_unlock(data, dnscache); + return CURLE_OUT_OF_MEMORY; +} + +struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + if(!dns) + return NULL; + else { + struct Curl_dnscache *dnscache = dnscache_get(data); + dnscache_lock(data, dnscache); + dns->refcount++; + dnscache_unlock(data, dnscache); + return dns; + } +} + +/* + * Curl_dns_entry_unlink() releases a reference to the given cached DNS entry. + * When the reference count reaches 0, the entry is destroyed. It is important + * that only one unlink is made for each Curl_resolv() call. + * + * May be called with 'data' == NULL for global cache. + */ +void Curl_dns_entry_unlink(struct Curl_easy *data, + struct Curl_dns_entry **pdns) +{ + if(*pdns) { + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns = *pdns; + *pdns = NULL; + dnscache_lock(data, dnscache); + dns->refcount--; + if(dns->refcount == 0) + dnscache_entry_free(dns); + dnscache_unlock(data, dnscache); + } +} + +static void dnscache_entry_dtor(void *entry) +{ + struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry; + DEBUGASSERT(dns && (dns->refcount > 0)); + dns->refcount--; + if(dns->refcount == 0) + dnscache_entry_free(dns); +} + +/* + * Curl_dnscache_init() inits a new DNS cache. + */ +void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size) +{ + Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare, + dnscache_entry_dtor); +} + +void Curl_dnscache_destroy(struct Curl_dnscache *dns) +{ + Curl_hash_destroy(&dns->entries); +} + +CURLcode Curl_loadhostpairs(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct curl_slist *hostp; + + if(!dnscache) + return CURLE_FAILED_INIT; + + /* Default is no wildcard found */ + data->state.wildcard_resolve = FALSE; + + for(hostp = data->state.resolve; hostp; hostp = hostp->next) { + char entry_id[MAX_HOSTCACHE_LEN]; + const char *host = hostp->data; + struct Curl_str source; + if(!host) + continue; + if(*host == '-') { + curl_off_t num = 0; + size_t entry_len; + host++; + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']') || + curlx_str_single(&host, ':')) + continue; + } + else { + if(curlx_str_until(&host, &source, 4096, ':') || + curlx_str_single(&host, ':')) { + continue; + } + } + + if(!curlx_str_number(&host, &num, 0xffff)) { + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(curlx_str(&source), + curlx_strlen(&source), (uint16_t)num, + entry_id, sizeof(entry_id)); + dnscache_lock(data, dnscache); + /* delete entry, ignore if it did not exist */ + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + dnscache_unlock(data, dnscache); + } + } + else { + struct Curl_dns_entry *dns; + struct Curl_addrinfo *head = NULL, *tail = NULL; + size_t entry_len; + char address[64]; + curl_off_t tmpofft = 0; + uint16_t port = 0; + bool permanent = TRUE; + bool error = TRUE; + VERBOSE(const char *addresses = NULL); + + if(*host == '+') { + host++; + permanent = FALSE; + } + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']')) + continue; + } + else { + if(curlx_str_until(&host, &source, 4096, ':')) + continue; + } + if(curlx_str_single(&host, ':') || + curlx_str_number(&host, &tmpofft, 0xffff) || + curlx_str_single(&host, ':')) + goto err; + port = (uint16_t)tmpofft; + + VERBOSE(addresses = host); + + /* start the address section */ + while(*host) { + struct Curl_str target; + struct Curl_addrinfo *ai; + CURLcode result; + + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']')) + goto err; + } + else { + if(curlx_str_until(&host, &target, 4096, ',')) { + if(curlx_str_single(&host, ',')) + goto err; + /* survive nothing but a comma */ + continue; + } + } +#ifndef USE_IPV6 + if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) { + infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.", + (int)curlx_strlen(&target), curlx_str(&target)); + if(curlx_str_single(&host, ',')) + goto err; + continue; + } +#endif + + if(curlx_strlen(&target) >= sizeof(address)) + goto err; + + memcpy(address, curlx_str(&target), curlx_strlen(&target)); + address[curlx_strlen(&target)] = '\0'; + + result = Curl_str2addr(address, port, &ai); + if(result) { + infof(data, "Resolve address '%s' found illegal", address); + goto err; + } + + if(tail) { + tail->ai_next = ai; + tail = tail->ai_next; + } + else { + head = tail = ai; + } + if(curlx_str_single(&host, ',')) + break; + } + + if(!head) + goto err; + + error = FALSE; +err: + if(error) { + failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data); + Curl_freeaddrinfo(head); + return CURLE_SETOPT_OPTION_SYNTAX; + } + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source), + port, entry_id, sizeof(entry_id)); + + dnscache_lock(data, dnscache); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + + if(dns) { + infof(data, "RESOLVE %.*s:%u - old addresses discarded", + (int)curlx_strlen(&source), + curlx_str(&source), port); + /* delete old entry, there are two reasons for this + 1. old entry may have different addresses. + 2. even if entry with correct addresses is already in the cache, + but if it is close to expire, then by the time next http + request is made, it can get expired and pruned because old + entry is not necessarily marked as permanent. + 3. when adding a non-permanent entry, we want it to remove and + replace an existing permanent entry. + 4. when adding a non-permanent entry, we want it to get a "fresh" + timeout that starts _now_. */ + + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + } + + /* put this new host in the cache, an overridy for ALL dns queries */ + dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, + &head, curlx_str(&source), + curlx_strlen(&source), port, permanent); + if(dns) + /* release the returned reference; the cache itself will keep the + * entry alive: */ + dns->refcount--; + + dnscache_unlock(data, dnscache); + + if(!dns) + return CURLE_OUT_OF_MEMORY; + + infof(data, "Added %.*s:%u:%s to DNS cache%s", + (int)curlx_strlen(&source), curlx_str(&source), port, addresses, + permanent ? "" : " (non-permanent)"); + + /* Wildcard hostname */ + if(curlx_str_casecompare(&source, "*")) { + infof(data, "RESOLVE *:%u using wildcard", port); + data->state.wildcard_resolve = TRUE; + } + } + } + data->state.resolve = NULL; /* dealt with now */ + + return CURLE_OK; +}
diff --git a/Utilities/cmcurl/lib/dnscache.h b/Utilities/cmcurl/lib/dnscache.h new file mode 100644 index 0000000..ebe25f6 --- /dev/null +++ b/Utilities/cmcurl/lib/dnscache.h
@@ -0,0 +1,143 @@ +#ifndef HEADER_CURL_DNSCACHE_H +#define HEADER_CURL_DNSCACHE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "hash.h" +#include "curlx/timeval.h" + +struct addrinfo; +struct hostent; +struct Curl_easy; +struct connectdata; +struct easy_pollset; +struct Curl_https_rrinfo; +struct Curl_multi; + +struct Curl_dns_entry { + struct Curl_addrinfo *addr; +#ifdef USE_HTTPSRR + struct Curl_https_rrinfo *hinfo; +#endif + /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ + struct curltime timestamp; + /* reference counter, entry is freed on reaching 0 */ + uint32_t refcount; + /* hostname port number that resolved to addr. */ + uint16_t port; + uint8_t dns_queries; /* CURL_DNSQ_* type of queries performed for this */ + uint8_t dns_responses; /* CURL_DNSQ_* type this entry has responses for */ + /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ + char hostname[1]; +}; + +/* + * Create a `Curl_dns_entry` with a reference count of 1. + * Use `Curl_dns_entry_unlink()` to release your hold on it. + * + * The call takes ownership of `paddr`, even in case of failure, and always + * clears `*paddr`. It makes a copy of `hostname`. + * + * Returns entry or NULL on OOM. + */ +struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + uint16_t port); + +struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + uint16_t port); + +#ifdef USE_HTTPSRR +void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, + struct Curl_https_rrinfo *hinfo); +#endif /* USE_HTTPSRR */ + +/* Increase the ref counter and return it for storing in another place. + * May be called with NULL, in which case it returns NULL. */ +struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, + struct Curl_dns_entry *dns); + +/* unlink a dns entry, frees all resources if it was the last reference. + * Always clears `*pdns`` */ +void Curl_dns_entry_unlink(struct Curl_easy *data, + struct Curl_dns_entry **pdns); + +struct Curl_dnscache { + struct Curl_hash entries; +}; + +/* init a new dns cache */ +void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size); + +void Curl_dnscache_destroy(struct Curl_dnscache *dns); + +/* prune old entries from the DNS cache */ +void Curl_dnscache_prune(struct Curl_easy *data); + +/* clear the DNS cache */ +void Curl_dnscache_clear(struct Curl_easy *data); + +/* + * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. + * + * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after + * use, or we will leak memory! + * Returns CURLE_OK or CURLE_COULDNT_RESOLVE_HOST when a negative + * entry was in the cache. + */ +CURLcode Curl_dnscache_get(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pentry); + +/* + * Curl_dnscache_addr() adds `entry` to the cache, increasing its + * reference count on success. + */ +CURLcode Curl_dnscache_add(struct Curl_easy *data, + struct Curl_dns_entry *entry); + +/* Store a "negative" entry for host:port, e.g. remember that + * it could not be resolved. */ +CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, + uint8_t dns_queries, + const char *host, + uint16_t port); + +/* + * Populate the cache with specified entries from CURLOPT_RESOLVE. + */ +CURLcode Curl_loadhostpairs(struct Curl_easy *data); + +#endif /* HEADER_CURL_DNSCACHE_H */
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index 9052fab..3044135 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c
@@ -28,19 +28,22 @@ #include "urldata.h" #include "curl_addrinfo.h" #include "doh.h" - #include "curl_trc.h" +#include "httpsrr.h" #include "multiif.h" #include "url.h" #include "connect.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "curlx/dynbuf.h" -#include "escape.h" +#include "escape.h" /* for Curl_hexencode() */ #include "urlapi-int.h" #define DNS_CLASS_IN 0x01 -#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void doh_close(struct Curl_easy *data, + struct Curl_resolv_async *async); + +#ifdef CURLVERBOSE static const char * const errors[] = { "", "Bad label", @@ -65,12 +68,17 @@ return "bad error code"; } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ /* @unittest 1655 */ UNITTEST DOHcode doh_req_encode(const char *host, DNStype dnstype, + unsigned char *dnsp, /* buffer */ + size_t len, /* buffer size */ + size_t *olen); /* output length */ +UNITTEST DOHcode doh_req_encode(const char *host, + DNStype dnstype, unsigned char *dnsp, /* buffer */ size_t len, /* buffer size */ size_t *olen) /* output length */ @@ -113,7 +121,7 @@ if(len < expected_len) return DOH_TOO_SMALL_BUFFER; - *dnsp++ = 0; /* 16 bit id */ + *dnsp++ = 0; /* 16-bit id */ *dnsp++ = 0; *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */ *dnsp++ = '\0'; /* |RA| Z | RCODE | */ @@ -129,7 +137,7 @@ /* encode each label and store it in the QNAME */ while(*hostp) { size_t labellen; - char *dot = strchr(hostp, '.'); + const char *dot = strchr(hostp, '.'); if(dot) labellen = dot - hostp; else @@ -152,10 +160,10 @@ *dnsp++ = 0; /* append zero-length label for root */ /* There are assigned TYPE codes beyond 255: use range [1..65535] */ - *dnsp++ = (unsigned char)(255 & (dnstype >> 8)); /* upper 8 bit TYPE */ - *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */ + *dnsp++ = (unsigned char)(255 & (dnstype >> 8)); /* upper 8-bit TYPE */ + *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8-bit TYPE */ - *dnsp++ = '\0'; /* upper 8 bit CLASS */ + *dnsp++ = '\0'; /* upper 8-bit CLASS */ *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */ *olen = dnsp - orig; @@ -181,7 +189,7 @@ return realsize; } -#if defined(USE_HTTPSRR) && defined(DEBUGBUILD) +#if defined(USE_HTTPSRR) && defined(DEBUGBUILD) && defined(CURLVERBOSE) /* doh_print_buf truncates if the hex string will be more than this */ #define LOCAL_PB_HEXMAX 400 @@ -201,56 +209,66 @@ infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr); else infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr); - return; } #endif /* called from multi when a sub transfer, e.g. doh probe, is done. - * This looks up the the probe response at its meta CURL_EZM_DOH_PROBE + * This looks up the probe response at its meta CURL_EZM_DOH_PROBE * and copies the response body over to the struct at the master's * meta at CURL_EZM_DOH_MASTER. */ static void doh_probe_done(struct Curl_easy *data, struct Curl_easy *doh, CURLcode result) { - struct doh_probes *dohp = data->state.async.doh; - DEBUGASSERT(dohp); - if(dohp) { - struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); - int i; + struct Curl_resolv_async *async = NULL; + struct doh_probes *dohp = NULL; + struct doh_request *doh_req = NULL; + int i; - for(i = 0; i < DOH_SLOT_COUNT; ++i) { - if(dohp->probe_resp[i].probe_mid == doh->mid) - break; - } - if(i >= DOH_SLOT_COUNT) { - failf(data, "unknown sub request done"); - return; - } + doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); + if(!doh_req) { + DEBUGASSERT(0); + return; + } - dohp->pending--; - infof(doh, "a DoH request is completed, %u to go", dohp->pending); - dohp->probe_resp[i].result = result; - /* We expect either the meta data still to exist or the sub request - * to have already failed. */ - DEBUGASSERT(doh_req || result); - if(doh_req) { - if(!result) { - dohp->probe_resp[i].dnstype = doh_req->dnstype; - result = curlx_dyn_addn(&dohp->probe_resp[i].body, - curlx_dyn_ptr(&doh_req->resp_body), - curlx_dyn_len(&doh_req->resp_body)); - curlx_dyn_free(&doh_req->resp_body); - } - Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); - } + async = Curl_async_get(data, doh_req->resolv_id); + if(!async) { + CURL_TRC_DNS(data, "[%u] ignoring outdated DoH response", + doh_req->resolv_id); + return; + } + dohp = async->doh; - if(result) - infof(doh, "DoH request %s", curl_easy_strerror(result)); + for(i = 0; i < DOH_SLOT_COUNT; ++i) { + if(dohp->probe_resp[i].probe_mid == doh->mid) + break; + } + /* We really should have found the slot where to store the response */ + if(i >= DOH_SLOT_COUNT) { + DEBUGASSERT(0); + failf(data, "DoH: unknown sub request done"); + return; + } - if(!dohp->pending) { - /* DoH completed, run the transfer picking up the results */ - Curl_multi_mark_dirty(data); - } + dohp->pending--; + infof(doh, "a DoH request is completed, %u to go", dohp->pending); + dohp->probe_resp[i].result = result; + /* We expect either the meta data still to exist or the sub request + * to have already failed. */ + if(!result) { + dohp->probe_resp[i].dnstype = doh_req->dnstype; + result = curlx_dyn_addn(&dohp->probe_resp[i].body, + curlx_dyn_ptr(&doh_req->resp_body), + curlx_dyn_len(&doh_req->resp_body)); + curlx_dyn_free(&doh_req->resp_body); + } + Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); + + if(result) + infof(doh, "DoH request %s", curl_easy_strerror(result)); + + if(!dohp->pending) { + /* DoH completed, run the transfer picking up the results */ + Curl_multi_mark_dirty(data); } } @@ -279,6 +297,7 @@ DNStype dnstype, const char *host, const char *url, CURLM *multi, + uint32_t resolv_id, uint32_t *pmid) { struct Curl_easy *doh = NULL; @@ -292,6 +311,7 @@ doh_req = curlx_calloc(1, sizeof(*doh_req)); if(!doh_req) return CURLE_OUT_OF_MEMORY; + doh_req->resolv_id = resolv_id; doh_req->dnstype = dnstype; curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE); @@ -304,8 +324,8 @@ goto error; } - timeout_ms = Curl_timeleft_ms(data, TRUE); - if(timeout_ms <= 0) { + timeout_ms = Curl_timeleft_ms(data); + if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; goto error; } @@ -324,9 +344,7 @@ /* pass in the struct pointer via a local variable to please coverity and the gcc typecheck helpers */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - doh->state.feat = &Curl_trc_feat_dns; -#endif + VERBOSE(doh->state.feat = &Curl_trc_feat_dns); ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_probe_write_cb); @@ -434,29 +452,22 @@ * a 'Curl_addrinfo *' with the address information. */ -CURLcode Curl_doh(struct Curl_easy *data, const char *hostname, - int port, int ip_version) +CURLcode Curl_doh(struct Curl_easy *data, + struct Curl_resolv_async *async) { CURLcode result = CURLE_OK; struct doh_probes *dohp = NULL; - struct connectdata *conn = data->conn; size_t i; - DEBUGASSERT(conn); - DEBUGASSERT(!data->state.async.doh); - DEBUGASSERT(hostname && hostname[0]); - if(data->state.async.doh) - Curl_doh_cleanup(data); - - data->state.async.done = FALSE; - data->state.async.port = port; - data->state.async.ip_version = ip_version; - data->state.async.hostname = curlx_strdup(hostname); - if(!data->state.async.hostname) - return CURLE_OUT_OF_MEMORY; + DEBUGASSERT(!async->doh); + DEBUGASSERT(async->hostname[0]); + if(async->doh) { + DEBUGASSERT(0); /* should not happen */ + Curl_doh_cleanup(data, async); + } /* start clean, consider allocating this struct on demand */ - data->state.async.doh = dohp = curlx_calloc(1, sizeof(struct doh_probes)); + async->doh = dohp = curlx_calloc(1, sizeof(struct doh_probes)); if(!dohp) return CURLE_OUT_OF_MEMORY; @@ -465,28 +476,29 @@ curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE); } - conn->bits.doh = TRUE; - dohp->host = data->state.async.hostname; - dohp->port = data->state.async.port; + dohp->host = async->hostname; + dohp->port = async->port; /* We are making sub easy handles and want to be called back when * one is done. */ data->sub_xfer_done = doh_probe_done; /* create IPv4 DoH request */ - result = doh_probe_run(data, CURL_DNS_TYPE_A, - hostname, data->set.str[STRING_DOH], - data->multi, - &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); - if(result) - goto error; - dohp->pending++; + if(async->dns_queries & CURL_DNSQ_A) { + result = doh_probe_run(data, CURL_DNS_TYPE_A, + async->hostname, data->set.str[STRING_DOH], + data->multi, async->id, + &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); + if(result) + goto error; + dohp->pending++; + } #ifdef USE_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { + if(async->dns_queries & CURL_DNSQ_AAAA) { /* create IPv6 DoH request */ result = doh_probe_run(data, CURL_DNS_TYPE_AAAA, - hostname, data->set.str[STRING_DOH], - data->multi, + async->hostname, data->set.str[STRING_DOH], + data->multi, async->id, &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid); if(result) goto error; @@ -495,17 +507,17 @@ #endif #ifdef USE_HTTPSRR - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { - /* Only use HTTPS RR for HTTP(S) transfers */ + if(async->dns_queries & CURL_DNSQ_HTTPS) { char *qname = NULL; - if(port != PORT_HTTPS) { - qname = curl_maprintf("_%d._https.%s", port, hostname); + if(async->port != PORT_HTTPS) { + qname = curl_maprintf("_%d._https.%s", async->port, async->hostname); if(!qname) goto error; } result = doh_probe_run(data, CURL_DNS_TYPE_HTTPS, - qname ? qname : hostname, data->set.str[STRING_DOH], - data->multi, + qname ? qname : async->hostname, + data->set.str[STRING_DOH], data->multi, + async->id, &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid); curlx_free(qname); if(result) @@ -516,7 +528,7 @@ return CURLE_OK; error: - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); return result; } @@ -594,7 +606,7 @@ /* silently ignore RRs over the limit */ if(d->numhttps_rrs < DOH_MAX_HTTPS) { struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs]; - h->val = Curl_memdup(&doh[index], len); + h->val = curlx_memdup(&doh[index], len); if(!h->val) return DOH_OUT_OF_MEM; h->len = len; @@ -692,15 +704,17 @@ return rc; break; case CURL_DNS_TYPE_DNAME: - /* explicit for clarity; just skip; rely on synthesized CNAME */ + /* explicit for clarity; skip; rely on synthesized CNAME */ break; default: - /* unsupported type, just skip it */ + /* unsupported type, skip it */ break; } return DOH_OK; } +/* @unittest 1655 */ +UNITTEST void de_init(struct dohentry *de); UNITTEST void de_init(struct dohentry *de) { int i; @@ -710,6 +724,11 @@ curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME); } +/* @unittest 1655 */ +UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d); UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, size_t dohlen, DNStype dnstype, @@ -757,9 +776,9 @@ return DOH_DNS_OUT_OF_RANGE; type = doh_get16bit(doh, index); - if((type != CURL_DNS_TYPE_CNAME) /* may be synthesized from DNAME */ - && (type != CURL_DNS_TYPE_DNAME) /* if present, accept and ignore */ - && (type != dnstype)) + if((type != CURL_DNS_TYPE_CNAME) && /* may be synthesized from DNAME */ + (type != CURL_DNS_TYPE_DNAME) && /* if present, accept and ignore */ + (type != dnstype)) /* Not the same type as was asked for nor CNAME nor DNAME */ return DOH_DNS_UNEXPECTED_TYPE; index += 2; @@ -841,7 +860,7 @@ if(index != dohlen) return DOH_DNS_MALFORMAT; /* something is wrong */ -#ifdef USE_HTTTPS +#ifdef USE_HTTPSRR if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs) #else @@ -853,7 +872,7 @@ return DOH_OK; /* ok */ } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static void doh_show(struct Curl_easy *data, const struct dohentry *d) { @@ -886,13 +905,13 @@ } #ifdef USE_HTTPSRR for(i = 0; i < d->numhttps_rrs; i++) { -# ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) doh_print_buf(data, "DoH HTTPS", d->https_rrs[i].val, d->https_rrs[i].len); -# else +#else infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len); -# endif - } #endif + } +#endif /* USE_HTTPSRR */ for(i = 0; i < d->numcname; i++) { infof(data, "CNAME: %s", curlx_dyn_ptr(&d->cname[i])); } @@ -982,7 +1001,7 @@ addr = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); - addr->sin_family = (CURL_SA_FAMILY_T)addrtype; + addr->sin_family = addrtype; addr->sin_port = htons((unsigned short)port); break; @@ -991,7 +1010,7 @@ addr6 = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); - addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype; + addr6->sin6_family = addrtype; addr6->sin6_port = htons((unsigned short)port); break; #endif @@ -1009,7 +1028,7 @@ return result; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static const char *doh_type2name(DNStype dnstype) { switch(dnstype) { @@ -1027,6 +1046,8 @@ } #endif +/* @unittest 1655 */ +UNITTEST void de_cleanup(struct dohentry *d); UNITTEST void de_cleanup(struct dohentry *d) { int i = 0; @@ -1035,7 +1056,7 @@ } #ifdef USE_HTTPSRR for(i = 0; i < d->numhttps_rrs; i++) - Curl_safefree(d->https_rrs[i].val); + curlx_safefree(d->https_rrs[i].val); #endif } @@ -1051,9 +1072,9 @@ * The encoding here is defined in * https://datatracker.ietf.org/doc/html/rfc1035#section-3.1 * - * The input buffer pointer will be modified so it points to - * just after the end of the DNS name encoding on output. (And - * that is why it is an "unsigned char **" :-) + * The input buffer pointer will be modified so it points to after the end of + * the DNS name encoding on output. (that is why it is an "unsigned char + * **" :-) */ static CURLcode doh_decode_rdata_name(const unsigned char **buf, size_t *remaining, char **dnsname) @@ -1098,11 +1119,10 @@ return CURLE_OK; } +/* @unittest 1658 */ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, const unsigned char *cp, size_t len, struct Curl_https_rrinfo **hrr); - -/* @unittest 1658 */ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, const unsigned char *cp, size_t len, struct Curl_https_rrinfo **hrr) @@ -1114,6 +1134,7 @@ CURLcode result = CURLE_OUT_OF_MEMORY; size_t olen; + (void)data; *hrr = NULL; if(len <= 2) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1131,7 +1152,6 @@ result = CURLE_WEIRD_SERVER_REPLY; goto err; } - lhrr->port = -1; /* until set */ while(len >= 4) { pcode = doh_get16bit(cp, 0); plen = doh_get16bit(cp, 2); @@ -1141,9 +1161,10 @@ result = CURLE_WEIRD_SERVER_REPLY; goto err; } - result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen); + result = Curl_httpsrr_set(lhrr, pcode, cp, plen); if(result) goto err; + Curl_httpsrr_trace(data, lhrr); cp += plen; len -= plen; expected_min_pcode = pcode + 1; @@ -1153,16 +1174,13 @@ return CURLE_OK; err: Curl_httpsrr_cleanup(lhrr); - Curl_safefree(lhrr); + curlx_safefree(lhrr); return result; } -#ifdef DEBUGBUILD -UNITTEST void doh_print_httpsrr(struct Curl_easy *data, - struct Curl_https_rrinfo *hrr); - -UNITTEST void doh_print_httpsrr(struct Curl_easy *data, - struct Curl_https_rrinfo *hrr) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) +static void doh_print_httpsrr(struct Curl_easy *data, + struct Curl_https_rrinfo *hrr) { DEBUGASSERT(hrr); infof(data, "HTTPS RR: priority %d, target: %s", hrr->priority, hrr->target); @@ -1193,37 +1211,35 @@ } else infof(data, "HTTPS RR: no ipv6hints"); - return; } # endif #endif -CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dnsp) +CURLcode Curl_doh_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) { + struct doh_probes *dohp = async->doh; CURLcode result = CURLE_OK; - struct doh_probes *dohp = data->state.async.doh; struct dohentry de; - *dnsp = NULL; /* defaults to no response */ + + *pdns = NULL; /* defaults to no response */ if(!dohp) return CURLE_OUT_OF_MEMORY; if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT32_MAX && dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT32_MAX) { failf(data, "Could not DoH-resolve: %s", dohp->host); - return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY : - CURLE_COULDNT_RESOLVE_HOST; + return async->for_proxy ? + CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } else if(!dohp->pending) { DOHcode rc[DOH_SLOT_COUNT]; int slot; - /* Clear any result the might still be there */ - Curl_resolv_unlink(data, &data->state.async.dns); - memset(rc, 0, sizeof(rc)); /* remove DoH handles from multi handle and close them */ - Curl_doh_close(data); + doh_close(data, async); /* parse the responses, create the struct and return it! */ de_init(&de); for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { @@ -1233,15 +1249,12 @@ rc[slot] = doh_resp_decode(curlx_dyn_uptr(&p->body), curlx_dyn_len(&p->body), p->dnstype, &de); -#ifndef CURL_DISABLE_VERBOSE_STRINGS if(rc[slot]) { CURL_TRC_DNS(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), doh_type2name(p->dnstype), dohp->host); } -#endif } /* next slot */ - result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { /* we have an address, of one kind or other */ struct Curl_dns_entry *dns; @@ -1257,51 +1270,56 @@ goto error; /* we got a response, create a dns entry. */ - dns = Curl_dnscache_mk_entry(data, ai, dohp->host, 0, dohp->port, FALSE); - if(dns) { - /* Now add and HTTPSRR information if we have */ -#ifdef USE_HTTPSRR - if(de.numhttps_rrs > 0 && result == CURLE_OK) { - struct Curl_https_rrinfo *hrr = NULL; - result = doh_resp_decode_httpsrr(data, de.https_rrs->val, - de.https_rrs->len, &hrr); - if(result) { - infof(data, "Failed to decode HTTPS RR"); - Curl_resolv_unlink(data, &dns); - goto error; - } - infof(data, "Some HTTPS RR to process"); -# ifdef DEBUGBUILD - doh_print_httpsrr(data, hrr); -# endif - dns->hinfo = hrr; - } -#endif - /* and add the entry to the cache */ - data->state.async.dns = dns; - result = Curl_dnscache_add(data, dns); - *dnsp = data->state.async.dns; + dns = Curl_dnscache_mk_entry(data, async->dns_queries, + &ai, dohp->host, dohp->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto error; } - else - Curl_freeaddrinfo(ai); - } /* address processing done */ - /* All done */ - data->state.async.done = TRUE; + /* Now add and HTTPSRR information if we have */ +#ifdef USE_HTTPSRR + if(de.numhttps_rrs > 0 && result == CURLE_OK) { + struct Curl_https_rrinfo *hrr = NULL; + result = doh_resp_decode_httpsrr(data, de.https_rrs->val, + de.https_rrs->len, &hrr); + if(result) { + infof(data, "Failed to decode HTTPS RR"); + Curl_dns_entry_unlink(data, &dns); + goto error; + } + infof(data, "Some HTTPS RR to process"); +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) + doh_print_httpsrr(data, hrr); +#endif + Curl_dns_entry_set_https_rr(dns, hrr); + } +#endif /* USE_HTTPSRR */ + + /* and add the entry to the cache */ + result = Curl_dnscache_add(data, dns); + *pdns = dns; + } /* address processing done */ + else { + result = async->for_proxy ? + CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; + } + } /* !dohp->pending */ else /* wait for pending DoH transactions to complete */ - return CURLE_OK; + return CURLE_AGAIN; error: de_cleanup(&de); - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); return result; } -void Curl_doh_close(struct Curl_easy *data) +static void doh_close(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct doh_probes *doh = data->state.async.doh; + struct doh_probes *doh = async ? async->doh : NULL; if(doh && data->multi) { struct Curl_easy *probe_data; uint32_t mid; @@ -1327,16 +1345,17 @@ } } -void Curl_doh_cleanup(struct Curl_easy *data) +void Curl_doh_cleanup(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct doh_probes *dohp = data->state.async.doh; + struct doh_probes *dohp = async->doh; if(dohp) { int i; - Curl_doh_close(data); + doh_close(data, async); for(i = 0; i < DOH_SLOT_COUNT; ++i) { curlx_dyn_free(&dohp->probe_resp[i].body); } - Curl_safefree(data->state.async.doh); + curlx_safefree(async->doh); } }
diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h index 43bc323..428b230 100644 --- a/Utilities/cmcurl/lib/doh.h +++ b/Utilities/cmcurl/lib/doh.h
@@ -23,26 +23,23 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "urldata.h" -#include "curl_addrinfo.h" -#ifdef USE_HTTPSRR -# include <stdint.h> -# include "httpsrr.h" -#endif -#ifndef CURL_DISABLE_DOH +/* enums outside of the #ifdef to make the types work in unitprotos.h even on + builds without DoH support */ + +struct Curl_resolv_async; typedef enum { DOH_OK, - DOH_DNS_BAD_LABEL, /* 1 */ - DOH_DNS_OUT_OF_RANGE, /* 2 */ - DOH_DNS_LABEL_LOOP, /* 3 */ - DOH_TOO_SMALL_BUFFER, /* 4 */ - DOH_OUT_OF_MEM, /* 5 */ - DOH_DNS_RDATA_LEN, /* 6 */ - DOH_DNS_MALFORMAT, /* 7 */ - DOH_DNS_BAD_RCODE, /* 8 - no such name */ + DOH_DNS_BAD_LABEL, /* 1 */ + DOH_DNS_OUT_OF_RANGE, /* 2 */ + DOH_DNS_LABEL_LOOP, /* 3 */ + DOH_TOO_SMALL_BUFFER, /* 4 */ + DOH_OUT_OF_MEM, /* 5 */ + DOH_DNS_RDATA_LEN, /* 6 */ + DOH_DNS_MALFORMAT, /* 7 */ + DOH_DNS_BAD_RCODE, /* 8 - no such name */ DOH_DNS_UNEXPECTED_TYPE, /* 9 */ DOH_DNS_UNEXPECTED_CLASS, /* 10 */ DOH_NO_CONTENT, /* 11 */ @@ -59,6 +56,10 @@ CURL_DNS_TYPE_HTTPS = 65 } DNStype; +struct dohentry; /* forward-declare for non-DoH builds */ + +#ifndef CURL_DISABLE_DOH + enum doh_slot_num { /* Explicit values for first two symbols so as to match hard-coded * constants in existing code @@ -92,6 +93,7 @@ struct curl_slist *req_hds; struct dynbuf resp_body; size_t req_body_len; + uint32_t resolv_id; /* id of the resolve operation */ DNStype dnstype; }; @@ -107,7 +109,7 @@ struct doh_probes { struct doh_response probe_resp[DOH_SLOT_COUNT]; unsigned int pending; /* still outstanding probes */ - int port; + uint16_t port; const char *host; }; @@ -115,12 +117,12 @@ * Curl_doh() starts a name resolve using DoH (DNS-over-HTTPS). It resolves a * name and returns a 'Curl_addrinfo *' with the address information. */ +CURLcode Curl_doh(struct Curl_easy *data, + struct Curl_resolv_async *async); -CURLcode Curl_doh(struct Curl_easy *data, const char *hostname, - int port, int ip_version); - -CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns); +CURLcode Curl_doh_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns); #define DOH_MAX_ADDR 24 #define DOH_MAX_CNAME 4 @@ -161,27 +163,15 @@ #endif }; -void Curl_doh_close(struct Curl_easy *data); -void Curl_doh_cleanup(struct Curl_easy *data); +void Curl_doh_cleanup(struct Curl_easy *data, + struct Curl_resolv_async *async); +#define Curl_doh_wanted(d) (!!(d)->set.doh) -#ifdef UNITTESTS -UNITTEST DOHcode doh_req_encode(const char *host, - DNStype dnstype, - unsigned char *dnsp, /* buffer */ - size_t len, /* buffer size */ - size_t *olen); /* output length */ -UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, - size_t dohlen, - DNStype dnstype, - struct dohentry *d); - -UNITTEST void de_init(struct dohentry *d); -UNITTEST void de_cleanup(struct dohentry *d); -#endif #else /* CURL_DISABLE_DOH */ -#define Curl_doh(a, b, c, d, e) NULL -#define Curl_doh_is_resolved(x, y) CURLE_COULDNT_RESOLVE_HOST +#define Curl_doh(a, b) NULL +#define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST +#define Curl_doh_wanted(d) FALSE #endif /* !CURL_DISABLE_DOH */ #endif /* HEADER_CURL_DOH_H */
diff --git a/Utilities/cmcurl/lib/dynhds.c b/Utilities/cmcurl/lib/dynhds.c index c69d312..097306c 100644 --- a/Utilities/cmcurl/lib/dynhds.c +++ b/Utilities/cmcurl/lib/dynhds.c
@@ -26,11 +26,6 @@ #include "dynhds.h" #include "strcase.h" -#ifdef USE_NGHTTP2 -#include <stdint.h> -#include <nghttp2/nghttp2.h> -#endif /* USE_NGHTTP2 */ - static struct dynhds_entry *entry_new(const char *name, size_t namelen, const char *value, size_t valuelen, int opts) @@ -81,7 +76,7 @@ entry_free(dynhds->hds[i]); } } - Curl_safefree(dynhds->hds); + curlx_safefree(dynhds->hds); dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; } @@ -163,7 +158,7 @@ if(dynhds->hds) { memcpy(nhds, dynhds->hds, dynhds->hds_len * sizeof(struct dynhds_entry *)); - Curl_safefree(dynhds->hds); + curlx_safefree(dynhds->hds); } dynhds->hds = nhds; dynhds->hds_allc = nallc; @@ -225,21 +220,36 @@ } #ifdef UNITTESTS -/* used by unit2602.c */ +/* @unittest 2602 */ -bool Curl_dynhds_contains(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Return TRUE iff one or more headers with the given name exist. + */ +UNITTEST bool dynhds_contains(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST bool dynhds_contains(struct dynhds *dynhds, + const char *name, size_t namelen) { return !!Curl_dynhds_get(dynhds, name, namelen); } -bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name) +/* @unittest 2602 */ +UNITTEST bool dynhds_ccontains(struct dynhds *dynhds, const char *name); +UNITTEST bool dynhds_ccontains(struct dynhds *dynhds, const char *name) { - return Curl_dynhds_contains(dynhds, name, strlen(name)); + return dynhds_contains(dynhds, name, strlen(name)); } -size_t Curl_dynhds_count_name(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Return how often the given name appears in `dynhds`. + * Names are case-insensitive. + * + * @unittest 2602 + */ +UNITTEST size_t dynhds_count_name(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST size_t dynhds_count_name(struct dynhds *dynhds, + const char *name, size_t namelen) { size_t n = 0; if(dynhds->hds_len) { @@ -253,21 +263,30 @@ return n; } -size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name) +/** + * Return how often the given null-terminated name appears in `dynhds`. + * Names are case-insensitive. + * + * @unittest 2602 + */ +UNITTEST size_t dynhds_ccount_name(struct dynhds *dynhds, + const char *name); +UNITTEST size_t dynhds_ccount_name(struct dynhds *dynhds, + const char *name) { - return Curl_dynhds_count_name(dynhds, name, strlen(name)); + return dynhds_count_name(dynhds, name, strlen(name)); } -CURLcode Curl_dynhds_set(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen) -{ - Curl_dynhds_remove(dynhds, name, namelen); - return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); -} - -size_t Curl_dynhds_remove(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Remove all entries with the given name. + * Returns number of entries removed. + * + * @unittest 2602 + */ +UNITTEST size_t dynhds_remove(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST size_t dynhds_remove(struct dynhds *dynhds, + const char *name, size_t namelen) { size_t n = 0; if(dynhds->hds_len) { @@ -292,12 +311,32 @@ return n; } -size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name) +/** + * Set the give header name and value, replacing any entries with + * the same name. The header is added at the end of all (remaining) + * entries. + * + * @unittest 2602 + */ +UNITTEST CURLcode dynhds_set(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen); +UNITTEST CURLcode dynhds_set(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen) { - return Curl_dynhds_remove(dynhds, name, strlen(name)); + dynhds_remove(dynhds, name, namelen); + return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); } -#endif +/* @unittest 2602 */ +UNITTEST size_t dynhds_cremove(struct dynhds *dynhds, const char *name); +UNITTEST size_t dynhds_cremove(struct dynhds *dynhds, const char *name) +{ + return dynhds_remove(dynhds, name, strlen(name)); +} + +#endif /* UNITTESTS */ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) {
diff --git a/Utilities/cmcurl/lib/dynhds.h b/Utilities/cmcurl/lib/dynhds.h index 170721c..d5337eb 100644 --- a/Utilities/cmcurl/lib/dynhds.h +++ b/Utilities/cmcurl/lib/dynhds.h
@@ -50,7 +50,7 @@ int opts; }; -#define DYNHDS_OPT_NONE (0) +#define DYNHDS_OPT_NONE 0 #define DYNHDS_OPT_LOWERCASE (1 << 0) /** @@ -94,47 +94,6 @@ const char *name, size_t namelen); struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name); -#ifdef UNITTESTS -/* used by unit2602.c */ - -/** - * Return TRUE iff one or more headers with the given name exist. - */ -bool Curl_dynhds_contains(struct dynhds *dynhds, - const char *name, size_t namelen); -bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name); - -/** - * Return how often the given name appears in `dynhds`. - * Names are case-insensitive. - */ -size_t Curl_dynhds_count_name(struct dynhds *dynhds, - const char *name, size_t namelen); - -/** - * Return how often the given null-terminated name appears in `dynhds`. - * Names are case-insensitive. - */ -size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name); - -/** - * Remove all entries with the given name. - * Returns number of entries removed. - */ -size_t Curl_dynhds_remove(struct dynhds *dynhds, - const char *name, size_t namelen); -size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name); - -/** - * Set the give header name and value, replacing any entries with - * the same name. The header is added at the end of all (remaining) - * entries. - */ -CURLcode Curl_dynhds_set(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen); -#endif - CURLcode Curl_dynhds_cset(struct dynhds *dynhds, const char *name, const char *value); @@ -153,29 +112,26 @@ const char *name, const char *value); /** - * Add a single header from an HTTP/1.1 formatted line at the end. Line - * may contain a delimiting CRLF or just LF. Any characters after - * that will be ignored. + * Add a single header from an HTTP/1.1 formatted line at the end. Line may + * contain a delimiting CRLF or LF. Any characters after that will be ignored. */ CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line); /** - * Add a single header from an HTTP/1.1 formatted line at the end. Line - * may contain a delimiting CRLF or just LF. Any characters after - * that will be ignored. + * Add a single header from an HTTP/1.1 formatted line at the end. Line may + * contain a delimiting CRLF or LF. Any characters after that will be ignored. */ CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, const char *line, size_t line_len); /** * Add the headers to the given `dynbuf` in HTTP/1.1 format with - * cr+lf line endings. Will NOT output a last empty line. + * CR+LF line endings. Does NOT output a last empty line. */ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf); #ifdef USE_NGHTTP2 -#include <stdint.h> #include <nghttp2/nghttp2.h> nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount);
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 398b20e..a472d6d 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c
@@ -51,7 +51,7 @@ #include "url.h" #include "getinfo.h" #include "hostip.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "easyif.h" #include "multiif.h" #include "multi_ev.h" @@ -94,21 +94,6 @@ #endif -/* - * strdup (and other memory functions) is redefined in complicated - * ways, but at this point it must be defined as the system-supplied strdup - * so the callback pointer is initialized correctly. - */ -#ifdef HAVE_STRDUP -#ifdef _WIN32 -#define system_strdup _strdup -#else -#define system_strdup strdup -#endif -#else -#define system_strdup Curl_strdup -#endif - #if defined(_MSC_VER) && defined(_DLL) # pragma warning(push) # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ @@ -121,7 +106,7 @@ curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; -curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; +curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)CURLX_STRDUP_LOW; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; #if defined(_MSC_VER) && defined(_DLL) @@ -146,7 +131,7 @@ Curl_cmalloc = (curl_malloc_callback)malloc; Curl_cfree = (curl_free_callback)free; Curl_crealloc = (curl_realloc_callback)realloc; - Curl_cstrdup = (curl_strdup_callback)system_strdup; + Curl_cstrdup = (curl_strdup_callback)CURLX_STRDUP_LOW; Curl_ccalloc = (curl_calloc_callback)calloc; } @@ -361,7 +346,7 @@ } global_init_unlock(); - /* We use curl_open() with undefined URL so far */ + /* We use Curl_open() with undefined URL so far */ result = Curl_open(&data); if(result) { DEBUGF(curl_mfprintf(stderr, "Error: Curl_open failed\n")); @@ -618,7 +603,9 @@ if(!pollrc) { /* timeout! */ ev->ms = 0; - /* curl_mfprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */ +#if 0 + curl_mfprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); +#endif mresult = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &ev->running_handles); } @@ -737,7 +724,7 @@ * easy handle, destroys the multi handle and returns the easy handle's return * code. * - * REALITY: it cannot just create and destroy the multi handle that easily. It + * REALITY: it cannot create and destroy the multi handle that easily. It * needs to keep it around since if this easy handle is used again by this * function, the same multi handle must be reused so that the same pools and * caches can be used. @@ -750,7 +737,7 @@ struct Curl_multi *multi; CURLMcode mresult; CURLcode result = CURLE_OK; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -792,8 +779,9 @@ if(multi->in_callback) return CURLE_RECURSIVE_API_CALL; - /* Copy the MAXCONNECTS option to the multi handle */ + /* Copy relevant easy options to the multi handle */ curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects); + curl_multi_setopt(multi, CURLMOPT_QUICK_EXIT, (long)data->set.quick_exit); data->multi_easy = NULL; /* pretend it does not exist */ mresult = curl_multi_add_handle(multi, data); @@ -807,8 +795,8 @@ /* assign this after curl_multi_add_handle() */ data->multi_easy = multi; - sigpipe_init(&pipe_st); - sigpipe_apply(data, &pipe_st); + sigpipe_init(&sigpipe_ctx); + sigpipe_apply(data, &sigpipe_ctx); /* run the transfer */ result = events ? easy_events(multi) : easy_transfer(multi); @@ -817,7 +805,7 @@ a failure here, room for future improvement! */ (void)curl_multi_remove_handle(multi, data); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); /* The multi handle is kept alive, owned by the easy handle */ return result; @@ -827,9 +815,9 @@ * curl_easy_perform() is the external interface that performs a blocking * transfer as previously setup. */ -CURLcode curl_easy_perform(CURL *data) +CURLcode curl_easy_perform(CURL *curl) { - return easy_perform(data, FALSE); + return easy_perform(curl, FALSE); } #ifdef DEBUGBUILD @@ -837,9 +825,9 @@ * curl_easy_perform_ev() is the external interface that performs a blocking * transfer using the event-based API internally. */ -CURLcode curl_easy_perform_ev(struct Curl_easy *data) +CURLcode curl_easy_perform_ev(struct Curl_easy *easy) { - return easy_perform(data, TRUE); + return easy_perform(easy, TRUE); } #endif @@ -847,14 +835,14 @@ * curl_easy_cleanup() is the external interface to cleaning/freeing the given * easy handle. */ -void curl_easy_cleanup(CURL *ptr) +void curl_easy_cleanup(CURL *curl) { - struct Curl_easy *data = ptr; + struct Curl_easy *data = curl; if(GOOD_EASY_HANDLE(data)) { - SIGPIPE_VARIABLE(pipe_st); - sigpipe_ignore(data, &pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; + sigpipe_ignore(data, &sigpipe_ctx); Curl_close(&data); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); } } @@ -863,9 +851,9 @@ * information from a performed transfer and similar. */ #undef curl_easy_getinfo -CURLcode curl_easy_getinfo(CURL *easy, CURLINFO info, ...) +CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; va_list arg; void *paramp; CURLcode result; @@ -891,8 +879,9 @@ /* Copy src->set into dst->set first, then deal with the strings afterwards */ dst->set = src->set; - Curl_mime_initpart(&dst->set.mimepost); - +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + dst->set.mimepostp = NULL; +#endif /* clear all dest string and blob pointers first, in case we error out mid-function */ memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); @@ -918,17 +907,28 @@ if(src->set.postfieldsize == -1) dst->set.str[i] = curlx_strdup(src->set.str[i]); else - /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ - dst->set.str[i] = Curl_memdup(src->set.str[i], - curlx_sotouz(src->set.postfieldsize)); + /* postfieldsize is curl_off_t, curlx_memdup() takes a size_t ... */ + dst->set.str[i] = curlx_memdup(src->set.str[i], + curlx_sotouz(src->set.postfieldsize)); if(!dst->set.str[i]) return CURLE_OUT_OF_MEMORY; /* point to the new copy */ dst->set.postfields = dst->set.str[i]; } - /* Duplicate mime data. */ - result = Curl_mime_duppart(dst, &dst->set.mimepost, &src->set.mimepost); +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + if(src->set.mimepostp) { + /* Duplicate mime data. Get a mimepost struct for the clone as well */ + dst->set.mimepostp = curlx_malloc(sizeof(*dst->set.mimepostp)); + if(!dst->set.mimepostp) + return CURLE_OUT_OF_MEMORY; + + Curl_mime_initpart(dst->set.mimepostp); + result = Curl_mime_duppart(dst, dst->set.mimepostp, src->set.mimepostp); + if(result) + return result; + } +#endif if(src->set.resolve) dst->state.resolve = dst->set.resolve; @@ -939,7 +939,7 @@ static void dupeasy_meta_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -950,9 +950,9 @@ * given input easy handle. The returned handle will be a new working handle * with all options set exactly as the input source handle. */ -CURL *curl_easy_duphandle(CURL *d) +CURL *curl_easy_duphandle(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; struct Curl_easy *outcurl = NULL; if(!GOOD_EASY_HANDLE(data)) @@ -1081,9 +1081,9 @@ * curl_easy_reset() is an external interface that allows an app to re- * initialize a session handle to the default values. */ -void curl_easy_reset(CURL *d) +void curl_easy_reset(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return; @@ -1092,10 +1092,6 @@ /* clear all meta data */ Curl_meta_reset(data); - /* clear any resolve data */ - Curl_async_shutdown(data); - Curl_resolv_unlink(data, &data->state.dns[0]); - Curl_resolv_unlink(data, &data->state.dns[1]); /* zero out UserDefined data: */ Curl_freeset(data); memset(&data->set, 0, sizeof(struct UserDefined)); @@ -1134,12 +1130,12 @@ * NOTE: This is one of few API functions that are allowed to be called from * within a callback. */ -CURLcode curl_easy_pause(CURL *d, int action) +CURLcode curl_easy_pause(CURL *curl, int action) { CURLcode result = CURLE_OK; bool recursive = FALSE; bool changed = FALSE; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; bool recv_paused, recv_paused_new; bool send_paused, send_paused_new; @@ -1158,12 +1154,14 @@ if((send_paused != send_paused_new) || (send_paused_new != Curl_creader_is_paused(data))) { changed = TRUE; - result = Curl_1st_err(result, Curl_xfer_pause_send(data, send_paused_new)); + result = Curl_1st_fatal( + result, Curl_xfer_pause_send(data, send_paused_new)); } if(recv_paused != recv_paused_new) { changed = TRUE; - result = Curl_1st_err(result, Curl_xfer_pause_recv(data, recv_paused_new)); + result = Curl_1st_fatal( + result, Curl_xfer_pause_recv(data, recv_paused_new)); } /* If not completely pausing both directions now, run again in any case. */ @@ -1222,11 +1220,11 @@ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. * Returns CURLE_OK on success, error code on error. */ -CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n) +CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) { CURLcode result; struct connectdata *c; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1275,7 +1273,7 @@ { CURLcode result; struct connectdata *c = NULL; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; *n = 0; result = easy_connection(data, &c); @@ -1287,9 +1285,9 @@ needs to be reattached */ Curl_attach_connection(data, c); - sigpipe_ignore(data, &pipe_st); + sigpipe_ignore(data, &sigpipe_ctx); result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); if(result && result != CURLE_AGAIN) return CURLE_SEND_ERROR; @@ -1300,11 +1298,12 @@ * Sends data over the connected socket. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n) +CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, + size_t *n) { size_t written = 0; CURLcode result; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; if(Curl_is_in_callback(data)) @@ -1318,9 +1317,9 @@ /* * Performs connection upkeep for the given session handle. */ -CURLcode curl_easy_upkeep(CURL *d) +CURLcode curl_easy_upkeep(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; /* Verify that we got an easy handle we can work with. */ if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1332,18 +1331,20 @@ return Curl_cpool_upkeep(data); } -CURLcode curl_easy_ssls_import(CURL *d, const char *session_key, +CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, const unsigned char *shmac, size_t shmac_len, const unsigned char *sdata, size_t sdata_len) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data)) + return CURLE_RECURSIVE_API_CALL; return Curl_ssl_session_import(data, session_key, shmac, shmac_len, sdata, sdata_len); #else - (void)d; + (void)curl; (void)session_key; (void)shmac; (void)shmac_len; @@ -1353,17 +1354,19 @@ #endif } -CURLcode curl_easy_ssls_export(CURL *d, +CURLcode curl_easy_ssls_export(CURL *curl, curl_ssls_export_cb *export_fn, void *userptr) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data)) + return CURLE_RECURSIVE_API_CALL; return Curl_ssl_session_export(data, export_fn, userptr); #else - (void)d; + (void)curl; (void)export_fn; (void)userptr; return CURLE_NOT_BUILT_IN;
diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h index 10c84c6..b8f916f 100644 --- a/Utilities/cmcurl/lib/easy_lock.h +++ b/Utilities/cmcurl/lib/easy_lock.h
@@ -27,7 +27,7 @@ #define GLOBAL_INIT_IS_THREADSAFE -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 +#ifdef _WIN32 #define curl_simple_lock SRWLOCK #define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT @@ -49,8 +49,8 @@ __has_builtin() function, so override it. */ /* if GCC on i386/x86_64 or if the built-in is present */ -#if (defined(__GNUC__) && !defined(__clang__)) && \ - (defined(__i386__) || defined(__x86_64__)) +#if (defined(__GNUC__) && !defined(__clang__)) && \ + (defined(__i386__) || defined(__x86_64__)) #define HAVE_BUILTIN_IA32_PAUSE #elif defined(__has_builtin) /* Keep this PP check separate from others */ #if __has_builtin(__builtin_ia32_pause) @@ -72,8 +72,6 @@ __builtin_ia32_pause(); #elif defined(__aarch64__) __asm__ volatile("yield" ::: "memory"); -#elif defined(_WIN32) - Sleep(1); #elif defined(HAVE_SCHED_YIELD) sched_yield(); #endif @@ -86,9 +84,7 @@ atomic_store_explicit(lock, false, memory_order_release); } -#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - -#include <pthread.h> +#elif defined(HAVE_THREADS_POSIX) #define curl_simple_lock pthread_mutex_t #define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER
diff --git a/Utilities/cmcurl/lib/easygetopt.c b/Utilities/cmcurl/lib/easygetopt.c index 2685ada..533d7e3 100644 --- a/Utilities/cmcurl/lib/easygetopt.c +++ b/Utilities/cmcurl/lib/easygetopt.c
@@ -62,8 +62,8 @@ } /* Iterates over available options */ -const struct curl_easyoption * -curl_easy_option_next(const struct curl_easyoption *prev) +const struct curl_easyoption *curl_easy_option_next( + const struct curl_easyoption *prev) { if(prev && prev->name) { prev++; @@ -88,8 +88,8 @@ return NULL; } -const struct curl_easyoption * -curl_easy_option_next(const struct curl_easyoption *prev) +const struct curl_easyoption *curl_easy_option_next( + const struct curl_easyoption *prev) { (void)prev; return NULL;
diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c index afdf18c..4aff583 100644 --- a/Utilities/cmcurl/lib/escape.c +++ b/Utilities/cmcurl/lib/escape.c
@@ -33,9 +33,9 @@ #include "curl_printf.h" /* for ABI-compatibility with previous versions */ -char *curl_escape(const char *string, int inlength) +char *curl_escape(const char *string, int length) { - return curl_easy_escape(NULL, string, inlength); + return curl_easy_escape(NULL, string, length); } /* for ABI-compatibility with previous versions */ @@ -47,25 +47,25 @@ /* Escapes for URL the given unescaped string of given length. * 'data' is ignored since 7.82.0. */ -char *curl_easy_escape(CURL *data, const char *string, int inlength) +char *curl_easy_escape(CURL *curl, const char *string, int length) { - size_t length; + size_t len; struct dynbuf d; - (void)data; + (void)curl; - if(!string || (inlength < 0)) + if(!string || (length < 0)) return NULL; - length = (inlength ? (size_t)inlength : strlen(string)); - if(!length) + len = (length ? (size_t)length : strlen(string)); + if(!len) return curlx_strdup(""); - if(length > SIZE_MAX / 16) + if(len > SIZE_MAX / 16) return NULL; - curlx_dyn_init(&d, length * 3 + 1); + curlx_dyn_init(&d, (len * 3) + 1); - while(length--) { + while(len--) { /* treat the characters unsigned */ unsigned char in = (unsigned char)*string++; @@ -138,7 +138,7 @@ if(((ctrl == REJECT_CTRL) && (in < 0x20)) || ((ctrl == REJECT_ZERO) && (in == 0))) { - Curl_safefree(*ostring); + curlx_safefree(*ostring); return CURLE_URL_MALFORMAT; } @@ -160,24 +160,25 @@ * If olen == NULL, no output length is stored. * 'data' is ignored since 7.82.0. */ -char *curl_easy_unescape(CURL *data, const char *string, int length, int *olen) +char *curl_easy_unescape(CURL *curl, const char *string, int inlength, + int *outlength) { char *str = NULL; - (void)data; - if(string && (length >= 0)) { - size_t inputlen = (size_t)length; + (void)curl; + if(string && (inlength >= 0)) { + size_t inputlen = (size_t)inlength; size_t outputlen; CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, REJECT_NADA); if(res) return NULL; - if(olen) { + if(outlength) { if(outputlen <= (size_t)INT_MAX) - *olen = curlx_uztosi(outputlen); + *outlength = curlx_uztosi(outputlen); else /* too large to return in an int, fail! */ - Curl_safefree(str); + curlx_safefree(str); } } return str;
diff --git a/Utilities/cmcurl/lib/escape.h b/Utilities/cmcurl/lib/escape.h index 2ea06c4..9f9a89e 100644 --- a/Utilities/cmcurl/lib/escape.h +++ b/Utilities/cmcurl/lib/escape.h
@@ -24,7 +24,7 @@ * ***************************************************************************/ /* Escape and unescape URL encoding in strings. The functions return a new - * allocated string or NULL if an error occurred. */ + * allocated string or NULL if an error occurred. */ enum urlreject { REJECT_NADA = 2,
diff --git a/Utilities/cmcurl/lib/fake_addrinfo.c b/Utilities/cmcurl/lib/fake_addrinfo.c index bf5a448..5a89202 100644 --- a/Utilities/cmcurl/lib/fake_addrinfo.c +++ b/Utilities/cmcurl/lib/fake_addrinfo.c
@@ -41,16 +41,16 @@ } struct context { - struct ares_addrinfo *result; + struct ares_addrinfo *addr; }; static void async_addrinfo_cb(void *userp, int status, int timeouts, - struct ares_addrinfo *result) + struct ares_addrinfo *addr) { struct context *ctx = (struct context *)userp; (void)timeouts; if(ARES_SUCCESS == status) { - ctx->result = result; + ctx->addr = addr; } } @@ -67,8 +67,8 @@ for(ai = aihead->nodes; ai != NULL; ai = ai->ai_next) { size_t ss_size; size_t namelen = name ? strlen(name) + 1 : 0; - /* ignore elements with unsupported address family, */ - /* settle family-specific sockaddr structure size. */ + /* ignore elements with unsupported address family, + settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); else if(ai->ai_family == AF_INET6) @@ -90,8 +90,8 @@ return NULL; } - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ + /* copy each structure member individually, member ordering, + size, or padding might be different for each platform. */ ca->ai_flags = ai->ai_flags; ca->ai_family = ai->ai_family; @@ -184,11 +184,11 @@ /* Wait until no more requests are left to be processed */ ares_queue_wait_empty(channel, -1); - if(ctx.result) { + if(ctx.addr) { /* convert the c-ares version */ - *res = mk_getaddrinfo(ctx.result); + *res = mk_getaddrinfo(ctx.addr); /* free the old */ - ares_freeaddrinfo(ctx.result); + ares_freeaddrinfo(ctx.addr); } else rc = EAI_NONAME; /* got nothing */
diff --git a/Utilities/cmcurl/lib/fake_addrinfo.h b/Utilities/cmcurl/lib/fake_addrinfo.h index db701be..07d5b6d 100644 --- a/Utilities/cmcurl/lib/fake_addrinfo.h +++ b/Utilities/cmcurl/lib/fake_addrinfo.h
@@ -29,8 +29,8 @@ #include <ares.h> #endif -#if defined(CURLDEBUG) && defined(USE_ARES) && defined(HAVE_GETADDRINFO) && \ - (ARES_VERSION >= 0x011a00) /* >= 1.26.0 */ +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \ + defined(USE_ARES) && (ARES_VERSION >= 0x011a00) /* >= 1.26.0 */ #define USE_FAKE_GETADDRINFO 1 #endif
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index 982f254..fff8fee 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c
@@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "file.h" #ifndef CURL_DISABLE_FILE @@ -53,12 +55,10 @@ #include <dirent.h> #endif -#include "urldata.h" #include "progress.h" #include "sendf.h" #include "curl_trc.h" #include "escape.h" -#include "file.h" #include "multiif.h" #include "transfer.h" #include "url.h" @@ -82,55 +82,12 @@ int fd; /* open file descriptor to read from! */ }; -/* - * Forward declarations. - */ - -static CURLcode file_do(struct Curl_easy *data, bool *done); -static CURLcode file_done(struct Curl_easy *data, - CURLcode status, bool premature); -static CURLcode file_connect(struct Curl_easy *data, bool *done); -static CURLcode file_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); -static CURLcode file_setup_connection(struct Curl_easy *data, - struct connectdata *conn); - -/* - * FILE scheme handler. - */ - -const struct Curl_handler Curl_handler_file = { - "file", /* scheme */ - file_setup_connection, /* setup_connection */ - file_do, /* do_it */ - file_done, /* done */ - ZERO_NULL, /* do_more */ - file_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - file_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - 0, /* defport */ - CURLPROTO_FILE, /* protocol */ - CURLPROTO_FILE, /* family */ - PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */ -}; - static void file_cleanup(struct FILEPROTO *file) { - Curl_safefree(file->freepath); + curlx_safefree(file->freepath); file->path = NULL; if(file->fd != -1) { - close(file->fd); + curlx_close(file->fd); file->fd = -1; } } @@ -151,6 +108,8 @@ (void)conn; /* allocate the FILE specific struct */ filep = curlx_calloc(1, sizeof(*filep)); + if(filep) + filep->fd = -1; if(!filep || Curl_meta_set(data, CURL_META_FILE_EASY, filep, file_easy_dtor)) return CURLE_OUT_OF_MEMORY; @@ -158,6 +117,19 @@ return CURLE_OK; } +static CURLcode file_done(struct Curl_easy *data, + CURLcode status, bool premature) +{ + struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); + (void)status; + (void)premature; + + if(file) + file_cleanup(file); + + return CURLE_OK; +} + /* * file_connect() gets called from Curl_protocol_connect() to allow us to * do protocol-specific actions at connect-time. We emulate a @@ -221,7 +193,7 @@ if(actual_path[i] == '/') actual_path[i] = '\\'; else if(!actual_path[i]) { /* binary zero */ - Curl_safefree(real_path); + curlx_safefree(real_path); return CURLE_URL_MALFORMAT; } @@ -230,11 +202,11 @@ #else if(memchr(real_path, 0, real_path_len)) { /* binary zeroes indicate foul play */ - Curl_safefree(real_path); + curlx_safefree(real_path); return CURLE_URL_MALFORMAT; } - #ifdef AMIGA_FILESYSTEM +#ifdef AMIGA_FILESYSTEM /* * A leading slash in an AmigaDOS path denotes the parent * directory, and hence we block this as it is relative. @@ -257,10 +229,10 @@ fd = curlx_open(real_path, O_RDONLY); } } - #else +#else fd = curlx_open(real_path, O_RDONLY); file->path = real_path; - #endif +#endif #endif curlx_free(file->freepath); file->freepath = real_path; /* free this when done */ @@ -276,19 +248,6 @@ return CURLE_OK; } -static CURLcode file_done(struct Curl_easy *data, - CURLcode status, bool premature) -{ - struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); - (void)status; - (void)premature; - - if(file) - file_cleanup(file); - - return CURLE_OK; -} - static CURLcode file_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead_connection) @@ -313,7 +272,7 @@ CURLcode result = CURLE_OK; char *xfer_ulbuf; size_t xfer_ulblen; - struct_stat file_stat; + curlx_struct_stat file_stat; const char *sendbuf; bool eos = FALSE; @@ -354,8 +313,8 @@ /* treat the negative resume offset value as the case of "-" */ if(data->state.resume_from < 0) { - if(fstat(fd, &file_stat)) { - close(fd); + if(curlx_fstat(fd, &file_stat)) { + curlx_close(fd); failf(data, "cannot get the size of %s", file->path); return CURLE_WRITE_ERROR; } @@ -410,7 +369,7 @@ result = Curl_pgrsUpdate(data); out: - close(fd); + curlx_close(fd); Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf); return result; @@ -433,9 +392,7 @@ */ struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); CURLcode result = CURLE_OK; - struct_stat statbuf; /* struct_stat instead of struct stat just to allow the - Windows version to have a different struct without - having to redefine the simple word 'stat' */ + curlx_struct_stat statbuf; curl_off_t expected_size = -1; bool size_known; bool fstated = FALSE; @@ -454,7 +411,7 @@ fd = file->fd; /* VMS: This only works reliable for STREAMLF files */ - if(fstat(fd, &statbuf) != -1) { + if(curlx_fstat(fd, &statbuf) != -1) { if(!S_ISDIR(statbuf.st_mode)) expected_size = statbuf.st_size; /* and store the modification time */ @@ -560,13 +517,8 @@ if(data->state.resume_from) { if(!S_ISDIR(statbuf.st_mode)) { -#ifdef __AMIGA__ if(data->state.resume_from != - lseek(fd, (off_t)data->state.resume_from, SEEK_SET)) -#else - if(data->state.resume_from != - lseek(fd, data->state.resume_from, SEEK_SET)) -#endif + curl_lseek(fd, data->state.resume_from, SEEK_SET)) return CURLE_BAD_DOWNLOAD_RESUME; } else { @@ -648,4 +600,24 @@ return result; } +const struct Curl_protocol Curl_protocol_file = { + file_setup_connection, /* setup_connection */ + file_do, /* do_it */ + file_done, /* done */ + ZERO_NULL, /* do_more */ + file_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + file_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif
diff --git a/Utilities/cmcurl/lib/file.h b/Utilities/cmcurl/lib/file.h index cb3552d..0aa411b 100644 --- a/Utilities/cmcurl/lib/file.h +++ b/Utilities/cmcurl/lib/file.h
@@ -24,7 +24,7 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_FILE -extern const struct Curl_handler Curl_handler_file; +extern const struct Curl_protocol Curl_protocol_file; #endif #endif /* HEADER_CURL_FILE_H */
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c index 8c4dc48..3619c15 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c
@@ -31,7 +31,7 @@ #include "urldata.h" /* for struct Curl_easy */ #include "mime.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "bufref.h" #include "curlx/fopen.h" @@ -168,10 +168,9 @@ * * Stores a formpost parameter and builds the appropriate linked list. * - * Has two principal functionalities: using files and byte arrays as - * post parts. Byte arrays are either copied or just the pointer is stored - * (as the user requests) while for files only the filename and not the - * content is stored. + * Has two principal functionalities: using files and byte arrays as post + * parts. Byte arrays are either copied or the pointer is stored (as the user + * requests) while for files only the filename and not the content is stored. * * While you may have only one byte array for each name, multiple filenames * are allowed (and because of this feature CURLFORM_END is needed after @@ -304,13 +303,13 @@ struct FormInfo *first_form, *curr, *form = NULL; CURLFORMcode retval = CURL_FORMADD_OK; CURLformoption option; - struct curl_forms *forms = NULL; + const struct curl_forms *forms = NULL; char *avalue = NULL; struct curl_httppost *newchain = NULL; struct curl_httppost *lastnode = NULL; #define form_ptr_arg(t) (forms ? (t)(void *)avalue : va_arg(params, t)) -#ifdef HAVE_STDINT_H +#ifdef HAVE_UINTPTR_T #define form_int_arg(t) (forms ? (t)(uintptr_t)avalue : va_arg(params, t)) #else #define form_int_arg(t) (forms ? (t)(void *)avalue : va_arg(params, t)) @@ -610,11 +609,11 @@ struct curl_httppost **last_post, ...) { va_list arg; - CURLFORMcode result; + CURLFORMcode form; va_start(arg, last_post); - result = FormAdd(httppost, last_post, arg); + form = FormAdd(httppost, last_post, arg); va_end(arg); - return result; + return form; } /* @@ -667,7 +666,7 @@ struct curl_httppost *next; if(!form) - /* no form to free, just get out of this */ + /* no form to free, get out of this */ return; do { @@ -692,16 +691,16 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) { char *zname; - CURLcode res; + CURLcode result; if(!name || !len) return curl_mime_name(part, name); - zname = Curl_memdup0(name, len); + zname = curlx_memdup0(name, len); if(!zname) return CURLE_OUT_OF_MEMORY; - res = curl_mime_name(part, zname); + result = curl_mime_name(part, zname); curlx_free(zname); - return res; + return result; } /* @@ -710,8 +709,8 @@ * mime part at '*finalform'. * * This function will not do a failf() for the potential memory failures but - * should for all other errors it spots. Just note that this function MAY get - * a NULL pointer in the 'data' argument. + * should for all other errors it spots. Note that this function MAY get a + * NULL pointer in the 'data' argument. */ CURLcode Curl_getformdata(CURL *data, @@ -840,8 +839,7 @@ return result; } -#else -/* if disabled */ +#else /* if disabled */ CURLFORMcode curl_formadd(struct curl_httppost **httppost, struct curl_httppost **last_post, ...) {
diff --git a/Utilities/cmcurl/lib/formdata.h b/Utilities/cmcurl/lib/formdata.h index ce592a8..33d7d0a 100644 --- a/Utilities/cmcurl/lib/formdata.h +++ b/Utilities/cmcurl/lib/formdata.h
@@ -47,7 +47,7 @@ }; CURLcode Curl_getformdata(CURL *data, - curl_mimepart *, + curl_mimepart *finalform, struct curl_httppost *post, curl_read_callback fread_func); #endif /* CURL_DISABLE_FORM_API */
diff --git a/Utilities/cmcurl/lib/ftp-int.h b/Utilities/cmcurl/lib/ftp-int.h new file mode 100644 index 0000000..68d26f3 --- /dev/null +++ b/Utilities/cmcurl/lib/ftp-int.h
@@ -0,0 +1,160 @@ +#ifndef HEADER_CURL_FTP_INT_H +#define HEADER_CURL_FTP_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "pingpong.h" + +#ifndef CURL_DISABLE_FTP + +/**************************************************************************** + * FTP unique setup + ***************************************************************************/ +enum { + FTP_STOP, /* do nothing state, stops the state machine */ + FTP_WAIT220, /* waiting for the initial 220 response immediately after + a connect */ + FTP_AUTH, + FTP_USER, + FTP_PASS, + FTP_ACCT, + FTP_PBSZ, + FTP_PROT, + FTP_CCC, + FTP_PWD, + FTP_SYST, + FTP_NAMEFMT, + FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ + FTP_RETR_PREQUOTE, + FTP_STOR_PREQUOTE, + FTP_LIST_PREQUOTE, + FTP_POSTQUOTE, + FTP_CWD, /* change directory */ + FTP_MKD, /* if the directory did not exist */ + FTP_MDTM, /* to figure out the datestamp */ + FTP_TYPE, /* to set type when doing a head-like request */ + FTP_LIST_TYPE, /* set type when about to do a directory list */ + FTP_RETR_LIST_TYPE, + FTP_RETR_TYPE, /* set type when about to RETR a file */ + FTP_STOR_TYPE, /* set type when about to STOR a file */ + FTP_SIZE, /* get the remote file's size for head-like request */ + FTP_RETR_SIZE, /* get the remote file's size for RETR */ + FTP_STOR_SIZE, /* get the size for STOR */ + FTP_REST, /* when used to check if the server supports it in head-like */ + FTP_RETR_REST, /* when asking for "resume" in for RETR */ + FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ + FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ + FTP_PASV, /* generic state for PASV and EPSV, check count1 */ + FTP_LIST, /* generic state for LIST, NLST or a custom list command */ + FTP_RETR, + FTP_STOR, /* generic state for STOR and APPE */ + FTP_QUIT, + FTP_LAST /* never used */ +}; +typedef unsigned char ftpstate; /* use the enum values */ + +struct ftp_parselist_data; /* defined later in ftplistparser.c */ + +struct ftp_wc { + struct ftp_parselist_data *parser; + + struct { + curl_write_callback write_function; + FILE *file_descriptor; + } backup; +}; + +/* This FTP struct is used in the Curl_easy. All FTP data that is + connection-oriented must be in FTP_conn to properly deal with the fact that + perhaps the Curl_easy is changed between the times the connection is + used. */ +struct FTP { + char *path; /* points to the urlpieces struct field */ + char *pathalloc; /* if non-NULL a pointer to an allocated path */ + + /* transfer a file/body or not, done as a typedefed enum to make debuggers + display the full symbol and not the numerical value */ + curl_pp_transfer transfer; + curl_off_t downloadsize; +}; + +/* one struct entry for each path component (of 'rawpath') */ +struct pathcomp { + int start; /* start column */ + int len; /* length in bytes */ +}; + +/* ftp_conn is used for struct connection-oriented data in the connectdata + struct */ +struct ftp_conn { + struct pingpong pp; + char *account; + char *alternative_to_user; + char *entrypath; /* the PWD reply when we logged on */ + const char *file; /* url-decoded filename (or path), points into rawpath */ + char *rawpath; /* URL decoded, allocated, version of the path */ + struct pathcomp *dirs; /* allocated array for path components */ + char *prevpath; /* url-decoded conn->path from the previous transfer */ + char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a + and others (A/I or zero) */ + char *server_os; /* The target server operating system. */ + curl_off_t known_filesize; /* file size is different from -1, if wildcard + LIST parsing was done and wc_statemach set + it */ + int count1; /* general purpose counter for the state machine */ + int count2; /* general purpose counter for the state machine */ + int count3; /* general purpose counter for the state machine */ + unsigned short dirdepth; /* number of entries used in the 'dirs' array, + < FTP_MAX_DIR_DEPTH */ + unsigned short cwdcount; /* number of CWD commands issued, + < FTP_MAX_DIR_DEPTH */ + unsigned char state; /* (ftpstate enum) always use ftp.c:state() to change + state! */ + unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or + IMAP or POP3 or others! (type: curl_usessl)*/ + unsigned char ccc; /* ccc level for this connection */ + BIT(ftp_trying_alternative); + BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) + file size and 226/250 status check. It should still + read the line, ignore the result. */ + BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If + the connection has timed out or been closed, this + should be FALSE when it gets to Curl_ftp_quit() */ + BIT(cwddone); /* if it has been determined that the proper CWD combo + already has been done */ + BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent + caching the current directory */ + BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ + BIT(shutdown); /* connection is being shutdown, e.g. QUIT */ +}; + +/* meta key for storing `struct FTP` as easy meta data */ +#define CURL_META_FTP_EASY "meta:proto:ftp:easy" +/* meta key for storing `struct ftp_conn` as connection meta data */ +#define CURL_META_FTP_CONN "meta:proto:ftp:conn" + +#endif /* CURL_DISABLE_FTP */ + +#endif /* HEADER_CURL_FTP_INT_H */
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 9c3beda..61a78c2 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c
@@ -22,6 +22,7 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_FTP @@ -39,8 +40,8 @@ #include <inet.h> #endif -#include "urldata.h" #include "sendf.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "if2ip.h" #include "hostip.h" @@ -48,7 +49,7 @@ #include "transfer.h" #include "escape.h" #include "ftp.h" -#include "fileinfo.h" +#include "ftp-int.h" #include "ftplistparser.h" #include "curl_range.h" #include "strcase.h" @@ -64,9 +65,10 @@ #include "multiif.h" #include "url.h" #include "http_proxy.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "curlx/strerr.h" #include "curlx/strparse.h" +#include "curl_ctype.h" #ifndef NI_MAXHOST #define NI_MAXHOST 1025 @@ -78,16 +80,13 @@ /* macro to check for a three-digit ftp status code at the start of the given string */ #define STATUSCODE(line) \ - (ISDIGIT(line[0]) && ISDIGIT(line[1]) && ISDIGIT(line[2])) + (ISDIGIT((line)[0]) && ISDIGIT((line)[1]) && ISDIGIT((line)[2])) /* macro to check for the last line in an FTP server response */ -#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3])) +#define LASTLINE(line) (STATUSCODE(line) && (' ' == (line)[3])) -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define ftp_pasv_verbose(a, b, c, d) Curl_nop_stmt -#define FTP_CSTATE(c) ((void)(c), "") -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - /* for tracing purposes */ +#ifdef CURLVERBOSE +/* for tracing purposes */ static const char * const ftp_state_names[] = { "STOP", "WAIT220", @@ -129,7 +128,7 @@ }; #define FTP_CSTATE(ftpc) ((ftpc) ? ftp_state_names[(ftpc)->state] : "???") -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ /* This is the ONLY way to change FTP state! */ static void ftp_state_low(struct Curl_easy *data, @@ -140,22 +139,16 @@ #endif ) { -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; + if(ftpc->state != newstate) { #ifdef DEBUGBUILD - (void)lineno; -#endif -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - if(ftpc->state != newstate) -#ifdef DEBUGBUILD + NOVERBOSE((void)lineno); CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_CSTATE(ftpc), ftp_state_names[newstate], lineno); #else CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_CSTATE(ftpc), ftp_state_names[newstate]); #endif -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ - + } ftpc->state = newstate; } @@ -166,23 +159,6 @@ #define ftp_state(x, y, z) ftp_state_low(x, y, z, __LINE__) #endif /* DEBUGBUILD */ -static CURLcode ftp_sendquote(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct curl_slist *quote); -static CURLcode ftp_quit(struct Curl_easy *data, struct ftp_conn *ftpc); -static CURLcode ftp_parse_url_path(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp); -static CURLcode ftp_regular_transfer(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool *done); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void ftp_pasv_verbose(struct Curl_easy *data, - struct Curl_addrinfo *ai, - char *newhost, /* ASCII version */ - int port); -#endif static CURLcode ftp_state_mdtm(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp); @@ -194,109 +170,185 @@ struct ftp_conn *ftpc, struct FTP *ftp, bool ascii, ftpstate newstate); -static int ftp_need_type(struct ftp_conn *ftpc, bool ascii); -static CURLcode ftp_do(struct Curl_easy *data, bool *done); -static CURLcode ftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode ftp_connect(struct Curl_easy *data, bool *done); -static CURLcode ftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection); -static CURLcode ftp_do_more(struct Curl_easy *data, int *completed); -static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode ftp_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode ftp_domore_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode ftp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode ftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode init_wc_data(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp); -static CURLcode wc_statemach(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp); -static void wc_data_dtor(void *ptr); -static CURLcode ftp_state_retr(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - curl_off_t filesize); -static CURLcode ftp_readresp(struct Curl_easy *data, - struct ftp_conn *ftpc, - int sockindex, - struct pingpong *pp, - int *ftpcode, - size_t *size); -static CURLcode ftp_dophase_done(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool connected); +static CURLcode getftpresponse(struct Curl_easy *data, size_t *nreadp, + int *ftpcodep); -/* - * FTP protocol handler. +static void freedirs(struct ftp_conn *ftpc) +{ + curlx_safefree(ftpc->dirs); + ftpc->dirdepth = 0; + curlx_safefree(ftpc->rawpath); + ftpc->file = NULL; +} + +static size_t numof_slashes(const char *str) +{ + const char *slashPos; + size_t num = 0; + do { + slashPos = strchr(str, '/'); + if(slashPos) { + ++num; + str = slashPos + 1; + } + } while(slashPos); + return num; +} + +#define FTP_MAX_DIR_DEPTH 1000 + +/*********************************************************************** + * + * ftp_parse_url_path() + * + * Parse the URL path into separate path components. + * */ +static CURLcode ftp_parse_url_path(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + const char *slashPos = NULL; + const char *fileName = NULL; + CURLcode result = CURLE_OK; + const char *rawPath = NULL; /* URL-decoded "raw" path */ + size_t pathLen = 0; -const struct Curl_handler Curl_handler_ftp = { - "ftp", /* scheme */ - ftp_setup_connection, /* setup_connection */ - ftp_do, /* do_it */ - ftp_done, /* done */ - ftp_do_more, /* do_more */ - ftp_connect, /* connect_it */ - ftp_multi_statemach, /* connecting */ - ftp_doing, /* doing */ - ftp_pollset, /* proto_pollset */ - ftp_pollset, /* doing_pollset */ - ftp_domore_pollset, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ftp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_FTP, /* defport */ - CURLPROTO_FTP, /* protocol */ - CURLPROTO_FTP, /* family */ - PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | - PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | - PROTOPT_WILDCARD | PROTOPT_SSL_REUSE | - PROTOPT_CONN_REUSE /* flags */ -}; + ftpc->ctl_valid = FALSE; + ftpc->cwdfail = FALSE; -#ifdef USE_SSL -/* - * FTPS protocol handler. + if(ftpc->rawpath) + freedirs(ftpc); + /* URL-decode ftp path before further evaluation */ + result = Curl_urldecode(ftp->path, 0, &ftpc->rawpath, &pathLen, REJECT_CTRL); + if(result) { + failf(data, "path contains control characters"); + return result; + } + rawPath = ftpc->rawpath; + + switch(data->set.ftp_filemethod) { + case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ + + if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) + fileName = rawPath; /* this is a full file path */ + /* + else: ftpc->file is not used anywhere other than for operations on + a file. In other words, never for directory operations, + so we can safely leave filename as NULL here and use it as a + argument in dir/file decisions. + */ + break; + + case FTPFILE_SINGLECWD: + slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* get path before last slash, except for / */ + size_t dirlen = slashPos - rawPath; + if(dirlen == 0) + dirlen = 1; + + ftpc->dirs = curlx_calloc(1, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) + return CURLE_OUT_OF_MEMORY; + + ftpc->dirs[0].start = 0; + ftpc->dirs[0].len = (int)dirlen; + ftpc->dirdepth = 1; /* we consider it to be a single directory */ + fileName = slashPos + 1; /* rest is filename */ + } + else + fileName = rawPath; /* filename only (or empty) */ + break; + + default: /* allow pretty much anything */ + case FTPFILE_MULTICWD: { + /* current position: begin of next path component */ + const char *curPos = rawPath; + + /* number of entries to allocate for the 'dirs' array */ + size_t dirAlloc = numof_slashes(rawPath); + + if(dirAlloc >= FTP_MAX_DIR_DEPTH) + /* suspiciously deep directory hierarchy */ + return CURLE_URL_MALFORMAT; + + if(dirAlloc) { + ftpc->dirs = curlx_calloc(dirAlloc, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) + return CURLE_OUT_OF_MEMORY; + + /* parse the URL path into separate path components */ + while(dirAlloc--) { + const char *spos = strchr(curPos, '/'); + size_t clen = spos - curPos; + + /* path starts with a slash: add that as a directory */ + if(!clen && (ftpc->dirdepth == 0)) + ++clen; + + /* we skip empty path components, like "x//y" since the FTP command + CWD requires a parameter and a non-existent parameter a) does not + work on many servers and b) has no effect on the others. */ + if(clen) { + ftpc->dirs[ftpc->dirdepth].start = (int)(curPos - rawPath); + ftpc->dirs[ftpc->dirdepth].len = (int)clen; + ftpc->dirdepth++; + } + curPos = spos + 1; + } + } + fileName = curPos; /* the rest is the filename (or empty) */ + } + break; + } /* switch */ + + if(fileName && *fileName) + ftpc->file = fileName; + else + ftpc->file = NULL; /* instead of point to a zero byte, + we make it a NULL pointer */ + + if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { + /* We need a filename when uploading. Return error! */ + failf(data, "Uploading to a URL without a filename"); + return CURLE_URL_MALFORMAT; + } + + ftpc->cwddone = FALSE; /* default to not done */ + + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + ftpc->cwddone = TRUE; /* skip CWD for absolute paths */ + else { /* newly created FTP connections are already in entry path */ + const char *oldPath = data->conn->bits.reuse ? ftpc->prevpath : ""; + if(oldPath) { + size_t n = pathLen; + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + n = 0; /* CWD to entry for relative paths */ + else + n -= ftpc->file ? strlen(ftpc->file) : 0; + + if((strlen(oldPath) == n) && rawPath && !strncmp(rawPath, oldPath, n)) { + infof(data, "Request has same path as previous transfer"); + ftpc->cwddone = TRUE; + } + } + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_need_type() + * + * Returns TRUE if we in the current situation should send TYPE */ - -const struct Curl_handler Curl_handler_ftps = { - "ftps", /* scheme */ - ftp_setup_connection, /* setup_connection */ - ftp_do, /* do_it */ - ftp_done, /* done */ - ftp_do_more, /* do_more */ - ftp_connect, /* connect_it */ - ftp_multi_statemach, /* connecting */ - ftp_doing, /* doing */ - ftp_pollset, /* proto_pollset */ - ftp_pollset, /* doing_pollset */ - ftp_domore_pollset, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ftp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_FTPS, /* defport */ - CURLPROTO_FTPS, /* protocol */ - CURLPROTO_FTP, /* family */ - PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | - PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD | - PROTOPT_CONN_REUSE /* flags */ -}; -#endif +static int ftp_need_type(struct ftp_conn *ftpc, + bool ascii_wanted) +{ + return ftpc->transfertype != (ascii_wanted ? 'A' : 'I'); +} static void close_secondarysocket(struct Curl_easy *data, struct ftp_conn *ftpc) @@ -307,19 +359,11 @@ Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); } -static void freedirs(struct ftp_conn *ftpc) -{ - Curl_safefree(ftpc->dirs); - ftpc->dirdepth = 0; - Curl_safefree(ftpc->rawpath); - ftpc->file = NULL; -} - #ifdef CURL_PREFER_LF_LINEENDS /* * Lineend Conversions * On ASCII transfers, e.g. directory listings, we might get lines - * ending in '\r\n' and we prefer just '\n'. + * ending in '\r\n' and we prefer '\n'. * We might also get a lonely '\r' which we convert into a '\n'. */ struct ftp_cw_lc_ctx { @@ -357,8 +401,8 @@ if(result) return result; } - /* either we just wrote the newline or it is part of the next - * chunk of bytes we write. */ + /* either we wrote the newline or it is part of the next chunk of bytes + * we write. */ ctx->newline_pending = FALSE; } @@ -408,9 +452,6 @@ #endif /* CURL_PREFER_LF_LINEENDS */ -static CURLcode getftpresponse(struct Curl_easy *data, size_t *nread, - int *ftpcode); - /*********************************************************************** * * ftp_check_ctrl_on_data_wait() @@ -516,9 +557,6 @@ size prior to the actual upload. */ Curl_pgrsSetUploadSize(data, data->state.infilesize); - /* set the SO_SNDBUF for the secondary socket for those who need it */ - Curl_sndbuf_init(data->conn->sock[SECONDARYSOCKET]); - /* FTP upload, shutdown DATA, ignore shutdown errors, as we rely * on the server response on the CONTROL connection. */ Curl_xfer_setup_send(data, SECONDARYSOCKET); @@ -593,15 +631,15 @@ */ static CURLcode getftpresponse(struct Curl_easy *data, size_t *nreadp, /* return number of bytes - read */ + read */ int *ftpcodep) /* return the ftp-code */ { /* - * We cannot read just one byte per read() and then go back to select() as - * the OpenSSL read() does not grok that properly. + * We cannot read one byte per read() and then go back to select() as the + * OpenSSL read() does not grok that properly. * * Alas, read as much as possible, split up into lines, use the ending - * line in a response or continue reading. */ + * line in a response or continue reading. */ struct connectdata *conn = data->conn; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; @@ -621,7 +659,7 @@ while(!*ftpcodep && !result) { /* check and reset timeout value every lap */ - timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE); + timediff_t timeout = Curl_pp_state_timeleft_ms(data, pp); timediff_t interval_ms; if(timeout <= 0) { @@ -640,10 +678,10 @@ * * A caution here is that the ftp_readresp() function has a cache that may * contain pieces of a response from the previous invoke and we need to - * make sure we do not just wait for input while there is unhandled data in - * that cache. But also, if the cache is there, we call ftp_readresp() and - * the cache was not good enough to continue we must not just busy-loop - * around this function. + * make sure we do not wait for input while there is unhandled data in + * that cache. Also, if the cache is there, we call ftp_readresp() and + * the cache was not good enough to continue we must not busy-loop around + * this function. * */ @@ -666,7 +704,7 @@ } else if(ev == 0) { result = Curl_pgrsUpdate(data); - continue; /* just continue in our loop for the timeout duration */ + continue; /* continue in our loop for the timeout duration */ } } @@ -694,7 +732,7 @@ } /* while there is buffer left and loop is requested */ pp->pending_resp = FALSE; - CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zd, ftpcode=%d", + CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zu, ftpcode=%d", result, *nreadp, *ftpcodep); return result; @@ -746,8 +784,8 @@ return CURLE_OK; /* When in DO_MORE state, we could be either waiting for us to connect to a - * remote site, or we could wait for that site to connect to us. Or just - * handle ordinary commands. + * remote site, or we could wait for that site to connect to us. Or handle + * ordinary commands. */ CURL_TRC_FTP(data, "[%s] ftp_domore_pollset()", FTP_CSTATE(ftpc)); @@ -837,199 +875,226 @@ DONE } ftpport; -static CURLcode ftp_state_use_port(struct Curl_easy *data, - struct ftp_conn *ftpc, - ftpport fcmd) /* start with this */ +/* + * Parse the CURLOPT_FTPPORT string + * "(ipv4|ipv6|domain|interface)?(:port(-range)?)?" + * and extract addr/addrlen and port_min/port_max. + */ +static CURLcode ftp_port_parse_string(struct Curl_easy *data, + struct connectdata *conn, + const char *string_ftpport, + struct Curl_sockaddr_storage *ss, + unsigned short *port_minp, + unsigned short *port_maxp, + const char **hostp, + char *hbuf, size_t hbuflen) { - CURLcode result = CURLE_FTP_PORT_FAILED; - struct connectdata *conn = data->conn; - curl_socket_t portsock = CURL_SOCKET_BAD; - char myhost[MAX_IPADR_LEN + 1] = ""; - - struct Curl_sockaddr_storage ss; - struct Curl_addrinfo *res, *ai; - curl_socklen_t sslen; - char hbuf[NI_MAXHOST]; - struct sockaddr *sa = (struct sockaddr *)&ss; - struct sockaddr_in * const sa4 = (void *)sa; -#ifdef USE_IPV6 - struct sockaddr_in6 * const sa6 = (void *)sa; -#endif - static const char mode[][5] = { "EPRT", "PORT" }; - int error; - char *host = NULL; - char *string_ftpport = data->set.str[STRING_FTPPORT]; - struct Curl_dns_entry *dns_entry = NULL; + const char *ip_end = NULL; + const char *addr = NULL; + size_t addrlen = 0; unsigned short port_min = 0; unsigned short port_max = 0; - unsigned short port; - bool possibly_non_local = TRUE; - char buffer[STRERROR_LEN]; - char *addr = NULL; - size_t addrlen = 0; char ipstr[50]; +#ifndef USE_IPV6 + (void)conn; + (void)ss; +#endif - /* Step 1, figure out what is requested, - * accepted format : - * (ipv4|ipv6|domain|interface)?(:port(-range)?)? - */ + /* default to nothing */ + *hostp = NULL; + *port_minp = *port_maxp = 0; - if(data->set.str[STRING_FTPPORT] && - (strlen(data->set.str[STRING_FTPPORT]) > 1)) { - char *ip_end = NULL; + if(!string_ftpport || (strlen(string_ftpport) <= 1)) + goto done; #ifdef USE_IPV6 - if(*string_ftpport == '[') { - /* [ipv6]:port(-range) */ - char *ip_start = string_ftpport + 1; - ip_end = strchr(ip_start, ']'); - if(ip_end) { - addrlen = ip_end - ip_start; - addr = ip_start; - } - } - else -#endif - if(*string_ftpport == ':') { - /* :port */ - ip_end = string_ftpport; - } - else { - ip_end = strchr(string_ftpport, ':'); - addr = string_ftpport; - if(ip_end) { - /* either ipv6 or (ipv4|domain|interface):port(-range) */ - addrlen = ip_end - string_ftpport; -#ifdef USE_IPV6 - if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { - /* ipv6 */ - port_min = port_max = 0; - ip_end = NULL; /* this got no port ! */ - } -#endif - } - else - /* ipv4|interface */ - addrlen = strlen(string_ftpport); - } - - /* parse the port */ + if(*string_ftpport == '[') { + /* [ipv6]:port(-range) */ + const char *ip_start = string_ftpport + 1; + ip_end = strchr(ip_start, ']'); if(ip_end) { - const char *portp = strchr(ip_end, ':'); - if(portp) { - curl_off_t start; - curl_off_t end; - portp++; - if(!curlx_str_number(&portp, &start, 0xffff)) { - /* got the first number */ - port_min = (unsigned short)start; - if(!curlx_str_single(&portp, '-')) { - /* got the dash */ - if(!curlx_str_number(&portp, &end, 0xffff)) - /* got the second number */ - port_max = (unsigned short)end; - } + addrlen = ip_end - ip_start; + addr = ip_start; + } + } + else +#endif + if(*string_ftpport == ':') { + /* :port */ + ip_end = string_ftpport; + } + else { + ip_end = strchr(string_ftpport, ':'); + addr = string_ftpport; + if(ip_end) { +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)ss; +#endif + /* either ipv6 or (ipv4|domain|interface):port(-range) */ + addrlen = ip_end - string_ftpport; +#ifdef USE_IPV6 + if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { + /* ipv6 */ + addrlen = strlen(string_ftpport); + ip_end = NULL; /* this got no port ! */ } +#endif + } + else + /* ipv4|interface */ + addrlen = strlen(string_ftpport); + } + + /* parse the port */ + if(ip_end) { + const char *portp = strchr(ip_end, ':'); + if(portp) { + curl_off_t start; + curl_off_t end; + portp++; + if(!curlx_str_number(&portp, &start, 0xffff)) { + port_min = (unsigned short)start; + if(!curlx_str_single(&portp, '-') && + !curlx_str_number(&portp, &end, 0xffff)) + port_max = (unsigned short)end; else port_max = port_min; } } - - /* correct errors like: - * :1234-1230 - * :-4711, in this case port_min is (unsigned)-1, - * therefore port_min > port_max for all cases - * but port_max = (unsigned)-1 - */ - if(port_min > port_max) - port_min = port_max = 0; - - if(addrlen) { - const struct Curl_sockaddr_ex *remote_addr = - Curl_conn_get_remote_addr(data, FIRSTSOCKET); - - DEBUGASSERT(remote_addr); - if(!remote_addr) - goto out; - DEBUGASSERT(addr); - if(addrlen >= sizeof(ipstr)) - goto out; - memcpy(ipstr, addr, addrlen); - ipstr[addrlen] = 0; - - /* attempt to get the address of the given interface name */ - switch(Curl_if2ip(remote_addr->family, -#ifdef USE_IPV6 - Curl_ipv6_scope(&remote_addr->curl_sa_addr), - conn->scope_id, -#endif - ipstr, hbuf, sizeof(hbuf))) { - case IF2IP_NOT_FOUND: - /* not an interface, use the given string as hostname instead */ - host = ipstr; - break; - case IF2IP_AF_NOT_SUPPORTED: - goto out; - case IF2IP_FOUND: - host = hbuf; /* use the hbuf for hostname */ - break; - } - } - else - /* there was only a port(-range) given, default the host */ - host = NULL; - } /* data->set.ftpport */ - - if(!host) { - const char *r; - /* not an interface and not a hostname, get default by extracting - the IP from the control connection */ - sslen = sizeof(ss); - if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { - failf(data, "getsockname() failed: %s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; - } - switch(sa->sa_family) { -#ifdef USE_IPV6 - case AF_INET6: - r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); - break; -#endif - default: - r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); - break; - } - if(!r) { - goto out; - } - host = hbuf; /* use this hostname */ - possibly_non_local = FALSE; /* we know it is local now */ } - /* resolv ip/host to ip */ - res = NULL; - result = Curl_resolv_blocking(data, host, 0, conn->ip_version, &dns_entry); - if(!result) { - DEBUGASSERT(dns_entry); - res = dns_entry->addr; - } + /* correct errors like :1234-1230 or :-4711 */ + if(port_min > port_max) + port_min = port_max = 0; - if(!res) { + if(addrlen) { + const struct Curl_sockaddr_ex *remote_addr = + Curl_conn_get_remote_addr(data, FIRSTSOCKET); + + DEBUGASSERT(remote_addr); + DEBUGASSERT(addr); + if(!remote_addr || (addrlen >= sizeof(ipstr)) || (addrlen >= hbuflen)) + return CURLE_FTP_PORT_FAILED; + memcpy(ipstr, addr, addrlen); + ipstr[addrlen] = 0; + + /* attempt to get the address of the given interface name */ + switch(Curl_if2ip(remote_addr->family, +#ifdef USE_IPV6 + Curl_ipv6_scope(&remote_addr->curl_sa_addr), + conn->scope_id, +#endif + ipstr, hbuf, hbuflen)) { + case IF2IP_NOT_FOUND: + /* not an interface, use the string as hostname instead */ + memcpy(hbuf, addr, addrlen); + hbuf[addrlen] = 0; + *hostp = hbuf; + break; + case IF2IP_AF_NOT_SUPPORTED: + return CURLE_FTP_PORT_FAILED; + case IF2IP_FOUND: + *hostp = hbuf; /* use the hbuf for hostname */ + break; + } + } + /* else: only a port(-range) given, leave host as NULL */ + +done: + *port_minp = port_min; + *port_maxp = port_max; + return CURLE_OK; +} + +/* + * If no host was derived from the FTPPORT string, fall back to the IP address + * of the control connection's local socket. + */ +static CURLcode ftp_port_default_host(struct Curl_easy *data, + struct connectdata *conn, + struct Curl_sockaddr_storage *ss, + curl_socklen_t *sslenp, + const char **hostp, + char *hbuf, size_t hbuflen, + bool *non_localp) +{ + struct sockaddr *sa = (struct sockaddr *)ss; + struct sockaddr_in * const sa4 = (void *)sa; +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)sa; +#endif + char buffer[STRERROR_LEN]; + const char *r; + + *sslenp = sizeof(*ss); + if(getsockname(conn->sock[FIRSTSOCKET], sa, sslenp)) { + failf(data, "getsockname() failed: %s", + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; + } + switch(sa->sa_family) { +#ifdef USE_IPV6 + case AF_INET6: + r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, hbuflen); + break; +#endif + default: + r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, hbuflen); + break; + } + if(!r) + return CURLE_FTP_PORT_FAILED; + + *hostp = hbuf; + *non_localp = FALSE; /* we know it is local now */ + return CURLE_OK; +} + +/* + * Resolve the host string to a list of addresses. + */ +static CURLcode ftp_port_resolve_host(struct Curl_easy *data, + struct connectdata *conn, + const char *host, + struct Curl_dns_entry **dns_entryp, + const struct Curl_addrinfo **resp) +{ + CURLcode result; + + *resp = NULL; + result = Curl_resolv_blocking( + data, Curl_resolv_dns_queries(data, conn->ip_version), + host, 0, Curl_conn_get_transport(data, conn), dns_entryp); + if(result) failf(data, "failed to resolve the address provided to PORT: %s", host); - goto out; + else { + DEBUGASSERT(*dns_entryp); + *resp = (*dns_entryp)->addr; } + return result; +} - host = NULL; +/* + * Open a TCP socket for the resolved address family. + */ +static CURLcode ftp_port_open_socket(struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *res, + const struct Curl_addrinfo **aip, + curl_socket_t *portsockp) +{ + char buffer[STRERROR_LEN]; + int error = 0; + const struct Curl_addrinfo *ai; + CURLcode result = CURLE_FTP_PORT_FAILED; - /* step 2, create a socket for the requested address */ - error = 0; for(ai = res; ai; ai = ai->ai_next) { - result = Curl_socket_open(data, ai, NULL, - Curl_conn_get_transport(data, conn), &portsock); + result = + Curl_socket_open(data, ai, NULL, + Curl_conn_get_transport(data, conn), portsockp); if(result) { if(result == CURLE_OUT_OF_MEMORY) - goto out; + return result; result = CURLE_FTP_PORT_FAILED; error = SOCKERRNO; continue; @@ -1039,15 +1104,38 @@ if(!ai) { failf(data, "socket failure: %s", curlx_strerror(error, buffer, sizeof(buffer))); - goto out; + return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket", - FTP_CSTATE(ftpc)); + *aip = ai; + return result; +} - /* step 3, bind to a suitable local address */ +/* + * Bind the socket to a local address and port within the requested range. + * Falls back to the control-connection address if the user-requested address + * is non-local. + */ +static CURLcode ftp_port_bind_socket(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t portsock, + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_storage *ss, + curl_socklen_t *sslen_io, + unsigned short port_min, + unsigned short port_max, + bool non_local) +{ + struct sockaddr *sa = (struct sockaddr *)ss; + struct sockaddr_in * const sa4 = (void *)sa; +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)sa; +#endif + char buffer[STRERROR_LEN]; + unsigned short port; + int error; memcpy(sa, ai->ai_addr, ai->ai_addrlen); - sslen = ai->ai_addrlen; + *sslen_io = ai->ai_addrlen; for(port = port_min; port <= port_max;) { if(sa->sa_family == AF_INET) @@ -1056,71 +1144,97 @@ else sa6->sin6_port = htons(port); #endif - /* Try binding the given address. */ - if(bind(portsock, sa, sslen)) { - /* It failed. */ + if(bind(portsock, sa, *sslen_io)) { error = SOCKERRNO; - if(possibly_non_local && (error == SOCKEADDRNOTAVAIL)) { + if(non_local && (error == SOCKEADDRNOTAVAIL)) { /* The requested bind address is not local. Use the address used for - * the control connection instead and restart the port loop + * the control connection instead and restart the port loop. */ infof(data, "bind(port=%hu) on non-local address failed: %s", port, curlx_strerror(error, buffer, sizeof(buffer))); - sslen = sizeof(ss); - if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { + *sslen_io = sizeof(*ss); + if(getsockname(conn->sock[FIRSTSOCKET], sa, sslen_io)) { failf(data, "getsockname() failed: %s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + return CURLE_FTP_PORT_FAILED; } port = port_min; - possibly_non_local = FALSE; /* do not try this again */ + non_local = FALSE; /* do not try this again */ continue; } if(error != SOCKEADDRINUSE && error != SOCKEACCES) { failf(data, "bind(port=%hu) failed: %s", port, curlx_strerror(error, buffer, sizeof(buffer))); - goto out; + return CURLE_FTP_PORT_FAILED; } } else break; - /* check if port is the maximum value here, because it might be 0xffff and - then the increment below will wrap the 16 bit counter */ + /* check if port is the maximum value here, because it might be 0xffff + and then the increment below will wrap the 16-bit counter */ if(port == port_max) { - /* maybe all ports were in use already */ failf(data, "bind() failed, ran out of ports"); - goto out; + return CURLE_FTP_PORT_FAILED; } port++; } - /* get the name again after the bind() so that we can extract the - port number it uses now */ - sslen = sizeof(ss); - if(getsockname(portsock, sa, &sslen)) { + /* re-read the name so we can extract the actual port chosen */ + *sslen_io = sizeof(*ss); + if(getsockname(portsock, sa, sslen_io)) { failf(data, "getsockname() failed: %s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d", - FTP_CSTATE(ftpc), port); + CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", + port); + return CURLE_OK; +} - /* step 4, listen on the socket */ +/* + * Start listening on the data socket. + */ +static CURLcode ftp_port_listen(struct Curl_easy *data, curl_socket_t portsock) +{ + char buffer[STRERROR_LEN]; if(listen(portsock, 1)) { failf(data, "socket failure: %s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d", - FTP_CSTATE(ftpc), port); + CURL_TRC_FTP(data, "ftp_port_listen(), listening on port"); + return CURLE_OK; +} - /* step 5, send the proper FTP command */ +/* + * Send the EPRT or PORT command to the server. + */ +static CURLcode ftp_port_send_command(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct connectdata *conn, + struct Curl_sockaddr_storage *ss, + const struct Curl_addrinfo *ai, + ftpport fcmd) +{ + static const char mode[][5] = { "EPRT", "PORT" }; + struct sockaddr *sa = (struct sockaddr *)ss; + struct sockaddr_in * const sa4 = (void *)sa; +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)sa; +#endif + char myhost[MAX_IPADR_LEN + 1] = ""; + unsigned short port; + CURLcode result; - /* get a plain printable version of the numerical address to work with - below */ + /* Get a plain printable version of the numerical address to work with. This + logic uses the address provided by the FTPPORT option, which at times + might differ from the address in 'ss' used to bind to: when a user asks + the server to connect to a specific address knowing that it works, but + curl instead selects to listen to the local address because it cannot use + the provided address. FTP is strange. */ Curl_printable_address(ai, myhost, sizeof(myhost)); #ifdef USE_IPV6 @@ -1161,21 +1275,20 @@ * * EPRT |2|1080::8:800:200C:417A|5282| */ - result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd], sa->sa_family == AF_INET ? 1 : 2, myhost, port); if(result) { failf(data, "Failure sending EPRT command: %s", curl_easy_strerror(result)); - goto out; + return result; } break; } if(PORT == fcmd) { /* large enough for [IP address],[num],[num] */ char target[sizeof(myhost) + 20]; - char *source = myhost; + const char *source = myhost; char *dest = target; /* translate x.x.x.x to x,x,x,x */ @@ -1194,7 +1307,7 @@ if(result) { failf(data, "Failure sending PORT command: %s", curl_easy_strerror(result)); - goto out; + return result; } break; } @@ -1203,21 +1316,84 @@ /* store which command was sent */ ftpc->count1 = fcmd; ftp_state(data, ftpc, FTP_PORT); + return CURLE_OK; +} - /* Replace any filter on SECONDARY with one listening on this socket */ - result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); +/* + * ftp_state_use_port() + * + * Set up an active-mode FTP data connection (using PORT or EPRT) and start + * listening for the server's incoming connection on SECONDARYSOCKET. + */ +static CURLcode ftp_state_use_port(struct Curl_easy *data, + struct ftp_conn *ftpc, + ftpport fcmd) /* start with this */ +{ + CURLcode result = CURLE_FTP_PORT_FAILED; + struct connectdata *conn = data->conn; + curl_socket_t portsock = CURL_SOCKET_BAD; + + struct Curl_sockaddr_storage ss; + curl_socklen_t sslen; + char hbuf[NI_MAXHOST]; + const char *host = NULL; + const char *string_ftpport = data->set.str[STRING_FTPPORT]; + struct Curl_dns_entry *dns_entry = NULL; + const struct Curl_addrinfo *res = NULL; + const struct Curl_addrinfo *ai = NULL; + unsigned short port_min = 0; + unsigned short port_max = 0; + bool non_local = TRUE; + + /* parse the FTPPORT string for address and port range */ + result = ftp_port_parse_string(data, conn, string_ftpport, + &ss, &port_min, &port_max, + &host, hbuf, sizeof(hbuf)); + if(!result && !host) + /* if no host was specified, use the control connection's local IP */ + result = ftp_port_default_host(data, conn, &ss, &sslen, &host, + hbuf, sizeof(hbuf), &non_local); + + /* resolve host string to address list */ + if(!result) + result = ftp_port_resolve_host(data, conn, host, &dns_entry, &res); + + /* Open a TCP socket for the data connection */ + if(!result) + result = ftp_port_open_socket(data, conn, res, &ai, &portsock); + if(!result) { + CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket", + FTP_CSTATE(ftpc)); + + /* bind to a suitable local address / port */ + result = ftp_port_bind_socket(data, conn, portsock, ai, &ss, &sslen, + port_min, port_max, non_local); + } + + /* listen */ + if(!result) + result = ftp_port_listen(data, portsock); + + /* send the PORT / EPRT command */ + if(!result) + result = ftp_port_send_command(data, ftpc, conn, &ss, ai, fcmd); + + /* replace any filter on SECONDARY with one listening on this socket */ + if(!result) + result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); + if(!result) portsock = CURL_SOCKET_BAD; /* now held in filter */ -out: - /* If we looked up a dns_entry, now is the time to safely release it */ + /* cleanup */ + if(dns_entry) - Curl_resolv_unlink(data, &dns_entry); + Curl_dns_entry_unlink(data, &dns_entry); if(result) { ftp_state(data, ftpc, FTP_STOP); } else { - /* successfully setup the list socket filter. Do we need more? */ + /* successfully set up the listen socket filter. SSL needed? */ if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); @@ -1379,14 +1555,14 @@ Whether the server will support this, is uncertain. The other ftp_filemethods will CWD into dir/dir/ first and - then just do LIST (in that case: nothing to do here) + then do LIST (in that case: nothing to do here) */ const char *lstArg = NULL; int lstArglen = 0; char *cmd; if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { - /* url-decode before evaluation: e.g. paths starting/ending with %2f */ + /* URL-decode before evaluation: e.g. paths starting/ending with %2f */ const char *rawPath = ftpc->rawpath; const char *slashPos = strrchr(rawPath, '/'); if(slashPos) { @@ -1454,7 +1630,7 @@ information. Which in FTP cannot be much more than the file size and date. */ if(data->req.no_body && ftpc->file && - ftp_need_type(ftpc, data->state.prefer_ascii)) { + ftp_need_type(ftpc, (bool)data->state.prefer_ascii)) { /* The SIZE command is _not_ RFC 959 specified, and therefore many servers may not support it! It is however the only way we have to get a file's size! */ @@ -1464,7 +1640,8 @@ /* Some servers return different sizes for different modes, and thus we must set the proper type before we check the size */ - result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, FTP_TYPE); + result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii, + FTP_TYPE); if(result) return result; } @@ -1505,21 +1682,21 @@ bool sizechecked) { CURLcode result = CURLE_OK; - bool append = data->set.remote_append; + curl_bit append = data->set.remote_append; if((data->state.resume_from && !sizechecked) || ((data->state.resume_from > 0) && sizechecked)) { - /* we are about to continue the uploading of a file */ - /* 1. get already existing file's size. We use the SIZE command for this - which may not exist in the server! The SIZE command is not in - RFC959. */ + /* we are about to continue the uploading of a file + 1. get already existing file's size. We use the SIZE command for this + which may not exist in the server! The SIZE command is not in + RFC959. - /* 2. This used to set REST. But since we can do append, we - do not another ftp command. We just skip the source file - offset and then we APPEND the rest on the file instead */ + 2. This used to set REST, but since we can do append, we issue no + another ftp command. Skip the source file offset and APPEND the rest + on the file instead - /* 3. pass file-size number of bytes in the source file */ - /* 4. lower the infilesize counter */ + 3. pass file-size number of bytes in the source file + 4. lower the infilesize counter */ /* => transfer as usual */ int seekerr = CURL_SEEKFUNC_OK; @@ -1598,6 +1775,89 @@ return result; } +static CURLcode ftp_state_retr(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + curl_off_t filesize) +{ + CURLcode result = CURLE_OK; + + CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_CSTATE(ftpc)); + if(data->set.max_filesize && (filesize > data->set.max_filesize)) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + ftp->downloadsize = filesize; + + if(data->state.resume_from) { + /* We always (attempt to) get the size of downloads, so it is done before + this even when not doing resumes. */ + if(filesize == -1) { + infof(data, "ftp server does not support SIZE"); + /* We could not get the size and therefore we cannot know if there + really is a part of the file left to get, although the server will + close the connection when we start the connection so it will not + cause us any harm, not make us exit as nicely. */ + } + else { + /* We got a file size report, so we check that there actually is a + part of the file left to get, or else we go home. */ + if(data->state.resume_from < 0) { + /* We are supposed to download the last abs(from) bytes */ + if(filesize < -data->state.resume_from) { + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", + data->state.resume_from, filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* convert to size to download */ + ftp->downloadsize = -data->state.resume_from; + /* download from where? */ + data->state.resume_from = filesize - ftp->downloadsize; + } + else { + if(filesize < data->state.resume_from) { + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", + data->state.resume_from, filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* Now store the number of bytes we are expected to download */ + ftp->downloadsize = filesize - data->state.resume_from; + } + } + + if(ftp->downloadsize == 0) { + /* no data to transfer */ + Curl_xfer_setup_nop(data); + infof(data, "File already completely downloaded"); + + /* Set ->transfer so that we will not get any error in ftp_done() + * because we did not transfer the any file */ + ftp->transfer = PPTRANSFER_NONE; + ftp_state(data, ftpc, FTP_STOP); + return CURLE_OK; + } + + /* Set resume file transfer offset */ + infof(data, "Instructs server to resume from offset %" FMT_OFF_T, + data->state.resume_from); + + result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T, + data->state.resume_from); + if(!result) + ftp_state(data, ftpc, FTP_RETR_REST); + } + else { + /* no resume */ + result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_RETR); + } + + return result; +} + static CURLcode ftp_state_quote(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp, @@ -1643,7 +1903,7 @@ i++; } if(item) { - char *cmd = item->data; + const char *cmd = item->data; if(cmd[0] == '*') { cmd++; ftpc->count2 = 1; /* the sent command is allowed to fail */ @@ -1685,8 +1945,8 @@ behavior. In addition: asking for the size for 'TYPE A' transfers is not - constructive since servers do not report the converted size. So - skip it. + constructive since servers do not report the converted size. + Thus, skip it. */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) @@ -1801,15 +2061,15 @@ CURLcode result; struct Curl_dns_entry *dns = NULL; unsigned short connectport; /* the local port connect() should use! */ - struct pingpong *pp = &ftpc->pp; + const struct pingpong *pp = &ftpc->pp; char *newhost = NULL; unsigned short newport = 0; - char *str = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */ - + const char *str = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ if((ftpc->count1 == 0) && (ftpcode == 229)) { /* positive EPSV response */ - char *ptr = strchr(str, '('); + const char *ptr = strchr(str, '('); if(ptr) { char sep; ptr++; @@ -1902,9 +2162,10 @@ if(result) goto error; - (void)Curl_resolv_blocking(data, host_name, ipquad.remote_port, - is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4, - &dns); + (void)Curl_resolv_blocking( + data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A, + host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn), + &dns); /* we connect to the proxy's port */ connectport = (unsigned short)ipquad.remote_port; @@ -1928,7 +2189,9 @@ goto error; } - (void)Curl_resolv_blocking(data, newhost, newport, conn->ip_version, &dns); + (void)Curl_resolv_blocking( + data, Curl_resolv_dns_queries(data, conn->ip_version), + newhost, newport, Curl_conn_get_transport(data, conn), &dns); connectport = newport; /* we connect to the remote port */ if(!dns) { @@ -1938,6 +2201,12 @@ } } + DEBUGASSERT(newhost); + curlx_free(conn->secondaryhostname); + conn->secondary_port = newport; + conn->secondaryhostname = newhost; + newhost = NULL; + result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, conn->bits.ftp_use_data_ssl ? CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); @@ -1945,10 +2214,8 @@ if(result) { if((result != CURLE_OUT_OF_MEMORY) && (ftpc->count1 == 0) && (ftpcode == 229)) { - curlx_free(newhost); - return ftp_epsv_disable(data, ftpc, conn); + result = ftp_epsv_disable(data, ftpc, conn); } - goto error; } @@ -1958,26 +2225,238 @@ * connect to connect. */ - if(data->set.verbose) - /* this just dumps information about this second connection */ - ftp_pasv_verbose(data, dns->addr, newhost, connectport); - - curlx_free(conn->secondaryhostname); - conn->secondary_port = newport; - conn->secondaryhostname = curlx_strdup(newhost); - if(!conn->secondaryhostname) { - result = CURLE_OUT_OF_MEMORY; - goto error; +#ifdef CURLVERBOSE + if(data->set.verbose) { + /* Dump information about this second connection when we have issued a PASV + * command before and thus we have connected to a possibly new IP address. + */ + char buf[256]; + Curl_printable_address(dns->addr, buf, sizeof(buf)); + infof(data, "Connecting to %s (%s) port %d", + conn->secondaryhostname, buf, connectport); } +#endif conn->bits.do_more = TRUE; ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */ error: + Curl_dns_entry_unlink(data, &dns); curlx_free(newhost); return result; } +/* called repeatedly until done from multi.c */ +static CURLcode ftp_statemach(struct Curl_easy *data, + struct ftp_conn *ftpc, + bool *done) +{ + CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); + + /* Check for the state outside of the Curl_socket_check() return code checks + since at times we are in fact already in this state when this function + gets called. */ + *done = (ftpc->state == FTP_STOP); + + return result; +} + +/* + * ftp_do_more() + * + * This function shall be called when the second FTP (data) connection is + * connected. + * + * 'complete' can return 0 for incomplete, 1 for done and -1 for go back + * (which is for when PASV is being sent to retry a failed EPSV). + */ +static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) +{ + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); + CURLcode result = CURLE_OK; + bool connected = FALSE; + bool complete = FALSE; + /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP + * proxy then the state will not be valid until after that connection is + * complete */ + + if(!ftpc || !ftp) + return CURLE_FAILED_INIT; + + *completep = 0; /* default to stay in the state */ + + /* if the second connection has been set up, try to connect it fully + * to the remote host. This may not complete at this time, for several + * reasons: + * - we do EPTR and the server will not connect to our listen socket + * until we send more FTP commands + * - an SSL filter is in place and the server will not start the TLS + * handshake until we send more FTP commands + */ + if(conn->cfilter[SECONDARYSOCKET]) { + bool is_eptr = Curl_conn_is_tcp_listen(data, SECONDARYSOCKET); + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); + if(result == CURLE_OUT_OF_MEMORY) + return result; + if(result || (!connected && !is_eptr && + !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) { + if(result && !is_eptr && (ftpc->count1 == 0)) { + *completep = -1; /* go back to DOING please */ + /* this is a EPSV connect failing, try PASV instead */ + return ftp_epsv_disable(data, ftpc, conn); + } + return result; + } + } + + if(ftpc->state) { + /* already in a state so skip the initial commands. + They are only done to kickstart the do_more state */ + result = ftp_statemach(data, ftpc, &complete); + + *completep = (int)complete; + + /* if we got an error or if we do not wait for a data connection return + immediately */ + if(result || !ftpc->wait_data_conn) + return result; + + /* if we reach the end of the FTP state machine here, *complete will be + TRUE but so is ftpc->wait_data_conn, which says we need to wait for the + data connection and therefore we are not actually complete */ + *completep = 0; + } + + if(ftp->transfer <= PPTRANSFER_INFO) { + /* a transfer is about to take place, or if not a filename was given so we + will do a SIZE on it later and then we need the right TYPE first */ + + if(ftpc->wait_data_conn) { + bool serv_conned; + + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned); + if(result) + return result; /* Failed to accept data connection */ + + if(serv_conned) { + /* It looks data connection is established */ + ftpc->wait_data_conn = FALSE; + result = ftp_initiate_transfer(data, ftpc); + + if(result) + return result; + + *completep = 1; /* this state is now complete when the server has + connected back to us */ + } + else { + result = ftp_check_ctrl_on_data_wait(data, ftpc); + if(result) + return result; + } + } + else if(data->state.upload) { + result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii, + FTP_STOR_TYPE); + if(result) + return result; + + result = ftp_statemach(data, ftpc, &complete); + /* ftp_nb_type() might have skipped sending `TYPE A|I` when not + * deemed necessary and directly sent `STORE name`. If this was + * then complete, but we are still waiting on the data connection, + * the transfer has not been initiated yet. */ + *completep = (int)(ftpc->wait_data_conn ? 0 : complete); + } + else { + /* download */ + ftp->downloadsize = -1; /* unknown as of yet */ + + result = Curl_range(data); + + if(result == CURLE_OK && data->req.maxdownload >= 0) { + /* Do not check for successful transfer */ + ftpc->dont_check = TRUE; + } + + if(result) + ; + else if((data->state.list_only || !ftpc->file) && + !(data->set.prequote)) { + /* The specified path ends with a slash, and therefore we think this + is a directory that is requested, use LIST. Before that, we also + need to set ASCII transfer mode. */ + + /* Only if a body transfer was requested. */ + if(ftp->transfer == PPTRANSFER_BODY) { + result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE); + if(result) + return result; + } + /* otherwise fall through */ + } + else { + if(data->set.prequote && !ftpc->file) { + result = ftp_nb_type(data, ftpc, ftp, TRUE, + FTP_RETR_LIST_TYPE); + } + else { + result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii, + FTP_RETR_TYPE); + } + if(result) + return result; + } + + result = ftp_statemach(data, ftpc, &complete); + *completep = (int)complete; + } + return result; + } + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + if(!ftpc->wait_data_conn) { + /* no waiting for the data connection so this is now complete */ + *completep = 1; + CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc), + (int)result); + } + + return result; +} + +/* call this when the DO phase has completed */ +static CURLcode ftp_dophase_done(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool connected) +{ + if(connected) { + int completed; + CURLcode result = ftp_do_more(data, &completed); + + if(result) { + close_secondarysocket(data, ftpc); + return result; + } + } + + if(ftp->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + else if(!connected) + /* since we did not connect now, we want do_more to get called */ + data->conn->bits.do_more = TRUE; + + ftpc->ctl_valid = TRUE; /* seems good */ + + return CURLE_OK; +} + static CURLcode ftp_state_port_resp(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp, @@ -2015,24 +2494,32 @@ return result; } -static int twodigit(const char *p) +/* return TRUE on error, FALSE on success */ +static bool twodigit(const char *p, int *val) { - return (p[0] - '0') * 10 + (p[1] - '0'); + if(!ISDIGIT(p[0]) || !ISDIGIT(p[1])) + return TRUE; + /* curlx_hexval() works fine here since we make sure it is decimal above */ + *val = (curlx_hexval(p[0]) * 10) + curlx_hexval(p[1]); + return FALSE; } -static bool ftp_213_date(const char *p, int *year, int *month, int *day, - int *hour, int *minute, int *second) +/* + * @unittest 1668 + */ +UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second); +UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second) { - size_t len = strlen(p); - if(len < 14) + int century; + if((strlen(p) < 14) || twodigit(&p[0], ¢ury) || twodigit(&p[2], year) || + twodigit(&p[4], month) || twodigit(&p[6], day) || + twodigit(&p[8], hour) || twodigit(&p[10], minute) || + twodigit(&p[12], second)) return FALSE; - *year = twodigit(&p[0]) * 100 + twodigit(&p[2]); - *month = twodigit(&p[4]); - *day = twodigit(&p[6]); - *hour = twodigit(&p[8]); - *minute = twodigit(&p[10]); - *second = twodigit(&p[12]); + *year += century * 100; if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) || (*second > 60)) return FALSE; @@ -2057,7 +2544,7 @@ * headers from CONNECT should not automatically be part of the * output. */ CURLcode result; - bool save = data->set.include_header; + bool save = (bool)data->set.include_header; data->set.include_header = TRUE; result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen); data->set.include_header = save; @@ -2077,7 +2564,7 @@ last .sss part is optional and means fractions of a second */ int year, month, day, hour, minute, second; struct pingpong *pp = &ftpc->pp; - char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4; + const char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4; bool showtime = FALSE; if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) { /* we have a time, reformat it */ @@ -2093,7 +2580,7 @@ /* If we asked for a time of the file and we actually got one as well, we "emulate" an HTTP-style header in our output. */ -#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) +#if defined(CURL_HAVE_DIAG) && (defined(__DJGPP__) || defined(__AMIGA__)) #pragma GCC diagnostic push /* 'time_t' is unsigned in MSDOS and AmigaOS. Silence: warning: comparison of unsigned expression in '>= 0' is always true */ @@ -2101,7 +2588,7 @@ #endif if(data->req.no_body && ftpc->file && data->set.get_filetime && showtime) { -#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) +#if defined(CURL_HAVE_DIAG) && (defined(__DJGPP__) || defined(__AMIGA__)) #pragma GCC diagnostic pop #endif char headerbuf[128]; @@ -2211,89 +2698,6 @@ return result; } -static CURLcode ftp_state_retr(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - curl_off_t filesize) -{ - CURLcode result = CURLE_OK; - - CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_CSTATE(ftpc)); - if(data->set.max_filesize && (filesize > data->set.max_filesize)) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - ftp->downloadsize = filesize; - - if(data->state.resume_from) { - /* We always (attempt to) get the size of downloads, so it is done before - this even when not doing resumes. */ - if(filesize == -1) { - infof(data, "ftp server does not support SIZE"); - /* We could not get the size and therefore we cannot know if there really - is a part of the file left to get, although the server will just - close the connection when we start the connection so it will not cause - us any harm, just not make us exit as nicely. */ - } - else { - /* We got a file size report, so we check that there actually is a - part of the file left to get, or else we go home. */ - if(data->state.resume_from < 0) { - /* We are supposed to download the last abs(from) bytes */ - if(filesize < -data->state.resume_from) { - failf(data, "Offset (%" FMT_OFF_T - ") was beyond file size (%" FMT_OFF_T ")", - data->state.resume_from, filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* convert to size to download */ - ftp->downloadsize = -data->state.resume_from; - /* download from where? */ - data->state.resume_from = filesize - ftp->downloadsize; - } - else { - if(filesize < data->state.resume_from) { - failf(data, "Offset (%" FMT_OFF_T - ") was beyond file size (%" FMT_OFF_T ")", - data->state.resume_from, filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* Now store the number of bytes we are expected to download */ - ftp->downloadsize = filesize - data->state.resume_from; - } - } - - if(ftp->downloadsize == 0) { - /* no data to transfer */ - Curl_xfer_setup_nop(data); - infof(data, "File already completely downloaded"); - - /* Set ->transfer so that we will not get any error in ftp_done() - * because we did not transfer the any file */ - ftp->transfer = PPTRANSFER_NONE; - ftp_state(data, ftpc, FTP_STOP); - return CURLE_OK; - } - - /* Set resume file transfer offset */ - infof(data, "Instructs server to resume from offset %" FMT_OFF_T, - data->state.resume_from); - - result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T, - data->state.resume_from); - if(!result) - ftp_state(data, ftpc, FTP_RETR_REST); - } - else { - /* no resume */ - result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_RETR); - } - - return result; -} - static CURLcode ftp_state_size_resp(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp, @@ -2302,7 +2706,7 @@ { CURLcode result = CURLE_OK; curl_off_t filesize = -1; - char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); + const char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); size_t len = ftpc->pp.nfinal; /* get the size from the ascii string: */ @@ -2310,7 +2714,7 @@ /* To allow servers to prepend "rubbish" in the response string, we scan for all the digits at the end of the response and parse only those as a number. */ - char *start = &buf[4]; + const char *start = &buf[4]; const char *fdigit = memchr(start, '\r', len - 4); if(fdigit) { fdigit--; @@ -2481,7 +2885,7 @@ size_t i; for(i = 0; i < len - 7; i++) { curl_off_t what; - char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); + const char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); const char *c = &buf[i]; if(!curlx_str_number(&c, &what, CURL_OFF_T_MAX) && !curlx_str_single(&c, ' ') && @@ -2525,7 +2929,7 @@ } else { if((instate == FTP_LIST) && (ftpcode == 450)) { - /* simply no matching files in the directory listing */ + /* no matching files in the directory listing */ ftp->transfer = PPTRANSFER_NONE; /* do not download anything */ ftp_state(data, ftpc, FTP_STOP); /* this phase is over */ } @@ -2578,7 +2982,7 @@ { CURLcode result = CURLE_OK; - /* some need password anyway, and others just return 2xx ignored */ + /* some need password anyway, and others return 2xx ignored */ if((ftpcode == 331) && (ftpc->state == FTP_USER)) { /* 331 Password required for ... (the server requires to send the user's password too) */ @@ -2651,8 +3055,8 @@ CURLcode result; if(ftpcode == 257) { - char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first - letter */ + const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ bool entry_extracted = FALSE; struct dynbuf out; curlx_dyn_init(&out, 1000); @@ -2686,10 +3090,18 @@ break; /* get out of this loop */ } } - else + else { + if(ISCNTRL(*ptr)) { + /* control characters have no business in a path */ + curlx_dyn_free(&out); + return CURLE_WEIRD_SERVER_REPLY; + } result = curlx_dyn_addn(&out, ptr, 1); - if(result) + } + if(result) { + curlx_dyn_free(&out); return result; + } } } if(entry_extracted) { @@ -2709,7 +3121,7 @@ if(!ftpc->server_os && dir[0] != '/') { result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST"); if(result) { - curlx_free(dir); + curlx_dyn_free(&out); return result; } } @@ -2924,10 +3336,10 @@ case FTP_SYST: if(ftpcode == 215) { - char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first - letter */ + const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ + const char *start; char *os; - char *start; /* Reply format is like 215<space><OS-name><space><commentary> @@ -2936,7 +3348,7 @@ ptr++; for(start = ptr; *ptr && *ptr != ' '; ptr++) ; - os = Curl_memdup0(start, ptr - start); + os = curlx_memdup0(start, ptr - start); if(!os) return CURLE_OUT_OF_MEMORY; @@ -3109,21 +3521,6 @@ } /* called repeatedly until done from multi.c */ -static CURLcode ftp_statemach(struct Curl_easy *data, - struct ftp_conn *ftpc, - bool *done) -{ - CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); - - /* Check for the state outside of the Curl_socket_check() return code checks - since at times we are in fact already in this state when this function - gets called. */ - *done = (ftpc->state == FTP_STOP); - - return result; -} - -/* called repeatedly until done from multi.c */ static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done) { @@ -3191,6 +3588,61 @@ /*********************************************************************** * + * ftp_sendquote() + * + * Where a 'quote' means a list of custom commands to send to the server. + * The quote list is passed as an argument. + * + * BLOCKING + */ +static CURLcode ftp_sendquote(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct curl_slist *quote) +{ + struct curl_slist *item; + struct pingpong *pp = &ftpc->pp; + + item = quote; + while(item) { + if(item->data) { + size_t nread; + const char *cmd = item->data; + bool acceptfail = FALSE; + CURLcode result; + int ftpcode = 0; + + /* if a command starts with an asterisk, which a legal FTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + acceptfail = TRUE; + } + + result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); + if(!result) { + pp->response = *Curl_pgrs_now(data); /* timeout relative now */ + result = getftpresponse(data, &nread, &ftpcode); + } + if(result) + return result; + + if(!acceptfail && (ftpcode >= 400)) { + failf(data, "QUOT string not accepted: %s", cmd); + return CURLE_QUOTE_ERROR; + } + } + + item = item->next; + } + + return CURLE_OK; +} + +/*********************************************************************** + * * ftp_done() * * The DONE function. This does what needs to be done after a single DO has @@ -3277,9 +3729,9 @@ if(data->set.ftp_filemethod == FTPFILE_NOCWD) pathLen = 0; /* relative path => working directory is FTP home */ else - /* file is url-decoded */ + /* file is URL-decoded */ pathLen -= ftpc->file ? strlen(ftpc->file) : 0; - ftpc->prevpath = Curl_memdup0(rawPath, pathLen); + ftpc->prevpath = curlx_memdup0(rawPath, pathLen); } else ftpc->prevpath = NULL; /* no path */ @@ -3309,15 +3761,15 @@ if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && pp->pending_resp && !premature) { /* - * Let's see what the server says about the transfer we just performed, - * but lower the timeout as sometimes this connection has died while the - * data has been transferred. This happens when doing through NATs etc that + * Let's see what the server says about the transfer we performed, but + * lower the timeout as sometimes this connection has died while the data + * has been transferred. This happens when doing through NATs etc that * abandon old silent connections. */ pp->response = *Curl_pgrs_now(data); /* timeout relative now */ result = getftpresponse(data, &nread, &ftpcode); - if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) { + if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) { failf(data, "control connection looks dead"); ftpc->ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */ @@ -3327,7 +3779,7 @@ return result; if(ftpc->dont_check && data->req.maxdownload > 0) { - /* we have just sent ABOR and there is no reliable way to check if it was + /* we have sent ABOR and there is no reliable way to check if it was * successful or not; we have to close the connection now */ infof(data, "partial download completed, closing connection"); connclose(conn, "Partial download with no ability to check"); @@ -3399,73 +3851,6 @@ /*********************************************************************** * - * ftp_sendquote() - * - * Where a 'quote' means a list of custom commands to send to the server. - * The quote list is passed as an argument. - * - * BLOCKING - */ -static CURLcode ftp_sendquote(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct curl_slist *quote) -{ - struct curl_slist *item; - struct pingpong *pp = &ftpc->pp; - - item = quote; - while(item) { - if(item->data) { - size_t nread; - char *cmd = item->data; - bool acceptfail = FALSE; - CURLcode result; - int ftpcode = 0; - - /* if a command starts with an asterisk, which a legal FTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server responds. */ - - if(cmd[0] == '*') { - cmd++; - acceptfail = TRUE; - } - - result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); - if(!result) { - pp->response = *Curl_pgrs_now(data); /* timeout relative now */ - result = getftpresponse(data, &nread, &ftpcode); - } - if(result) - return result; - - if(!acceptfail && (ftpcode >= 400)) { - failf(data, "QUOT string not accepted: %s", cmd); - return CURLE_QUOTE_ERROR; - } - } - - item = item->next; - } - - return CURLE_OK; -} - -/*********************************************************************** - * - * ftp_need_type() - * - * Returns TRUE if we in the current situation should send TYPE - */ -static int ftp_need_type(struct ftp_conn *ftpc, - bool ascii_wanted) -{ - return ftpc->transfertype != (ascii_wanted ? 'A' : 'I'); -} - -/*********************************************************************** - * * ftp_nb_type() * * Set TYPE. We only deal with ASCII or BINARY so this function @@ -3495,197 +3880,6 @@ return result; } -/*************************************************************************** - * - * ftp_pasv_verbose() - * - * This function only outputs some informationals about this second connection - * when we have issued a PASV command before and thus we have connected to a - * possibly new IP address. - * - */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void -ftp_pasv_verbose(struct Curl_easy *data, - struct Curl_addrinfo *ai, - char *newhost, /* ASCII version */ - int port) -{ - char buf[256]; - Curl_printable_address(ai, buf, sizeof(buf)); - infof(data, "Connecting to %s (%s) port %d", newhost, buf, port); -} -#endif - -/* - * ftp_do_more() - * - * This function shall be called when the second FTP (data) connection is - * connected. - * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back - * (which basically is only for when PASV is being sent to retry a failed - * EPSV). - */ -static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) -{ - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); - CURLcode result = CURLE_OK; - bool connected = FALSE; - bool complete = FALSE; - /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP - * proxy then the state will not be valid until after that connection is - * complete */ - - if(!ftpc || !ftp) - return CURLE_FAILED_INIT; - - *completep = 0; /* default to stay in the state */ - - /* if the second connection has been set up, try to connect it fully - * to the remote host. This may not complete at this time, for several - * reasons: - * - we do EPTR and the server will not connect to our listen socket - * until we send more FTP commands - * - an SSL filter is in place and the server will not start the TLS - * handshake until we send more FTP commands - */ - if(conn->cfilter[SECONDARYSOCKET]) { - bool is_eptr = Curl_conn_is_tcp_listen(data, SECONDARYSOCKET); - result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); - if(result == CURLE_OUT_OF_MEMORY) - return result; - if(result || (!connected && !is_eptr && - !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) { - if(result && !is_eptr && (ftpc->count1 == 0)) { - *completep = -1; /* go back to DOING please */ - /* this is a EPSV connect failing, try PASV instead */ - return ftp_epsv_disable(data, ftpc, conn); - } - return result; - } - } - - if(ftpc->state) { - /* already in a state so skip the initial commands. - They are only done to kickstart the do_more state */ - result = ftp_statemach(data, ftpc, &complete); - - *completep = (int)complete; - - /* if we got an error or if we do not wait for a data connection return - immediately */ - if(result || !ftpc->wait_data_conn) - return result; - - /* if we reach the end of the FTP state machine here, *complete will be - TRUE but so is ftpc->wait_data_conn, which says we need to wait for the - data connection and therefore we are not actually complete */ - *completep = 0; - } - - if(ftp->transfer <= PPTRANSFER_INFO) { - /* a transfer is about to take place, or if not a filename was given so we - will do a SIZE on it later and then we need the right TYPE first */ - - if(ftpc->wait_data_conn) { - bool serv_conned; - - result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned); - if(result) - return result; /* Failed to accept data connection */ - - if(serv_conned) { - /* It looks data connection is established */ - ftpc->wait_data_conn = FALSE; - result = ftp_initiate_transfer(data, ftpc); - - if(result) - return result; - - *completep = 1; /* this state is now complete when the server has - connected back to us */ - } - else { - result = ftp_check_ctrl_on_data_wait(data, ftpc); - if(result) - return result; - } - } - else if(data->state.upload) { - result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, - FTP_STOR_TYPE); - if(result) - return result; - - result = ftp_statemach(data, ftpc, &complete); - /* ftp_nb_type() might have skipped sending `TYPE A|I` when not - * deemed necessary and directly sent `STORE name`. If this was - * then complete, but we are still waiting on the data connection, - * the transfer has not been initiated yet. */ - *completep = (int)(ftpc->wait_data_conn ? 0 : complete); - } - else { - /* download */ - ftp->downloadsize = -1; /* unknown as of yet */ - - result = Curl_range(data); - - if(result == CURLE_OK && data->req.maxdownload >= 0) { - /* Do not check for successful transfer */ - ftpc->dont_check = TRUE; - } - - if(result) - ; - else if((data->state.list_only || !ftpc->file) && - !(data->set.prequote)) { - /* The specified path ends with a slash, and therefore we think this - is a directory that is requested, use LIST. But before that we - need to set ASCII transfer mode. */ - - /* But only if a body transfer was requested. */ - if(ftp->transfer == PPTRANSFER_BODY) { - result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE); - if(result) - return result; - } - /* otherwise just fall through */ - } - else { - if(data->set.prequote && !ftpc->file) { - result = ftp_nb_type(data, ftpc, ftp, TRUE, - FTP_RETR_LIST_TYPE); - } - else { - result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, - FTP_RETR_TYPE); - } - if(result) - return result; - } - - result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; - } - return result; - } - - /* no data to transfer */ - Curl_xfer_setup_nop(data); - - if(!ftpc->wait_data_conn) { - /* no waiting for the data connection so this is now complete */ - *completep = 1; - CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc), - (int)result); - } - - return result; -} - /*********************************************************************** * * ftp_perform() @@ -3693,12 +3887,12 @@ * This is the actual DO function for FTP. Get a file/directory according to * the options previously setup. */ -static -CURLcode ftp_perform(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool *connected, /* connect status after PASV / PORT */ - bool *dophase_done) +static CURLcode ftp_perform( + struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool *connected, /* connect status after PASV / PORT */ + bool *dophase_done) { /* this is FTP and no proxy */ CURLcode result = CURLE_OK; @@ -3832,7 +4026,7 @@ Curl_ftp_parselist_data_free(&ftpwc->parser); curlx_free(ftpwc); } - Curl_safefree(wildcard->pattern); + curlx_safefree(wildcard->pattern); wildcard->dtor = ZERO_NULL; wildcard->ftpwc = NULL; return result; @@ -3971,6 +4165,52 @@ /*********************************************************************** * + * ftp_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + * + * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the + * ftp_done() function without finding any major problem. + */ +static CURLcode ftp_regular_transfer(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + data->req.size = -1; /* make sure this is unknown at this point */ + + Curl_pgrsReset(data); + + ftpc->ctl_valid = TRUE; /* starts good */ + + result = ftp_perform(data, ftpc, ftp, + &connected, /* have we connected after PASV/PORT */ + dophase_done); /* all commands in the DO-phase done? */ + + if(!result) { + + if(!*dophase_done) + /* the DO phase has not completed yet */ + return CURLE_OK; + + result = ftp_dophase_done(data, ftpc, ftp, connected); + + if(result) + return result; + } + else + freedirs(ftpc); + + return result; +} + +/*********************************************************************** + * * ftp_do() * * This function is registered as 'curl_do' function. It decodes the path @@ -4083,7 +4323,7 @@ disconnect wait in vain and cause more problems than we need to. ftp_quit() will check the state of ftp->ctl_valid. If it is ok it - will try to send the QUIT command, otherwise it will just return. + will try to send the QUIT command, otherwise it will return. */ ftpc->shutdown = TRUE; if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp)) @@ -4094,191 +4334,6 @@ return CURLE_OK; } -static size_t numof_slashes(const char *str) -{ - const char *slashPos; - size_t num = 0; - do { - slashPos = strchr(str, '/'); - if(slashPos) { - ++num; - str = slashPos + 1; - } - } while(slashPos); - return num; -} - -#define FTP_MAX_DIR_DEPTH 1000 - -/*********************************************************************** - * - * ftp_parse_url_path() - * - * Parse the URL path into separate path components. - * - */ -static CURLcode ftp_parse_url_path(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) -{ - const char *slashPos = NULL; - const char *fileName = NULL; - CURLcode result = CURLE_OK; - const char *rawPath = NULL; /* url-decoded "raw" path */ - size_t pathLen = 0; - - ftpc->ctl_valid = FALSE; - ftpc->cwdfail = FALSE; - - if(ftpc->rawpath) - freedirs(ftpc); - /* url-decode ftp path before further evaluation */ - result = Curl_urldecode(ftp->path, 0, &ftpc->rawpath, &pathLen, REJECT_CTRL); - if(result) { - failf(data, "path contains control characters"); - return result; - } - rawPath = ftpc->rawpath; - - switch(data->set.ftp_filemethod) { - case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ - - if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) - fileName = rawPath; /* this is a full file path */ - /* - else: ftpc->file is not used anywhere other than for operations on - a file. In other words, never for directory operations. - So we can safely leave filename as NULL here and use it as a - argument in dir/file decisions. - */ - break; - - case FTPFILE_SINGLECWD: - slashPos = strrchr(rawPath, '/'); - if(slashPos) { - /* get path before last slash, except for / */ - size_t dirlen = slashPos - rawPath; - if(dirlen == 0) - dirlen = 1; - - ftpc->dirs = curlx_calloc(1, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; - - ftpc->dirs[0].start = 0; - ftpc->dirs[0].len = (int)dirlen; - ftpc->dirdepth = 1; /* we consider it to be a single directory */ - fileName = slashPos + 1; /* rest is filename */ - } - else - fileName = rawPath; /* filename only (or empty) */ - break; - - default: /* allow pretty much anything */ - case FTPFILE_MULTICWD: { - /* current position: begin of next path component */ - const char *curPos = rawPath; - - /* number of entries to allocate for the 'dirs' array */ - size_t dirAlloc = numof_slashes(rawPath); - - if(dirAlloc >= FTP_MAX_DIR_DEPTH) - /* suspiciously deep directory hierarchy */ - return CURLE_URL_MALFORMAT; - - if(dirAlloc) { - ftpc->dirs = curlx_calloc(dirAlloc, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; - - /* parse the URL path into separate path components */ - while(dirAlloc--) { - const char *spos = strchr(curPos, '/'); - size_t clen = spos - curPos; - - /* path starts with a slash: add that as a directory */ - if(!clen && (ftpc->dirdepth == 0)) - ++clen; - - /* we skip empty path components, like "x//y" since the FTP command - CWD requires a parameter and a non-existent parameter a) does not - work on many servers and b) has no effect on the others. */ - if(clen) { - ftpc->dirs[ftpc->dirdepth].start = (int)(curPos - rawPath); - ftpc->dirs[ftpc->dirdepth].len = (int)clen; - ftpc->dirdepth++; - } - curPos = spos + 1; - } - } - fileName = curPos; /* the rest is the filename (or empty) */ - } - break; - } /* switch */ - - if(fileName && *fileName) - ftpc->file = fileName; - else - ftpc->file = NULL; /* instead of point to a zero byte, - we make it a NULL pointer */ - - if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { - /* We need a filename when uploading. Return error! */ - failf(data, "Uploading to a URL without a filename"); - return CURLE_URL_MALFORMAT; - } - - ftpc->cwddone = FALSE; /* default to not done */ - - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) - ftpc->cwddone = TRUE; /* skip CWD for absolute paths */ - else { /* newly created FTP connections are already in entry path */ - const char *oldPath = data->conn->bits.reuse ? ftpc->prevpath : ""; - if(oldPath) { - size_t n = pathLen; - if(data->set.ftp_filemethod == FTPFILE_NOCWD) - n = 0; /* CWD to entry for relative paths */ - else - n -= ftpc->file ? strlen(ftpc->file) : 0; - - if((strlen(oldPath) == n) && rawPath && !strncmp(rawPath, oldPath, n)) { - infof(data, "Request has same path as previous transfer"); - ftpc->cwddone = TRUE; - } - } - } - - return CURLE_OK; -} - -/* call this when the DO phase has completed */ -static CURLcode ftp_dophase_done(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool connected) -{ - if(connected) { - int completed; - CURLcode result = ftp_do_more(data, &completed); - - if(result) { - close_secondarysocket(data, ftpc); - return result; - } - } - - if(ftp->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_xfer_setup_nop(data); - else if(!connected) - /* since we did not connect now, we want do_more to get called */ - data->conn->bits.do_more = TRUE; - - ftpc->ctl_valid = TRUE; /* seems good */ - - return CURLE_OK; -} - /* called from multi.c while DOing */ static CURLcode ftp_doing(struct Curl_easy *data, bool *dophase_done) @@ -4301,58 +4356,12 @@ return result; } -/*********************************************************************** - * - * ftp_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - * - * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the - * ftp_done() function without finding any major problem. - */ -static CURLcode ftp_regular_transfer(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - data->req.size = -1; /* make sure this is unknown at this point */ - - Curl_pgrsReset(data); - - ftpc->ctl_valid = TRUE; /* starts good */ - - result = ftp_perform(data, ftpc, ftp, - &connected, /* have we connected after PASV/PORT */ - dophase_done); /* all commands in the DO-phase done? */ - - if(!result) { - - if(!*dophase_done) - /* the DO phase has not completed yet */ - return CURLE_OK; - - result = ftp_dophase_done(data, ftpc, ftp, connected); - - if(result) - return result; - } - else - freedirs(ftpc); - - return result; -} - static void ftp_easy_dtor(void *key, size_t klen, void *entry) { struct FTP *ftp = entry; (void)key; (void)klen; - Curl_safefree(ftp->pathalloc); + curlx_safefree(ftp->pathalloc); curlx_free(ftp); } @@ -4362,11 +4371,11 @@ (void)key; (void)klen; freedirs(ftpc); - Curl_safefree(ftpc->account); - Curl_safefree(ftpc->alternative_to_user); - Curl_safefree(ftpc->entrypath); - Curl_safefree(ftpc->prevpath); - Curl_safefree(ftpc->server_os); + curlx_safefree(ftpc->account); + curlx_safefree(ftpc->alternative_to_user); + curlx_safefree(ftpc->entrypath); + curlx_safefree(ftpc->prevpath); + curlx_safefree(ftpc->server_os); Curl_pp_disconnect(&ftpc->pp); curlx_free(ftpc); } @@ -4429,7 +4438,7 @@ ftpc->alternative_to_user = curlx_strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); if(!ftpc->alternative_to_user) { - Curl_safefree(ftpc->account); + curlx_safefree(ftpc->account); Curl_conn_meta_remove(conn, CURL_META_FTP_CONN); return CURLE_OUT_OF_MEMORY; } @@ -4465,4 +4474,27 @@ return TRUE; } +/* + * FTP protocol. + */ +const struct Curl_protocol Curl_protocol_ftp = { + ftp_setup_connection, /* setup_connection */ + ftp_do, /* do_it */ + ftp_done, /* done */ + ftp_do_more, /* do_more */ + ftp_connect, /* connect_it */ + ftp_multi_statemach, /* connecting */ + ftp_doing, /* doing */ + ftp_pollset, /* proto_pollset */ + ftp_pollset, /* doing_pollset */ + ftp_domore_pollset, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_FTP */
diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h index 0e32d39..257f595 100644 --- a/Utilities/cmcurl/lib/ftp.h +++ b/Utilities/cmcurl/lib/ftp.h
@@ -25,76 +25,11 @@ ***************************************************************************/ #include "curl_setup.h" -#include "pingpong.h" - #ifndef CURL_DISABLE_FTP -extern const struct Curl_handler Curl_handler_ftp; - -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_ftps; -#endif +extern const struct Curl_protocol Curl_protocol_ftp; bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn); -#endif /* CURL_DISABLE_FTP */ - -/**************************************************************************** - * FTP unique setup - ***************************************************************************/ -enum { - FTP_STOP, /* do nothing state, stops the state machine */ - FTP_WAIT220, /* waiting for the initial 220 response immediately after - a connect */ - FTP_AUTH, - FTP_USER, - FTP_PASS, - FTP_ACCT, - FTP_PBSZ, - FTP_PROT, - FTP_CCC, - FTP_PWD, - FTP_SYST, - FTP_NAMEFMT, - FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ - FTP_RETR_PREQUOTE, - FTP_STOR_PREQUOTE, - FTP_LIST_PREQUOTE, - FTP_POSTQUOTE, - FTP_CWD, /* change directory */ - FTP_MKD, /* if the directory did not exist */ - FTP_MDTM, /* to figure out the datestamp */ - FTP_TYPE, /* to set type when doing a head-like request */ - FTP_LIST_TYPE, /* set type when about to do a directory list */ - FTP_RETR_LIST_TYPE, - FTP_RETR_TYPE, /* set type when about to RETR a file */ - FTP_STOR_TYPE, /* set type when about to STOR a file */ - FTP_SIZE, /* get the remote file's size for head-like request */ - FTP_RETR_SIZE, /* get the remote file's size for RETR */ - FTP_STOR_SIZE, /* get the size for STOR */ - FTP_REST, /* when used to check if the server supports it in head-like */ - FTP_RETR_REST, /* when asking for "resume" in for RETR */ - FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ - FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ - FTP_PASV, /* generic state for PASV and EPSV, check count1 */ - FTP_LIST, /* generic state for LIST, NLST or a custom list command */ - FTP_RETR, - FTP_STOR, /* generic state for STOR and APPE */ - FTP_QUIT, - FTP_LAST /* never used */ -}; -typedef unsigned char ftpstate; /* use the enum values */ - -struct ftp_parselist_data; /* defined later in ftplistparser.c */ - -struct ftp_wc { - struct ftp_parselist_data *parser; - - struct { - curl_write_callback write_function; - FILE *file_descriptor; - } backup; -}; - typedef enum { FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */ @@ -102,74 +37,7 @@ file */ } curl_ftpfile; -/* This FTP struct is used in the Curl_easy. All FTP data that is - connection-oriented must be in FTP_conn to properly deal with the fact that - perhaps the Curl_easy is changed between the times the connection is - used. */ -struct FTP { - char *path; /* points to the urlpieces struct field */ - char *pathalloc; /* if non-NULL a pointer to an allocated path */ - - /* transfer a file/body or not, done as a typedefed enum just to make - debuggers display the full symbol and not just the numerical value */ - curl_pp_transfer transfer; - curl_off_t downloadsize; -}; - -/* one struct entry for each path component (of 'rawpath') */ -struct pathcomp { - int start; /* start column */ - int len; /* length in bytes */ -}; - -/* ftp_conn is used for struct connection-oriented data in the connectdata - struct */ -struct ftp_conn { - struct pingpong pp; - char *account; - char *alternative_to_user; - char *entrypath; /* the PWD reply when we logged on */ - const char *file; /* url-decoded filename (or path), points into rawpath */ - char *rawpath; /* URL decoded, allocated, version of the path */ - struct pathcomp *dirs; /* allocated array for path components */ - char *prevpath; /* url-decoded conn->path from the previous transfer */ - char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a - and others (A/I or zero) */ - char *server_os; /* The target server operating system. */ - curl_off_t known_filesize; /* file size is different from -1, if wildcard - LIST parsing was done and wc_statemach set - it */ - int count1; /* general purpose counter for the state machine */ - int count2; /* general purpose counter for the state machine */ - int count3; /* general purpose counter for the state machine */ - unsigned short dirdepth; /* number of entries used in the 'dirs' array, - < FTP_MAX_DIR_DEPTH */ - unsigned short cwdcount; /* number of CWD commands issued, - < FTP_MAX_DIR_DEPTH */ - unsigned char state; /* (ftpstate enum) always use ftp.c:state() to change - state! */ - unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! (type: curl_usessl)*/ - unsigned char ccc; /* ccc level for this connection */ - BIT(ftp_trying_alternative); - BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) - file size and 226/250 status check. It should still - read the line, just ignore the result. */ - BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If - the connection has timed out or been closed, this - should be FALSE when it gets to Curl_ftp_quit() */ - BIT(cwddone); /* if it has been determined that the proper CWD combo - already has been done */ - BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent - caching the current directory */ - BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ - BIT(shutdown); /* connection is being shutdown, e.g. QUIT */ -}; - -/* meta key for storing `struct FTP` as easy meta data */ -#define CURL_META_FTP_EASY "meta:proto:ftp:easy" -/* meta key for storing `struct ftp_conn` as connection meta data */ -#define CURL_META_FTP_CONN "meta:proto:ftp:conn" +#endif /* CURL_DISABLE_FTP */ #define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index f82895d..b5d9338 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -44,6 +44,7 @@ #include "fileinfo.h" #include "llist.h" #include "ftp.h" +#include "ftp-int.h" #include "ftplistparser.h" #include "curl_fnmatch.h" #include "multiif.h" @@ -324,8 +325,7 @@ /* filter pattern-corresponding filenames */ Curl_set_in_callback(data, TRUE); - if(compare(data->set.fnmatch_data, wc->pattern, - finfo->filename) == 0) { + if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) { /* discard symlink which is containing multiple " -> " */ if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && (strstr(finfo->strings.target, " -> "))) {
diff --git a/Utilities/cmcurl/lib/ftplistparser.h b/Utilities/cmcurl/lib/ftplistparser.h index afb90de..5d7aa49 100644 --- a/Utilities/cmcurl/lib/ftplistparser.h +++ b/Utilities/cmcurl/lib/ftplistparser.h
@@ -37,7 +37,7 @@ struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void); -void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data); +void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp); /* list of wildcard process states */ typedef enum {
diff --git a/Utilities/cmcurl/lib/functypes.h b/Utilities/cmcurl/lib/functypes.h index ba754f9..887c261 100644 --- a/Utilities/cmcurl/lib/functypes.h +++ b/Utilities/cmcurl/lib/functypes.h
@@ -23,8 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "curl_setup.h" - /* defaults: ssize_t recv(int, void *, size_t, int); @@ -37,7 +35,7 @@ 2. For systems with config-*.h files, define them there. */ -#ifdef _WIN32 +#ifdef USE_WINSOCK /* int recv(SOCKET, char *, int, int) */ #define RECV_TYPE_ARG1 SOCKET #define RECV_TYPE_ARG2 char * @@ -59,9 +57,9 @@ #define RECV_TYPE_ARG4 long #define RECV_TYPE_RETV long -/* int send(int, const char *, int, int); */ +/* int send(int, char *, int, int); */ #define SEND_TYPE_ARG1 int -#define SEND_QUAL_ARG2 +#define SEND_NONCONST_ARG2 #define SEND_TYPE_ARG2 char * #define SEND_TYPE_ARG3 int #define SEND_TYPE_RETV int @@ -87,10 +85,6 @@ #define RECV_TYPE_RETV ssize_t #endif -#ifndef SEND_QUAL_ARG2 -#define SEND_QUAL_ARG2 const -#endif - #ifndef SEND_TYPE_ARG1 #define SEND_TYPE_ARG1 int #endif
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c index e094ffd..fab63e6 100644 --- a/Utilities/cmcurl/lib/getinfo.c +++ b/Utilities/cmcurl/lib/getinfo.c
@@ -78,6 +78,7 @@ info->conn_scheme = 0; info->conn_protocol = 0; + info->used_proxy = 0; #ifdef USE_SSL Curl_ssl_free_certinfo(data); @@ -127,7 +128,7 @@ *param_charp = data->info.contenttype; break; case CURLINFO_PRIVATE: - *param_charp = (char *)data->set.private_data; + *param_charp = (const char *)data->set.private_data; break; case CURLINFO_FTP_ENTRY_PATH: /* Return the entrypath string from the most recent connection. @@ -281,7 +282,12 @@ *param_longp = data->state.os_errno; break; case CURLINFO_NUM_CONNECTS: - *param_longp = data->info.numconnects; +#if SIZEOF_LONG < SIZEOF_CURL_OFF_T + if(data->info.numconnects > LONG_MAX) + *param_longp = LONG_MAX; + else +#endif + *param_longp = (long)data->info.numconnects; break; case CURLINFO_LASTSOCKET: sockfd = Curl_getconnectinfo(data, NULL); @@ -374,7 +380,7 @@ return CURLE_OK; } -#define DOUBLE_SECS(x) (double)(x) / 1000000 +#define DOUBLE_SECS(x) ((double)(x) / 1000000) static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, curl_off_t *param_offt) @@ -409,6 +415,9 @@ case CURLINFO_FILETIME_T: *param_offt = (curl_off_t)data->info.filetime; break; + case CURLINFO_SIZE_DELIVERED: + *param_offt = data->progress.deliver; + break; case CURLINFO_SIZE_UPLOAD_T: *param_offt = data->progress.ul.cur_size; break; @@ -577,14 +586,16 @@ break; case CURLINFO_TLS_SESSION: case CURLINFO_TLS_SSL_PTR: { + int query = (info == CURLINFO_TLS_SSL_PTR) ? + CF_QUERY_SSL_INFO : CF_QUERY_SSL_CTX_INFO; struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **) - param_slistp; + param_slistp; struct curl_tlssessioninfo *tsi = &data->tsi; /* we are exposing a pointer to internal memory with unknown * lifetime here. */ *tsip = tsi; - if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, tsi)) { + if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, query, tsi)) { tsi->backend = Curl_ssl_backend(); tsi->internals = NULL; }
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c index 186530d..f087121 100644 --- a/Utilities/cmcurl/lib/gopher.c +++ b/Utilities/cmcurl/lib/gopher.c
@@ -22,87 +22,21 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "gopher.h" #ifndef CURL_DISABLE_GOPHER -#include "urldata.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" #include "cfilters.h" #include "connect.h" -#include "gopher.h" #include "select.h" #include "url.h" #include "escape.h" -/* - * Forward declarations. - */ - -static CURLcode gopher_do(struct Curl_easy *data, bool *done); #ifdef USE_SSL -static CURLcode gopher_connect(struct Curl_easy *data, bool *done); -static CURLcode gopher_connecting(struct Curl_easy *data, bool *done); -#endif - -/* - * Gopher protocol handler. - * This is also a nice simple template to build off for simple - * connect-command-download protocols. - */ - -const struct Curl_handler Curl_handler_gopher = { - "gopher", /* scheme */ - ZERO_NULL, /* setup_connection */ - gopher_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_GOPHER, /* defport */ - CURLPROTO_GOPHER, /* protocol */ - CURLPROTO_GOPHER, /* family */ - PROTOPT_NONE /* flags */ -}; - -#ifdef USE_SSL -const struct Curl_handler Curl_handler_gophers = { - "gophers", /* scheme */ - ZERO_NULL, /* setup_connection */ - gopher_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - gopher_connect, /* connect_it */ - gopher_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_GOPHER, /* defport */ - CURLPROTO_GOPHERS, /* protocol */ - CURLPROTO_GOPHER, /* family */ - PROTOPT_SSL /* flags */ -}; - static CURLcode gopher_connect(struct Curl_easy *data, bool *done) { (void)data; @@ -129,8 +63,8 @@ struct connectdata *conn = data->conn; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; char *gopherpath; - char *path = data->state.up.path; - char *query = data->state.up.query; + const char *path = data->state.up.path; + const char *query = data->state.up.query; const char *buf = NULL; char *buf_alloc = NULL; size_t nwritten, buf_len; @@ -157,7 +91,7 @@ curlx_free(gopherpath); } else { - char *newp; + const char *newp; /* Otherwise, drop / and the first character (i.e., item type) ... */ newp = gopherpath; @@ -191,7 +125,7 @@ else break; - timeout_ms = Curl_timeleft_ms(data, FALSE); + timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; break; @@ -231,4 +165,53 @@ Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); return CURLE_OK; } + +/* + * Gopher protocol handler. + * This is also a nice simple template to build off for simple + * connect-command-download protocols. + */ + +const struct Curl_protocol Curl_protocol_gopher = { + ZERO_NULL, /* setup_connection */ + gopher_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#ifdef USE_SSL +const struct Curl_protocol Curl_protocol_gophers = { + ZERO_NULL, /* setup_connection */ + gopher_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + gopher_connect, /* connect_it */ + gopher_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; +#endif + #endif /* CURL_DISABLE_GOPHER */
diff --git a/Utilities/cmcurl/lib/gopher.h b/Utilities/cmcurl/lib/gopher.h index 1e6a5d2..1c126be 100644 --- a/Utilities/cmcurl/lib/gopher.h +++ b/Utilities/cmcurl/lib/gopher.h
@@ -24,10 +24,8 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_GOPHER -extern const struct Curl_handler Curl_handler_gopher; -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_gophers; -#endif +extern const struct Curl_protocol Curl_protocol_gopher; +extern const struct Curl_protocol Curl_protocol_gophers; #endif #endif /* HEADER_CURL_GOPHER_H */
diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c index 0b6ecc4..3eae1d2 100644 --- a/Utilities/cmcurl/lib/hash.c +++ b/Utilities/cmcurl/lib/hash.c
@@ -155,7 +155,7 @@ ++h->size; } -#define CURL_HASH_SLOT(x, y, z) x->table[x->hash_func(y, z, x->slots)] +#define CURL_HASH_SLOT(x, y, z) x->table[(x)->hash_func(y, z, (x)->slots)] #define CURL_HASH_SLOT_ADDR(x, y, z) &CURL_HASH_SLOT(x, y, z) void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, @@ -177,7 +177,7 @@ if(h->comp_func(he->key, he->key_len, key, key_len)) { /* existing key entry, overwrite by clearing old pointer */ hash_elem_clear_ptr(h, he); - he->ptr = (void *)p; + he->ptr = p; he->dtor = dtor; return p; } @@ -265,7 +265,7 @@ DEBUGASSERT(h->init == HASHINIT); if(h->table) { Curl_hash_clean(h); - Curl_safefree(h->table); + curlx_safefree(h->table); } DEBUGASSERT(h->size == 0); h->slots = 0; @@ -359,8 +359,8 @@ #endif } -struct Curl_hash_element * -Curl_hash_next_element(struct Curl_hash_iterator *iter) +struct Curl_hash_element *Curl_hash_next_element( + struct Curl_hash_iterator *iter) { struct Curl_hash *h; DEBUGASSERT(iter->init == ITERINIT);
diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h index 8bf47cc..1d7d3de 100644 --- a/Utilities/cmcurl/lib/hash.h +++ b/Utilities/cmcurl/lib/hash.h
@@ -85,7 +85,7 @@ void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, Curl_hash_elem_dtor dtor); int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len); -void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len); +void *Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len); void Curl_hash_destroy(struct Curl_hash *h); size_t Curl_hash_count(struct Curl_hash *h); @@ -97,8 +97,8 @@ size_t key2_len); void Curl_hash_start_iterate(struct Curl_hash *hash, struct Curl_hash_iterator *iter); -struct Curl_hash_element * -Curl_hash_next_element(struct Curl_hash_iterator *iter); +struct Curl_hash_element *Curl_hash_next_element( + struct Curl_hash_iterator *iter); void Curl_hash_print(struct Curl_hash *h, void (*func)(void *));
diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c index c188045..195e12b 100644 --- a/Utilities/cmcurl/lib/headers.c +++ b/Utilities/cmcurl/lib/headers.c
@@ -52,23 +52,23 @@ } /* public API */ -CURLHcode curl_easy_header(CURL *easy, +CURLHcode curl_easy_header(CURL *curl, const char *name, size_t nameindex, - unsigned int type, + unsigned int origin, int request, struct curl_header **hout) { struct Curl_llist_node *e; struct Curl_llist_node *e_pick = NULL; - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; size_t match = 0; size_t amount = 0; struct Curl_header_store *hs = NULL; struct Curl_header_store *pick = NULL; if(!name || !hout || !data || - (type > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | - CURLH_PSEUDO)) || !type || (request < -1)) + (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | + CURLH_PSEUDO)) || !origin || (request < -1)) return CURLHE_BAD_ARGUMENT; if(!Curl_llist_count(&data->state.httphdrs)) return CURLHE_NOHEADERS; /* no headers available */ @@ -81,7 +81,7 @@ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); if(curl_strequal(hs->name, name) && - (hs->type & type) && + (hs->type & origin) && (hs->request == request)) { amount++; pick = hs; @@ -100,7 +100,7 @@ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); if(curl_strequal(hs->name, name) && - (hs->type & type) && + (hs->type & origin) && (hs->request == request) && (match++ == nameindex)) { e_pick = e; @@ -118,12 +118,12 @@ } /* public API */ -struct curl_header *curl_easy_nextheader(CURL *easy, - unsigned int type, +struct curl_header *curl_easy_nextheader(CURL *curl, + unsigned int origin, int request, struct curl_header *prev) { - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; struct Curl_llist_node *pick; struct Curl_llist_node *e; struct Curl_header_store *hs; @@ -149,7 +149,7 @@ /* make sure it is the next header of the desired type */ do { hs = Curl_node_elem(pick); - if((hs->type & type) && (hs->request == request)) + if((hs->type & origin) && (hs->request == request)) break; pick = Curl_node_next(pick); } while(pick); @@ -167,7 +167,7 @@ struct Curl_header_store *check = Curl_node_elem(e); if(curl_strequal(hs->name, check->name) && (check->request == request) && - (check->type & type)) + (check->type & origin)) amount++; if(e == pick) index = amount - 1; @@ -326,7 +326,7 @@ struct Curl_cwriter *writer; CURLcode result; - if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) { + if(data->conn && (data->conn->scheme->protocol & PROTO_FAMILY_HTTP)) { /* avoid installing it twice */ if(Curl_cwriter_get_by_name(data, hds_cw_collect.name)) return CURLE_OK;
diff --git a/Utilities/cmcurl/lib/headers.h b/Utilities/cmcurl/lib/headers.h index adb03af..283179a 100644 --- a/Utilities/cmcurl/lib/headers.h +++ b/Utilities/cmcurl/lib/headers.h
@@ -30,7 +30,7 @@ struct Curl_header_store { struct Curl_llist_node node; char *name; /* points into 'buffer' */ - char *value; /* points into 'buffer */ + char *value; /* points into 'buffer' */ int request; /* 0 is the first request, then 1.. 2.. */ unsigned char type; /* CURLH_* defines */ char buffer[1]; /* this is the raw header blob */ @@ -38,7 +38,7 @@ /* * Initialize header collecting for a transfer. - * Will add a client writer that catches CLIENTWRITE_HEADER writes. + * Add a client writer that catches CLIENTWRITE_HEADER writes. */ CURLcode Curl_headers_init(struct Curl_easy *data); @@ -54,9 +54,10 @@ CURLcode Curl_headers_cleanup(struct Curl_easy *data); #else -#define Curl_headers_init(x) CURLE_OK -#define Curl_headers_push(x,y,z,a) CURLE_OK -#define Curl_headers_cleanup(x) Curl_nop_stmt +#define Curl_headers_init(x) CURLE_OK +#define Curl_headers_push(x, y, z, a) \ + ((void)(x), (void)(y), (void)(z), (void)(a), CURLE_OK) +#define Curl_headers_cleanup(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_HEADER_H */
diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c index 9080588..a4d6ebd 100644 --- a/Utilities/cmcurl/lib/hmac.c +++ b/Utilities/cmcurl/lib/hmac.c
@@ -25,8 +25,8 @@ ***************************************************************************/ #include "curl_setup.h" -#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ - !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ + !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_SSL) #include "curl_hmac.h" @@ -52,7 +52,7 @@ unsigned char b; /* Create HMAC context. */ - i = sizeof(*ctxt) + 2 * hashparams->ctxtsize + hashparams->resultlen; + i = sizeof(*ctxt) + (2 * hashparams->ctxtsize) + hashparams->resultlen; ctxt = curlx_malloc(i); if(!ctxt) @@ -98,13 +98,12 @@ return NULL; } -int Curl_HMAC_update(struct HMAC_context *ctxt, - const unsigned char *ptr, - unsigned int len) +void Curl_HMAC_update(struct HMAC_context *ctxt, + const unsigned char *data, + unsigned int len) { /* Update first hash calculation. */ - ctxt->hash->hupdate(ctxt->hashctxt1, ptr, len); - return 0; + ctxt->hash->hupdate(ctxt->hashctxt1, data, len); } int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output) @@ -143,17 +142,24 @@ */ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, - const unsigned char *buf, const size_t buflen, + const unsigned char *data, size_t datalen, unsigned char *output) { - struct HMAC_context *ctxt = - Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen)); + struct HMAC_context *ctxt; + if(keylen > UINT_MAX) /* unlikely to ever happen */ + return CURLE_BAD_FUNCTION_ARGUMENT; + ctxt = Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen)); if(!ctxt) return CURLE_OUT_OF_MEMORY; /* Update the digest with the given challenge */ - Curl_HMAC_update(ctxt, buf, curlx_uztoui(buflen)); + do { + unsigned int ilen = (unsigned int)CURLMIN(datalen, UINT_MAX); + Curl_HMAC_update(ctxt, data, ilen); + datalen -= ilen; + data += ilen; + } while(datalen); /* Finalise the digest */ Curl_HMAC_final(ctxt, output);
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index a0e1d40..85f53c4 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c
@@ -44,21 +44,20 @@ #include <signal.h> #include "urldata.h" +#include "curl_addrinfo.h" #include "curl_trc.h" -#include "connect.h" +#include "dnscache.h" #include "hostip.h" -#include "hash.h" -#include "rand.h" -#include "curl_share.h" +#include "httpsrr.h" #include "url.h" -#include "curlx/inet_ntop.h" -#include "curlx/inet_pton.h" #include "multiif.h" -#include "doh.h" #include "progress.h" +#include "doh.h" #include "select.h" #include "strcase.h" #include "easy_lock.h" +#include "curlx/inet_ntop.h" +#include "curlx/inet_pton.h" #include "curlx/strcopy.h" #include "curlx/strparse.h" @@ -75,6 +74,21 @@ #define MAX_DNS_CACHE_SIZE 29999 +#define RESOLV_FAIL(for_proxy) \ + ((for_proxy) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST) + +#define IS_RESOLV_FAIL(result) \ + (((result) == CURLE_COULDNT_RESOLVE_HOST) || \ + ((result) == CURLE_COULDNT_RESOLVE_PROXY)) +/* + * ipv6works() returns TRUE if IPv6 seems to work. + */ +#ifdef USE_IPV6 +static bool ipv6works(struct Curl_easy *data); +#else +#define ipv6works(x) FALSE +#endif + /* * hostip.c explained * ================== @@ -87,10 +101,10 @@ * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4 * defined. * - * CURLRES_ARES - is defined if libcurl is built to use c-ares for + * USE_RESOLV_ARES - is defined if libcurl is built to use c-ares for * asynchronous name resolves. This can be Windows or *nix. * - * CURLRES_THREADED - is defined if libcurl is built to run under (native) + * USE_RESOLV_THREADED - is defined if libcurl is built to run under (native) * Windows, and then the name resolve will be done in a new thread, and the * supported API will be the same as for ares-builds. * @@ -112,13 +126,47 @@ * CURLRES_* defines based on the config*.h and curl_setup.h defines. */ -static void dnscache_entry_free(struct Curl_dns_entry *dns); +uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version) +{ + (void)data; + switch(ip_version) { + case CURL_IPRESOLVE_V6: + return CURL_DNSQ_AAAA; + case CURL_IPRESOLVE_V4: + return CURL_DNSQ_A; + default: + if(ipv6works(data)) + return (CURL_DNSQ_A | CURL_DNSQ_AAAA); + else + return CURL_DNSQ_A; + } +} -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void show_resolve_info(struct Curl_easy *data, - struct Curl_dns_entry *dns); -#else -#define show_resolve_info(x, y) Curl_nop_stmt +#ifdef CURLVERBOSE +const char *Curl_resolv_query_str(uint8_t dns_queries) +{ + switch(dns_queries) { + case (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS): + return "A+AAAA+HTTPS"; + case (CURL_DNSQ_A | CURL_DNSQ_AAAA): + return "A+AAAA"; + case (CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS): + return "AAAA+HTTPS"; + case (CURL_DNSQ_AAAA): + return "AAAA"; + case (CURL_DNSQ_A | CURL_DNSQ_HTTPS): + return "A+HTTPS"; + case (CURL_DNSQ_A): + return "A"; + case (CURL_DNSQ_HTTPS): + return "HTTPS"; + case 0: + return "-"; + default: + DEBUGASSERT(0); + return "???"; + } +} #endif /* @@ -154,444 +202,17 @@ } } -/* - * Create a hostcache id string for the provided host + port, to be used by - * the DNS caching. Without alloc. Return length of the id string. - */ -static size_t create_dnscache_id(const char *name, - size_t nlen, /* 0 or actual name length */ - int port, char *ptr, size_t buflen) -{ - size_t len = nlen ? nlen : strlen(name); - DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); - if(len > (buflen - 7)) - len = buflen - 7; - /* store and lower case the name */ - Curl_strntolower(ptr, name, len); - return curl_msnprintf(&ptr[len], 7, ":%u", port) + len; -} - -struct dnscache_prune_data { - struct curltime now; - timediff_t oldest_ms; /* oldest time in cache not pruned. */ - timediff_t max_age_ms; -}; - -/* - * This function is set as a callback to be called for every entry in the DNS - * cache when we want to prune old unused entries. - * - * Returning non-zero means remove the entry, return 0 to keep it in the - * cache. - */ -static int dnscache_entry_is_stale(void *datap, void *hc) -{ - struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap; - struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc; - - if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) { - /* get age in milliseconds */ - timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp); - if(!dns->addr) - age *= 2; /* negative entries age twice as fast */ - if(age >= prune->max_age_ms) - return TRUE; - if(age > prune->oldest_ms) - prune->oldest_ms = age; - } - return FALSE; -} - -/* - * Prune the DNS cache. This assumes that a lock has already been taken. - * Returns the 'age' of the oldest still kept entry - in milliseconds. - */ -static timediff_t dnscache_prune(struct Curl_hash *hostcache, - timediff_t cache_timeout_ms, - struct curltime now) -{ - struct dnscache_prune_data user; - - user.max_age_ms = cache_timeout_ms; - user.now = now; - user.oldest_ms = 0; - - Curl_hash_clean_with_criterium(hostcache, - (void *)&user, - dnscache_entry_is_stale); - - return user.oldest_ms; -} - -static struct Curl_dnscache *dnscache_get(struct Curl_easy *data) -{ - if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) - return &data->share->dnscache; - if(data->multi) - return &data->multi->dnscache; - return NULL; -} - -static void dnscache_lock(struct Curl_easy *data, - struct Curl_dnscache *dnscache) -{ - if(data->share && dnscache == &data->share->dnscache) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); -} - -static void dnscache_unlock(struct Curl_easy *data, - struct Curl_dnscache *dnscache) -{ - if(data->share && dnscache == &data->share->dnscache) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); -} - -/* - * Library-wide function for pruning the DNS cache. This function takes and - * returns the appropriate locks. - */ -void Curl_dnscache_prune(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - /* the timeout may be set -1 (forever) */ - timediff_t timeout_ms = data->set.dns_cache_timeout_ms; - - if(!dnscache || (timeout_ms == -1)) - /* NULL hostcache means we cannot do it */ - return; - - dnscache_lock(data, dnscache); - - do { - /* Remove outdated and unused entries from the hostcache */ - timediff_t oldest_ms = - dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data)); - - if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE) - /* prune the ones over half this age */ - timeout_ms = oldest_ms / 2; - else - break; - - /* if the cache size is still too big, use the oldest age as new prune - limit */ - } while(timeout_ms); - - dnscache_unlock(data, dnscache); -} - -void Curl_dnscache_clear(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - if(dnscache) { - dnscache_lock(data, dnscache); - Curl_hash_clean(&dnscache->entries); - dnscache_unlock(data, dnscache); - } -} - #ifdef USE_ALARM_TIMEOUT /* Beware this is a global and unique instance. This is used to store the return address that we can jump back to from inside a signal handler. This is not thread-safe stuff. */ static sigjmp_buf curl_jmpenv; -static curl_simple_lock curl_jmpenv_lock; +static curl_simple_lock curl_jmpenv_lock = CURL_SIMPLE_LOCK_INIT; #endif -/* lookup address, returns entry if found and not stale */ -static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - const char *hostname, - int port, - int ip_version) -{ - struct Curl_dns_entry *dns = NULL; - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; - - if(!dnscache) - return NULL; - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, 0, port, - entry_id, sizeof(entry_id)); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - - /* No entry found in cache, check if we might have a wildcard entry */ - if(!dns && data->state.wildcard_resolve) { - entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id)); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - } - - if(dns && (data->set.dns_cache_timeout_ms != -1)) { - /* See whether the returned entry is stale. Done before we release lock */ - struct dnscache_prune_data user; - - user.now = *Curl_pgrs_now(data); - user.max_age_ms = data->set.dns_cache_timeout_ms; - user.oldest_ms = 0; - - if(dnscache_entry_is_stale(&user, dns)) { - infof(data, "Hostname in DNS cache was stale, zapped"); - dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - } - - /* See if the returned entry matches the required resolve mode */ - if(dns && ip_version != CURL_IPRESOLVE_WHATEVER) { - int pf = PF_INET; - bool found = FALSE; - struct Curl_addrinfo *addr = dns->addr; - -#ifdef PF_INET6 - if(ip_version == CURL_IPRESOLVE_V6) - pf = PF_INET6; -#endif - - while(addr) { - if(addr->ai_family == pf) { - found = TRUE; - break; - } - addr = addr->ai_next; - } - - if(!found) { - infof(data, "Hostname in DNS cache does not have needed family, zapped"); - dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - } - return dns; -} - -/* - * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. - * - * Curl_resolv() checks initially and multi_runsingle() checks each time - * it discovers the handle in the state WAITRESOLVE whether the hostname - * has already been resolved and the address has already been stored in - * the DNS cache. This short circuits waiting for a lot of pending - * lookups for the same hostname requested by different handles. - * - * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -struct Curl_dns_entry *Curl_dnscache_get(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = NULL; - - dnscache_lock(data, dnscache); - - dns = fetch_addr(data, dnscache, hostname, port, ip_version); - if(dns) - dns->refcount++; /* we use it! */ - - dnscache_unlock(data, dnscache); - - return dns; -} - -#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); -/* - * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' - * struct by re-linking its linked list. - * - * The addr argument should be the address of a pointer to the head node of a - * `Curl_addrinfo` list and it will be modified to point to the new head after - * shuffling. - * - * Not declared static only to make it easy to use in a unit test! - * - * @unittest: 1608 - */ -UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - struct Curl_addrinfo **addr) -{ - CURLcode result = CURLE_OK; - const int num_addrs = num_addresses(*addr); - - if(num_addrs > 1) { - struct Curl_addrinfo **nodes; - infof(data, "Shuffling %i addresses", num_addrs); - - nodes = curlx_malloc(num_addrs * sizeof(*nodes)); - if(nodes) { - int i; - unsigned int *rnd; - const size_t rnd_size = num_addrs * sizeof(*rnd); - - /* build a plain array of Curl_addrinfo pointers */ - nodes[0] = *addr; - for(i = 1; i < num_addrs; i++) { - nodes[i] = nodes[i - 1]->ai_next; - } - - rnd = curlx_malloc(rnd_size); - if(rnd) { - /* Fisher-Yates shuffle */ - if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { - struct Curl_addrinfo *swap_tmp; - for(i = num_addrs - 1; i > 0; i--) { - swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)]; - nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i]; - nodes[i] = swap_tmp; - } - - /* relink list in the new order */ - for(i = 1; i < num_addrs; i++) { - nodes[i - 1]->ai_next = nodes[i]; - } - - nodes[num_addrs - 1]->ai_next = NULL; - *addr = nodes[0]; - } - curlx_free(rnd); - } - else - result = CURLE_OUT_OF_MEMORY; - curlx_free(nodes); - } - else - result = CURLE_OUT_OF_MEMORY; - } - return result; -} -#endif - -struct Curl_dns_entry * -Curl_dnscache_mk_entry(struct Curl_easy *data, - struct Curl_addrinfo *addr, - const char *hostname, - size_t hostlen, /* length or zero */ - int port, - bool permanent) -{ - struct Curl_dns_entry *dns; - -#ifndef CURL_DISABLE_SHUFFLE_DNS - /* shuffle addresses if requested */ - if(data->set.dns_shuffle_addresses) { - CURLcode result = Curl_shuffle_addr(data, &addr); - if(result) - return NULL; - } -#else - (void)data; -#endif - if(!hostlen) - hostlen = strlen(hostname); - - /* Create a new cache entry */ - dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen); - if(!dns) - return NULL; - - dns->refcount = 1; /* the cache has the first reference */ - dns->addr = addr; /* this is the address(es) */ - if(permanent) { - dns->timestamp.tv_sec = 0; /* an entry that never goes stale */ - dns->timestamp.tv_usec = 0; /* an entry that never goes stale */ - } - else { - dns->timestamp = *Curl_pgrs_now(data); - } - dns->hostport = port; - if(hostlen) - memcpy(dns->hostname, hostname, hostlen); - - return dns; -} - -static struct Curl_dns_entry * -dnscache_add_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - struct Curl_addrinfo *addr, - const char *hostname, - size_t hlen, /* length or zero */ - int port, - bool permanent) -{ - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; - struct Curl_dns_entry *dns; - struct Curl_dns_entry *dns2; - - dns = Curl_dnscache_mk_entry(data, addr, hostname, hlen, port, permanent); - if(!dns) - return NULL; - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, hlen, port, - entry_id, sizeof(entry_id)); - - /* Store the resolved data in our DNS cache. */ - dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, - (void *)dns); - if(!dns2) { - dns->addr = NULL; - dnscache_entry_free(dns); - return NULL; - } - - dns = dns2; - dns->refcount++; /* mark entry as in-use */ - return dns; -} - -CURLcode Curl_dnscache_add(struct Curl_easy *data, - struct Curl_dns_entry *entry) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - char id[MAX_HOSTCACHE_LEN]; - size_t idlen; - - if(!dnscache) - return CURLE_FAILED_INIT; - /* Create an entry id, based upon the hostname and port */ - idlen = create_dnscache_id(entry->hostname, 0, entry->hostport, - id, sizeof(id)); - - /* Store the resolved data in our DNS cache and up ref count */ - dnscache_lock(data, dnscache); - if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) { - dnscache_unlock(data, dnscache); - return CURLE_OUT_OF_MEMORY; - } - entry->refcount++; - dnscache_unlock(data, dnscache); - return CURLE_OK; -} - #ifdef USE_IPV6 /* return a static IPv6 ::1 for the name */ -static struct Curl_addrinfo *get_localhost6(int port, const char *name) +static struct Curl_addrinfo *get_localhost6(uint16_t port, const char *name) { struct Curl_addrinfo *ca; const size_t ss_size = sizeof(struct sockaddr_in6); @@ -603,12 +224,9 @@ if(!ca) return NULL; + memset(&sa6, 0, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(port16); - sa6.sin6_flowinfo = 0; -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - sa6.sin6_scope_id = 0; -#endif (void)curlx_inet_pton(AF_INET6, "::1", ipv6); memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6)); @@ -630,7 +248,7 @@ #endif /* return a static IPv4 127.0.0.1 for the given name */ -static struct Curl_addrinfo *get_localhost(int port, const char *name) +static struct Curl_addrinfo *get_localhost(uint16_t port, const char *name) { struct Curl_addrinfo *ca; struct Curl_addrinfo *ca6; @@ -669,36 +287,33 @@ } #ifdef USE_IPV6 -/* - * Curl_ipv6works() returns TRUE if IPv6 seems to work. - */ -bool Curl_ipv6works(struct Curl_easy *data) +/* the nature of most systems is that IPv6 status does not come and go during a + program's lifetime so we only probe the first time and then we have the + info kept for fast reuse */ +CURLcode Curl_probeipv6(struct Curl_multi *multi) { - if(data) { - /* the nature of most system is that IPv6 status does not come and go - during a program's lifetime so we only probe the first time and then we - have the info kept for fast reuse */ - DEBUGASSERT(data); - DEBUGASSERT(data->multi); - if(data->multi->ipv6_up == IPV6_UNKNOWN) { - bool works = Curl_ipv6works(NULL); - data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD; - } - return data->multi->ipv6_up == IPV6_WORKS; + /* probe to see if we have a working IPv6 stack */ + curl_socket_t s = CURL_SOCKET(PF_INET6, SOCK_DGRAM, 0); + multi->ipv6_works = FALSE; + if(s == CURL_SOCKET_BAD) { + if(SOCKERRNO == SOCKENOMEM) + return CURLE_OUT_OF_MEMORY; } else { - int ipv6_works = -1; - /* probe to see if we have a working IPv6 stack */ - curl_socket_t s = CURL_SOCKET(PF_INET6, SOCK_DGRAM, 0); - if(s == CURL_SOCKET_BAD) - /* an IPv6 address was requested but we cannot get/use one */ - ipv6_works = 0; - else { - ipv6_works = 1; - sclose(s); - } - return ipv6_works > 0; + multi->ipv6_works = TRUE; + sclose(s); } + return CURLE_OK; +} + +/* + * ipv6works() returns TRUE if IPv6 seems to work. + */ +static bool ipv6works(struct Curl_easy *data) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + return data ? data->multi->ipv6_works : FALSE; } #endif /* USE_IPV6 */ @@ -730,86 +345,354 @@ return curl_strnequal(part, &full[flen - plen], plen); } -static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version) +static CURLcode hostip_resolv_failed(struct Curl_easy *data, + const char *hostname, + bool for_proxy) { -#ifdef CURLRES_IPV6 - if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data)) - return FALSE; -#elif defined(CURLRES_IPV4) + failf(data, "Could not resolve %s: %s", + for_proxy ? "proxy" : "host", hostname); + return RESOLV_FAIL(for_proxy); +} + +static bool can_resolve_dns_queries(struct Curl_easy *data, + uint8_t dns_queries) +{ (void)data; - if(ip_version == CURL_IPRESOLVE_V6) + if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !ipv6works(data)) return FALSE; -#else -#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined -#endif return TRUE; } -static CURLcode store_negative_resolve(struct Curl_easy *data, - const char *host, - int port) +CURLcode Curl_resolv_announce_start(struct Curl_easy *data, + void *resolver) { - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns; - DEBUGASSERT(dnscache); - if(!dnscache) - return CURLE_FAILED_INIT; + if(data->set.resolver_start) { + int rc; - /* put this new host in the cache */ - dns = dnscache_add_addr(data, dnscache, NULL, host, 0, port, FALSE); - if(dns) { - /* release the returned reference; the cache itself will keep the - * entry alive: */ - dns->refcount--; - infof(data, "Store negative name resolve for %s:%d", host, port); - return CURLE_OK; + CURL_TRC_DNS(data, "announcing resolve to application"); + Curl_set_in_callback(data, TRUE); + rc = data->set.resolver_start(resolver, NULL, + data->set.resolver_start_client); + Curl_set_in_callback(data, FALSE); + if(rc) { + CURL_TRC_DNS(data, "application aborted resolve"); + return CURLE_ABORTED_BY_CALLBACK; + } } - return CURLE_OUT_OF_MEMORY; + return CURLE_OK; } -/* - * Curl_resolv() is the main name resolve function within libcurl. It resolves - * a name and returns a pointer to the entry in the 'entry' argument. This - * function might return immediately if we are using asynch resolves. See the - * return codes. - * - * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlink() later (when you are - * done using this struct) to decrease the reference counter again. - * - * Return codes: - * CURLE_OK = success, *entry set to non-NULL - * CURLE_AGAIN = resolving in progress, *entry == NULL - * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL - * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL - */ -CURLcode Curl_resolv(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - bool allowDOH, - struct Curl_dns_entry **entry) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *addr = NULL; - bool respwait = FALSE; - size_t hostname_len; - CURLcode result = CURLE_COULDNT_RESOLVE_HOST; +#ifdef USE_CURL_ASYNC - *entry = NULL; +static struct Curl_resolv_async *hostip_async_new(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms) +{ + struct Curl_resolv_async *async; + size_t hostlen = strlen(hostname); + + if(!data->multi) { + DEBUGASSERT(0); + return NULL; + } + + /* struct size already includes the NUL for hostname */ + async = curlx_calloc(1, sizeof(*async) + hostlen); + if(!async) + return NULL; + + /* Give every async resolve operation a "unique" id. This may + * wrap around after a long time, making collisions highly unlikely. + * As we keep the async structs at the easy handle, chances of + * easy `mid plus resolv->id` colliding should be astronomical. + * `resolv_id == 0` is never used. */ + if(data->multi->last_resolv_id == UINT32_MAX) + data->multi->last_resolv_id = 1; /* wrap around */ + else + data->multi->last_resolv_id++; + async->id = data->multi->last_resolv_id; + async->dns_queries = dns_queries; + async->port = port; + async->transport = transport; + async->for_proxy = for_proxy; + async->start = *Curl_pgrs_now(data); + async->timeout_ms = timeout_ms; + if(hostlen) { + memcpy(async->hostname, hostname, hostlen); + async->is_ipaddr = Curl_is_ipaddr(async->hostname); + if(async->is_ipaddr) + async->is_ipv4addr = Curl_is_ipv4addr(async->hostname); + } + + return async; +} + +static CURLcode hostip_resolv_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) +{ + CURLcode result; + + /* If async resolving is ongoing, this must be set */ + if(!async) + return CURLE_FAILED_INIT; #ifndef CURL_DISABLE_DOH - data->conn->bits.doh = FALSE; /* default is not */ + if(async->doh) + result = Curl_doh_take_result(data, async, pdns); + else +#endif + result = Curl_async_take_result(data, async, pdns); + + if(result == CURLE_AGAIN) { + CURL_TRC_DNS(data, "resolve incomplete, queries=%s, responses=%s, " + "ongoing=%d for %s:%d", + Curl_resolv_query_str(async->dns_queries), + Curl_resolv_query_str(async->dns_responses), + async->queries_ongoing, async->hostname, async->port); + result = CURLE_OK; + } + else if(result) { + result = Curl_async_failed(data, async, NULL); + } + else { + CURL_TRC_DNS(data, "resolve complete for %s:%u", + async->hostname, async->port); + DEBUGASSERT(*pdns); + } + + return result; +} + +timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data, + uint32_t resolv_id) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(!async) + return CURL_TIMEOUT_RESOLVE_MS; + return curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); +} + +bool Curl_resolv_has_answers(struct Curl_easy *data, + uint32_t resolv_id, uint8_t dns_queries) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + uint8_t check_queries; + /* a no longer existing/running resolve has all answers. */ + if(!async || async->done) + return TRUE; + /* Relevant are only queries undertaken. Others are considered answered. */ + check_queries = (dns_queries & async->dns_queries); + if((check_queries & async->dns_responses) != check_queries) { + return FALSE; + } + return TRUE; +} + +const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, + uint32_t resolv_id, + int ai_family, + unsigned int index) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + (void)index; + if(!async) + return NULL; + if((ai_family == AF_INET) && !(async->dns_queries & CURL_DNSQ_A)) + return NULL; +#ifdef USE_IPV6 + if((ai_family == AF_INET6) && !(async->dns_queries & CURL_DNSQ_AAAA)) + return NULL; +#endif + return Curl_async_get_ai(data, async, ai_family, index); +} + + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo * +Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(!async) + return NULL; + return Curl_async_get_https(data, async); +} + +bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(!async) + return TRUE; + return Curl_async_knows_https(data, async); +} + +#endif /* USE_HTTPSRR */ + +#endif /* USE_CURL_ASYNC */ + +static CURLcode hostip_resolv_start(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + bool allowDOH, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) +{ +#ifdef USE_CURL_ASYNC + struct Curl_resolv_async *async = NULL; +#endif + struct Curl_addrinfo *addr = NULL; + size_t hostname_len; + CURLcode result = CURLE_OK; + + (void)timeout_ms; /* not in all ifdefs */ + *presolv_id = 0; + *pdns = NULL; + + /* Check for "known" things to resolve ourselves. */ +#ifndef USE_RESOLVE_ON_IPS + if(Curl_is_ipaddr(hostname)) { + /* test655 verifies that the announce is done, even though there + * is no real resolving. So, keep doing this. */ + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + /* shortcut literal IP addresses, if we are not told to resolve them. */ + result = Curl_str2addr(hostname, port, &addr); + goto out; + } +#endif + + hostname_len = strlen(hostname); + if(curl_strequal(hostname, "localhost") || + curl_strequal(hostname, "localhost.") || + tailmatch(hostname, hostname_len, STRCONST(".localhost")) || + tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + addr = get_localhost(port, hostname); + if(!addr) + result = CURLE_OUT_OF_MEMORY; + goto out; + } + +#ifndef CURL_DISABLE_DOH + if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + if(!async) { + async = hostip_async_new(data, dns_queries, hostname, port, + transport, for_proxy, timeout_ms); + if(!async) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + result = Curl_doh(data, async); + goto out; + } #else (void)allowDOH; #endif - DEBUGASSERT(dnscache); - if(!dnscache) { - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto error; + + /* Can we provide the requested IP specifics in resolving? */ + if(!can_resolve_dns_queries(data, dns_queries)) { + result = RESOLV_FAIL(for_proxy); + goto out; } +#ifdef CURLRES_ASYNCH + (void)addr; + if(!async) { + async = hostip_async_new(data, dns_queries, hostname, port, + transport, for_proxy, timeout_ms); + if(!async) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + result = Curl_async_getaddrinfo(data, async); + if(result == CURLE_AGAIN) { + /* the answer might be there already. Check. */ + CURLcode r2 = hostip_resolv_take_result(data, async, pdns); + if(r2) + result = r2; + else if(*pdns) + result = CURLE_OK; + } +#else + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport); + if(!addr) + result = RESOLV_FAIL(for_proxy); +#endif + +out: + if(!result) { + if(addr) { + /* we got a response, create a dns entry, add to cache, return */ + DEBUGASSERT(!*pdns); + *pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port); + if(!*pdns) + result = CURLE_OUT_OF_MEMORY; + } + else if(!*pdns) + result = CURLE_AGAIN; + } + else if(*pdns) + Curl_dns_entry_unlink(data, pdns); + else if(addr) + Curl_freeaddrinfo(addr); + +#ifdef USE_CURL_ASYNC + if(async) { + if(result == CURLE_AGAIN) { /* still need it, link, return id. */ + *presolv_id = async->id; + async->next = data->state.async; + data->state.async = async; + } + else { + Curl_async_destroy(data, async); + } + } +#endif + return result; +} + +static CURLcode hostip_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + bool allowDOH, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) +{ + size_t hostname_len; + CURLcode result = RESOLV_FAIL(for_proxy); + bool cache_dns = FALSE; + + (void)timeout_ms; /* not used in all ifdefs */ + *presolv_id = 0; + *pdns = NULL; + +#ifdef CURL_DISABLE_DOH + (void)allowDOH; +#endif + /* We should intentionally error and not resolve .onion TLDs */ hostname_len = strlen(hostname); DEBUGASSERT(hostname_len); @@ -817,154 +700,88 @@ (curl_strequal(&hostname[hostname_len - 6], ".onion") || curl_strequal(&hostname[hostname_len - 7], ".onion."))) { failf(data, "Not resolving .onion address (RFC 7686)"); - goto error; + goto out; } +#ifdef DEBUGBUILD + CURL_TRC_DNS(data, "hostip_resolv(%s:%u, queries=%s)", + hostname, port, Curl_resolv_query_str(dns_queries)); + if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && + getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { + infof(data, "DEBUG fail ipv6 resolve"); + result = hostip_resolv_failed(data, hostname, for_proxy); + goto out; + } +#endif /* Let's check our DNS cache first */ - dnscache_lock(data, dnscache); - dns = fetch_addr(data, dnscache, hostname, port, ip_version); - if(dns) - dns->refcount++; /* we pass out the reference. */ - dnscache_unlock(data, dnscache); - if(dns) { + result = Curl_dnscache_get(data, dns_queries, hostname, port, pdns); + if(*pdns) { infof(data, "Hostname %s was found in DNS cache", hostname); result = CURLE_OK; - goto out; } - - /* No luck, we need to resolve hostname. Notify user callback. */ - if(data->set.resolver_start) { - void *resolver = NULL; - int st; -#ifdef CURLRES_ASYNCH - result = Curl_async_get_impl(data, &resolver); - if(result) - goto error; -#endif - Curl_set_in_callback(data, TRUE); - st = data->set.resolver_start(resolver, NULL, - data->set.resolver_start_client); - Curl_set_in_callback(data, FALSE); - if(st) { - result = CURLE_ABORTED_BY_CALLBACK; - goto error; - } + else if(result) { + infof(data, "Negative DNS entry"); + result = hostip_resolv_failed(data, hostname, for_proxy); } - - if(Curl_is_ipaddr(hostname)) { -#ifndef USE_RESOLVE_ON_IPS - /* shortcut literal IP addresses, if we are not told to resolve them. */ - result = Curl_str2addr(hostname, port, &addr); - if(result) - goto error; - goto out; -#endif - } - - if(curl_strequal(hostname, "localhost") || - curl_strequal(hostname, "localhost.") || - tailmatch(hostname, hostname_len, STRCONST(".localhost")) || - tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { - addr = get_localhost(port, hostname); - result = addr ? CURLE_OK : CURLE_OUT_OF_MEMORY; - } -#ifndef CURL_DISABLE_DOH - else if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { - result = Curl_doh(data, hostname, port, ip_version); - respwait = TRUE; - } -#endif else { - /* Can we provide the requested IP specifics in resolving? */ - if(!can_resolve_ip_version(data, ip_version)) { - result = CURLE_COULDNT_RESOLVE_HOST; - goto error; - } - -#ifdef CURLRES_ASYNCH - result = Curl_async_getaddrinfo(data, hostname, port, ip_version); - respwait = TRUE; -#else - respwait = FALSE; /* no async waiting here */ - addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version); - if(addr) - result = CURLE_OK; -#endif + /* No luck, we need to start resolving. */ + cache_dns = TRUE; + result = hostip_resolv_start(data, dns_queries, hostname, port, + transport, for_proxy, timeout_ms, allowDOH, + presolv_id, pdns); } out: - /* We either have found a `dns` or looked up the `addr` or `respwait` is set - * for an async operation. Everything else is a failure to resolve. */ - if(result) - ; - else if(dns) { - if(!dns->addr) { - infof(data, "Negative DNS entry"); - dns->refcount--; - return CURLE_COULDNT_RESOLVE_HOST; + if(result && (result != CURLE_AGAIN)) { + Curl_dns_entry_unlink(data, pdns); + if(IS_RESOLV_FAIL(result)) { + if(cache_dns) + Curl_dnscache_add_negative(data, dns_queries, hostname, port); + failf(data, "Could not resolve: %s:%u", hostname, port); } - *entry = dns; - return CURLE_OK; - } - else if(addr) { - /* we got a response, create a dns entry, add to cache, return */ - dns = Curl_dnscache_mk_entry(data, addr, hostname, 0, port, FALSE); - if(!dns || Curl_dnscache_add(data, dns)) { - /* this is OOM or similar, do not store such negative resolves */ - Curl_freeaddrinfo(addr); - if(dns) - /* avoid a dangling pointer to addr in the dying dns entry */ - dns->addr = NULL; - result = CURLE_OUT_OF_MEMORY; - goto error; + else { + failf(data, "Error %d resolving %s:%u", result, hostname, port); } - show_resolve_info(data, dns); - *entry = dns; - return CURLE_OK; } - else if(respwait) { - if(!Curl_resolv_check(data, &dns)) { - *entry = dns; - return dns ? CURLE_OK : CURLE_AGAIN; - } - result = CURLE_COULDNT_RESOLVE_HOST; + else if(cache_dns && *pdns) { + result = Curl_dnscache_add(data, *pdns); + if(result) + Curl_dns_entry_unlink(data, pdns); } -error: - if(dns) - Curl_resolv_unlink(data, &dns); - Curl_async_shutdown(data); - if(result == CURLE_COULDNT_RESOLVE_HOST) - store_negative_resolve(data, hostname, port); - DEBUGASSERT(result); + return result; } CURLcode Curl_resolv_blocking(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **dnsentry) + uint16_t port, + uint8_t transport, + struct Curl_dns_entry **pdns) { CURLcode result; + uint32_t resolv_id; DEBUGASSERT(hostname && *hostname); - *dnsentry = NULL; - result = Curl_resolv(data, hostname, port, ip_version, FALSE, dnsentry); + *pdns = NULL; + /* We cannot do a blocking resolve using DoH currently */ + result = hostip_resolv(data, dns_queries, + hostname, port, transport, FALSE, 0, FALSE, + &resolv_id, pdns); switch(result) { case CURLE_OK: - DEBUGASSERT(*dnsentry); - return CURLE_OK; + DEBUGASSERT(*pdns); + break; +#ifdef USE_CURL_ASYNC case CURLE_AGAIN: - DEBUGASSERT(!*dnsentry); - result = Curl_async_await(data, dnsentry); - if(result || !*dnsentry) { - /* close the connection, since we cannot return failure here without - cleaning up this connection properly. */ - connclose(data->conn, "async resolve failed"); - } - return result; + DEBUGASSERT(!*pdns); + result = Curl_async_await(data, resolv_id, pdns); + Curl_resolv_destroy(data, resolv_id); + break; +#endif default: - return result; + break; } + return result; } #ifdef USE_ALARM_TIMEOUT @@ -980,35 +797,18 @@ } #endif /* USE_ALARM_TIMEOUT */ -/* - * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a - * timeout. This function might return immediately if we are using asynch - * resolves. See the return codes. - * - * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlink() later (when you are - * done using this struct) to decrease the reference counter again. - * - * If built with a synchronous resolver and use of signals is not - * disabled by the application, then a nonzero timeout will cause a - * timeout after the specified number of milliseconds. Otherwise, timeout - * is ignored. - * - * Return codes: - * CURLE_OK = success, *entry set to non-NULL - * CURLE_AGAIN = resolving in progress, *entry == NULL - * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL - * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL - */ - -CURLcode Curl_resolv_timeout(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **entry, - timediff_t timeoutms) -{ #ifdef USE_ALARM_TIMEOUT + +static CURLcode resolv_alarm_timeout(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **entry) +{ #ifdef HAVE_SIGACTION struct sigaction keep_sigact; /* store the old struct here */ volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */ @@ -1020,32 +820,17 @@ #endif /* HAVE_SIGACTION */ volatile long timeout; volatile unsigned int prev_alarm = 0; -#endif /* USE_ALARM_TIMEOUT */ CURLcode result; DEBUGASSERT(hostname && *hostname); - *entry = NULL; - - if(timeoutms < 0) - /* got an already expired timeout */ - return CURLE_OPERATION_TIMEDOUT; - -#ifdef USE_ALARM_TIMEOUT - if(data->set.no_signal) - /* Ignore the timeout when signals are disabled */ - timeout = 0; - else - timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms; - - if(!timeout + DEBUGASSERT(timeout_ms > 0); + DEBUGASSERT(!data->set.no_signal); #ifndef CURL_DISABLE_DOH - || data->set.doh + DEBUGASSERT(!data->set.doh); #endif - ) - /* USE_ALARM_TIMEOUT defined, but no timeout actually requested or resolve - done using DoH */ - return Curl_resolv(data, hostname, port, ip_version, TRUE, entry); + *entry = NULL; + timeout = (timeout_ms > LONG_MAX) ? LONG_MAX : (long)timeout_ms; if(timeout < 1000) { /* The alarm() function only provides integer second resolution, so if we want to wait less than one second we must bail out already now. */ @@ -1096,23 +881,12 @@ prev_alarm = alarm(curlx_sltoui(timeout / 1000L)); } -#else /* !USE_ALARM_TIMEOUT */ -#ifndef CURLRES_ASYNCH - if(timeoutms) - infof(data, "timeout on name lookup is not supported"); -#else - (void)timeoutms; -#endif -#endif /* USE_ALARM_TIMEOUT */ - /* Perform the actual name resolution. This might be interrupted by an - * alarm if it takes too long. - */ - result = Curl_resolv(data, hostname, port, ip_version, TRUE, entry); + * alarm if it takes too long. */ + result = hostip_resolv(data, dns_queries, hostname, port, transport, + for_proxy, timeout_ms, FALSE, presolv_id, entry); -#ifdef USE_ALARM_TIMEOUT clean_up: - if(!prev_alarm) /* deactivate a possibly active alarm before uninstalling the handler */ alarm(0); @@ -1155,449 +929,202 @@ else alarm((unsigned int)alarm_set); } -#endif /* USE_ALARM_TIMEOUT */ return result; } -static void dnscache_entry_free(struct Curl_dns_entry *dns) -{ - Curl_freeaddrinfo(dns->addr); -#ifdef USE_HTTPSRR - if(dns->hinfo) { - Curl_httpsrr_cleanup(dns->hinfo); - curlx_free(dns->hinfo); - } -#endif - curlx_free(dns); -} +#endif /* USE_ALARM_TIMEOUT */ /* - * Curl_resolv_unlink() releases a reference to the given cached DNS entry. - * When the reference count reaches 0, the entry is destroyed. It is important - * that only one unlink is made for each Curl_resolv() call. + * Curl_resolv() is the main name resolve function within libcurl. It resolves + * a name and returns a pointer to the entry in the 'entry' argument. This + * function might return immediately if we are using asynch resolves. See the + * return codes. * - * May be called with 'data' == NULL for global cache. + * The cache entry we return will get its 'inuse' counter increased when this + * function is used. You MUST call Curl_dns_entry_unlink() later (when you are + * done using this struct) to decrease the reference counter again. + * + * If built with a synchronous resolver and use of signals is not + * disabled by the application, then a nonzero timeout will cause a + * timeout after the specified number of milliseconds. Otherwise, timeout + * is ignored. + * + * Return codes: + * CURLE_OK = success, *pdns set to non-NULL + * CURLE_AGAIN = resolving in progress, *pdns == NULL + * any other CURLcode error, *pdns == NULL */ -void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns) +CURLcode Curl_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) { - if(*pdns) { - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = *pdns; - *pdns = NULL; - dnscache_lock(data, dnscache); - dns->refcount--; - if(dns->refcount == 0) - dnscache_entry_free(dns); - dnscache_unlock(data, dnscache); + DEBUGASSERT(hostname && *hostname); + *presolv_id = 0; + *pdns = NULL; + + if(timeout_ms < 0) + /* got an already expired timeout */ + return CURLE_OPERATION_TIMEDOUT; + else if(!timeout_ms) + timeout_ms = CURL_TIMEOUT_RESOLVE_MS; + +#ifdef USE_ALARM_TIMEOUT + if(timeout_ms && data->set.no_signal) { + /* Cannot use ALARM when signals are disabled */ + timeout_ms = 0; } -} - -static void dnscache_entry_dtor(void *entry) -{ - struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry; - DEBUGASSERT(dns && (dns->refcount > 0)); - dns->refcount--; - if(dns->refcount == 0) - dnscache_entry_free(dns); -} - -/* - * Curl_dnscache_init() inits a new DNS cache. - */ -void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size) -{ - Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare, - dnscache_entry_dtor); -} - -void Curl_dnscache_destroy(struct Curl_dnscache *dns) -{ - Curl_hash_destroy(&dns->entries); -} - -CURLcode Curl_loadhostpairs(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct curl_slist *hostp; - - if(!dnscache) - return CURLE_FAILED_INIT; - - /* Default is no wildcard found */ - data->state.wildcard_resolve = FALSE; - - for(hostp = data->state.resolve; hostp; hostp = hostp->next) { - char entry_id[MAX_HOSTCACHE_LEN]; - const char *host = hostp->data; - struct Curl_str source; - if(!host) - continue; - if(*host == '-') { - curl_off_t num = 0; - size_t entry_len; - host++; - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']') || - curlx_str_single(&host, ':')) - continue; - } - else { - if(curlx_str_until(&host, &source, 4096, ':') || - curlx_str_single(&host, ':')) { - continue; - } - } - - if(!curlx_str_number(&host, &num, 0xffff)) { - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), - curlx_strlen(&source), (int)num, - entry_id, sizeof(entry_id)); - dnscache_lock(data, dnscache); - /* delete entry, ignore if it did not exist */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - dnscache_unlock(data, dnscache); - } - } - else { - struct Curl_dns_entry *dns; - struct Curl_addrinfo *head = NULL, *tail = NULL; - size_t entry_len; - char address[64]; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *addresses = NULL; -#endif - curl_off_t port = 0; - bool permanent = TRUE; - bool error = TRUE; - - if(*host == '+') { - host++; - permanent = FALSE; - } - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']')) - continue; - } - else { - if(curlx_str_until(&host, &source, 4096, ':')) - continue; - } - if(curlx_str_single(&host, ':') || - curlx_str_number(&host, &port, 0xffff) || - curlx_str_single(&host, ':')) - goto err; - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - addresses = host; -#endif - - /* start the address section */ - while(*host) { - struct Curl_str target; - struct Curl_addrinfo *ai; - CURLcode result; - - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']')) - goto err; - } - else { - if(curlx_str_until(&host, &target, 4096, ',')) { - if(curlx_str_single(&host, ',')) - goto err; - /* survive nothing but just a comma */ - continue; - } - } -#ifndef USE_IPV6 - if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) { - infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.", - (int)curlx_strlen(&target), curlx_str(&target)); - if(curlx_str_single(&host, ',')) - goto err; - continue; - } -#endif - - if(curlx_strlen(&target) >= sizeof(address)) - goto err; - - memcpy(address, curlx_str(&target), curlx_strlen(&target)); - address[curlx_strlen(&target)] = '\0'; - - result = Curl_str2addr(address, (int)port, &ai); - if(result) { - infof(data, "Resolve address '%s' found illegal", address); - goto err; - } - - if(tail) { - tail->ai_next = ai; - tail = tail->ai_next; - } - else { - head = tail = ai; - } - if(curlx_str_single(&host, ',')) - break; - } - - if(!head) - goto err; - - error = FALSE; -err: - if(error) { - failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data); - Curl_freeaddrinfo(head); - return CURLE_SETOPT_OPTION_SYNTAX; - } - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source), - (int)port, - entry_id, sizeof(entry_id)); - - dnscache_lock(data, dnscache); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - - if(dns) { - infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T - " - old addresses discarded", (int)curlx_strlen(&source), - curlx_str(&source), port); - /* delete old entry, there are two reasons for this - 1. old entry may have different addresses. - 2. even if entry with correct addresses is already in the cache, - but if it is close to expire, then by the time next http - request is made, it can get expired and pruned because old - entry is not necessarily marked as permanent. - 3. when adding a non-permanent entry, we want it to remove and - replace an existing permanent entry. - 4. when adding a non-permanent entry, we want it to get a "fresh" - timeout that starts _now_. */ - - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - - /* put this new host in the cache */ - dns = dnscache_add_addr(data, dnscache, head, curlx_str(&source), - curlx_strlen(&source), (int)port, permanent); - if(dns) - /* release the returned reference; the cache itself will keep the - * entry alive: */ - dns->refcount--; - else - Curl_freeaddrinfo(head); - - dnscache_unlock(data, dnscache); - - if(!dns) - return CURLE_OUT_OF_MEMORY; - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s", - (int)curlx_strlen(&source), curlx_str(&source), port, addresses, - permanent ? "" : " (non-permanent)"); -#endif - - /* Wildcard hostname */ - if(curlx_str_casecompare(&source, "*")) { - infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard", - port); - data->state.wildcard_resolve = TRUE; - } - } + if(timeout_ms && !Curl_doh_wanted(data)) { + return resolv_alarm_timeout(data, dns_queries, hostname, port, transport, + for_proxy, timeout_ms, presolv_id, pdns); } - data->state.resolve = NULL; /* dealt with now */ +#endif /* !USE_ALARM_TIMEOUT */ - return CURLE_OK; +#ifndef CURLRES_ASYNCH + if(timeout_ms) + infof(data, "timeout on name lookup is not supported"); +#endif + + return hostip_resolv(data, dns_queries, hostname, port, transport, + for_proxy, timeout_ms, TRUE, presolv_id, pdns); } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void show_resolve_info(struct Curl_easy *data, - struct Curl_dns_entry *dns) -{ - struct Curl_addrinfo *a; - CURLcode result = CURLE_OK; -#ifdef CURLRES_IPV6 - struct dynbuf out[2]; -#else - struct dynbuf out[1]; -#endif - DEBUGASSERT(data); - DEBUGASSERT(dns); - - if(!data->set.verbose || - /* ignore no name or numerical IP addresses */ - !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) - return; - - a = dns->addr; - - infof(data, "Host %s:%d was resolved.", - (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport); - - curlx_dyn_init(&out[0], 1024); -#ifdef CURLRES_IPV6 - curlx_dyn_init(&out[1], 1024); -#endif - - while(a) { - if( -#ifdef CURLRES_IPV6 - a->ai_family == PF_INET6 || -#endif - a->ai_family == PF_INET) { - char buf[MAX_IPADR_LEN]; - struct dynbuf *d = &out[(a->ai_family != PF_INET)]; - Curl_printable_address(a, buf, sizeof(buf)); - if(curlx_dyn_len(d)) - result = curlx_dyn_addn(d, ", ", 2); - if(!result) - result = curlx_dyn_add(d, buf); - if(result) { - infof(data, "too many IP, cannot show"); - goto fail; - } - } - a = a->ai_next; - } - -#ifdef CURLRES_IPV6 - infof(data, "IPv6: %s", - (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)")); -#endif - infof(data, "IPv4: %s", - (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)")); - -fail: - curlx_dyn_free(&out[0]); -#ifdef CURLRES_IPV6 - curlx_dyn_free(&out[1]); -#endif -} -#endif - #ifdef USE_CURL_ASYNC -CURLcode Curl_resolv_check(struct Curl_easy *data, - struct Curl_dns_entry **dns) + +struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data, + uint32_t resolv_id) { + struct Curl_resolv_async *async = data->state.async; + for(; async; async = async->next) { + if(async->id == resolv_id) + return async; + } + return NULL; +} + +CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); CURLcode result; /* If async resolving is ongoing, this must be set */ - if(!data->state.async.hostname) + if(!async) return CURLE_FAILED_INIT; /* check if we have the name resolved by now (from someone else) */ - *dns = Curl_dnscache_get(data, data->state.async.hostname, - data->state.async.port, - data->state.async.ip_version); - if(*dns) { + result = Curl_dnscache_get(data, async->dns_queries, + async->hostname, async->port, pdns); + if(*pdns) { /* Tell a possibly async resolver we no longer need the results. */ - infof(data, "Hostname '%s' was found in DNS cache", - data->state.async.hostname); - Curl_async_shutdown(data); - data->state.async.dns = *dns; - data->state.async.done = TRUE; + infof(data, "Hostname '%s' was found in DNS cache", async->hostname); + Curl_async_shutdown(data, async); return CURLE_OK; } - -#ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) { - result = Curl_doh_is_resolved(data, dns); - if(result) - Curl_resolver_error(data, NULL); + else if(result) { + Curl_async_shutdown(data, async); + return Curl_async_failed(data, async, NULL); } - else -#endif - result = Curl_async_is_resolved(data, dns); - if(*dns) - show_resolve_info(data, *dns); - if((result == CURLE_COULDNT_RESOLVE_HOST) || - (result == CURLE_COULDNT_RESOLVE_PROXY)) - store_negative_resolve(data, data->state.async.hostname, - data->state.async.port); + + result = hostip_resolv_take_result(data, async, pdns); + + if(*pdns) { + /* Add to cache */ + result = Curl_dnscache_add(data, *pdns); + if(result) + Curl_dns_entry_unlink(data, pdns); + } + else if(IS_RESOLV_FAIL(result)) { + Curl_dnscache_add_negative(data, async->dns_queries, + async->hostname, async->port); + failf(data, "Could not resolve: %s:%u", async->hostname, async->port); + } + else if(result) { + failf(data, "Error %d resolving %s:%u", + result, async->hostname, async->port); + } return result; } -#endif CURLcode Curl_resolv_pollset(struct Curl_easy *data, struct easy_pollset *ps) { -#ifdef CURLRES_ASYNCH -#ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) - /* nothing to wait for during DoH resolve, those handles have their own - sockets */ - return CURLE_OK; -#endif - return Curl_async_pollset(data, ps); -#else - (void)data; + struct Curl_resolv_async *async = data->state.async; + CURLcode result = CURLE_OK; + (void)ps; - return CURLE_OK; + for(; async && !result; async = async->next) { +#ifndef CURL_DISABLE_DOH + if(async->doh) /* DoH has nothing for the pollset */ + continue; #endif -} - -/* Call this function after Curl_connect() has returned async=TRUE and - then a successful name resolve has been received. - - Note: this function disconnects and frees the conn data in case of - resolve failure */ -CURLcode Curl_once_resolved(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_done) -{ - CURLcode result; - struct connectdata *conn = data->conn; - -#ifdef USE_CURL_ASYNC - if(data->state.async.dns) { - DEBUGASSERT(data->state.async.dns == dns); - data->state.async.dns = NULL; - } -#endif - - result = Curl_setup_conn(data, dns, protocol_done); - - if(result) { - Curl_detach_connection(data); - Curl_conn_terminate(data, conn, TRUE); + result = Curl_async_pollset(data, async, ps); } return result; } -/* - * Curl_resolver_error() calls failf() with the appropriate message after a - * resolve error - */ - -#ifdef USE_CURL_ASYNC -CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail) +void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id) { - struct connectdata *conn = data->conn; - const char *host_or_proxy = "host"; - const char *name = conn->host.dispname; - CURLcode result = CURLE_COULDNT_RESOLVE_HOST; + struct Curl_resolv_async **panchor = &data->state.async; -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy) { - host_or_proxy = "proxy"; - result = CURLE_COULDNT_RESOLVE_PROXY; - name = conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : - conn->http_proxy.host.dispname; + for(; *panchor; panchor = &(*panchor)->next) { + struct Curl_resolv_async *async = *panchor; + if(async->id == resolv_id) { + *panchor = async->next; + Curl_async_destroy(data, async); + break; + } } -#endif - - failf(data, "Could not resolve %s: %s%s%s%s", host_or_proxy, name, - detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); - return result; } + +void Curl_resolv_shutdown_all(struct Curl_easy *data) +{ + struct Curl_resolv_async *async = data->state.async; + for(; async; async = async->next) { + Curl_async_shutdown(data, async); + } +} + +void Curl_resolv_destroy_all(struct Curl_easy *data) +{ + while(data->state.async) { + struct Curl_resolv_async *async = data->state.async; + data->state.async = async->next; + Curl_async_destroy(data, async); + } +} + #endif /* USE_CURL_ASYNC */ + +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_resolv_unix(struct Curl_easy *data, + const char *unix_path, + bool abstract_path, + struct Curl_dns_entry **pdns) +{ + struct Curl_addrinfo *addr; + CURLcode result; + + DEBUGASSERT(unix_path); + *pdns = NULL; + + result = Curl_unix2addr(unix_path, abstract_path, &addr); + if(result) { + if(result == CURLE_TOO_LARGE) { + /* Long paths are not supported for now */ + failf(data, "Unix socket path too long: '%s'", unix_path); + result = CURLE_COULDNT_RESOLVE_HOST; + } + return result; + } + + *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0); + return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} +#endif /* USE_UNIX_SOCKETS */
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index 712b8ea..780fb4d 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h
@@ -26,14 +26,7 @@ #include "curl_setup.h" #include "hash.h" -#include "curl_addrinfo.h" #include "curlx/timeval.h" /* for curltime, timediff_t */ -#include "asyn.h" -#include "httpsrr.h" - -#ifdef USE_HTTPSRR -# include <stdint.h> -#endif /* Allocate enough memory to hold the full name information structs and * everything. OSF1 is known to require at least 8872 bytes. The buffer @@ -42,14 +35,31 @@ */ #define CURL_HOSTENT_SIZE 9000 -#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this - many seconds for a name resolve */ +#define CURL_TIMEOUT_RESOLVE_MS (300 * 1000) struct addrinfo; struct hostent; struct Curl_easy; struct connectdata; struct easy_pollset; +struct Curl_https_rrinfo; +struct Curl_multi; +struct Curl_dns_entry; + +/* DNS query types */ +#define CURL_DNSQ_A (1U << 0) +#define CURL_DNSQ_AAAA (1U << 1) +#define CURL_DNSQ_HTTPS (1U << 2) + +#define CURL_DNSQ_ALL (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS) +#define CURL_DNSQ_IP(x) (uint8_t)((x)&(CURL_DNSQ_A | CURL_DNSQ_AAAA)) + +#ifdef CURLVERBOSE +const char *Curl_resolv_query_str(uint8_t dns_queries); +#endif + +/* Return CURL_DNSQ_* bits for the transfer and ip_version. */ +uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version); enum alpnid { ALPN_none = 0, @@ -58,146 +68,108 @@ ALPN_h3 = CURLALTSVC_H3 }; -struct Curl_dns_entry { - struct Curl_addrinfo *addr; -#ifdef USE_HTTPSRR - struct Curl_https_rrinfo *hinfo; -#endif - /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ - struct curltime timestamp; - /* reference counter, entry is freed on reaching 0 */ - size_t refcount; - /* hostname port number that resolved to addr. */ - int hostport; - /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ - char hostname[1]; -}; - -struct Curl_dnscache { - struct Curl_hash entries; -}; - bool Curl_host_is_ipnum(const char *hostname); -/* - * Curl_resolv() returns an entry with the info for the specified host - * and port. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -CURLcode Curl_resolv(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - bool allowDOH, - struct Curl_dns_entry **dnsentry); - -CURLcode Curl_resolv_blocking(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **dnsentry); - -CURLcode Curl_resolv_timeout(struct Curl_easy *data, - const char *hostname, int port, - int ip_version, - struct Curl_dns_entry **dnsentry, - timediff_t timeoutms); - #ifdef USE_IPV6 -/* - * Curl_ipv6works() returns TRUE if IPv6 seems to work. - */ -bool Curl_ipv6works(struct Curl_easy *data); +/* probe if it seems to work */ +CURLcode Curl_probeipv6(struct Curl_multi *multi); #else -#define Curl_ipv6works(x) FALSE +#define Curl_probeipv6(x) CURLE_OK #endif -/* unlink a dns entry, potentially shared with a cache */ -void Curl_resolv_unlink(struct Curl_easy *data, - struct Curl_dns_entry **pdns); - -/* init a new dns cache */ -void Curl_dnscache_init(struct Curl_dnscache *dns, size_t hashsize); - -void Curl_dnscache_destroy(struct Curl_dnscache *dns); - -/* prune old entries from the DNS cache */ -void Curl_dnscache_prune(struct Curl_easy *data); - -/* clear the DNS cache */ -void Curl_dnscache_clear(struct Curl_easy *data); - /* IPv4 thread-safe resolve function used for synch and asynch builds */ -struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); - -CURLcode Curl_once_resolved(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_connect); +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port); /* * Curl_printable_address() returns a printable version of the 1st address - * given in the 'ip' argument. The result will be stored in the buf that is + * given in the 'ai' argument. The result will be stored in the buf that is * bufsize bytes big. */ -void Curl_printable_address(const struct Curl_addrinfo *ip, +void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, size_t bufsize); -/* - * Make a `Curl_dns_entry`. - * Creates a dnscache entry *without* adding it to a dnscache. This allows - * further modifications of the entry *before* then adding it to a cache. - * - * The entry is created with a reference count of 1. - * Use `Curl_resolv_unlink()` to release your hold on it. - * - * The call takes ownership of `addr`and makes a copy of `hostname`. - * - * Returns entry or NULL on OOM. +/* Start DNS resolving for the given parameters. Returns + * - CURLE_OK: `*pdns` is the resolved DNS entry (needs to be unlinked). + * `*presolv_id` is 0. + * - CURLE_AGAIN: resolve is asynchronous and not finished yet. + * `presolv_id` is the identifier for querying results later. + * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. */ -struct Curl_dns_entry * -Curl_dnscache_mk_entry(struct Curl_easy *data, - struct Curl_addrinfo *addr, - const char *hostname, - size_t hostlen, /* length or zero */ - int port, - bool permanent); +CURLcode Curl_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns); -/* - * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. - * - * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -struct Curl_dns_entry *Curl_dnscache_get(struct Curl_easy *data, - const char *hostname, int port, - int ip_version); +CURLcode Curl_resolv_blocking(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + struct Curl_dns_entry **pdns); -/* - * Curl_dnscache_addr() adds `entry` to the cache, increasing its - * reference count on success. - */ -CURLcode Curl_dnscache_add(struct Curl_easy *data, - struct Curl_dns_entry *entry); - -/* - * Populate the cache with specified entries from CURLOPT_RESOLVE. - */ -CURLcode Curl_loadhostpairs(struct Curl_easy *data); +/* Announce start of a resolve operation to application callback, + * passing the resolver implementation (maybe NULL). */ +CURLcode Curl_resolv_announce_start(struct Curl_easy *data, + void *resolver); #ifdef USE_CURL_ASYNC -CURLcode Curl_resolv_check(struct Curl_easy *data, - struct Curl_dns_entry **dns); -#else -#define Curl_resolv_check(x, y) CURLE_NOT_BUILT_IN -#endif + CURLcode Curl_resolv_pollset(struct Curl_easy *data, struct easy_pollset *ps); -CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail); +/* Get the `async` struct for the given `resolv_id`, if it exists. */ +struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data, + uint32_t resolv_id); + +/* Shut down all resolves of the given easy handle. */ +void Curl_resolv_shutdown_all(struct Curl_easy *data); + +/* Destroy all resolve resources of the given easy handle. */ +void Curl_resolv_destroy_all(struct Curl_easy *data); + +CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns); + +void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id); + +/* How much time has gone by since start of resolve. + * Returns CURL_TIMEOUT_RESOLVE_MS if `resolv_id` is no longer valid. */ +timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data, + uint32_t resolv_id); + +/* Return TRUE if `resolv_id` has answers (positive or negative) to + * all queries in `dns_queries`. + * Queries not requested are considered answered. */ +bool Curl_resolv_has_answers(struct Curl_easy *data, + uint32_t resolv_id, uint8_t dns_queries); + +const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, + uint32_t resolv_id, + int ai_family, + unsigned int index); +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_resolv_get_https(struct Curl_easy *data, + uint32_t resolv_id); +bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id); +#endif /* USE_HTTPSRR */ + +#else /* !USE_CURL_ASYNC */ +#define Curl_resolv_shutdown_all(x) Curl_nop_stmt +#define Curl_resolv_destroy_all(x) Curl_nop_stmt +#define Curl_resolv_take_result(x, y, z) CURLE_NOT_BUILT_IN +#define Curl_resolv_elapsed_ms(x, y) CURL_TIMEOUT_RESOLVE_MS +#define Curl_resolv_has_answers(x, y, z) TRUE +#define Curl_resolv_get_ai(x, y, z, a) NULL +#define Curl_resolv_get_https(x, y) NULL +#define Curl_resolv_knows_https(x, y) TRUE +#define Curl_resolv_pollset(x, y) CURLE_OK +#define Curl_resolv_destroy(x, y) Curl_nop_stmt +#endif /* USE_CURL_ASYNC */ #ifdef CURLRES_SYNCH /* @@ -206,10 +178,17 @@ * support and platform. */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version); + uint16_t port, + uint8_t transport); +#endif +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_resolv_unix(struct Curl_easy *data, + const char *unix_path, + bool abstract_path, + struct Curl_dns_entry **pdns); #endif #endif /* HEADER_CURL_HOSTIP_H */
diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c index 422e0b2..fb35e39 100644 --- a/Utilities/cmcurl/lib/hostip4.c +++ b/Utilities/cmcurl/lib/hostip4.c
@@ -43,6 +43,7 @@ #endif #include "urldata.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" #include "url.h" @@ -67,16 +68,15 @@ * */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version) + uint16_t port, + uint8_t transport) { struct Curl_addrinfo *ai = NULL; - (void)ip_version; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif + (void)dns_queries; + (void)transport; ai = Curl_ipv4_resolve_r(hostname, port); if(!ai) @@ -87,7 +87,8 @@ #endif /* CURLRES_SYNCH */ #endif /* CURLRES_IPV4 */ -#if defined(CURLRES_IPV4) && !defined(CURLRES_ARES) && !defined(CURLRES_AMIGA) +#if defined(CURLRES_IPV4) && !defined(USE_RESOLV_ARES) && \ + !defined(CURLRES_AMIGA) /* * Curl_ipv4_resolve_r() - ipv4 thread-safe resolver function. @@ -97,7 +98,7 @@ * */ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, - int port) + uint16_t port) { #if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) && \ defined(HAVE_GETHOSTBYNAME_R_3) @@ -279,4 +280,4 @@ return ai; } -#endif /* CURLRES_IPV4 && !CURLRES_ARES && !CURLRES_AMIGA */ +#endif /* CURLRES_IPV4 && !USE_RESOLV_ARES && !CURLRES_AMIGA */
diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c index e4793d7..7412f42 100644 --- a/Utilities/cmcurl/lib/hostip6.c +++ b/Utilities/cmcurl/lib/hostip6.c
@@ -44,6 +44,7 @@ #include "urldata.h" #include "cfilters.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" #include "url.h" @@ -52,23 +53,6 @@ #ifdef CURLRES_SYNCH -#ifdef DEBUG_ADDRINFO -static void dump_addrinfo(const struct Curl_addrinfo *ai) -{ - curl_mprintf("dump_addrinfo:\n"); - for(; ai; ai = ai->ai_next) { - char buf[INET6_ADDRSTRLEN]; - curl_mprintf(" fam %2d, CNAME %s, ", - ai->ai_family, - ai->ai_canonname ? ai->ai_canonname : "<none>"); - Curl_printable_address(ai, buf, sizeof(buf)); - curl_mprintf("%s\n", buf); - } -} -#else -#define dump_addrinfo(x) Curl_nop_stmt -#endif - /* * Curl_sync_getaddrinfo() when built IPv6-enabled (non-threading and * non-ares version). @@ -79,9 +63,10 @@ * Curl_freeaddrinfo(), nothing else. */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version) + uint16_t port, + uint8_t transport) { struct addrinfo hints; struct Curl_addrinfo *res; @@ -93,14 +78,12 @@ #endif int pf = PF_INET; - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) - /* The stack seems to be IPv6-enabled */ + if(dns_queries & CURL_DNSQ_AAAA) pf = PF_UNSPEC; memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? + hints.ai_socktype = (transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; #ifndef USE_RESOLVE_ON_IPS @@ -130,8 +113,6 @@ Curl_addrinfo_set_port(res, port); } - dump_addrinfo(res); - return res; } #endif /* CURLRES_SYNCH */
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c index de6c573..400b442 100644 --- a/Utilities/cmcurl/lib/hsts.c +++ b/Utilities/cmcurl/lib/hsts.c
@@ -40,7 +40,7 @@ #define MAX_HSTS_LINE 4095 #define MAX_HSTS_HOSTLEN 2048 -#define MAX_HSTS_DATELEN 256 +#define MAX_HSTS_DATELEN 17 #define UNLIMITED "unlimited" #if defined(DEBUGBUILD) || defined(UNITTESTS) @@ -91,6 +91,20 @@ } } +/* append the new entry to the list after possibly removing an old entry + first */ +static void hsts_append(struct hsts *h, struct stsentry *sts) +{ + if(Curl_llist_count(&h->list) == MAX_HSTS_ENTRIES) { + /* It's full. Remove the first entry in the list */ + struct Curl_llist_node *e = Curl_llist_head(&h->list); + struct stsentry *oldsts = Curl_node_elem(e); + Curl_node_remove(e); + hsts_free(oldsts); + } + Curl_llist_append(&h->list, sts, &sts->node); +} + static CURLcode hsts_create(struct hsts *h, const char *hostname, size_t hlen, @@ -111,7 +125,7 @@ memcpy(sts->host, hostname, hlen); sts->expires = expires; sts->includeSubDomains = subdomains; - Curl_llist_append(&h->list, sts, &sts->node); + hsts_append(h, sts); } return CURLE_OK; } @@ -206,7 +220,7 @@ /* check if it already exists */ sts = Curl_hsts(h, hostname, hlen, FALSE); if(sts) { - /* just update these fields */ + /* update these fields */ sts->expires = expires; sts->includeSubDomains = subdomains; } @@ -294,7 +308,7 @@ stamp.tm_hour, stamp.tm_min, stamp.tm_sec); } else - curlx_strcopy(e.expire, sizeof(e.expire), UNLIMITED, strlen(UNLIMITED)); + curlx_strcopy(e.expire, sizeof(e.expire), STRCONST(UNLIMITED)); sc = data->set.hsts_write(data, &e, i, data->set.hsts_write_userp); *stop = (sc != CURLSTS_OK); @@ -318,7 +332,7 @@ } else curl_mfprintf(fp, "%s%s \"%s\"\n", - sts->includeSubDomains ? ".": "", sts->host, UNLIMITED); + sts->includeSubDomains ? "." : "", sts->host, UNLIMITED); return CURLE_OK; } @@ -386,6 +400,61 @@ } /* only returns SERIOUS errors */ +static CURLcode hsts_add_host_expire(struct hsts *h, + const char *host, size_t hostlen, + const char *expire, size_t explen, + bool subdomain) /* default */ +{ + CURLcode result = CURLE_OK; + struct stsentry *e; + char dbuf[MAX_HSTS_DATELEN + 1]; + time_t expires = 0; + time_t now = time(NULL); + + /* The date parser works on a null-terminated string. */ + if(explen > MAX_HSTS_DATELEN) + return CURLE_BAD_FUNCTION_ARGUMENT; + memcpy(dbuf, expire, explen); + dbuf[explen] = 0; + + if(!strcmp(dbuf, UNLIMITED)) + expires = TIME_T_MAX; + else + Curl_getdate_capped(dbuf, &expires); + + if(expires <= now) + /* this entry already expired */ + return CURLE_OK; + + if(host[0] == '.') { + host++; + hostlen--; + subdomain = TRUE; + } + if(hostlen && (host[hostlen - 1] == '.')) + /* strip off any trailing dot */ + hostlen--; + + if(hostlen) { + /* only add it if not already present */ + e = Curl_hsts(h, host, hostlen, subdomain); + if(!e) + result = hsts_create(h, host, hostlen, subdomain, expires); + /* 'host' is not necessarily null terminated */ + else if((hostlen == strlen(e->host) && + curl_strnequal(host, e->host, hostlen))) { + /* the same hostname, use the largest expire time and keep the strictest + subdomain policy */ + if(expires > e->expires) + e->expires = expires; + if(subdomain) + e->includeSubDomains = TRUE; + } + } + return result; +} + +/* only returns SERIOUS errors */ static CURLcode hsts_add(struct hsts *h, const char *line) { /* Example lines: @@ -401,39 +470,9 @@ curlx_str_newline(&line)) ; else { - CURLcode result = CURLE_OK; - bool subdomain = FALSE; - struct stsentry *e; - char dbuf[MAX_HSTS_DATELEN + 1]; - time_t expires = 0; - const char *hp = curlx_str(&host); - - /* The date parser works on a null-terminated string. The maximum length - is upheld by curlx_str_quotedword(). */ - memcpy(dbuf, curlx_str(&date), curlx_strlen(&date)); - dbuf[curlx_strlen(&date)] = 0; - - if(!strcmp(dbuf, UNLIMITED)) - expires = TIME_T_MAX; - else - Curl_getdate_capped(dbuf, &expires); - - if(hp[0] == '.') { - curlx_str_nudge(&host, 1); - subdomain = TRUE; - } - /* only add it if not already present */ - e = Curl_hsts(h, curlx_str(&host), curlx_strlen(&host), subdomain); - if(!e) - result = hsts_create(h, curlx_str(&host), curlx_strlen(&host), - subdomain, expires); - else if(curlx_str_casecompare(&host, e->host)) { - /* the same hostname, use the largest expire time */ - if(expires > e->expires) - e->expires = expires; - } - if(result) - return result; + return hsts_add_host_expire(h, curlx_str(&host), curlx_strlen(&host), + curlx_str(&date), curlx_strlen(&date), + FALSE); } return CURLE_OK; @@ -456,23 +495,23 @@ e.namelen = sizeof(buffer) - 1; e.includeSubDomains = FALSE; /* default */ e.expire[0] = 0; - e.name[0] = 0; /* just to make it clean */ + e.expire[MAX_HSTS_DATELEN] = 0; + e.name[0] = 0; /* to make it clean */ + e.name[MAX_HSTS_HOSTLEN] = 0; sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp); if(sc == CURLSTS_OK) { - time_t expires = 0; CURLcode result; - DEBUGASSERT(e.name[0]); - if(!e.name[0]) - /* bail out if no name was stored */ + const char *date = e.expire; + if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] || + e.name[MAX_HSTS_HOSTLEN]) + /* bail out if no name was stored or if a null terminator is gone */ return CURLE_BAD_FUNCTION_ARGUMENT; - if(e.expire[0]) - Curl_getdate_capped(e.expire, &expires); - else - expires = TIME_T_MAX; /* the end of time */ - result = hsts_create(h, e.name, strlen(e.name), - /* bitfield to bool conversion: */ - e.includeSubDomains ? TRUE : FALSE, - expires); + if(!date[0]) + date = UNLIMITED; + result = hsts_add_host_expire(h, e.name, strlen(e.name), + date, strlen(date), + /* bitfield to bool conversion: */ + e.includeSubDomains ? TRUE : FALSE); if(result) return result; } @@ -505,26 +544,28 @@ fp = curlx_fopen(file, FOPEN_READTEXT); if(fp) { - struct dynbuf buf; - bool eof = FALSE; - curlx_dyn_init(&buf, MAX_HSTS_LINE); - do { - result = Curl_get_line(&buf, fp, &eof); - if(!result) { - const char *lineptr = curlx_dyn_ptr(&buf); - curlx_str_passblanks(&lineptr); + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + struct dynbuf buf; + bool eof = FALSE; + curlx_dyn_init(&buf, MAX_HSTS_LINE); + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + curlx_str_passblanks(&lineptr); - /* - * Skip empty or commented lines, since we know the line will have a - * trailing newline from Curl_get_line we can treat length 1 as empty. - */ - if((*lineptr == '#') || strlen(lineptr) <= 1) - continue; + /* Skip empty or commented lines, since we know the line will have + a trailing newline from Curl_get_line we can treat length 1 as + empty. */ + if((*lineptr == '#') || strlen(lineptr) <= 1) + continue; - hsts_add(h, lineptr); - } - } while(!result && !eof); - curlx_dyn_free(&buf); /* free the line buffer */ + hsts_add(h, lineptr); + } + } while(!result && !eof); + curlx_dyn_free(&buf); /* free the line buffer */ + } curlx_fclose(fp); } return result;
diff --git a/Utilities/cmcurl/lib/hsts.h b/Utilities/cmcurl/lib/hsts.h index e2c9922..0e6585f 100644 --- a/Utilities/cmcurl/lib/hsts.h +++ b/Utilities/cmcurl/lib/hsts.h
@@ -28,6 +28,8 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) #include "llist.h" +#define MAX_HSTS_ENTRIES 10000 + #if defined(DEBUGBUILD) || defined(UNITTESTS) extern time_t deltatime; #endif @@ -49,7 +51,7 @@ struct hsts *Curl_hsts_init(void); void Curl_hsts_cleanup(struct hsts **hp); CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, - const char *sts); + const char *header); struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, size_t hlen, bool subdomain); CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index a78e2f6..9118c7e 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c
@@ -22,6 +22,7 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_HTTP @@ -46,7 +47,6 @@ #include <sys/param.h> #endif -#include "urldata.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" @@ -77,7 +77,7 @@ #include "http2.h" #include "cfilters.h" #include "connect.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "altsvc.h" #include "hsts.h" #include "rtsp.h" @@ -85,94 +85,6 @@ #include "bufref.h" #include "curlx/strparse.h" -/* - * Forward declarations. - */ - -static bool http_should_fail(struct Curl_easy *data, int httpcode); -static bool http_exp100_is_waiting(struct Curl_easy *data); -static CURLcode http_exp100_add_reader(struct Curl_easy *data); -static void http_exp100_send_anyway(struct Curl_easy *data); -static bool http_exp100_is_selected(struct Curl_easy *data); -static void http_exp100_got100(struct Curl_easy *data); -static CURLcode http_firstwrite(struct Curl_easy *data); -static CURLcode http_header(struct Curl_easy *data, - const char *hd, size_t hdlen); -static CURLcode http_range(struct Curl_easy *data, - Curl_HttpReq httpreq); -static CURLcode http_req_set_TE(struct Curl_easy *data, - struct dynbuf *req, - int httpversion); -static CURLcode http_size(struct Curl_easy *data); -static CURLcode http_statusline(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req); -static CURLcode http_useragent(struct Curl_easy *data); -static CURLcode http_write_header(struct Curl_easy *data, - const char *hd, size_t hdlen); - -/* - * HTTP handler interface. - */ -const struct Curl_handler Curl_handler_http = { - "http", /* scheme */ - Curl_http_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - Curl_http_doing_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - Curl_http_perform_pollset, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - Curl_http_write_resp, /* write_resp */ - Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - Curl_http_follow, /* follow */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTP, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE - -}; - -#ifdef USE_SSL -/* - * HTTPS handler interface. - */ -const struct Curl_handler Curl_handler_https = { - "https", /* scheme */ - Curl_http_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - NULL, /* connecting */ - ZERO_NULL, /* doing */ - NULL, /* proto_pollset */ - Curl_http_doing_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - Curl_http_perform_pollset, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - Curl_http_write_resp, /* write_resp */ - Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - Curl_http_follow, /* follow */ - PORT_HTTPS, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE -}; - -#endif - void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg) { memset(neg, 0, sizeof(*neg)); @@ -252,22 +164,31 @@ return NULL; } -#else -/* disabled */ -#define Curl_checkProxyheaders(x, y, z, a) NULL #endif +/* If the header has a value, this function returns TRUE and the value is in + 'outp' with blanks trimmed off. +*/ +static bool header_has_value(const char **headerp, struct Curl_str *outp) +{ + bool value = !curlx_str_cspn(headerp, outp, ";:") && + (!curlx_str_single(headerp, ':') || !curlx_str_single(headerp, ';')); + + if(value) { + curlx_str_untilnl(headerp, outp, MAX_HTTP_RESP_HEADER_SIZE); + curlx_str_trimblanks(outp); + } + return value; +} + static bool http_header_is_empty(const char *header) { struct Curl_str out; - if(!curlx_str_cspn(&header, &out, ";:") && - (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) { - curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE); - curlx_str_trimblanks(&out); + if(header_has_value(&header, &out)) { return curlx_strlen(&out) == 0; } - return TRUE; /* invalid head format, treat as empty */ + return TRUE; /* invalid header format, treat as empty */ } /* @@ -283,12 +204,8 @@ struct Curl_str out; /* find the end of the header name */ - if(!curlx_str_cspn(&header, &out, ";:") && - (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) { - curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE); - curlx_str_trimblanks(&out); - - *valp = Curl_memdup0(curlx_str(&out), curlx_strlen(&out)); + if(header_has_value(&header, &out)) { + *valp = curlx_memdup0(curlx_str(&out), curlx_strlen(&out)); if(*valp) return CURLE_OK; return CURLE_OUT_OF_MEMORY; @@ -305,6 +222,8 @@ * * This function MUST be used after the header has already been confirmed to * lead with "word:". + * + * @unittest: 1626 */ char *Curl_copy_header_value(const char *header) { @@ -315,7 +234,7 @@ !curlx_str_single(&header, ':')) { curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE); curlx_str_trimblanks(&out); - return Curl_memdup0(curlx_str(&out), curlx_strlen(&out)); + return curlx_memdup0(curlx_str(&out), curlx_strlen(&out)); } /* bad input, should never happen */ DEBUGASSERT(0); @@ -345,7 +264,7 @@ connection */ if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = &data->state.aptr.proxyuserpwd; + userp = &data->req.proxyuserpwd; user = data->state.aptr.proxyuser; pwd = data->state.aptr.proxypasswd; #else @@ -353,7 +272,7 @@ #endif } else { - userp = &data->state.aptr.userpwd; + userp = &data->req.userpwd; user = data->state.aptr.user; pwd = data->state.aptr.passwd; } @@ -401,7 +320,7 @@ char **userp; CURLcode result = CURLE_OK; - userp = &data->state.aptr.userpwd; + userp = &data->req.userpwd; curlx_free(*userp); *userp = curl_maprintf("Authorization: Bearer %s\r\n", data->set.str[STRING_BEARER]); @@ -419,7 +338,7 @@ #endif -/* pickoneauth() selects the most favourable authentication method from the +/* pickoneauth() selects the most favorable authentication method from the * ones available and the ones we want. * * return TRUE if one was picked @@ -482,10 +401,10 @@ * amount remains. This may be overridden by authentications further * below! */ bool abort_upload = (!data->req.upload_done && !little_upload_remains); - const char *ongoing_auth = NULL; + VERBOSE(const char *ongoing_auth = NULL); /* We need a rewind before uploading client read data again. The - * checks below just influence of the upload is to be continued + * checks below influence of the upload is to be continued * or aborted early. * This depends on how much remains to be sent and in what state * the authentication is. Some auth schemes such as NTLM do not work @@ -504,7 +423,7 @@ #ifdef USE_NTLM if((data->state.authproxy.picked == CURLAUTH_NTLM) || (data->state.authhost.picked == CURLAUTH_NTLM)) { - ongoing_auth = "NTLM"; + VERBOSE(ongoing_auth = "NTLM"); if((conn->http_ntlm_state != NTLMSTATE_NONE) || (conn->proxy_ntlm_state != NTLMSTATE_NONE)) { /* The NTLM-negotiation has started, keep on sending. @@ -517,7 +436,7 @@ /* There is still data left to send */ if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) || (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) { - ongoing_auth = "NEGOTIATE"; + VERBOSE(ongoing_auth = "NEGOTIATE"); if((conn->http_negotiate_state != GSS_AUTHNONE) || (conn->proxy_negotiate_state != GSS_AUTHNONE)) { /* The NEGOTIATE-negotiation has started, keep on sending. @@ -547,20 +466,90 @@ return CURLE_OK; } +/** + * http_should_fail() determines whether an HTTP response code has gotten us + * into an error state or not. + * + * @retval FALSE communications should continue + * + * @retval TRUE communications should not continue + */ +static bool http_should_fail(struct Curl_easy *data, int httpcode) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + + /* + * If we have not been asked to fail on error, + * do not fail. + */ + if(!data->set.http_fail_on_error) + return FALSE; + + /* + * Any code < 400 is never terminal. + */ + if(httpcode < 400) + return FALSE; + + /* + * A 416 response to a resume request is presumably because the file is + * already completely downloaded and thus not actually a fail. + */ + if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && + httpcode == 416) + return FALSE; + + /* + * Any code >= 400 that is not 401 or 407 is always + * a terminal error + */ + if((httpcode != 401) && (httpcode != 407)) + return TRUE; + + /* + * All we have left to deal with is 401 and 407 + */ + DEBUGASSERT((httpcode == 401) || (httpcode == 407)); + + /* + * Examine the current authentication state to see if this is an error. The + * idea is for this function to get called after processing all the headers + * in a response message. If we have been to asked to authenticate + * a particular stage, and we have done it, we are OK. If we are already + * completely authenticated, it is not OK to get another 401 or 407. + * + * It is possible for authentication to go stale such that the client needs + * to reauthenticate. Once that info is available, use it here. + */ + + /* + * Either we are not authenticating, or we are supposed to be authenticating + * something else. This is an error. + */ + if((httpcode == 401) && !data->state.aptr.user) + return TRUE; +#ifndef CURL_DISABLE_PROXY + if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) + return TRUE; +#endif + + return (bool)data->state.authproblem; +} + /* * Curl_http_auth_act() gets called when all HTTP headers have been received * and it checks what authentication methods that are available and decides * which one (if any) to use. It will set 'newurl' if an auth method was * picked. */ - CURLcode Curl_http_auth_act(struct Curl_easy *data) { struct connectdata *conn = data->conn; bool pickhost = FALSE; bool pickproxy = FALSE; CURLcode result = CURLE_OK; - unsigned long authmask = ~0ul; + unsigned long authmask = ~0UL; if(!data->set.str[STRING_BEARER]) authmask &= (unsigned long)~CURLAUTH_BEARER; @@ -705,11 +694,12 @@ /* Basic */ if( #ifndef CURL_DISABLE_PROXY - (proxy && conn->bits.proxy_user_passwd && - !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || + (proxy && conn->bits.proxy_user_passwd && + !Curl_checkProxyheaders(data, conn, + STRCONST("Proxy-authorization"))) || #endif - (!proxy && data->state.aptr.user && - !Curl_checkheaders(data, STRCONST("Authorization")))) { + (!proxy && data->state.aptr.user && + !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Basic"; result = http_output_basic(data, proxy); if(result) @@ -724,8 +714,9 @@ #ifndef CURL_DISABLE_BEARER_AUTH if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ - if((!proxy && data->set.str[STRING_BEARER] && - !Curl_checkheaders(data, STRCONST("Authorization")))) { + if(!proxy && data->set.str[STRING_BEARER] && + Curl_auth_allowed_to_host(data) && + !Curl_checkheaders(data, STRCONST("Authorization"))) { auth = "Bearer"; result = http_output_bearer(data); if(result) @@ -740,6 +731,10 @@ if(auth) { #ifndef CURL_DISABLE_PROXY + if(proxy) + data->info.proxyauthpicked = authstatus->picked; + else + data->info.httpauthpicked = authstatus->picked; infof(data, "%s auth using %s with user '%s'", proxy ? "Proxy" : "Server", auth, proxy ? (data->state.aptr.proxyuser ? @@ -754,8 +749,13 @@ #endif authstatus->multipass = !authstatus->done; } - else + else { authstatus->multipass = FALSE; + if(proxy) + data->info.proxyauthpicked = 0; + else + data->info.httpauthpicked = 0; + } return result; } @@ -774,14 +774,14 @@ * * @returns CURLcode */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel) /* TRUE if this is the request setting - up the proxy tunnel */ +CURLcode Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel) /* TRUE if this is + the request setting up + the proxy tunnel */ { CURLcode result = CURLE_OK; struct auth *authhost; @@ -824,7 +824,7 @@ #ifndef CURL_DISABLE_PROXY /* Send proxy authentication header if needed */ if(conn->bits.httpproxy && - (conn->bits.tunnel_proxy == (bit)proxytunnel)) { + (conn->bits.tunnel_proxy == (curl_bit)proxytunnel)) { result = output_auth_headers(data, conn, authproxy, request, path, TRUE); if(result) return result; @@ -898,7 +898,7 @@ bool proxy, const char *auth, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { if((authp->avail & CURLAUTH_NEGOTIATE) || Curl_auth_is_spnego_supported()) { *availp |= CURLAUTH_NEGOTIATE; @@ -931,7 +931,7 @@ bool proxy, const char *auth, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { /* NTLM support requires the SSL crypto libs */ if((authp->avail & CURLAUTH_NTLM) || Curl_auth_is_ntlm_supported()) { @@ -960,10 +960,12 @@ bool proxy, const char *auth, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { - if(authp->avail & CURLAUTH_DIGEST) + if(authp->avail & CURLAUTH_DIGEST) { + *availp |= CURLAUTH_DIGEST; infof(data, "Ignoring duplicate digest auth header."); + } else if(Curl_auth_is_digest_supported()) { CURLcode result; @@ -989,14 +991,13 @@ #ifndef CURL_DISABLE_BASIC_AUTH static CURLcode auth_basic(struct Curl_easy *data, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { *availp |= CURLAUTH_BASIC; authp->avail |= CURLAUTH_BASIC; if(authp->picked == CURLAUTH_BASIC) { - /* We asked for Basic authentication but got a 40X back - anyway, which basically means our name+password is not - valid. */ + /* We asked for Basic authentication but got a 40X back anyway, which + means our name+password is not valid. */ authp->avail = CURLAUTH_NONE; infof(data, "Basic authentication problem, ignoring."); data->state.authproblem = TRUE; @@ -1008,13 +1009,13 @@ #ifndef CURL_DISABLE_BEARER_AUTH static CURLcode auth_bearer(struct Curl_easy *data, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { *availp |= CURLAUTH_BEARER; authp->avail |= CURLAUTH_BEARER; if(authp->picked == CURLAUTH_BEARER) { - /* We asked for Bearer authentication but got a 40X back - anyway, which basically means our token is not valid. */ + /* We asked for Bearer authentication but got a 40X back anyway, which + means our token is not valid. */ authp->avail = CURLAUTH_NONE; infof(data, "Bearer authentication problem, ignoring."); data->state.authproblem = TRUE; @@ -1042,7 +1043,7 @@ !defined(CURL_DISABLE_BASIC_AUTH) || \ !defined(CURL_DISABLE_BEARER_AUTH) - unsigned long *availp; + uint32_t *availp; struct auth *authp; CURLcode result = CURLE_OK; DEBUGASSERT(auth); @@ -1116,82 +1117,13 @@ #endif } -/** - * http_should_fail() determines whether an HTTP response code has gotten us - * into an error state or not. - * - * @retval FALSE communications should continue - * - * @retval TRUE communications should not continue - */ -static bool http_should_fail(struct Curl_easy *data, int httpcode) -{ - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - - /* - ** If we have not been asked to fail on error, - ** do not fail. - */ - if(!data->set.http_fail_on_error) - return FALSE; - - /* - ** Any code < 400 is never terminal. - */ - if(httpcode < 400) - return FALSE; - - /* - ** A 416 response to a resume request is presumably because the file is - ** already completely downloaded and thus not actually a fail. - */ - if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && - httpcode == 416) - return FALSE; - - /* - ** Any code >= 400 that is not 401 or 407 is always - ** a terminal error - */ - if((httpcode != 401) && (httpcode != 407)) - return TRUE; - - /* - ** All we have left to deal with is 401 and 407 - */ - DEBUGASSERT((httpcode == 401) || (httpcode == 407)); - - /* - ** Examine the current authentication state to see if this is an error. The - ** idea is for this function to get called after processing all the headers - ** in a response message. So, if we have been to asked to authenticate a - ** particular stage, and we have done it, we are OK. If we are already - ** completely authenticated, it is not OK to get another 401 or 407. - ** - ** It is possible for authentication to go stale such that the client needs - ** to reauthenticate. Once that info is available, use it here. - */ - - /* - ** Either we are not authenticating, or we are supposed to be authenticating - ** something else. This is an error. - */ - if((httpcode == 401) && !data->state.aptr.user) - return TRUE; -#ifndef CURL_DISABLE_PROXY - if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) - return TRUE; -#endif - - return data->state.authproblem; -} - static void http_switch_to_get(struct Curl_easy *data, int code) { const char *req = data->set.str[STRING_CUSTOMREQUEST]; + if((req || data->state.httpreq != HTTPREQ_GET) && (data->set.http_follow_mode == CURLFOLLOW_OBEYCODE)) { + NOVERBOSE((void)code); infof(data, "Switch to GET because of %d response", code); data->state.http_ignorecustom = TRUE; } @@ -1202,6 +1134,11 @@ Curl_creader_set_rewind(data, FALSE); } +#define HTTPREQ_IS_POST(data) \ + ((data)->state.httpreq == HTTPREQ_POST || \ + (data)->state.httpreq == HTTPREQ_POST_FORM || \ + (data)->state.httpreq == HTTPREQ_POST_MIME) + CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, followtype type) { @@ -1284,66 +1221,49 @@ } /* the URL could not be parsed for some reason, but since this is FAKE - mode, just duplicate the field as-is */ + mode, duplicate the field as-is */ follow_url = curlx_strdup(newurl); if(!follow_url) return CURLE_OUT_OF_MEMORY; } else { - uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0); - if(uc) + bool same_origin; + CURLcode result; + CURLU *u = curl_url(); + if(!u) + return CURLE_OUT_OF_MEMORY; + uc = curl_url_set(u, CURLUPART_URL, + Curl_bufref_ptr(&data->state.url), + CURLU_URLENCODE | CURLU_ALLOW_SPACE); + if(!uc) + uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0); + if(uc) { + curl_url_cleanup(u); return Curl_uc_to_curlcode(uc); + } - /* Clear auth if this redirects to a different port number or protocol, - unless permitted */ - if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { - int port; - bool clear = FALSE; + same_origin = Curl_url_same_origin(u, data->state.uh); + curl_url_cleanup(u); - if(data->set.use_port && data->state.allow_port) - /* a custom port is used */ - port = (int)data->set.use_port; - else { - curl_off_t value; - char *portnum; - const char *p; - uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, - CURLU_DEFAULT_PORT); - if(uc) { - curlx_free(follow_url); - return Curl_uc_to_curlcode(uc); - } - p = portnum; - curlx_str_number(&p, &value, 0xffff); - port = (int)value; - curlx_free(portnum); - } - if(port != data->info.conn_remote_port) { - infof(data, "Clear auth, redirects to port from %u to %u", - data->info.conn_remote_port, port); - clear = TRUE; - } - else { - char *scheme; - const struct Curl_handler *p; - uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - curlx_free(follow_url); - return Curl_uc_to_curlcode(uc); - } +#ifndef CURL_DISABLE_DIGEST_AUTH + if(!same_origin) + Curl_auth_digest_cleanup(&data->state.digest); +#endif - p = Curl_get_scheme_handler(scheme); - if(p && (p->protocol != data->info.conn_protocol)) { - infof(data, "Clear auth, redirects scheme from %s to %s", - data->info.conn_scheme, scheme); - clear = TRUE; - } - curlx_free(scheme); + if((!same_origin && !data->set.allow_auth_to_other_hosts) || + !data->set.str[STRING_USERNAME]) { + result = Curl_reset_userpwd(data); + if(result) { + curlx_free(follow_url); + return result; } - if(clear) { - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); - } + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); + } + result = Curl_reset_proxypwd(data); + if(result) { + curlx_free(follow_url); + return result; } } DEBUGASSERT(follow_url); @@ -1410,10 +1330,7 @@ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and * can be overridden with CURLOPT_POSTREDIR. */ - if((data->state.httpreq == HTTPREQ_POST || - data->state.httpreq == HTTPREQ_POST_FORM || - data->state.httpreq == HTTPREQ_POST_MIME) && - !(data->set.keep_post & CURL_REDIR_POST_301)) { + if(HTTPREQ_IS_POST(data) && !data->set.post301) { http_switch_to_get(data, 301); switch_to_get = TRUE; } @@ -1435,10 +1352,7 @@ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and * can be overridden with CURLOPT_POSTREDIR. */ - if((data->state.httpreq == HTTPREQ_POST || - data->state.httpreq == HTTPREQ_POST_FORM || - data->state.httpreq == HTTPREQ_POST_MIME) && - !(data->set.keep_post & CURL_REDIR_POST_302)) { + if(HTTPREQ_IS_POST(data) && !data->set.post302) { http_switch_to_get(data, 302); switch_to_get = TRUE; } @@ -1448,13 +1362,8 @@ /* 'See Other' location is not the resource but a substitute for the * resource. In this case we switch the method to GET/HEAD, unless the * method is POST and the user specified to keep it as POST. - * https://github.com/curl/curl/issues/5237#issuecomment-614641049 */ - if(data->state.httpreq != HTTPREQ_GET && - ((data->state.httpreq != HTTPREQ_POST && - data->state.httpreq != HTTPREQ_POST_FORM && - data->state.httpreq != HTTPREQ_POST_MIME) || - !(data->set.keep_post & CURL_REDIR_POST_303))) { + if(!HTTPREQ_IS_POST(data) || !data->set.post303) { http_switch_to_get(data, 303); switch_to_get = TRUE; } @@ -1489,8 +1398,10 @@ /* * Curl_compareheader() * - * Returns TRUE if 'headerline' contains the 'header' with given 'content'. - * Pass headers WITH the colon. + * Returns TRUE if 'headerline' contains the 'header' with given 'content' + * (within a comma-separated list of tokens). Pass 'header' WITH the colon. + * + * @unittest: 1625 */ bool Curl_compareheader(const char *headerline, /* line to check */ const char *header, /* header keyword _with_ colon */ @@ -1524,14 +1435,168 @@ if(curlx_strlen(&val) >= clen) { size_t len; p = curlx_str(&val); - for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) { - if(curl_strnequal(p, content, clen)) + for(len = curlx_strlen(&val); len >= clen;) { + struct Curl_str next; + const char *o = p; + /* after a match there must be a comma, space, newline or null byte */ + if(curl_strnequal(p, content, clen) && + ((p[clen] == ',') || ISBLANK(p[clen]) || ISNEWLINE(p[clen]) || + !p[clen])) return TRUE; /* match! */ + /* advance to the next comma */ + if(curlx_str_until(&p, &next, MAX_HTTP_RESP_HEADER_SIZE, ',') || + curlx_str_single(&p, ',')) + break; /* no comma, get out */ + + /* if there are more dummy commas, move over them as well */ + do + curlx_str_passblanks(&p); + while(!curlx_str_single(&p, ',')); + len -= (p - o); } } return FALSE; /* no match */ } +struct cr_exp100_ctx { + struct Curl_creader super; + struct curltime start; /* time started waiting */ + enum expect100 state; +}; + +/* Expect: 100-continue client reader, blocking uploads */ + +static void http_exp100_continue(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + if(ctx->state > EXP100_SEND_DATA) { + ctx->state = EXP100_SEND_DATA; + Curl_expire_done(data, EXPIRE_100_TIMEOUT); + } +} + +static CURLcode cr_exp100_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *nread, bool *eos) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + timediff_t ms; + + switch(ctx->state) { + case EXP100_SENDING_REQUEST: + if(!Curl_req_sendbuf_empty(data)) { + /* The initial request data has not been fully sent yet. Do + * not start the timer yet. */ + DEBUGF(infof(data, "cr_exp100_read, request not full sent yet")); + *nread = 0; + *eos = FALSE; + return CURLE_OK; + } + /* We are now waiting for a reply from the server or + * a timeout on our side IFF the request has been fully sent. */ + DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, " + "timeout %dms", data->set.expect_100_timeout)); + ctx->state = EXP100_AWAITING_CONTINUE; + ctx->start = *Curl_pgrs_now(data); + Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); + *nread = 0; + *eos = FALSE; + return CURLE_OK; + case EXP100_FAILED: + DEBUGF(infof(data, "cr_exp100_read, expectation failed, error")); + *nread = 0; + *eos = FALSE; + return CURLE_READ_ERROR; + case EXP100_AWAITING_CONTINUE: + ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start); + if(ms < data->set.expect_100_timeout) { + DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); + *nread = 0; + *eos = FALSE; + return CURLE_OK; + } + /* we have waited long enough, continue anyway */ + http_exp100_continue(data, reader); + infof(data, "Done waiting for 100-continue"); + FALLTHROUGH(); + default: + DEBUGF(infof(data, "cr_exp100_read, pass through")); + return Curl_creader_read(data, reader->next, buf, blen, nread, eos); + } +} + +static void cr_exp100_done(struct Curl_easy *data, + struct Curl_creader *reader, int premature) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA; + Curl_expire_done(data, EXPIRE_100_TIMEOUT); +} + +static const struct Curl_crtype cr_exp100 = { + "cr-exp100", + Curl_creader_def_init, + cr_exp100_read, + Curl_creader_def_close, + Curl_creader_def_needs_rewind, + Curl_creader_def_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_cntrl, + Curl_creader_def_is_paused, + cr_exp100_done, + sizeof(struct cr_exp100_ctx) +}; + +static CURLcode http_exp100_add_reader(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL); + if(!result) + result = Curl_creader_add(data, reader); + if(!result) { + struct cr_exp100_ctx *ctx = reader->ctx; + ctx->state = EXP100_SENDING_REQUEST; + } + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +static void http_exp100_got100(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) + http_exp100_continue(data, r); +} + +static bool http_exp100_is_waiting(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) { + struct cr_exp100_ctx *ctx = r->ctx; + return ctx->state == EXP100_AWAITING_CONTINUE; + } + return FALSE; +} + +static void http_exp100_send_anyway(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) + http_exp100_continue(data, r); +} + +static bool http_exp100_is_selected(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + return !!r; +} + /* this returns the socket to wait for in the DO and DOING state for the multi interface and then we are always _sending_ a request and thus we wait for the single socket to become writable only */ @@ -1548,7 +1613,7 @@ struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(CURL_WANT_RECV(data)) { + if(CURL_REQ_WANT_RECV(data)) { result = Curl_pollset_add_in(data, ps, conn->sock[FIRSTSOCKET]); } @@ -1559,6 +1624,33 @@ return result; } +static CURLcode http_write_header(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + CURLcode result; + int writetype; + + /* now, only output this if the header AND body are requested: + */ + Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen); + + writetype = CLIENTWRITE_HEADER | + ((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0); + + result = Curl_client_write(data, writetype, hd, hdlen); + if(result) + return result; + + result = Curl_bump_headersize(data, hdlen, FALSE); + if(result) + return result; + + data->req.deductheadercount = (100 <= data->req.httpcode && + 199 >= data->req.httpcode) ? + data->req.headerbytecount : 0; + return result; +} + /* * Curl_http_done() gets called after a single HTTP request has been * performed. @@ -1590,7 +1682,7 @@ (data->req.bytecount + data->req.headerbytecount - data->req.deductheadercount) <= 0) { - /* If this connection is not simply closed to be retried, AND nothing was + /* If this connection is not closed to be retried, AND nothing was read from the HTTP server (that counts), this cannot be right so we return an error here */ failf(data, "Empty reply from server"); @@ -1853,11 +1945,11 @@ Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; const char *request; #ifndef CURL_DISABLE_WEBSOCKETS - if(data->conn->handler->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) + if(data->conn->scheme->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) httpreq = HTTPREQ_GET; else #endif - if((data->conn->handler->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_FTP)) && + if((data->conn->scheme->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_FTP)) && data->state.upload) httpreq = HTTPREQ_PUT; @@ -1896,7 +1988,7 @@ static CURLcode http_useragent(struct Curl_easy *data) { - /* The User-Agent string might have been allocated in url.c already, because + /* The User-Agent string might have been allocated already, because it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ @@ -1922,9 +2014,12 @@ return CURLE_OUT_OF_MEMORY; data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->handler->protocol; + data->state.first_remote_protocol = conn->scheme->protocol; } - Curl_safefree(aptr->host); + curlx_safefree(aptr->host); +#ifndef CURL_DISABLE_COOKIES + curlx_safefree(data->req.cookiehost); +#endif ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || @@ -1948,7 +2043,7 @@ if(*cookiehost == '[') { char *closingbracket; /* since the 'cookiehost' is an allocated memory area that will be - freed later we cannot simply increment the pointer */ + freed later we cannot increment the pointer */ memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1); closingbracket = strchr(cookiehost, ']'); if(closingbracket) @@ -1960,8 +2055,7 @@ if(colon) *colon = 0; /* The host must not include an embedded port number */ } - curlx_free(aptr->cookiehost); - aptr->cookiehost = cookiehost; + data->req.cookiehost = cookiehost; } #endif @@ -1972,9 +2066,9 @@ } } else { - /* When building Host: headers, we must put the hostname within - [brackets] if the hostname is a plain IPv6-address. RFC2732-style. */ - const char *host = conn->host.name; + /* Use the hostname as present in the URL if it was IPv6. */ + char *host = (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name; if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && (conn->remote_port == PORT_HTTPS)) || @@ -1982,14 +2076,9 @@ (conn->remote_port == PORT_HTTP))) /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include the port number in the host string */ - aptr->host = curl_maprintf("Host: %s%s%s\r\n", - conn->bits.ipv6_ip ? "[" : "", - host, conn->bits.ipv6_ip ? "]" : ""); + aptr->host = curl_maprintf("Host: %s\r\n", host); else - aptr->host = curl_maprintf("Host: %s%s%s:%d\r\n", - conn->bits.ipv6_ip ? "[" : "", - host, conn->bits.ipv6_ip ? "]" : "", - conn->remote_port); + aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->remote_port); if(!aptr->host) /* without Host: we cannot make a nice request */ @@ -2020,7 +2109,7 @@ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { /* Using a proxy but does not tunnel through it */ - /* The path sent to the proxy is in fact the entire URL. But if the remote + /* The path sent to the proxy is in fact the entire URL, but if the remote host is a IDN-name, we must make sure that the request we produce only uses the encoded hostname! */ @@ -2117,7 +2206,7 @@ switch(httpreq) { #ifndef CURL_DISABLE_MIME case HTTPREQ_POST_MIME: - data->state.mimepost = &data->set.mimepost; + data->state.mimepost = data->set.mimepostp; break; #endif #ifndef CURL_DISABLE_FORM_API @@ -2132,7 +2221,7 @@ result = Curl_getformdata(data, data->state.formp, data->set.httppost, data->state.fread_func); if(result) { - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); return result; } data->state.mimepost = data->state.formp; @@ -2341,7 +2430,7 @@ return CURLE_OK; /* For really small puts we do not use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make + the somewhat bigger ones we allow the app to disable it. Make sure that the expect100header is always set to the preferred value here. */ ptr = Curl_checkheaders(data, STRCONST("Expect")); @@ -2461,8 +2550,8 @@ if(data->cookies && data->state.cookie_engine) { bool okay; - const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : data->conn->host.name; + const char *host = data->req.cookiehost ? + data->req.cookiehost : data->conn->host.name; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); result = Curl_cookie_getlist(data, data->conn, &okay, host, &list); if(!result && okay) { @@ -2549,7 +2638,6 @@ data->state.aptr.rangeline = curl_maprintf("Content-Range: bytes 0-%" FMT_OFF_T "/" "%" FMT_OFF_T "\r\n", req_clen - 1, req_clen); - } else if(data->state.resume_from) { /* This is because "resume" was selected */ @@ -2565,8 +2653,8 @@ data->state.range, total_len - 1, total_len); } else { - /* Range was selected and then we just pass the incoming range and - append total size */ + /* Range was selected and then we pass the incoming range and append + total size */ data->state.aptr.rangeline = curl_maprintf("Content-Range: bytes %s/%" FMT_OFF_T "\r\n", data->state.range, req_clen); @@ -2587,7 +2675,7 @@ if(conn->bits.close) { /* Abort after the headers if "follow Location" is set and we are set to close anyway. */ - k->keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); k->done = TRUE; return CURLE_OK; } @@ -2606,7 +2694,7 @@ infof(data, "The entire document is already downloaded"); streamclose(conn, "already downloaded"); /* Abort download */ - k->keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); k->done = TRUE; return CURLE_OK; } @@ -2809,14 +2897,14 @@ #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_AUTH: - if(data->state.aptr.proxyuserpwd) - result = curlx_dyn_add(req, data->state.aptr.proxyuserpwd); + if(data->req.proxyuserpwd) + result = curlx_dyn_add(req, data->req.proxyuserpwd); break; #endif case H1_HD_USER_AUTH: - if(data->state.aptr.userpwd) - result = curlx_dyn_add(req, data->state.aptr.userpwd); + if(data->req.userpwd) + result = curlx_dyn_add(req, data->req.userpwd); break; case H1_HD_RANGE: @@ -2847,7 +2935,7 @@ break; case H1_HD_ACCEPT_ENCODING: - Curl_safefree(data->state.aptr.accept_encoding); + curlx_safefree(data->state.aptr.accept_encoding); if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n", @@ -2855,7 +2943,7 @@ break; case H1_HD_REFERER: - Curl_safefree(data->state.aptr.ref); + curlx_safefree(data->state.aptr.ref); if(Curl_bufref_ptr(&data->state.referer) && !Curl_checkheaders(data, STRCONST("Referer"))) result = curlx_dyn_addf(req, "Referer: %s\r\n", @@ -2879,7 +2967,7 @@ #ifndef CURL_DISABLE_ALTSVC case H1_HD_ALT_USED: if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) - result = curlx_dyn_addf(req, "Alt-Used: %s:%d\r\n", + result = curlx_dyn_addf(req, "Alt-Used: %s:%u\r\n", conn->conn_to_host.name, conn->conn_to_port); break; @@ -2894,7 +2982,7 @@ result = Curl_http2_request_upgrade(req, data); } #ifndef CURL_DISABLE_WEBSOCKETS - if(!result && conn->handler->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) + if(!result && conn->scheme->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) result = Curl_ws_request(data, req); #endif break; @@ -3024,15 +3112,9 @@ data->req.upload_chunky = FALSE; out: - if(CURLE_TOO_LARGE == result) + if(result == CURLE_TOO_LARGE) failf(data, "HTTP request too large"); - /* clear userpwd and proxyuserpwd to avoid reusing old credentials - * from reused connections */ - Curl_safefree(data->state.aptr.userpwd); -#ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuserpwd); -#endif curlx_dyn_free(&req); return result; } @@ -3080,13 +3162,13 @@ static statusline checkrtspprefix(struct Curl_easy *data, const char *s, size_t len) { - statusline result = STATUS_BAD; + statusline status = STATUS_BAD; statusline onmatch = len >= 5 ? STATUS_DONE : STATUS_UNKNOWN; (void)data; if(checkprefixmax("RTSP/", s, len)) - result = onmatch; + status = onmatch; - return result; + return status; } #endif /* CURL_DISABLE_RTSP */ @@ -3095,7 +3177,7 @@ const char *s, size_t len) { #ifndef CURL_DISABLE_RTSP - if(conn->handler->protocol & CURLPROTO_RTSP) + if(conn->scheme->protocol & CURLPROTO_RTSP) return checkrtspprefix(data, s, len); #else (void)conn; @@ -3106,11 +3188,11 @@ /* HTTP header has field name `n` (a string constant) */ #define HD_IS(hd, hdlen, n) \ - (((hdlen) >= (sizeof(n) - 1)) && curl_strnequal((n), (hd), (sizeof(n) - 1))) + (((hdlen) >= (sizeof(n) - 1)) && curl_strnequal(n, hd, sizeof(n) - 1)) #define HD_VAL(hd, hdlen, n) \ - ((((hdlen) >= (sizeof(n) - 1)) && \ - curl_strnequal((n), (hd), (sizeof(n) - 1)))? (hd + (sizeof(n) - 1)) : NULL) + ((((hdlen) >= (sizeof(n) - 1)) && (hd) && \ + curl_strnequal(n, hd, sizeof(n) - 1)) ? ((hd) + (sizeof(n) - 1)) : NULL) /* HTTP header has field name `n` (a string constant) and contains `v` * (a string constant) in its value(s) */ @@ -3129,7 +3211,7 @@ const char *v; struct connectdata *conn = data->conn; v = (data->asi && - (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) || + (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") @@ -3202,7 +3284,7 @@ return CURLE_OK; } } - /* negative, different value or just rubbish - bad HTTP */ + /* negative, different value or rubbish - bad HTTP */ failf(data, "Invalid Content-Length: value"); return CURLE_WEIRD_SERVER_REPLY; } @@ -3212,11 +3294,10 @@ HD_VAL(hd, hdlen, "Content-Encoding:") : NULL; if(v) { /* - * Process Content-Encoding. Look for the values: identity, - * gzip, deflate, compress, x-gzip and x-compress. x-gzip and - * x-compress are the same as gzip and compress. (Sec 3.5 RFC - * 2616). zlib cannot handle compress. However, errors are - * handled further down when the response body is processed + * Process Content-Encoding. Look for the values: identity, gzip, deflate, + * compress, x-gzip and x-compress. x-gzip and x-compress are the same as + * gzip and compress. (Sec 3.5 RFC 2616). zlib cannot handle compress. + * Errors are handled further down when the response body is processed */ return Curl_build_unencoding_stack(data, v, FALSE); } @@ -3470,8 +3551,8 @@ if(v) { /* If there is a custom-set Host: name, use it here, or else use * real peer hostname. */ - const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : conn->host.name; + const char *host = data->req.cookiehost ? + data->req.cookiehost : conn->host.name; const bool secure_context = Curl_secure_context(conn, host); CURLcode result; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); @@ -3484,7 +3565,7 @@ #ifndef CURL_DISABLE_HSTS /* If enabled, the header is incoming and this is over HTTPS */ v = (data->hsts && - (Curl_conn_is_ssl(conn, FIRSTSOCKET) || + (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_HSTS_HTTP") @@ -3494,11 +3575,11 @@ ) ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; if(v) { - CURLcode check = + CURLcode result = Curl_hsts_parse(data->hsts, conn->host.name, v); - if(check) { - if(check == CURLE_OUT_OF_MEMORY) - return check; + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + return result; infof(data, "Illegal STS header skipped"); } #ifdef DEBUGBUILD @@ -3624,7 +3705,7 @@ if(!result) { struct connectdata *conn = data->conn; - if(conn->handler->protocol & CURLPROTO_RTSP) + if(conn->scheme->protocol & CURLPROTO_RTSP) result = Curl_rtsp_parseheader(data, hd); } return result; @@ -3651,25 +3732,25 @@ /* no major version switch mid-connection */ if(k->httpversion_sent && (k->httpversion / 10 != k->httpversion_sent / 10)) { - failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)", + failf(data, "Version mismatch (from HTTP/%d to HTTP/%d)", k->httpversion_sent / 10, k->httpversion / 10); return CURLE_WEIRD_SERVER_REPLY; } break; default: - failf(data, "Unsupported HTTP version (%u.%d) in response", + failf(data, "Unsupported HTTP version (%d.%d) in response", k->httpversion / 10, k->httpversion % 10); return CURLE_UNSUPPORTED_PROTOCOL; } data->info.httpcode = k->httpcode; data->info.httpversion = k->httpversion; - conn->httpversion_seen = (unsigned char)k->httpversion; + conn->httpversion_seen = k->httpversion; if(!data->state.http_neg.rcvd_min || data->state.http_neg.rcvd_min > k->httpversion) /* store the lowest server version we encounter */ - data->state.http_neg.rcvd_min = (unsigned char)k->httpversion; + data->state.http_neg.rcvd_min = k->httpversion; /* * This code executes as part of processing the header. As a @@ -3681,8 +3762,8 @@ */ if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && k->httpcode == 416) { - /* "Requested Range Not Satisfiable", just proceed and - pretend this is no error */ + /* "Requested Range Not Satisfiable", proceed and pretend this is no + error */ k->ignorebody = TRUE; /* Avoid appending error msg to good data. */ } @@ -3700,7 +3781,7 @@ /* (quote from RFC2616, section 10.3.5): The 304 response * MUST NOT contain a message-body, and thus is always * terminated by the first empty line after the header - * fields. */ + * fields. */ if(data->set.timecondition) data->info.timecond = TRUE; FALLTHROUGH(); @@ -3750,7 +3831,7 @@ const char *hd, size_t hdlen) { struct SingleRequest *k = &data->req; - char *ptr = memchr(hd, 0x00, hdlen); + const char *ptr = memchr(hd, 0x00, hdlen); if(ptr) { /* this is bad, bail out */ failf(data, "Nul byte in header"); @@ -3800,163 +3881,112 @@ return CURLE_OK; } -static CURLcode http_write_header(struct Curl_easy *data, - const char *hd, size_t hdlen) -{ - CURLcode result; - int writetype; - - /* now, only output this if the header AND body are requested: - */ - Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen); - - writetype = CLIENTWRITE_HEADER | - ((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0); - - result = Curl_client_write(data, writetype, hd, hdlen); - if(result) - return result; - - result = Curl_bump_headersize(data, hdlen, FALSE); - if(result) - return result; - - data->req.deductheadercount = (100 <= data->req.httpcode && - 199 >= data->req.httpcode) ? - data->req.headerbytecount : 0; - return result; -} - -static CURLcode http_on_response(struct Curl_easy *data, - const char *last_hd, size_t last_hd_len, - const char *buf, size_t blen, - size_t *pconsumed) +/* + * Handle a 101 Switching Protocols response. Performs the actual protocol + * upgrade to HTTP/2 or WebSocket based on what was requested. + */ +static CURLcode http_on_101_upgrade(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed, + bool *conn_changed) { struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - bool conn_changed = FALSE; - (void)buf; /* not used without HTTP2 enabled */ - *pconsumed = 0; - - if(k->upgr101 == UPGR101_RECEIVED) { - /* supposedly upgraded to http2 now */ - if(data->req.httpversion != 20) - infof(data, "Lying server, not serving HTTP/2"); - } - - if(k->httpcode < 200 && last_hd) { - /* Intermediate responses might trigger processing of more - * responses, write the last header to the client before - * proceeding. */ - result = http_write_header(data, last_hd, last_hd_len); - last_hd = NULL; /* handled it */ - if(result) - goto out; - } - - if(k->httpcode < 100) { - failf(data, "Unsupported response code in HTTP response"); - result = CURLE_UNSUPPORTED_PROTOCOL; - goto out; - } - else if(k->httpcode < 200) { - /* "A user agent MAY ignore unexpected 1xx status responses." - * By default, we expect to get more responses after this one. */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - - switch(k->httpcode) { - case 100: - /* - * We have made an HTTP PUT or POST and this is 1.1-lingo - * that tells us that the server is OK with this and ready - * to receive the data. - */ - http_exp100_got100(data); - break; - case 101: { - int upgr101_requested = k->upgr101; - - if(k->httpversion_sent != 11) { - /* invalid for other HTTP versions */ - failf(data, "server sent 101 response while not talking HTTP/1.1"); - result = CURLE_WEIRD_SERVER_REPLY; - goto out; - } - - /* Whatever the success, upgrade was selected. */ - k->upgr101 = UPGR101_RECEIVED; - data->conn->bits.upgrade_in_progress = FALSE; - conn_changed = TRUE; - - /* To be fully conform, we would check the "Upgrade:" response header - * to mention the protocol we requested. */ - switch(upgr101_requested) { - case UPGR101_H2: - /* Switch to HTTP/2, where we will get more responses. - * blen bytes in bug are already h2 protocol bytes */ - infof(data, "Received 101, Switching to HTTP/2"); - result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); - if(result) - goto out; - *pconsumed += blen; - break; -#ifndef CURL_DISABLE_WEBSOCKETS - case UPGR101_WS: - /* Switch to WebSocket, where we now stream ws frames. - * blen bytes in bug are already ws protocol bytes */ - infof(data, "Received 101, Switching to WebSocket"); - result = Curl_ws_accept(data, buf, blen); - if(result) - goto out; - *pconsumed += blen; /* ws accept handled the data */ - break; +#if !defined(USE_NGHTTP2) && defined(CURL_DISABLE_WEBSOCKETS) + (void)buf; + (void)blen; + (void)pconsumed; +#else + CURLcode result; + int upgr101_requested = k->upgr101; #endif - default: - /* We silently accept this as the final response. What are we - * switching to if we did not ask for an Upgrade? Maybe the - * application provided an `Upgrade: xxx` header? */ - k->header = FALSE; - break; - } - /* processed 101 */ - break; - } - default: - /* The server may send us other 1xx responses, like informative - * 103. This have no influence on request processing and we expect - * to receive a final response eventually. */ - break; - } - goto out; + + if(k->httpversion_sent != 11) { + /* invalid for other HTTP versions */ + failf(data, "server sent 101 response while not talking HTTP/1.1"); + return CURLE_WEIRD_SERVER_REPLY; } - /* k->httpcode >= 200, final response */ + /* Whatever the success, upgrade was selected. */ + k->upgr101 = UPGR101_RECEIVED; + conn->bits.upgrade_in_progress = FALSE; + *conn_changed = TRUE; + + /* To be fully compliant, we would check the "Upgrade:" response header to + * mention the protocol we requested. */ +#ifdef USE_NGHTTP2 + if(upgr101_requested == UPGR101_H2) { + /* Switch to HTTP/2, where we will get more responses. blen bytes in buf + * are already h2 protocol bytes */ + infof(data, "Received 101, Switching to HTTP/2"); + result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); + if(!result) + *pconsumed += blen; + return result; + } +#endif +#ifndef CURL_DISABLE_WEBSOCKETS + if(upgr101_requested == UPGR101_WS) { + /* Switch to WebSocket, where we now stream ws frames. blen bytes in buf + * are already ws protocol bytes */ + infof(data, "Received 101, Switching to WebSocket"); + result = Curl_ws_accept(data, buf, blen); + if(!result) + *pconsumed += blen; /* ws accept handled the data */ + return result; + } +#endif + /* We silently accept this as the final response. What are we switching to + * if we did not ask for an Upgrade? Maybe the application provided an + * `Upgrade: xxx` header? */ k->header = FALSE; - if(data->conn->bits.upgrade_in_progress) { - /* Asked for protocol upgrade, but it was not selected by the server */ - data->conn->bits.upgrade_in_progress = FALSE; - conn_changed = TRUE; - } + return CURLE_OK; +} - if((k->size == -1) && !k->chunk && !conn->bits.close && - (k->httpversion == 11) && - !(conn->handler->protocol & CURLPROTO_RTSP) && - data->state.httpreq != HTTPREQ_HEAD) { - /* On HTTP 1.1, when connection is not to get closed, but no - Content-Length nor Transfer-Encoding chunked have been - received, according to RFC2616 section 4.4 point 5, we - assume that the server will close the connection to - signal the end of the document. */ - infof(data, "no chunk, no close, no size. Assume close to " - "signal end"); - streamclose(conn, "HTTP: No end-of-message indicator"); - } +/* + * Handle 1xx intermediate HTTP responses. Sets up state for more + * headers and processes 100-continue and 101 upgrade responses. + */ +static CURLcode http_on_1xx_response(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed, + bool *conn_changed) +{ + struct SingleRequest *k = &data->req; - /* At this point we have some idea about the fate of the connection. - If we are closing the connection it may result auth failure. */ + /* "A user agent MAY ignore unexpected 1xx status responses." + * By default, we expect to get more responses after this one. */ + k->header = TRUE; + k->headerline = 0; /* restart the header line counter */ + + switch(k->httpcode) { + case 100: + /* We have made an HTTP PUT or POST and this is 1.1-lingo that tells us + * that the server is OK with this and ready to receive the data. */ + http_exp100_got100(data); + break; + case 101: + return http_on_101_upgrade(data, buf, blen, pconsumed, conn_changed); + default: + /* The server may send us other 1xx responses, like informative 103. This + * has no influence on request processing and we expect to receive a + * final response eventually. */ + break; + } + return CURLE_OK; +} + +#if defined(USE_NTLM) || defined(USE_SPNEGO) +/* + * Check if NTLM or SPNEGO authentication negotiation failed due to + * connection closure (typically on HTTP/1.0 servers). + */ +static void http_check_auth_closure(struct Curl_easy *data, + struct connectdata *conn) +{ + /* At this point we have some idea about the fate of the connection. If we + are closing the connection it may result auth failure. */ #ifdef USE_NTLM if(conn->bits.close && (((data->req.httpcode == 401) && @@ -3985,9 +4015,154 @@ conn->proxy_negotiate_state = GSS_AUTHSUCC; } #endif +} +#else +#define http_check_auth_closure(x, y) /* empty */ +#endif + +/* + * Handle an error response (>= 300) received while still sending the + * request body. Deals with 417 Expectation Failed retries, keep-sending + * on error, and aborting the send. + */ +static CURLcode http_handle_send_error(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct SingleRequest *k = &data->req; + CURLcode result = CURLE_OK; + + if(!data->req.authneg && !conn->bits.close && + !Curl_creader_will_rewind(data)) { + /* + * General treatment of errors when about to send data. + * Including: "417 Expectation Failed", while waiting for + * 100-continue. + * + * The check for close above is done because if something + * else has already deemed the connection to get closed then + * something else should have considered the big picture and + * we avoid this check. + */ + + switch(data->state.httpreq) { + case HTTPREQ_PUT: + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + /* We got an error response. If this happened before the + * whole request body has been sent we stop sending and + * mark the connection for closure after we have read the + * entire response. */ + if(!Curl_req_done_sending(data)) { + if((k->httpcode == 417) && http_exp100_is_selected(data)) { + /* 417 Expectation Failed - try again without the + Expect header */ + if(!k->writebytecount && http_exp100_is_waiting(data)) { + 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; + Curl_req_abort_sending(data); + DEBUGASSERT(!data->req.newurl); + data->req.newurl = Curl_bufref_dup(&data->state.url); + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + } + else if(data->set.http_keep_sending_on_error) { + infof(data, "HTTP error before end of send, keep sending"); + http_exp100_send_anyway(data); + } + else { + infof(data, "HTTP error before end of send, stop sending"); + streamclose(conn, "Stop sending data before everything sent"); + result = Curl_req_abort_sending(data); + if(result) + return result; + } + } + break; + + default: /* default label present to avoid compiler warnings */ + break; + } + } + + if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) { + /* We rewind before next send, continue sending now */ + infof(data, "Keep sending data to get tossed away"); + CURL_REQ_SET_SEND(data); + } + return result; +} + +static CURLcode http_on_response(struct Curl_easy *data, + const char *last_hd, size_t last_hd_len, + const char *buf, size_t blen, + size_t *pconsumed) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + bool conn_changed = FALSE; + + (void)buf; /* not used without HTTP2 enabled */ + *pconsumed = 0; + + if(k->upgr101 == UPGR101_RECEIVED) { + /* supposedly upgraded to http2 now */ + if(data->req.httpversion != 20) + infof(data, "Lying server, not serving HTTP/2"); + } + + if(k->httpcode < 200 && last_hd) { + /* Intermediate responses might trigger processing of more responses, + * write the last header to the client before proceeding. */ + result = http_write_header(data, last_hd, last_hd_len); + last_hd = NULL; /* handled it */ + if(result) + goto out; + } + + if(k->httpcode < 100) { + failf(data, "Unsupported response code in HTTP response"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + else if(k->httpcode < 200) { + result = http_on_1xx_response(data, buf, blen, pconsumed, &conn_changed); + goto out; + } + + /* k->httpcode >= 200, final response */ + k->header = FALSE; + if(conn->bits.upgrade_in_progress) { + /* Asked for protocol upgrade, but it was not selected */ + conn->bits.upgrade_in_progress = FALSE; + conn_changed = TRUE; + } + + if((k->size == -1) && !k->chunk && !conn->bits.close && + (k->httpversion == 11) && + !(conn->scheme->protocol & CURLPROTO_RTSP) && + data->state.httpreq != HTTPREQ_HEAD) { + /* On HTTP 1.1, when connection is not to get closed, but no + Content-Length nor Transfer-Encoding chunked have been received, + according to RFC2616 section 4.4 point 5, we assume that the server + will close the connection to signal the end of the document. */ + infof(data, "no chunk, no close, no size. Assume close to signal end"); + streamclose(conn, "HTTP: No end-of-message indicator"); + } + + http_check_auth_closure(data, conn); #ifndef CURL_DISABLE_WEBSOCKETS - /* All >=200 HTTP status codes are errors when wanting WebSocket */ + /* All >=200 HTTP status codes are errors when wanting ws */ if(data->req.upgr101 == UPGR101_WS) { failf(data, "Refused WebSocket upgrade: %d", k->httpcode); result = CURLE_HTTP_RETURNED_ERROR; @@ -3997,88 +4172,23 @@ /* Check if this response means the transfer errored. */ if(http_should_fail(data, data->req.httpcode)) { - failf(data, "The requested URL returned error: %d", k->httpcode); + failf(data, "The requested URL returned error: %d", + k->httpcode); result = CURLE_HTTP_RETURNED_ERROR; goto out; } - /* Curl_http_auth_act() checks what authentication methods - * that are available and decides which one (if any) to - * use. It will set 'newurl' if an auth method was picked. */ + /* Curl_http_auth_act() checks what authentication methods that are + * available and decides which one (if any) to use. It will set 'newurl' if + * an auth method was picked. */ result = Curl_http_auth_act(data); if(result) goto out; if(k->httpcode >= 300) { - if((!data->req.authneg) && !conn->bits.close && - !Curl_creader_will_rewind(data)) { - /* - * General treatment of errors when about to send data. Including : - * "417 Expectation Failed", while waiting for 100-continue. - * - * The check for close above is done simply because of something - * else has already deemed the connection to get closed then - * something else should have considered the big picture and we - * avoid this check. - * - */ - - switch(data->state.httpreq) { - case HTTPREQ_PUT: - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - /* We got an error response. If this happened before the whole - * request body has been sent we stop sending and mark the - * connection for closure after we have read the entire response. - */ - if(!Curl_req_done_sending(data)) { - if((k->httpcode == 417) && http_exp100_is_selected(data)) { - /* 417 Expectation Failed - try again without the Expect - header */ - if(!k->writebytecount && http_exp100_is_waiting(data)) { - 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) - goto out; - } - data->state.disableexpect = TRUE; - Curl_req_abort_sending(data); - DEBUGASSERT(!data->req.newurl); - data->req.newurl = Curl_bufref_dup(&data->state.url); - if(!data->req.newurl) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - else if(data->set.http_keep_sending_on_error) { - infof(data, "HTTP error before end of send, keep sending"); - http_exp100_send_anyway(data); - } - else { - infof(data, "HTTP error before end of send, stop sending"); - streamclose(conn, "Stop sending data before everything sent"); - result = Curl_req_abort_sending(data); - if(result) - goto out; - } - } - break; - - default: /* default label present to avoid compiler warnings */ - break; - } - } - - if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) { - /* We rewind before next send, continue sending now */ - infof(data, "Keep sending data to get tossed away"); - k->keepon |= KEEP_SEND; - } + result = http_handle_send_error(data); + if(result) + goto out; } /* If we requested a "no body", this is a good time to get @@ -4087,11 +4197,10 @@ if(data->req.no_body) k->download_done = TRUE; - /* If max download size is *zero* (nothing) we already have - nothing and can safely return ok now! But for HTTP/2, we would - like to call http2_handle_stream_close to properly close a - stream. In order to do this, we keep reading until we - close the stream. */ + /* If max download size is *zero* (nothing) we already have nothing and can + safely return ok now! For HTTP/2, we would like to call + http2_handle_stream_close to properly close a stream. In order to do + this, we keep reading until we close the stream. */ if((k->maxdownload == 0) && (k->httpversion_sent < 20)) k->download_done = TRUE; @@ -4099,21 +4208,18 @@ result = http_firstwrite(data); if(!result) - /* This is the last response that we get for the current request. - * Check on the body size and determine if the response is complete. - */ + /* This is the last response that we get for the current request. Check on + * the body size and determine if the response is complete. */ result = http_size(data); out: - if(last_hd) { + if(last_hd) /* if not written yet, write it now */ - result = Curl_1st_err( - result, http_write_header(data, last_hd, last_hd_len)); - } - if(conn_changed) { - /* poke the multi handle to allow any pending pipewait to retry now */ + result = Curl_1st_fatal(result, + http_write_header(data, last_hd, last_hd_len)); + if(conn_changed) + /* poke the multi handle to allow pending pipewait to retry */ Curl_multi_connchanged(data->multi); - } return result; } @@ -4125,7 +4231,7 @@ CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; int writetype; - DEBUGASSERT(!hd[hdlen]); /* null terminated */ + DEBUGASSERT(!hd[hdlen]); /* null-terminated */ *pconsumed = 0; if((0x0a == *hd) || (0x0d == *hd)) { @@ -4162,7 +4268,7 @@ bool fine_statusline = FALSE; k->httpversion = 0; /* Do not know yet */ - if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) { + if(data->conn->scheme->protocol & PROTO_FAMILY_HTTP) { /* * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 * @@ -4183,7 +4289,7 @@ k->httpversion = (unsigned char)(10 + (p[1] - '0')); p += 3; if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + k->httpcode = ((p[0] - '0') * 100) + ((p[1] - '0') * 10) + (p[2] - '0'); /* RFC 9112 requires a single space following the status code, but the browsers do not so let's not insist */ @@ -4203,7 +4309,7 @@ k->httpversion = (unsigned char)((*p - '0') * 10); p += 2; if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + k->httpcode = ((p[0] - '0') * 100) + ((p[1] - '0') * 10) + (p[2] - '0'); p += 3; if(!ISBLANK(*p)) @@ -4229,7 +4335,7 @@ } } } - else if(data->conn->handler->protocol & CURLPROTO_RTSP) { + else if(data->conn->scheme->protocol & CURLPROTO_RTSP) { const char *p = hd; struct Curl_str ver; curl_off_t status; @@ -4291,12 +4397,12 @@ void Curl_http_to_fold(struct dynbuf *bf) { size_t len = curlx_dyn_len(bf); - char *hd = curlx_dyn_ptr(bf); + const char *hd = curlx_dyn_ptr(bf); if(len && (hd[len - 1] == '\n')) len--; if(len && (hd[len - 1] == '\r')) len--; - while(len && (ISBLANK(hd[len - 1]))) /* strip off trailing whitespace */ + while(len && ISBLANK(hd[len - 1])) /* strip off trailing whitespace */ len--; curlx_dyn_setlen(bf, len); } @@ -4317,7 +4423,7 @@ struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - char *end_ptr; + const char *end_ptr; bool leftover_body = FALSE; /* we have bytes for the next header, make sure it is not a folded header @@ -4345,7 +4451,7 @@ while(blen && k->header) { size_t consumed; size_t hlen; - char *hd; + const char *hd; size_t unfold_len = 0; if(data->state.leading_unfold) { @@ -4486,18 +4592,18 @@ CURLcode result; size_t consumed; char tmp = 0; - DEBUGASSERT(!hd[hdlen]); /* null terminated */ + DEBUGASSERT(!hd[hdlen]); /* null-terminated */ result = http_rw_hd(data, hd, hdlen, &tmp, 0, &consumed); if(!result && is_eos) { - result = Curl_client_write(data, (CLIENTWRITE_BODY|CLIENTWRITE_EOS), + result = Curl_client_write(data, (CLIENTWRITE_BODY | CLIENTWRITE_EOS), &tmp, 0); } return result; } /* - * HTTP protocol `write_resp` implementation. Will parse headers + * HTTP protocol `write_resp` implementation. Parse headers * when not done yet and otherwise return without consuming data. */ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, @@ -4607,17 +4713,17 @@ #pragma GCC diagnostic pop #endif if(scheme) { - req->scheme = Curl_memdup0(scheme, s_len); + req->scheme = curlx_memdup0(scheme, s_len); if(!req->scheme) goto out; } if(authority) { - req->authority = Curl_memdup0(authority, a_len); + req->authority = curlx_memdup0(authority, a_len); if(!req->authority) goto out; } if(path) { - req->path = Curl_memdup0(path, p_len); + req->path = curlx_memdup0(path, p_len); if(!req->path) goto out; } @@ -4844,7 +4950,7 @@ infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme); } else { - scheme = Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ? "https" : "http"; + scheme = Curl_xfer_is_secure(data) ? "https" : "http"; } } @@ -4931,143 +5037,27 @@ } } -struct cr_exp100_ctx { - struct Curl_creader super; - struct curltime start; /* time started waiting */ - enum expect100 state; +/* + * HTTP handler interface. + */ +const struct Curl_protocol Curl_protocol_http = { + Curl_http_setup_conn, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + Curl_http_doing_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + Curl_http_perform_pollset, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + Curl_http_write_resp, /* write_resp */ + Curl_http_write_resp_hd, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ }; -/* Expect: 100-continue client reader, blocking uploads */ - -static void http_exp100_continue(struct Curl_easy *data, - struct Curl_creader *reader) -{ - struct cr_exp100_ctx *ctx = reader->ctx; - if(ctx->state > EXP100_SEND_DATA) { - ctx->state = EXP100_SEND_DATA; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - } -} - -static CURLcode cr_exp100_read(struct Curl_easy *data, - struct Curl_creader *reader, - char *buf, size_t blen, - size_t *nread, bool *eos) -{ - struct cr_exp100_ctx *ctx = reader->ctx; - timediff_t ms; - - switch(ctx->state) { - case EXP100_SENDING_REQUEST: - if(!Curl_req_sendbuf_empty(data)) { - /* The initial request data has not been fully sent yet. Do - * not start the timer yet. */ - DEBUGF(infof(data, "cr_exp100_read, request not full sent yet")); - *nread = 0; - *eos = FALSE; - return CURLE_OK; - } - /* We are now waiting for a reply from the server or - * a timeout on our side IFF the request has been fully sent. */ - DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, " - "timeout %dms", data->set.expect_100_timeout)); - ctx->state = EXP100_AWAITING_CONTINUE; - ctx->start = *Curl_pgrs_now(data); - Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); - *nread = 0; - *eos = FALSE; - return CURLE_OK; - case EXP100_FAILED: - DEBUGF(infof(data, "cr_exp100_read, expectation failed, error")); - *nread = 0; - *eos = FALSE; - return CURLE_READ_ERROR; - case EXP100_AWAITING_CONTINUE: - ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start); - if(ms < data->set.expect_100_timeout) { - DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); - *nread = 0; - *eos = FALSE; - return CURLE_OK; - } - /* we have waited long enough, continue anyway */ - http_exp100_continue(data, reader); - infof(data, "Done waiting for 100-continue"); - FALLTHROUGH(); - default: - DEBUGF(infof(data, "cr_exp100_read, pass through")); - return Curl_creader_read(data, reader->next, buf, blen, nread, eos); - } -} - -static void cr_exp100_done(struct Curl_easy *data, - struct Curl_creader *reader, int premature) -{ - struct cr_exp100_ctx *ctx = reader->ctx; - ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); -} - -static const struct Curl_crtype cr_exp100 = { - "cr-exp100", - Curl_creader_def_init, - cr_exp100_read, - Curl_creader_def_close, - Curl_creader_def_needs_rewind, - Curl_creader_def_total_length, - Curl_creader_def_resume_from, - Curl_creader_def_cntrl, - Curl_creader_def_is_paused, - cr_exp100_done, - sizeof(struct cr_exp100_ctx) -}; - -static CURLcode http_exp100_add_reader(struct Curl_easy *data) -{ - struct Curl_creader *reader = NULL; - CURLcode result; - - result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL); - if(!result) - result = Curl_creader_add(data, reader); - if(!result) { - struct cr_exp100_ctx *ctx = reader->ctx; - ctx->state = EXP100_SENDING_REQUEST; - } - - if(result && reader) - Curl_creader_free(data, reader); - return result; -} - -static void http_exp100_got100(struct Curl_easy *data) -{ - struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - if(r) - http_exp100_continue(data, r); -} - -static bool http_exp100_is_waiting(struct Curl_easy *data) -{ - struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - if(r) { - struct cr_exp100_ctx *ctx = r->ctx; - return ctx->state == EXP100_AWAITING_CONTINUE; - } - return FALSE; -} - -static void http_exp100_send_anyway(struct Curl_easy *data) -{ - struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - if(r) - http_exp100_continue(data, r); -} - -static bool http_exp100_is_selected(struct Curl_easy *data) -{ - struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - return !!r; -} - #endif /* CURL_DISABLE_HTTP */
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index 89810b1..6e33c00 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h
@@ -37,16 +37,6 @@ HTTPREQ_HEAD } Curl_HttpReq; -/* When redirecting transfers. */ -typedef enum { - FOLLOW_NONE, /* not used within the function, just a placeholder to - allow initing to this */ - FOLLOW_FAKE, /* only records stuff, not actually following */ - FOLLOW_RETRY, /* set if this is a request retry as opposed to a real - redirect following */ - FOLLOW_REDIR /* a full true redirect */ -} followtype; - #define CURL_HTTP_V1x (1 << 0) #define CURL_HTTP_V2x (1 << 1) #define CURL_HTTP_V3x (1 << 2) @@ -55,15 +45,7 @@ #ifndef CURL_DISABLE_HTTP -#ifdef USE_HTTP3 -#include <stdint.h> -#endif - -extern const struct Curl_handler Curl_handler_http; - -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_https; -#endif +extern const struct Curl_protocol Curl_protocol_http; struct dynhds; @@ -107,13 +89,14 @@ void Curl_http_to_fold(struct dynbuf *bf); void Curl_http_method(struct Curl_easy *data, - const char **method, Curl_HttpReq *); + const char **method, Curl_HttpReq *reqp); /* protocol-specific functions set up to be called by the main engine */ CURLcode Curl_http_setup_conn(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_http(struct Curl_easy *data, bool *done); -CURLcode Curl_http_done(struct Curl_easy *data, CURLcode, bool premature); +CURLcode Curl_http_done(struct Curl_easy *data, + CURLcode status, bool premature); CURLcode Curl_http_doing_pollset(struct Curl_easy *data, struct easy_pollset *ps); CURLcode Curl_http_perform_pollset(struct Curl_easy *data, @@ -202,14 +185,14 @@ * * @returns CURLcode */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel); /* TRUE if this is the request setting - up the proxy tunnel */ +CURLcode Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel); /* TRUE if this is + the request setting up + the proxy tunnel */ /* Decode HTTP status code string. */ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len);
diff --git a/Utilities/cmcurl/lib/http1.c b/Utilities/cmcurl/lib/http1.c index 9e58424..60ad32c 100644 --- a/Utilities/cmcurl/lib/http1.c +++ b/Utilities/cmcurl/lib/http1.c
@@ -269,6 +269,11 @@ size_t nread; *pnread = 0; + + DEBUGASSERT(buf); + if(!buf) + return CURLE_BAD_FUNCTION_ARGUMENT; + while(!parser->done) { result = next_line(parser, buf, buflen, options, &nread); if(result) {
diff --git a/Utilities/cmcurl/lib/http1.h b/Utilities/cmcurl/lib/http1.h index 124d32e..c289461 100644 --- a/Utilities/cmcurl/lib/http1.h +++ b/Utilities/cmcurl/lib/http1.h
@@ -29,7 +29,7 @@ #include "bufq.h" #include "http.h" -#define H1_PARSE_OPT_NONE (0) +#define H1_PARSE_OPT_NONE 0 #define H1_PARSE_OPT_STRICT (1 << 0) #define H1_PARSE_DEFAULT_MAX_LINE_LEN DYN_HTTP_REQUEST
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index a8b8d5c..7be5abd 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c
@@ -24,7 +24,6 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2) -#include <stdint.h> #include <nghttp2/nghttp2.h> #include "urldata.h" @@ -52,10 +51,6 @@ #error too old nghttp2 version, upgrade! #endif -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define nghttp2_session_callbacks_set_error_callback(x, y) -#endif - #if (NGHTTP2_VERSION_NUM >= 0x010c00) #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 #endif @@ -68,9 +63,9 @@ #define H2_CONN_WINDOW_SIZE (10 * 1024 * 1024) /* on receiving from TLS, we prep for holding a full stream window */ #define H2_NW_RECV_CHUNKS (H2_CONN_WINDOW_SIZE / H2_CHUNK_SIZE) -/* on send into TLS, we just want to accumulate small frames */ +/* on send into TLS, we want to accumulate small frames */ #define H2_NW_SEND_CHUNKS 1 -/* this is how much we want "in flight" for a stream, unthrottled */ +/* this is how much we want "in flight" for a stream, unthrottled */ #define H2_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) /* this is how much we want "in flight" for a stream, initially, IFF * nghttp2 allows us to tweak the local window size. */ @@ -124,7 +119,60 @@ #undef CF_CTX_CALL_DATA #define CF_CTX_CALL_DATA(cf) ((struct cf_h2_ctx *)(cf)->ctx)->call_data -static void h2_stream_hash_free(unsigned int id, void *stream); +/** + * All about the H2 internals of a stream + */ +struct h2_stream_ctx { + 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 */ + curl_off_t nrcvd_data; /* number of DATA bytes received */ + + char **push_headers; /* allocated array */ + size_t push_headers_used; /* number of entries filled in */ + size_t push_headers_alloc; /* number of entries allocated */ + + int status_code; /* HTTP response status code */ + uint32_t error; /* stream error code */ + CURLcode xfer_result; /* Result of writing out response */ + int32_t local_window_size; /* the local recv window size */ + int32_t id; /* HTTP/2 protocol identifier for stream */ + BIT(resp_hds_complete); /* we have a complete, final response */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(reset_by_server); /* TRUE on stream reset by server */ + BIT(close_handled); /* TRUE if stream closure is handled by libcurl */ + BIT(bodystarted); + BIT(body_eos); /* the complete body has been added to `sendbuf` and + * is being/has been processed from there. */ + BIT(write_paused); /* stream write is paused */ +}; + +static void free_push_headers(struct h2_stream_ctx *stream) +{ + size_t i; + for(i = 0; i < stream->push_headers_used; i++) + curlx_free(stream->push_headers[i]); + curlx_safefree(stream->push_headers); + stream->push_headers_used = 0; +} + +static void h2_stream_ctx_free(struct h2_stream_ctx *stream) +{ + Curl_bufq_free(&stream->sendbuf); + Curl_h1_req_parse_free(&stream->h1); + Curl_dynhds_free(&stream->resp_trailers); + free_push_headers(stream); + curlx_free(stream); +} + +static void h2_stream_hash_free(unsigned int id, void *stream) +{ + (void)id; + DEBUGASSERT(stream); + h2_stream_ctx_free((struct h2_stream_ctx *)stream); +} static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade) { @@ -155,6 +203,7 @@ { if(ctx->h2) { nghttp2_session_del(ctx->h2); + ctx->h2 = NULL; } } @@ -164,9 +213,9 @@ /* If the transfer has a rate-limit lower than the default initial * stream window size, use that. It needs to be at least 8k or servers * may be unhappy. */ - if(data->progress.dl.rlimit.rate_per_step && - (data->progress.dl.rlimit.rate_per_step < H2_STREAM_WINDOW_SIZE_INITIAL)) - return CURLMAX((uint32_t)data->progress.dl.rlimit.rate_per_step, 8192); + curl_off_t rps = Curl_rlimit_per_step(&data->progress.dl.rlimit); + if((rps > 0) && (rps < H2_STREAM_WINDOW_SIZE_INITIAL)) + return CURLMAX((uint32_t)rps, 8192); #endif return H2_STREAM_WINDOW_SIZE_INITIAL; } @@ -212,44 +261,9 @@ return CURLE_OK; } -static CURLcode nw_out_flush(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static CURLcode h2_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data); - -/** - * All about the H2 internals of a stream - */ -struct h2_stream_ctx { - 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 */ - curl_off_t nrcvd_data; /* number of DATA bytes received */ - - char **push_headers; /* allocated array */ - size_t push_headers_used; /* number of entries filled in */ - size_t push_headers_alloc; /* number of entries allocated */ - - int status_code; /* HTTP response status code */ - uint32_t error; /* stream error code */ - CURLcode xfer_result; /* Result of writing out response */ - int32_t local_window_size; /* the local recv window size */ - int32_t id; /* HTTP/2 protocol identifier for stream */ - BIT(resp_hds_complete); /* we have a complete, final response */ - BIT(closed); /* TRUE on stream close */ - BIT(reset); /* TRUE on stream reset */ - BIT(close_handled); /* TRUE if stream closure is handled by libcurl */ - BIT(bodystarted); - BIT(body_eos); /* the complete body has been added to `sendbuf` and - * is being/has been processed from there. */ - BIT(write_paused); /* stream write is paused */ -}; - -#define H2_STREAM_CTX(ctx, data) \ - ((struct h2_stream_ctx *)( \ - data? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL)) +#define H2_STREAM_CTX(ctx, data) \ + ((struct h2_stream_ctx *)( \ + (data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL)) static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) { @@ -275,31 +289,6 @@ return stream; } -static void free_push_headers(struct h2_stream_ctx *stream) -{ - size_t i; - for(i = 0; i < stream->push_headers_used; i++) - curlx_free(stream->push_headers[i]); - Curl_safefree(stream->push_headers); - stream->push_headers_used = 0; -} - -static void h2_stream_ctx_free(struct h2_stream_ctx *stream) -{ - Curl_bufq_free(&stream->sendbuf); - Curl_h1_req_parse_free(&stream->h1); - Curl_dynhds_free(&stream->resp_trailers); - free_push_headers(stream); - curlx_free(stream); -} - -static void h2_stream_hash_free(unsigned int id, void *stream) -{ - (void)id; - DEBUGASSERT(stream); - h2_stream_ctx_free((struct h2_stream_ctx *)stream); -} - #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -387,6 +376,10 @@ (void)cf; DEBUGASSERT(data); + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + stream = H2_STREAM_CTX(ctx, data); if(stream) { *pstream = stream; @@ -406,6 +399,29 @@ return CURLE_OK; } +static CURLcode nw_out_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + size_t nwritten; + CURLcode result; + + if(Curl_bufq_is_empty(&ctx->outbufq)) + return CURLE_OK; + + result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0, + &nwritten); + if(result) { + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->outbufq)); + ctx->nw_out_blocked = 1; + } + return result; + } + return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN; +} + static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; @@ -464,154 +480,6 @@ return rc; } -static ssize_t send_callback(nghttp2_session *h2, - const uint8_t *mem, size_t length, int flags, - void *userp); -static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, - void *userp); -static int cf_h2_on_invalid_frame_recv(nghttp2_session *session, - const nghttp2_frame *frame, - int lib_error_code, - void *user_data); -#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); -static int on_stream_close(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *userp); -static int on_begin_headers(nghttp2_session *session, - const nghttp2_frame *frame, void *userp); -static int on_header(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int error_callback(nghttp2_session *session, const char *msg, - size_t len, void *userp); -#endif -static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - struct h2_stream_ctx *stream; - CURLcode result = CURLE_OUT_OF_MEMORY; - int rc; - nghttp2_session_callbacks *cbs = NULL; - - DEBUGASSERT(!ctx->h2); - DEBUGASSERT(ctx->initialized); - - rc = nghttp2_session_callbacks_new(&cbs); - if(rc) { - failf(data, "Could not initialize nghttp2 callbacks"); - goto out; - } - - nghttp2_session_callbacks_set_send_callback(cbs, send_callback); - nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); - nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs, - cf_h2_on_invalid_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); - nghttp2_session_callbacks_set_on_begin_headers_callback( - cbs, on_begin_headers); - nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - nghttp2_session_callbacks_set_error_callback(cbs, error_callback); -#endif - - /* The nghttp2 session is not yet setup, do it */ - rc = h2_client_new(cf, cbs); - if(rc) { - failf(data, "Could not initialize nghttp2"); - goto out; - } - ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; - - if(ctx->via_h1_upgrade) { - /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted - * in the H1 request and we upgrade from there. This stream - * is opened implicitly as #1. */ - uint8_t binsettings[H2_BINSETTINGS_LEN]; - ssize_t rclen; - size_t binlen; /* length of the binsettings data */ - - rclen = populate_binsettings(binsettings, data); - - if(!curlx_sztouz(rclen, &binlen) || !binlen) { - failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); - result = CURLE_FAILED_INIT; - goto out; - } - - result = http2_data_setup(cf, data, &stream); - if(result) - goto out; - DEBUGASSERT(stream); - stream->id = 1; - /* queue SETTINGS frame (again) */ - rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen, - data->state.httpreq == HTTPREQ_HEAD, - NULL); - if(rc) { - failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - - rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id, - data); - if(rc) { - infof(data, "http/2: failed to set user_data for stream %u", - stream->id); - DEBUGASSERT(0); - } - CURL_TRC_CF(data, cf, "created session via Upgrade"); - } - else { - nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; - size_t ivlen; - - ivlen = populate_settings(iv, data, ctx); - rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, - iv, ivlen); - if(rc) { - failf(data, "nghttp2_submit_settings() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - } - - rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, - HTTP2_HUGE_WINDOW_SIZE); - if(rc) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - - /* all set, traffic will be send on connect */ - result = CURLE_OK; - CURL_TRC_CF(data, cf, "[0] created h2 session%s", - ctx->via_h1_upgrade ? " (via h1 upgrade)" : ""); - -out: - if(cbs) - nghttp2_session_callbacks_del(cbs); - return result; -} - /* * Returns nonzero if current HTTP/2 session should be closed. */ @@ -662,8 +530,8 @@ } /* - * The server may send us data at any point (e.g. PING frames). Therefore, - * we cannot assume that an HTTP/2 socket is dead just because it is readable. + * The server may send us data at any point (e.g. PING frames). Therefore, we + * cannot assume that an HTTP/2 socket is dead because it is readable. * * Check the lower filters first and, if successful, peek at the socket * and distinguish between closed and data. @@ -738,30 +606,6 @@ (void)curl_msnprintf(p, len, "nghttp2/%s", h2->version_str); } -static CURLcode nw_out_flush(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - size_t nwritten; - CURLcode result; - - (void)data; - if(Curl_bufq_is_empty(&ctx->outbufq)) - return CURLE_OK; - - result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0, - &nwritten); - if(result) { - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", - Curl_bufq_len(&ctx->outbufq)); - ctx->nw_out_blocked = 1; - } - return result; - } - return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN; -} - /* * The implementation of nghttp2_send_callback type. Here we write |data| with * size |length| to the network and return the number of bytes actually @@ -831,28 +675,28 @@ /* * push header access function. Only to be used from within the push callback */ -char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) +char *curl_pushheader_byname(struct curl_pushheaders *h, const char *name) { struct h2_stream_ctx *stream; size_t len; size_t i; /* Verify that we got a good easy handle in the push header struct, mostly to detect rubbish input fast(er). Also empty header name - is just a rubbish too. We have to allow ":" at the beginning of + is rubbish too. We have to allow ":" at the beginning of the header, but header == ":" must be rejected. If we have ':' in the middle of header, it could be matched in middle of the value, this is because we do prefix match.*/ - if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] || - !strcmp(header, ":") || strchr(header + 1, ':')) + if(!h || !GOOD_EASY_HANDLE(h->data) || !name || !name[0] || + !strcmp(name, ":") || strchr(name + 1, ':')) return NULL; stream = h->stream; if(!stream) return NULL; - len = strlen(header); + len = strlen(name); for(i = 0; i < stream->push_headers_used; i++) { - if(!strncmp(header, stream->push_headers[i], len)) { + if(!strncmp(name, stream->push_headers[i], len)) { /* sub-match, make sure that it is followed by a colon */ if(stream->push_headers[i][len] != ':') continue; @@ -874,8 +718,9 @@ return second; } -static int set_transfer_url(struct Curl_easy *data, - struct curl_pushheaders *hp) +static int set_transfer_url(struct Curl_easy *newhandle, + struct curl_pushheaders *hp, + struct Curl_easy *data) { const char *v; CURLUcode uc; @@ -913,6 +758,13 @@ } } + /* We can only allow PUSH of resource from the same origin, e.g. + * scheme + hostname + port */ + if(!Curl_url_same_origin(data->state.uh, u)) { + rc = 1; + goto fail; + } + uc = curl_url_get(u, CURLUPART_URL, &url, 0); if(uc) rc = 4; @@ -921,7 +773,7 @@ if(rc) return rc; - Curl_bufref_set(&data->state.url, url, 0, curl_free); + Curl_bufref_set(&newhandle->state.url, url, 0, curl_free); return 0; } @@ -967,7 +819,7 @@ heads.stream = stream; heads.frame = frame; - rv = set_transfer_url(newhandle, &heads); + rv = set_transfer_url(newhandle, &heads, data); if(rv) { CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d", frame->promised_stream_id, rv); @@ -1024,7 +876,7 @@ newstream->id, newhandle); if(rv) { - infof(data, "failed to set user_data for stream %u", + infof(data, "failed to set user_data for stream %d", newstream->id); DEBUGASSERT(0); discard_newhandle(cf, newhandle); @@ -1049,7 +901,6 @@ struct h2_stream_ctx *stream, const char *buf, size_t blen, bool eos) { - /* If we already encountered an error, skip further writes */ if(!stream->xfer_result) { stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); @@ -1066,7 +917,6 @@ struct h2_stream_ctx *stream, const char *buf, size_t blen, bool eos) { - /* If we already encountered an error, skip further writes */ if(!stream->xfer_result) stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); @@ -1143,7 +993,8 @@ else stream->status_code = -1; - h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); + h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), + (bool)stream->closed); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -1167,14 +1018,12 @@ } break; case NGHTTP2_RST_STREAM: - stream->closed = TRUE; - if(frame->rst_stream.error_code) { - stream->reset = TRUE; - } + if(frame->rst_stream.error_code) + stream->reset_by_server = TRUE; Curl_multi_mark_dirty(data); break; case NGHTTP2_WINDOW_UPDATE: - if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) { + if(CURL_REQ_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) { /* need more data, force processing of transfer */ Curl_multi_mark_dirty(data); } @@ -1207,8 +1056,9 @@ return CURLE_OK; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) +#ifdef CURLVERBOSE +int Curl_nghttp2_fr_print(const nghttp2_frame *frame, char *buffer, + size_t blen) { switch(frame->hd.type) { case NGHTTP2_DATA: { @@ -1243,18 +1093,16 @@ return curl_msnprintf(buffer, blen, "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); } - case NGHTTP2_PUSH_PROMISE: { + case NGHTTP2_PUSH_PROMISE: return curl_msnprintf(buffer, blen, "FRAME[PUSH_PROMISE, len=%d, hend=%d]", (int)frame->hd.length, !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); - } - case NGHTTP2_PING: { + case NGHTTP2_PING: return curl_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 = CURL_ARRAYSIZE(scratch); @@ -1264,7 +1112,7 @@ memcpy(scratch, frame->goaway.opaque_data, len); scratch[len] = '\0'; return curl_msnprintf(buffer, blen, - "FRAME[GOAWAY, error=%d, reason='%s', " + "FRAME[GOAWAY, error=%u, reason='%s', " "last_stream=%d]", frame->goaway.error_code, scratch, frame->goaway.last_stream_id); } @@ -1289,23 +1137,23 @@ (void)session; DEBUGASSERT(data); - if(data && Curl_trc_cf_is_verbose(cf, data)) { + if(Curl_trc_cf_is_verbose(cf, data)) { char buffer[256]; int len; - len = fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_fr_print(frame, buffer, sizeof(buffer) - 1); buffer[len] = 0; CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); } if((frame->hd.type == NGHTTP2_GOAWAY) && !ctx->sent_goaway) { /* A GOAWAY not initiated by us, but by nghttp2 itself on detecting * a protocol error on the connection */ - failf(data, "nghttp2 shuts down connection with error %d: %s", + failf(data, "nghttp2 shuts down connection with error %u: %s", frame->goaway.error_code, nghttp2_http2_strerror(frame->goaway.error_code)); } return 0; } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp) @@ -1316,15 +1164,15 @@ int32_t stream_id = frame->hd.stream_id; DEBUGASSERT(data); -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_cf_is_verbose(cf, data)) { char buffer[256]; int len; - len = fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_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 */ +#endif /* CURLVERBOSE */ if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ @@ -1337,7 +1185,7 @@ 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", + CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %u", ctx->max_concurrent_streams); CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s", ctx->enable_push ? "TRUE" : "false"); @@ -1352,7 +1200,7 @@ * 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(CURL_WANT_SEND(data)) + if(CURL_REQ_WANT_SEND(data)) Curl_multi_mark_dirty(data); } break; @@ -1362,7 +1210,7 @@ ctx->goaway_error = frame->goaway.error_code; ctx->remote_max_sid = frame->goaway.last_stream_id; if(data) { - infof(data, "received GOAWAY, error=%u, last_stream=%u", + infof(data, "received GOAWAY, error=%u, last_stream=%d", ctx->goaway_error, ctx->remote_max_sid); Curl_multi_connchanged(data->multi); } @@ -1394,14 +1242,14 @@ data = nghttp2_session_get_stream_user_data(session, stream_id); if(data) { struct h2_stream_ctx *stream; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE char buffer[256]; int len; - len = fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_fr_print(frame, buffer, sizeof(buffer) - 1); buffer[len] = 0; failf(data, "[HTTP2] [%d] received invalid frame: %s, error %d: %s", stream_id, buffer, ngerr, nghttp2_strerror(ngerr)); -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ stream = H2_STREAM_CTX(ctx, data); if(stream) { nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1488,12 +1336,11 @@ stream->closed = TRUE; stream->error = error_code; - if(stream->error) { + if(stream->error) stream->reset = TRUE; - } if(stream->error) - CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", + CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %u)", stream_id, nghttp2_http2_strerror(error_code), error_code); else CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id); @@ -1502,7 +1349,7 @@ /* remove `data_s` from the nghttp2 stream */ rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); if(rv) { - infof(data_s, "http/2: failed to clear user_data for stream %u", + infof(data_s, "http/2: failed to clear user_data for stream %d", stream_id); DEBUGASSERT(0); } @@ -1640,8 +1487,11 @@ stream->push_headers = headp; } h = curl_maprintf("%s:%s", name, value); - if(h) - stream->push_headers[stream->push_headers_used++] = h; + if(!h) { + free_push_headers(stream); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + stream->push_headers[stream->push_headers_used++] = h; return 0; } @@ -1671,7 +1521,7 @@ cf_h2_header_error(cf, data_s, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } - hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r", + hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r", stream->status_code); result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO); if(result) { @@ -1779,7 +1629,7 @@ return (nread == 0) ? NGHTTP2_ERR_DEFERRED : nread; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static int error_callback(nghttp2_session *session, const char *msg, size_t len, @@ -1842,36 +1692,31 @@ CURLcode result; *pnlen = 0; - if(stream->error == NGHTTP2_REFUSED_STREAM) { - CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " - "connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ - data->state.refused_stream = TRUE; - return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - } - else if(stream->error != NGHTTP2_NO_ERROR) { - if(stream->resp_hds_complete && data->req.no_body) { - CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " - "not want a body anyway, ignore: %s (err %u)", - stream->id, nghttp2_http2_strerror(stream->error), - stream->error); - stream->close_handled = TRUE; - return CURLE_OK; + if(stream->reset) { + if(stream->error == NGHTTP2_REFUSED_STREAM) { + infof(data, "HTTP/2 stream %d refused by server, try again on a new " + "connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + data->state.refused_stream = TRUE; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } - failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", - stream->id, nghttp2_http2_strerror(stream->error), - stream->error); - return CURLE_HTTP2_STREAM; + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " + "not want a body anyway, ignore: %s (err %u)", + stream->id, nghttp2_http2_strerror(stream->error), + stream->error); + stream->close_handled = TRUE; + return CURLE_OK; + } + failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)", + stream->id, stream->reset_by_server ? "server" : "curl", + stream->error, nghttp2_http2_strerror(stream->error)); + return stream->error ? CURLE_HTTP2_STREAM : + (data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2); } - else if(stream->reset) { - failf(data, "HTTP/2 stream %u was reset", stream->id); - return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2; - } - - if(!stream->bodystarted) { - failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " - " all response header fields, treated as error", - stream->id); + else if(!stream->bodystarted) { + failf(data, "HTTP/2 stream %d was closed cleanly, but before getting " + "all response header fields, treated as error", stream->id); return CURLE_HTTP2_STREAM; } @@ -1888,8 +1733,8 @@ break; curlx_dyn_reset(&dbuf); result = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a", - (int)e->namelen, e->name, - (int)e->valuelen, e->value); + (int)e->namelen, e->name, + (int)e->valuelen, e->value); if(result) break; Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf), @@ -1988,7 +1833,7 @@ /* Defer flushing during the connect phase so that the SETTINGS and * other initial frames are sent together with the first request. * Unless we are 'connect_only' where the request will never come. */ - if(!cf->connected && !cf->conn->connect_only) + if(!cf->connected && !cf->conn->bits.connect_only) return CURLE_OK; return nw_out_flush(cf, data); } @@ -2060,10 +1905,9 @@ while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { stream = H2_STREAM_CTX(ctx, data); if(stream && (stream->closed || !data_max_bytes)) { - /* We would like to abort here and stop processing, so that - * the transfer loop can handle the data/close here. However, - * this may leave data in underlying buffers that will not - * be consumed. */ + /* We would like to abort here and stop processing, so that the transfer + * loop can handle the data/close here. This may leave data in + * underlying buffers that will not be consumed. */ if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data)) Curl_multi_mark_dirty(data); break; @@ -2112,10 +1956,15 @@ char *buf, size_t len, size_t *pnread) { struct cf_h2_ctx *ctx = cf->ctx; - struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + struct h2_stream_ctx *stream; CURLcode result, r2; struct cf_call_data save; + if(!data) + return CURLE_HTTP2; + + stream = H2_STREAM_CTX(ctx, data); + *pnread = 0; if(!stream) { /* Abnormal call sequence: either this transfer has never opened a stream @@ -2158,7 +2007,7 @@ /* pending data to send, need to be called again. Ideally, we * monitor the socket for POLLOUT, but when not SENDING * any more, we force processing of the transfer. */ - if(!CURL_WANT_SEND(data)) + if(!CURL_REQ_WANT_SEND(data)) Curl_multi_mark_dirty(data); } else if(r2) { @@ -2203,7 +2052,7 @@ return CURLE_OK; } /* Server closed before we got a response, this is an error */ - infof(data, "stream %u closed", stream->id); + infof(data, "stream %d closed", stream->id); return CURLE_SEND_ERROR; } @@ -2303,13 +2152,13 @@ } if(stream_id < 0) { - CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", + CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%d", nghttp2_strerror(stream_id), stream_id); result = CURLE_SEND_ERROR; goto out; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE #define MAX_ACC 60000 /* <64KB to account for some overhead */ if(Curl_trc_is_verbose(data)) { size_t acc = 0, i; @@ -2352,7 +2201,7 @@ out: CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu", stream ? stream->id : -1, result, *pnwritten); - Curl_safefree(nva); + curlx_safefree(nva); *pstream = stream; Curl_dynhds_free(&h2_headers); return result; @@ -2403,7 +2252,7 @@ /* 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) { - infof(data, "stream %u closed", stream->id); + infof(data, "stream %d closed", stream->id); result = CURLE_SEND_ERROR; goto out; } @@ -2534,6 +2383,125 @@ return result; } +static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; + CURLcode result = CURLE_OUT_OF_MEMORY; + int rc; + nghttp2_session_callbacks *cbs = NULL; + + DEBUGASSERT(!ctx->h2); + DEBUGASSERT(ctx->initialized); + + rc = nghttp2_session_callbacks_new(&cbs); + if(rc) { + failf(data, "Could not initialize nghttp2 callbacks"); + goto out; + } + + nghttp2_session_callbacks_set_send_callback(cbs, send_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs, + cf_h2_on_invalid_frame_recv); +#ifdef CURLVERBOSE + 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); + nghttp2_session_callbacks_set_on_begin_headers_callback( + cbs, on_begin_headers); + nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); +#ifdef CURLVERBOSE + nghttp2_session_callbacks_set_error_callback(cbs, error_callback); +#endif + + /* The nghttp2 session is not yet setup, do it */ + rc = h2_client_new(cf, cbs); + if(rc) { + failf(data, "Could not initialize nghttp2"); + goto out; + } + ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; + + if(ctx->via_h1_upgrade) { + /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted + * in the H1 request and we upgrade from there. This stream + * is opened implicitly as #1. */ + uint8_t binsettings[H2_BINSETTINGS_LEN]; + ssize_t rclen; + size_t binlen; /* length of the binsettings data */ + + rclen = populate_binsettings(binsettings, data); + + if(!curlx_sztouz(rclen, &binlen) || !binlen) { + failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); + result = CURLE_FAILED_INIT; + goto out; + } + + result = http2_data_setup(cf, data, &stream); + if(result) + goto out; + DEBUGASSERT(stream); + stream->id = 1; + /* queue SETTINGS frame (again) */ + rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen, + data->state.httpreq == HTTPREQ_HEAD, + NULL); + if(rc) { + failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id, + data); + if(rc) { + infof(data, "http/2: failed to set user_data for stream %d", + stream->id); + DEBUGASSERT(0); + } + CURL_TRC_CF(data, cf, "created session via Upgrade"); + } + else { + nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; + size_t ivlen; + + ivlen = populate_settings(iv, data, ctx); + rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, + iv, ivlen); + if(rc) { + failf(data, "nghttp2_submit_settings() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + } + + rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, + HTTP2_HUGE_WINDOW_SIZE); + if(rc) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + /* all set, traffic will be send on connect */ + result = CURLE_OK; + CURL_TRC_CF(data, cf, "[0] created h2 session%s", + ctx->via_h1_upgrade ? " (via h1 upgrade)" : ""); + +out: + if(cbs) + nghttp2_session_callbacks_del(cbs); + return result; +} + static CURLcode cf_h2_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -3051,10 +3019,10 @@ return NULL; } -char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) +char *curl_pushheader_byname(struct curl_pushheaders *h, const char *name) { (void)h; - (void)header; + (void)name; return NULL; }
diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h index 950b2f7..e38dc57 100644 --- a/Utilities/cmcurl/lib/http2.h +++ b/Utilities/cmcurl/lib/http2.h
@@ -36,6 +36,11 @@ */ void Curl_http2_ver(char *p, size_t len); +#ifdef CURLVERBOSE +int Curl_nghttp2_fr_print(const nghttp2_frame *frame, char *buffer, + size_t blen); +#endif + CURLcode Curl_http2_request_upgrade(struct dynbuf *req, struct Curl_easy *data); @@ -50,7 +55,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, struct connectdata *conn, int sockindex, - const char *ptr, size_t nread); + const char *mem, size_t nread); void *Curl_nghttp2_malloc(size_t size, void *user_data); void Curl_nghttp2_free(void *ptr, void *user_data); @@ -65,7 +70,6 @@ #define Curl_http2_request_upgrade(x, y) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_switch(a) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_upgrade(a, b, c, d, e) CURLE_UNSUPPORTED_PROTOCOL #define Curl_h2_http_1_1_error(x) 0 #endif
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index 598d8cb..55efa12 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -27,7 +27,7 @@ #include "urldata.h" #include "strcase.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "http_aws_sigv4.h" #include "curl_sha256.h" #include "transfer.h" @@ -38,22 +38,22 @@ #include <time.h> -#define HMAC_SHA256(k, kl, d, dl, o) \ - do { \ - result = Curl_hmacit(&Curl_HMAC_SHA256, \ - (const unsigned char *)k, \ - kl, \ - (const unsigned char *)d, \ - dl, o); \ - if(result) { \ - goto fail; \ - } \ +#define HMAC_SHA256(k, kl, d, dl, o) \ + do { \ + result = Curl_hmacit(&Curl_HMAC_SHA256, \ + (const unsigned char *)(k), \ + kl, \ + (const unsigned char *)(d), \ + dl, o); \ + if(result) { \ + goto fail; \ + } \ } while(0) #define TIMESTAMP_SIZE 17 /* hex-encoded with trailing null */ -#define SHA256_HEX_LENGTH (2 * CURL_SHA256_DIGEST_LENGTH + 1) +#define SHA256_HEX_LENGTH ((2 * CURL_SHA256_DIGEST_LENGTH) + 1) #define MAX_QUERY_COMPONENTS 128 @@ -62,20 +62,6 @@ struct dynbuf value; }; -static void dyn_array_free(struct dynbuf *db, size_t num_elements); -static void pair_array_free(struct pair *pair_array, size_t num_elements); -static CURLcode split_to_dyn_array(const char *source, - struct dynbuf db[MAX_QUERY_COMPONENTS], - size_t *num_splits); -static bool is_reserved_char(const char c); -static CURLcode uri_encode_path(struct Curl_str *original_path, - struct dynbuf *new_path); -static CURLcode encode_query_component(char *component, size_t len, - struct dynbuf *db); -static CURLcode http_aws_decode_encode(const char *in, size_t in_len, - struct dynbuf *out); -static bool should_urlencode(struct Curl_str *service_name); - static void sha256_to_hex(char *dst, unsigned char *sha) { Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH, @@ -129,6 +115,171 @@ } } +/* + * Frees all allocated strings in a dynbuf pair array, and the dynbuf itself + */ +static void pair_array_free(struct pair *pair_array, size_t num_elements) +{ + size_t index; + + for(index = 0; index != num_elements; index++) { + curlx_dyn_free(&pair_array[index].key); + curlx_dyn_free(&pair_array[index].value); + } +} + +/* + * Frees all allocated strings in a split dynbuf, and the dynbuf itself + */ +static void dyn_array_free(struct dynbuf *db, size_t num_elements) +{ + size_t index; + + for(index = 0; index < num_elements; index++) + curlx_dyn_free((&db[index])); +} + +/* + * Splits source string by SPLIT_BY, and creates an array of dynbuf in db. + * db is initialized by this function. + * Caller is responsible for freeing the array elements with dyn_array_free + */ + +#define SPLIT_BY '&' + +static CURLcode split_to_dyn_array(const char *source, + struct dynbuf db[MAX_QUERY_COMPONENTS], + size_t *num_splits_out) +{ + CURLcode result = CURLE_OK; + size_t len = strlen(source); + size_t pos; /* Position in result buffer */ + size_t start = 0; /* Start of current segment */ + size_t segment_length = 0; + size_t index = 0; + size_t num_splits = 0; + + /* Split source_ptr on SPLIT_BY and store the segment offsets and length in + * array */ + for(pos = 0; pos < len; pos++) { + if(source[pos] == SPLIT_BY) { + if(segment_length) { + curlx_dyn_init(&db[index], segment_length + 1); + result = curlx_dyn_addn(&db[index], &source[start], segment_length); + if(result) + goto fail; + + segment_length = 0; + index++; + if(++num_splits == MAX_QUERY_COMPONENTS) { + result = CURLE_TOO_LARGE; + goto fail; + } + } + start = pos + 1; + } + else { + segment_length++; + } + } + + if(segment_length) { + curlx_dyn_init(&db[index], segment_length + 1); + result = curlx_dyn_addn(&db[index], &source[start], segment_length); + if(!result) { + if(++num_splits == MAX_QUERY_COMPONENTS) + result = CURLE_TOO_LARGE; + } + } +fail: + *num_splits_out = num_splits; + return result; +} + +static bool is_reserved_char(const char c) +{ + return (ISALNUM(c) || ISURLPUNTCS(c)); +} + +static CURLcode uri_encode_path(struct Curl_str *original_path, + struct dynbuf *new_path) +{ + const char *p = curlx_str(original_path); + size_t i; + + for(i = 0; i < curlx_strlen(original_path); i++) { + /* Do not encode slashes or unreserved chars from RFC 3986 */ + CURLcode result = CURLE_OK; + unsigned char c = p[i]; + if(is_reserved_char(c) || c == '/') + result = curlx_dyn_addn(new_path, &c, 1); + else + result = curlx_dyn_addf(new_path, "%%%02X", c); + if(result) + return result; + } + + return CURLE_OK; +} + +/* Normalize the query part. Make sure %2B is left percent encoded, and not + decoded to plus, then encoded to space. +*/ +static CURLcode normalize_query(const char *string, size_t len, + struct dynbuf *db) +{ + CURLcode result = CURLE_OK; + + while(len && !result) { + unsigned char in = (unsigned char)*string; + if(('%' == in) && (len > 2) && + ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { + /* this is two hexadecimal digits following a '%' */ + in = (unsigned char)((curlx_hexval(string[1]) << 4) | + curlx_hexval(string[2])); + string += 3; + len -= 3; + if(in == '+') { + /* decodes to plus, so leave this encoded */ + result = curlx_dyn_addn(db, "%2B", 3); + continue; + } + } + else { + string++; + len--; + } + + if(is_reserved_char(in)) + /* Escape unreserved chars from RFC 3986 */ + result = curlx_dyn_addn(db, &in, 1); + else if(in == '+') + /* Encode '+' as space */ + result = curlx_dyn_add(db, "%20"); + else + result = curlx_dyn_addf(db, "%%%02X", in); + } + + return result; +} + +static bool should_urlencode(struct Curl_str *service_name) +{ + /* + * These services require unmodified (not additionally URL-encoded) URL + * paths. + * should_urlencode == true is equivalent to should_urlencode_uri_path + * from the AWS SDK. Urls are already normalized by the curl URL parser + */ + + if(curlx_str_cmp(service_name, "s3") || + curlx_str_cmp(service_name, "s3-express") || + curlx_str_cmp(service_name, "s3-outposts")) { + return false; + } + return true; +} + /* maximum length for the aws sivg4 parts */ #define MAX_SIGV4_LEN 64 #define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date")) @@ -182,8 +333,8 @@ if(compare_header_names(curr->data, next->data) == 0) { struct dynbuf buf; - char *colon_next; - char *val_next; + const char *colon_next; + const char *val_next; curlx_dyn_init(&buf, CURL_MAX_HTTP_HEADER); @@ -254,7 +405,7 @@ if(data->state.aptr.host) { /* remove /r/n as the separator for canonical request must be '\n' */ size_t pos = strcspn(data->state.aptr.host, "\n\r"); - fullhost = Curl_memdup0(data->state.aptr.host, pos); + fullhost = curlx_memdup0(data->state.aptr.host, pos); } else fullhost = curl_maprintf("host:%s", hostname); @@ -290,8 +441,9 @@ semi-colon, are not added to this list. */ for(l = data->set.headers; l; l = l->next) { - char *dupdata, *ptr; - char *sep = strchr(l->data, ':'); + char *dupdata; + const char *ptr; + const char *sep = strchr(l->data, ':'); if(!sep) sep = strchr(l->data, ';'); if(!sep || (*sep == ':' && !*(sep + 1))) @@ -493,7 +645,6 @@ static int compare_func(const void *a, const void *b) { - const struct pair *aa = a; const struct pair *bb = b; const size_t aa_key_len = curlx_dyn_len(&aa->key); @@ -528,6 +679,10 @@ return compare; } +/* @unittest 1979 */ +UNITTEST CURLcode canon_path(const char *q, size_t len, + struct dynbuf *new_path, + bool do_uri_encode); UNITTEST CURLcode canon_path(const char *q, size_t len, struct dynbuf *new_path, bool do_uri_encode) @@ -554,6 +709,8 @@ return result; } +/* @unittest 1980 */ +UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq); UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) { CURLcode result = CURLE_OK; @@ -578,9 +735,9 @@ for(index = 0; index < num_query_components; index++) { const char *in_key; size_t in_key_len; - char *offset; + const char *offset; size_t query_part_len = curlx_dyn_len(&query_array[index]); - char *query_part = curlx_dyn_ptr(&query_array[index]); + const char *query_part = curlx_dyn_ptr(&query_array[index]); in_key = query_part; @@ -593,13 +750,15 @@ in_key_len = offset - in_key; } - curlx_dyn_init(&encoded_query_array[index].key, query_part_len * 3 + 1); - curlx_dyn_init(&encoded_query_array[index].value, query_part_len * 3 + 1); + curlx_dyn_init(&encoded_query_array[index].key, + (query_part_len * 3) + 1); + curlx_dyn_init(&encoded_query_array[index].value, + (query_part_len * 3) + 1); counted_query_components++; /* Decode/encode the key */ - result = http_aws_decode_encode(in_key, in_key_len, - &encoded_query_array[index].key); + result = normalize_query(in_key, in_key_len, + &encoded_query_array[index].key); if(result) { goto fail; } @@ -609,8 +768,8 @@ size_t in_value_len; const char *in_value = offset + 1; in_value_len = query_part + query_part_len - (offset + 1); - result = http_aws_decode_encode(in_value, in_value_len, - &encoded_query_array[index].value); + result = normalize_query(in_value, in_value_len, + &encoded_query_array[index].value); if(result) { goto fail; } @@ -636,8 +795,8 @@ if(index) result = curlx_dyn_addn(dq, "&", 1); if(!result) { - char *key_ptr = curlx_dyn_ptr(&encoded_query_array[index].key); - char *value_ptr = curlx_dyn_ptr(&encoded_query_array[index].value); + const char *key_ptr = curlx_dyn_ptr(&encoded_query_array[index].key); + const char *value_ptr = curlx_dyn_ptr(&encoded_query_array[index].value); size_t vlen = curlx_dyn_len(&encoded_query_array[index].value); if(value_ptr && vlen) { result = curlx_dyn_addf(dq, "%s=%s", key_ptr, value_ptr); @@ -834,8 +993,8 @@ goto fail; result = canon_path(data->state.up.path, strlen(data->state.up.path), - &canonical_path, - should_urlencode(&service)); + &canonical_path, + should_urlencode(&service)); if(result) goto fail; result = CURLE_OUT_OF_MEMORY; @@ -954,8 +1113,8 @@ Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], curlx_str(&provider0), curlx_strlen(&provider0)); - curlx_free(data->state.aptr.userpwd); - data->state.aptr.userpwd = auth_headers; + curlx_free(data->req.userpwd); + data->req.userpwd = auth_headers; data->state.authhost.done = TRUE; result = CURLE_OK; @@ -973,172 +1132,4 @@ return result; } -/* - * Frees all allocated strings in a dynbuf pair array, and the dynbuf itself - */ - -static void pair_array_free(struct pair *pair_array, size_t num_elements) -{ - size_t index; - - for(index = 0; index != num_elements; index++) { - curlx_dyn_free(&pair_array[index].key); - curlx_dyn_free(&pair_array[index].value); - } -} - -/* - * Frees all allocated strings in a split dynbuf, and the dynbuf itself - */ - -static void dyn_array_free(struct dynbuf *db, size_t num_elements) -{ - size_t index; - - for(index = 0; index < num_elements; index++) - curlx_dyn_free((&db[index])); -} - -/* - * Splits source string by SPLIT_BY, and creates an array of dynbuf in db. - * db is initialized by this function. - * Caller is responsible for freeing the array elements with dyn_array_free - */ - -#define SPLIT_BY '&' - -static CURLcode split_to_dyn_array(const char *source, - struct dynbuf db[MAX_QUERY_COMPONENTS], - size_t *num_splits_out) -{ - CURLcode result = CURLE_OK; - size_t len = strlen(source); - size_t pos; /* Position in result buffer */ - size_t start = 0; /* Start of current segment */ - size_t segment_length = 0; - size_t index = 0; - size_t num_splits = 0; - - /* Split source_ptr on SPLIT_BY and store the segment offsets and length in - * array */ - for(pos = 0; pos < len; pos++) { - if(source[pos] == SPLIT_BY) { - if(segment_length) { - curlx_dyn_init(&db[index], segment_length + 1); - result = curlx_dyn_addn(&db[index], &source[start], segment_length); - if(result) - goto fail; - - segment_length = 0; - index++; - if(++num_splits == MAX_QUERY_COMPONENTS) { - result = CURLE_TOO_LARGE; - goto fail; - } - } - start = pos + 1; - } - else { - segment_length++; - } - } - - if(segment_length) { - curlx_dyn_init(&db[index], segment_length + 1); - result = curlx_dyn_addn(&db[index], &source[start], segment_length); - if(!result) { - if(++num_splits == MAX_QUERY_COMPONENTS) - result = CURLE_TOO_LARGE; - } - } -fail: - *num_splits_out = num_splits; - return result; -} - -static bool is_reserved_char(const char c) -{ - return (ISALNUM(c) || ISURLPUNTCS(c)); -} - -static CURLcode uri_encode_path(struct Curl_str *original_path, - struct dynbuf *new_path) -{ - const char *p = curlx_str(original_path); - size_t i; - - for(i = 0; i < curlx_strlen(original_path); i++) { - /* Do not encode slashes or unreserved chars from RFC 3986 */ - CURLcode result = CURLE_OK; - unsigned char c = p[i]; - if(is_reserved_char(c) || c == '/') - result = curlx_dyn_addn(new_path, &c, 1); - else - result = curlx_dyn_addf(new_path, "%%%02X", c); - if(result) - return result; - } - - return CURLE_OK; -} - -static CURLcode encode_query_component(char *component, size_t len, - struct dynbuf *db) -{ - size_t i; - for(i = 0; i < len; i++) { - CURLcode result = CURLE_OK; - unsigned char this_char = component[i]; - - if(is_reserved_char(this_char)) - /* Escape unreserved chars from RFC 3986 */ - result = curlx_dyn_addn(db, &this_char, 1); - else if(this_char == '+') - /* Encode '+' as space */ - result = curlx_dyn_add(db, "%20"); - else - result = curlx_dyn_addf(db, "%%%02X", this_char); - if(result) - return result; - } - - return CURLE_OK; -} - -/* - * Populates a dynbuf containing url_encode(url_decode(in)) - */ - -static CURLcode http_aws_decode_encode(const char *in, size_t in_len, - struct dynbuf *out) -{ - char *out_s; - size_t out_s_len; - CURLcode result = - Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA); - - if(!result) { - result = encode_query_component(out_s, out_s_len, out); - Curl_safefree(out_s); - } - return result; -} - -static bool should_urlencode(struct Curl_str *service_name) -{ - /* - * These services require unmodified (not additionally URL-encoded) URL - * paths. - * should_urlencode == true is equivalent to should_urlencode_uri_path - * from the AWS SDK. Urls are already normalized by the curl URL parser - */ - - if(curlx_str_cmp(service_name, "s3") || - curlx_str_cmp(service_name, "s3-express") || - curlx_str_cmp(service_name, "s3-outposts")) { - return false; - } - return true; -} - #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_AWS */
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.h b/Utilities/cmcurl/lib/http_aws_sigv4.h index 25009c6..7dd0024 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.h +++ b/Utilities/cmcurl/lib/http_aws_sigv4.h
@@ -31,10 +31,4 @@ /* this is for creating aws_sigv4 header output */ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data); -#ifdef UNITTESTS -UNITTEST CURLcode canon_path(const char *q, size_t len, - struct dynbuf *new_path, bool normalize); -UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq); -#endif - #endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index f78a89e..aa45c79 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c
@@ -33,12 +33,12 @@ #include "curlx/strparse.h" /* - * Chunk format (simplified): - * - * <HEX SIZE>[ chunk extension ] CRLF - * <DATA> CRLF - * - * Highlights from RFC2616 section 3.6 say: + Chunk format (simplified): + + <HEX SIZE>[ chunk extension ] CRLF + <DATA> CRLF + + Highlights from RFC2616 section 3.6 say: The chunked encoding modifies the body of a message in order to transfer it as a series of chunks, each with its own size indicator, @@ -228,7 +228,7 @@ case CHUNK_POSTLF: if(*buf == 0x0a) { /* The last one before we go back to hex state and start all over. */ - Curl_httpchunk_reset(data, ch, ch->ignore_body); + Curl_httpchunk_reset(data, ch, (bool)ch->ignore_body); } else if(*buf != 0x0d) { ch->state = CHUNK_FAILED; @@ -242,7 +242,7 @@ case CHUNK_TRAILER: if((*buf == 0x0d) || (*buf == 0x0a)) { - char *tr = curlx_dyn_ptr(&ch->trailer); + const char *tr = curlx_dyn_ptr(&ch->trailer); /* this is the end of a trailer, but if the trailer was zero bytes there was no trailer and we move on */ @@ -258,12 +258,12 @@ size_t trlen = curlx_dyn_len(&ch->trailer); if(cw_next) result = Curl_cwriter_write(data, cw_next, - CLIENTWRITE_HEADER| + CLIENTWRITE_HEADER | CLIENTWRITE_TRAILER, tr, trlen); else result = Curl_client_write(data, - CLIENTWRITE_HEADER| + CLIENTWRITE_HEADER | CLIENTWRITE_TRAILER, tr, trlen); if(result) { @@ -460,7 +460,7 @@ }; /* max length of an HTTP chunk that we want to generate */ -#define CURL_CHUNKED_MINLEN (1024) +#define CURL_CHUNKED_MINLEN 1024 #define CURL_CHUNKED_MAXLEN (64 * 1024) struct chunked_reader { @@ -517,7 +517,7 @@ for(tr = trailers; tr; tr = tr->next) { /* only add correctly formatted trailers */ - char *ptr = strchr(tr->data, ':'); + const char *ptr = strchr(tr->data, ':'); if(!ptr || *(ptr + 1) != ' ') { infof(data, "Malformatted trailing header, skipping trailer"); continue; @@ -583,7 +583,7 @@ if(!result) result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n); CURL_TRC_READ(data, "http_chunk, made chunk of %zu bytes -> %d", - nread, result); + nread, result); if(result) return result; } @@ -602,7 +602,7 @@ CURLcode result = CURLE_READ_ERROR; *pnread = 0; - *peos = ctx->eos; + *peos = (bool)ctx->eos; if(!ctx->eos) { if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) {
diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c index ccc67f5..b700707 100644 --- a/Utilities/cmcurl/lib/http_digest.c +++ b/Utilities/cmcurl/lib/http_digest.c
@@ -69,7 +69,7 @@ { CURLcode result; unsigned char *path = NULL; - char *tmp = NULL; + const char *tmp = NULL; char *response; size_t len; bool have_chlg; @@ -91,7 +91,7 @@ return CURLE_NOT_BUILT_IN; #else digest = &data->state.proxydigest; - allocuserpwd = &data->state.aptr.proxyuserpwd; + allocuserpwd = &data->req.proxyuserpwd; userp = data->state.aptr.proxyuser; passwdp = data->state.aptr.proxypasswd; authp = &data->state.authproxy; @@ -99,13 +99,13 @@ } else { digest = &data->state.digest; - allocuserpwd = &data->state.aptr.userpwd; + allocuserpwd = &data->req.userpwd; userp = data->state.aptr.user; passwdp = data->state.aptr.passwd; authp = &data->state.authhost; } - Curl_safefree(*allocuserpwd); + curlx_safefree(*allocuserpwd); /* not set means empty */ if(!userp) @@ -125,7 +125,7 @@ return CURLE_OK; } - /* So IE browsers < v7 cut off the URI part at the query part when they + /* IE browsers < v7 cut off the URI part at the query part when they evaluate the MD5 and some (IIS?) servers work with them so we may need to do the Digest IE-style. Note that the different ways cause different MD5 sums to get sent.
diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c index be1063d..74d63d6 100644 --- a/Utilities/cmcurl/lib/http_negotiate.c +++ b/Utilities/cmcurl/lib/http_negotiate.c
@@ -217,13 +217,13 @@ if(proxy) { #ifndef CURL_DISABLE_PROXY - curlx_free(data->state.aptr.proxyuserpwd); - data->state.aptr.proxyuserpwd = userp; + curlx_free(data->req.proxyuserpwd); + data->req.proxyuserpwd = userp; #endif } else { - curlx_free(data->state.aptr.userpwd); - data->state.aptr.userpwd = userp; + curlx_free(data->req.userpwd); + data->req.userpwd = userp; } curlx_free(base64); @@ -233,19 +233,19 @@ } *state = GSS_AUTHSENT; - #ifdef HAVE_GSSAPI +#ifdef HAVE_GSSAPI if(neg_ctx->status == GSS_S_COMPLETE || neg_ctx->status == GSS_S_CONTINUE_NEEDED) { *state = GSS_AUTHDONE; } - #else - #ifdef USE_WINDOWS_SSPI +#else +#ifdef USE_WINDOWS_SSPI if(neg_ctx->status == SEC_E_OK || neg_ctx->status == SEC_I_CONTINUE_NEEDED) { *state = GSS_AUTHDONE; } - #endif - #endif +#endif +#endif } if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) {
diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c index c4dbdf9..82b0505 100644 --- a/Utilities/cmcurl/lib/http_ntlm.c +++ b/Utilities/cmcurl/lib/http_ntlm.c
@@ -139,7 +139,7 @@ if(proxy) { #ifndef CURL_DISABLE_PROXY - allocuserpwd = &data->state.aptr.proxyuserpwd; + allocuserpwd = &data->req.proxyuserpwd; userp = data->state.aptr.proxyuser; passwdp = data->state.aptr.proxypasswd; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? @@ -152,7 +152,7 @@ #endif } else { - allocuserpwd = &data->state.aptr.userpwd; + allocuserpwd = &data->req.userpwd; userp = data->state.aptr.user; passwdp = data->state.aptr.passwd; service = data->set.str[STRING_SERVICE_NAME] ? @@ -176,9 +176,9 @@ #ifdef USE_WINDOWS_SSPI if(!Curl_pSecFn) { /* not thread-safe and leaks - use curl_global_init() to avoid */ - CURLcode err = Curl_sspi_global_init(); + result = Curl_sspi_global_init(); if(!Curl_pSecFn) - return err; + return result; } #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS ntlm->sslContext = conn->sslContext; @@ -242,7 +242,7 @@ data->info.proxyauthpicked = CURLAUTH_NTLM; else data->info.httpauthpicked = CURLAUTH_NTLM; - Curl_safefree(*allocuserpwd); + curlx_safefree(*allocuserpwd); authp->done = TRUE; break; }
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index c5b2f3e..a4bdd7e 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -34,7 +34,6 @@ #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" #include "connect.h" -#include "transfer.h" #include "vauth/vauth.h" #include "curlx/strparse.h" @@ -165,7 +164,7 @@ void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, const char **phostname, - int *pport, bool *pipv6_ip) + uint16_t *pport, bool *pipv6_ip) { DEBUGASSERT(cf); DEBUGASSERT(cf->conn); @@ -184,10 +183,7 @@ else *pport = cf->conn->remote_port; - if(*phostname != cf->conn->host.name) - *pipv6_ip = (strchr(*phostname, ':') != NULL); - else - *pipv6_ip = cf->conn->bits.ipv6_ip; + *pipv6_ip = (strchr(*phostname, ':') != NULL); } struct cf_proxy_ctx { @@ -203,14 +199,14 @@ struct cf_proxy_ctx *ctx = cf->ctx; const char *hostname = NULL; char *authority = NULL; - int port; + uint16_t port; bool ipv6_ip; CURLcode result; struct httpreq *req = NULL; Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - authority = curl_maprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname, + authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, ipv6_ip ? "]" : "", port); if(!authority) { result = CURLE_OUT_OF_MEMORY; @@ -237,9 +233,9 @@ goto out; } - if(data->state.aptr.proxyuserpwd) { + if(data->req.proxyuserpwd) { result = Curl_dynhds_h1_cadd_line(&req->headers, - data->state.aptr.proxyuserpwd); + data->req.proxyuserpwd); if(result) goto out; } @@ -380,7 +376,6 @@ { struct cf_proxy_ctx *ctx = cf->ctx; - (void)data; CURL_TRC_CF(data, cf, "destroy"); curlx_free(ctx); } @@ -396,7 +391,7 @@ struct Curl_cftype Curl_cft_http_proxy = { "HTTP-PROXY", - CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SETUP, 0, http_proxy_cf_destroy, http_proxy_cf_connect,
diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h index c27787b..155b222 100644 --- a/Utilities/cmcurl/lib/http_proxy.h +++ b/Utilities/cmcurl/lib/http_proxy.h
@@ -37,7 +37,7 @@ void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, const char **phostname, - int *pport, bool *pipv6_ip); + uint16_t *pport, bool *pipv6_ip); CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf,
diff --git a/Utilities/cmcurl/lib/httpsrr.c b/Utilities/cmcurl/lib/httpsrr.c index d642a4c..90fcb52 100644 --- a/Utilities/cmcurl/lib/httpsrr.c +++ b/Utilities/cmcurl/lib/httpsrr.c
@@ -29,7 +29,8 @@ #include "httpsrr.h" #include "connect.h" #include "curl_trc.h" -#include "strdup.h" +#include "curlx/strdup.h" +#include "curlx/inet_ntop.h" static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len, unsigned char *alpns) @@ -68,73 +69,166 @@ return CURLE_OK; } -CURLcode Curl_httpsrr_set(struct Curl_easy *data, - struct Curl_https_rrinfo *hi, +#ifdef CURLVERBOSE + +static CURLcode httpsrr_print_addr(struct dynbuf *dyn, + int ai_family, + const uint8_t *addr, + size_t total_len) +{ + char buf[MAX_IPADR_LEN]; + size_t i, alen = (ai_family == AF_INET6) ? 16 : 4; + const char *sep = ""; + CURLcode result = CURLE_OK; + + for(i = 0; (i < (total_len / alen)) && !result; ++i) { + if(!curlx_inet_ntop(ai_family, addr + (i * alen), buf, sizeof(buf))) + result = curlx_dyn_add(dyn, "<error parsing address>"); + else + result = curlx_dyn_addf(dyn, "%s%s", sep, buf); + sep = ","; + } + return result; +} + +void Curl_httpsrr_trace(struct Curl_easy *data, + struct Curl_https_rrinfo *rr) +{ + struct dynbuf tmp; + CURLcode result; + + if(!rr || !rr->complete) { + CURL_TRC_DNS(data, "[HTTPS-RR] not available"); + return; + } + curlx_dyn_init(&tmp, 1024); + result = Curl_httpsrr_print(&tmp, rr); + if(!result) + CURL_TRC_DNS(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); + else + CURL_TRC_DNS(data, "Error printing HTTPS-RR information"); + curlx_dyn_free(&tmp); +} + +CURLcode Curl_httpsrr_print(struct dynbuf *tmp, + struct Curl_https_rrinfo *rr) +{ + CURLcode result; + int i; + + curlx_dyn_reset(tmp); + result = curlx_dyn_addf(tmp, "%u %s", rr->priority, + rr->target ? rr->target : "."); + if(!result && rr->mandatory) + result = curlx_dyn_add(tmp, " mandatory-keys(ignored)"); + if(!result && rr->alpns[0]) { + const char *sep = "", *name; + result = curlx_dyn_add(tmp, " alpn="); + for(i = 0; !result && (i < 4); ++i) { + switch(rr->alpns[i]) { + case ALPN_h1: + name = "http/1.1"; + break; + case ALPN_h2: + name = "h2"; + break; + case ALPN_h3: + name = "h3"; + break; + default: + name = NULL; + } + if(name) { + result = curlx_dyn_addf(tmp, "%s%s", sep, name); + sep = ","; + } + } + } + if(!result && rr->port_set) { + result = curlx_dyn_addf(tmp, " port=%u", rr->port); + } + if(!result && rr->no_def_alpn) + result = curlx_dyn_add(tmp, " no-default-alpn"); + if(!result && rr->ipv6hints_len) { + result = curlx_dyn_add(tmp, " ipv6hint="); + if(!result) + result = httpsrr_print_addr( + tmp, AF_INET6, rr->ipv6hints, rr->ipv6hints_len); + } + if(!result && rr->ipv4hints_len) { + result = curlx_dyn_add(tmp, " ipv4hint="); + if(!result) + result = httpsrr_print_addr( + tmp, AF_INET, rr->ipv4hints, rr->ipv4hints_len); + } + if(!result && rr->echconfiglist_len) + result = curlx_dyn_addf(tmp, " ech=<%zu bytes>", rr->echconfiglist_len); + + return result; +} + +#endif /* CURLVERBOSE */ + +CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, uint16_t rrkey, const uint8_t *val, size_t vlen) { CURLcode result = CURLE_OK; switch(rrkey) { case HTTPS_RR_CODE_MANDATORY: - CURL_TRC_DNS(data, "HTTPS RR MANDATORY left to implement"); + rr->mandatory = TRUE; break; case HTTPS_RR_CODE_ALPN: /* str_list */ - result = httpsrr_decode_alpn(val, vlen, hi->alpns); - CURL_TRC_DNS(data, "HTTPS RR ALPN: %u %u %u %u", - hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]); + result = httpsrr_decode_alpn(val, vlen, rr->alpns); break; case HTTPS_RR_CODE_NO_DEF_ALPN: if(vlen) /* no data */ return CURLE_BAD_FUNCTION_ARGUMENT; - hi->no_def_alpn = TRUE; - CURL_TRC_DNS(data, "HTTPS RR no-def-alpn"); + rr->no_def_alpn = TRUE; break; case HTTPS_RR_CODE_IPV4: /* addr4 list */ if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */ return CURLE_BAD_FUNCTION_ARGUMENT; - curlx_free(hi->ipv4hints); - hi->ipv4hints = Curl_memdup(val, vlen); - if(!hi->ipv4hints) + curlx_free(rr->ipv4hints); + rr->ipv4hints = curlx_memdup(val, vlen); + if(!rr->ipv4hints) return CURLE_OUT_OF_MEMORY; - hi->ipv4hints_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR IPv4"); + rr->ipv4hints_len = vlen; break; case HTTPS_RR_CODE_ECH: if(!vlen) return CURLE_BAD_FUNCTION_ARGUMENT; - curlx_free(hi->echconfiglist); - hi->echconfiglist = Curl_memdup(val, vlen); - if(!hi->echconfiglist) + curlx_free(rr->echconfiglist); + rr->echconfiglist = curlx_memdup(val, vlen); + if(!rr->echconfiglist) return CURLE_OUT_OF_MEMORY; - hi->echconfiglist_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR ECH"); + rr->echconfiglist_len = vlen; break; case HTTPS_RR_CODE_IPV6: /* addr6 list */ if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */ return CURLE_BAD_FUNCTION_ARGUMENT; - curlx_free(hi->ipv6hints); - hi->ipv6hints = Curl_memdup(val, vlen); - if(!hi->ipv6hints) + curlx_free(rr->ipv6hints); + rr->ipv6hints = curlx_memdup(val, vlen); + if(!rr->ipv6hints) return CURLE_OUT_OF_MEMORY; - hi->ipv6hints_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR IPv6"); + rr->ipv6hints_len = vlen; break; case HTTPS_RR_CODE_PORT: if(vlen != 2) return CURLE_BAD_FUNCTION_ARGUMENT; - hi->port = (unsigned short)((val[0] << 8) | val[1]); - CURL_TRC_DNS(data, "HTTPS RR port %u", hi->port); + rr->port = (uint16_t)((val[0] << 8) | val[1]); + rr->port_set = TRUE; break; default: - CURL_TRC_DNS(data, "HTTPS RR unknown code"); + /* unknown code */ break; } return result; } -struct Curl_https_rrinfo * -Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo) +struct Curl_https_rrinfo *Curl_httpsrr_dup_move( + struct Curl_https_rrinfo *rrinfo) { - struct Curl_https_rrinfo *dup = Curl_memdup(rrinfo, sizeof(*rrinfo)); + struct Curl_https_rrinfo *dup = curlx_memdup(rrinfo, sizeof(*rrinfo)); if(dup) memset(rrinfo, 0, sizeof(*rrinfo)); return dup; @@ -142,17 +236,27 @@ void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo) { - Curl_safefree(rrinfo->target); - Curl_safefree(rrinfo->echconfiglist); - Curl_safefree(rrinfo->ipv4hints); - Curl_safefree(rrinfo->ipv6hints); - Curl_safefree(rrinfo->rrname); + curlx_safefree(rrinfo->target); + curlx_safefree(rrinfo->echconfiglist); + curlx_safefree(rrinfo->ipv4hints); + curlx_safefree(rrinfo->ipv6hints); + curlx_safefree(rrinfo->rrname); + rrinfo->complete = FALSE; +} + +bool Curl_httpsrr_applicable(struct Curl_easy *data, + const struct Curl_https_rrinfo *rr) +{ + if(!data->conn || !rr) + return FALSE; + return (!rr->target || !rr->target[0] || + (rr->target[0] == '.' && !rr->target[1])) && + (!rr->port_set || rr->port == data->conn->remote_port); } #ifdef USE_ARES -static CURLcode httpsrr_opt(struct Curl_easy *data, - const ares_dns_rr_t *rr, +static CURLcode httpsrr_opt(const ares_dns_rr_t *rr, ares_dns_rr_key_t key, size_t idx, struct Curl_https_rrinfo *hinfo) { @@ -161,11 +265,10 @@ size_t len = 0; code = ares_dns_rr_get_opt(rr, key, idx, &val, &len); - return Curl_httpsrr_set(data, hinfo, code, val, len); + return Curl_httpsrr_set(hinfo, code, val, len); } -CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, - const ares_dns_record_t *dnsrec, +CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, struct Curl_https_rrinfo *hinfo) { CURLcode result = CURLE_OK; @@ -188,19 +291,18 @@ result = CURLE_OUT_OF_MEMORY; goto out; } - CURL_TRC_DNS(data, "HTTPS RR target: %s", hinfo->target); } - CURL_TRC_DNS(data, "HTTPS RR priority: %u", - ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY)); + hinfo->priority = ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY); for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS); opt++) { - result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); + result = httpsrr_opt(rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); if(result) break; } } out: - Curl_safefree(hinfo->rrname); + hinfo->complete = !result; + curlx_safefree(hinfo->rrname); return result; }
diff --git a/Utilities/cmcurl/lib/httpsrr.h b/Utilities/cmcurl/lib/httpsrr.h index 8fe7c0c..28a790d 100644 --- a/Utilities/cmcurl/lib/httpsrr.h +++ b/Utilities/cmcurl/lib/httpsrr.h
@@ -35,6 +35,7 @@ #define MAX_HTTPSRR_ALPNS 4 struct Curl_easy; +struct dynbuf; struct Curl_https_rrinfo { char *rrname; /* if NULL, the same as the URL hostname */ @@ -51,20 +52,26 @@ size_t ipv6hints_len; unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */ /* store parsed alpnid entries in the array, end with ALPN_none */ - int port; /* -1 means not set */ + uint16_t port; uint16_t priority; BIT(no_def_alpn); /* keytag = 2 */ + BIT(mandatory); /* keytag = 0 */ + BIT(port_set); /* port value has been assigned */ + BIT(complete); /* values have been successfully assigned */ }; -CURLcode Curl_httpsrr_set(struct Curl_easy *data, - struct Curl_https_rrinfo *hi, +CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, uint16_t rrkey, const uint8_t *val, size_t vlen); -struct Curl_https_rrinfo * -Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo); +struct Curl_https_rrinfo *Curl_httpsrr_dup_move( + struct Curl_https_rrinfo *rrinfo); void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); +/* TRUE if the record is applicable to the transfer and its connection. */ +bool Curl_httpsrr_applicable(struct Curl_easy *data, + const struct Curl_https_rrinfo *rr); + /* * Code points for DNS wire format SvcParams as per RFC 9460 */ @@ -77,10 +84,19 @@ #define HTTPS_RR_CODE_IPV6 0x06 #ifdef USE_ARES -CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, - const ares_dns_record_t *dnsrec, +CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, struct Curl_https_rrinfo *hinfo); #endif /* USE_ARES */ + +#ifdef CURLVERBOSE +CURLcode Curl_httpsrr_print(struct dynbuf *tmp, + struct Curl_https_rrinfo *rr); +void Curl_httpsrr_trace(struct Curl_easy *data, + struct Curl_https_rrinfo *rr); +#else +#define Curl_httpsrr_trace(a, b) Curl_nop_stmt +#endif + #endif /* USE_HTTPSRR */ #endif /* HEADER_CURL_HTTPSRR_H */
diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index f055882..f2b954e 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c
@@ -33,11 +33,11 @@ #include <idn2.h> #if defined(_WIN32) && defined(UNICODE) -#define IDN2_LOOKUP(name, host, flags) \ +#define IDN2_LOOKUP(name, host, flags) \ idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) #else -#define IDN2_LOOKUP(name, host, flags) \ - idn2_lookup_ul((const char *)name, (char **)host, flags) +#define IDN2_LOOKUP(name, host, flags) \ + idn2_lookup_ul((const char *)(name), (char **)(host), flags) #endif #endif /* USE_LIBIDN2 */ @@ -143,20 +143,6 @@ #ifdef USE_WIN32_IDN /* using Windows kernel32 and normaliz libraries. */ -#if (!defined(_WIN32_WINNT) || _WIN32_WINNT < _WIN32_WINNT_VISTA) && \ - (!defined(WINVER) || WINVER < 0x600) -WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags, - const WCHAR *lpUnicodeCharStr, - int cchUnicodeChar, - WCHAR *lpASCIICharStr, - int cchASCIIChar); -WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, - const WCHAR *lpASCIICharStr, - int cchASCIIChar, - WCHAR *lpUnicodeCharStr, - int cchUnicodeChar); -#endif - #define IDN_MAX_LENGTH 255 static char *idn_curlx_convert_wchar_to_UTF8(const wchar_t *str_w, int chars) @@ -362,7 +348,7 @@ */ void Curl_free_idnconverted_hostname(struct hostname *host) { - Curl_safefree(host->encalloc); + curlx_safefree(host->encalloc); } #endif /* USE_IDN */
diff --git a/Utilities/cmcurl/lib/idn.h b/Utilities/cmcurl/lib/idn.h index 42613af..90d8e81 100644 --- a/Utilities/cmcurl/lib/idn.h +++ b/Utilities/cmcurl/lib/idn.h
@@ -30,7 +30,7 @@ #define USE_IDN void Curl_free_idnconverted_hostname(struct hostname *host); CURLcode Curl_idn_decode(const char *input, char **output); -CURLcode Curl_idn_encode(const char *input, char **output); +CURLcode Curl_idn_encode(const char *puny, char **output); #else #define Curl_free_idnconverted_hostname(x) #define Curl_idn_decode(x) NULL
diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c index d940c87..b71254a 100644 --- a/Utilities/cmcurl/lib/if2ip.c +++ b/Utilities/cmcurl/lib/if2ip.c
@@ -203,20 +203,20 @@ return IF2IP_NOT_FOUND; dummy = CURL_SOCKET(AF_INET, SOCK_STREAM, 0); - if(CURL_SOCKET_BAD == dummy) + if(dummy == CURL_SOCKET_BAD) return IF2IP_NOT_FOUND; memset(&req, 0, sizeof(req)); memcpy(req.ifr_name, interf, len + 1); req.ifr_addr.sa_family = AF_INET; -#if defined(__GNUC__) && defined(_AIX) +#if defined(CURL_HAVE_DIAG) && defined(_AIX) /* Suppress warning inside system headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshift-sign-overflow" #endif if(ioctl(dummy, SIOCGIFADDR, &req) < 0) { -#if defined(__GNUC__) && defined(_AIX) +#if defined(CURL_HAVE_DIAG) && defined(_AIX) #pragma GCC diagnostic pop #endif sclose(dummy);
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index 0af1878..5ef2a2c 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c
@@ -35,6 +35,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "imap.h" #ifndef CURL_DISABLE_IMAP @@ -53,7 +55,6 @@ #endif #include "curlx/dynbuf.h" -#include "urldata.h" #include "sendf.h" #include "curl_trc.h" #include "hostip.h" @@ -61,7 +62,6 @@ #include "transfer.h" #include "escape.h" #include "pingpong.h" -#include "imap.h" #include "mime.h" #include "curlx/strparse.h" #include "strcase.h" @@ -137,124 +137,10 @@ BIT(uidvalidity_set); }; -/* Local API functions */ -static CURLcode imap_regular_transfer(struct Curl_easy *data, - struct IMAP *imap, - bool *done); -static CURLcode imap_do(struct Curl_easy *data, bool *done); -static CURLcode imap_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode imap_connect(struct Curl_easy *data, bool *done); -static CURLcode imap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode imap_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode imap_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static char *imap_atom(const char *str, bool escape_only); -static CURLcode imap_sendf(struct Curl_easy *data, - struct imap_conn *imapc, - const char *fmt, ...) CURL_PRINTF(3, 4); -static CURLcode imap_parse_url_options(struct connectdata *conn, - struct imap_conn *imapc); -static CURLcode imap_parse_url_path(struct Curl_easy *data, - struct IMAP *imap); -static CURLcode imap_parse_custom_request(struct Curl_easy *data, - struct IMAP *imap); -static CURLcode imap_perform_authenticate(struct Curl_easy *data, - const char *mech, - const struct bufref *initresp); -static CURLcode imap_continue_authenticate(struct Curl_easy *data, - const char *mech, - const struct bufref *resp); -static CURLcode imap_cancel_authenticate(struct Curl_easy *data, - const char *mech); -static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out); -static void imap_easy_reset(struct IMAP *imap); - -/* - * IMAP protocol handler. - */ - -const struct Curl_handler Curl_handler_imap = { - "imap", /* scheme */ - imap_setup_connection, /* setup_connection */ - imap_do, /* do_it */ - imap_done, /* done */ - ZERO_NULL, /* do_more */ - imap_connect, /* connect_it */ - imap_multi_statemach, /* connecting */ - imap_doing, /* doing */ - imap_pollset, /* proto_pollset */ - imap_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - imap_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_IMAP, /* defport */ - CURLPROTO_IMAP, /* protocol */ - CURLPROTO_IMAP, /* family */ - PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | - PROTOPT_CONN_REUSE -}; - -#ifdef USE_SSL -/* - * IMAPS protocol handler. - */ - -const struct Curl_handler Curl_handler_imaps = { - "imaps", /* scheme */ - imap_setup_connection, /* setup_connection */ - imap_do, /* do_it */ - imap_done, /* done */ - ZERO_NULL, /* do_more */ - imap_connect, /* connect_it */ - imap_multi_statemach, /* connecting */ - imap_doing, /* doing */ - imap_pollset, /* proto_pollset */ - imap_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - imap_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_IMAPS, /* defport */ - CURLPROTO_IMAPS, /* protocol */ - CURLPROTO_IMAP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE -}; -#endif - #define IMAP_RESP_OK 1 #define IMAP_RESP_NOT_OK 2 #define IMAP_RESP_PREAUTH 3 -/* SASL parameters for the imap protocol */ -static const struct SASLproto saslimap = { - "imap", /* The service name */ - imap_perform_authenticate, /* Send authentication command */ - imap_continue_authenticate, /* Send authentication continuation */ - imap_cancel_authenticate, /* Send authentication cancellation */ - imap_get_message, /* Get SASL response message */ - 0, /* No maximum initial response length */ - '+', /* Code received when continuation is expected */ - IMAP_RESP_OK, /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ -}; - struct ulbits { int bit; const char *flag; @@ -262,6 +148,124 @@ /*********************************************************************** * + * imap_sendf() + * + * Sends the formatted string as an IMAP command to the server. + * + * Designed to never block. + */ +static CURLcode imap_sendf(struct Curl_easy *data, + struct imap_conn *imapc, + const char *fmt, ...) CURL_PRINTF(3, 0); +static CURLcode imap_sendf(struct Curl_easy *data, + struct imap_conn *imapc, + const char *fmt, ...) +{ + CURLcode result = CURLE_OK; + + DEBUGASSERT(fmt); + + /* Calculate the tag based on the connection ID and command ID */ + curl_msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", + 'A' + curlx_sltosi((long)(data->conn->connection_id % 26)), + ++imapc->cmdid); + + /* start with a blank buffer */ + curlx_dyn_reset(&imapc->dyn); + + /* append tag + space + fmt */ + result = curlx_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt); + if(!result) { + va_list ap; + va_start(ap, fmt); +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + result = Curl_pp_vsendf(data, &imapc->pp, curlx_dyn_ptr(&imapc->dyn), ap); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + va_end(ap); + } + return result; +} + +/*********************************************************************** + * + * imap_atom() + * + * Checks the input string for characters that need escaping and returns an + * atom ready for sending to the server. + * + * The returned string needs to be freed. + * + */ +static char *imap_atom(const char *str, bool escape_only) +{ + struct dynbuf line; + size_t nclean; + size_t len; + + if(!str) + return NULL; + + len = strlen(str); + nclean = strcspn(str, "() {%*]\\\""); + if(len == nclean) + /* nothing to escape, return a strdup */ + return curlx_strdup(str); + + curlx_dyn_init(&line, 2000); + + if(!escape_only && curlx_dyn_addn(&line, "\"", 1)) + return NULL; + + while(*str) { + if((*str == '\\' || *str == '"') && + curlx_dyn_addn(&line, "\\", 1)) + return NULL; + if(curlx_dyn_addn(&line, str, 1)) + return NULL; + str++; + } + + if(!escape_only && curlx_dyn_addn(&line, "\"", 1)) + return NULL; + + return curlx_dyn_ptr(&line); +} + +/* + * Finds the start of a literal '{size}' in line, skipping over quoted strings. + */ +static const char *imap_find_literal(const char *line, size_t len) +{ + const char *end = line + len; + bool in_quote = FALSE; + + while(line < end) { + if(in_quote) { + if(*line == '\\' && (line + 1) < end) { + line += 2; + continue; + } + if(*line == '"') + in_quote = FALSE; + } + else { + if(*line == '"') + in_quote = TRUE; + else if(*line == '{') + return line; + } + line++; + } + return NULL; +} + +/*********************************************************************** + * * imap_matchresp() * * Determines whether the untagged response is related to the specified @@ -433,13 +437,12 @@ if(len > 2) { /* Find the start of the message */ len -= 2; - for(message += 2; *message == ' ' || *message == '\t'; message++, len--) + for(message += 2; ISBLANK(*message); message++, len--) ; /* Find the end of the message */ while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') + if(!ISNEWLINE(message[len]) && !ISBLANK(message[len])) break; /* Terminate the message */ @@ -463,7 +466,7 @@ struct imap_conn *imapc, imapstate newstate) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* for debug purposes */ static const char * const names[] = { "STOP", @@ -487,8 +490,9 @@ if(imapc->state != newstate) infof(data, "IMAP %p state change from %s to %s", (void *)imapc, names[imapc->state], names[newstate]); -#endif +#else (void)data; +#endif imapc->state = newstate; } @@ -555,13 +559,13 @@ if(result) goto out; /* Change the connection handler */ - conn->handler = &Curl_handler_imaps; + conn->scheme = &Curl_scheme_imaps; } DEBUGASSERT(!imapc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + result, ssldone)); if(!result && ssldone) { imapc->ssldone = ssldone; /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */ @@ -706,7 +710,8 @@ } /* Calculate the SASL login details */ - result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress); + result = Curl_sasl_start(&imapc->sasl, data, (bool)imapc->ir_supported, + &progress); if(!result) { if(progress == SASL_INPROGRESS) @@ -770,7 +775,8 @@ char *mailbox; /* Invalidate old information as we are switching mailboxes */ - Curl_safefree(imapc->mailbox); + curlx_safefree(imapc->mailbox); + imapc->mb_uidvalidity_set = FALSE; /* Check we have a mailbox */ if(!imap->mailbox) { @@ -859,22 +865,24 @@ #ifndef CURL_DISABLE_MIME /* Prepare the mime data if some. */ - if(data->set.mimepost.kind != MIMEKIND_NONE) { + if(IS_MIME_POST(data)) { + curl_mimepart *postp = data->set.mimepostp; + /* Use the whole structure as data. */ - data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; + postp->flags &= ~(unsigned int)MIME_BODY_ONLY; /* Add external headers and mime version. */ - curl_mime_headers(&data->set.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, + curl_mime_headers(postp, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, postp, NULL, NULL, MIMESTRATEGY_MAIL); if(!result) if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) - result = Curl_mime_add_header(&data->set.mimepost.curlheaders, + result = Curl_mime_add_header(&postp->curlheaders, "Mime-Version: 1.0"); if(!result) - result = Curl_creader_set_mime(data, &data->set.mimepost); + result = Curl_creader_set_mime(data, postp); if(result) return result; data->state.infilesize = Curl_creader_client_length(data); @@ -1017,7 +1025,6 @@ imapstate instate) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); (void)instate; @@ -1029,20 +1036,15 @@ /* Loop through the data line */ for(;;) { size_t wordlen; - while(*line && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - + while(*line && (ISBLANK(*line) || ISNEWLINE(*line))) line++; - } if(!*line) break; /* Extract the word */ - for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) + for(wordlen = 0; line[wordlen] && !ISBLANK(line[wordlen]) && + !ISNEWLINE(line[wordlen]);) wordlen++; /* Does the server support the STARTTLS capability? */ @@ -1074,7 +1076,7 @@ line += wordlen; } } - else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + else if(data->set.use_ssl && !Curl_xfer_is_secure(data)) { /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ @@ -1139,7 +1141,7 @@ imap_state(data, imapc, IMAP_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ - if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) + if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) /* Perform clear text authentication */ result = imap_perform_login(data, imapc, data->conn); else { @@ -1174,6 +1176,43 @@ return result; } +/* Detect IMAP listings vs. downloading a single email */ +static bool is_custom_fetch_listing_match(const char *params) +{ + /* match " 1:* (FLAGS ..." or " 1,2,3 (FLAGS ..." */ + if(*params++ != ' ') + return FALSE; + + while(ISDIGIT(*params)) { + params++; + if(*params == 0) + return FALSE; + } + if(*params == ':') + return true; + if(*params == ',') + return true; + return FALSE; +} + +static bool is_custom_fetch_listing(struct IMAP *imap) +{ + /* filter out "UID FETCH 1:* (FLAGS ..." queries to list emails */ + if(!imap->custom) + return FALSE; + else if(curl_strequal(imap->custom, "FETCH") && imap->custom_params) { + const char *p = imap->custom_params; + return is_custom_fetch_listing_match(p); + } + else if(curl_strequal(imap->custom, "UID") && imap->custom_params) { + if(curl_strnequal(imap->custom_params, " FETCH ", 7)) { + const char *p = imap->custom_params + 6; + return is_custom_fetch_listing_match(p); + } + } + return FALSE; +} + /* For LIST and SEARCH responses */ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data, struct imap_conn *imapc, @@ -1181,17 +1220,24 @@ imapstate instate) { CURLcode result = CURLE_OK; - char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); + const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); size_t len = imapc->pp.nfinal; + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + DEBUGASSERT(imap); + if(!imap) + return CURLE_FAILED_INIT; (void)instate; - if(imapcode == '*') { + if(imapcode == '*' && is_custom_fetch_listing(imap)) { + /* custom FETCH or UID FETCH for listing is not handled here */ + } + else if(imapcode == '*') { /* Check if this response contains a literal (e.g. FETCH responses with body data). Literal syntax is {size}\r\n */ const char *cr = memchr(line, '\r', len); size_t line_len = cr ? (size_t)(cr - line) : len; - const char *ptr = memchr(line, '{', line_len); + const char *ptr = imap_find_literal(line, line_len); if(ptr) { curl_off_t size = 0; bool parsed = FALSE; @@ -1276,12 +1322,12 @@ imap_state(data, imapc, IMAP_STOP); } else { - /* Failed to parse literal, just write the line */ + /* Failed to parse literal, write the line */ result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); } } else { - /* No literal, just write the line as-is */ + /* No literal, write the line as-is */ result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); } } @@ -1370,7 +1416,7 @@ /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse the continuation data contained within the curly brackets */ - ptr = memchr(ptr, '{', len); + ptr = imap_find_literal(ptr, len); if(ptr) { ptr++; if(!curlx_str_number(&ptr, &size, CURL_OFF_T_MAX) && @@ -1406,10 +1452,10 @@ if(result) return result; - infof(data, "Written %zu bytes, %" FMT_OFF_TU - " bytes are left for transfer", chunk, size - chunk); + infof(data, "Written %zu bytes, %" FMT_OFF_T + " bytes are left for transfer", chunk, (curl_off_t)(size - chunk)); - /* Have we used the entire overflow or just part of it?*/ + /* Have we used the entire overflow or part of it?*/ if(pp->overflow > chunk) { /* remember the remaining trailing overflow data */ pp->overflow -= chunk; @@ -1517,7 +1563,6 @@ struct pingpong *pp; size_t nread = 0; - (void)data; if(!imapc || !imap) return CURLE_FAILED_INIT; pp = &imapc->pp; @@ -1649,446 +1694,21 @@ return imapc ? Curl_pp_pollset(data, &imapc->pp, ps) : CURLE_OK; } -/*********************************************************************** - * - * imap_connect() - * - * This function should do everything that is to be considered a part of the - * connection phase. - * - * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE if not. - */ -static CURLcode imap_connect(struct Curl_easy *data, bool *done) -{ - struct imap_conn *imapc = - Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN); - CURLcode result = CURLE_OK; - - *done = FALSE; /* default to not done yet */ - if(!imapc) - return CURLE_FAILED_INIT; - - /* Parse the URL options */ - result = imap_parse_url_options(data->conn, imapc); - if(result) - return result; - - /* Start off waiting for the server greeting response */ - imap_state(data, imapc, IMAP_SERVERGREET); - - /* Start off with an response id of '*' */ - curlx_strcopy(imapc->resptag, sizeof(imapc->resptag), "*", 1); - - result = imap_multi_statemach(data, done); - - return result; -} - -/*********************************************************************** - * - * imap_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode imap_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); - struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); - - (void)premature; - - if(!imapc) - return CURLE_FAILED_INIT; - if(!imap) - return CURLE_OK; - - if(status) { - connclose(conn, "IMAP done with bad status"); /* marked for closure */ - result = status; /* use the already set error code */ - } - else if(!data->set.connect_only && - ((!imap->custom && (imap->uid || imap->mindex)) || - (imap->custom && data->req.maxdownload > 0) || - data->state.upload || IS_MIME_POST(data))) { - /* Handle responses after FETCH or APPEND transfer has finished. - For custom commands, check if we set up a download which indicates - a FETCH-like command with literal data. */ - - if(!data->state.upload && !IS_MIME_POST(data)) - imap_state(data, imapc, IMAP_FETCH_FINAL); - else { - /* End the APPEND command first by sending an empty line */ - result = Curl_pp_sendf(data, &imapc->pp, "%s", ""); - if(!result) - imap_state(data, imapc, IMAP_APPEND_FINAL); - } - - /* Run the state-machine */ - if(!result) - result = imap_block_statemach(data, imapc, FALSE); - } - - imap_easy_reset(imap); - return result; -} - -/*********************************************************************** - * - * imap_perform() - * - * This is the actual DO function for IMAP. Fetch or append a message, or do - * other things according to the options previously setup. - */ -static CURLcode imap_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done) -{ - /* This is IMAP and no proxy */ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); - struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); - bool selected = FALSE; - - DEBUGF(infof(data, "DO phase starts")); - if(!imapc || !imap) - return CURLE_FAILED_INIT; - - if(data->req.no_body) { - /* Requested no body means no transfer */ - imap->transfer = PPTRANSFER_INFO; - } - - *dophase_done = FALSE; /* not done yet */ - - /* Determine if the requested mailbox (with the same UIDVALIDITY if set) - has already been selected on this connection */ - if(imap->mailbox && imapc->mailbox && - curl_strequal(imap->mailbox, imapc->mailbox) && - (!imap->uidvalidity_set || !imapc->mb_uidvalidity_set || - (imap->uidvalidity == imapc->mb_uidvalidity))) - selected = TRUE; - - /* Start the first command in the DO phase */ - if(data->state.upload || IS_MIME_POST(data)) - /* APPEND can be executed directly */ - result = imap_perform_append(data, imapc, imap); - else if(imap->custom && (selected || !imap->mailbox)) - /* Custom command using the same mailbox or no mailbox */ - result = imap_perform_list(data, imapc, imap); - else if(!imap->custom && selected && (imap->uid || imap->mindex)) - /* FETCH from the same mailbox */ - result = imap_perform_fetch(data, imapc, imap); - else if(!imap->custom && selected && imap->query) - /* SEARCH the current mailbox */ - result = imap_perform_search(data, imapc, imap); - else if(imap->mailbox && !selected && - (imap->custom || imap->uid || imap->mindex || imap->query)) - /* SELECT the mailbox */ - result = imap_perform_select(data, imapc, imap); - else - /* LIST */ - result = imap_perform_list(data, imapc, imap); - - if(result) - return result; - - /* Run the state-machine */ - result = imap_multi_statemach(data, dophase_done); - - *connected = Curl_conn_is_connected(conn, FIRSTSOCKET); - - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete")); - - return result; -} - -/*********************************************************************** - * - * imap_do() - * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (imap_perform). - * - * The input argument is already checked for validity. - */ -static CURLcode imap_do(struct Curl_easy *data, bool *done) -{ - struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); - CURLcode result = CURLE_OK; - *done = FALSE; /* default to false */ - - if(!imap) - return CURLE_FAILED_INIT; - /* Parse the URL path */ - result = imap_parse_url_path(data, imap); - if(result) - return result; - - /* Parse the custom request */ - result = imap_parse_custom_request(data, imap); - if(result) - return result; - - result = imap_regular_transfer(data, imap, done); - - return result; -} - -/*********************************************************************** - * - * imap_disconnect() - * - * Disconnect from an IMAP server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode imap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); - - (void)data; - if(imapc) { - /* We cannot send quit unconditionally. If this connection is stale or - bad in any way (pingpong has pending data to send), - sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to. */ - if(!dead_connection && conn->bits.protoconnstart && - !Curl_pp_needs_flush(data, &imapc->pp)) { - if(!imap_perform_logout(data, imapc)) - (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */ - } - } - return CURLE_OK; -} - -/* Call this when the DO phase has completed */ -static CURLcode imap_dophase_done(struct Curl_easy *data, - struct IMAP *imap, - bool connected) -{ - (void)connected; - - if(imap->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_xfer_setup_nop(data); - - return CURLE_OK; -} - -/* Called from multi.c while DOing */ -static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done) -{ - struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); - CURLcode result; - - if(!imap) - return CURLE_FAILED_INIT; - - result = imap_multi_statemach(data, dophase_done); - if(result) - DEBUGF(infof(data, "DO phase failed")); - else if(*dophase_done) { - result = imap_dophase_done(data, imap, FALSE /* not connected */); - - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/*********************************************************************** - * - * imap_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - */ -static CURLcode imap_regular_transfer(struct Curl_easy *data, - struct IMAP *imap, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - - /* Make sure size is unknown at this point */ - data->req.size = -1; - - /* Set the progress data */ - Curl_pgrsReset(data); - - /* Carry out the perform */ - result = imap_perform(data, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = imap_dophase_done(data, imap, connected); - - return result; -} - static void imap_easy_reset(struct IMAP *imap) { - Curl_safefree(imap->mailbox); - Curl_safefree(imap->uid); - Curl_safefree(imap->mindex); - Curl_safefree(imap->section); - Curl_safefree(imap->partial); - Curl_safefree(imap->query); - Curl_safefree(imap->custom); - Curl_safefree(imap->custom_params); + curlx_safefree(imap->mailbox); + curlx_safefree(imap->uid); + curlx_safefree(imap->mindex); + curlx_safefree(imap->section); + curlx_safefree(imap->partial); + curlx_safefree(imap->query); + curlx_safefree(imap->custom); + curlx_safefree(imap->custom_params); + imap->uidvalidity_set = FALSE; /* Clear the transfer mode for the next request */ imap->transfer = PPTRANSFER_BODY; } -static void imap_easy_dtor(void *key, size_t klen, void *entry) -{ - struct IMAP *imap = entry; - (void)key; - (void)klen; - imap_easy_reset(imap); - curlx_free(imap); -} - -static void imap_conn_dtor(void *key, size_t klen, void *entry) -{ - struct imap_conn *imapc = entry; - (void)key; - (void)klen; - Curl_pp_disconnect(&imapc->pp); - curlx_dyn_free(&imapc->dyn); - Curl_safefree(imapc->mailbox); - curlx_free(imapc); -} - -static CURLcode imap_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct imap_conn *imapc; - struct pingpong *pp; - struct IMAP *imap; - - imapc = curlx_calloc(1, sizeof(*imapc)); - if(!imapc) - return CURLE_OUT_OF_MEMORY; - - pp = &imapc->pp; - PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp); - - /* Set the default preferred authentication type and mechanism */ - imapc->preftype = IMAP_TYPE_ANY; - Curl_sasl_init(&imapc->sasl, data, &saslimap); - - curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD); - Curl_pp_init(pp, Curl_pgrs_now(data)); - - if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor)) - return CURLE_OUT_OF_MEMORY; - - imap = curlx_calloc(1, sizeof(struct IMAP)); - if(!imap || - Curl_meta_set(data, CURL_META_IMAP_EASY, imap, imap_easy_dtor)) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -/*********************************************************************** - * - * imap_sendf() - * - * Sends the formatted string as an IMAP command to the server. - * - * Designed to never block. - */ -static CURLcode imap_sendf(struct Curl_easy *data, - struct imap_conn *imapc, - const char *fmt, ...) -{ - CURLcode result = CURLE_OK; - - DEBUGASSERT(fmt); - - /* Calculate the tag based on the connection ID and command ID */ - curl_msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", - 'A' + curlx_sltosi((long)(data->conn->connection_id % 26)), - ++imapc->cmdid); - - /* start with a blank buffer */ - curlx_dyn_reset(&imapc->dyn); - - /* append tag + space + fmt */ - result = curlx_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt); - if(!result) { - va_list ap; - va_start(ap, fmt); -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#endif - result = Curl_pp_vsendf(data, &imapc->pp, curlx_dyn_ptr(&imapc->dyn), ap); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - va_end(ap); - } - return result; -} - -/*********************************************************************** - * - * imap_atom() - * - * Checks the input string for characters that need escaping and returns an - * atom ready for sending to the server. - * - * The returned string needs to be freed. - * - */ -static char *imap_atom(const char *str, bool escape_only) -{ - struct dynbuf line; - size_t nclean; - size_t len; - - if(!str) - return NULL; - - len = strlen(str); - nclean = strcspn(str, "() {%*]\\\""); - if(len == nclean) - /* nothing to escape, return a strdup */ - return curlx_strdup(str); - - curlx_dyn_init(&line, 2000); - - if(!escape_only && curlx_dyn_addn(&line, "\"", 1)) - return NULL; - - while(*str) { - if((*str == '\\' || *str == '"') && - curlx_dyn_addn(&line, "\\", 1)) - return NULL; - if(curlx_dyn_addn(&line, str, 1)) - return NULL; - str++; - } - - if(!escape_only && curlx_dyn_addn(&line, "\"", 1)) - return NULL; - - return curlx_dyn_ptr(&line); -} - /*********************************************************************** * * imap_is_bchar() @@ -2098,40 +1718,9 @@ */ static bool imap_is_bchar(char ch) { - /* Performing the alnum check with this macro is faster because of ASCII + /* Performing the alnum check first with macro is faster because of ASCII arithmetic */ - if(ISALNUM(ch)) - return TRUE; - - switch(ch) { - /* bchar */ - case ':': - case '@': - case '/': - /* bchar -> achar */ - case '&': - case '=': - /* bchar -> achar -> uchar -> unreserved (without alphanumeric) */ - case '-': - case '.': - case '_': - case '~': - /* bchar -> achar -> uchar -> sub-delims-sh */ - case '!': - case '$': - case '\'': - case '(': - case ')': - case '*': - case '+': - case ',': - /* bchar -> achar -> uchar -> pct-encoded */ - case '%': /* HEXDIG chars are already included above */ - return TRUE; - - default: - return FALSE; - } + return ch && (ISALNUM(ch) || strchr(":@/&=-._~!$\'()*+,%", ch)); } /*********************************************************************** @@ -2359,4 +1948,379 @@ return result; } +/*********************************************************************** + * + * imap_connect() + * + * This function should do everything that is to be considered a part of the + * connection phase. + * + * The variable 'done' points to will be TRUE if the protocol-layer connect + * phase is done when this function returns, or FALSE if not. + */ +static CURLcode imap_connect(struct Curl_easy *data, bool *done) +{ + struct imap_conn *imapc = + Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN); + CURLcode result = CURLE_OK; + + *done = FALSE; /* default to not done yet */ + if(!imapc) + return CURLE_FAILED_INIT; + + /* Parse the URL options */ + result = imap_parse_url_options(data->conn, imapc); + if(result) + return result; + + /* Start off waiting for the server greeting response */ + imap_state(data, imapc, IMAP_SERVERGREET); + + /* Start off with an response id of '*' */ + curlx_strcopy(imapc->resptag, sizeof(imapc->resptag), STRCONST("*")); + + result = imap_multi_statemach(data, done); + + return result; +} + +/*********************************************************************** + * + * imap_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode imap_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + + (void)premature; + + if(!imapc) + return CURLE_FAILED_INIT; + if(!imap) + return CURLE_OK; + + if(status) { + connclose(conn, "IMAP done with bad status"); /* marked for closure */ + result = status; /* use the already set error code */ + } + else if(!data->set.connect_only && + ((!imap->custom && (imap->uid || imap->mindex)) || + (imap->custom && data->req.maxdownload > 0) || + data->state.upload || IS_MIME_POST(data))) { + /* Handle responses after FETCH or APPEND transfer has finished. + For custom commands, check if we set up a download which indicates + a FETCH-like command with literal data. */ + + if(!data->state.upload && !IS_MIME_POST(data)) + imap_state(data, imapc, IMAP_FETCH_FINAL); + else { + /* End the APPEND command first by sending an empty line */ + result = Curl_pp_sendf(data, &imapc->pp, "%s", ""); + if(!result) + imap_state(data, imapc, IMAP_APPEND_FINAL); + } + + /* Run the state-machine */ + if(!result) + result = imap_block_statemach(data, imapc, FALSE); + } + + imap_easy_reset(imap); + return result; +} + +/*********************************************************************** + * + * imap_perform() + * + * This is the actual DO function for IMAP. Fetch or append a message, or do + * other things according to the options previously setup. + */ +static CURLcode imap_perform(struct Curl_easy *data, bool *connected, + bool *dophase_done) +{ + /* This is IMAP and no proxy */ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + bool selected = FALSE; + + DEBUGF(infof(data, "DO phase starts")); + if(!imapc || !imap) + return CURLE_FAILED_INIT; + + if(data->req.no_body) { + /* Requested no body means no transfer */ + imap->transfer = PPTRANSFER_INFO; + } + + *dophase_done = FALSE; /* not done yet */ + + /* Determine if the requested mailbox (with the same UIDVALIDITY if set) + has already been selected on this connection */ + if(imap->mailbox && imapc->mailbox && + curl_strequal(imap->mailbox, imapc->mailbox) && + (!imap->uidvalidity_set || !imapc->mb_uidvalidity_set || + (imap->uidvalidity == imapc->mb_uidvalidity))) + selected = TRUE; + + /* Start the first command in the DO phase */ + if(data->state.upload || IS_MIME_POST(data)) + /* APPEND can be executed directly */ + result = imap_perform_append(data, imapc, imap); + else if(imap->custom && (selected || !imap->mailbox)) + /* Custom command using the same mailbox or no mailbox */ + result = imap_perform_list(data, imapc, imap); + else if(!imap->custom && selected && (imap->uid || imap->mindex)) + /* FETCH from the same mailbox */ + result = imap_perform_fetch(data, imapc, imap); + else if(!imap->custom && selected && imap->query) + /* SEARCH the current mailbox */ + result = imap_perform_search(data, imapc, imap); + else if(imap->mailbox && !selected && + (imap->custom || imap->uid || imap->mindex || imap->query)) + /* SELECT the mailbox */ + result = imap_perform_select(data, imapc, imap); + else + /* LIST */ + result = imap_perform_list(data, imapc, imap); + + if(result) + return result; + + /* Run the state-machine */ + result = imap_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(conn, FIRSTSOCKET); + + if(*dophase_done) + DEBUGF(infof(data, "DO phase is complete")); + + return result; +} + +/* Call this when the DO phase has completed */ +static CURLcode imap_dophase_done(struct Curl_easy *data, + struct IMAP *imap, + bool connected) +{ + (void)connected; + + if(imap->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + return CURLE_OK; +} + +/*********************************************************************** + * + * imap_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + */ +static CURLcode imap_regular_transfer(struct Curl_easy *data, + struct IMAP *imap, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + + /* Make sure size is unknown at this point */ + data->req.size = -1; + + /* Set the progress data */ + Curl_pgrsReset(data); + + /* Carry out the perform */ + result = imap_perform(data, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = imap_dophase_done(data, imap, connected); + + return result; +} + +/*********************************************************************** + * + * imap_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (imap_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode imap_do(struct Curl_easy *data, bool *done) +{ + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + CURLcode result = CURLE_OK; + *done = FALSE; /* default to false */ + + if(!imap) + return CURLE_FAILED_INIT; + /* Parse the URL path */ + result = imap_parse_url_path(data, imap); + if(result) + return result; + + /* Parse the custom request */ + result = imap_parse_custom_request(data, imap); + if(result) + return result; + + result = imap_regular_transfer(data, imap, done); + + return result; +} + +/*********************************************************************** + * + * imap_disconnect() + * + * Disconnect from an IMAP server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode imap_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); + + if(imapc) { + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way (pingpong has pending data to send), + sending quit and waiting around here will make the + disconnect wait in vain and cause more problems than we need to. */ + if(!dead_connection && conn->bits.protoconnstart && + !Curl_pp_needs_flush(data, &imapc->pp)) { + if(!imap_perform_logout(data, imapc)) + (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */ + } + } + return CURLE_OK; +} + +/* Called from multi.c while DOing */ +static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done) +{ + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + CURLcode result; + + if(!imap) + return CURLE_FAILED_INIT; + + result = imap_multi_statemach(data, dophase_done); + if(result) + DEBUGF(infof(data, "DO phase failed")); + else if(*dophase_done) { + result = imap_dophase_done(data, imap, FALSE /* not connected */); + + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +static void imap_easy_dtor(void *key, size_t klen, void *entry) +{ + struct IMAP *imap = entry; + (void)key; + (void)klen; + imap_easy_reset(imap); + curlx_free(imap); +} + +static void imap_conn_dtor(void *key, size_t klen, void *entry) +{ + struct imap_conn *imapc = entry; + (void)key; + (void)klen; + Curl_pp_disconnect(&imapc->pp); + curlx_dyn_free(&imapc->dyn); + curlx_safefree(imapc->mailbox); + curlx_free(imapc); +} + +/* SASL parameters for the imap protocol */ +static const struct SASLproto saslimap = { + "imap", /* The service name */ + imap_perform_authenticate, /* Send authentication command */ + imap_continue_authenticate, /* Send authentication continuation */ + imap_cancel_authenticate, /* Send authentication cancellation */ + imap_get_message, /* Get SASL response message */ + 0, /* No maximum initial response length */ + '+', /* Code received when continuation is expected */ + IMAP_RESP_OK, /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + +static CURLcode imap_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + struct imap_conn *imapc; + struct pingpong *pp; + struct IMAP *imap; + + imapc = curlx_calloc(1, sizeof(*imapc)); + if(!imapc) + return CURLE_OUT_OF_MEMORY; + + pp = &imapc->pp; + PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp); + + /* Set the default preferred authentication type and mechanism */ + imapc->preftype = IMAP_TYPE_ANY; + Curl_sasl_init(&imapc->sasl, data, &saslimap); + + curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD); + Curl_pp_init(pp, Curl_pgrs_now(data)); + + if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor)) + return CURLE_OUT_OF_MEMORY; + + imap = curlx_calloc(1, sizeof(struct IMAP)); + if(!imap || + Curl_meta_set(data, CURL_META_IMAP_EASY, imap, imap_easy_dtor)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +/* + * IMAP protocol. + */ +const struct Curl_protocol Curl_protocol_imap = { + imap_setup_connection, /* setup_connection */ + imap_do, /* do_it */ + imap_done, /* done */ + ZERO_NULL, /* do_more */ + imap_connect, /* connect_it */ + imap_multi_statemach, /* connecting */ + imap_doing, /* doing */ + imap_pollset, /* proto_pollset */ + imap_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + imap_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_IMAP */
diff --git a/Utilities/cmcurl/lib/imap.h b/Utilities/cmcurl/lib/imap.h index 26306a6..ecfb1ec 100644 --- a/Utilities/cmcurl/lib/imap.h +++ b/Utilities/cmcurl/lib/imap.h
@@ -23,8 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_handler Curl_handler_imap; -extern const struct Curl_handler Curl_handler_imaps; /* Authentication type flags */ #define IMAP_TYPE_CLEARTEXT (1 << 0) @@ -34,4 +32,8 @@ #define IMAP_TYPE_NONE 0 #define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT | IMAP_TYPE_SASL) +#ifndef CURL_DISABLE_IMAP +extern const struct Curl_protocol Curl_protocol_imap; +#endif + #endif /* HEADER_CURL_IMAP_H */
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 95a982f..16c93ee 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c
@@ -22,10 +22,12 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "curl_ldap.h" #if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP) -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -41,41 +43,27 @@ * OpenLDAP library versions, USE_OPENLDAP shall not be defined. */ -/* Wincrypt must be included before anything that could include OpenSSL. */ -#ifdef USE_WIN32_CRYPTO -#include <wincrypt.h> -/* Undefine wincrypt conflicting symbols for BoringSSL. */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#endif - #ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ -# ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable:4201) -# endif -# include <subauth.h> /* for [P]UNICODE_STRING */ -# ifdef _MSC_VER -# pragma warning(pop) -# endif -# include <winldap.h> -# include <winber.h> +# include <winldap.h> +/* Undefine indirect <wincrypt.h> symbols conflicting with BoringSSL/AWS-LC. */ +# undef X509_NAME +# undef X509_EXTENSIONS +# undef PKCS7_ISSUER_AND_SERIAL +# undef PKCS7_SIGNER_INFO +# undef OCSP_REQUEST +# undef OCSP_RESPONSE +# include <winber.h> #else -# define LDAP_DEPRECATED 1 /* Be sure ldap_init() is defined. */ -# ifdef HAVE_LBER_H -# include <lber.h> -# endif -# include <ldap.h> -# if defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H) -# include <ldap_ssl.h> -# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */ +# define LDAP_DEPRECATED 1 /* Be sure ldap_init() is defined. */ +# ifdef HAVE_LBER_H +# include <lber.h> +# endif +# include <ldap.h> +# if defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H) +# include <ldap_ssl.h> +# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */ #endif -#include "urldata.h" #include "cfilters.h" #include "sendf.h" #include "curl_trc.h" @@ -84,7 +72,6 @@ #include "transfer.h" #include "curlx/strparse.h" #include "bufref.h" -#include "curl_ldap.h" #include "curlx/multibyte.h" #include "curlx/base64.h" #include "connect.h" @@ -93,7 +80,7 @@ #define FREE_ON_WINLDAP(x) curlx_free(x) #define curl_ldap_num_t ULONG #else -#define FREE_ON_WINLDAP(x) +#define FREE_ON_WINLDAP(x) do {} while(0) #define curl_ldap_num_t int #endif @@ -128,7 +115,7 @@ static curl_ldap_num_t ldap_url_parse_low(struct Curl_easy *data, const struct connectdata *conn, - LDAPURLDesc **ludp); + LDAPURLDesc **ludpp); static void ldap_free_urldesc_low(LDAPURLDesc *ludp); #undef ldap_free_urldesc @@ -163,66 +150,6 @@ #define LDAP_OPT_OFF ((void *)(size_t)0) #endif -static CURLcode ldap_do(struct Curl_easy *data, bool *done); - -/* - * LDAP protocol handler. - */ -const struct Curl_handler Curl_handler_ldap = { - "ldap", /* scheme */ - ZERO_NULL, /* setup_connection */ - ldap_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_LDAP, /* defport */ - CURLPROTO_LDAP, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL_REUSE /* flags */ -}; - -#ifdef HAVE_LDAP_SSL -/* - * LDAPS protocol handler. - */ -const struct Curl_handler Curl_handler_ldaps = { - "ldaps", /* scheme */ - ZERO_NULL, /* setup_connection */ - ldap_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_LDAPS, /* defport */ - CURLPROTO_LDAPS, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL /* flags */ -}; -#endif - #ifdef USE_WIN32_LDAP #ifdef USE_WINDOWS_SSPI @@ -258,8 +185,8 @@ } if(method && user && passwd) { - CURLcode res = Curl_create_sspi_identity(user, passwd, &cred); - if(!res) { + CURLcode result = Curl_create_sspi_identity(user, passwd, &cred); + if(!result) { rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method); Curl_sspi_free_identity(&cred); } @@ -376,9 +303,8 @@ if(ldap_ssl) server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1); else -#else - server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); #endif + server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); if(!server) { failf(data, "LDAP: cannot setup connect to %s:%u", conn->host.dispname, ipquad.remote_port); @@ -398,7 +324,7 @@ ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); #else /* !USE_WIN32_LDAP */ int ldap_option; - char *ldap_ca = conn->ssl_config.CAfile; + const char *ldap_ca = conn->ssl_config.CAfile; #ifdef LDAP_OPT_X_TLS if(conn->ssl_config.verifypeer) { /* OpenLDAP SDK supports BASE64 files. */ @@ -461,7 +387,6 @@ } #ifdef USE_WIN32_LDAP - ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); rc = ldap_win_bind(data, server, user, passwd); #else rc = ldap_simple_bind_s(server, user, passwd); @@ -489,7 +414,7 @@ Curl_pgrsReset(data); rc = ldap_search_s(server, ludp->lud_dn, - (curl_ldap_num_t)ludp->lud_scope, + ludp->lud_scope, ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); if(rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED) { @@ -1028,28 +953,48 @@ unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); unsigned int minor = - (((unsigned int)api.ldapai_vendor_version - major * 10000) + (((unsigned int)api.ldapai_vendor_version - (major * 10000)) - patch) / 100; - #ifdef __OS400__ - curl_msnprintf(buf, bufsz, "IBMLDAP/%u.%u.%u", - major, minor, patch); - + curl_msnprintf(buf, bufsz, "IBMLDAP/%u.%u.%u", major, minor, patch); ldap_value_free(api.ldapai_extensions); + (void)flavor; #else curl_msnprintf(buf, bufsz, "%s/%u.%u.%u%s", api.ldapai_vendor_name, major, minor, patch, flavor); - ldap_memfree(api.ldapai_vendor_name); ber_memvfree((void **)api.ldapai_extensions); #endif } else curl_msnprintf(buf, bufsz, "LDAP/1"); -#endif +#endif /* USE_WIN32_LDAP */ } -#if defined(__GNUC__) && defined(__APPLE__) +/* + * LDAP protocol handler. + */ +const struct Curl_protocol Curl_protocol_ldap = { + ZERO_NULL, /* setup_connection */ + ldap_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/libcurl.rc b/Utilities/cmcurl/lib/libcurl.rc index 1ceb469..81cd8d7 100644 --- a/Utilities/cmcurl/lib/libcurl.rc +++ b/Utilities/cmcurl/lib/libcurl.rc
@@ -32,7 +32,7 @@ FILEVERSION RC_VERSION PRODUCTVERSION RC_VERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(CURLDEBUG) || defined(_DEBUG) +#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(_DEBUG) FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0L
diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c index 0e3084b..3ec85e4 100644 --- a/Utilities/cmcurl/lib/llist.c +++ b/Utilities/cmcurl/lib/llist.c
@@ -40,7 +40,7 @@ #define VERIFYNODE(x) x #endif /* - * @unittest: 1300 + * @unittest 1300 */ void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) { @@ -62,7 +62,7 @@ * * The 'ne' argument should be a pointer into the object to store. * - * @unittest: 1300 + * @unittest 1300 */ void Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_node *e, /* may be NULL */ @@ -112,7 +112,7 @@ * * The 'ne' argument should be a pointer into the object to store. * - * @unittest: 1300 + * @unittest 1300 */ void Curl_llist_append(struct Curl_llist *list, const void *p, struct Curl_llist_node *ne) @@ -168,11 +168,7 @@ return ptr; } -/* - * @unittest: 1300 - */ -UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user); -UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user) +static void node_uremove(struct Curl_llist_node *e, void *user) { struct Curl_llist *list; void *ptr; @@ -190,7 +186,7 @@ void Curl_node_remove(struct Curl_llist_node *e) { - Curl_node_uremove(e, NULL); + node_uremove(e, NULL); } void Curl_llist_destroy(struct Curl_llist *list, void *user) @@ -198,7 +194,7 @@ if(list) { DEBUGASSERT(list->_init == LLISTINIT); while(list->_size > 0) - Curl_node_uremove(list->_tail, user); + node_uremove(list->_tail, user); } } @@ -212,9 +208,13 @@ } #ifdef UNITTESTS -/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which - might be NULL */ -struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) +/* llist_tail() returns the last 'struct Curl_llist_node *', which might be + NULL + + @unittest 1300 +*/ +UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list); +UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -248,16 +248,18 @@ } #ifdef UNITTESTS +/* llist_node_prev() returns the previous element in a list from a given + Curl_llist_node -/* Curl_node_prev() returns the previous element in a list from a given - Curl_llist_node */ -struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) + @unittest 1300 +*/ +UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n); +UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); return VERIFYNODE(n->_prev); } - #endif struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n)
diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h index 1d73db1..28e958d 100644 --- a/Utilities/cmcurl/lib/llist.h +++ b/Utilities/cmcurl/lib/llist.h
@@ -50,22 +50,18 @@ #endif }; -void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor); -void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_node *, - const void *, struct Curl_llist_node *node); -void Curl_llist_append(struct Curl_llist *, const void *, - struct Curl_llist_node *node); -void Curl_node_remove(struct Curl_llist_node *); -void Curl_llist_destroy(struct Curl_llist *, void *); +void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor); +void Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_node *e, + const void *p, struct Curl_llist_node *ne); +void Curl_llist_append(struct Curl_llist *list, const void *p, + struct Curl_llist_node *ne); +void Curl_node_remove(struct Curl_llist_node *e); +void Curl_llist_destroy(struct Curl_llist *list, void *user); /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which might be NULL */ struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list); -/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which - might be NULL */ -struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list); - /* Curl_llist_count() returns a size_t the number of nodes in the list */ size_t Curl_llist_count(struct Curl_llist *list); @@ -73,17 +69,13 @@ void *Curl_node_elem(struct Curl_llist_node *n); /* Remove the node from the list and return the custom data - * from a Curl_llist_node. Will NOT invoke a registered `dtor`. */ -void *Curl_node_take_elem(struct Curl_llist_node *); + * from a Curl_llist_node. Does NOT invoke a registered `dtor`. */ +void *Curl_node_take_elem(struct Curl_llist_node *e); /* Curl_node_next() returns the next element in a list from a given Curl_llist_node */ struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n); -/* Curl_node_prev() returns the previous element in a list from a given - Curl_llist_node */ -struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n); - /* Curl_node_llist() return the list the node is in or NULL. */ struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n);
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index e7ecf58..0213483 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c
@@ -33,24 +33,53 @@ /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ #define OPENSSL_NO_MD4 #else -/* Cover also OPENSSL_NO_MD4 configured in openssl */ +/* Cover also OPENSSL_NO_MD4 configured in OpenSSL */ #include <openssl/opensslconf.h> #endif #endif /* USE_OPENSSL */ #ifdef USE_WOLFSSL #include <wolfssl/options.h> -#define VOID_MD4_INIT -#ifdef NO_MD4 -#define WOLFSSL_NO_MD4 -#endif #endif /* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ -#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) -#include <wolfssl/openssl/md4.h> -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) + +#if defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) #include <openssl/md4.h> + +/* Map directly to OpenSSL implementation */ +#define my_md4_ctx MD4_CTX +#define my_md4_init MD4_Init +#define my_md4_update MD4_Update +#define my_md4_final MD4_Final + +#elif defined(USE_WOLFSSL) && !defined(NO_MD4) +#include <wolfssl/wolfcrypt/md4.h> +#include <wolfssl/version.h> + +#if LIBWOLFSSL_VERSION_HEX >= 0x05007006 +typedef wc_Md4 my_md4_ctx; +#else +typedef Md4 my_md4_ctx; +#endif + +static int my_md4_init(my_md4_ctx *ctx) +{ + wc_InitMd4(ctx); + return 1; +} + +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) +{ + wc_Md4Update(ctx, input, (word32)len); +} + +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) +{ + wc_Md4Final(ctx, digest); +} + #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ @@ -59,52 +88,36 @@ (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) -#define AN_APPLE_OS #include <CommonCrypto/CommonDigest.h> -#elif defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> -#elif defined(USE_GNUTLS) -#include <nettle/md4.h> -#endif -#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) +typedef CC_MD4_CTX my_md4_ctx; -#ifdef OPENSSL_COEXIST - #define MD4_CTX WOLFSSL_MD4_CTX - #define MD4_Init wolfSSL_MD4_Init - #define MD4_Update wolfSSL_MD4_Update - #define MD4_Final wolfSSL_MD4_Final -#endif - -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) - -#elif defined(AN_APPLE_OS) -typedef CC_MD4_CTX MD4_CTX; - -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { return CC_MD4_Init(ctx); } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { - (void)CC_MD4_Update(ctx, data, (CC_LONG)size); + (void)CC_MD4_Update(ctx, input, (CC_LONG)len); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { - (void)CC_MD4_Final(result, ctx); + (void)CC_MD4_Final(digest, ctx); } #elif defined(USE_WIN32_CRYPTO) +#include <wincrypt.h> struct md4_ctx { HCRYPTPROV hCryptProv; HCRYPTHASH hHash; }; -typedef struct md4_ctx MD4_CTX; +typedef struct md4_ctx my_md4_ctx; -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { ctx->hCryptProv = 0; ctx->hHash = 0; @@ -122,18 +135,19 @@ return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { - CryptHashData(ctx->hHash, (const BYTE *)data, (unsigned int)size, 0); + CryptHashData(ctx->hHash, (const BYTE *)input, (unsigned int)len, 0); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { unsigned long length = 0; CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); if(length == MD4_DIGEST_LENGTH) - CryptGetHashParam(ctx->hHash, HP_HASHVAL, result, &length, 0); + CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); if(ctx->hHash) CryptDestroyHash(ctx->hHash); @@ -143,23 +157,25 @@ } #elif defined(USE_GNUTLS) +#include <nettle/md4.h> -typedef struct md4_ctx MD4_CTX; +typedef struct md4_ctx my_md4_ctx; -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { md4_init(ctx); return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { - md4_update(ctx, size, data); + md4_update(ctx, len, input); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { - md4_digest(ctx, MD4_DIGEST_SIZE, result); + md4_digest(ctx, MD4_DIGEST_SIZE, digest); } #else @@ -170,7 +186,7 @@ * MD4 Message-Digest Algorithm (RFC 1320). * * Homepage: - https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + * https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 * * Author: * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> @@ -179,8 +195,8 @@ * claimed, and the software is hereby placed in the public domain. In case * this attempt to disclaim copyright and place the software in the public * domain is deemed null and void, then the software is Copyright (c) 2001 - * Alexander Peslyak and it is hereby released to the general public under the - * following terms: + * Alexander Peslyak and it is hereby released to the general public under + * the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. @@ -190,20 +206,13 @@ * (This is a heavily cut-down "BSD license".) */ -/* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int MD4_u32plus; - struct md4_ctx { - MD4_u32plus lo, hi; - MD4_u32plus a, b, c, d; + uint32_t lo, hi; + uint32_t a, b, c, d; unsigned char buffer[64]; - MD4_u32plus block[16]; + uint32_t block[16]; }; -typedef struct md4_ctx MD4_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); +typedef struct md4_ctx my_md4_ctx; /* * The basic MD4 functions. @@ -219,40 +228,39 @@ * The MD4 transformation for all three rounds. */ #define MD4_STEP(f, a, b, c, d, x, s) \ - (a) += f((b), (c), (d)) + (x); \ + (a) += f(b, c, d) + (x); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); /* * SET reads 4 input bytes in little-endian byte order and stores them * in a properly aligned word in host byte order. * - * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * does not work. + * The check for little-endian architectures that tolerate unaligned memory + * accesses is an optimization. Nothing will break if it does not work. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define MD4_SET(n) (*(const MD4_u32plus *)(const void *)&ptr[(n) * 4]) +#define MD4_SET(n) (*(const uint32_t *)(const void *)&ptr[(n) * 4]) #define MD4_GET(n) MD4_SET(n) #else -#define MD4_SET(n) (ctx->block[(n)] = \ - (MD4_u32plus)ptr[(n) * 4] | \ - ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \ - ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \ - ((MD4_u32plus)ptr[(n) * 4 + 3] << 24)) -#define MD4_GET(n) (ctx->block[(n)]) +#define MD4_SET(n) (ctx->block[n] = \ + (uint32_t)ptr[(n) * 4] | \ + ((uint32_t)ptr[((n) * 4) + 1] << 8) | \ + ((uint32_t)ptr[((n) * 4) + 2] << 16) | \ + ((uint32_t)ptr[((n) * 4) + 3] << 24)) +#define MD4_GET(n) ctx->block[n] #endif /* * This processes one or more 64-byte data blocks, but does NOT update * the bit counters. There are no alignment requirements. */ -static const void *my_md4_body(MD4_CTX *ctx, - const void *data, unsigned long size) +static const void *my_md4_body(my_md4_ctx *ctx, + const void *input, unsigned long size) { const unsigned char *ptr; - MD4_u32plus a, b, c, d; + uint32_t a, b, c, d; - ptr = (const unsigned char *)data; + ptr = (const unsigned char *)input; a = ctx->a; b = ctx->b; @@ -260,7 +268,7 @@ d = ctx->d; do { - MD4_u32plus saved_a, saved_b, saved_c, saved_d; + uint32_t saved_a, saved_b, saved_c, saved_d; saved_a = a; saved_b = b; @@ -337,7 +345,7 @@ return ptr; } -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -346,45 +354,47 @@ ctx->lo = 0; ctx->hi = 0; + return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { - MD4_u32plus saved_lo; + uint32_t saved_lo; unsigned long used; saved_lo = ctx->lo; - ctx->lo = (saved_lo + size) & 0x1fffffff; + ctx->lo = (saved_lo + len) & 0x1fffffff; if(ctx->lo < saved_lo) ctx->hi++; - ctx->hi += (MD4_u32plus)size >> 29; + ctx->hi += (uint32_t)len >> 29; used = saved_lo & 0x3f; if(used) { unsigned long available = 64 - used; - if(size < available) { - memcpy(&ctx->buffer[used], data, size); + if(len < available) { + memcpy(&ctx->buffer[used], input, len); return; } - memcpy(&ctx->buffer[used], data, available); - data = (const unsigned char *)data + available; - size -= available; + memcpy(&ctx->buffer[used], input, available); + input = (const unsigned char *)input + available; + len -= available; my_md4_body(ctx, ctx->buffer, 64); } - if(size >= 64) { - data = my_md4_body(ctx, data, size & ~(unsigned long)0x3f); - size &= 0x3f; + if(len >= 64) { + input = my_md4_body(ctx, input, len & ~(unsigned long)0x3f); + len &= 0x3f; } - memcpy(ctx->buffer, data, size); + memcpy(ctx->buffer, input, len); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { unsigned long used, available; @@ -415,42 +425,36 @@ my_md4_body(ctx, ctx->buffer, 64); - result[0] = curlx_ultouc((ctx->a) & 0xff); - result[1] = curlx_ultouc((ctx->a >> 8) & 0xff); - result[2] = curlx_ultouc((ctx->a >> 16) & 0xff); - result[3] = curlx_ultouc(ctx->a >> 24); - result[4] = curlx_ultouc((ctx->b) & 0xff); - result[5] = curlx_ultouc((ctx->b >> 8) & 0xff); - result[6] = curlx_ultouc((ctx->b >> 16) & 0xff); - result[7] = curlx_ultouc(ctx->b >> 24); - result[8] = curlx_ultouc((ctx->c) & 0xff); - result[9] = curlx_ultouc((ctx->c >> 8) & 0xff); - result[10] = curlx_ultouc((ctx->c >> 16) & 0xff); - result[11] = curlx_ultouc(ctx->c >> 24); - result[12] = curlx_ultouc((ctx->d) & 0xff); - result[13] = curlx_ultouc((ctx->d >> 8) & 0xff); - result[14] = curlx_ultouc((ctx->d >> 16) & 0xff); - result[15] = curlx_ultouc(ctx->d >> 24); + digest[0] = curlx_ultouc((ctx->a) & 0xff); + digest[1] = curlx_ultouc((ctx->a >> 8) & 0xff); + digest[2] = curlx_ultouc((ctx->a >> 16) & 0xff); + digest[3] = curlx_ultouc(ctx->a >> 24); + digest[4] = curlx_ultouc((ctx->b) & 0xff); + digest[5] = curlx_ultouc((ctx->b >> 8) & 0xff); + digest[6] = curlx_ultouc((ctx->b >> 16) & 0xff); + digest[7] = curlx_ultouc(ctx->b >> 24); + digest[8] = curlx_ultouc((ctx->c) & 0xff); + digest[9] = curlx_ultouc((ctx->c >> 8) & 0xff); + digest[10] = curlx_ultouc((ctx->c >> 16) & 0xff); + digest[11] = curlx_ultouc(ctx->c >> 24); + digest[12] = curlx_ultouc((ctx->d) & 0xff); + digest[13] = curlx_ultouc((ctx->d >> 8) & 0xff); + digest[14] = curlx_ultouc((ctx->d >> 16) & 0xff); + digest[15] = curlx_ultouc(ctx->d >> 24); memset(ctx, 0, sizeof(*ctx)); } #endif /* CRYPTO LIBS */ -CURLcode 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)) + my_md4_ctx ctx; + if(!my_md4_init(&ctx)) return CURLE_FAILED_INIT; -#endif - - MD4_Update(&ctx, input, curlx_uztoui(len)); - MD4_Final(output, &ctx); + my_md4_update(&ctx, input, curlx_uztoui(len)); + my_md4_final(output, &ctx); return CURLE_OK; }
diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c index 3e068cc..4dd0d7c 100644 --- a/Utilities/cmcurl/lib/md5.c +++ b/Utilities/cmcurl/lib/md5.c
@@ -30,53 +30,23 @@ #include "curl_hmac.h" #ifdef USE_OPENSSL - #include <openssl/opensslconf.h> - #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0) - #define USE_OPENSSL_MD5 - #endif +#include <openssl/opensslconf.h> #endif #ifdef USE_WOLFSSL - #include <wolfssl/options.h> - #ifndef NO_MD5 - #define USE_WOLFSSL_MD5 - #endif +#include <wolfssl/options.h> #endif #ifdef USE_MBEDTLS - #include <mbedtls/version.h> - #if MBEDTLS_VERSION_NUMBER < 0x03020000 - #error "mbedTLS 3.2.0 or later required" - #endif - #include <psa/crypto_config.h> - #if defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 /* mbedTLS 4+ */ - #define USE_MBEDTLS_MD5 - #endif +#include <mbedtls/version.h> +#if MBEDTLS_VERSION_NUMBER < 0x03020000 +#error "mbedTLS 3.2.0 or later required" +#endif +#include <psa/crypto_config.h> #endif #ifdef USE_GNUTLS #include <nettle/md5.h> -#elif defined(USE_OPENSSL_MD5) -#include <openssl/md5.h> -#elif defined(USE_WOLFSSL_MD5) -#include <wolfssl/openssl/md5.h> -#elif defined(USE_MBEDTLS_MD5) -#include <psa/crypto.h> -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ - defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ - (__MAC_OS_X_VERSION_MIN_REQUIRED < 101500)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ - defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ - (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) -#define AN_APPLE_OS -#include <CommonCrypto/CommonDigest.h> -#elif defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> -#endif - -#ifdef USE_GNUTLS typedef struct md5_ctx my_md5_ctx; @@ -87,10 +57,9 @@ } static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int inputLen) + const unsigned char *input, unsigned int len) { - md5_update(ctx, inputLen, input); + md5_update(ctx, len, input); } static void my_md5_final(unsigned char *digest, void *ctx) @@ -98,8 +67,9 @@ md5_digest(ctx, 16, digest); } -#elif defined(USE_OPENSSL_MD5) || \ - (defined(USE_WOLFSSL_MD5) && !defined(OPENSSL_COEXIST)) +#elif defined(USE_OPENSSL) && \ + !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0) +#include <openssl/md5.h> typedef MD5_CTX my_md5_ctx; @@ -112,8 +82,7 @@ } static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int len) + const unsigned char *input, unsigned int len) { (void)MD5_Update(ctx, input, len); } @@ -123,31 +92,32 @@ (void)MD5_Final(digest, ctx); } -#elif defined(USE_WOLFSSL_MD5) +#elif defined(USE_WOLFSSL) && !defined(NO_MD5) +#include <wolfssl/wolfcrypt/md5.h> -typedef WOLFSSL_MD5_CTX my_md5_ctx; +typedef wc_Md5 my_md5_ctx; static CURLcode my_md5_init(void *ctx) { - if(!wolfSSL_MD5_Init(ctx)) + if(wc_InitMd5(ctx)) return CURLE_OUT_OF_MEMORY; - return CURLE_OK; } static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int len) + const unsigned char *input, unsigned int len) { - (void)wolfSSL_MD5_Update(ctx, input, len); + (void)wc_Md5Update(ctx, input, (word32)len); } static void my_md5_final(unsigned char *digest, void *ctx) { - (void)wolfSSL_MD5_Final(digest, ctx); + (void)wc_Md5Final(ctx, digest); } -#elif defined(USE_MBEDTLS_MD5) +#elif defined(USE_MBEDTLS) && \ + defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 /* mbedTLS 4+ */ +#include <psa/crypto.h> typedef psa_hash_operation_t my_md5_ctx; @@ -160,10 +130,9 @@ } static void my_md5_update(void *ctx, - const unsigned char *data, - unsigned int length) + const unsigned char *input, unsigned int len) { - (void)psa_hash_update(ctx, data, length); + (void)psa_hash_update(ctx, input, len); } static void my_md5_final(unsigned char *digest, void *ctx) @@ -172,7 +141,15 @@ (void)psa_hash_finish(ctx, digest, 16, &actual_length); } -#elif defined(AN_APPLE_OS) +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ + defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ + (__MAC_OS_X_VERSION_MIN_REQUIRED < 101500)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ + defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ + (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) +#include <CommonCrypto/CommonDigest.h> /* For Apple operating systems: CommonCrypto has the functions we need. These functions are available on Tiger and later, as well as iOS 2.0 @@ -191,10 +168,9 @@ } static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int inputLen) + const unsigned char *input, unsigned int len) { - CC_MD5_Update(ctx, input, inputLen); + CC_MD5_Update(ctx, input, len); } static void my_md5_final(unsigned char *digest, void *ctx) @@ -203,6 +179,7 @@ } #elif defined(USE_WIN32_CRYPTO) +#include <wincrypt.h> struct md5_ctx { HCRYPTPROV hCryptProv; @@ -227,11 +204,10 @@ } static void my_md5_update(void *in, - const unsigned char *input, - unsigned int inputLen) + const unsigned char *input, unsigned int len) { my_md5_ctx *ctx = in; - CryptHashData(ctx->hHash, (const BYTE *)input, inputLen, 0); + CryptHashData(ctx->hHash, (const BYTE *)input, len, 0); } static void my_md5_final(unsigned char *digest, void *in) @@ -256,17 +232,17 @@ * MD5 Message-Digest Algorithm (RFC 1321). * * Homepage: - https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 * * Author: * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> * * This software was written by Alexander Peslyak in 2001. No copyright is - * claimed, and the software is hereby placed in the public domain. - * In case this attempt to disclaim copyright and place the software in the - * public domain is deemed null and void, then the software is - * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the - * general public under the following terms: + * claimed, and the software is hereby placed in the public domain. In case + * this attempt to disclaim copyright and place the software in the public + * domain is deemed null and void, then the software is Copyright (c) 2001 + * Alexander Peslyak and it is hereby released to the general public under + * the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. @@ -276,27 +252,19 @@ * (This is a heavily cut-down "BSD license".) */ -/* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int MD5_u32plus; - struct md5_ctx { - MD5_u32plus lo, hi; - MD5_u32plus a, b, c, d; + uint32_t lo, hi; + uint32_t a, b, c, d; unsigned char buffer[64]; - MD5_u32plus block[16]; + uint32_t block[16]; }; typedef struct md5_ctx my_md5_ctx; -static CURLcode my_md5_init(void *ctx); -static void my_md5_update(void *ctx, const unsigned char *data, - unsigned int size); -static void my_md5_final(unsigned char *result, void *ctx); - /* * The basic MD5 functions. * * F and G are optimized compared to their RFC 1321 definitions for - * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * architectures that lack an AND-NOT instruction, like in Colin Plumb's * implementation. */ #define MD5_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) @@ -309,7 +277,7 @@ * The MD5 transformation for all four rounds. */ #define MD5_STEP(f, a, b, c, d, x, t, s) \ - (a) += f((b), (c), (d)) + (x) + (t); \ + (a) += f(b, c, d) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b); @@ -317,20 +285,19 @@ * SET reads 4 input bytes in little-endian byte order and stores them * in a properly aligned word in host byte order. * - * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * does not work. + * The check for little-endian architectures that tolerate unaligned memory + * accesses is an optimization. Nothing will break if it does not work. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define MD5_SET(n) (*(const MD5_u32plus *)(const void *)&ptr[(n) * 4]) +#define MD5_SET(n) (*(const uint32_t *)(const void *)&ptr[(n) * 4]) #define MD5_GET(n) MD5_SET(n) #else -#define MD5_SET(n) (ctx->block[(n)] = \ - (MD5_u32plus)ptr[(n) * 4] | \ - ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ - ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ - ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) -#define MD5_GET(n) (ctx->block[(n)]) +#define MD5_SET(n) (ctx->block[n] = \ + (uint32_t)ptr[(n) * 4] | \ + ((uint32_t)ptr[((n) * 4) + 1] << 8) | \ + ((uint32_t)ptr[((n) * 4) + 2] << 16) | \ + ((uint32_t)ptr[((n) * 4) + 3] << 24)) +#define MD5_GET(n) ctx->block[n] #endif /* @@ -341,7 +308,7 @@ const void *data, unsigned long size) { const unsigned char *ptr; - MD5_u32plus a, b, c, d; + uint32_t a, b, c, d; ptr = (const unsigned char *)data; @@ -351,7 +318,7 @@ d = ctx->d; do { - MD5_u32plus saved_a, saved_b, saved_c, saved_d; + uint32_t saved_a, saved_b, saved_c, saved_d; saved_a = a; saved_b = b; @@ -460,44 +427,44 @@ return CURLE_OK; } -static void my_md5_update(void *in, const unsigned char *data, - unsigned int size) +static void my_md5_update(void *in, + const unsigned char *input, unsigned int len) { - MD5_u32plus saved_lo; + uint32_t saved_lo; unsigned int used; my_md5_ctx *ctx = (my_md5_ctx *)in; saved_lo = ctx->lo; - ctx->lo = (saved_lo + size) & 0x1fffffff; + ctx->lo = (saved_lo + len) & 0x1fffffff; if(ctx->lo < saved_lo) ctx->hi++; - ctx->hi += (MD5_u32plus)size >> 29; + ctx->hi += (uint32_t)len >> 29; used = saved_lo & 0x3f; if(used) { unsigned int available = 64 - used; - if(size < available) { - memcpy(&ctx->buffer[used], data, size); + if(len < available) { + memcpy(&ctx->buffer[used], input, len); return; } - memcpy(&ctx->buffer[used], data, available); - data = (const unsigned char *)data + available; - size -= available; + memcpy(&ctx->buffer[used], input, available); + input = (const unsigned char *)input + available; + len -= available; my_md5_body(ctx, ctx->buffer, 64); } - if(size >= 64) { - data = my_md5_body(ctx, data, size & ~(unsigned long)0x3f); - size &= 0x3f; + if(len >= 64) { + input = my_md5_body(ctx, input, len & ~(unsigned long)0x3f); + len &= 0x3f; } - memcpy(ctx->buffer, data, size); + memcpy(ctx->buffer, input, len); } -static void my_md5_final(unsigned char *result, void *in) +static void my_md5_final(unsigned char *digest, void *in) { unsigned int used, available; my_md5_ctx *ctx = (my_md5_ctx *)in; @@ -529,22 +496,22 @@ my_md5_body(ctx, ctx->buffer, 64); - result[0] = curlx_ultouc((ctx->a) & 0xff); - result[1] = curlx_ultouc((ctx->a >> 8) & 0xff); - result[2] = curlx_ultouc((ctx->a >> 16) & 0xff); - result[3] = curlx_ultouc(ctx->a >> 24); - result[4] = curlx_ultouc((ctx->b) & 0xff); - result[5] = curlx_ultouc((ctx->b >> 8) & 0xff); - result[6] = curlx_ultouc((ctx->b >> 16) & 0xff); - result[7] = curlx_ultouc(ctx->b >> 24); - result[8] = curlx_ultouc((ctx->c) & 0xff); - result[9] = curlx_ultouc((ctx->c >> 8) & 0xff); - result[10] = curlx_ultouc((ctx->c >> 16) & 0xff); - result[11] = curlx_ultouc(ctx->c >> 24); - result[12] = curlx_ultouc((ctx->d) & 0xff); - result[13] = curlx_ultouc((ctx->d >> 8) & 0xff); - result[14] = curlx_ultouc((ctx->d >> 16) & 0xff); - result[15] = curlx_ultouc(ctx->d >> 24); + digest[0] = curlx_ultouc((ctx->a) & 0xff); + digest[1] = curlx_ultouc((ctx->a >> 8) & 0xff); + digest[2] = curlx_ultouc((ctx->a >> 16) & 0xff); + digest[3] = curlx_ultouc(ctx->a >> 24); + digest[4] = curlx_ultouc((ctx->b) & 0xff); + digest[5] = curlx_ultouc((ctx->b >> 8) & 0xff); + digest[6] = curlx_ultouc((ctx->b >> 16) & 0xff); + digest[7] = curlx_ultouc(ctx->b >> 24); + digest[8] = curlx_ultouc((ctx->c) & 0xff); + digest[9] = curlx_ultouc((ctx->c >> 8) & 0xff); + digest[10] = curlx_ultouc((ctx->c >> 16) & 0xff); + digest[11] = curlx_ultouc(ctx->c >> 24); + digest[12] = curlx_ultouc((ctx->d) & 0xff); + digest[13] = curlx_ultouc((ctx->d >> 8) & 0xff); + digest[14] = curlx_ultouc((ctx->d >> 16) & 0xff); + digest[15] = curlx_ultouc(ctx->d >> 24); memset(ctx, 0, sizeof(*ctx)); } @@ -572,16 +539,21 @@ * @unittest: 1601 * Returns CURLE_OK on success. */ -CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input, - const size_t len) +CURLcode Curl_md5it(unsigned char *output, + const unsigned char *input, size_t len) { CURLcode result; my_md5_ctx ctx; result = my_md5_init(&ctx); if(!result) { - my_md5_update(&ctx, input, curlx_uztoui(len)); - my_md5_final(outbuffer, &ctx); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + my_md5_update(&ctx, input, ilen); + input += ilen; + len -= ilen; + } while(len); + my_md5_final(output, &ctx); } return result; } @@ -615,10 +587,9 @@ } CURLcode Curl_MD5_update(struct MD5_context *context, - const unsigned char *data, - unsigned int len) + const unsigned char *input, unsigned int len) { - (*context->md5_hash->md5_update_func)(context->md5_hashctx, data, len); + (*context->md5_hash->md5_update_func)(context->md5_hashctx, input, len); return CURLE_OK; }
diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c index a99972f..6eda7a8 100644 --- a/Utilities/cmcurl/lib/memdebug.c +++ b/Utilities/cmcurl/lib/memdebug.c
@@ -23,7 +23,7 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef CURLDEBUG +#ifdef CURL_MEMDEBUG #include <stddef.h> /* for offsetof() */ @@ -32,7 +32,7 @@ #include "curlx/fopen.h" /* for CURLX_FOPEN_LOW(), CURLX_FREOPEN_LOW() */ #ifdef USE_BACKTRACE -#include "backtrace.h" +#include <backtrace.h> #endif struct memdebug { @@ -65,14 +65,14 @@ static char membuf[10000]; static size_t memwidx = 0; /* write index */ -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_MUTEX static bool dbg_mutex_init = 0; static curl_mutex_t dbg_mutex; #endif static bool curl_dbg_lock(void) { -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_MUTEX if(dbg_mutex_init) { Curl_mutex_acquire(&dbg_mutex); return TRUE; @@ -83,7 +83,7 @@ static void curl_dbg_unlock(bool was_locked) { -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_MUTEX if(was_locked) Curl_mutex_release(&dbg_mutex); #else @@ -99,6 +99,7 @@ _exit() comes after the atexit handlers are called. curl/curl#6620 */ static void curl_dbg_cleanup(void) { + bool locked = curl_dbg_lock(); if(curl_dbg_logfile && curl_dbg_logfile != stderr && curl_dbg_logfile != stdout) { @@ -108,13 +109,15 @@ fclose(curl_dbg_logfile); } curl_dbg_logfile = NULL; -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) + curl_dbg_unlock(locked); +#ifdef USE_MUTEX if(dbg_mutex_init) { Curl_mutex_destroy(&dbg_mutex); dbg_mutex_init = FALSE; } #endif } + #ifdef USE_BACKTRACE static void error_bt_callback(void *data, const char *message, int error_number) @@ -156,7 +159,7 @@ setbuf(curl_dbg_logfile, (char *)NULL); #endif } -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_MUTEX if(!dbg_mutex_init) { dbg_mutex_init = TRUE; Curl_mutex_init(&dbg_mutex); @@ -218,7 +221,7 @@ /* alloc at least 64 bytes */ size = sizeof(struct memdebug) + wantedsize; - mem = (Curl_cmalloc)(size); + mem = Curl_cmalloc(size); if(mem) { mem->size = wantedsize; } @@ -248,7 +251,7 @@ user_size = wanted_size * wanted_elements; size = sizeof(struct memdebug) + user_size; - mem = (Curl_ccalloc)(1, size); + mem = Curl_ccalloc(1, size); if(mem) mem->size = user_size; @@ -343,7 +346,7 @@ # pragma warning(pop) #endif - mem = (Curl_crealloc)(mem, size); + mem = Curl_crealloc(mem, size); if(source) curl_dbg_log_locked("MEM %s:%d realloc(%p, %zu) = %p\n", source, line, (void *)ptr, wantedsize, @@ -379,7 +382,7 @@ #endif /* free for real */ - (Curl_cfree)(mem); + Curl_cfree(mem); } } @@ -496,8 +499,7 @@ FILE *curl_dbg_fdopen(int filedes, const char *mode, int line, const char *source) { - /* !checksrc! disable BANNEDFUNC 1 */ - FILE *res = fdopen(filedes, mode); + FILE *res = CURLX_FDOPEN_LOW(filedes, mode); if(source) curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n", source, line, filedes, mode, (void *)res); @@ -574,4 +576,4 @@ curl_dbg_unlock(was_locked); } -#endif /* CURLDEBUG */ +#endif /* CURL_MEMDEBUG */
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index 37b50de..b7e51aa 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c
@@ -30,59 +30,30 @@ #include "sendf.h" #include "curl_trc.h" #include "transfer.h" -#include "strdup.h" +#include "curlx/strdup.h" +#include "curlx/basename.h" #include "curlx/strcopy.h" #include "curlx/fopen.h" #include "curlx/base64.h" -#if !defined(CURL_DISABLE_MIME) && \ - (!defined(CURL_DISABLE_HTTP) || \ - !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP)) - -#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) -#include <libgen.h> -#endif +#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \ + !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP)) #include "rand.h" #include "slist.h" #include "curlx/dynbuf.h" -#ifdef _WIN32 -# ifndef R_OK -# define R_OK 4 -# endif -#endif +#define MAX_MIME_LEVELS 40 /* avoid deep nesting */ #define READ_ERROR ((size_t)-1) #define STOP_FILLING ((size_t)-2) static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, - void *instream, bool *hasread); - -/* Encoders. */ -static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_nop_size(curl_mimepart *part); -static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_base64_size(curl_mimepart *part); -static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_qp_size(curl_mimepart *part); + void *instream, bool *hasread, + size_t call_depth); static curl_off_t mime_size(curl_mimepart *part); -static const struct mime_encoder encoders[] = { - { "binary", encoder_nop_read, encoder_nop_size }, - { "8bit", encoder_nop_read, encoder_nop_size }, - { "7bit", encoder_7bit_read, encoder_nop_size }, - { "base64", encoder_base64_read, encoder_base64_size }, - { "quoted-printable", encoder_qp_read, encoder_qp_size }, - { ZERO_NULL, ZERO_NULL, ZERO_NULL } -}; - /* Quoted-printable character class table. * * We cannot rely on ctype functions since quoted-printable input data @@ -123,7 +94,7 @@ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x41\x42\x43\x44\x45\x46"; #ifndef __VMS -#define filesize(name, stat_data) (stat_data.st_size) +#define filesize(name, stat_data) stat_data.st_size #define fopen_read curlx_fopen #else @@ -139,7 +110,7 @@ * and CD/DVD images should be either a STREAM_LF format or a fixed format. * */ -curl_off_t VmsRealFileSize(const char *name, const struct_stat *stat_buf) +curl_off_t VmsRealFileSize(const char *name, const curlx_struct_stat *stat_buf) { char buffer[8192]; curl_off_t count; @@ -168,7 +139,8 @@ * if not to call a routine to get the correct size. * */ -static curl_off_t VmsSpecialSize(const char *name, const struct_stat *stat_buf) +static curl_off_t VmsSpecialSize(const char *name, + const curlx_struct_stat *stat_buf) { switch(stat_buf->st_fab_rfm) { case FAB$C_VAR: @@ -192,73 +164,25 @@ */ static FILE *vmsfopenread(const char *file, const char *mode) { - struct_stat statbuf; - int result; + curlx_struct_stat statbuf; + int res = curlx_stat(file, &statbuf); - result = curlx_stat(file, &statbuf); - - switch(statbuf.st_fab_rfm) { - case FAB$C_VAR: - case FAB$C_VFC: - case FAB$C_STMCR: - return curlx_fopen(file, FOPEN_READTEXT); /* VMS */ - break; - default: - return curlx_fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm"); + if(res != -1) { + switch(statbuf.st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + case FAB$C_STMCR: + return curlx_fopen(file, FOPEN_READTEXT); /* VMS */ + break; + default: + return curlx_fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm"); + } } + return NULL; } #define fopen_read vmsfopenread -#endif - -#ifndef HAVE_BASENAME -/* - (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 - Edition) - - The basename() function shall take the pathname pointed to by path and - return a pointer to the final component of the pathname, deleting any - trailing '/' characters. - - If the string pointed to by path consists entirely of the '/' character, - basename() shall return a pointer to the string "/". If the string pointed - to by path is exactly "//", it is implementation-defined whether '/' or "//" - is returned. - - If path is a null pointer or points to an empty string, basename() shall - return a pointer to the string ".". - - The basename() function may modify the string pointed to by path, and may - return a pointer to static storage that may then be overwritten by a - subsequent call to basename(). - - The basename() function need not be reentrant. A function that is not - required to be reentrant is not required to be thread-safe. - -*/ -static char *Curl_basename(char *path) -{ - /* Ignore all the details above for now and make a quick and simple - implementation here */ - char *s1; - char *s2; - - s1 = strrchr(path, '/'); - s2 = strrchr(path, '\\'); - - if(s1 && s2) { - path = (s1 > s2 ? s1 : s2) + 1; - } - else if(s1) - path = s1 + 1; - else if(s2) - path = s2 + 1; - - return path; -} - -#define basename(x) Curl_basename(x) -#endif +#endif /* !__VMS */ /* Set readback state. */ static void mimesetstate(struct mime_state *state, @@ -348,7 +272,7 @@ the buffer it works on */ if(!filename) return NULL; - base = curlx_strdup(basename(filename)); + base = curlx_strdup(curlx_basename(filename)); curlx_free(filename); /* free temporary buffer */ @@ -363,7 +287,7 @@ p->bufend = 0; } -/* Dummy encoder. This is used for 8bit and binary content encodings. */ +/* Dummy encoder. This is used for 8-bit and binary content encodings. */ static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, struct curl_mimepart *part) { @@ -390,7 +314,7 @@ return part->datasize; } -/* 7bit encoder: the encoder is just a data validity check. */ +/* 7-bit encoder: the encoder is a data validity check. */ static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, curl_mimepart *part) { @@ -503,10 +427,10 @@ return size; /* Unknown size or no data. */ /* Compute base64 character count. */ - size = 4 * (1 + (size - 1) / 3); + size = 4 * (1 + ((size - 1) / 3)); /* Effective character count must include CRLFs. */ - return size + 2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH); + return size + (2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH)); } /* Quoted-printable lookahead. @@ -544,7 +468,7 @@ while(st->bufbeg < st->bufend) { size_t len = 1; size_t consumed = 1; - int i = st->buf[st->bufbeg]; + int i = (unsigned char)st->buf[st->bufbeg]; buf[0] = (char)i; buf[1] = aschex[(i >> 4) & 0xF]; buf[2] = aschex[i & 0xF]; @@ -602,7 +526,7 @@ } } if(softlinebreak) { - curlx_strcopy(buf, sizeof(buf), "\x3D\x0D\x0A", 3); /* "=\r\n" */ + curlx_strcopy(buf, sizeof(buf), STRCONST("\x3D\x0D\x0A")); /* =\r\n */ len = 3; consumed = 0; } @@ -679,7 +603,7 @@ static void mime_mem_free(void *ptr) { - Curl_safefree(((curl_mimepart *)ptr)->data); + curlx_safefree(((curl_mimepart *)ptr)->data); } /* Named file callbacks. */ @@ -730,7 +654,7 @@ curlx_fclose(part->fp); part->fp = NULL; } - Curl_safefree(part->data); + curlx_safefree(part->data); } /* Subparts callbacks. */ @@ -766,11 +690,16 @@ } /* Read a non-encoded part content. */ -static size_t read_part_content(curl_mimepart *part, - char *buffer, size_t bufsize, bool *hasread) + +static size_t read_part_content(curl_mimepart *part, char *buffer, + size_t bufsize, bool *hasread, + size_t call_depth) { size_t sz = 0; + if(++call_depth > MAX_MIME_LEVELS) + return READ_ERROR; + switch(part->lastreadstatus) { case 0: case CURL_READFUNC_ABORT: @@ -793,7 +722,8 @@ * Cannot be processed as other kinds since read function requires * an additional parameter and is highly recursive. */ - sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread); + sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread, + call_depth); break; case MIMEKIND_FILE: if(part->fp && feof(part->fp)) @@ -832,13 +762,17 @@ /* Read and encode part content. */ static size_t read_encoded_part_content(curl_mimepart *part, char *buffer, - size_t bufsize, bool *hasread) + size_t bufsize, bool *hasread, + size_t call_depth) { struct mime_encoder_state *st = &part->encstate; size_t cursize = 0; size_t sz; bool ateof = FALSE; + if(++call_depth > MAX_MIME_LEVELS) + return READ_ERROR; + for(;;) { if(st->bufbeg < st->bufend || ateof) { /* Encode buffered data. */ @@ -871,7 +805,7 @@ if(st->bufend >= sizeof(st->buf)) return cursize ? cursize : READ_ERROR; /* Buffer full. */ sz = read_part_content(part, st->buf + st->bufend, - sizeof(st->buf) - st->bufend, hasread); + sizeof(st->buf) - st->bufend, hasread, call_depth); switch(sz) { case 0: ateof = TRUE; @@ -892,12 +826,15 @@ /* Readback a mime part. */ static size_t readback_part(curl_mimepart *part, - char *buffer, size_t bufsize, bool *hasread) + char *buffer, size_t bufsize, bool *hasread, + size_t call_depth) { size_t cursize = 0; - /* Readback from part. */ + if(++call_depth > MAX_MIME_LEVELS) + return READ_ERROR; + /* Readback from part. */ while(bufsize) { size_t sz = 0; struct curl_slist *hdr = (struct curl_slist *)part->state.ptr; @@ -940,9 +877,10 @@ break; case MIMESTATE_CONTENT: if(part->encoder) - sz = read_encoded_part_content(part, buffer, bufsize, hasread); + sz = read_encoded_part_content(part, buffer, bufsize, hasread, + call_depth); else - sz = read_part_content(part, buffer, bufsize, hasread); + sz = read_part_content(part, buffer, bufsize, hasread, call_depth); switch(sz) { case 0: mimesetstate(&part->state, MIMESTATE_END, NULL); @@ -976,12 +914,16 @@ /* Readback from mime. Warning: not a read callback function. */ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, - void *instream, bool *hasread) + void *instream, bool *hasread, + size_t call_depth) { curl_mime *mime = (curl_mime *)instream; size_t cursize = 0; (void)size; /* Always 1 */ + if(++call_depth > MAX_MIME_LEVELS) + return READ_ERROR; + while(nitems) { size_t sz = 0; curl_mimepart *part = mime->state.ptr; @@ -1016,7 +958,7 @@ mimesetstate(&mime->state, MIMESTATE_END, NULL); break; } - sz = readback_part(part, buffer, nitems, hasread); + sz = readback_part(part, buffer, nitems, hasread, call_depth); switch(sz) { case CURL_READFUNC_ABORT: case CURL_READFUNC_PAUSE: @@ -1081,7 +1023,7 @@ { curl_mime *mime = (curl_mime *)instream; curl_mimepart *part; - int result = CURL_SEEKFUNC_OK; + int rc = CURL_SEEKFUNC_OK; if(whence != SEEK_SET || offset) return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */ @@ -1092,13 +1034,13 @@ for(part = mime->firstpart; part; part = part->nextpart) { int res = mime_part_rewind(part); if(res != CURL_SEEKFUNC_OK) - result = res; + rc = res; } - if(result == CURL_SEEKFUNC_OK) + if(rc == CURL_SEEKFUNC_OK) mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL); - return result; + return rc; } /* Release part content. */ @@ -1151,9 +1093,9 @@ 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); + curlx_safefree(part->mimetype); + curlx_safefree(part->name); + curlx_safefree(part->filename); Curl_mime_initpart(part); } } @@ -1285,7 +1227,7 @@ /* Initialize a mime part. */ void Curl_mime_initpart(curl_mimepart *part) { - memset((char *)part, 0, sizeof(*part)); + memset(part, 0, sizeof(*part)); part->lastreadstatus = 1; /* Successful read status. */ mimesetstate(&part->state, MIMESTATE_BEGIN, NULL); } @@ -1321,7 +1263,7 @@ if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->name); + curlx_safefree(part->name); if(name) { part->name = curlx_strdup(name); @@ -1338,7 +1280,7 @@ if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->filename); + curlx_safefree(part->filename); if(filename) { part->filename = curlx_strdup(filename); @@ -1350,18 +1292,18 @@ } /* Set mime part content from memory data. */ -CURLcode curl_mime_data(curl_mimepart *part, const char *ptr, size_t datasize) +CURLcode curl_mime_data(curl_mimepart *part, const char *data, size_t datasize) { if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; cleanup_part_content(part); - if(ptr) { + if(data) { if(datasize == CURL_ZERO_TERMINATED) - datasize = strlen(ptr); + datasize = strlen(data); - part->data = Curl_memdup0(ptr, datasize); + part->data = curlx_memdup0(data, datasize); if(!part->data) return CURLE_OUT_OF_MEMORY; @@ -1388,7 +1330,7 @@ if(filename) { char *base; - struct_stat sbuf; + curlx_struct_stat sbuf; if(curlx_stat(filename, &sbuf)) result = CURLE_READ_ERROR; @@ -1430,7 +1372,7 @@ if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->mimetype); + curlx_safefree(part->mimetype); if(mimetype) { part->mimetype = curlx_strdup(mimetype); @@ -1441,6 +1383,15 @@ return CURLE_OK; } +static const struct mime_encoder encoders[] = { + { "binary", encoder_nop_read, encoder_nop_size }, + { "8bit", encoder_nop_read, encoder_nop_size }, + { "7bit", encoder_7bit_read, encoder_nop_size }, + { "base64", encoder_base64_read, encoder_base64_size }, + { "quoted-printable", encoder_qp_read, encoder_qp_size }, + { ZERO_NULL, ZERO_NULL, ZERO_NULL } +}; + /* Set mime data transfer encoder. */ CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) { @@ -1576,7 +1527,7 @@ * adding any data and this loops infinitely. */ do { hasread = FALSE; - ret = readback_part(part, buffer, nitems, &hasread); + ret = readback_part(part, buffer, nitems, &hasread, 0); /* * If this is not possible to get some data without calling more than * one read callback (probably because a content encoder is not able to @@ -1751,7 +1702,7 @@ const char *boundary = NULL; char *customct; const char *cte = NULL; - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; /* Get rid of previously prepared headers. */ curl_slist_free_all(part->curlheaders); @@ -1813,35 +1764,35 @@ if(part->name) { name = escape_string(data, part->name, strategy); if(!name) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } - if(!ret && part->filename) { + if(!result && part->filename) { filename = escape_string(data, part->filename, strategy); if(!filename) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } - if(!ret) - ret = Curl_mime_add_header(&part->curlheaders, - "Content-Disposition: %s%s%s%s%s%s%s", - disposition, - name ? "; name=\"" : "", - name ? name : "", - name ? "\"" : "", - filename ? "; filename=\"" : "", - filename ? filename : "", - filename ? "\"" : ""); - Curl_safefree(name); - Curl_safefree(filename); - if(ret) - return ret; + if(!result) + result = Curl_mime_add_header(&part->curlheaders, + "Content-Disposition: %s%s%s%s%s%s%s", + disposition, + name ? "; name=\"" : "", + name ? name : "", + name ? "\"" : "", + filename ? "; filename=\"" : "", + filename ? filename : "", + filename ? "\"" : ""); + curlx_safefree(name); + curlx_safefree(filename); + if(result) + return result; } } /* Issue Content-Type header. */ if(contenttype) { - ret = add_content_type(&part->curlheaders, contenttype, boundary); - if(ret) - return ret; + result = add_content_type(&part->curlheaders, contenttype, boundary); + if(result) + return result; } /* Content-Transfer-Encoding header. */ @@ -1853,10 +1804,10 @@ part->kind != MIMEKIND_MULTIPART) cte = "8bit"; if(cte) { - ret = Curl_mime_add_header(&part->curlheaders, - "Content-Transfer-Encoding: %s", cte); - if(ret) - return ret; + result = Curl_mime_add_header(&part->curlheaders, + "Content-Transfer-Encoding: %s", cte); + if(result) + return result; } } @@ -1873,13 +1824,13 @@ if(content_type_match(contenttype, STRCONST("multipart/form-data"))) disposition = "form-data"; for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) { - ret = Curl_mime_prepare_headers(data, subpart, NULL, - disposition, strategy); - if(ret) - return ret; + result = Curl_mime_prepare_headers(data, subpart, NULL, + disposition, strategy); + if(result) + return result; } } - return ret; + return result; } /* Recursively reset paused status in the given part. */ @@ -2000,14 +1951,14 @@ else nread = Curl_mime_read(buf, 1, blen, ctx->part); - CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zd", + CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zu", blen, nread); switch(nread) { case 0: if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { failf(data, "client mime read EOF fail, " - "only %"FMT_OFF_T"/%"FMT_OFF_T + "only %" FMT_OFF_T "/%" FMT_OFF_T " of needed bytes read", ctx->read_len, ctx->total_len); return CURLE_READ_ERROR; } @@ -2057,12 +2008,12 @@ if(ctx->total_len >= 0) ctx->seen_eos = (ctx->read_len >= ctx->total_len); *pnread = nread; - *peos = ctx->seen_eos; + *peos = (bool)ctx->seen_eos; break; } CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T - ", read=%"FMT_OFF_T") -> %d, %zu, %d", + ", read=%" FMT_OFF_T ") -> %d, %zu, %d", blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); return result; } @@ -2194,7 +2145,8 @@ } #else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP || - !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */ + !CURL_DISABLE_SMTP || + !CURL_DISABLE_IMAP) */ /* Mime not compiled in: define stubs for externally-referenced functions. */ curl_mime *curl_mime_init(CURL *easy)
diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h index 81080b4..e84f040 100644 --- a/Utilities/cmcurl/lib/mime.h +++ b/Utilities/cmcurl/lib/mime.h
@@ -160,15 +160,14 @@ */ CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part); -#else -/* if disabled */ +#else /* if disabled */ #define Curl_mime_initpart(x) #define Curl_mime_cleanpart(x) #define Curl_mime_duppart(x, y, z) CURLE_OK /* Nothing to duplicate. Succeed */ #define Curl_mime_set_subparts(a, b, c) CURLE_NOT_BUILT_IN #define Curl_mime_prepare_headers(a, b, c, d, e) CURLE_NOT_BUILT_IN #define Curl_mime_read NULL -#define Curl_creader_set_mime(x, y) ((void)x, CURLE_NOT_BUILT_IN) +#define Curl_creader_set_mime(x, y) ((void)(x), CURLE_NOT_BUILT_IN) #endif #endif /* HEADER_CURL_MIME_H */
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index 50ede21..6eaea66 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c
@@ -26,67 +26,41 @@ #include "curlx/dynbuf.h" #include "curl_printf.h" #include "curlx/strparse.h" - -#ifdef HAVE_LONGLONG -# define LONG_LONG_TYPE long long -# define HAVE_LONG_LONG_TYPE -#elif defined(_MSC_VER) -# define LONG_LONG_TYPE __int64 -# define HAVE_LONG_LONG_TYPE -#else -# undef LONG_LONG_TYPE -# undef HAVE_LONG_LONG_TYPE -#endif - -/* - * Max integer data types that mprintf.c is capable - */ - -#ifdef HAVE_LONG_LONG_TYPE -# define mp_intmax_t LONG_LONG_TYPE -# define mp_uintmax_t unsigned LONG_LONG_TYPE -#else -# define mp_intmax_t long -# define mp_uintmax_t unsigned long -#endif +#include "curlx/snprintf.h" /* for curlx_win32_snprintf() */ #define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should fit negative DBL_MAX (317 letters) */ #define MAX_PARAMETERS 128 /* number of input arguments */ #define MAX_SEGMENTS 128 /* number of output segments */ -#ifdef __AMIGA__ -# undef FORMAT_INT -#endif - -/* Lower-case digits. */ +/* Lower-case digits. */ const unsigned char Curl_ldigits[] = "0123456789abcdef"; -/* Upper-case digits. */ +/* Upper-case digits. */ const unsigned char Curl_udigits[] = "0123456789ABCDEF"; -#define OUTCHAR(x) \ - do { \ - if(stream((unsigned char)x, userp)) \ - return TRUE; \ - (*donep)++; \ +#define OUTCHAR(x) \ + do { \ + if(stream((unsigned char)(x), userp)) \ + return TRUE; \ + (*donep)++; \ } while(0) /* Data type to read from the arglist */ typedef enum { - FORMAT_STRING, - FORMAT_PTR, - FORMAT_INTPTR, - FORMAT_INT, - FORMAT_LONG, - FORMAT_LONGLONG, - FORMAT_INTU, - FORMAT_LONGU, - FORMAT_LONGLONGU, - FORMAT_DOUBLE, - FORMAT_LONGDOUBLE, - FORMAT_WIDTH, - FORMAT_PRECISION + MTYPE_STRING, + MTYPE_PTR, + MTYPE_INTPTR, + MTYPE_INT, + MTYPE_LONG, + MTYPE_LONGLONG, + MTYPE_INTU, + MTYPE_LONGU, + MTYPE_LONGLONGU, + MTYPE_DOUBLE, + MTYPE_LONGDOUBLE, + MTYPE_WIDTH, + MTYPE_PRECISION } FormatType; /* conversion and display flags */ @@ -128,8 +102,8 @@ union { const char *str; void *ptr; - mp_intmax_t nums; /* signed */ - mp_uintmax_t numu; /* unsigned */ + int64_t nums; /* signed */ + uint64_t numu; /* unsigned */ double dnum; } val; }; @@ -158,9 +132,9 @@ }; /* the provided input number is 1-based but this returns the number 0-based. - - returns -1 if no valid number was provided. -*/ + * + * returns -1 if no valid number was provided. + */ static int dollarstring(const char *p, const char **end) { curl_off_t num; @@ -172,7 +146,7 @@ } #define is_arg_used(x, y) ((x)[(y) / 8] & (1 << ((y) & 7))) -#define mark_arg_used(x, y) ((x)[y / 8] |= (unsigned char)(1 << ((y) & 7))) +#define mark_arg_used(x, y) ((x)[(y) / 8] |= (unsigned char)(1 << ((y) & 7))) /* * Parse the format string. @@ -195,6 +169,261 @@ #define PFMT_PRECARG 10 /* attempted to use same arg twice, for prec */ #define PFMT_MANYSEGS 11 /* maxed out output segments */ +static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar, + int *precp, int *widthp) +{ + const char *fmt = *fmtp; + bool loopit = TRUE; + unsigned int flags = 0; + int width = 0; + int precision = 0; + + /* Handle the flags */ + do { + switch(*fmt++) { + case ' ': + flags |= FLAGS_SPACE; + break; + case '+': + flags |= FLAGS_SHOWSIGN; + break; + case '-': + flags |= FLAGS_LEFT; + flags &= ~(unsigned int)FLAGS_PAD_NIL; + break; + case '#': + flags |= FLAGS_ALT; + break; + case '.': + if('*' == *fmt) { + /* The precision is picked from a specified parameter */ + flags |= FLAGS_PRECPARAM; + fmt++; + + if(use_dollar == DOLLAR_USE) { + precision = dollarstring(fmt, &fmt); + if(precision < 0) + /* illegal combo */ + return PFMT_DOLLARPREC; + } + else + /* get it from the next argument */ + precision = -1; + } + else { + bool is_neg; + curl_off_t num; + flags |= FLAGS_PREC; + is_neg = ('-' == *fmt); + if(is_neg) + fmt++; + if(curlx_str_number(&fmt, &num, INT_MAX)) + return PFMT_PREC; + precision = (int)num; + if(is_neg) + precision = -precision; + } + if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) == + (FLAGS_PREC | FLAGS_PRECPARAM)) + /* it is not permitted to use both kinds of precision for the same + argument */ + return PFMT_PRECMIX; + break; + case 'h': + flags |= FLAGS_SHORT; + break; +#ifdef _WIN32 + case 'I': + /* Non-ANSI integer extensions I32 I64 */ + if((fmt[0] == '3') && (fmt[1] == '2')) { + flags |= FLAGS_LONG; + fmt += 2; + } + else if((fmt[0] == '6') && (fmt[1] == '4')) { + flags |= FLAGS_LONGLONG; + fmt += 2; + } + else { +#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + } + break; +#endif /* _WIN32 */ + case 'l': + if(flags & FLAGS_LONG) + flags |= FLAGS_LONGLONG; + else + flags |= FLAGS_LONG; + break; + case 'L': + flags |= FLAGS_LONGDOUBLE; + break; + case 'q': + flags |= FLAGS_LONGLONG; + break; + case 'z': + /* the code below generates a warning if -Wunreachable-code is + used */ +#if (SIZEOF_SIZE_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + break; + case 'O': +#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + break; + case '0': + if(!(flags & FLAGS_LEFT)) + flags |= FLAGS_PAD_NIL; + FALLTHROUGH(); + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + curl_off_t num; + flags |= FLAGS_WIDTH; + fmt--; + if(curlx_str_number(&fmt, &num, INT_MAX)) + return PFMT_WIDTH; + width = (int)num; + break; + } + case '*': /* read width from argument list */ + flags |= FLAGS_WIDTHPARAM; + if(use_dollar == DOLLAR_USE) { + width = dollarstring(fmt, &fmt); + if(width < 0) + /* illegal combo */ + return PFMT_DOLLARWIDTH; + } + else + /* pick from the next argument */ + width = -1; + break; + default: + loopit = FALSE; + fmt--; + break; + } /* switch */ + } while(loopit); /* do */ + *flagsp = flags; + *precp = precision; + *widthp = width; + *fmtp = fmt; + return PFMT_OK; +} + +static bool parse_conversion(const char f, unsigned int *flagp, + FormatType *typep) +{ + unsigned int flags = *flagp; + FormatType type; + switch(f) { + case 'S': + flags |= FLAGS_ALT; + type = MTYPE_STRING; + break; + case 's': + type = MTYPE_STRING; + break; + case 'n': + type = MTYPE_INTPTR; + break; + case 'p': + type = MTYPE_PTR; + break; + case 'd': + case 'i': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONG; + else if(flags & FLAGS_LONG) + type = MTYPE_LONG; + else + type = MTYPE_INT; + break; + case 'u': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONGU; + else if(flags & FLAGS_LONG) + type = MTYPE_LONGU; + else + type = MTYPE_INTU; + flags |= FLAGS_UNSIGNED; + break; + case 'o': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONGU; + else if(flags & FLAGS_LONG) + type = MTYPE_LONGU; + else + type = MTYPE_INTU; + flags |= FLAGS_OCTAL | FLAGS_UNSIGNED; + break; + case 'x': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONGU; + else if(flags & FLAGS_LONG) + type = MTYPE_LONGU; + else + type = MTYPE_INTU; + flags |= FLAGS_HEX | FLAGS_UNSIGNED; + break; + case 'X': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONGU; + else if(flags & FLAGS_LONG) + type = MTYPE_LONGU; + else + type = MTYPE_INTU; + flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED; + break; + case 'c': + type = MTYPE_INT; + flags |= FLAGS_CHAR; + break; + case 'f': + type = MTYPE_DOUBLE; + break; + case 'e': + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATE; + break; + case 'E': + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATE | FLAGS_UPPER; + break; + case 'g': + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATG; + break; + case 'G': + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATG | FLAGS_UPPER; + break; + default: + /* invalid instruction, disregard and continue */ + return TRUE; + } /* switch */ + + *flagp |= flags; + *typep = type; + return FALSE; +} + + static int parsefmt(const char *format, struct outsegment *out, struct va_input *in, @@ -218,12 +447,12 @@ while(*fmt) { if(*fmt == '%') { struct va_input *iptr; - bool loopit = TRUE; FormatType type; unsigned int flags = 0; int width = 0; int precision = 0; int param = -1; + int rc; fmt++; outlen = (size_t)(fmt - start - 1); if(*fmt == '%') { @@ -250,7 +479,7 @@ /* illegal combo */ return PFMT_DOLLAR; - /* we got no positional, just get the next arg */ + /* we got no positional, get the next arg */ param = -1; use_dollar = DOLLAR_NOPE; } @@ -258,233 +487,12 @@ use_dollar = DOLLAR_USE; } - /* Handle the flags */ - while(loopit) { - switch(*fmt++) { - case ' ': - flags |= FLAGS_SPACE; - break; - case '+': - flags |= FLAGS_SHOWSIGN; - break; - case '-': - flags |= FLAGS_LEFT; - flags &= ~(unsigned int)FLAGS_PAD_NIL; - break; - case '#': - flags |= FLAGS_ALT; - break; - case '.': - if('*' == *fmt) { - /* The precision is picked from a specified parameter */ - flags |= FLAGS_PRECPARAM; - fmt++; + rc = parse_flags(&fmt, &flags, use_dollar, &precision, &width); + if(rc) + return rc; - if(use_dollar == DOLLAR_USE) { - precision = dollarstring(fmt, &fmt); - if(precision < 0) - /* illegal combo */ - return PFMT_DOLLARPREC; - } - else - /* get it from the next argument */ - precision = -1; - } - else { - bool is_neg; - curl_off_t num; - flags |= FLAGS_PREC; - is_neg = ('-' == *fmt); - if(is_neg) - fmt++; - if(curlx_str_number(&fmt, &num, INT_MAX)) - return PFMT_PREC; - precision = (int)num; - if(is_neg) - precision = -precision; - } - if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) == - (FLAGS_PREC | FLAGS_PRECPARAM)) - /* it is not permitted to use both kinds of precision for the same - argument */ - return PFMT_PRECMIX; - break; - case 'h': - flags |= FLAGS_SHORT; - break; -#ifdef _WIN32 - case 'I': - /* Non-ANSI integer extensions I32 I64 */ - if((fmt[0] == '3') && (fmt[1] == '2')) { - flags |= FLAGS_LONG; - fmt += 2; - } - else if((fmt[0] == '6') && (fmt[1] == '4')) { - flags |= FLAGS_LONGLONG; - fmt += 2; - } - else { -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - } - break; -#endif /* _WIN32 */ - case 'l': - if(flags & FLAGS_LONG) - flags |= FLAGS_LONGLONG; - else - flags |= FLAGS_LONG; - break; - case 'L': - flags |= FLAGS_LONGDOUBLE; - break; - case 'q': - flags |= FLAGS_LONGLONG; - break; - case 'z': - /* the code below generates a warning if -Wunreachable-code is - used */ -#if (SIZEOF_SIZE_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - break; - case 'O': -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - break; - case '0': - if(!(flags & FLAGS_LEFT)) - flags |= FLAGS_PAD_NIL; - FALLTHROUGH(); - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - curl_off_t num; - flags |= FLAGS_WIDTH; - fmt--; - if(curlx_str_number(&fmt, &num, INT_MAX)) - return PFMT_WIDTH; - width = (int)num; - break; - } - case '*': /* read width from argument list */ - flags |= FLAGS_WIDTHPARAM; - if(use_dollar == DOLLAR_USE) { - width = dollarstring(fmt, &fmt); - if(width < 0) - /* illegal combo */ - return PFMT_DOLLARWIDTH; - } - else - /* pick from the next argument */ - width = -1; - break; - default: - loopit = FALSE; - fmt--; - break; - } /* switch */ - } /* while */ - - switch(*fmt) { - case 'S': - flags |= FLAGS_ALT; - FALLTHROUGH(); - case 's': - type = FORMAT_STRING; - break; - case 'n': - type = FORMAT_INTPTR; - break; - case 'p': - type = FORMAT_PTR; - break; - case 'd': - case 'i': - if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONG; - else if(flags & FLAGS_LONG) - type = FORMAT_LONG; - else - type = FORMAT_INT; - break; - case 'u': - if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONGU; - else if(flags & FLAGS_LONG) - type = FORMAT_LONGU; - else - type = FORMAT_INTU; - flags |= FLAGS_UNSIGNED; - break; - case 'o': - if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONGU; - else if(flags & FLAGS_LONG) - type = FORMAT_LONGU; - else - type = FORMAT_INTU; - flags |= FLAGS_OCTAL | FLAGS_UNSIGNED; - break; - case 'x': - if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONGU; - else if(flags & FLAGS_LONG) - type = FORMAT_LONGU; - else - type = FORMAT_INTU; - flags |= FLAGS_HEX | FLAGS_UNSIGNED; - break; - case 'X': - if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONGU; - else if(flags & FLAGS_LONG) - type = FORMAT_LONGU; - else - type = FORMAT_INTU; - flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED; - break; - case 'c': - type = FORMAT_INT; - flags |= FLAGS_CHAR; - break; - case 'f': - type = FORMAT_DOUBLE; - break; - case 'e': - type = FORMAT_DOUBLE; - flags |= FLAGS_FLOATE; - break; - case 'E': - type = FORMAT_DOUBLE; - flags |= FLAGS_FLOATE | FLAGS_UPPER; - break; - case 'g': - type = FORMAT_DOUBLE; - flags |= FLAGS_FLOATG; - break; - case 'G': - type = FORMAT_DOUBLE; - flags |= FLAGS_FLOATG | FLAGS_UPPER; - break; - default: - /* invalid instruction, disregard and continue */ + if(parse_conversion(*fmt, &flags, &type)) continue; - } /* switch */ if(flags & FLAGS_WIDTHPARAM) { if(width < 0) @@ -499,7 +507,7 @@ if(width >= max_param) max_param = width; - in[width].type = FORMAT_WIDTH; + in[width].type = MTYPE_WIDTH; /* mark as used */ mark_arg_used(usedinput, width); } @@ -517,7 +525,7 @@ if(precision >= max_param) max_param = precision; - in[precision].type = FORMAT_PRECISION; + in[precision].type = MTYPE_PRECISION; mark_arg_used(usedinput, precision); } @@ -572,42 +580,42 @@ /* based on the type, read the correct argument */ switch(iptr->type) { - case FORMAT_STRING: + case MTYPE_STRING: iptr->val.str = va_arg(arglist, const char *); break; - case FORMAT_INTPTR: - case FORMAT_PTR: + case MTYPE_INTPTR: + case MTYPE_PTR: iptr->val.ptr = va_arg(arglist, void *); break; - case FORMAT_LONGLONGU: - iptr->val.numu = (mp_uintmax_t)va_arg(arglist, mp_uintmax_t); + case MTYPE_LONGLONGU: + iptr->val.numu = va_arg(arglist, uint64_t); break; - case FORMAT_LONGLONG: - iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t); + case MTYPE_LONGLONG: + iptr->val.nums = va_arg(arglist, int64_t); break; - case FORMAT_LONGU: - iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned long); + case MTYPE_LONGU: + iptr->val.numu = va_arg(arglist, unsigned long); break; - case FORMAT_LONG: - iptr->val.nums = (mp_intmax_t)va_arg(arglist, long); + case MTYPE_LONG: + iptr->val.nums = va_arg(arglist, long); break; - case FORMAT_INTU: - iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned int); + case MTYPE_INTU: + iptr->val.numu = va_arg(arglist, unsigned int); break; - case FORMAT_INT: - case FORMAT_WIDTH: - case FORMAT_PRECISION: - iptr->val.nums = (mp_intmax_t)va_arg(arglist, int); + case MTYPE_INT: + case MTYPE_WIDTH: + case MTYPE_PRECISION: + iptr->val.nums = va_arg(arglist, int); break; - case FORMAT_DOUBLE: + case MTYPE_DOUBLE: iptr->val.dnum = va_arg(arglist, double); break; @@ -623,8 +631,8 @@ } struct mproperty { - int width; /* Width of a field. */ - int prec; /* Precision of a field. */ + int width; /* Width of a field. */ + int prec; /* Precision of a field. */ unsigned int flags; }; @@ -634,9 +642,9 @@ double dnum, char *work, int *donep) { - char formatbuf[32] = "%"; - char *fptr = &formatbuf[1]; - size_t left = sizeof(formatbuf) - strlen(formatbuf); + char fmt[32] = "%"; + char *fptr = &fmt[1]; + size_t left = sizeof(fmt) - strlen(fmt); int flags = p->flags; int width = p->width; int prec = p->prec; @@ -696,26 +704,22 @@ *fptr = 0; /* and a final null-termination */ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#endif /* NOTE NOTE NOTE!! Not all sprintf implementations return number of output characters */ -#ifdef HAVE_SNPRINTF +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef _WIN32 + curlx_win32_snprintf(work, BUFFSIZE, fmt, dnum); +#else + /* !checksrc! disable BANNEDFUNC 1 */ /* !checksrc! disable LONGLINE */ /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ - (snprintf)(work, BUFFSIZE, formatbuf, dnum); -#ifdef _WIN32 - /* Old versions of the Windows CRT do not terminate the snprintf output - buffer if it reaches the max size so we do that here. */ - work[BUFFSIZE - 1] = 0; + snprintf(work, BUFFSIZE, fmt, dnum); #endif -#else - (sprintf)(work, formatbuf, dnum); -#endif -#ifdef __clang__ -#pragma clang diagnostic pop +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop #endif DEBUGASSERT(strlen(work) < BUFFSIZE); while(*work) { @@ -729,8 +733,8 @@ static bool out_number(void *userp, int (*stream)(unsigned char, void *), struct mproperty *p, - mp_uintmax_t num, - mp_intmax_t nums, + uint64_t num, + int64_t nums, char *work, int *donep) { const unsigned char *digits = Curl_ldigits; @@ -748,7 +752,7 @@ char *w; if(flags & FLAGS_CHAR) { - /* Character. */ + /* Character. */ if(!(flags & FLAGS_LEFT)) while(--width > 0) OUTCHAR(' '); @@ -772,23 +776,23 @@ ; else { - /* Decimal integer. */ + /* Decimal integer. */ is_neg = (nums < 0); if(is_neg) { /* signed_num might fail to hold absolute negative minimum by 1 */ - mp_intmax_t signed_num; /* Used to convert negative in positive. */ - signed_num = nums + (mp_intmax_t)1; + int64_t signed_num; /* Used to convert negative in positive. */ + signed_num = nums + (int64_t)1; signed_num = -signed_num; - num = (mp_uintmax_t)signed_num; - num += (mp_uintmax_t)1; + num = (uint64_t)signed_num; + num += (uint64_t)1; } } - /* Supply a default precision if none was given. */ + /* Supply a default precision if none was given. */ if(prec == -1) prec = 1; - /* Put the number in WORK. */ + /* Put the number in WORK. */ w = workend; DEBUGASSERT(base <= 16); switch(base) { @@ -848,7 +852,7 @@ while(width-- > 0) OUTCHAR('0'); - /* Write the number. */ + /* Write the number. */ while(++w <= workend) { OUTCHAR(*w); } @@ -874,7 +878,7 @@ size_t len; if(!str) { - /* Write null string if there is space. */ + /* Write null string if there is space. */ if(prec == -1 || prec >= (int)sizeof(nilstr) - 1) { str = nilstr; len = sizeof(nilstr) - 1; @@ -921,17 +925,17 @@ char *work, int *donep) { - /* Generic pointer. */ + /* Generic pointer. */ if(ptr) { size_t num = (size_t)ptr; - /* If the pointer is not NULL, write it as a %#x spec. */ + /* If the pointer is not NULL, write it as a %#x spec. */ p->flags |= FLAGS_HEX | FLAGS_ALT; if(out_number(userp, stream, p, num, 0, work, donep)) return TRUE; } else { - /* Write "(nil)" for a nil pointer. */ + /* Write "(nil)" for a nil pointer. */ const char *point; int width = p->width; int flags = p->flags; @@ -965,14 +969,14 @@ * All output is sent to the 'stream()' callback, one byte at a time. */ -static int formatf(void *userp, /* untouched by format(), just sent to the +static int formatf(void *userp, /* untouched by format(), sent to the stream() function in the second argument */ /* function pointer called for each output character */ int (*stream)(unsigned char, void *), const char *format, /* %-formatted string */ va_list ap_save) /* list of parameters */ { - int done = 0; /* number of characters written */ + int done = 0; /* number of characters written */ int i; int ocount = 0; /* number of output segments */ int icount = 0; /* number of input arguments */ @@ -999,7 +1003,7 @@ done++; } if(optr->flags & FLAGS_SUBSTR) - /* this is just a substring */ + /* this is a substring */ continue; } @@ -1036,48 +1040,46 @@ p.prec = -1; switch(iptr->type) { - case FORMAT_INTU: - case FORMAT_LONGU: - case FORMAT_LONGLONGU: + case MTYPE_INTU: + case MTYPE_LONGU: + case MTYPE_LONGLONGU: p.flags |= FLAGS_UNSIGNED; if(out_number(userp, stream, &p, iptr->val.numu, 0, work, &done)) return done; break; - case FORMAT_INT: - case FORMAT_LONG: - case FORMAT_LONGLONG: + case MTYPE_INT: + case MTYPE_LONG: + case MTYPE_LONGLONG: if(out_number(userp, stream, &p, iptr->val.numu, iptr->val.nums, work, &done)) return done; break; - case FORMAT_STRING: + case MTYPE_STRING: if(out_string(userp, stream, &p, iptr->val.str, &done)) return done; break; - case FORMAT_PTR: + case MTYPE_PTR: if(out_pointer(userp, stream, &p, iptr->val.ptr, work, &done)) return done; break; - case FORMAT_DOUBLE: + case MTYPE_DOUBLE: if(out_double(userp, stream, &p, iptr->val.dnum, work, &done)) return done; break; - case FORMAT_INTPTR: - /* Answer the count of characters written. */ -#ifdef HAVE_LONG_LONG_TYPE + case MTYPE_INTPTR: + /* Answer the count of characters written. */ if(p.flags & FLAGS_LONGLONG) - *(LONG_LONG_TYPE *)iptr->val.ptr = (LONG_LONG_TYPE)done; + *(int64_t *)iptr->val.ptr = (int64_t)done; else -#endif if(p.flags & FLAGS_LONG) *(long *)iptr->val.ptr = (long)done; else if(!(p.flags & FLAGS_SHORT)) - *(int *)iptr->val.ptr = (int)done; + *(int *)iptr->val.ptr = done; else *(short *)iptr->val.ptr = (short)done; break; @@ -1103,7 +1105,7 @@ } int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, - va_list ap_save) + va_list args) { int retcode; struct nsprintf info; @@ -1112,7 +1114,7 @@ info.length = 0; info.max = maxlength; - retcode = formatf(&info, addbyter, format, ap_save); + retcode = formatf(&info, addbyter, format, args); if(info.max) { /* we terminate this with a zero byte */ if(info.max == info.length) { @@ -1130,10 +1132,10 @@ int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = curl_mvsnprintf(buffer, maxlength, format, args); + va_end(args); return retcode; } @@ -1150,13 +1152,13 @@ } /* appends the formatted string, returns MERR error code */ -int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) +int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args) { struct asprintf info; info.b = dyn; info.merr = MERR_OK; - (void)formatf(&info, alloc_addbyter, format, ap_save); + (void)formatf(&info, alloc_addbyter, format, args); if(info.merr) { curlx_dyn_free(info.b); return info.merr; @@ -1164,7 +1166,7 @@ return 0; } -char *curl_mvaprintf(const char *format, va_list ap_save) +char *curl_mvaprintf(const char *format, va_list args) { struct asprintf info; struct dynbuf dyn; @@ -1172,7 +1174,7 @@ curlx_dyn_init(info.b, DYN_APRINTF); info.merr = MERR_OK; - (void)formatf(&info, alloc_addbyter, format, ap_save); + (void)formatf(&info, alloc_addbyter, format, args); if(info.merr) { curlx_dyn_free(info.b); return NULL; @@ -1184,11 +1186,11 @@ char *curl_maprintf(const char *format, ...) { - va_list ap_save; + va_list args; char *s; - va_start(ap_save, format); - s = curl_mvaprintf(format, ap_save); - va_end(ap_save); + va_start(args, format); + s = curl_mvaprintf(format, args); + va_end(args); return s; } @@ -1202,11 +1204,11 @@ int curl_msprintf(char *buffer, const char *format, ...) { - va_list ap_save; /* argument pointer */ + va_list args; /* argument pointer */ int retcode; - va_start(ap_save, format); - retcode = formatf(&buffer, storebuffer, format, ap_save); - va_end(ap_save); + va_start(args, format); + retcode = formatf(&buffer, storebuffer, format, args); + va_end(args); *buffer = 0; /* we terminate this with a zero byte */ return retcode; } @@ -1222,36 +1224,36 @@ int curl_mprintf(const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = formatf(stdout, fputc_wrapper, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = formatf(stdout, fputc_wrapper, format, args); + va_end(args); return retcode; } -int curl_mfprintf(FILE *whereto, const char *format, ...) +int curl_mfprintf(FILE *fd, const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = formatf(whereto, fputc_wrapper, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = formatf(fd, fputc_wrapper, format, args); + va_end(args); return retcode; } -int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) +int curl_mvsprintf(char *buffer, const char *format, va_list args) { - int retcode = formatf(&buffer, storebuffer, format, ap_save); + int retcode = formatf(&buffer, storebuffer, format, args); *buffer = 0; /* we terminate this with a zero byte */ return retcode; } -int curl_mvprintf(const char *format, va_list ap_save) +int curl_mvprintf(const char *format, va_list args) { - return formatf(stdout, fputc_wrapper, format, ap_save); + return formatf(stdout, fputc_wrapper, format, args); } -int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) +int curl_mvfprintf(FILE *fd, const char *format, va_list args) { - return formatf(whereto, fputc_wrapper, format, ap_save); + return formatf(fd, fputc_wrapper, format, args); }
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index 2ba1ca7..84fd272 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c
@@ -23,10 +23,10 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_MQTT -#include "urldata.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" @@ -36,6 +36,8 @@ #include "url.h" #include "escape.h" #include "rand.h" +#include "cfilters.h" +#include "connect.h" /* first byte is command. second byte is for flags. */ @@ -89,48 +91,6 @@ BIT(pingsent); /* 1 while we wait for ping response */ }; -/* - * Forward declarations. - */ - -static CURLcode mqtt_do(struct Curl_easy *data, bool *done); -static CURLcode mqtt_done(struct Curl_easy *data, - CURLcode status, bool premature); -static CURLcode mqtt_doing(struct Curl_easy *data, bool *done); -static CURLcode mqtt_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode mqtt_setup_conn(struct Curl_easy *data, - struct connectdata *conn); - -/* - * MQTT protocol handler. - */ - -const struct Curl_handler Curl_handler_mqtt = { - "mqtt", /* scheme */ - mqtt_setup_conn, /* setup_connection */ - mqtt_do, /* do_it */ - mqtt_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - mqtt_doing, /* doing */ - ZERO_NULL, /* proto_pollset */ - mqtt_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_MQTT, /* defport */ - CURLPROTO_MQTT, /* protocol */ - CURLPROTO_MQTT, /* family */ - PROTOPT_NONE /* flags */ -}; - static void mqtt_easy_dtor(void *key, size_t klen, void *entry) { struct MQTT *mq = entry; @@ -391,8 +351,8 @@ end: if(packet) curlx_free(packet); - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); return result; } @@ -419,6 +379,8 @@ result = Curl_xfer_recv(data, (char *)readbuf, nbytes - rlen, &nread); if(result) return result; + if(!nread) /* EOF */ + return CURLE_RECV_ERROR; if(curlx_dyn_addn(&mq->recvbuf, readbuf, nread)) return CURLE_OUT_OF_MEMORY; rlen = curlx_dyn_len(&mq->recvbuf); @@ -443,15 +405,20 @@ { struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY); CURLcode result; - char *ptr; + const char *ptr; DEBUGASSERT(mq); if(!mq) return CURLE_FAILED_INIT; + if(mq->remaining_length != 2) { + failf(data, "CONNACK expected Remaining Length 2, got %zu", + mq->remaining_length); + return CURLE_WEIRD_SERVER_REPLY; + } result = mqtt_recv_atleast(data, MQTT_CONNACK_LEN); if(result) - goto fail; + return result; /* verify CONNACK */ DEBUGASSERT(curlx_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN); @@ -462,18 +429,16 @@ failf(data, "Expected %02x%02x but got %02x%02x", 0x00, 0x00, ptr[0], ptr[1]); curlx_dyn_reset(&mq->recvbuf); - result = CURLE_WEIRD_SERVER_REPLY; - goto fail; + return CURLE_WEIRD_SERVER_REPLY; } mqtt_recv_consume(data, MQTT_CONNACK_LEN); -fail: - return result; + return CURLE_OK; } static CURLcode mqtt_get_topic(struct Curl_easy *data, char **topic, size_t *topiclen) { - char *path = data->state.up.path; + const char *path = data->state.up.path; CURLcode result = CURLE_URL_MALFORMAT; if(strlen(path) > 1) { result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA); @@ -546,11 +511,17 @@ struct connectdata *conn = data->conn; struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN); CURLcode result; - char *ptr; + const char *ptr; if(!mqtt || !mq) return CURLE_FAILED_INIT; + if(mq->remaining_length != 3) { + failf(data, "SUBACK expected Remaining Length 3, got %zu", + mq->remaining_length); + return CURLE_WEIRD_SERVER_REPLY; + } + result = mqtt_recv_atleast(data, MQTT_SUBACK_LEN); if(result) goto fail; @@ -604,7 +575,7 @@ remaininglength = payloadlen + 2 + topiclen; encodelen = mqtt_encode_len(encodedbytes, remaininglength); - if(MAX_MQTT_MESSAGE_SIZE - remaininglength - 1 < encodelen) { + if(remaininglength > (MAX_MQTT_MESSAGE_SIZE - encodelen - 1)) { result = CURLE_TOO_LARGE; goto fail; } @@ -635,7 +606,8 @@ } /* return 0 on success, non-zero on error */ -static int mqtt_decode_len(size_t *lenp, unsigned char *buf, size_t buflen) +static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, + size_t buflen) { size_t len = 0; size_t mult = 1; @@ -654,7 +626,7 @@ return 0; } -#ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) static const char *statenames[] = { "MQTT_FIRST", "MQTT_REMAINING_LENGTH", @@ -755,7 +727,7 @@ rest = sizeof(buffer); result = Curl_xfer_recv(data, buffer, rest, &nread); if(result) { - if(CURLE_AGAIN == result) { + if(result == CURLE_AGAIN) { infof(data, "EEEE AAAAGAIN"); } goto end; @@ -976,4 +948,65 @@ return result; } +#ifdef USE_SSL + +static CURLcode mqtts_connecting(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + CURLcode result; + + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); + if(result) + connclose(conn, "Failed TLS connection"); + return result; +} + +/* + * MQTTS protocol. + */ +const struct Curl_protocol Curl_protocol_mqtts = { + mqtt_setup_conn, /* setup_connection */ + mqtt_do, /* do_it */ + mqtt_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + mqtts_connecting, /* connecting */ + mqtt_doing, /* doing */ + ZERO_NULL, /* proto_pollset */ + mqtt_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif + +/* + * MQTT protocol. + */ +const struct Curl_protocol Curl_protocol_mqtt = { + mqtt_setup_conn, /* setup_connection */ + mqtt_do, /* do_it */ + mqtt_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + mqtt_doing, /* doing */ + ZERO_NULL, /* proto_pollset */ + mqtt_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_MQTT */
diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h index 3e45815..3d95293 100644 --- a/Utilities/cmcurl/lib/mqtt.h +++ b/Utilities/cmcurl/lib/mqtt.h
@@ -24,7 +24,10 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_MQTT -extern const struct Curl_handler Curl_handler_mqtt; +extern const struct Curl_protocol Curl_protocol_mqtt; +#ifdef USE_SSL +extern const struct Curl_protocol Curl_protocol_mqtts; +#endif #endif #endif /* HEADER_CURL_MQTT_H */
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 7cfd053..7520253 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c
@@ -74,7 +74,6 @@ #define CURL_MULTI_HANDLE 0x000bab1e - #ifdef DEBUGBUILD /* On a debug build, we want to fail hard on multi handles that * are not NULL, but no longer have the MAGIC touch. This gives @@ -140,7 +139,6 @@ NULL, /* PENDING */ NULL, /* SETUP */ Curl_init_CONNECT, /* CONNECT */ - NULL, /* RESOLVING */ NULL, /* CONNECTING */ NULL, /* PROTOCONNECT */ NULL, /* PROTOCONNECTING */ @@ -160,6 +158,7 @@ return; #ifdef DEBUGBUILD + NOVERBOSE((void)lineno); CURL_TRC_M(data, "-> [%s] (line %d)", CURL_MSTATE_NAME(state), lineno); #else CURL_TRC_M(data, "-> [%s]", CURL_MSTATE_NAME(state)); @@ -206,7 +205,7 @@ static void ph_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -253,6 +252,10 @@ multi->multiplexing = TRUE; multi->max_concurrent_streams = 100; multi->last_timeout_ms = -1; +#ifdef ENABLE_WAKEUP + multi->wakeup_pair[0] = CURL_SOCKET_BAD; + multi->wakeup_pair[1] = CURL_SOCKET_BAD; +#endif if(Curl_mntfy_resize(multi) || Curl_uint32_bset_resize(&multi->process, xfer_table_size) || @@ -293,10 +296,25 @@ multi->wsa_event = WSACreateEvent(); if(multi->wsa_event == WSA_INVALID_EVENT) goto error; -#elif defined(ENABLE_WAKEUP) - if(wakeup_create(multi->wakeup_pair, TRUE) < 0) { - multi->wakeup_pair[0] = CURL_SOCKET_BAD; - multi->wakeup_pair[1] = CURL_SOCKET_BAD; +#endif +#ifdef ENABLE_WAKEUP + /* When enabled, rely on this to work. We ignore this in previous + * versions, but that seems an unnecessary complication. */ + if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) + goto error; +#endif + + if(Curl_probeipv6(multi)) + goto error; + +#ifdef USE_RESOLV_THREADED + if(xfer_table_size < CURL_XFER_TABLE_SIZE) { /* easy multi */ + if(Curl_async_thrdd_multi_init(multi, 0, 2, 10)) + goto error; + } + else { /* real multi handle */ + if(Curl_async_thrdd_multi_init(multi, 0, 20, 2000)) + goto error; } #endif @@ -304,6 +322,9 @@ error: +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_destroy(multi, TRUE); +#endif Curl_multi_ev_cleanup(multi); Curl_hash_destroy(&multi->proto_hash); Curl_dnscache_destroy(&multi->dnscache); @@ -313,6 +334,7 @@ Curl_ssl_scache_destroy(multi->ssl_scache); #endif if(multi->admin) { + Curl_multi_ev_xfer_done(multi, multi->admin); multi->admin->multi = NULL; Curl_close(&multi->admin); } @@ -323,6 +345,9 @@ Curl_uint32_bset_destroy(&multi->pending); Curl_uint32_bset_destroy(&multi->msgsent); Curl_uint32_tbl_destroy(&multi->xfers); +#ifdef ENABLE_WAKEUP + Curl_wakeup_destroy(multi->wakeup_pair); +#endif curlx_free(multi); return NULL; @@ -337,7 +362,7 @@ CURL_TLS_SESSION_SIZE); } -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) { if(!multi->warned) { @@ -351,6 +376,22 @@ #define multi_warn_debug(x, y) Curl_nop_stmt #endif +bool Curl_is_connecting(struct Curl_easy *data) +{ + return data->mstate < MSTATE_DO; +} + +static CURLMcode multi_assess_wakeup(struct Curl_multi *multi) +{ +#ifdef ENABLE_WAKEUP + if(multi->socket_cb) + return Curl_multi_ev_assess_xfer(multi, multi->admin); +#else + (void)multi; +#endif + return CURLM_OK; +} + static CURLMcode multi_xfers_add(struct Curl_multi *multi, struct Curl_easy *data) { @@ -363,8 +404,8 @@ if(capacity < max_capacity) { /* We want `multi->xfers` to have "sufficient" free rows, so that we do - * have to reuse the `mid` from a just removed easy right away. - * Since uint_tbl and uint_bset are quite memory efficient, + * have to reuse the `mid` from a removed easy right away. + * Since uint_tbl and uint_bset are memory efficient, * regard less than 25% free as insufficient. * (for low capacities, e.g. multi_easy, 4 or less). */ uint32_t used = Curl_uint32_tbl_count(&multi->xfers); @@ -410,11 +451,11 @@ return CURLM_OK; } -CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) +CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) { CURLMcode mresult; struct Curl_multi *multi = m; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -517,6 +558,12 @@ data->set.server_response_timeout; multi->admin->set.no_signal = data->set.no_signal; + mresult = multi_assess_wakeup(multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; + } + CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u", data->mid, Curl_multi_xfers_running(multi), Curl_uint32_tbl_count(&multi->xfers)); @@ -569,8 +616,8 @@ /* Unless this connection is for a "connect-only" transfer, it * needs to be closed if the protocol handler does not support reuse. */ - if(!data->set.connect_only && conn->handler && - !(conn->handler->flags & PROTOPT_CONN_REUSE)) + if(!data->set.connect_only && conn->scheme && + !(conn->scheme->flags & PROTOPT_CONN_REUSE)) return TRUE; /* if premature is TRUE, it means this connection was said to be DONE before @@ -589,22 +636,6 @@ void *userdata) { struct multi_done_ctx *mdctx = userdata; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *host = -#ifndef CURL_DISABLE_PROXY - conn->bits.socksproxy ? - conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : -#endif - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname; - int port = -#ifndef CURL_DISABLE_PROXY - conn->bits.httpproxy ? conn->http_proxy.port : -#endif - conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; -#endif Curl_detach_connection(data); @@ -616,41 +647,34 @@ return; } - data->state.done = TRUE; /* called just now! */ + data->state.done = TRUE; /* called now! */ data->state.recent_conn_id = conn->connection_id; - Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */ - Curl_resolv_unlink(data, &data->state.dns[1]); Curl_dnscache_prune(data); - if(multi_conn_should_close(conn, data, mdctx->premature)) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%d, " + if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) { + CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s, " "forbid=%d, close=%d, premature=%d, conn_multiplex=%d", - conn->connection_id, host, port, data->set.reuse_forbid, - conn->bits.close, mdctx->premature, + conn->connection_id, conn->destination, + data->set.reuse_forbid, conn->bits.close, mdctx->premature, Curl_conn_is_multiplex(conn, FIRSTSOCKET)); -#endif connclose(conn, "disconnecting"); - Curl_conn_terminate(data, conn, mdctx->premature); + Curl_conn_terminate(data, conn, (bool)mdctx->premature); } else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s:%d was shutdown" - " by server, not reusing", conn->connection_id, host, port); -#endif + CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown" + " by server, not reusing", conn->connection_id, + conn->destination); connclose(conn, "server shutdown"); - Curl_conn_terminate(data, conn, mdctx->premature); + Curl_conn_terminate(data, conn, (bool)mdctx->premature); } else { /* the connection is no longer in use by any transfer */ if(Curl_cpool_conn_now_idle(data, conn)) { /* connection kept in the cpool */ data->state.lastconnect_id = conn->connection_id; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - infof(data, "Connection #%" FMT_OFF_T " to host %s:%d left intact", - conn->connection_id, host, port); -#endif + infof(data, "Connection #%" FMT_OFF_T " to host %s left intact", + conn->connection_id, conn->destination); } else { /* connection was removed from the cpool and destroyed. */ @@ -666,9 +690,6 @@ { CURLcode result; struct connectdata *conn = data->conn; - struct multi_done_ctx mdctx; - - memset(&mdctx, 0, sizeof(mdctx)); CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d", (int)status, (int)premature, data->state.done); @@ -678,20 +699,20 @@ return CURLE_OK; /* Shut down any ongoing async resolver operation. */ - Curl_async_shutdown(data); + Curl_resolv_shutdown_all(data); /* Cleanup possible redirect junk */ - Curl_safefree(data->req.newurl); - Curl_safefree(data->req.location); + curlx_safefree(data->req.newurl); + curlx_safefree(data->req.location); switch(status) { case CURLE_ABORTED_BY_CALLBACK: case CURLE_READ_ERROR: case CURLE_WRITE_ERROR: - /* When we are aborted due to a callback return code it basically have to - be counted as premature as there is trouble ahead if we do not. We have - many callbacks and protocols work differently, we could potentially do - this more fine-grained in the future. */ + /* When we are aborted due to a callback return code it has to be counted + as premature as there is trouble ahead if we do not. We have many + callbacks and protocols work differently, we could potentially do this + more fine-grained in the future. */ premature = TRUE; FALLTHROUGH(); default: @@ -699,34 +720,43 @@ } /* this calls the protocol-specific function pointer previously set */ - if(conn->handler->done && (data->mstate >= MSTATE_PROTOCONNECT)) - result = conn->handler->done(data, status, premature); + if(conn && conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT)) + result = conn->scheme->run->done(data, status, premature); else result = status; - if(CURLE_ABORTED_BY_CALLBACK != result) { - /* avoid this if we already aborted by callback to avoid this calling - another callback */ + if(data->mstate > MSTATE_CONNECTING && + (result != CURLE_ABORTED_BY_CALLBACK)) { + /* avoid this if + * - the transfer has not connected + * - we already aborted by callback to avoid this calling another callback + */ int rc = Curl_pgrsDone(data); if(!result && rc) result = CURLE_ABORTED_BY_CALLBACK; } /* Make sure that transfer client writes are really done now. */ - result = Curl_1st_err(result, Curl_xfer_write_done(data, premature)); + result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature)); /* Inform connection filters that this transfer is done */ - Curl_conn_ev_data_done(data, premature); + if(conn) + Curl_conn_ev_data_done(data, premature); process_pending_handles(data->multi); /* connection / multiplex */ if(!result) result = Curl_req_done(&data->req, data, premature); - /* Under the potential connection pool's share lock, decide what to - * do with the transfer's connection. */ - mdctx.premature = premature; - Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); + if(conn) { + /* Under the potential connection pool's share lock, decide what to + * do with the transfer's connection. */ + struct multi_done_ctx mdctx; + + memset(&mdctx, 0, sizeof(mdctx)); + mdctx.premature = premature; + Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); + } /* flush the netrc cache */ Curl_netrc_cleanup(&data->state.netrc); @@ -739,18 +769,17 @@ { (void)userdata; (void)data; - if(conn->connect_only) + if(conn->bits.connect_only) connclose(conn, "Removing connect-only easy handle"); } -CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) +CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) { struct Curl_multi *multi = m; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; bool premature; struct Curl_llist_node *e; CURLMcode mresult; - bool removed_timer = FALSE; uint32_t mid; /* First, make some basic checks that the CURLM handle is a good handle */ @@ -797,7 +826,7 @@ /* multi_done() clears the association between the easy handle and the connection. - Note that this ignores the return code simply because there is + Note that this ignores the return code because there is nothing really useful to do with it anyway! */ (void)multi_done(data, data->result, premature); } @@ -805,7 +834,7 @@ /* The timer must be shut down before data->multi is set to NULL, else the timenode will remain in the splay tree after curl_easy_cleanup is called. Do it after multi_done() in case that sets another time! */ - removed_timer = Curl_expire_clear(data); + Curl_expire_clear(data); /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) @@ -878,10 +907,14 @@ We do not touch the easy handle here! */ process_pending_handles(multi); - if(removed_timer) { - mresult = Curl_update_timer(multi); - if(mresult) - return mresult; + mresult = Curl_update_timer(multi); + if(mresult) + return mresult; + + mresult = multi_assess_wakeup(multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; } CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u", @@ -936,8 +969,8 @@ conn->attached_multi = data->multi; DEBUGASSERT(conn->attached_multi == data->multi); - if(conn->handler && conn->handler->attach) - conn->handler->attach(data, conn); + if(conn->scheme && conn->scheme->run->attach) + conn->scheme->run->attach(data, conn); } /* adjust pollset for rate limits/pauses */ @@ -1015,8 +1048,8 @@ struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(conn->handler->proto_pollset) - result = conn->handler->proto_pollset(data, ps); + if(conn->scheme->run->proto_pollset) + result = conn->scheme->run->proto_pollset(data, ps); else { curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; if(sockfd != CURL_SOCKET_BAD) { @@ -1037,8 +1070,8 @@ struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(conn->handler->doing_pollset) - result = conn->handler->doing_pollset(data, ps); + if(conn->scheme->run->doing_pollset) + result = conn->scheme->run->doing_pollset(data, ps); else if(CONN_SOCK_IDX_VALID(conn->send_idx)) { /* Default is that we want to send something to the server */ result = Curl_pollset_add_out(data, ps, conn->sock[conn->send_idx]); @@ -1056,8 +1089,8 @@ struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(conn->handler->domore_pollset) - result = conn->handler->domore_pollset(data, ps); + if(conn->scheme->run->domore_pollset) + result = conn->scheme->run->domore_pollset(data, ps); else if(CONN_SOCK_IDX_VALID(conn->send_idx)) { /* Default is that we want to send something to the server */ result = Curl_pollset_add_out(data, ps, conn->sock[conn->send_idx]); @@ -1075,10 +1108,10 @@ struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(conn->handler->perform_pollset) - result = conn->handler->perform_pollset(data, ps); + if(conn->scheme->run->perform_pollset) + result = conn->scheme->run->perform_pollset(data, ps); else { - /* Default is to obey the data->req.keepon flags for send/recv */ + /* Default is to obey the request flags for send/recv */ if(Curl_req_want_recv(data) && CONN_SOCK_IDX_VALID(conn->recv_idx)) { result = Curl_pollset_add_in(data, ps, conn->sock[conn->recv_idx]); } @@ -1099,78 +1132,80 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; + Curl_pollset_reset(ps); +#ifdef ENABLE_WAKEUP + /* The admin handle always listens on the wakeup socket when there + * are transfers alive. */ + if(data->multi && (data == data->multi->admin) && + data->multi->xfers_alive) { + result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); + } +#endif /* If the transfer has no connection, this is fine. Happens when called via curl_multi_remove_handle() => Curl_multi_ev_assess() => Curl_multi_pollset(). */ - Curl_pollset_reset(ps); - if(!data->conn) - return CURLM_OK; + if(!result && data->conn) { + switch(data->mstate) { + case MSTATE_INIT: + case MSTATE_PENDING: + case MSTATE_SETUP: + case MSTATE_CONNECT: + /* nothing to poll for yet */ + break; - switch(data->mstate) { - case MSTATE_INIT: - case MSTATE_PENDING: - case MSTATE_SETUP: - case MSTATE_CONNECT: - /* nothing to poll for yet */ - break; + case MSTATE_CONNECTING: + if(data->conn && !data->conn->bits.dns_resolved) + result = Curl_resolv_pollset(data, ps); + if(!result) + result = mstate_connecting_pollset(data, ps); + break; - case MSTATE_RESOLVING: - result = Curl_resolv_pollset(data, ps); - break; + case MSTATE_PROTOCONNECT: + case MSTATE_PROTOCONNECTING: + result = mstate_protocol_pollset(data, ps); + break; - case MSTATE_CONNECTING: - result = mstate_connecting_pollset(data, ps); - break; + case MSTATE_DO: + case MSTATE_DOING: + result = mstate_do_pollset(data, ps); + break; - case MSTATE_PROTOCONNECT: - case MSTATE_PROTOCONNECTING: - result = mstate_protocol_pollset(data, ps); - break; + case MSTATE_DOING_MORE: + result = mstate_domore_pollset(data, ps); + break; - case MSTATE_DO: - case MSTATE_DOING: - result = mstate_do_pollset(data, ps); - break; + case MSTATE_DID: /* same as PERFORMING in regard to polling */ + case MSTATE_PERFORMING: + result = mstate_perform_pollset(data, ps); + break; - case MSTATE_DOING_MORE: - result = mstate_domore_pollset(data, ps); - break; + case MSTATE_RATELIMITING: + /* we need to let time pass, ignore socket(s) */ + break; - case MSTATE_DID: /* same as PERFORMING in regard to polling */ - case MSTATE_PERFORMING: - result = mstate_perform_pollset(data, ps); - break; + case MSTATE_DONE: + case MSTATE_COMPLETED: + case MSTATE_MSGSENT: + /* nothing more to poll for */ + break; - case MSTATE_RATELIMITING: - /* we need to let time pass, ignore socket(s) */ - break; - - case MSTATE_DONE: - case MSTATE_COMPLETED: - case MSTATE_MSGSENT: - /* nothing more to poll for */ - break; - - default: - failf(data, "multi_getsock: unexpected multi state %d", data->mstate); - DEBUGASSERT(0); - break; + default: + failf(data, "multi_getsock: unexpected multi state %d", data->mstate); + DEBUGASSERT(0); + break; + } } if(result) { if(result == CURLE_OUT_OF_MEMORY) - mresult = CURLM_OUT_OF_MEMORY; - else { - failf(data, "error determining pollset: %d", result); - mresult = CURLM_INTERNAL_ERROR; - } - goto out; + return CURLM_OUT_OF_MEMORY; + failf(data, "error determining pollset: %d", result); + return CURLM_INTERNAL_ERROR; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(CURL_TRC_M_is_verbose(data)) { size_t timeout_count = Curl_llist_count(&data->state.timeoutlist); switch(ps->n) { @@ -1206,8 +1241,7 @@ } #endif -out: - return mresult; + return CURLM_OK; } CURLMcode curl_multi_fdset(CURLM *m, @@ -1245,17 +1279,10 @@ if(!FDSET_SOCK(ps.sockets[i])) /* pretend it does not exist */ continue; -#ifdef __DJGPP__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warith-conversion" -#endif if(ps.actions[i] & CURL_POLL_IN) FD_SET(ps.sockets[i], read_fd_set); if(ps.actions[i] & CURL_POLL_OUT) FD_SET(ps.sockets[i], write_fd_set); -#ifdef __DJGPP__ -#pragma GCC diagnostic pop -#endif if((int)ps.sockets[i] > this_max_fd) this_max_fd = (int)ps.sockets[i]; } @@ -1331,9 +1358,169 @@ int t; int l = (int)sizeof(t); if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM) - send(s, NULL, 0, 0); + swrite(s, NULL, 0); } -#endif + +static CURLMcode multi_winsock_select(struct Curl_multi *multi, + struct curl_pollfds *cpfds, + unsigned int curl_nfds, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + bool extrawait, + int *pnevents) +{ + CURLMcode mresult = CURLM_OK; + WSANETWORKEVENTS wsa_events; + int nevents = 0; + size_t i; + + DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); + + /* Set the WSA events based on the collected pollds */ + for(i = 0; i < cpfds->n; i++) { + long mask = 0; + if(cpfds->pfds[i].events & POLLIN) + mask |= FD_READ | FD_ACCEPT | FD_CLOSE; + if(cpfds->pfds[i].events & POLLPRI) + mask |= FD_OOB; + if(cpfds->pfds[i].events & POLLOUT) { + mask |= FD_WRITE | FD_CONNECT | FD_CLOSE; + reset_socket_fdwrite(cpfds->pfds[i].fd); + } + if(mask) { + if(WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, mask) != 0) { + mresult = CURLM_OUT_OF_MEMORY; + goto out; + } + } + } + + if(cpfds->n || extrawait) { + int pollrc = 0; + + if(cpfds->n) { /* pre-check with Winsock */ + pollrc = Curl_poll(cpfds->pfds, cpfds->n, 0); + if(pollrc < 0) { + mresult = CURLM_UNRECOVERABLE_POLL; + goto out; + } + nevents = pollrc; + } + + if(!nevents) { + /* now wait... if not ready during the pre-check (pollrc == 0) */ + WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, + FALSE); + } + + /* With Winsock, we have to run the following section unconditionally + to call WSAEventSelect(fd, event, 0) on all the sockets */ + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ + for(i = 0; i < extra_nfds; i++) { + unsigned short mask = 0; + curl_socket_t s = extra_fds[i].fd; + + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { + if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) + mask |= CURL_WAIT_POLLIN; + if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE)) + mask |= CURL_WAIT_POLLOUT; + if(wsa_events.lNetworkEvents & FD_OOB) + mask |= CURL_WAIT_POLLPRI; + if(!pollrc && wsa_events.lNetworkEvents) + nevents++; + } + WSAEventSelect(s, multi->wsa_event, 0); + if(!pollrc) { + extra_fds[i].revents = (short)mask; + continue; + } + else { + unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents; + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; + extra_fds[i].revents = (short)mask; + } + } + + /* Count up all our own sockets that had activity, + and remove them from the event. */ + for(i = 0; i < curl_nfds; ++i) { + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(cpfds->pfds[i].fd, NULL, &wsa_events) == 0) { + if(!pollrc && wsa_events.lNetworkEvents) + nevents++; + } + WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, 0); + } + WSAResetEvent(multi->wsa_event); + } + +out: + *pnevents = nevents; + return mresult; +} + +#else /* USE_WINSOCK */ + +static CURLMcode multi_posix_poll(struct Curl_multi *multi, + struct curl_pollfds *cpfds, + unsigned int curl_nfds, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + bool extrawait, + int *pnevents) +{ + CURLMcode mresult = CURLM_OK; + int nevents = 0; + size_t i; + + (void)multi; + if(cpfds->n) { + int pollrc = Curl_poll(cpfds->pfds, cpfds->n, timeout_ms); /* wait... */ + if(pollrc < 0) { + mresult = CURLM_UNRECOVERABLE_POLL; + goto out; + } + nevents = pollrc; + + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ + for(i = 0; i < extra_nfds; i++) { + unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents; + unsigned short mask = 0; + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; + extra_fds[i].revents = (short)mask; + } + } + else if(extrawait) { + /* No fds to poll, but asked to obey timeout_ms anyway. We cannot + * use Curl_poll() as it, on some platforms, returns immediately + * without fds. */ + curlx_wait_ms(timeout_ms); + } + +out: + *pnevents = nevents; + return mresult; +} + +#endif /* !USE_WINSOCK */ #define NUM_POLLS_ON_STACK 10 @@ -1342,13 +1529,12 @@ unsigned int extra_nfds, int timeout_ms, int *ret, - bool extrawait, /* when no socket, wait */ - bool use_wakeup) + bool extrawait) /* when no socket, wait */ { size_t i; struct curltime expire_time; long timeout_internal; - int retcode = 0; + int nevents = 0; struct easy_pollset ps; struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct curl_pollfds cpfds; @@ -1356,13 +1542,8 @@ struct Curl_easy *data = NULL; CURLMcode mresult = CURLM_OK; uint32_t mid; - -#ifdef USE_WINSOCK - WSANETWORKEVENTS wsa_events; - DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); -#endif -#ifndef ENABLE_WAKEUP - (void)use_wakeup; +#ifdef ENABLE_WAKEUP + int wakeup_idx = -1; #endif if(!GOOD_MULTI_HANDLE(multi)) @@ -1400,6 +1581,20 @@ goto out; } +#ifdef ENABLE_WAKEUP + /* If `extrawait` is TRUE *or* we have `extra_fds`to poll *or* we + * have transfer sockets to poll, we obey `timeout_ms`. + * Then we need to also monitor the multi's wakeup + * socket to catch calls to `curl_multi_wakeup()` during the wait. */ + if(extrawait || cpfds.n || extra_nfds) { + wakeup_idx = cpfds.n; + if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { + mresult = CURLM_OUT_OF_MEMORY; + goto out; + } + } +#endif + curl_nfds = cpfds.n; /* what curl internally uses in cpfds */ /* Add external file descriptions from poll-like struct curl_waitfd */ for(i = 0; i < extra_nfds; i++) { @@ -1416,198 +1611,64 @@ } } -#ifdef USE_WINSOCK - /* Set the WSA events based on the collected pollds */ - for(i = 0; i < cpfds.n; i++) { - long mask = 0; - if(cpfds.pfds[i].events & POLLIN) - mask |= FD_READ | FD_ACCEPT | FD_CLOSE; - if(cpfds.pfds[i].events & POLLPRI) - mask |= FD_OOB; - if(cpfds.pfds[i].events & POLLOUT) { - mask |= FD_WRITE | FD_CONNECT | FD_CLOSE; - reset_socket_fdwrite(cpfds.pfds[i].fd); - } - if(mask) { - if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) { - mresult = CURLM_OUT_OF_MEMORY; - goto out; - } - } - } -#endif - -#ifdef ENABLE_WAKEUP -#ifndef USE_WINSOCK - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { - mresult = CURLM_OUT_OF_MEMORY; - goto out; - } - } -#endif -#endif - /* We check the internal timeout *AFTER* we collected all sockets to * poll. Collecting the sockets may install new timers by protocols * and connection filters. - * Use the shorter one of the internal and the caller requested timeout. */ + * Use the shorter one of the internal and the caller requested timeout. + * If we are called with `!extrawait` and multi_timeout() reports no + * timeouts exist, do not wait. */ multi_timeout(multi, &expire_time, &timeout_internal); if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) timeout_ms = (int)timeout_internal; if(data) - CURL_TRC_M(data, "multi_wait(fds=%d, timeout=%d) tinternal=%ld", + CURL_TRC_M(data, "multi_wait(fds=%u, timeout=%d) tinternal=%ld", cpfds.n, timeout_ms, timeout_internal); -#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(cpfds.n || use_wakeup) { -#else - if(cpfds.n) { -#endif - int pollrc; -#ifdef USE_WINSOCK - if(cpfds.n) /* just pre-check with Winsock */ - pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0); - else - pollrc = 0; -#else - pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */ -#endif - if(pollrc < 0) { - mresult = CURLM_UNRECOVERABLE_POLL; - goto out; - } - - if(pollrc > 0) { - retcode = pollrc; -#ifdef USE_WINSOCK - } - else { /* now wait... if not ready during the pre-check (pollrc == 0) */ - WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, - FALSE); - } - /* With Winsock, we have to run the following section unconditionally - to call WSAEventSelect(fd, event, 0) on all the sockets */ - { -#endif - /* copy revents results from the poll to the curl_multi_wait poll - struct, the bit values of the actual underlying poll() implementation - may not be the same as the ones in the public libcurl API! */ - for(i = 0; i < extra_nfds; i++) { - unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents; - unsigned short mask = 0; -#ifdef USE_WINSOCK - curl_socket_t s = extra_fds[i].fd; - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { - if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) - mask |= CURL_WAIT_POLLIN; - if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE)) - mask |= CURL_WAIT_POLLOUT; - if(wsa_events.lNetworkEvents & FD_OOB) - mask |= CURL_WAIT_POLLPRI; - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(s, multi->wsa_event, 0); - if(!pollrc) { - extra_fds[i].revents = (short)mask; - continue; - } -#endif - if(r & POLLIN) - mask |= CURL_WAIT_POLLIN; - if(r & POLLOUT) - mask |= CURL_WAIT_POLLOUT; - if(r & POLLPRI) - mask |= CURL_WAIT_POLLPRI; - extra_fds[i].revents = (short)mask; - } #ifdef USE_WINSOCK - /* Count up all our own sockets that had activity, - and remove them from the event. */ - for(i = 0; i < curl_nfds; ++i) { - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(cpfds.pfds[i].fd, NULL, &wsa_events) == 0) { - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, 0); - } - WSAResetEvent(multi->wsa_event); + mresult = multi_winsock_select(multi, &cpfds, curl_nfds, + extra_fds, extra_nfds, + timeout_ms, extrawait, &nevents); #else + mresult = multi_posix_poll(multi, &cpfds, curl_nfds, + extra_fds, extra_nfds, + timeout_ms, extrawait, &nevents); +#endif + #ifdef ENABLE_WAKEUP - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) { - char buf[64]; - ssize_t nread; - while(1) { - /* the reading socket is non-blocking, try to read - data from it until it receives an error (except EINTR). - In normal cases it will get EAGAIN or EWOULDBLOCK - when there is no more data, breaking the loop. */ - nread = wakeup_read(multi->wakeup_pair[0], buf, sizeof(buf)); - if(nread <= 0) { - if(nread < 0 && SOCKEINTR == SOCKERRNO) - continue; - break; - } - } - /* do not count the wakeup socket into the returned value */ - retcode--; - } + if(nevents && (wakeup_idx >= 0)) { + if(cpfds.pfds[wakeup_idx].revents & POLLIN) { + (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); + /* do not count the wakeup socket into the returned value */ + nevents--; } -#endif -#endif } - } - - if(ret) - *ret = retcode; -#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(extrawait && !cpfds.n && !use_wakeup) { -#else - if(extrawait && !cpfds.n) { #endif - long sleep_ms = 0; - - /* Avoid busy-looping when there is nothing particular to wait for */ - multi_timeout(multi, &expire_time, &sleep_ms); - if(sleep_ms) { - if(sleep_ms > timeout_ms) - sleep_ms = timeout_ms; - /* when there are no easy handles in the multi, this holds a -1 - timeout */ - else if(sleep_ms < 0) - sleep_ms = timeout_ms; - curlx_wait_ms(sleep_ms); - } - } out: Curl_pollset_cleanup(&ps); Curl_pollfds_cleanup(&cpfds); + if(ret) + *ret = nevents; return mresult; } -CURLMcode curl_multi_wait(CURLM *multi, +CURLMcode curl_multi_wait(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, int *ret) { - return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE, - FALSE); + return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE); } -CURLMcode curl_multi_poll(CURLM *multi, +CURLMcode curl_multi_poll(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, int *ret) { - return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, TRUE); + return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE); } CURLMcode curl_multi_wakeup(CURLM *m) @@ -1616,56 +1677,24 @@ it has to be careful only to access parts of the Curl_multi struct that are constant */ struct Curl_multi *multi = m; + CURLMcode mresult = CURLM_WAKEUP_FAILURE; /* GOOD_MULTI_HANDLE can be safely called */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; #ifdef ENABLE_WAKEUP -#ifdef USE_WINSOCK - if(WSASetEvent(multi->wsa_event)) - return CURLM_OK; -#else /* the wakeup_pair variable is only written during init and cleanup, making it safe to access from another thread after the init part and before cleanup */ - if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) { - while(1) { -#ifdef USE_EVENTFD - /* eventfd has a stringent rule of requiring the 8-byte buffer when - calling write(2) on it */ - const uint64_t buf[1] = { 1 }; -#else - const char buf[1] = { 1 }; + if(!Curl_wakeup_signal(multi->wakeup_pair)) + mresult = CURLM_OK; #endif - /* swrite() is not thread-safe in general, because concurrent calls - can have their messages interleaved, but in this case the content - of the messages does not matter, which makes it ok to call. - - The write socket is set to non-blocking, this way this function - cannot block, making it safe to call even from the same thread - that will call curl_multi_wait(). If swrite() returns that it - would block, it is considered successful because it means that - previous calls to this function will wake up the poll(). */ - if(wakeup_write(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) { - int err = SOCKERRNO; - int return_success; #ifdef USE_WINSOCK - return_success = SOCKEWOULDBLOCK == err; -#else - if(SOCKEINTR == err) - continue; - return_success = SOCKEWOULDBLOCK == err || EAGAIN == err; + if(WSASetEvent(multi->wsa_event)) + mresult = CURLM_OK; #endif - if(!return_success) - return CURLM_WAKEUP_FAILURE; - } - return CURLM_OK; - } - } -#endif -#endif - return CURLM_WAKEUP_FAILURE; + return mresult; } /* @@ -1678,7 +1707,7 @@ */ static bool multi_ischanged(struct Curl_multi *multi, bool clear) { - bool retval = multi->recheckstate; + bool retval = (bool)multi->recheckstate; if(clear) multi->recheckstate = FALSE; return retval; @@ -1707,7 +1736,6 @@ mresult = curl_multi_add_handle(multi, data); if(!mresult) { - struct SingleRequest *k = &data->req; CURLcode result; /* pass in NULL for 'conn' here since we do not want to init the @@ -1721,7 +1749,7 @@ /* take this handle to the perform state right away */ multistate(data, MSTATE_PERFORMING); Curl_attach_connection(data, conn); - k->keepon |= KEEP_RECV; /* setup to receive! */ + CURL_REQ_SET_RECV(data); } return mresult; } @@ -1732,18 +1760,18 @@ struct connectdata *conn = data->conn; DEBUGASSERT(conn); - DEBUGASSERT(conn->handler); + DEBUGASSERT(conn->scheme); - if(conn->handler->do_it) - result = conn->handler->do_it(data, done); + if(conn->scheme->run->do_it) + result = conn->scheme->run->do_it(data, done); return result; } /* - * multi_do_more() is called during the DO_MORE multi state. It is basically a - * second stage DO state which (wrongly) was introduced to support FTP's - * second connection. + * multi_do_more() is called during the DO_MORE multi state. It is a second + * stage DO state which (wrongly) was introduced to support FTP's second + * connection. * * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to * DOING state there is more work to do! @@ -1756,8 +1784,8 @@ *complete = 0; - if(conn->handler->do_more) - result = conn->handler->do_more(data, complete); + if(conn->scheme->run->do_more) + result = conn->scheme->run->do_more(data, complete); return result; } @@ -1769,24 +1797,19 @@ bool *stream_error, CURLcode *result) { - bool connect_timeout = data->mstate < MSTATE_DO; timediff_t timeout_ms; - timeout_ms = Curl_timeleft_ms(data, connect_timeout); + timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { /* Handle timed out */ struct curltime since; - if(connect_timeout) + if(Curl_is_connecting(data)) since = data->progress.t_startsingle; else since = data->progress.t_startop; - if(data->mstate == MSTATE_RESOLVING) - failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T - " milliseconds", - curlx_ptimediff_ms(Curl_pgrs_now(data), &since)); - else if(data->mstate == MSTATE_CONNECTING) - failf(data, "Connection timed out after %" FMT_TIMEDIFF_T - " milliseconds", + if(data->mstate == MSTATE_CONNECTING) + failf(data, "%s timed out after %" FMT_TIMEDIFF_T " milliseconds", + data->conn->bits.dns_resolved ? "Connection" : "Resolving", curlx_ptimediff_ms(Curl_pgrs_now(data), &since)); else { struct SingleRequest *k = &data->req; @@ -1830,9 +1853,9 @@ CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - if(conn && conn->handler->connecting) { + if(conn && conn->scheme->run->connecting) { *done = FALSE; - result = conn->handler->connecting(data, done); + result = conn->scheme->run->connecting(data, done); } else *done = TRUE; @@ -1850,9 +1873,9 @@ CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - if(conn && conn->handler->doing) { + if(conn && conn->scheme->run->doing) { *done = FALSE; - result = conn->handler->doing(data, done); + result = conn->scheme->run->doing(data, done); } else *done = TRUE; @@ -1876,9 +1899,9 @@ *protocol_done = FALSE; if(!conn->bits.protoconnstart) { - if(conn->handler->connect_it) { + if(conn->scheme->run->connect_it) { /* Call the protocol-specific connect function */ - result = conn->handler->connect_it(data, protocol_done); + result = conn->scheme->run->connect_it(data, protocol_done); if(result) return result; } @@ -1887,7 +1910,7 @@ /* Unless this protocol does not have any protocol-connect callback, as then we know we are done. */ - if(!conn->handler->connecting) + if(!conn->scheme->run->connecting) *protocol_done = TRUE; return CURLE_OK; } @@ -1902,7 +1925,7 @@ */ static void multi_posttransfer(struct Curl_easy *data) { -#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(MSG_NOSIGNAL) /* restore the signal handler for SIGPIPE before we get back */ if(!data->set.no_signal) signal(SIGPIPE, data->state.prev_signal); @@ -1915,39 +1938,62 @@ * multi_follow() handles the URL redirect magic. Pass in the 'newurl' string * as given by the remote server and set up the new URL to request. * - * This function DOES NOT FREE the given url. + * This function DOES NOT FREE the given URL. */ static CURLcode multi_follow(struct Curl_easy *data, - const struct Curl_handler *handler, + const struct Curl_scheme *handler, const char *newurl, /* the Location: string */ followtype type) /* see transfer.h */ { - if(handler && handler->follow) - return handler->follow(data, newurl, type); + if(handler && handler->run->follow) + return handler->run->follow(data, newurl, type); + + if(type == FOLLOW_RETRY) + /* Retries are generic and do not require protocol-specific redirect + handling. */ + return CURLE_OK; + return CURLE_TOO_MANY_REDIRECTS; } static CURLcode mspeed_check(struct Curl_easy *data) { - const struct curltime *pnow = Curl_pgrs_now(data); - timediff_t recv_wait_ms = 0; - timediff_t send_wait_ms = 0; + if(Curl_rlimit_active(&data->progress.dl.rlimit) || + Curl_rlimit_active(&data->progress.ul.rlimit)) { + /* check if our send/recv limits require idle waits */ + const struct curltime *pnow = Curl_pgrs_now(data); + timediff_t recv_ms, send_ms; - /* check if our send/recv limits require idle waits */ - send_wait_ms = Curl_rlimit_wait_ms(&data->progress.ul.rlimit, pnow); - recv_wait_ms = Curl_rlimit_wait_ms(&data->progress.dl.rlimit, pnow); + send_ms = Curl_rlimit_wait_ms(&data->progress.ul.rlimit, pnow); + recv_ms = Curl_rlimit_wait_ms(&data->progress.dl.rlimit, pnow); - if(send_wait_ms || recv_wait_ms) { - if(data->mstate != MSTATE_RATELIMITING) { - multistate(data, MSTATE_RATELIMITING); + if(send_ms || recv_ms) { + if(data->mstate != MSTATE_RATELIMITING) { + multistate(data, MSTATE_RATELIMITING); + } + Curl_expire(data, CURLMAX(send_ms, recv_ms), EXPIRE_TOOFAST); + Curl_multi_clear_dirty(data); + CURL_TRC_M(data, "[RLIMIT] waiting %" FMT_TIMEDIFF_T "ms", + CURLMAX(send_ms, recv_ms)); + return CURLE_AGAIN; } - Curl_expire(data, CURLMAX(send_wait_ms, recv_wait_ms), EXPIRE_TOOFAST); - Curl_multi_clear_dirty(data); - CURL_TRC_M(data, "[RLIMIT] waiting %" FMT_TIMEDIFF_T "ms", - CURLMAX(send_wait_ms, recv_wait_ms)); - return CURLE_AGAIN; + else { + /* when will the rate limits increase next? The transfer needs + * to run again at that time or it may stall. */ + send_ms = Curl_rlimit_next_step_ms(&data->progress.ul.rlimit, pnow); + recv_ms = Curl_rlimit_next_step_ms(&data->progress.dl.rlimit, pnow); + if(send_ms || recv_ms) { + timediff_t next_ms = CURLMIN(send_ms, recv_ms); + if(!next_ms) + next_ms = CURLMAX(send_ms, recv_ms); + Curl_expire(data, next_ms, EXPIRE_TOOFAST); + CURL_TRC_M(data, "[RLIMIT] next token update in %" FMT_TIMEDIFF_T "ms", + next_ms); + } + } } - else if(data->mstate != MSTATE_PERFORMING) { + + if(data->mstate != MSTATE_PERFORMING) { CURL_TRC_M(data, "[RLIMIT] wait over, continue"); multistate(data, MSTATE_PERFORMING); } @@ -1989,7 +2035,7 @@ } } #ifndef CURL_DISABLE_HTTP - else if((CURLE_HTTP2_STREAM == result) && + else if((result == CURLE_HTTP2_STREAM) && Curl_h2_http_1_1_error(data)) { CURLcode ret = Curl_retry_request(data, &newurl); @@ -2028,7 +2074,7 @@ * connection. */ - if(!(data->conn->handler->flags & PROTOPT_DUAL) && + if(!(data->conn->scheme->flags & PROTOPT_DUAL) && result != CURLE_HTTP2_STREAM) streamclose(data->conn, "Transfer returned error"); @@ -2036,7 +2082,7 @@ multi_done(data, result, TRUE); } else if(data->req.done && !Curl_cwriter_is_paused(data)) { - const struct Curl_handler *handler = data->conn->handler; + const struct Curl_scheme *handler = data->conn->scheme; /* call this even if the readwrite function returned error */ multi_posttransfer(data); @@ -2046,7 +2092,7 @@ if(data->req.newurl || retry) { followtype follow = FOLLOW_NONE; if(!retry) { - /* if the URL is a follow-location and not just a retried request then + /* if the URL is a follow-location and not a retried request then figure out the URL here */ curlx_free(newurl); newurl = data->req.newurl; @@ -2168,14 +2214,14 @@ mresult = CURLM_CALL_MULTI_PERFORM; } } - else if((CURLE_SEND_ERROR == result) && + else if((result == CURLE_SEND_ERROR) && data->conn->bits.reuse) { /* * In this situation, a connection that we were trying to use may have * unexpectedly died. If possible, send the connection back to the * CONNECT phase so we can try again. */ - const struct Curl_handler *handler = data->conn->handler; + const struct Curl_scheme *handler = data->conn->scheme; char *newurl = NULL; followtype follow = FOLLOW_NONE; CURLcode drc; @@ -2240,7 +2286,7 @@ result = Curl_pgrsCheck(data); if(result) { - if(!(data->conn->handler->flags & PROTOPT_DUAL) && + if(!(data->conn->scheme->flags & PROTOPT_DUAL) && result != CURLE_HTTP2_STREAM) streamclose(data->conn, "Transfer returned error"); @@ -2255,56 +2301,6 @@ return mresult; } -static CURLMcode state_resolving(struct Curl_multi *multi, - struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) -{ - struct Curl_dns_entry *dns = NULL; - CURLcode result; - CURLMcode mresult = CURLM_OK; - - result = Curl_resolv_check(data, &dns); - CURL_TRC_DNS(data, "Curl_resolv_check() -> %d, %s", - result, dns ? "found" : "missing"); - /* Update sockets here, because the socket(s) may have been closed and the - application thus needs to be told, even if it is likely that the same - socket(s) will again be used further down. If the name has not yet been - resolved, it is likely that new sockets have been opened in an attempt to - contact another resolver. */ - mresult = Curl_multi_ev_assess_xfer(multi, data); - if(mresult) - return mresult; - - if(dns) { - bool connected; - /* Perform the next step in the connection phase, and then move on to the - WAITCONNECT state */ - result = Curl_once_resolved(data, dns, &connected); - - if(result) - /* if Curl_once_resolved() returns failure, the connection struct is - already freed and gone */ - data->conn = NULL; /* no more connection */ - else { - /* call again please so that we get the next socket setup */ - mresult = CURLM_CALL_MULTI_PERFORM; - if(connected) - multistate(data, MSTATE_PROTOCONNECT); - else { - multistate(data, MSTATE_CONNECTING); - } - } - } - - if(result) - /* failure detected */ - *stream_errorp = TRUE; - - *resultp = result; - return mresult; -} - static CURLMcode state_connect(struct Curl_multi *multi, struct Curl_easy *data, CURLcode *resultp) @@ -2312,10 +2308,9 @@ /* Connect. We want to get a connection identifier filled in. This state can be entered from SETUP and from PENDING. */ bool connected; - bool async; CURLMcode mresult = CURLM_OK; - CURLcode result = Curl_connect(data, &async, &connected); - if(CURLE_NO_CONNECTION_AVAILABLE == result) { + CURLcode result = Curl_connect(data, &connected); + if(result == CURLE_NO_CONNECTION_AVAILABLE) { /* There was no connection available. We will go to the pending state and wait for an available connection. */ multistate(data, MSTATE_PENDING); @@ -2330,36 +2325,134 @@ process_pending_handles(data->multi); if(!result) { - if(async) - /* We are now waiting for an asynchronous name lookup */ - multistate(data, MSTATE_RESOLVING); - else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to WAITDO or - DO! */ - mresult = CURLM_CALL_MULTI_PERFORM; + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to WAITDO or + DO! */ + mresult = CURLM_CALL_MULTI_PERFORM; - if(connected) { - if(!data->conn->bits.reuse && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); - } - multistate(data, MSTATE_PROTOCONNECT); + if(connected) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); } - else { - multistate(data, MSTATE_CONNECTING); - } + multistate(data, MSTATE_PROTOCONNECT); + } + else { + multistate(data, MSTATE_CONNECTING); } } *resultp = result; return mresult; } -static CURLMcode multi_runsingle(struct Curl_multi *multi, - struct Curl_easy *data) +/* returns the possibly updated result */ +static CURLcode is_finished(struct Curl_multi *multi, + struct Curl_easy *data, + bool stream_error, + CURLcode result) { - struct Curl_message *msg = NULL; + if(data->mstate < MSTATE_COMPLETED) { + if(result) { + /* + * If an error was returned, and we are not in completed state now, + * then we go to completed and consider this transfer aborted. + */ + + /* No attempt to disconnect connections must be made before this - + connection detach and termination happens only here */ + + /* Check if we can move pending requests to send pipe */ + process_pending_handles(multi); /* connection */ + + if(data->conn) { + if(stream_error) { + /* Do not attempt to send data over a connection that timed out */ + bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; + struct connectdata *conn = data->conn; + + /* This is where we make sure that the conn pointer is reset. + We do not have to do this in every case block above where a + failure is detected */ + Curl_detach_connection(data); + Curl_conn_terminate(data, conn, dead_connection); + } + } + else if(data->mstate == MSTATE_CONNECT) { + /* Curl_connect() failed */ + multi_posttransfer(data); + Curl_pgrsUpdate_nometer(data); + } + + multistate(data, MSTATE_COMPLETED); + return result; + } + /* if there is still a connection to use, call the progress function */ + else if(data->conn && Curl_conn_is_connected(data->conn, FIRSTSOCKET)) { + result = Curl_pgrsUpdate(data); + if(result) { + /* aborted due to progress callback return code must close the + connection */ + streamclose(data->conn, "Aborted by callback"); + + /* if not yet in DONE state, go there, otherwise COMPLETED */ + multistate(data, (data->mstate < MSTATE_DONE) ? + MSTATE_DONE : MSTATE_COMPLETED); + return result; + } + } + } + return result; +} + +static void handle_completed(struct Curl_multi *multi, + struct Curl_easy *data, + CURLcode result) +{ + if(data->master_mid != UINT32_MAX) { + /* A sub transfer, not for msgsent to application */ + struct Curl_easy *mdata; + + CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid); + mdata = Curl_multi_get_easy(multi, data->master_mid); + if(mdata) { + if(mdata->sub_xfer_done) + mdata->sub_xfer_done(mdata, data, result); + else + CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.", + data->master_mid); + } + else { + CURL_TRC_M(data, "master easy %u already gone.", data->master_mid); + } + } + else { + /* now fill in the Curl_message with this info */ + struct Curl_message *msg = &data->msg; + + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = data; + msg->extmsg.data.result = result; + + multi_addmsg(multi, msg); + DEBUGASSERT(!data->conn); + } + multistate(data, MSTATE_MSGSENT); + + /* remove from the other sets, add to msgsent */ + Curl_uint32_bset_remove(&multi->process, data->mid); + Curl_uint32_bset_remove(&multi->dirty, data->mid); + Curl_uint32_bset_remove(&multi->pending, data->mid); + Curl_uint32_bset_add(&multi->msgsent, data->mid); + --multi->xfers_alive; + if(!multi->xfers_alive) + multi_assess_wakeup(multi); +} + +static CURLMcode multi_runsingle(struct Curl_multi *multi, + struct Curl_easy *data, + struct Curl_sigpipe_ctx *sigpipe_ctx) +{ bool connected; bool protocol_connected = FALSE; bool dophase_done = FALSE; @@ -2386,10 +2479,14 @@ Curl_uint32_bset_remove(&multi->dirty, data->mid); if(data == multi->admin) { - Curl_cshutdn_perform(&multi->cshutdn, multi->admin, CURL_SOCKET_TIMEOUT); +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_process(multi); +#endif + Curl_cshutdn_perform(&multi->cshutdn, multi->admin, sigpipe_ctx); return CURLM_OK; } + sigpipe_apply(data, sigpipe_ctx); do { /* A "stream" here is a logical stream if the protocol can handle that (HTTP/2), or the full connection for older protocols */ @@ -2426,7 +2523,7 @@ /* after init, go SETUP */ multistate(data, MSTATE_SETUP); - (void)Curl_pgrsTime(data, TIMER_STARTOP); + Curl_pgrsTime(data, TIMER_STARTOP); FALLTHROUGH(); case MSTATE_SETUP: @@ -2448,15 +2545,14 @@ mresult = state_connect(multi, data, &result); break; - case MSTATE_RESOLVING: - /* awaiting an asynch name resolve to complete */ - mresult = state_resolving(multi, data, &stream_error, &result); - break; - case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ - DEBUGASSERT(data->conn); - if(!Curl_xfer_recv_is_paused(data)) { + if(!data->conn) { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + break; + } + else if(!Curl_xfer_recv_is_paused(data)) { result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); if(connected && !result) { if(!data->conn->bits.reuse && @@ -2469,6 +2565,7 @@ } else if(result) { /* failure detected */ + CURL_TRC_M(data, "connect failed -> %d", result); multi_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; @@ -2586,7 +2683,7 @@ else { #ifndef CURL_DISABLE_FTP if(data->state.wildcardmatch && - ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) { + ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) { data->wildcard->state = CURLWC_DONE; } #endif @@ -2660,94 +2757,12 @@ statemachine_end: - if(data->mstate < MSTATE_COMPLETED) { - if(result) { - /* - * If an error was returned, and we are not in completed state now, - * then we go to completed and consider this transfer aborted. - */ - - /* NOTE: no attempt to disconnect connections must be made - in the case blocks above - cleanup happens only here */ - - /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* connection */ - - if(data->conn) { - if(stream_error) { - /* Do not attempt to send data over a connection that timed out */ - bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; - struct connectdata *conn = data->conn; - - /* This is where we make sure that the conn pointer is reset. - We do not have to do this in every case block above where a - failure is detected */ - Curl_detach_connection(data); - Curl_conn_terminate(data, conn, dead_connection); - } - } - else if(data->mstate == MSTATE_CONNECT) { - /* Curl_connect() failed */ - multi_posttransfer(data); - Curl_pgrsUpdate_nometer(data); - } - - multistate(data, MSTATE_COMPLETED); - mresult = CURLM_CALL_MULTI_PERFORM; - } - /* if there is still a connection to use, call the progress function */ - else if(data->conn) { - result = Curl_pgrsUpdate(data); - if(result) { - /* aborted due to progress callback return code must close the - connection */ - streamclose(data->conn, "Aborted by callback"); - - /* if not yet in DONE state, go there, otherwise COMPLETED */ - multistate(data, (data->mstate < MSTATE_DONE) ? - MSTATE_DONE : MSTATE_COMPLETED); - mresult = CURLM_CALL_MULTI_PERFORM; - } - } - } + result = is_finished(multi, data, stream_error, result); + if(result) + mresult = CURLM_CALL_MULTI_PERFORM; if(MSTATE_COMPLETED == data->mstate) { - if(data->master_mid != UINT32_MAX) { - /* A sub transfer, not for msgsent to application */ - struct Curl_easy *mdata; - - CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid); - mdata = Curl_multi_get_easy(multi, data->master_mid); - if(mdata) { - if(mdata->sub_xfer_done) - mdata->sub_xfer_done(mdata, data, result); - else - CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.", - data->master_mid); - } - else { - CURL_TRC_M(data, "master easy %u already gone.", data->master_mid); - } - } - else { - /* now fill in the Curl_message with this info */ - msg = &data->msg; - - msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = data; - msg->extmsg.data.result = result; - - multi_addmsg(multi, msg); - DEBUGASSERT(!data->conn); - } - multistate(data, MSTATE_MSGSENT); - - /* remove from the other sets, add to msgsent */ - Curl_uint32_bset_remove(&multi->process, data->mid); - Curl_uint32_bset_remove(&multi->dirty, data->mid); - Curl_uint32_bset_remove(&multi->pending, data->mid); - Curl_uint32_bset_add(&multi->msgsent, data->mid); - --multi->xfers_alive; + handle_completed(multi, data, result); return CURLM_OK; } } while((mresult == CURLM_CALL_MULTI_PERFORM) || @@ -2763,7 +2778,7 @@ CURLMcode returncode = CURLM_OK; struct curltime start = *multi_now(multi); uint32_t mid; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; @@ -2771,7 +2786,8 @@ if(multi->in_ntfy_callback) return CURLM_RECURSIVE_API_CALL; - sigpipe_init(&pipe_st); + sigpipe_init(&sigpipe_ctx); + if(Curl_uint32_bset_first(&multi->process, &mid)) { CURL_TRC_M(multi->admin, "multi_perform(running=%u)", Curl_multi_xfers_running(multi)); @@ -2784,13 +2800,12 @@ Curl_uint32_bset_remove(&multi->dirty, mid); continue; } - sigpipe_apply(data, &pipe_st); - mresult = multi_runsingle(multi, data); + mresult = multi_runsingle(multi, data, &sigpipe_ctx); if(mresult) returncode = mresult; } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); if(multi_ischanged(multi, TRUE)) process_pending_handles(multi); @@ -2799,7 +2814,7 @@ returncode = Curl_mntfy_dispatch_all(multi); /* - * Simply remove all expired timers from the splay since handles are dealt + * Remove all expired timers from the splay since handles are dealt * with unconditionally by this function and curl_multi_timeout() requires * that already passed/handled expire times are removed from the splay. * @@ -2896,6 +2911,9 @@ } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)); } +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_destroy(multi, !multi->quick_exit); +#endif Curl_cpool_destroy(&multi->cpool); Curl_cshutdn_destroy(&multi->cshutdn, multi->admin); if(multi->admin) { @@ -2917,13 +2935,9 @@ #ifdef USE_WINSOCK WSACloseEvent(multi->wsa_event); -#else +#endif #ifdef ENABLE_WAKEUP - wakeup_close(multi->wakeup_pair[0]); -#ifndef USE_EVENTFD - wakeup_close(multi->wakeup_pair[1]); -#endif -#endif + Curl_wakeup_destroy(multi->wakeup_pair); #endif multi_xfer_bufs_free(multi); @@ -2999,7 +3013,7 @@ * add_next_timeout() * * Each Curl_easy has a list of timeouts. The add_next_timeout() is called - * when it has just been removed from the splay tree because the timeout has + * when it has been removed from the splay tree because the timeout has * expired. This function is then to advance in the list to pick the next * timeout to use (skip the already expired ones) and add this node back to * the splay tree again. @@ -3050,22 +3064,15 @@ return CURLM_OK; } -struct multi_run_ctx { - struct Curl_multi *multi; - size_t run_xfers; - SIGPIPE_MEMBER(pipe_st); -}; - -static void multi_mark_expired_as_dirty(struct multi_run_ctx *mrc, +static void multi_mark_expired_as_dirty(struct Curl_multi *multi, const struct curltime *ts) { - struct Curl_multi *multi = mrc->multi; struct Curl_easy *data = NULL; struct Curl_tree *t = NULL; /* * The loop following here will go on as long as there are expire-times left - * to process (compared to mrc->now) in the splay and 'data' will be + * to process (compared to `ts`) in the splay and 'data' will be * re-assigned for every expired handle we deal with. */ while(1) { @@ -3078,7 +3085,7 @@ data = Curl_splayget(t); /* assign this for next loop */ if(!data) continue; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(CURL_TRC_TIMER_is_verbose(data)) { struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); if(e) { @@ -3092,12 +3099,14 @@ } } -static CURLMcode multi_run_dirty(struct multi_run_ctx *mrc) +static CURLMcode multi_run_dirty(struct Curl_multi *multi, + struct Curl_sigpipe_ctx *sigpipe_ctx, + uint32_t *pnum) { - struct Curl_multi *multi = mrc->multi; CURLMcode mresult = CURLM_OK; uint32_t mid; + *pnum = 0; if(Curl_uint32_bset_first(&multi->dirty, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); @@ -3110,10 +3119,9 @@ continue; } - mrc->run_xfers++; - sigpipe_apply(data, &mrc->pipe_st); + (*pnum)++; /* runsingle() clears the dirty mid */ - mresult = multi_runsingle(multi, data); + mresult = multi_runsingle(multi, data, sigpipe_ctx); if(CURLM_OK >= mresult) { /* reassess event handling of data */ @@ -3140,12 +3148,11 @@ int *running_handles) { CURLMcode mresult = CURLM_OK; - struct multi_run_ctx mrc; + struct Curl_sigpipe_ctx pipe_ctx; + uint32_t run_xfers; (void)ev_bitmask; - memset(&mrc, 0, sizeof(mrc)); - mrc.multi = multi; - sigpipe_init(&mrc.pipe_st); + sigpipe_init(&pipe_ctx); if(checkall) { /* *perform() deals with running_handles on its own */ @@ -3169,25 +3176,33 @@ handles the case when the application asks libcurl to run the timeout prematurely. */ memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts)); + + /* Applications may set `socket_cb` *after* having added transfers + * first. *Then* kick off processing with a + * curl_multi_socket_action(TIMEOUT) afterwards. Make sure our + * admin handle registers its pollset with the callbacks present. */ + mresult = multi_assess_wakeup(multi); + if(mresult) + goto out; } - multi_mark_expired_as_dirty(&mrc, multi_now(multi)); - mresult = multi_run_dirty(&mrc); + multi_mark_expired_as_dirty(multi, multi_now(multi)); + mresult = multi_run_dirty(multi, &pipe_ctx, &run_xfers); if(mresult) goto out; - if(mrc.run_xfers) { + if(run_xfers) { /* Running transfers takes time. With a new timestamp, we might catch * other expires which are due now. Instead of telling the application * to set a 0 timeout and call us again, we run them here. * Do that only once or it might be unfair to transfers on other * sockets. */ - multi_mark_expired_as_dirty(&mrc, &multi->now); - mresult = multi_run_dirty(&mrc); + multi_mark_expired_as_dirty(multi, &multi->now); + mresult = multi_run_dirty(multi, &pipe_ctx, &run_xfers); } out: - sigpipe_restore(&mrc.pipe_st); + sigpipe_restore(&pipe_ctx); if(multi_ischanged(multi, TRUE)) process_pending_handles(multi); @@ -3276,6 +3291,11 @@ } case CURLMOPT_NETWORK_CHANGED: { long val = va_arg(param, long); + if(val & CURLMNWC_CLEAR_ALL) + /* In the beginning, all values available to set were 1 by mistake. We + converted this to mean "all", thus setting all the bits + automatically */ + val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS; if(val & CURLMNWC_CLEAR_DNS) { Curl_dnscache_clear(multi->admin); } @@ -3290,6 +3310,34 @@ case CURLMOPT_NOTIFYDATA: multi->ntfy.ntfy_cb_data = va_arg(param, void *); break; + case CURLMOPT_RESOLVE_THREADS_MAX: +#ifdef USE_RESOLV_THREADED + uarg = va_arg(param, long); + if((uarg <= 0) || (uarg > UINT32_MAX)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + else { + CURLcode result = Curl_async_thrdd_multi_set_props( + multi, 0, (uint32_t)uarg, 2000); + switch(result) { + case CURLE_OK: + mresult = CURLM_OK; + break; + case CURLE_BAD_FUNCTION_ARGUMENT: + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + break; + case CURLE_OUT_OF_MEMORY: + mresult = CURLM_OUT_OF_MEMORY; + break; + default: + mresult = CURLM_INTERNAL_ERROR; + break; + } + } +#endif + break; + case CURLMOPT_QUICK_EXIT: + multi->quick_exit = va_arg(param, long) ? 1 : 0; + break; default: mresult = CURLM_UNKNOWN_OPTION; break; @@ -3358,9 +3406,7 @@ long *timeout_ms) { static const struct curltime tv_zero = { 0, 0 }; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - struct Curl_easy *data = NULL; -#endif + VERBOSE(struct Curl_easy *data = NULL); if(multi->dead) { *timeout_ms = 0; @@ -3387,19 +3433,14 @@ /* some time left before expiration */ timediff_t diff_ms = curlx_timediff_ceil_ms(multi->timetree->key, *pnow); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - data = Curl_splayget(multi->timetree); -#endif + VERBOSE(data = Curl_splayget(multi->timetree)); /* this should be safe even on 32-bit archs, as we do not use that overly long timeouts */ *timeout_ms = (long)diff_ms; } else { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(multi->timetree) { - data = Curl_splayget(multi->timetree); - } -#endif + if(multi->timetree) + VERBOSE(data = Curl_splayget(multi->timetree)); /* 0 means immediately */ *timeout_ms = 0; } @@ -3409,8 +3450,8 @@ *timeout_ms = -1; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(data && CURL_TRC_TIMER_is_verbose(data)) { +#ifdef CURLVERBOSE + if(CURL_TRC_TIMER_is_verbose(data)) { struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); if(e) { struct time_node *n = Curl_node_elem(e); @@ -3579,7 +3620,7 @@ set.tv_usec -= 1000000; } - /* Remove any timer with the same id just in case. */ + /* Remove any timer with the same id */ multi_deltimeout(data, id); /* Add it to the timer list. It must stay in the list until it has expired @@ -3637,11 +3678,11 @@ * Removes the expire timer. Marks it as done. * */ -void Curl_expire_done(struct Curl_easy *data, expire_id eid) +void Curl_expire_done(struct Curl_easy *data, expire_id id) { /* remove the timer, if there */ - multi_deltimeout(data, eid); - CURL_TRC_TIMER(data, eid, "cleared"); + multi_deltimeout(data, id); + CURL_TRC_TIMER(data, id, "cleared"); } /* @@ -3649,7 +3690,7 @@ * * Clear ALL timeout values for this handle. */ -bool Curl_expire_clear(struct Curl_easy *data) +void Curl_expire_clear(struct Curl_easy *data) { struct Curl_multi *multi = data->multi; struct curltime *nowp = &data->state.expiretime; @@ -3657,7 +3698,7 @@ /* this is only interesting while there is still an associated multi struct remaining! */ if(!multi) - return FALSE; + return; if(nowp->tv_sec || nowp->tv_usec) { /* Since this is an cleared time, we must remove the previous entry from @@ -3677,19 +3718,17 @@ CURL_TRC_M(data, "[TIMEOUT] all cleared"); nowp->tv_sec = 0; nowp->tv_usec = 0; - return TRUE; } - return FALSE; } -CURLMcode curl_multi_assign(CURLM *m, curl_socket_t s, - void *hashp) +CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, + void *sockp) { struct Curl_multi *multi = m; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - return Curl_multi_ev_assign(multi, s, hashp); + return Curl_multi_ev_assign(multi, sockfd, sockp); } static void move_pending_to_connect(struct Curl_multi *multi, @@ -3721,13 +3760,32 @@ */ static void process_pending_handles(struct Curl_multi *multi) { - uint32_t mid; + uint32_t mid = multi->last_pending_mid; + + if(mid) { + while(Curl_uint32_bset_next(&multi->pending, mid, &mid)) { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(data) { + move_pending_to_connect(multi, data); + multi->last_pending_mid = mid; + return; + } + /* transfer no longer known, should not happen */ + Curl_uint32_bset_remove(&multi->pending, mid); + DEBUGASSERT(0); + } + /* found no pending transfers with `mid` larger than `last_pending_mid`. + * Start at the beginning of the pending set again. */ + multi->last_pending_mid = 0; + } + if(Curl_uint32_bset_first(&multi->pending, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); if(data) { move_pending_to_connect(multi, data); - break; + multi->last_pending_mid = mid; + return; } /* transfer no longer known, should not happen */ Curl_uint32_bset_remove(&multi->pending, mid); @@ -3757,7 +3815,7 @@ { struct Curl_multi *multi = m; void *entry; - unsigned int count = Curl_uint32_tbl_count(&multi->xfers); + size_t count = Curl_uint32_tbl_count(&multi->xfers); CURL **a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1)); if(a) { unsigned int i = 0; @@ -3971,13 +4029,13 @@ static void multi_xfer_bufs_free(struct Curl_multi *multi) { DEBUGASSERT(multi); - Curl_safefree(multi->xfer_buf); + curlx_safefree(multi->xfer_buf); multi->xfer_buf_len = 0; multi->xfer_buf_borrowed = FALSE; - Curl_safefree(multi->xfer_ulbuf); + curlx_safefree(multi->xfer_ulbuf); multi->xfer_ulbuf_len = 0; multi->xfer_ulbuf_borrowed = FALSE; - Curl_safefree(multi->xfer_sockbuf); + curlx_safefree(multi->xfer_sockbuf); multi->xfer_sockbuf_len = 0; multi->xfer_sockbuf_borrowed = FALSE; } @@ -3986,7 +4044,7 @@ uint32_t mid) { struct Curl_easy *data = Curl_uint32_tbl_get(&multi->xfers, mid); - if(data && GOOD_EASY_HANDLE(data)) + if(GOOD_EASY_HANDLE(data)) return data; CURL_TRC_M(multi->admin, "invalid easy handle in xfer table for mid=%u", mid);
diff --git a/Utilities/cmcurl/lib/multi_ev.c b/Utilities/cmcurl/lib/multi_ev.c index 0c89721..937e7ce 100644 --- a/Utilities/cmcurl/lib/multi_ev.c +++ b/Utilities/cmcurl/lib/multi_ev.c
@@ -195,6 +195,7 @@ /* We managed this socket before, tell the socket callback to forget it. */ if(entry->announced && multi->socket_cb) { + NOVERBOSE((void)cause); CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s); mev_in_callback(multi, TRUE); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, @@ -252,7 +253,7 @@ DEBUGASSERT(entry->writers + entry->readers); CURL_TRC_M(data, "ev update fd=%" FMT_SOCKET_T ", action '%s%s' -> '%s%s'" - " (%d/%d r/w)", s, + " (%u/%u r/w)", s, (last_action & CURL_POLL_IN) ? "IN" : "", (last_action & CURL_POLL_OUT) ? "OUT" : "", (cur_action & CURL_POLL_IN) ? "IN" : "", @@ -567,9 +568,9 @@ /* Unmatched socket, we cannot act on it but we ignore this fact. In real-world tests it has been proved that libevent can in fact give - the application actions even though the socket was just previously + the application actions even though the socket was previously asked to get removed, so thus we better survive stray socket actions - and just move on. */ + and move on. */ if(entry) { struct Curl_easy *data; uint32_t mid; @@ -602,10 +603,8 @@ struct Curl_easy *data) { DEBUGASSERT(!data->conn); /* transfer should have been detached */ - if(data != multi->admin) { - (void)mev_assess(multi, data, NULL); - Curl_meta_remove(data, CURL_META_MEV_POLLSET); - } + (void)mev_assess(multi, data, NULL); + Curl_meta_remove(data, CURL_META_MEV_POLLSET); } void Curl_multi_ev_conn_done(struct Curl_multi *multi,
diff --git a/Utilities/cmcurl/lib/multi_ntfy.c b/Utilities/cmcurl/lib/multi_ntfy.c index 4921fc5..1319aae 100644 --- a/Utilities/cmcurl/lib/multi_ntfy.c +++ b/Utilities/cmcurl/lib/multi_ntfy.c
@@ -110,7 +110,7 @@ /* only when notification has not been disabled in the meantime */ if(data && Curl_uint32_bset_contains(&multi->ntfy.enabled, e->type)) { /* this may cause new notifications to be added! */ - CURL_TRC_M(multi->admin, "[NTFY] dispatch %d to xfer %u", + CURL_TRC_M(multi->admin, "[NTFY] dispatch %u to xfer %u", e->type, e->mid); multi->ntfy.ntfy_cb(multi, e->type, data, multi->ntfy.ntfy_cb_data); } @@ -168,7 +168,7 @@ Curl_uint32_bset_contains(&multi->ntfy.enabled, (uint32_t)type)) { /* append to list of outstanding notifications */ struct mntfy_chunk *tail = mntfy_non_full_tail(&multi->ntfy); - CURL_TRC_M(data, "[NTFY] add %d for xfer %u", type, data->mid); + CURL_TRC_M(data, "[NTFY] add %u for xfer %u", type, data->mid); if(tail) mntfy_chunk_append(tail, data, (uint32_t)type); else
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index f66853f..c5cdfbe 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h
@@ -27,7 +27,7 @@ #include "hash.h" #include "conncache.h" #include "cshutdn.h" -#include "hostip.h" +#include "dnscache.h" #include "multi_ev.h" #include "multi_ntfy.h" #include "psl.h" @@ -53,7 +53,6 @@ MSTATE_PENDING, /* no connections, waiting for one */ MSTATE_SETUP, /* start a new transfer */ MSTATE_CONNECT, /* resolve/connect has been sent off */ - MSTATE_RESOLVING, /* awaiting the resolve to finalize */ MSTATE_CONNECTING, /* awaiting the TCP connect to finalize */ MSTATE_PROTOCONNECT, /* initiate protocol connect procedure */ MSTATE_PROTOCONNECTING, /* completing the protocol-specific connect phase */ @@ -71,7 +70,7 @@ #define CURLPIPE_ANY (CURLPIPE_MULTIPLEX) -#ifndef CURL_DISABLE_SOCKETPAIR +#if !defined(CURL_DISABLE_SOCKETPAIR) && !defined(USE_WINSOCK) #define ENABLE_WAKEUP #endif @@ -109,6 +108,9 @@ struct Curl_dnscache dnscache; /* DNS cache */ struct Curl_ssl_scache *ssl_scache; /* TLS session pool */ +#ifdef USE_RESOLV_THREADED + struct curl_thrdq *resolv_thrdq; +#endif #ifdef USE_LIBPSL /* PSL cache. */ @@ -160,13 +162,12 @@ #ifdef USE_WINSOCK WSAEVENT wsa_event; /* Winsock event used for waits */ -#else +#endif #ifdef ENABLE_WAKEUP curl_socket_t wakeup_pair[2]; /* eventfd()/pipe()/socketpair() used for wakeup 0 is used for read, 1 is used for write */ #endif -#endif unsigned int max_concurrent_streams; unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of entries we are allowed to grow the connection @@ -174,10 +175,9 @@ #ifdef DEBUGBUILD unsigned int now_access_count; #endif -#define IPV6_UNKNOWN 0 -#define IPV6_DEAD 1 -#define IPV6_WORKS 2 - unsigned char ipv6_up; /* IPV6_* defined */ + uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */ + uint32_t last_resolv_id; /* id of the last DNS resolve operation */ + BIT(ipv6_works); BIT(multiplexing); /* multiplexing wanted */ BIT(recheckstate); /* see Curl_multi_connchanged */ BIT(in_callback); /* true while executing a callback */ @@ -190,6 +190,7 @@ BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */ BIT(xfer_ulbuf_borrowed); /* xfer_ulbuf is currently being borrowed */ BIT(xfer_sockbuf_borrowed); /* xfer_sockbuf is currently being borrowed */ + BIT(quick_exit); /* do not join threads on cleanup */ #ifdef DEBUGBUILD BIT(warned); /* true after user warned of DEBUGBUILD */ #endif
diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h index 15fc070..039db26 100644 --- a/Utilities/cmcurl/lib/multiif.h +++ b/Utilities/cmcurl/lib/multiif.h
@@ -27,10 +27,10 @@ * Prototypes for library-wide functions provided by multi.c */ -void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id); +void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id); void Curl_expire_ex(struct Curl_easy *data, timediff_t milli, expire_id id); -bool Curl_expire_clear(struct Curl_easy *data); +void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connection(struct Curl_easy *data, @@ -40,13 +40,14 @@ void Curl_set_in_callback(struct Curl_easy *data, bool value); bool Curl_is_in_callback(struct Curl_easy *data); CURLcode Curl_preconnect(struct Curl_easy *data); +bool Curl_is_connecting(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(uint32_t xfer_table_size, - size_t hashsize, + size_t ev_hashsize, size_t chashsize, size_t dnssize, size_t sesssize);
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c index 0678733..72d8fee 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c
@@ -72,35 +72,329 @@ { NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ FILE *file = curlx_fopen(filename, FOPEN_READTEXT); - struct dynbuf linebuf; - curlx_dyn_init(&linebuf, MAX_NETRC_LINE); if(file) { - CURLcode result = CURLE_OK; - bool eof; - ret = NETRC_OK; - do { - const char *line; - result = Curl_get_line(&linebuf, file, &eof); - if(!result) { - line = curlx_dyn_ptr(&linebuf); - /* skip comments on load */ - curlx_str_passblanks(&line); - if(*line == '#') - continue; - result = curlx_dyn_add(filebuf, line); - } - if(result) { - curlx_dyn_free(filebuf); - ret = curl2netrc(result); + curlx_struct_stat stat; + if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + CURLcode result = CURLE_OK; + bool eof; + struct dynbuf linebuf; + curlx_dyn_init(&linebuf, MAX_NETRC_LINE); + ret = NETRC_OK; + do { + const char *line; + /* Curl_get_line always returns lines ending with a newline */ + result = Curl_get_line(&linebuf, file, &eof); + if(!result) { + line = curlx_dyn_ptr(&linebuf); + /* skip comments on load */ + curlx_str_passblanks(&line); + if(*line == '#') + continue; + result = curlx_dyn_add(filebuf, line); + } + if(result) { + curlx_dyn_free(filebuf); + ret = curl2netrc(result); + break; + } + } while(!eof); + curlx_dyn_free(&linebuf); + } + curlx_fclose(file); + } + return ret; +} + +/* bundled parser state to keep function signatures compact */ +struct netrc_state { + char *login; + char *password; + enum host_lookup_state state; + enum found_state keyword; + NETRCcode retcode; + unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */ + bool our_login; + bool done; + bool specific_login; +}; + +/* + * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t + * escape sequences. Advances *tok_endp past the closing '"'. + * + * Returns NETRC_OK or error. + */ +static NETRCcode netrc_quoted_token(const char **tok_endp, + struct dynbuf *token) +{ + bool escape = FALSE; + NETRCcode rc = NETRC_SYNTAX_ERROR; + const char *tok_end = *tok_endp; + tok_end++; /* pass the leading quote */ + while(*tok_end) { + CURLcode result; + char s = *tok_end; + if(escape) { + escape = FALSE; + switch(s) { + case 'n': + s = '\n'; + break; + case 'r': + s = '\r'; + break; + case 't': + s = '\t'; break; } - } while(!eof); + } + else if(s == '\\') { + escape = TRUE; + tok_end++; + continue; + } + else if(s == '\"') { + tok_end++; /* pass the ending quote */ + rc = NETRC_OK; + break; + } + result = curlx_dyn_addn(token, &s, 1); + if(result) { + *tok_endp = tok_end; + return curl2netrc(result); + } + tok_end++; } - curlx_dyn_free(&linebuf); - if(file) - curlx_fclose(file); - return ret; + *tok_endp = tok_end; + return rc; +} + +/* + * Gets the next token from the netrc buffer at *tokp. Writes the token into + * the 'token' dynbuf. Advances *tok_endp past the consumed token in the input + * buffer. Updates *statep for MACDEF newline handling. Sets *lineend = TRUE + * when the line is exhausted. + * + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_get_token(const char **tokp, + const char **tok_endp, + struct dynbuf *token, + enum host_lookup_state *statep, + bool *lineend) +{ + const char *tok = *tokp; + const char *tok_end; + + *lineend = FALSE; + curlx_dyn_reset(token); + curlx_str_passblanks(&tok); + + /* tok is first non-space letter */ + if(*statep == MACDEF) { + if((*tok == '\n') || (*tok == '\r')) + *statep = NOTHING; /* end of macro definition */ + *lineend = TRUE; + *tokp = tok; + return NETRC_OK; + } + + if(!*tok || (*tok == '\n')) { + /* end of line */ + *lineend = TRUE; + *tokp = tok; + return NETRC_OK; + } + + tok_end = tok; + if(*tok == '\"') { + /* quoted string */ + NETRCcode ret = netrc_quoted_token(&tok_end, token); + if(ret) + return ret; + } + else { + /* unquoted token */ + size_t len = 0; + CURLcode result; + while(*tok_end > ' ') { + tok_end++; + len++; + } + if(!len) + return NETRC_SYNTAX_ERROR; + result = curlx_dyn_addn(token, tok, len); + if(result) + return curl2netrc(result); + } + + *tok_endp = tok_end; + + if(curlx_dyn_len(token)) + *tokp = curlx_dyn_ptr(token); + else + /* set it to blank to avoid NULL */ + *tokp = ""; + + return NETRC_OK; +} + +/* + * Reset parser for a new machine entry. Frees password and optionally login + * if it was not user-specified. + */ +static void netrc_new_machine(struct netrc_state *ns) +{ + ns->keyword = NONE; + ns->found = 0; + ns->our_login = FALSE; + curlx_safefree(ns->password); + if(!ns->specific_login) + curlx_safefree(ns->login); +} + +/* + * Process a parsed token through the HOSTVALID state machine branch. This + * handles login/password values and keyword transitions for the matched host. + * + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) +{ + if(ns->keyword == LOGIN) { + if(ns->specific_login) + ns->our_login = !Curl_timestrcmp(ns->login, tok); + else { + ns->our_login = TRUE; + curlx_free(ns->login); + ns->login = curlx_strdup(tok); + if(!ns->login) + return NETRC_OUT_OF_MEMORY; + } + ns->found |= FOUND_LOGIN; + ns->keyword = NONE; + } + else if(ns->keyword == PASSWORD) { + curlx_free(ns->password); + ns->password = curlx_strdup(tok); + if(!ns->password) + return NETRC_OUT_OF_MEMORY; + ns->found |= FOUND_PASSWORD; + ns->keyword = NONE; + } + else if(curl_strequal("login", tok)) + ns->keyword = LOGIN; + else if(curl_strequal("password", tok)) + ns->keyword = PASSWORD; + else if(curl_strequal("machine", tok)) { + /* a new machine here */ + + if(ns->found & FOUND_PASSWORD && + /* a password was provided for this host */ + + ((!ns->specific_login || ns->our_login) || + /* either there was no specific login to search for, or this + is the specific one we wanted */ + (ns->specific_login && !(ns->found & FOUND_LOGIN)))) { + /* or we look for a specific login, but that was not specified */ + + ns->done = TRUE; + return NETRC_OK; + } + + ns->state = HOSTFOUND; + netrc_new_machine(ns); + } + else if(curl_strequal("default", tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + netrc_new_machine(ns); + } + if((ns->found == (FOUND_PASSWORD | FOUND_LOGIN)) && ns->our_login) + ns->done = TRUE; + return NETRC_OK; +} + +/* + * Process one parsed token through the netrc state + * machine. Updates the parser state in *ns. + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_handle_token(struct netrc_state *ns, + const char *tok, + const char *host) +{ + switch(ns->state) { + case NOTHING: + if(curl_strequal("macdef", tok)) + ns->state = MACDEF; + else if(curl_strequal("machine", tok)) { + ns->state = HOSTFOUND; + netrc_new_machine(ns); + } + else if(curl_strequal("default", tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + } + break; + case MACDEF: + if(!*tok) + ns->state = NOTHING; + break; + case HOSTFOUND: + if(curl_strequal(host, tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + } + else + ns->state = NOTHING; + break; + case HOSTVALID: + return netrc_hostvalid(ns, tok); + } + return NETRC_OK; +} + +/* + * Finalize the parse result: fill in defaults and free + * resources on error. + */ +static NETRCcode netrc_finalize(struct netrc_state *ns, + char **loginp, + char **passwordp, + struct store_netrc *store) +{ + NETRCcode retcode = ns->retcode; + if(!retcode) { + if(!ns->password && ns->our_login) { + /* success without a password, set a blank one */ + ns->password = curlx_strdup(""); + if(!ns->password) + retcode = NETRC_OUT_OF_MEMORY; + } + else if(!ns->login && !ns->password) + /* a default with no credentials */ + retcode = NETRC_NO_MATCH; + } + if(!retcode) { + /* success */ + if(!ns->specific_login) + *loginp = ns->login; + + /* netrc_finalize() can return a password even when specific_login is set + but our_login is false (e.g., host matched but the requested login + never matched). See test 685. */ + *passwordp = ns->password; + } + else { + curlx_dyn_free(&store->filebuf); + store->loaded = FALSE; + if(!ns->specific_login) + curlx_free(ns->login); + curlx_free(ns->password); + } + return retcode; } /* @@ -108,23 +402,20 @@ */ static NETRCcode parsenetrc(struct store_netrc *store, const char *host, - char **loginp, /* might point to a username */ + char **loginp, char **passwordp, const char *netrcfile) { - NETRCcode retcode = NETRC_NO_MATCH; - char *login = *loginp; - char *password = NULL; - bool specific_login = !!login; /* points to something */ - enum host_lookup_state state = NOTHING; - enum found_state keyword = NONE; - unsigned char found = 0; /* login + password found bits, as they can come in - any order */ - bool our_login = FALSE; /* found our login name */ - bool done = FALSE; - char *netrcbuffer; + const char *netrcbuffer; struct dynbuf token; struct dynbuf *filebuf = &store->filebuf; + struct netrc_state ns; + + memset(&ns, 0, sizeof(ns)); + ns.retcode = NETRC_NO_MATCH; + ns.login = *loginp; + ns.specific_login = !!ns.login; + DEBUGASSERT(!*passwordp); curlx_dyn_init(&token, MAX_NETRC_TOKEN); @@ -137,196 +428,33 @@ netrcbuffer = curlx_dyn_ptr(filebuf); - while(!done) { + while(!ns.done) { const char *tok = netrcbuffer; - while(tok && !done) { + while(tok && !ns.done) { const char *tok_end; - bool quoted; - curlx_dyn_reset(&token); - curlx_str_passblanks(&tok); - /* tok is first non-space letter */ - if(state == MACDEF) { - if((*tok == '\n') || (*tok == '\r')) - state = NOTHING; /* end of macro definition */ + bool lineend; + NETRCcode ret; + + ret = netrc_get_token(&tok, &tok_end, &token, &ns.state, &lineend); + if(ret) { + ns.retcode = ret; + goto out; } - - if(!*tok || (*tok == '\n')) - /* end of line */ + if(lineend) break; - /* leading double-quote means quoted string */ - quoted = (*tok == '\"'); - - tok_end = tok; - if(!quoted) { - size_t len = 0; - CURLcode result; - while(*tok_end > ' ') { - tok_end++; - len++; - } - if(!len) { - retcode = NETRC_SYNTAX_ERROR; - goto out; - } - result = curlx_dyn_addn(&token, tok, len); - if(result) { - retcode = curl2netrc(result); - goto out; - } + ret = netrc_handle_token(&ns, tok, host); + if(ret) { + ns.retcode = ret; + goto out; } - else { - bool escape = FALSE; - bool endquote = FALSE; - tok_end++; /* pass the leading quote */ - while(*tok_end) { - CURLcode result; - char s = *tok_end; - if(escape) { - escape = FALSE; - switch(s) { - case 'n': - s = '\n'; - break; - case 'r': - s = '\r'; - break; - case 't': - s = '\t'; - break; - } - } - else if(s == '\\') { - escape = TRUE; - tok_end++; - continue; - } - else if(s == '\"') { - tok_end++; /* pass the ending quote */ - endquote = TRUE; - break; - } - result = curlx_dyn_addn(&token, &s, 1); - if(result) { - retcode = curl2netrc(result); - goto out; - } - tok_end++; - } - if(escape || !endquote) { - /* bad syntax, get out */ - retcode = NETRC_SYNTAX_ERROR; - goto out; - } - } - - if(curlx_dyn_len(&token)) - tok = curlx_dyn_ptr(&token); - else - /* since tok might actually be NULL for no content, set it to blank - to avoid having to deal with it being NULL */ - tok = ""; - - switch(state) { - case NOTHING: - if(curl_strequal("macdef", tok)) - /* Define a macro. A macro is defined with the specified name; its - contents begin with the next .netrc line and continue until a - null line (consecutive new-line characters) is encountered. */ - state = MACDEF; - else if(curl_strequal("machine", tok)) { - /* the next tok is the machine name, this is in itself the delimiter - that starts the stuff entered for this machine, after this we - need to search for 'login' and 'password'. */ - state = HOSTFOUND; - keyword = NONE; - found = 0; - our_login = FALSE; - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - else if(curl_strequal("default", tok)) { - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - } - break; - case MACDEF: - if(!*tok) - state = NOTHING; - break; - case HOSTFOUND: - if(curl_strequal(host, tok)) { - /* and yes, this is our host! */ - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - } - else - /* not our host */ - state = NOTHING; - break; - case HOSTVALID: - /* we are now parsing sub-keywords concerning "our" host */ - if(keyword == LOGIN) { - if(specific_login) - our_login = !Curl_timestrcmp(login, tok); - else { - our_login = TRUE; - curlx_free(login); - login = curlx_strdup(tok); - if(!login) { - retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */ - goto out; - } - } - found |= FOUND_LOGIN; - keyword = NONE; - } - else if(keyword == PASSWORD) { - curlx_free(password); - password = curlx_strdup(tok); - if(!password) { - retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */ - goto out; - } - if(!specific_login || our_login) - found |= FOUND_PASSWORD; - keyword = NONE; - } - else if(curl_strequal("login", tok)) - keyword = LOGIN; - else if(curl_strequal("password", tok)) - keyword = PASSWORD; - else if(curl_strequal("machine", tok)) { - /* a new machine here */ - if(found & FOUND_PASSWORD) { - done = TRUE; - break; - } - state = HOSTFOUND; - keyword = NONE; - found = 0; - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - else if(curl_strequal("default", tok)) { - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - if((found == (FOUND_PASSWORD | FOUND_LOGIN)) && our_login) { - done = TRUE; - break; - } - break; - } /* switch (state) */ + /* tok_end cannot point to a null byte here since lines are always + newline terminated */ + DEBUGASSERT(*tok_end); tok = ++tok_end; } - if(!done) { - char *nl = NULL; + if(!ns.done) { + const char *nl = NULL; if(tok) nl = strchr(tok, '\n'); if(!nl) @@ -338,32 +466,7 @@ out: curlx_dyn_free(&token); - if(!retcode) { - if(!password && our_login) { - /* success without a password, set a blank one */ - password = curlx_strdup(""); - if(!password) - retcode = NETRC_OUT_OF_MEMORY; /* out of memory */ - } - else if(!login && !password) - /* a default with no credentials */ - retcode = NETRC_NO_MATCH; - } - if(!retcode) { - /* success */ - if(!specific_login) - *loginp = login; - *passwordp = password; - } - else { - curlx_dyn_free(filebuf); - store->loaded = FALSE; - if(!specific_login) - curlx_free(login); - curlx_free(password); - } - - return retcode; + return netrc_finalize(&ns, loginp, passwordp, store); } const char *Curl_netrc_strerror(NETRCcode ret) @@ -464,14 +567,14 @@ return retcode; } -void Curl_netrc_init(struct store_netrc *s) +void Curl_netrc_init(struct store_netrc *store) { - curlx_dyn_init(&s->filebuf, MAX_NETRC_FILE); - s->loaded = FALSE; + curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE); + store->loaded = FALSE; } -void Curl_netrc_cleanup(struct store_netrc *s) +void Curl_netrc_cleanup(struct store_netrc *store) { - curlx_dyn_free(&s->filebuf); - s->loaded = FALSE; + curlx_dyn_free(&store->filebuf); + store->loaded = FALSE; } #endif
diff --git a/Utilities/cmcurl/lib/netrc.h b/Utilities/cmcurl/lib/netrc.h index 99ddc62..90318c2 100644 --- a/Utilities/cmcurl/lib/netrc.h +++ b/Utilities/cmcurl/lib/netrc.h
@@ -45,12 +45,12 @@ } NETRCcode; const char *Curl_netrc_strerror(NETRCcode ret); -void Curl_netrc_init(struct store_netrc *s); -void Curl_netrc_cleanup(struct store_netrc *s); +void Curl_netrc_init(struct store_netrc *store); +void Curl_netrc_cleanup(struct store_netrc *store); -NETRCcode Curl_parsenetrc(struct store_netrc *s, const char *host, +NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, char **loginp, char **passwordp, - const char *filename); + const char *netrcfile); /* Assume: (*passwordp)[0]=0, host[0] != 0. * If (*loginp)[0] = 0, search for login and password within a machine * section in the netrc. @@ -58,7 +58,6 @@ */ #else /* disabled */ -#define Curl_parsenetrc(a, b, c, d, e, f) 1 #define Curl_netrc_init(x) #define Curl_netrc_cleanup(x) #endif
diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c index 541765b..05c59a0 100644 --- a/Utilities/cmcurl/lib/noproxy.c +++ b/Utilities/cmcurl/lib/noproxy.c
@@ -38,12 +38,17 @@ #endif /* - * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the + * cidr4_match() returns TRUE if the given IPv4 address is within the * specified CIDR address range. + * + * @unittest 1614 */ -UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits) +UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits); +UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits) { unsigned int address = 0; unsigned int check = 0; @@ -74,9 +79,11 @@ return address == check; } -UNITTEST bool Curl_cidr6_match(const char *ipv6, - const char *network, - unsigned int bits) +/* @unittest 1614 */ +UNITTEST bool cidr6_match(const char *ipv6, const char *network, + unsigned int bits); +UNITTEST bool cidr6_match(const char *ipv6, const char *network, + unsigned int bits) { #ifdef USE_IPV6 unsigned int bytes; @@ -148,7 +155,6 @@ static bool match_ip(int type, const char *token, size_t tokenlen, const char *name) { - const char *check = token; char *slash; unsigned int bits = 0; char checkip[128]; @@ -156,11 +162,10 @@ /* this cannot match */ return FALSE; /* copy the check name to a temp buffer */ - memcpy(checkip, check, tokenlen); + memcpy(checkip, token, tokenlen); checkip[tokenlen] = 0; - check = checkip; - slash = strchr(check, '/'); + slash = strchr(checkip, '/'); /* if the slash is part of this token, use it */ if(slash) { curl_off_t value; @@ -172,9 +177,9 @@ *slash = 0; /* null-terminate there */ } if(type == TYPE_IPV6) - return Curl_cidr6_match(name, check, bits); + return cidr6_match(name, checkip, bits); else - return Curl_cidr4_match(name, check, bits); + return cidr4_match(name, checkip, bits); } /**************************************************************** @@ -203,7 +208,7 @@ if(!strcmp("*", no_proxy)) return TRUE; - /* NO_PROXY was specified and it was not just an asterisk */ + /* NO_PROXY was specified and it was not only an asterisk */ /* Check if name is an IP address; if not, assume it being a hostname. */ namelen = strlen(name); @@ -253,7 +258,7 @@ while(*p == ',') p++; } /* while(*p) */ - } /* NO_PROXY was specified and it was not just an asterisk */ + } /* NO_PROXY was specified and it was not only an asterisk */ return FALSE; }
diff --git a/Utilities/cmcurl/lib/noproxy.h b/Utilities/cmcurl/lib/noproxy.h index 71ae7ea..e16c139 100644 --- a/Utilities/cmcurl/lib/noproxy.h +++ b/Utilities/cmcurl/lib/noproxy.h
@@ -26,17 +26,6 @@ #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY - -#ifdef UNITTESTS - -UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits); -UNITTEST bool Curl_cidr6_match(const char *ipv6, - const char *network, - unsigned int bits); -#endif - bool Curl_check_noproxy(const char *name, const char *no_proxy); #endif
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index f826a4d..48bf5b7 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c
@@ -91,98 +91,8 @@ LDAP **ld); #endif -static CURLcode oldap_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode oldap_do(struct Curl_easy *data, bool *done); -static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool); -static CURLcode oldap_connect(struct Curl_easy *data, bool *done); -static CURLcode oldap_connecting(struct Curl_easy *data, bool *done); -static CURLcode oldap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); - -static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out); - static Curl_recv oldap_recv; -/* - * LDAP protocol handler. - */ -const struct Curl_handler Curl_handler_ldap = { - "ldap", /* scheme */ - oldap_setup_connection, /* setup_connection */ - oldap_do, /* do_it */ - oldap_done, /* done */ - ZERO_NULL, /* do_more */ - oldap_connect, /* connect_it */ - oldap_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - oldap_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_LDAP, /* defport */ - CURLPROTO_LDAP, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL_REUSE | /* flags */ - PROTOPT_CONN_REUSE -}; - -#ifdef USE_SSL -/* - * LDAPS protocol handler. - */ -const struct Curl_handler Curl_handler_ldaps = { - "ldaps", /* scheme */ - oldap_setup_connection, /* setup_connection */ - oldap_do, /* do_it */ - oldap_done, /* done */ - ZERO_NULL, /* do_more */ - oldap_connect, /* connect_it */ - oldap_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - oldap_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_LDAPS, /* defport */ - CURLPROTO_LDAPS, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL | /* flags */ - PROTOPT_CONN_REUSE -}; -#endif - -/* SASL parameters for the ldap protocol */ -static const struct SASLproto saslldap = { - "ldap", /* The service name */ - oldap_perform_auth, /* Send authentication command */ - oldap_continue_auth, /* Send authentication continuation */ - oldap_cancel_auth, /* Send authentication cancellation */ - oldap_get_message, /* Get SASL response message */ - 0, /* Maximum initial response length (no max) */ - LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ - LDAP_SUCCESS, /* Code to receive upon authentication success */ - SASL_AUTH_NONE, /* Default mechanisms */ - 0 /* Configuration flags */ -}; - struct ldapconninfo { struct SASL sasl; /* SASL-related parameters */ LDAP *ld; /* Openldap connection handle. */ @@ -204,7 +114,6 @@ /* meta key for storing ldapconninfo at connection */ #define CURL_META_LDAP_CONN "meta:proto:ldap:conn" - /* * oldap_state() * @@ -213,7 +122,7 @@ static void oldap_state(struct Curl_easy *data, struct ldapconninfo *li, ldapstate newstate) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* for debug purposes */ static const char * const names[] = { "STOP", @@ -230,8 +139,9 @@ if(li->state != newstate) infof(data, "LDAP %p state change from %s to %s", (void *)li, names[li->state], names[newstate]); -#endif +#else (void)data; +#endif li->state = newstate; } @@ -426,7 +336,7 @@ { struct connectdata *conn = data->conn; struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - char *binddn = NULL; + const char *binddn = NULL; struct berval passwd; int rc; @@ -496,14 +406,86 @@ } #ifdef USE_SSL -static int ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg); -static int ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod); -static int ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg); +static int ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) +{ + sbiod->sbiod_pvt = arg; + return 0; +} + +static int ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) +{ + sbiod->sbiod_pvt = NULL; + return 0; +} + +/* We do not need to do anything because libcurl does it already */ +static int ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) +{ + (void)sbiod; + return 0; +} + +static int ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) +{ + (void)arg; + if(opt == LBER_SB_OPT_DATA_READY) { + struct Curl_easy *data = sbiod->sbiod_pvt; + return Curl_conn_data_pending(data, FIRSTSOCKET); + } + return 0; +} + static ber_slen_t ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len); + ber_len_t len) +{ + struct Curl_easy *data = sbiod->sbiod_pvt; + ber_slen_t ret = 0; + if(data) { + struct connectdata *conn = data->conn; + if(conn) { + struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + CURLcode result = CURLE_RECV_ERROR; + size_t nread; + + if(!li) { + SET_SOCKERRNO(SOCKEINVAL); + return -1; + } + result = (li->recv)(data, FIRSTSOCKET, buf, len, &nread); + if(result == CURLE_AGAIN) { + SET_SOCKERRNO(SOCKEWOULDBLOCK); + } + ret = result ? -1 : (ber_slen_t)nread; + } + } + return ret; +} + static ber_slen_t ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len); -static int ldapsb_tls_close(Sockbuf_IO_Desc *sbiod); + ber_len_t len) +{ + struct Curl_easy *data = sbiod->sbiod_pvt; + ber_slen_t ret = 0; + if(data) { + struct connectdata *conn = data->conn; + if(conn) { + struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + CURLcode result = CURLE_SEND_ERROR; + size_t nwritten; + + if(!li) { + SET_SOCKERRNO(SOCKEINVAL); + return -1; + } + result = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten); + if(result == CURLE_AGAIN) { + SET_SOCKERRNO(SOCKEWOULDBLOCK); + } + ret = result ? -1 : (ber_slen_t)nwritten; + } + } + return ret; +} static Sockbuf_IO ldapsb_tls = { ldapsb_tls_setup, @@ -585,6 +567,20 @@ curlx_free(li); } +/* SASL parameters for the ldap protocol */ +static const struct SASLproto saslldap = { + "ldap", /* The service name */ + oldap_perform_auth, /* Send authentication command */ + oldap_continue_auth, /* Send authentication continuation */ + oldap_cancel_auth, /* Send authentication cancellation */ + oldap_get_message, /* Get SASL response message */ + 0, /* Maximum initial response length (no max) */ + LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ + LDAP_SUCCESS, /* Code to receive upon authentication success */ + SASL_AUTH_NONE, /* Default mechanisms */ + 0 /* Configuration flags */ +}; + static CURLcode oldap_connect(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; @@ -618,11 +614,10 @@ if(result) goto out; - hosturl = curl_maprintf("%s://%s%s%s:%d", - conn->handler->scheme, - conn->bits.ipv6_ip ? "[" : "", - conn->host.name, - conn->bits.ipv6_ip ? "]" : "", + hosturl = curl_maprintf("%s://%s:%d", + conn->scheme->name, + (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name, conn->remote_port); if(!hosturl) { result = CURLE_OUT_OF_MEMORY; @@ -698,13 +693,19 @@ static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, LDAPMessage *msg, int code) { - struct connectdata *conn = data->conn; - struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + struct connectdata *conn; + struct ldapconninfo *li; int rc; BerElement *ber = NULL; CURLcode result = CURLE_OK; struct berval bv, *bvals; + if(!data) + return CURLE_FAILED_INIT; + + conn = data->conn; + li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + if(!li) return CURLE_FAILED_INIT; switch(ldap_msgtype(msg)) { @@ -727,8 +728,9 @@ if(bvals) { for(i = 0; bvals[i].bv_val; i++) { size_t llen; - unsigned short mech = Curl_sasl_decode_mech((char *)bvals[i].bv_val, - bvals[i].bv_len, &llen); + unsigned short mech = + Curl_sasl_decode_mech((const char *)bvals[i].bv_val, + bvals[i].bv_len, &llen); if(bvals[i].bv_len == llen) li->sasl.authmechs |= mech; } @@ -963,8 +965,7 @@ #ifdef USE_SSL if(ssl_installed(conn)) { Sockbuf *sb; - if((ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS) - || + if(ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS || ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data)) return CURLE_FAILED_INIT; } @@ -1242,88 +1243,6 @@ return result; } -#ifdef USE_SSL -static int ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) -{ - sbiod->sbiod_pvt = arg; - return 0; -} - -static int ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) -{ - sbiod->sbiod_pvt = NULL; - return 0; -} - -/* We do not need to do anything because libcurl does it already */ -static int ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) -{ - (void)sbiod; - return 0; -} - -static int ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) -{ - (void)arg; - if(opt == LBER_SB_OPT_DATA_READY) { - struct Curl_easy *data = sbiod->sbiod_pvt; - return Curl_conn_data_pending(data, FIRSTSOCKET); - } - return 0; -} - -static ber_slen_t ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len) -{ - struct Curl_easy *data = sbiod->sbiod_pvt; - ber_slen_t ret = 0; - if(data) { - struct connectdata *conn = data->conn; - if(conn) { - struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - CURLcode err = CURLE_RECV_ERROR; - size_t nread; - - if(!li) { - SET_SOCKERRNO(SOCKEINVAL); - return -1; - } - err = (li->recv)(data, FIRSTSOCKET, buf, len, &nread); - if(err == CURLE_AGAIN) { - SET_SOCKERRNO(SOCKEWOULDBLOCK); - } - ret = err ? -1 : (ber_slen_t)nread; - } - } - return ret; -} -static ber_slen_t ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len) -{ - struct Curl_easy *data = sbiod->sbiod_pvt; - ber_slen_t ret = 0; - if(data) { - struct connectdata *conn = data->conn; - if(conn) { - struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - CURLcode err = CURLE_SEND_ERROR; - size_t nwritten; - - if(!li) { - SET_SOCKERRNO(SOCKEINVAL); - return -1; - } - err = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten); - if(err == CURLE_AGAIN) { - SET_SOCKERRNO(SOCKEWOULDBLOCK); - } - ret = err ? -1 : (ber_slen_t)nwritten; - } - } - return ret; -} -#endif /* USE_SSL */ - void Curl_ldap_version(char *buf, size_t bufsz) { LDAPAPIInfo api; @@ -1333,7 +1252,7 @@ unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); unsigned int minor = - (((unsigned int)api.ldapai_vendor_version - major * 10000) + (((unsigned int)api.ldapai_vendor_version - (major * 10000)) - patch) / 100; curl_msnprintf(buf, bufsz, "%s/%u.%u.%u", api.ldapai_vendor_name, major, minor, patch); @@ -1344,4 +1263,29 @@ curl_msnprintf(buf, bufsz, "OpenLDAP"); } +/* + * LDAP protocol handler. + */ +const struct Curl_protocol Curl_protocol_ldap = { + oldap_setup_connection, /* setup_connection */ + oldap_do, /* do_it */ + oldap_done, /* done */ + ZERO_NULL, /* do_more */ + oldap_connect, /* connect_it */ + oldap_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + oldap_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */ + +/* The LDAP scheme structs are in ldap.c */
diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c index 9b4c483..ce358bd 100644 --- a/Utilities/cmcurl/lib/parsedate.c +++ b/Utilities/cmcurl/lib/parsedate.c
@@ -25,6 +25,7 @@ #include "parsedate.h" #include "curlx/strparse.h" +#include "curlx/strcopy.h" /* A brief summary of the date string formats this parser groks: @@ -80,26 +81,6 @@ */ -/* - * parsedate() - * - * Returns: - * - * PARSEDATE_OK - a fine conversion - * PARSEDATE_FAIL - failed to convert - * PARSEDATE_LATER - time overflow at the far end of time_t - * PARSEDATE_SOONER - time underflow at the low end of time_t - */ - -static int parsedate(const char *date, time_t *output); - -#define PARSEDATE_OK 0 -#define PARSEDATE_FAIL -1 -#define PARSEDATE_LATER 1 -#if defined(HAVE_TIME_T_UNSIGNED) || (SIZEOF_TIME_T < 5) -#define PARSEDATE_SOONER 2 -#endif - #if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \ !defined(CURL_DISABLE_FILE) || defined(USE_GNUTLS) /* These names are also used by FTP and FILE code */ @@ -112,96 +93,97 @@ }; #endif +#define PARSEDATE_OK 0 +#define PARSEDATE_FAIL (-1) + #ifndef CURL_DISABLE_PARSEDATE + +#define PARSEDATE_LATER 1 +#define PARSEDATE_SOONER 2 + static const char * const weekday[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; struct tzinfo { char name[5]; - int offset; /* +/- in minutes */ + int16_t offset; /* +/- in minutes */ }; -/* Here's a bunch of frequently used time zone names. These were supported - by the old getdate parser. */ -#define tDAYZONE -60 /* offset for daylight savings time */ +#define tDAYZONE (-60) /* offset for daylight savings time */ + +/* alpha-sorted list of time zones */ static const struct tzinfo tz[] = { - { "GMT", 0 }, /* Greenwich Mean */ - { "UT", 0 }, /* Universal Time */ - { "UTC", 0 }, /* Universal (Coordinated) */ - { "WET", 0 }, /* Western European */ - { "BST", 0 tDAYZONE }, /* British Summer */ - { "WAT", 60 }, /* West Africa */ - { "AST", 240 }, /* Atlantic Standard */ - { "ADT", 240 tDAYZONE }, /* Atlantic Daylight */ - { "EST", 300 }, /* Eastern Standard */ - { "EDT", 300 tDAYZONE }, /* Eastern Daylight */ - { "CST", 360 }, /* Central Standard */ - { "CDT", 360 tDAYZONE }, /* Central Daylight */ - { "MST", 420 }, /* Mountain Standard */ - { "MDT", 420 tDAYZONE }, /* Mountain Daylight */ - { "PST", 480 }, /* Pacific Standard */ - { "PDT", 480 tDAYZONE }, /* Pacific Daylight */ - { "YST", 540 }, /* Yukon Standard */ - { "YDT", 540 tDAYZONE }, /* Yukon Daylight */ - { "HST", 600 }, /* Hawaii Standard */ - { "HDT", 600 tDAYZONE }, /* Hawaii Daylight */ - { "CAT", 600 }, /* Central Alaska */ - { "AHST", 600 }, /* Alaska-Hawaii Standard */ - { "NT", 660 }, /* Nome */ /* spellchecker:disable-line */ - { "IDLW", 720 }, /* International Date Line West */ - { "CET", -60 }, /* Central European */ - { "MET", -60 }, /* Middle European */ - { "MEWT", -60 }, /* Middle European Winter */ - { "MEST", -60 tDAYZONE }, /* Middle European Summer */ - { "CEST", -60 tDAYZONE }, /* Central European Summer */ - { "MESZ", -60 tDAYZONE }, /* Middle European Summer */ - { "FWT", -60 }, /* French Winter */ - { "FST", -60 tDAYZONE }, /* French Summer */ - { "EET", -120 }, /* Eastern Europe, USSR Zone 1 */ + { "A", -1 * 60 }, /* Alpha */ + { "ADT", 240 + tDAYZONE }, /* Atlantic Daylight */ + { "AHST", 600 }, /* Alaska-Hawaii Standard */ + { "AST", 240 }, /* Atlantic Standard */ + { "B", -2 * 60 }, /* Bravo */ + { "BST", 0 + tDAYZONE }, /* British Summer */ + { "C", -3 * 60 }, /* Charlie */ + { "CAT", 600 }, /* Central Alaska */ + { "CCT", -480 }, /* China Coast, USSR Zone 7 */ + { "CDT", 360 + tDAYZONE }, /* Central Daylight */ + { "CEST", -60 + tDAYZONE }, /* Central European Summer */ + { "CET", -60 }, /* Central European */ + { "CST", 360 }, /* Central Standard */ + { "D", -4 * 60 }, /* Delta */ + { "E", -5 * 60 }, /* Echo */ + { "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */ + { "EAST", -600 }, /* Eastern Australian Standard */ + { "EDT", 300 + tDAYZONE }, /* Eastern Daylight */ + { "EET", -120 }, /* Eastern Europe, USSR Zone 1 */ + { "EST", 300 }, /* Eastern Standard */ + { "F", -6 * 60 }, /* Foxtrot */ + { "FST", -60 + tDAYZONE }, /* French Summer */ + { "FWT", -60 }, /* French Winter */ + { "G", -7 * 60 }, /* Golf */ + { "GMT", 0 }, /* Greenwich Mean */ + { "GST", -600 }, /* Guam Standard, USSR Zone 9 */ + { "H", -8 * 60 }, /* Hotel */ + { "HDT", 600 + tDAYZONE }, /* Hawaii Daylight */ + { "HST", 600 }, /* Hawaii Standard */ + { "I", -9 * 60 }, /* India */ + { "IDLE", -720 }, /* International Date Line East */ + { "IDLW", 720 }, /* International Date Line West */ + { "JST", -540 }, /* Japan Standard, USSR Zone 8 */ + { "K", -10 * 60 }, /* Kilo */ + { "L", -11 * 60 }, /* Lima */ + { "M", -12 * 60 }, /* Mike */ + { "MDT", 420 + tDAYZONE }, /* Mountain Daylight */ + { "MEST", -60 + tDAYZONE }, /* Middle European Summer */ + { "MESZ", -60 + tDAYZONE }, /* Middle European Summer */ + { "MET", -60 }, /* Middle European */ + { "MEWT", -60 }, /* Middle European Winter */ + { "MST", 420 }, /* Mountain Standard */ + { "N", 60 }, /* November */ + { "NT", 660 }, /* Nome */ /* spellchecker:disable-line */ + { "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */ + { "NZST", -720 }, /* New Zealand Standard */ + { "NZT", -720 }, /* New Zealand */ + { "O", 2 * 60 }, /* Oscar */ + { "P", 3 * 60 }, /* Papa */ + { "PDT", 480 + tDAYZONE }, /* Pacific Daylight */ + { "PST", 480 }, /* Pacific Standard */ + { "Q", 4 * 60 }, /* Quebec */ + { "R", 5 * 60 }, /* Romeo */ + { "S", 6 * 60 }, /* Sierra */ + { "T", 7 * 60 }, /* Tango */ + { "U", 8 * 60 }, /* Uniform */ + { "UT", 0 }, /* Universal Time */ + { "UTC", 0 }, /* Universal (Coordinated) */ + { "V", 9 * 60 }, /* Victor */ + { "W", 10 * 60 }, /* Whiskey */ + { "WADT", -420 + tDAYZONE }, /* West Australian Daylight */ { "WAST", -420 }, /* spellchecker:disable-line */ - /* West Australian Standard */ - { "WADT", -420 tDAYZONE }, /* West Australian Daylight */ - { "CCT", -480 }, /* China Coast, USSR Zone 7 */ - { "JST", -540 }, /* Japan Standard, USSR Zone 8 */ - { "EAST", -600 }, /* Eastern Australian Standard */ - { "EADT", -600 tDAYZONE }, /* Eastern Australian Daylight */ - { "GST", -600 }, /* Guam Standard, USSR Zone 9 */ - { "NZT", -720 }, /* New Zealand */ - { "NZST", -720 }, /* New Zealand Standard */ - { "NZDT", -720 tDAYZONE }, /* New Zealand Daylight */ - { "IDLE", -720 }, /* International Date Line East */ - /* Next up: Military timezone names. RFC822 allowed these, but (as noted in - RFC 1123) had their signs wrong. Here we use the correct signs to match - actual military usage. - */ - { "A", 1 * 60 }, /* Alpha */ - { "B", 2 * 60 }, /* Bravo */ - { "C", 3 * 60 }, /* Charlie */ - { "D", 4 * 60 }, /* Delta */ - { "E", 5 * 60 }, /* Echo */ - { "F", 6 * 60 }, /* Foxtrot */ - { "G", 7 * 60 }, /* Golf */ - { "H", 8 * 60 }, /* Hotel */ - { "I", 9 * 60 }, /* India */ - /* "J", Juliet is not used as a timezone, to indicate the observer's local - time */ - { "K", 10 * 60 }, /* Kilo */ - { "L", 11 * 60 }, /* Lima */ - { "M", 12 * 60 }, /* Mike */ - { "N", -1 * 60 }, /* November */ - { "O", -2 * 60 }, /* Oscar */ - { "P", -3 * 60 }, /* Papa */ - { "Q", -4 * 60 }, /* Quebec */ - { "R", -5 * 60 }, /* Romeo */ - { "S", -6 * 60 }, /* Sierra */ - { "T", -7 * 60 }, /* Tango */ - { "U", -8 * 60 }, /* Uniform */ - { "V", -9 * 60 }, /* Victor */ - { "W", -10 * 60 }, /* Whiskey */ - { "X", -11 * 60 }, /* X-ray */ - { "Y", -12 * 60 }, /* Yankee */ - { "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */ + /* West Australian Standard */ + { "WAT", 60 }, /* West Africa */ + { "WET", 0 }, /* Western European */ + { "X", 11 * 60 }, /* X-ray */ + { "Y", 12 * 60 }, /* Yankee */ + { "YDT", 540 + tDAYZONE }, /* Yukon Daylight */ + { "YST", 540 }, /* Yukon Standard */ + { "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */ }; /* returns: @@ -244,22 +226,24 @@ return -1; /* return the offset or -1, no real offset is -1 */ } -/* return the time zone offset between GMT and the input one, in number - of seconds or -1 if the timezone was not found/legal */ +static int tzcompare(const void *m1, const void *m2) +{ + const struct tzinfo *tz1 = m1; + const struct tzinfo *tz2 = m2; + return strcmp(tz1->name, tz2->name); +} +/* return the time zone offset between GMT and the input one, in number of + seconds or -1 if the timezone was not found/legal */ static int checktz(const char *check, size_t len) { - unsigned int i; - const struct tzinfo *what = tz; - if(len > 4) /* longer than any valid timezone */ - return -1; - - for(i = 0; i < CURL_ARRAYSIZE(tz); i++) { - size_t ilen = strlen(what->name); - if((ilen == len) && - curl_strnequal(check, what->name, len)) + if(len <= 4) { + const struct tzinfo *what; + struct tzinfo find; + curlx_strcopy(find.name, sizeof(find.name), check, len); + what = bsearch(&find, tz, CURL_ARRAYSIZE(tz), sizeof(tz[0]), tzcompare); + if(what) return what->offset * 60; - what++; } return -1; } @@ -271,28 +255,42 @@ (*date)++; } +/* each field is exactly -1 when unknown */ +struct when { + int wday; /* day of the week, 0-6 (mon-sun) */ + int mon; /* month of the year, 0-11 */ + int mday; /* day of month, 1 - 31 */ + int hour; /* hour of day, 0 - 23 */ + int min; /* minute of hour, 0 - 59 */ + int sec; /* second of minute, 0 - 60 (leap second) */ + int year; /* year, >= 1583 */ + int tzoff; /* time zone offset in seconds */ +}; + enum assume { DATE_MDAY, DATE_YEAR, DATE_TIME }; +/* (1969 / 4) - (1969 / 100) + (1969 / 400) = 492 - 19 + 4 = 477 */ +#define LEAP_DAYS_BEFORE_1969 477 + /* * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to * mktime but for GMT only. */ -static time_t time2epoch(int sec, int min, int hour, - int mday, int mon, int year) +static curl_off_t time2epoch(struct when *w) { - static const int month_days_cumulative[12] = { + static const int cumulative_days[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - int leap_days = year - (mon <= 1); - leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400) - - (1969 / 4) + (1969 / 100) - (1969 / 400)); - return ((((time_t)(year - 1970) * 365 - + leap_days + month_days_cumulative[mon] + mday - 1) * 24 - + hour) * 60 + min) * 60 + sec; + int y = w->year - (w->mon <= 1); + int leap_days = (y / 4) - (y / 100) + (y / 400) - LEAP_DAYS_BEFORE_1969; + curl_off_t days = (curl_off_t)(w->year - 1970) * 365 + leap_days + + cumulative_days[w->mon] + w->mday - 1; + + return (((days * 24 + w->hour) * 60 + w->min) * 60) + w->sec; } /* Returns the value of a single-digit or two-digit decimal number, return @@ -303,15 +301,14 @@ int num = date[0] - '0'; if(ISDIGIT(date[1])) { *endp = &date[2]; - return num * 10 + (date[1] - '0'); + return (num * 10) + (date[1] - '0'); } *endp = &date[1]; return num; } /* HH:MM:SS or HH:MM and accept single-digits too */ -static bool match_time(const char *date, - int *h, int *m, int *s, char **endp) +static bool match_time(const char *date, struct when *w, char **endp) { const char *p; int hh, mm, ss = 0; @@ -334,9 +331,9 @@ } return FALSE; /* not a time string */ match: - *h = hh; - *m = mm; - *s = ss; + w->hour = hh; + w->min = mm; + w->sec = ss; *endp = (char *)CURL_UNCONST(p); return TRUE; } @@ -355,209 +352,221 @@ /* Wednesday is the longest name this parser knows about */ #define NAME_LEN 12 +static void initwhen(struct when *w) +{ + w->wday = w->mon = w->mday = w->hour = w->min = w->sec = w->year = w->tzoff = + -1; +} + +static int datestring(const char **datep, struct when *w) +{ + /* a name coming up */ + size_t len = 0; + const char *p = *datep; + bool found = FALSE; + while(ISALPHA(*p) && (len < NAME_LEN)) { + p++; + len++; + } + + if(len != NAME_LEN) { + if(w->wday == -1) { + w->wday = checkday(*datep, len); + if(w->wday != -1) + found = TRUE; + } + if(!found && (w->mon == -1)) { + w->mon = checkmonth(*datep, len); + if(w->mon != -1) + found = TRUE; + } + + if(!found && (w->tzoff == -1)) { + /* this must be a time zone string */ + w->tzoff = checktz(*datep, len); + if(w->tzoff != -1) + found = TRUE; + } + } + if(!found) + return PARSEDATE_FAIL; /* bad string */ + + *datep += len; + return PARSEDATE_OK; +} + +static int datenum(const char *indate, const char **datep, struct when *w, + enum assume *dignextp) +{ + /* a digit */ + unsigned int val; + char *end; + const char *date = *datep; + enum assume dignext = *dignextp; + + if((w->sec == -1) && match_time(date, w, &end)) { + /* time stamp */ + date = end; + } + else { + bool found = FALSE; + curl_off_t lval; + int num_digits = 0; + const char *p = *datep; + if(curlx_str_number(&p, &lval, 99999999)) + return PARSEDATE_FAIL; + + /* we know num_digits cannot be larger than 8 */ + num_digits = (int)(p - *datep); + val = (unsigned int)lval; + + if((w->tzoff == -1) && + (num_digits == 4) && + (val <= 1400) && + (indate < date) && + (date[-1] == '+' || date[-1] == '-')) { + /* four digits and a value less than or equal to 1400 (to take into + account all sorts of funny time zone diffs) and it is preceded + with a plus or minus. This is a time zone indication. 1400 is + picked since +1300 is frequently used and +1400 is mentioned as + an edge number in the document "ISO C 200X Proposal: Timezone + Functions" at http://david.tribble.com/text/c0xtimezone.html If + anyone has a more authoritative source for the exact maximum time + zone offsets, please speak up! */ + found = TRUE; + w->tzoff = ((val / 100 * 60) + (val % 100)) * 60; + + /* the + and - prefix indicates the local time compared to GMT, + this we need their reversed math to get what we want */ + w->tzoff = date[-1] == '+' ? -w->tzoff : w->tzoff; + } + + else if((num_digits == 8) && (w->year == -1) && + (w->mon == -1) && (w->mday == -1)) { + /* 8 digits, no year, month or day yet. This is YYYYMMDD */ + found = TRUE; + w->year = val / 10000; + w->mon = ((val % 10000) / 100) - 1; /* month is 0 - 11 */ + w->mday = val % 100; + } + + if(!found && (dignext == DATE_MDAY) && (w->mday == -1)) { + if((val > 0) && (val < 32)) { + w->mday = val; + found = TRUE; + } + dignext = DATE_YEAR; + } + + if(!found && (dignext == DATE_YEAR) && (w->year == -1)) { + w->year = val; + found = TRUE; + if(w->year < 100) { + if(w->year > 70) + w->year += 1900; + else + w->year += 2000; + } + if(w->mday == -1) + dignext = DATE_MDAY; + } + + if(!found) + return PARSEDATE_FAIL; + + date = p; + } + *datep = date; + *dignextp = dignext; + return PARSEDATE_OK; +} + +static int datecheck(struct when *w) +{ + if(w->sec == -1) + w->sec = w->min = w->hour = 0; /* no time, make it zero */ + + if((w->mday == -1) || (w->mon == -1) || (w->year == -1)) + /* lacks vital info, fail */ + return PARSEDATE_FAIL; + + /* The Gregorian calendar was introduced 1582 */ + else if(w->year < 1583) + return PARSEDATE_FAIL; + + else if((w->mday > 31) || (w->mon > 11) || (w->hour > 23) || + (w->min > 59) || (w->sec > 60)) + return PARSEDATE_FAIL; /* clearly an illegal date */ + + return PARSEDATE_OK; +} + +static void tzadjust(curl_off_t *tp, struct when *w) +{ + if(w->tzoff == -1) /* unknown tz means no offset */ + w->tzoff = 0; + + /* Add the time zone diff between local time zone and GMT. */ + if((w->tzoff > 0) && (*tp > (curl_off_t)(CURL_OFF_T_MAX - w->tzoff))) + *tp = CURL_OFF_T_MAX; + else + *tp += w->tzoff; + /* this needs no minimum check since we require a year > 1582 */ +} + +static int mktimet(curl_off_t seconds, time_t *output) +{ +#if SIZEOF_TIME_T < 5 + if(seconds > TIME_T_MAX) { + *output = TIME_T_MAX; + return PARSEDATE_LATER; + } + else if(seconds < TIME_T_MIN) { + *output = TIME_T_MIN; + return PARSEDATE_SOONER; + } +#elif defined(HAVE_TIME_T_UNSIGNED) + if(seconds < 0) { + *output = 0; + return PARSEDATE_SOONER; + } +#endif + *output = (time_t)seconds; + return PARSEDATE_OK; +} + static int parsedate(const char *date, time_t *output) { - time_t t = 0; - int wdaynum = -1; /* day of the week number, 0-6 (mon-sun) */ - int monnum = -1; /* month of the year number, 0-11 */ - int mdaynum = -1; /* day of month, 1 - 31 */ - int hournum = -1; - int minnum = -1; - int secnum = -1; - int yearnum = -1; - int tzoff = -1; + curl_off_t seconds = 0; enum assume dignext = DATE_MDAY; const char *indate = date; /* save the original pointer */ int part = 0; /* max 6 parts */ + int rc = 0; + struct when w; + initwhen(&w); while(*date && (part < 6)) { - bool found = FALSE; - skip(&date); - if(ISALPHA(*date)) { - /* a name coming up */ - size_t len = 0; - const char *p = date; - while(ISALPHA(*p) && (len < NAME_LEN)) { - p++; - len++; - } - - if(len != NAME_LEN) { - if(wdaynum == -1) { - wdaynum = checkday(date, len); - if(wdaynum != -1) - found = TRUE; - } - if(!found && (monnum == -1)) { - monnum = checkmonth(date, len); - if(monnum != -1) - found = TRUE; - } - - if(!found && (tzoff == -1)) { - /* this just must be a time zone string */ - tzoff = checktz(date, len); - if(tzoff != -1) - found = TRUE; - } - } - if(!found) - return PARSEDATE_FAIL; /* bad string */ - - date += len; - } - else if(ISDIGIT(*date)) { - /* a digit */ - unsigned int val; - char *end; - if((secnum == -1) && - match_time(date, &hournum, &minnum, &secnum, &end)) { - /* time stamp */ - date = end; - } - else { - curl_off_t lval; - int num_digits = 0; - const char *p = date; - if(curlx_str_number(&p, &lval, 99999999)) - return PARSEDATE_FAIL; - - /* we know num_digits cannot be larger than 8 */ - num_digits = (int)(p - date); - val = (unsigned int)lval; - - if((tzoff == -1) && - (num_digits == 4) && - (val <= 1400) && - (indate < date) && - ((date[-1] == '+' || date[-1] == '-'))) { - /* four digits and a value less than or equal to 1400 (to take into - account all sorts of funny time zone diffs) and it is preceded - with a plus or minus. This is a time zone indication. 1400 is - picked since +1300 is frequently used and +1400 is mentioned as - an edge number in the document "ISO C 200X Proposal: Timezone - Functions" at http://david.tribble.com/text/c0xtimezone.html If - anyone has a more authoritative source for the exact maximum time - zone offsets, please speak up! */ - found = TRUE; - tzoff = (val / 100 * 60 + val % 100) * 60; - - /* the + and - prefix indicates the local time compared to GMT, - this we need their reversed math to get what we want */ - tzoff = date[-1] == '+' ? -tzoff : tzoff; - } - - else if((num_digits == 8) && - (yearnum == -1) && - (monnum == -1) && - (mdaynum == -1)) { - /* 8 digits, no year, month or day yet. This is YYYYMMDD */ - found = TRUE; - yearnum = val / 10000; - monnum = (val % 10000) / 100 - 1; /* month is 0 - 11 */ - mdaynum = val % 100; - } - - if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { - if((val > 0) && (val < 32)) { - mdaynum = val; - found = TRUE; - } - dignext = DATE_YEAR; - } - - if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { - yearnum = val; - found = TRUE; - if(yearnum < 100) { - if(yearnum > 70) - yearnum += 1900; - else - yearnum += 2000; - } - if(mdaynum == -1) - dignext = DATE_MDAY; - } - - if(!found) - return PARSEDATE_FAIL; - - date = p; - } - } + if(ISALPHA(*date)) + rc = datestring(&date, &w); + else if(ISDIGIT(*date)) + rc = datenum(indate, &date, &w, &dignext); + if(rc) + return rc; part++; } - if(-1 == secnum) - secnum = minnum = hournum = 0; /* no time, make it zero */ + rc = datecheck(&w); + if(rc) + return rc; - if((-1 == mdaynum) || - (-1 == monnum) || - (-1 == yearnum)) - /* lacks vital info, fail */ - return PARSEDATE_FAIL; + seconds = time2epoch(&w); /* get number of seconds */ + tzadjust(&seconds, &w); /* handle the time zone offset */ + rc = mktimet(seconds, output); /* squeeze seconds into a time_t */ -#ifdef HAVE_TIME_T_UNSIGNED - if(yearnum < 1970) { - /* only positive numbers cannot return earlier */ - *output = TIME_T_MIN; - return PARSEDATE_SOONER; - } -#endif - -#if (SIZEOF_TIME_T < 5) - -#ifdef HAVE_TIME_T_UNSIGNED - /* an unsigned 32-bit time_t can only hold dates to 2106 */ - if(yearnum > 2105) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; - } -#else - /* a signed 32-bit time_t can only hold dates to the beginning of 2038 */ - if(yearnum > 2037) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; - } - if(yearnum < 1903) { - *output = TIME_T_MIN; - return PARSEDATE_SOONER; - } -#endif - -#else - /* The Gregorian calendar was introduced 1582 */ - if(yearnum < 1583) - return PARSEDATE_FAIL; -#endif - - if((mdaynum > 31) || (monnum > 11) || - (hournum > 23) || (minnum > 59) || (secnum > 60)) - return PARSEDATE_FAIL; /* clearly an illegal date */ - - /* time2epoch() returns a time_t. time_t is often 32 bits, sometimes even on - architectures that feature a 64 bits 'long' but ultimately time_t is the - correct data type to use. - */ - t = time2epoch(secnum, minnum, hournum, mdaynum, monnum, yearnum); - - /* Add the time zone diff between local time zone and GMT. */ - if(tzoff == -1) - tzoff = 0; - - if((tzoff > 0) && (t > (time_t)(TIME_T_MAX - tzoff))) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; /* time_t overflow */ - } - - t += tzoff; - - *output = t; - - return PARSEDATE_OK; + return rc; } #else /* disabled */ @@ -569,11 +578,11 @@ } #endif -time_t curl_getdate(const char *p, const time_t *now) +time_t curl_getdate(const char *p, const time_t *unused) { time_t parsed = -1; int rc = parsedate(p, &parsed); - (void)now; /* legacy argument from the past that we ignore */ + (void)unused; /* legacy argument from the past that we ignore */ if(rc == PARSEDATE_OK) { if(parsed == (time_t)-1)
diff --git a/Utilities/cmcurl/lib/parsedate.h b/Utilities/cmcurl/lib/parsedate.h index a6ee43a..ea136bd 100644 --- a/Utilities/cmcurl/lib/parsedate.h +++ b/Utilities/cmcurl/lib/parsedate.h
@@ -30,6 +30,6 @@ TIME_T_MAX in case the parsed time value was too big, instead of an error. */ -int Curl_getdate_capped(const char *p, time_t *store); +int Curl_getdate_capped(const char *p, time_t *tp); #endif /* HEADER_CURL_PARSEDATE_H */
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c index 5af5305..44e4244 100644 --- a/Utilities/cmcurl/lib/pingpong.c +++ b/Utilities/cmcurl/lib/pingpong.c
@@ -27,48 +27,33 @@ #include "curl_setup.h" #include "urldata.h" -#include "cfilters.h" -#include "connect.h" -#include "sendf.h" -#include "curl_trc.h" -#include "select.h" -#include "progress.h" #include "pingpong.h" #ifdef USE_PINGPONG -/* Returns timeout in ms. 0 or negative number means the timeout has already - triggered */ -timediff_t Curl_pp_state_timeout(struct Curl_easy *data, - struct pingpong *pp, bool disconnecting) +#include "cfilters.h" +#include "connect.h" +#include "multiif.h" +#include "sendf.h" +#include "curl_trc.h" +#include "select.h" +#include "progress.h" + +timediff_t Curl_pp_state_timeleft_ms(struct Curl_easy *data, + struct pingpong *pp) { - timediff_t timeout_ms; /* in milliseconds */ - timediff_t response_time = data->set.server_response_timeout ? - data->set.server_response_timeout : RESP_TIMEOUT; + timediff_t xfer_remain_ms; + timediff_t remain_ms = data->set.server_response_timeout ? + data->set.server_response_timeout : PINGPONG_TIMEOUT_MS; - /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine - remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is - supposed to govern the response for any given server response, not for - the time from connect to the given server response. */ - - /* Without a requested timeout, we only wait 'response_time' seconds for the - full response to arrive before we bail out */ - timeout_ms = response_time - - curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response); - - if(data->set.timeout && !disconnecting) { - /* if timeout is requested, find out how much overall remains */ - timediff_t timeout2_ms = Curl_timeleft_ms(data, FALSE); - /* pick the lowest number */ - timeout_ms = CURLMIN(timeout_ms, timeout2_ms); - } - - if(disconnecting) { - timediff_t total_left_ms = Curl_timeleft_ms(data, FALSE); - timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0)); - } - - return timeout_ms; + /* If the overall transfer has less time remaining than pingpong + * has otherwise for the state, return that. */ + remain_ms -= curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response); + /* transfer remaining time is 0, when it has no timeout. */ + xfer_remain_ms = Curl_timeleft_ms(data); + if(xfer_remain_ms) + return CURLMIN(remain_ms, xfer_remain_ms); + return remain_ms; } /* @@ -82,7 +67,7 @@ curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc; timediff_t interval_ms; - timediff_t timeout_ms = Curl_pp_state_timeout(data, pp, disconnecting); + timediff_t timeout_ms = Curl_pp_state_timeleft_ms(data, pp); CURLcode result = CURLE_OK; if(timeout_ms <= 0) { @@ -101,7 +86,7 @@ if(Curl_conn_data_pending(data, FIRSTSOCKET)) rc = 1; else if(pp->overflow) - /* We are receiving and there is data in the cache so just read it */ + /* We are receiving and there is data in the cache so read it */ rc = 1; else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET)) /* We are receiving and there is data ready in the SSL library */ @@ -300,8 +285,8 @@ } do { - char *line = curlx_dyn_ptr(&pp->recvbuf); - char *nl = memchr(line, '\n', curlx_dyn_len(&pp->recvbuf)); + const char *line = curlx_dyn_ptr(&pp->recvbuf); + const char *nl = memchr(line, '\n', curlx_dyn_len(&pp->recvbuf)); if(nl) { /* a newline is CRLF in pp-talk, so the CR is ignored as the line is not really terminated until the LF comes */
diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h index a7292e6..864f2c6 100644 --- a/Utilities/cmcurl/lib/pingpong.h +++ b/Utilities/cmcurl/lib/pingpong.h
@@ -70,6 +70,10 @@ read */ }; +/* Default pingpong response timeout in milliseconds, unless a transfer + * has CURLOPT_SERVER_RESPONSE_TIMEOUT(_MS) set. */ +#define PINGPONG_TIMEOUT_MS (60 * 1000) + #define PINGPONG_SETUP(pp, s, e) \ do { \ (pp)->statemachine = s; \ @@ -88,10 +92,10 @@ /* initialize stuff to prepare for reading a fresh new response */ void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow); -/* Returns timeout in ms. 0 or negative number means the timeout has already - triggered */ -timediff_t Curl_pp_state_timeout(struct Curl_easy *data, - struct pingpong *pp, bool disconnecting); +/* Returns time remaining in ms. 0 or negative number means the + timeout has already triggered */ +timediff_t Curl_pp_state_timeleft_ms(struct Curl_easy *data, + struct pingpong *pp); /*********************************************************************** *
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index 8e2c8aa..317c04b 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c
@@ -37,6 +37,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "pop3.h" #ifndef CURL_DISABLE_POP3 @@ -54,7 +56,6 @@ #include <inet.h> #endif -#include "urldata.h" #include "sendf.h" #include "curl_trc.h" #include "hostip.h" @@ -62,7 +63,6 @@ #include "transfer.h" #include "escape.h" #include "pingpong.h" -#include "pop3.h" #include "vtls/vtls.h" #include "cfilters.h" #include "connect.h" @@ -71,7 +71,7 @@ #include "bufref.h" #include "curl_sasl.h" #include "curl_md5.h" -#include "strdup.h" +#include "curlx/strdup.h" /* Authentication type flags */ #define POP3_TYPE_CLEARTEXT (1 << 0) @@ -135,111 +135,6 @@ BIT(tls_supported); /* StartTLS capability supported by server */ }; -/* Local API functions */ -static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done); -static CURLcode pop3_do(struct Curl_easy *data, bool *done); -static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode pop3_connect(struct Curl_easy *data, bool *done); -static CURLcode pop3_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode pop3_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode pop3_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode pop3_parse_url_options(struct connectdata *conn); -static CURLcode pop3_parse_url_path(struct Curl_easy *data); -static CURLcode pop3_parse_custom_request(struct Curl_easy *data); -static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out); - -/* This function scans the body after the end-of-body and writes everything - * until the end is found */ -static CURLcode pop3_write(struct Curl_easy *data, - const char *str, size_t nread, bool is_eos); - -/* - * POP3 protocol handler. - */ - -const struct Curl_handler Curl_handler_pop3 = { - "pop3", /* scheme */ - pop3_setup_connection, /* setup_connection */ - pop3_do, /* do_it */ - pop3_done, /* done */ - ZERO_NULL, /* do_more */ - pop3_connect, /* connect_it */ - pop3_multi_statemach, /* connecting */ - pop3_doing, /* doing */ - pop3_pollset, /* proto_pollset */ - pop3_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - pop3_disconnect, /* disconnect */ - pop3_write, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_POP3, /* defport */ - CURLPROTO_POP3, /* protocol */ - CURLPROTO_POP3, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE -}; - -#ifdef USE_SSL -/* - * POP3S protocol handler. - */ - -const struct Curl_handler Curl_handler_pop3s = { - "pop3s", /* scheme */ - pop3_setup_connection, /* setup_connection */ - pop3_do, /* do_it */ - pop3_done, /* done */ - ZERO_NULL, /* do_more */ - pop3_connect, /* connect_it */ - pop3_multi_statemach, /* connecting */ - pop3_doing, /* doing */ - pop3_pollset, /* proto_pollset */ - pop3_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - pop3_disconnect, /* disconnect */ - pop3_write, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_POP3S, /* defport */ - CURLPROTO_POP3S, /* protocol */ - CURLPROTO_POP3, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE -}; -#endif - -/* SASL parameters for the pop3 protocol */ -static const struct SASLproto saslpop3 = { - "pop", /* The service name */ - pop3_perform_auth, /* Send authentication command */ - pop3_continue_auth, /* Send authentication continuation */ - pop3_cancel_auth, /* Send authentication cancellation */ - pop3_get_message, /* Get SASL response message */ - 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ - '*', /* Code received when continuation is expected */ - '+', /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ -}; - struct pop3_cmd { const char *name; unsigned short nlen; @@ -268,6 +163,105 @@ { "XTND", 4, TRUE, TRUE }, }; +/*********************************************************************** + * + * pop3_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode pop3_parse_url_options(struct connectdata *conn) +{ + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + CURLcode result = CURLE_OK; + const char *ptr = conn->options; + + if(!pop3c) + return CURLE_FAILED_INIT; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(curl_strnequal(key, "AUTH=", 5)) { + result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, + value, ptr - value); + + if(result && curl_strnequal(value, "+APOP", ptr - value)) { + pop3c->preftype = POP3_TYPE_APOP; + pop3c->sasl.prefmech = SASL_AUTH_NONE; + result = CURLE_OK; + } + } + else + result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; + } + + if(pop3c->preftype != POP3_TYPE_APOP) + switch(pop3c->sasl.prefmech) { + case SASL_AUTH_NONE: + pop3c->preftype = POP3_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + pop3c->preftype = POP3_TYPE_ANY; + break; + default: + pop3c->preftype = POP3_TYPE_SASL; + break; + } + + return result; +} + +/*********************************************************************** + * + * pop3_parse_url_path() + * + * Parse the URL path into separate path components. + */ +static CURLcode pop3_parse_url_path(struct Curl_easy *data) +{ + /* The POP3 struct is already initialised in pop3_connect() */ + struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); + const char *path = &data->state.up.path[1]; /* skip leading path */ + + if(!pop3) + return CURLE_FAILED_INIT; + /* URL decode the path for the message ID */ + return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL); +} + +/*********************************************************************** + * + * pop3_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode pop3_parse_custom_request(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + if(!pop3) + return CURLE_FAILED_INIT; + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL); + + return result; +} + /* Return iff a command is defined as "multi-line" (RFC 1939), * has a response terminated by a last line with a '.'. */ @@ -277,9 +271,9 @@ for(i = 0; i < CURL_ARRAYSIZE(pop3cmds); ++i) { if(curl_strnequal(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) { if(!cmdline[pop3cmds[i].nlen]) - return pop3cmds[i].multiline; + return (bool)pop3cmds[i].multiline; else if(cmdline[pop3cmds[i].nlen] == ' ') - return pop3cmds[i].multiline_with_args; + return (bool)pop3cmds[i].multiline_with_args; } } /* Unknown command, assume multi-line for backward compatibility with @@ -364,13 +358,12 @@ if(len > 2) { /* Find the start of the message */ len -= 2; - for(message += 2; *message == ' ' || *message == '\t'; message++, len--) + for(message += 2; ISBLANK(*message); message++, len--) ; /* Find the end of the message */ while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') + if(!ISBLANK(message[len]) && !ISNEWLINE(message[len])) break; /* Terminate the message */ @@ -395,7 +388,7 @@ struct pop3_conn *pop3c = Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); if(pop3c) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* for debug purposes */ static const char * const names[] = { "STOP", @@ -496,13 +489,13 @@ if(result) goto out; /* Change the connection handler */ - conn->handler = &Curl_handler_pop3s; + conn->scheme = &Curl_scheme_pop3s; } DEBUGASSERT(!pop3c->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + result, ssldone)); if(!result && ssldone) { pop3c->ssldone = ssldone; /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */ @@ -564,7 +557,7 @@ size_t i; struct MD5_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; - char secret[2 * MD5_DIGEST_LEN + 1]; + char secret[(2 * MD5_DIGEST_LEN) + 1]; if(!pop3c) return CURLE_FAILED_INIT; @@ -816,8 +809,8 @@ } else if(len > 3) { /* Does the server support APOP authentication? */ - char *lt; - char *gt = NULL; + const char *lt; + const char *gt = NULL; /* Look for the APOP timestamp */ lt = memchr(line, '<', len); @@ -827,13 +820,13 @@ if(gt) { /* the length of the timestamp, including the brackets */ size_t timestamplen = gt - lt + 1; - char *at = memchr(lt, '@', timestamplen); + const char *at = memchr(lt, '@', timestamplen); /* If the timestamp does not contain '@' it is not (as required by RFC-1939) conformant to the RFC-822 message id syntax, and we therefore do not use APOP authentication. */ if(at) { /* dupe the timestamp */ - pop3c->apoptimestamp = Curl_memdup0(lt, timestamplen); + pop3c->apoptimestamp = curlx_memdup0(lt, timestamplen); if(!pop3c->apoptimestamp) return CURLE_OUT_OF_MEMORY; /* Store the APOP capability */ @@ -1070,577 +1063,6 @@ return result; } -/* For command responses */ -static CURLcode pop3_state_command_resp(struct Curl_easy *data, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - struct pingpong *pp; - - (void)instate; - if(!pop3 || !pop3c) - return CURLE_FAILED_INIT; - - pp = &pop3c->pp; - if(pop3code != '+') { - pop3_state(data, POP3_STOP); - return CURLE_WEIRD_SERVER_REPLY; - } - - /* This 'OK' line ends with a CR LF pair which is the two first bytes of the - EOB string so count this is two matching bytes. This is necessary to make - the code detect the EOB if the only data than comes now is %2e CR LF like - when there is no body to return. */ - pop3c->eob = 2; - - /* But since this initial CR LF pair is not part of the actual body, we set - the strip counter here so that these bytes will not be delivered. */ - pop3c->strip = 2; - - if(pop3->transfer == PPTRANSFER_BODY) { - /* POP3 download */ - Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); - - if(pp->overflow) { - /* The recv buffer contains data that is actually body content so send - it as such. Note that there may even be additional "headers" after - the body */ - - /* keep only the overflow */ - curlx_dyn_tail(&pp->recvbuf, pp->overflow); - pp->nfinal = 0; /* done */ - - if(!data->req.no_body) { - result = pop3_write(data, curlx_dyn_ptr(&pp->recvbuf), - curlx_dyn_len(&pp->recvbuf), FALSE); - if(result) - return result; - } - - /* reset the buffer */ - curlx_dyn_reset(&pp->recvbuf); - pp->overflow = 0; - } - } - else - pp->overflow = 0; - - /* End of DO phase */ - pop3_state(data, POP3_STOP); - - return result; -} - -static CURLcode pop3_statemachine(struct Curl_easy *data, - struct connectdata *conn) -{ - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - CURLcode result = CURLE_OK; - int pop3code; - struct pingpong *pp; - size_t nread = 0; - (void)data; - - if(!pop3c) - return CURLE_FAILED_INIT; - - pp = &pop3c->pp; - /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ -upgrade_tls: - if(pop3c->state == POP3_UPGRADETLS) { - result = pop3_perform_upgrade_tls(data, conn); - if(result || (pop3c->state == POP3_UPGRADETLS)) - return result; - } - - /* Flush any data that needs to be sent */ - if(pp->sendleft) - return Curl_pp_flushsend(data, pp); - - do { - /* Read the response from the server */ - result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread); - if(result) - return result; - - if(!pop3code) - break; - - /* We have now received a full POP3 server response */ - switch(pop3c->state) { - case POP3_SERVERGREET: - result = pop3_state_servergreet_resp(data, pop3code, pop3c->state); - break; - - case POP3_CAPA: - result = pop3_state_capa_resp(data, pop3code, pop3c->state); - break; - - case POP3_STARTTLS: - result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state); - /* During UPGRADETLS, leave the read loop as we need to connect - * (e.g. TLS handshake) before we continue sending/receiving. */ - if(!result && (pop3c->state == POP3_UPGRADETLS)) - goto upgrade_tls; - break; - - case POP3_AUTH: - result = pop3_state_auth_resp(data, pop3code, pop3c->state); - break; - -#ifndef CURL_DISABLE_DIGEST_AUTH - case POP3_APOP: - result = pop3_state_apop_resp(data, pop3code, pop3c->state); - break; -#endif - - case POP3_USER: - result = pop3_state_user_resp(data, pop3code, pop3c->state); - break; - - case POP3_PASS: - result = pop3_state_pass_resp(data, pop3code, pop3c->state); - break; - - case POP3_COMMAND: - result = pop3_state_command_resp(data, pop3code, pop3c->state); - break; - - case POP3_QUIT: - pop3_state(data, POP3_STOP); - break; - - default: - /* internal error */ - pop3_state(data, POP3_STOP); - break; - } - } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); - - return result; -} - -/* Called repeatedly until done from multi.c */ -static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - - if(!pop3c) - return CURLE_FAILED_INIT; - result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE); - *done = (pop3c->state == POP3_STOP); - - return result; -} - -static CURLcode pop3_block_statemach(struct Curl_easy *data, - struct connectdata *conn, - bool disconnecting) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - - if(!pop3c) - return CURLE_FAILED_INIT; - - while(pop3c->state != POP3_STOP && !result) - result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting); - - return result; -} - -/* For the POP3 "protocol connect" and "doing" phases only */ -static CURLcode pop3_pollset(struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct pop3_conn *pop3c = - Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); - return pop3c ? Curl_pp_pollset(data, &pop3c->pp, ps) : CURLE_OK; -} - -/*********************************************************************** - * - * pop3_connect() - * - * This function should do everything that is to be considered a part of the - * connection phase. - * - * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE if not. - */ -static CURLcode pop3_connect(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - struct pingpong *pp = pop3c ? &pop3c->pp : NULL; - - *done = FALSE; /* default to not done yet */ - if(!pop3c) - return CURLE_FAILED_INIT; - - PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp); - - /* Set the default preferred authentication type and mechanism */ - pop3c->preftype = POP3_TYPE_ANY; - Curl_sasl_init(&pop3c->sasl, data, &saslpop3); - - /* Initialise the pingpong layer */ - Curl_pp_init(pp, Curl_pgrs_now(data)); - - /* Parse the URL options */ - result = pop3_parse_url_options(conn); - if(result) - return result; - - /* Start off waiting for the server greeting response */ - pop3_state(data, POP3_SERVERGREET); - - result = pop3_multi_statemach(data, done); - - return result; -} - -/*********************************************************************** - * - * pop3_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - CURLcode result = CURLE_OK; - struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); - - (void)premature; - - if(!pop3) - return CURLE_OK; - - if(status) { - connclose(data->conn, "POP3 done with bad status"); - result = status; /* use the already set error code */ - } - - /* Cleanup our per-request based variables */ - Curl_safefree(pop3->id); - Curl_safefree(pop3->custom); - - /* Clear the transfer mode for the next request */ - pop3->transfer = PPTRANSFER_BODY; - - return result; -} - -/*********************************************************************** - * - * pop3_perform() - * - * This is the actual DO function for POP3. Get a message/listing according to - * the options previously setup. - */ -static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done) -{ - /* This is POP3 and no proxy */ - CURLcode result = CURLE_OK; - struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); - - if(!pop3) - return CURLE_FAILED_INIT; - - DEBUGF(infof(data, "DO phase starts")); - - /* Start the first command in the DO phase, may alter data->req.no_body */ - result = pop3_perform_command(data); - if(result) - return result; - - if(data->req.no_body) - /* Requested no body means no transfer */ - pop3->transfer = PPTRANSFER_INFO; - - *dophase_done = FALSE; /* not done yet */ - - /* Run the state-machine */ - result = pop3_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); - - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete")); - - return result; -} - -/*********************************************************************** - * - * pop3_do() - * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (pop3_perform). - * - * The input argument is already checked for validity. - */ -static CURLcode pop3_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - *done = FALSE; /* default to false */ - - /* Parse the URL path */ - result = pop3_parse_url_path(data); - if(result) - return result; - - /* Parse the custom request */ - result = pop3_parse_custom_request(data); - if(result) - return result; - - result = pop3_regular_transfer(data, done); - - return result; -} - -/*********************************************************************** - * - * pop3_disconnect() - * - * Disconnect from an POP3 server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode pop3_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - (void)data; - - if(!pop3c) - return CURLE_FAILED_INIT; - - /* We cannot send quit unconditionally. If this connection is stale or - bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to. */ - - if(!dead_connection && conn->bits.protoconnstart && - !Curl_pp_needs_flush(data, &pop3c->pp)) { - if(!pop3_perform_quit(data, conn)) - (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */ - } - - /* Disconnect from the server */ - Curl_pp_disconnect(&pop3c->pp); - - /* Cleanup our connection based variables */ - Curl_safefree(pop3c->apoptimestamp); - - return CURLE_OK; -} - -/* Call this when the DO phase has completed */ -static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected) -{ - (void)data; - (void)connected; - - return CURLE_OK; -} - -/* Called from multi.c while DOing */ -static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done) -{ - CURLcode result = pop3_multi_statemach(data, dophase_done); - - if(result) - DEBUGF(infof(data, "DO phase failed")); - else if(*dophase_done) { - result = pop3_dophase_done(data, FALSE /* not connected */); - - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/*********************************************************************** - * - * pop3_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - */ -static CURLcode pop3_regular_transfer(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - - /* Make sure size is unknown at this point */ - data->req.size = -1; - - /* Set the progress data */ - Curl_pgrsReset(data); - - /* Carry out the perform */ - result = pop3_perform(data, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = pop3_dophase_done(data, connected); - - return result; -} - -static void pop3_easy_dtor(void *key, size_t klen, void *entry) -{ - struct POP3 *pop3 = entry; - (void)key; - (void)klen; - DEBUGASSERT(pop3); - /* Cleanup our per-request based variables */ - Curl_safefree(pop3->id); - Curl_safefree(pop3->custom); - curlx_free(pop3); -} - -static void pop3_conn_dtor(void *key, size_t klen, void *entry) -{ - struct pop3_conn *pop3c = entry; - (void)key; - (void)klen; - DEBUGASSERT(pop3c); - Curl_pp_disconnect(&pop3c->pp); - Curl_safefree(pop3c->apoptimestamp); - curlx_free(pop3c); -} - -static CURLcode pop3_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct pop3_conn *pop3c; - struct POP3 *pop3 = curlx_calloc(1, sizeof(*pop3)); - if(!pop3 || - Curl_meta_set(data, CURL_META_POP3_EASY, pop3, pop3_easy_dtor)) - return CURLE_OUT_OF_MEMORY; - - pop3c = curlx_calloc(1, sizeof(*pop3c)); - if(!pop3c || - Curl_conn_meta_set(conn, CURL_META_POP3_CONN, pop3c, pop3_conn_dtor)) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -/*********************************************************************** - * - * pop3_parse_url_options() - * - * Parse the URL login options. - */ -static CURLcode pop3_parse_url_options(struct connectdata *conn) -{ - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - CURLcode result = CURLE_OK; - const char *ptr = conn->options; - - if(!pop3c) - return CURLE_FAILED_INIT; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(curl_strnequal(key, "AUTH=", 5)) { - result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, - value, ptr - value); - - if(result && curl_strnequal(value, "+APOP", ptr - value)) { - pop3c->preftype = POP3_TYPE_APOP; - pop3c->sasl.prefmech = SASL_AUTH_NONE; - result = CURLE_OK; - } - } - else - result = CURLE_URL_MALFORMAT; - - if(*ptr == ';') - ptr++; - } - - if(pop3c->preftype != POP3_TYPE_APOP) - switch(pop3c->sasl.prefmech) { - case SASL_AUTH_NONE: - pop3c->preftype = POP3_TYPE_NONE; - break; - case SASL_AUTH_DEFAULT: - pop3c->preftype = POP3_TYPE_ANY; - break; - default: - pop3c->preftype = POP3_TYPE_SASL; - break; - } - - return result; -} - -/*********************************************************************** - * - * pop3_parse_url_path() - * - * Parse the URL path into separate path components. - */ -static CURLcode pop3_parse_url_path(struct Curl_easy *data) -{ - /* The POP3 struct is already initialised in pop3_connect() */ - struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); - const char *path = &data->state.up.path[1]; /* skip leading path */ - - if(!pop3) - return CURLE_FAILED_INIT; - /* URL decode the path for the message ID */ - return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL); -} - -/*********************************************************************** - * - * pop3_parse_custom_request() - * - * Parse the custom request. - */ -static CURLcode pop3_parse_custom_request(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - - if(!pop3) - return CURLE_FAILED_INIT; - /* URL decode the custom request */ - if(custom) - result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL); - - return result; -} - /*********************************************************************** * * pop3_write() @@ -1653,7 +1075,6 @@ { /* This code could be made into a special function in the handler struct */ CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; struct connectdata *conn = data->conn; struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); bool strip_dot = FALSE; @@ -1763,7 +1184,7 @@ message as per RFC-1939, sect. 3 */ result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2); - k->keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); pop3c->eob = 0; return result; @@ -1781,4 +1202,513 @@ return result; } +/* For command responses */ +static CURLcode pop3_state_command_resp(struct Curl_easy *data, + int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + struct pingpong *pp; + + (void)instate; + if(!pop3 || !pop3c) + return CURLE_FAILED_INIT; + + pp = &pop3c->pp; + if(pop3code != '+') { + pop3_state(data, POP3_STOP); + return CURLE_WEIRD_SERVER_REPLY; + } + + /* This 'OK' line ends with a CR LF pair which is the two first bytes of the + EOB string so count this is two matching bytes. This is necessary to make + the code detect the EOB if the only data than comes now is %2e CR LF like + when there is no body to return. */ + pop3c->eob = 2; + + /* Since this initial CR LF pair is not part of the actual body, we set + the strip counter here so that these bytes will not be delivered. */ + pop3c->strip = 2; + + if(pop3->transfer == PPTRANSFER_BODY) { + /* POP3 download */ + Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); + + if(pp->overflow) { + /* The recv buffer contains data that is actually body content so send + it as such. Note that there may even be additional "headers" after + the body */ + + /* keep only the overflow */ + curlx_dyn_tail(&pp->recvbuf, pp->overflow); + pp->nfinal = 0; /* done */ + + if(!data->req.no_body) { + result = pop3_write(data, curlx_dyn_ptr(&pp->recvbuf), + curlx_dyn_len(&pp->recvbuf), FALSE); + if(result) + return result; + } + + /* reset the buffer */ + curlx_dyn_reset(&pp->recvbuf); + pp->overflow = 0; + } + } + else + pp->overflow = 0; + + /* End of DO phase */ + pop3_state(data, POP3_STOP); + + return result; +} + +static CURLcode pop3_statemachine(struct Curl_easy *data, + struct connectdata *conn) +{ + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + CURLcode result = CURLE_OK; + int pop3code; + struct pingpong *pp; + size_t nread = 0; + (void)data; + + if(!pop3c) + return CURLE_FAILED_INIT; + + pp = &pop3c->pp; + /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ +upgrade_tls: + if(pop3c->state == POP3_UPGRADETLS) { + result = pop3_perform_upgrade_tls(data, conn); + if(result || (pop3c->state == POP3_UPGRADETLS)) + return result; + } + + /* Flush any data that needs to be sent */ + if(pp->sendleft) + return Curl_pp_flushsend(data, pp); + + do { + /* Read the response from the server */ + result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread); + if(result) + return result; + + if(!pop3code) + break; + + /* We have now received a full POP3 server response */ + switch(pop3c->state) { + case POP3_SERVERGREET: + result = pop3_state_servergreet_resp(data, pop3code, pop3c->state); + break; + + case POP3_CAPA: + result = pop3_state_capa_resp(data, pop3code, pop3c->state); + break; + + case POP3_STARTTLS: + result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state); + /* During UPGRADETLS, leave the read loop as we need to connect + * (e.g. TLS handshake) before we continue sending/receiving. */ + if(!result && (pop3c->state == POP3_UPGRADETLS)) + goto upgrade_tls; + break; + + case POP3_AUTH: + result = pop3_state_auth_resp(data, pop3code, pop3c->state); + break; + +#ifndef CURL_DISABLE_DIGEST_AUTH + case POP3_APOP: + result = pop3_state_apop_resp(data, pop3code, pop3c->state); + break; +#endif + + case POP3_USER: + result = pop3_state_user_resp(data, pop3code, pop3c->state); + break; + + case POP3_PASS: + result = pop3_state_pass_resp(data, pop3code, pop3c->state); + break; + + case POP3_COMMAND: + result = pop3_state_command_resp(data, pop3code, pop3c->state); + break; + + case POP3_QUIT: + pop3_state(data, POP3_STOP); + break; + + default: + /* internal error */ + pop3_state(data, POP3_STOP); + break; + } + } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); + + return result; +} + +/* Called repeatedly until done from multi.c */ +static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + + if(!pop3c) + return CURLE_FAILED_INIT; + result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE); + *done = (pop3c->state == POP3_STOP); + + return result; +} + +static CURLcode pop3_block_statemach(struct Curl_easy *data, + struct connectdata *conn, + bool disconnecting) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + + if(!pop3c) + return CURLE_FAILED_INIT; + + while(pop3c->state != POP3_STOP && !result) + result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting); + + return result; +} + +/* For the POP3 "protocol connect" and "doing" phases only */ +static CURLcode pop3_pollset(struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct pop3_conn *pop3c = + Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); + return pop3c ? Curl_pp_pollset(data, &pop3c->pp, ps) : CURLE_OK; +} + +/* SASL parameters for the pop3 protocol */ +static const struct SASLproto saslpop3 = { + "pop", /* The service name */ + pop3_perform_auth, /* Send authentication command */ + pop3_continue_auth, /* Send authentication continuation */ + pop3_cancel_auth, /* Send authentication cancellation */ + pop3_get_message, /* Get SASL response message */ + 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + '*', /* Code received when continuation is expected */ + '+', /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + +/*********************************************************************** + * + * pop3_connect() + * + * This function should do everything that is to be considered a part of the + * connection phase. + * + * The variable 'done' points to will be TRUE if the protocol-layer connect + * phase is done when this function returns, or FALSE if not. + */ +static CURLcode pop3_connect(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + struct pingpong *pp = pop3c ? &pop3c->pp : NULL; + + *done = FALSE; /* default to not done yet */ + if(!pop3c) + return CURLE_FAILED_INIT; + + PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp); + + /* Set the default preferred authentication type and mechanism */ + pop3c->preftype = POP3_TYPE_ANY; + Curl_sasl_init(&pop3c->sasl, data, &saslpop3); + + /* Initialise the pingpong layer */ + Curl_pp_init(pp, Curl_pgrs_now(data)); + + /* Parse the URL options */ + result = pop3_parse_url_options(conn); + if(result) + return result; + + /* Start off waiting for the server greeting response */ + pop3_state(data, POP3_SERVERGREET); + + result = pop3_multi_statemach(data, done); + + return result; +} + +/*********************************************************************** + * + * pop3_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + CURLcode result = CURLE_OK; + struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); + + (void)premature; + + if(!pop3) + return CURLE_OK; + + if(status) { + connclose(data->conn, "POP3 done with bad status"); + result = status; /* use the already set error code */ + } + + /* Cleanup our per-request based variables */ + curlx_safefree(pop3->id); + curlx_safefree(pop3->custom); + + /* Clear the transfer mode for the next request */ + pop3->transfer = PPTRANSFER_BODY; + + return result; +} + +/*********************************************************************** + * + * pop3_perform() + * + * This is the actual DO function for POP3. Get a message/listing according to + * the options previously setup. + */ +static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, + bool *dophase_done) +{ + /* This is POP3 and no proxy */ + CURLcode result = CURLE_OK; + struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); + + if(!pop3) + return CURLE_FAILED_INIT; + + DEBUGF(infof(data, "DO phase starts")); + + /* Start the first command in the DO phase, may alter data->req.no_body */ + result = pop3_perform_command(data); + if(result) + return result; + + if(data->req.no_body) + /* Requested no body means no transfer */ + pop3->transfer = PPTRANSFER_INFO; + + *dophase_done = FALSE; /* not done yet */ + + /* Run the state-machine */ + result = pop3_multi_statemach(data, dophase_done); + *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + + if(*dophase_done) + DEBUGF(infof(data, "DO phase is complete")); + + return result; +} + +/* Call this when the DO phase has completed */ +static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected) +{ + (void)data; + (void)connected; + + return CURLE_OK; +} + +/*********************************************************************** + * + * pop3_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + */ +static CURLcode pop3_regular_transfer(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + + /* Make sure size is unknown at this point */ + data->req.size = -1; + + /* Set the progress data */ + Curl_pgrsReset(data); + + /* Carry out the perform */ + result = pop3_perform(data, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = pop3_dophase_done(data, connected); + + return result; +} + +/*********************************************************************** + * + * pop3_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (pop3_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode pop3_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + *done = FALSE; /* default to false */ + + /* Parse the URL path */ + result = pop3_parse_url_path(data); + if(result) + return result; + + /* Parse the custom request */ + result = pop3_parse_custom_request(data); + if(result) + return result; + + result = pop3_regular_transfer(data, done); + + return result; +} + +/*********************************************************************** + * + * pop3_disconnect() + * + * Disconnect from an POP3 server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode pop3_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + (void)data; + + if(!pop3c) + return CURLE_FAILED_INIT; + + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way, sending quit and waiting around here will make the + disconnect wait in vain and cause more problems than we need to. */ + + if(!dead_connection && conn->bits.protoconnstart && + !Curl_pp_needs_flush(data, &pop3c->pp)) { + if(!pop3_perform_quit(data, conn)) + (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */ + } + + /* Disconnect from the server */ + Curl_pp_disconnect(&pop3c->pp); + + /* Cleanup our connection based variables */ + curlx_safefree(pop3c->apoptimestamp); + + return CURLE_OK; +} + +/* Called from multi.c while DOing */ +static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done) +{ + CURLcode result = pop3_multi_statemach(data, dophase_done); + + if(result) + DEBUGF(infof(data, "DO phase failed")); + else if(*dophase_done) { + result = pop3_dophase_done(data, FALSE /* not connected */); + + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +static void pop3_easy_dtor(void *key, size_t klen, void *entry) +{ + struct POP3 *pop3 = entry; + (void)key; + (void)klen; + DEBUGASSERT(pop3); + /* Cleanup our per-request based variables */ + curlx_safefree(pop3->id); + curlx_safefree(pop3->custom); + curlx_free(pop3); +} + +static void pop3_conn_dtor(void *key, size_t klen, void *entry) +{ + struct pop3_conn *pop3c = entry; + (void)key; + (void)klen; + DEBUGASSERT(pop3c); + Curl_pp_disconnect(&pop3c->pp); + curlx_safefree(pop3c->apoptimestamp); + curlx_free(pop3c); +} + +static CURLcode pop3_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + struct pop3_conn *pop3c; + struct POP3 *pop3 = curlx_calloc(1, sizeof(*pop3)); + if(!pop3 || + Curl_meta_set(data, CURL_META_POP3_EASY, pop3, pop3_easy_dtor)) + return CURLE_OUT_OF_MEMORY; + + pop3c = curlx_calloc(1, sizeof(*pop3c)); + if(!pop3c || + Curl_conn_meta_set(conn, CURL_META_POP3_CONN, pop3c, pop3_conn_dtor)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +/* + * POP3 protocol. + */ +const struct Curl_protocol Curl_protocol_pop3 = { + pop3_setup_connection, /* setup_connection */ + pop3_do, /* do_it */ + pop3_done, /* done */ + ZERO_NULL, /* do_more */ + pop3_connect, /* connect_it */ + pop3_multi_statemach, /* connecting */ + pop3_doing, /* doing */ + pop3_pollset, /* proto_pollset */ + pop3_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + pop3_disconnect, /* disconnect */ + pop3_write, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_POP3 */
diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h index ed00dd1..75c3a67 100644 --- a/Utilities/cmcurl/lib/pop3.h +++ b/Utilities/cmcurl/lib/pop3.h
@@ -23,7 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_handler Curl_handler_pop3; -extern const struct Curl_handler Curl_handler_pop3s; +#ifndef CURL_DISABLE_POP3 +extern const struct Curl_protocol Curl_protocol_pop3; +#endif #endif /* HEADER_CURL_POP3_H */
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index 9cbd267..919c151 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c
@@ -30,20 +30,17 @@ #include "transfer.h" #include "curlx/strcopy.h" -/* check rate limits within this many recent milliseconds, at minimum. */ -#define MIN_RATE_LIMIT_PERIOD 3000 - #ifndef CURL_DISABLE_PROGRESS_METER /* Provide a string that is 7 letters long (plus the zero byte). - Unit test 1636. + @unittest 1636 */ UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds); UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds) { curl_off_t h; if(seconds <= 0) { - curlx_strcopy(r, rsize, " ", 7); + curlx_strcopy(r, rsize, STRCONST(" ")); return; } h = seconds / 3600; @@ -76,7 +73,7 @@ if(y <= 99999) curl_msnprintf(r, rsize, "%6" FMT_OFF_T "y", y); else - curlx_strcopy(r, rsize, ">99999y", 7); + curlx_strcopy(r, rsize, STRCONST(">99999y")); } } } @@ -86,7 +83,7 @@ but never longer than 6 columns (+ one zero byte). Add suffix k, M, G when suitable... - Unit test 1636 + @unittest 1636 */ UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen); UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen) @@ -130,9 +127,11 @@ } /* - * @unittest: 1606 + * @unittest 1606 */ UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, + const struct curltime *pnow); +UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, const struct curltime *pnow) { if(!data->set.low_speed_time || !data->set.low_speed_limit || @@ -152,11 +151,9 @@ if(howlong >= data->set.low_speed_time * 1000) { /* too long */ - failf(data, - "Operation too slow. " - "Less than %ld bytes/sec transferred the last %ld seconds", - data->set.low_speed_limit, - data->set.low_speed_time); + failf(data, "Operation too slow. Less than %" FMT_OFF_T + " bytes/sec transferred the last %u seconds", + data->set.low_speed_limit, data->set.low_speed_time); return CURLE_OPERATION_TIMEDOUT; } } @@ -182,7 +179,6 @@ } /* - New proposed interface, 9th of February 2000: pgrsStartNow() - sets start time @@ -192,7 +188,6 @@ pgrsSetUploadCounter() - amount of data currently uploaded pgrsUpdate() - show progress pgrsDone() - transfer complete - */ int Curl_pgrsDone(struct Curl_easy *data) @@ -218,6 +213,7 @@ Curl_pgrsSetUploadSize(data, -1); Curl_pgrsSetDownloadSize(data, -1); data->progress.speeder_c = 0; /* reset speed records */ + data->progress.deliver = 0; pgrs_speedinit(data); } @@ -245,7 +241,6 @@ } /* - * * Curl_pgrsTimeWas(). Store the timestamp time at the given label. */ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, @@ -292,8 +287,8 @@ case TIMER_STARTTRANSFER: delta = &data->progress.t_starttransfer; /* prevent updating t_starttransfer unless: - * 1) this is the first time we are setting t_starttransfer - * 2) a redirect has occurred since the last time t_starttransfer was set + * 1. this is the first time we are setting t_starttransfer + * 2. a redirect has occurred since the last time t_starttransfer was set * This prevents repeated invocations of the function from incorrectly * changing the t_starttransfer time. */ @@ -323,7 +318,6 @@ } /* - * * Curl_pgrsTime(). Store the current time at the given label. This fetches a * fresh "now" and returns it. * @@ -348,6 +342,25 @@ p->ul_size_known = FALSE; } +/* check that the 'delta' amount of bytes are okay to deliver to the + application, or return error if not. */ +CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta) +{ + if(data->set.max_filesize && + ((curl_off_t)delta > data->set.max_filesize - data->progress.deliver)) { + failf(data, "Would have exceeded max file size"); + return CURLE_FILESIZE_EXCEEDED; + } + return CURLE_OK; +} + +/* this counts how much data is delivered to the application, which + in compressed cases may differ from downloaded amount */ +void Curl_pgrs_deliver_inc(struct Curl_easy *data, size_t delta) +{ + data->progress.deliver += delta; +} + void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta) { if(delta) { @@ -421,7 +434,7 @@ { struct Progress * const p = &data->progress; int i_next, i_oldest, i_latest; - timediff_t duration_ms; + timediff_t duration_us; curl_off_t amount; /* The time spent so far (from the start) in microseconds */ @@ -472,21 +485,19 @@ /* How much we transferred between oldest and current records */ amount = p->speed_amount[i_latest] - p->speed_amount[i_oldest]; /* How long this took */ - duration_ms = curlx_ptimediff_ms(&p->speed_time[i_latest], + duration_us = curlx_ptimediff_us(&p->speed_time[i_latest], &p->speed_time[i_oldest]); - if(duration_ms <= 0) - duration_ms = 1; + if(duration_us <= 0) + duration_us = 1; - if(amount > (CURL_OFF_T_MAX / 1000)) { + if(amount > (CURL_OFF_T_MAX / 1000000)) { /* the 'amount' value is bigger than would fit in 64 bits if - multiplied with 1000, so we use the double math for this */ + multiplied with 1000000, so we use the double math for this */ p->current_speed = - (curl_off_t)(((double)amount * 1000.0) / (double)duration_ms); + (curl_off_t)(((double)amount * 1000000.0) / (double)duration_us); } else { - /* the 'amount' value is small enough to fit within 32 bits even - when multiplied with 1000 */ - p->current_speed = amount * 1000 / duration_ms; + p->current_speed = amount * 1000000 / duration_us; } if((p->lastshow == pnow->tv_sec) && !data->req.done) @@ -609,8 +620,7 @@ /* we flush the output stream to make it appear as soon as possible */ fflush(data->set.err); } -#else - /* progress bar disabled */ +#else /* CURL_DISABLE_PROGRESS_METER */ #define progress_meter(x) Curl_nop_stmt #endif @@ -621,18 +631,18 @@ static CURLcode pgrsupdate(struct Curl_easy *data, bool showprogress) { if(!data->progress.hide) { + int rc; if(data->set.fxferinfo) { - int result; /* There is a callback set, call that */ Curl_set_in_callback(data, TRUE); - result = data->set.fxferinfo(data->set.progress_client, - data->progress.dl.total_size, - data->progress.dl.cur_size, - data->progress.ul.total_size, - data->progress.ul.cur_size); + rc = data->set.fxferinfo(data->set.progress_client, + data->progress.dl.total_size, + data->progress.dl.cur_size, + data->progress.ul.total_size, + data->progress.ul.cur_size); Curl_set_in_callback(data, FALSE); - if(result != CURL_PROGRESSFUNC_CONTINUE) { - if(result) { + if(rc != CURL_PROGRESSFUNC_CONTINUE) { + if(rc) { failf(data, "Callback aborted"); return CURLE_ABORTED_BY_CALLBACK; } @@ -640,17 +650,16 @@ } } else if(data->set.fprogress) { - int result; /* The older deprecated callback is set, call that */ Curl_set_in_callback(data, TRUE); - result = data->set.fprogress(data->set.progress_client, - (double)data->progress.dl.total_size, - (double)data->progress.dl.cur_size, - (double)data->progress.ul.total_size, - (double)data->progress.ul.cur_size); + rc = data->set.fprogress(data->set.progress_client, + (double)data->progress.dl.total_size, + (double)data->progress.dl.cur_size, + (double)data->progress.ul.total_size, + (double)data->progress.ul.cur_size); Curl_set_in_callback(data, FALSE); - if(result != CURL_PROGRESSFUNC_CONTINUE) { - if(result) { + if(rc != CURL_PROGRESSFUNC_CONTINUE) { + if(rc) { failf(data, "Callback aborted"); return CURLE_ABORTED_BY_CALLBACK; }
diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h index 9cb0924..7d419ec 100644 --- a/Utilities/cmcurl/lib/progress.h +++ b/Utilities/cmcurl/lib/progress.h
@@ -50,7 +50,8 @@ void Curl_pgrsStartNow(struct Curl_easy *data); void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size); void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size); - +CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta); +void Curl_pgrs_deliver_inc(struct Curl_easy *data, size_t delta); void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta); void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta); void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size); @@ -82,9 +83,4 @@ void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent); -#ifdef UNITTESTS -UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, - const struct curltime *pnow); -#endif - #endif /* HEADER_CURL_PROGRESS_H */
diff --git a/Utilities/cmcurl/lib/protocol.c b/Utilities/cmcurl/lib/protocol.c new file mode 100644 index 0000000..c8d4325 --- /dev/null +++ b/Utilities/cmcurl/lib/protocol.c
@@ -0,0 +1,485 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "protocol.h" +#include "strcase.h" + +#include "dict.h" +#include "file.h" +#include "ftp.h" +#include "gopher.h" +#include "http.h" +#include "imap.h" +#include "curl_ldap.h" +#include "mqtt.h" +#include "pop3.h" +#include "rtsp.h" +#include "smb.h" +#include "smtp.h" +#include "telnet.h" +#include "tftp.h" +#include "ws.h" +#include "vssh/ssh.h" + + +/* All URI schemes known to libcurl, but not necessarily implemented + * by protocol handlers. */ +const struct Curl_scheme Curl_scheme_dict = { + "dict", /* scheme */ +#ifdef CURL_DISABLE_DICT + ZERO_NULL, +#else + &Curl_protocol_dict, +#endif + CURLPROTO_DICT, /* protocol */ + CURLPROTO_DICT, /* family */ + PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */ + PORT_DICT, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_file = { + "file", /* scheme */ +#ifdef CURL_DISABLE_FILE + ZERO_NULL, +#else + &Curl_protocol_file, +#endif + CURLPROTO_FILE, /* protocol */ + CURLPROTO_FILE, /* family */ + PROTOPT_NONETWORK | PROTOPT_NOURLQUERY, /* flags */ + 0 /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ftp = { + "ftp", /* scheme */ +#ifdef CURL_DISABLE_FTP + ZERO_NULL, +#else + &Curl_protocol_ftp, +#endif + CURLPROTO_FTP, /* protocol */ + CURLPROTO_FTP, /* family */ + PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | + PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | + PROTOPT_WILDCARD | PROTOPT_SSL_REUSE | + PROTOPT_CONN_REUSE, /* flags */ + PORT_FTP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ftps = { + "ftps", /* scheme */ +#if defined(CURL_DISABLE_FTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_ftp, +#endif + CURLPROTO_FTPS, /* protocol */ + CURLPROTO_FTP, /* family */ + PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | + PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD | + PROTOPT_CONN_REUSE, /* flags */ + PORT_FTPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_gopher = { + "gopher", /* scheme */ +#ifdef CURL_DISABLE_GOPHER + ZERO_NULL, +#else + &Curl_protocol_gopher, +#endif + CURLPROTO_GOPHER, /* protocol */ + CURLPROTO_GOPHER, /* family */ + PROTOPT_NONE, /* flags */ + PORT_GOPHER, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_gophers = { + "gophers", /* scheme */ +#if defined(CURL_DISABLE_GOPHER) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_gophers, +#endif + CURLPROTO_GOPHERS, /* protocol */ + CURLPROTO_GOPHER, /* family */ + PROTOPT_SSL, /* flags */ + PORT_GOPHER, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_http = { + "http", /* scheme */ +#ifdef CURL_DISABLE_HTTP + ZERO_NULL, +#else + &Curl_protocol_http, +#endif + CURLPROTO_HTTP, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PORT_HTTP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_https = { + "https", /* scheme */ +#if defined(CURL_DISABLE_HTTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_http, +#endif + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PORT_HTTPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_imap = { + "imap", /* scheme */ +#ifdef CURL_DISABLE_IMAP + ZERO_NULL, +#else + &Curl_protocol_imap, +#endif + CURLPROTO_IMAP, /* protocol */ + CURLPROTO_IMAP, /* family */ + PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | + PROTOPT_CONN_REUSE, + PORT_IMAP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_imaps = { + "imaps", /* scheme */ +#if defined(CURL_DISABLE_IMAP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_imap, +#endif + CURLPROTO_IMAPS, /* protocol */ + CURLPROTO_IMAP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_IMAPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ldap = { + "ldap", /* scheme */ +#ifdef CURL_DISABLE_LDAP + ZERO_NULL, +#else + &Curl_protocol_ldap, +#endif + CURLPROTO_LDAP, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_SSL_REUSE, /* flags */ + PORT_LDAP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ldaps = { + "ldaps", /* scheme */ +#if defined(CURL_DISABLE_LDAP) || !defined(HAVE_LDAP_SSL) + ZERO_NULL, +#else + &Curl_protocol_ldap, +#endif + CURLPROTO_LDAPS, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_SSL, /* flags */ + PORT_LDAPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_mqtt = { + "mqtt", /* scheme */ +#ifdef CURL_DISABLE_MQTT + ZERO_NULL, +#else + &Curl_protocol_mqtt, +#endif + CURLPROTO_MQTT, /* protocol */ + CURLPROTO_MQTT, /* family */ + PROTOPT_NONE, /* flags */ + PORT_MQTT, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_mqtts = { + "mqtts", /* scheme */ +#if defined(CURL_DISABLE_MQTT) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_mqtts, +#endif + CURLPROTO_MQTTS, /* protocol */ + CURLPROTO_MQTT, /* family */ + PROTOPT_SSL, /* flags */ + PORT_MQTTS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_pop3 = { + "pop3", /* scheme */ +#ifdef CURL_DISABLE_POP3 + ZERO_NULL, +#else + &Curl_protocol_pop3, +#endif + CURLPROTO_POP3, /* protocol */ + CURLPROTO_POP3, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE, + PORT_POP3, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_pop3s = { + "pop3s", /* scheme */ +#if defined(CURL_DISABLE_POP3) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_pop3, +#endif + CURLPROTO_POP3S, /* protocol */ + CURLPROTO_POP3, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_POP3S, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_rtsp = { + "rtsp", /* scheme */ +#ifdef CURL_DISABLE_RTSP + ZERO_NULL, +#else + &Curl_protocol_rtsp, +#endif + CURLPROTO_RTSP, /* protocol */ + CURLPROTO_RTSP, /* family */ + PROTOPT_CONN_REUSE, /* flags */ + PORT_RTSP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_sftp = { + "sftp", /* scheme */ +#ifndef USE_SSH + NULL, +#else + &Curl_protocol_sftp, +#endif + CURLPROTO_SFTP, /* protocol */ + CURLPROTO_SFTP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE, + PORT_SSH /* defport */ +}; + +const struct Curl_scheme Curl_scheme_scp = { + "scp", /* scheme */ +#ifndef USE_SSH + NULL, +#else + &Curl_protocol_scp, +#endif + CURLPROTO_SCP, /* protocol */ + CURLPROTO_SCP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE, + PORT_SSH, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smb = { + "smb", /* scheme */ +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) + &Curl_protocol_smb, +#else + ZERO_NULL, +#endif + CURLPROTO_SMB, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_NONE, /* flags */ + PORT_SMB, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smbs = { + "smbs", /* scheme */ +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) && defined(USE_SSL) + &Curl_protocol_smb, +#else + ZERO_NULL, +#endif + CURLPROTO_SMBS, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_SSL, /* flags */ + PORT_SMBS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smtp = { + "smtp", /* scheme */ +#ifdef CURL_DISABLE_SMTP + ZERO_NULL, +#else + &Curl_protocol_smtp, +#endif + CURLPROTO_SMTP, /* protocol */ + CURLPROTO_SMTP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE, + PORT_SMTP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smtps = { + "smtps", /* scheme */ +#if defined(CURL_DISABLE_SMTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_smtp, +#endif + CURLPROTO_SMTPS, /* protocol */ + CURLPROTO_SMTP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_SMTPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_telnet = { + "telnet", /* scheme */ +#ifdef CURL_DISABLE_TELNET + ZERO_NULL, +#else + &Curl_protocol_telnet, +#endif + CURLPROTO_TELNET, /* protocol */ + CURLPROTO_TELNET, /* family */ + PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */ + PORT_TELNET, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_tftp = { + "tftp", /* scheme */ +#ifdef CURL_DISABLE_TFTP + ZERO_NULL, +#else + &Curl_protocol_tftp, +#endif + CURLPROTO_TFTP, /* protocol */ + CURLPROTO_TFTP, /* family */ + PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY, /* flags */ + PORT_TFTP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ws = { + "ws", /* scheme */ +#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP) + ZERO_NULL, +#else + &Curl_protocol_ws, +#endif + CURLPROTO_WS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL, + PORT_HTTP /* defport */ +}; + +const struct Curl_scheme Curl_scheme_wss = { + "wss", /* scheme */ +#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP) || \ + !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_ws, +#endif + CURLPROTO_WSS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL, + PORT_HTTPS /* defport */ +}; + +/* Returns a struct scheme pointer if the name is a known scheme. Check the + ->run struct field for non-NULL to figure out if an implementation is + present. */ +const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) +{ + /* table generated by schemetable.c: + 1. gcc schemetable.c && ./a.out + 2. check how small the table gets + 3. tweak the hash algorithm, then rerun from 1 + 4. when the table is good enough + 5. copy the table into this source code + 6. make sure this function uses the same hash function that worked for + schemetable.c + */ + static const struct Curl_scheme * const all_schemes[47] = { + &Curl_scheme_mqtt, + &Curl_scheme_smtp, + &Curl_scheme_tftp, + &Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &Curl_scheme_ldaps, + &Curl_scheme_dict, NULL, + &Curl_scheme_file, NULL, + &Curl_scheme_pop3s, + &Curl_scheme_ftp, + &Curl_scheme_scp, + &Curl_scheme_mqtts, + &Curl_scheme_imaps, + &Curl_scheme_ldap, + &Curl_scheme_http, + &Curl_scheme_smb, NULL, NULL, + &Curl_scheme_telnet, + &Curl_scheme_https, + &Curl_scheme_gopher, + &Curl_scheme_rtsp, NULL, NULL, + &Curl_scheme_wss, NULL, + &Curl_scheme_gophers, + &Curl_scheme_smtps, + &Curl_scheme_pop3, + &Curl_scheme_ws, NULL, NULL, + &Curl_scheme_sftp, + &Curl_scheme_ftps, NULL, + &Curl_scheme_smbs, NULL, + }; + + if(len && (len <= 7)) { + const char *s = scheme; + size_t l = len; + const struct Curl_scheme *h; + unsigned int c = 792; + while(l) { + c <<= 4; + c += (unsigned int)Curl_raw_tolower(*s); + s++; + l--; + } + + h = all_schemes[c % 47]; + if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) + return h; + } + return NULL; +} + +const struct Curl_scheme *Curl_get_scheme(const char *scheme) +{ + return Curl_getn_scheme(scheme, strlen(scheme)); +}
diff --git a/Utilities/cmcurl/lib/protocol.h b/Utilities/cmcurl/lib/protocol.h new file mode 100644 index 0000000..f825409 --- /dev/null +++ b/Utilities/cmcurl/lib/protocol.h
@@ -0,0 +1,276 @@ +#ifndef HEADER_CURL_PROTOCOL_H +#define HEADER_CURL_PROTOCOL_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 + * + ***************************************************************************/ +/* This file is for lib internal stuff */ +#include "curl_setup.h" + +struct Curl_easy; +struct connectdata; +struct easy_pollset; + +/* Known protocol default port numbers */ +#define PORT_FTP 21 +#define PORT_FTPS 990 +#define PORT_TELNET 23 +#define PORT_HTTP 80 +#define PORT_HTTPS 443 +#define PORT_DICT 2628 +#define PORT_LDAP 389 +#define PORT_LDAPS 636 +#define PORT_TFTP 69 +#define PORT_SSH 22 +#define PORT_IMAP 143 +#define PORT_IMAPS 993 +#define PORT_POP3 110 +#define PORT_POP3S 995 +#define PORT_SMB 445 +#define PORT_SMBS 445 +#define PORT_SMTP 25 +#define PORT_SMTPS 465 /* sometimes called SSMTP */ +#define PORT_RTSP 554 +#define PORT_GOPHER 70 +#define PORT_MQTT 1883 +#define PORT_MQTTS 8883 + +/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, + * the rest are internal information. If we use higher bits we only do this on + * platforms that have a >= 64-bit type and then we use such a type for the + * protocol fields in the scheme definition. + */ +#define CURLPROTO_WS (1L << 30) +#define CURLPROTO_WSS ((curl_prot_t)1 << 31) +#define CURLPROTO_MQTTS (1LL << 32) + +#define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff) + +/* the default protocols accepting a redirect to */ +#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \ + CURLPROTO_FTPS) + +typedef curl_off_t curl_prot_t; + +/* This mask is for all the old protocols that are provided and defined in the + public header and shall exclude protocols added since which are not exposed + in the API */ +#define CURLPROTO_MASK 0x3fffffff + +/* Convenience defines for checking protocols or their SSL based version. Each + protocol scheme should only ever have a single CURLPROTO_ in its protocol + field. */ +#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_WS | \ + CURLPROTO_WSS) +#define PROTO_FAMILY_FTP (CURLPROTO_FTP | CURLPROTO_FTPS) +#define PROTO_FAMILY_POP3 (CURLPROTO_POP3 | CURLPROTO_POP3S) +#define PROTO_FAMILY_SMB (CURLPROTO_SMB | CURLPROTO_SMBS) +#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 + +/* When redirecting transfers. */ +typedef enum { + FOLLOW_NONE, /* not used within the function, a placeholder to allow + initing to this */ + FOLLOW_FAKE, /* only records stuff, not actually following */ + FOLLOW_RETRY, /* set if this is a request retry as opposed to a real + redirect following */ + FOLLOW_REDIR /* a full true redirect */ +} followtype; + +/* + * Specific protocol handler, an implementation of one or more URI schemes. + */ +struct Curl_protocol { + /* Complement to setup_connection_internals(). This is done before the + transfer "owns" the connection. */ + CURLcode (*setup_connection)(struct Curl_easy *data, + struct connectdata *conn); + + /* These two functions MUST be set to be protocol dependent */ + CURLcode (*do_it)(struct Curl_easy *data, bool *done); + CURLcode (*done)(struct Curl_easy *, CURLcode, bool); + + /* If the curl_do() function is better made in two halves, this + * curl_do_more() function will be called afterwards, if set. For example + * for doing the FTP stuff after the PASV/PORT command. + */ + CURLcode (*do_more)(struct Curl_easy *, int *); + + /* This function *MAY* be set to a protocol-dependent function that is run + * after the connect() and everything is done, as a step in the connection. + * The 'done' pointer points to a bool that should be set to TRUE if the + * function completes before return. If it does not complete, the caller + * should call the ->connecting() function until it is. + */ + CURLcode (*connect_it)(struct Curl_easy *data, bool *done); + + /* See above. */ + CURLcode (*connecting)(struct Curl_easy *data, bool *done); + CURLcode (*doing)(struct Curl_easy *data, bool *done); + + /* Called from the multi interface during the PROTOCONNECT phase, and it + should then return a proper fd set */ + CURLcode (*proto_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DOING phase, and it should + then return a proper fd set */ + CURLcode (*doing_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DO_MORE phase, and it should + then return a proper fd set */ + CURLcode (*domore_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DO_DONE, PERFORM and + WAITPERFORM phases, and it should then return a proper fd set. Not setting + this will make libcurl use the generic default one. */ + CURLcode (*perform_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* This function *MAY* be set to a protocol-dependent function that is run + * by the curl_disconnect(), as a step in the disconnection. If the handler + * is called because the connection has been considered dead, + * dead_connection is set to TRUE. The connection is (again) associated with + * the transfer here. + */ + CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, + bool dead_connection); + + /* If used, this function gets called from transfer.c to + allow the protocol to do extra handling in writing response to + the client. */ + CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen, + bool is_eos); + + /* If used, this function gets called from transfer.c to + allow the protocol to do extra handling in writing a single response + header line to the client. */ + CURLcode (*write_resp_hd)(struct Curl_easy *data, + const char *hd, size_t hdlen, bool is_eos); + + /* If used, this function checks for a connection managed by this + protocol and currently not in use, if it should be considered dead. */ + bool (*connection_is_dead)(struct Curl_easy *data, + struct connectdata *conn); + + /* attach() attaches this transfer to this connection */ + void (*attach)(struct Curl_easy *data, struct connectdata *conn); + + /* return CURLE_OK if a redirect to `newurl` should be followed, + CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change + the way the follow request is performed. */ + CURLcode (*follow)(struct Curl_easy *data, const char *newurl, + followtype type); +}; + +#define PROTOPT_NONE 0 /* nothing extra */ +#define PROTOPT_SSL (1 << 0) /* uses SSL */ +#define PROTOPT_DUAL (1 << 1) /* this protocol uses two connections */ +#define PROTOPT_CLOSEACTION (1 << 2) /* need action before socket close */ +/* some protocols will have to call the underlying functions without regard to + what exact state the socket signals. IE even if the socket says "readable", + the send function might need to be called while uploading, or vice versa. +*/ +#define PROTOPT_DIRLOCK (1 << 3) +#define PROTOPT_NONETWORK (1 << 4) /* protocol does not use the network! */ +#define PROTOPT_NEEDSPWD (1 << 5) /* needs a password, and if none is set it + gets a default */ +#define PROTOPT_NOURLQUERY (1 << 6) /* protocol cannot handle + URL query strings (?foo=bar) ! */ +#define PROTOPT_CREDSPERREQUEST (1 << 7) /* requires login credentials per + request instead of per + connection */ +#define PROTOPT_ALPN (1 << 8) /* set ALPN for this */ +/* (1 << 9) was PROTOPT_STREAM, now free */ +#define PROTOPT_URLOPTIONS (1 << 10) /* allow options part in the userinfo + field of the URL */ +#define PROTOPT_PROXY_AS_HTTP (1 << 11) /* allow this non-HTTP scheme over a + HTTP proxy as HTTP proxies may know + this protocol and act as + a gateway */ +#define PROTOPT_WILDCARD (1 << 12) /* protocol supports wildcard matching */ +#define PROTOPT_USERPWDCTRL (1 << 13) /* Allow "control bytes" (< 32 ASCII) in + username and password */ +#define PROTOPT_NOTCPPROXY (1 << 14) /* this protocol cannot proxy over TCP */ +#define PROTOPT_SSL_REUSE (1 << 15) /* this protocol may reuse an existing + SSL connection in the same family + without having PROTOPT_SSL. */ +#define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ + +/* Everything about a URI scheme. */ +struct Curl_scheme { + const char *name; /* URL scheme name in lowercase */ + const struct Curl_protocol *run; /* implementation, optional */ + curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single + specific protocol bit */ + curl_prot_t family; /* single bit for protocol family; the non-TLS name + of the protocol this is */ + uint32_t flags; /* Extra particular characteristics, see PROTOPT_* */ + uint16_t defport; /* Default port. */ +}; + +/* Get scheme definition for a URI scheme name + * @param scheme URI scheme name, case-insensitive + * @return NULL if scheme is not known + */ +const struct Curl_scheme *Curl_get_scheme(const char *scheme); +const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len); + +/* For direct access to a URI scheme */ +extern const struct Curl_scheme Curl_scheme_dict; +extern const struct Curl_scheme Curl_scheme_file; +extern const struct Curl_scheme Curl_scheme_ftp; +extern const struct Curl_scheme Curl_scheme_ftps; +extern const struct Curl_scheme Curl_scheme_gopher; +extern const struct Curl_scheme Curl_scheme_gophers; +extern const struct Curl_scheme Curl_scheme_http; +extern const struct Curl_scheme Curl_scheme_https; +extern const struct Curl_scheme Curl_scheme_imap; +extern const struct Curl_scheme Curl_scheme_imaps; +extern const struct Curl_scheme Curl_scheme_ldap; +extern const struct Curl_scheme Curl_scheme_ldaps; +extern const struct Curl_scheme Curl_scheme_mqtt; +extern const struct Curl_scheme Curl_scheme_mqtts; +extern const struct Curl_scheme Curl_scheme_pop3; +extern const struct Curl_scheme Curl_scheme_pop3s; +extern const struct Curl_scheme Curl_scheme_rtsp; +extern const struct Curl_scheme Curl_scheme_scp; +extern const struct Curl_scheme Curl_scheme_sftp; +extern const struct Curl_scheme Curl_scheme_smb; +extern const struct Curl_scheme Curl_scheme_smbs; +extern const struct Curl_scheme Curl_scheme_smtp; +extern const struct Curl_scheme Curl_scheme_smtps; +extern const struct Curl_scheme Curl_scheme_telnet; +extern const struct Curl_scheme Curl_scheme_tftp; +extern const struct Curl_scheme Curl_scheme_ws; +extern const struct Curl_scheme Curl_scheme_wss; + +#endif /* HEADER_CURL_PROTOCOL_H */
diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index a0b168c..dd82750 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c
@@ -34,53 +34,20 @@ #include "escape.h" #ifdef _WIN32 - -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= _WIN32_WINNT_VISTA && \ - !defined(CURL_WINDOWS_UWP) -# define HAVE_WIN_BCRYPTGENRANDOM -# include <bcrypt.h> -# ifdef _MSC_VER -# pragma comment(lib, "bcrypt.lib") -# endif -# ifndef STATUS_SUCCESS -# define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -# endif -#elif defined(USE_WIN32_CRYPTO) -# include <wincrypt.h> -# ifdef _MSC_VER -# pragma comment(lib, "advapi32.lib") -# endif +#include <bcrypt.h> +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #endif CURLcode Curl_win32_random(unsigned char *entropy, size_t length) { memset(entropy, 0, length); -#ifdef HAVE_WIN_BCRYPTGENRANDOM if(BCryptGenRandom(NULL, entropy, (ULONG)length, BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) return CURLE_FAILED_INIT; return CURLE_OK; -#elif defined(USE_WIN32_CRYPTO) - { - HCRYPTPROV hCryptProv = 0; - - if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return CURLE_FAILED_INIT; - - if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) { - CryptReleaseContext(hCryptProv, 0UL); - return CURLE_FAILED_INIT; - } - - CryptReleaseContext(hCryptProv, 0UL); - } - return CURLE_OK; -#else - return CURLE_NOT_BUILT_IN; -#endif } #endif @@ -229,12 +196,6 @@ unsigned char buffer[128]; DEBUGASSERT(num > 1); -#ifdef __clang_analyzer__ - /* This silences a scan-build warning about accessing this buffer with - uninitialized memory. */ - memset(buffer, 0, sizeof(buffer)); -#endif - if((num / 2 >= sizeof(buffer)) || !(num & 1)) { /* make sure it fits in the local buffer and that it is an odd number! */ DEBUGF(infof(data, "invalid buffer size with Curl_rand_hex"));
diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h index 03c69a0..afccd0a 100644 --- a/Utilities/cmcurl/lib/rand.h +++ b/Utilities/cmcurl/lib/rand.h
@@ -25,14 +25,14 @@ ***************************************************************************/ CURLcode Curl_rand_bytes(struct Curl_easy *data, #ifdef DEBUGBUILD - bool allow_env_override, + bool env_override, #endif unsigned char *rnd, size_t num); #ifdef DEBUGBUILD -#define Curl_rand(a, b, c) Curl_rand_bytes((a), TRUE, (b), (c)) +#define Curl_rand(a, b, c) Curl_rand_bytes(a, TRUE, b, c) #else -#define Curl_rand(a, b, c) Curl_rand_bytes((a), (b), (c)) +#define Curl_rand(a, b, c) Curl_rand_bytes(a, b, c) #endif /*
diff --git a/Utilities/cmcurl/lib/ratelimit.c b/Utilities/cmcurl/lib/ratelimit.c index c7ba646..dc01375 100644 --- a/Utilities/cmcurl/lib/ratelimit.c +++ b/Utilities/cmcurl/lib/ratelimit.c
@@ -23,64 +23,18 @@ ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #include "ratelimit.h" -#define CURL_US_PER_SEC 1000000 -#define CURL_RLIMIT_MIN_CHUNK (16 * 1024) -#define CURL_RLIMIT_MAX_STEPS 2 /* 500ms interval */ +#define CURL_US_PER_SEC 1000000 +#define CURL_RLIMIT_MIN_RATE (4 * 1024) /* minimum step rate */ +#define CURL_RLIMIT_STEP_MIN_MS 2 /* minimum step duration */ -void Curl_rlimit_init(struct Curl_rlimit *r, - curl_off_t rate_per_s, - curl_off_t burst_per_s, - const struct curltime *pts) -{ - curl_off_t rate_steps; - - DEBUGASSERT(rate_per_s >= 0); - DEBUGASSERT(burst_per_s >= rate_per_s || !burst_per_s); - DEBUGASSERT(pts); - r->step_us = CURL_US_PER_SEC; - r->rate_per_step = rate_per_s; - r->burst_per_step = burst_per_s; - /* On rates that are multiples of CURL_RLIMIT_MIN_CHUNK, we reduce - * the interval `step_us` from 1 second to smaller steps with at - * most CURL_RLIMIT_MAX_STEPS. - * Smaller means more CPU, but also more precision. */ - rate_steps = rate_per_s / CURL_RLIMIT_MIN_CHUNK; - rate_steps = CURLMIN(rate_steps, CURL_RLIMIT_MAX_STEPS); - if(rate_steps >= 2) { - r->step_us /= rate_steps; - r->rate_per_step /= rate_steps; - r->burst_per_step /= rate_steps; - } - r->tokens = r->rate_per_step; - r->spare_us = 0; - r->ts = *pts; - r->blocked = FALSE; -} - -void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts) -{ - r->tokens = r->rate_per_step; - r->spare_us = 0; - r->ts = *pts; -} - -bool Curl_rlimit_active(struct Curl_rlimit *r) -{ - return (r->rate_per_step > 0) || r->blocked; -} - -bool Curl_rlimit_is_blocked(struct Curl_rlimit *r) -{ - return r->blocked; -} - -static void ratelimit_update(struct Curl_rlimit *r, - const struct curltime *pts) +static void rlimit_update(struct Curl_rlimit *r, + const struct curltime *pts) { timediff_t elapsed_us, elapsed_steps; - curl_off_t token_gain; + int64_t token_gain; DEBUGASSERT(r->rate_per_step); if((r->ts.tv_sec == pts->tv_sec) && (r->ts.tv_usec == pts->tv_usec)) @@ -102,31 +56,156 @@ r->spare_us = elapsed_us % r->step_us; /* How many tokens did we gain since the last update? */ - if(r->rate_per_step > (CURL_OFF_T_MAX / elapsed_steps)) - token_gain = CURL_OFF_T_MAX; + if(r->rate_per_step > (INT64_MAX / elapsed_steps)) + token_gain = INT64_MAX; else { token_gain = r->rate_per_step * elapsed_steps; } - /* Limit the token again by the burst rate per second (if set), so we + if((INT64_MAX - token_gain) > r->tokens) + r->tokens += token_gain; + else + r->tokens = INT64_MAX; + + /* Limit the token again by the burst rate (if set), so we * do not suddenly have a huge number of tokens after inactivity. */ - r->tokens += token_gain; if(r->burst_per_step && (r->tokens > r->burst_per_step)) { r->tokens = r->burst_per_step; } } -curl_off_t Curl_rlimit_avail(struct Curl_rlimit *r, - const struct curltime *pts) +static void rlimit_tune_steps(struct Curl_rlimit *r, + int64_t tokens_total) +{ + int64_t tokens_last, tokens_main, msteps; + + /* Tune the ratelimit at the start *if* we know how many tokens + * are expected to be consumed in total. + * The reason for tuning is that rlimit provides tokens to be consumed + * per "step" which starts out to be a second. The tokens may be consumed + * in full at the beginning of a step. The remainder of the second will + * have no tokens available, effectively blocking the consumption and + * so keeping the "step average" in line. + * This works will up to the last step. When no more tokens are needed, + * no wait will happen and the last step would be too fast. This is + * especially noticeable when only a few steps are needed. + * + * Example: downloading 1.5kb with a ratelimit of 1k could be done in + * roughly 1 second (1k in the first second and the 0.5 at the start of + * the second one). + * + * The tuning tries to make the last step small, using only + * 1 percent of the total tokens (at least 1). The rest of the tokens + * are to be consumed in the steps before by adjusting the duration of + * the step and the amount of tokens it provides. */ + if(!r->rate_per_step || + (tokens_total <= 1) || + (tokens_total > (INT64_MAX / 1000))) + return; + + /* Calculate tokens for the last step and the ones before. */ + tokens_last = tokens_total / 100; + if(!tokens_last) /* less than 100 total, use 1 */ + tokens_last = 1; + else if(tokens_last > CURL_RLIMIT_MIN_RATE) + tokens_last = CURL_RLIMIT_MIN_RATE; + DEBUGASSERT(tokens_last); + tokens_main = tokens_total - tokens_last; + DEBUGASSERT(tokens_main); + + /* how many milli-steps will it take to consume those, give the + * original rate limit per second? */ + DEBUGASSERT(r->step_us == CURL_US_PER_SEC); + + msteps = (tokens_main * 1000 / r->rate_per_step); + if(msteps < CURL_RLIMIT_STEP_MIN_MS) { + /* Steps this small will not work. Do not tune. */ + return; + } + else if(msteps < 1000) { + /* It needs less than one step to provide the needed tokens. + * Make it exactly that long and with exactly those tokens. */ + r->step_us = (timediff_t)msteps * 1000; + r->rate_per_step = tokens_main; + r->tokens = r->rate_per_step; + } + else { + /* More than 1 step. Spread the remainder milli steps and + * the tokens they need to provide across all steps. If integer + * arithmetic can do it. */ + curl_off_t ms_unaccounted = (msteps % 1000); + curl_off_t mstep_inc = (ms_unaccounted / (msteps / 1000)); + if(mstep_inc) { + curl_off_t rate_inc = ((r->rate_per_step * mstep_inc) / 1000); + if(rate_inc) { + r->step_us = CURL_US_PER_SEC + ((timediff_t)mstep_inc * 1000); + r->rate_per_step += rate_inc; + r->tokens = r->rate_per_step; + } + } + } + + if(r->burst_per_step) + r->burst_per_step = r->rate_per_step; +} + +void Curl_rlimit_init(struct Curl_rlimit *r, + int64_t rate_per_sec, + int64_t burst_per_sec, + const struct curltime *pts) +{ + DEBUGASSERT(rate_per_sec >= 0); + DEBUGASSERT(burst_per_sec >= rate_per_sec || !burst_per_sec); + DEBUGASSERT(pts); + r->rate_per_step = r->rate_per_sec = rate_per_sec; + r->burst_per_step = r->burst_per_sec = burst_per_sec; + r->step_us = CURL_US_PER_SEC; + r->spare_us = 0; + r->tokens = r->rate_per_step; + r->ts = *pts; + r->blocked = FALSE; +} + +void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts, + int64_t total_tokens) +{ + /* A start always resets the values to initial defaults, then + * fine tunes the intervals for the total_tokens expected. */ + r->rate_per_step = r->rate_per_sec; + r->burst_per_step = r->burst_per_sec; + r->step_us = CURL_US_PER_SEC; + r->spare_us = 0; + r->tokens = r->rate_per_step; + r->ts = *pts; + rlimit_tune_steps(r, total_tokens); +} + +int64_t Curl_rlimit_per_step(struct Curl_rlimit *r) +{ + return r->rate_per_step; +} + +bool Curl_rlimit_active(struct Curl_rlimit *r) +{ + return (r->rate_per_step > 0) || r->blocked; +} + +bool Curl_rlimit_is_blocked(struct Curl_rlimit *r) +{ + return (bool)r->blocked; +} + +int64_t Curl_rlimit_avail(struct Curl_rlimit *r, + const struct curltime *pts) { if(r->blocked) return 0; else if(r->rate_per_step) { - ratelimit_update(r, pts); + rlimit_update(r, pts); return r->tokens; } else - return CURL_OFF_T_MAX; + return INT64_MAX; } void Curl_rlimit_drain(struct Curl_rlimit *r, @@ -136,20 +215,19 @@ if(r->blocked || !r->rate_per_step) return; - ratelimit_update(r, pts); -#if SIZEOF_CURL_OFF_T <= SIZEOF_SIZE_T - if(tokens > CURL_OFF_T_MAX) { - r->tokens = CURL_OFF_T_MIN; - return; + rlimit_update(r, pts); +#if 8 <= SIZEOF_SIZE_T + if(tokens > INT64_MAX) { + r->tokens = INT64_MAX; } else #endif { - curl_off_t val = (curl_off_t)tokens; - if((CURL_OFF_T_MIN + val) < r->tokens) + int64_t val = (int64_t)tokens; + if((INT64_MIN + val) < r->tokens) r->tokens -= val; else - r->tokens = CURL_OFF_T_MIN; + r->tokens = INT64_MIN; } } @@ -160,14 +238,18 @@ if(r->blocked || !r->rate_per_step) return 0; - ratelimit_update(r, pts); + rlimit_update(r, pts); if(r->tokens > 0) return 0; /* How much time will it take tokens to become positive again? * Deduct `spare_us` and check against already elapsed time */ - wait_us = (1 + (-r->tokens / r->rate_per_step)) * r->step_us; - wait_us -= r->spare_us; + wait_us = r->step_us - r->spare_us; + if(r->tokens < 0) { + curl_off_t debt_pct = ((-r->tokens) * 100 / r->rate_per_step); + if(debt_pct) + wait_us += (r->step_us * debt_pct / 100); + } elapsed_us = curlx_ptimediff_us(pts, &r->ts); if(elapsed_us >= wait_us) @@ -176,6 +258,21 @@ return (wait_us + 999) / 1000; /* in milliseconds */ } +timediff_t Curl_rlimit_next_step_ms(struct Curl_rlimit *r, + const struct curltime *pts) +{ + if(!r->blocked && r->rate_per_step) { + timediff_t elapsed_us, next_us; + + elapsed_us = curlx_ptimediff_us(pts, &r->ts) + r->spare_us; + if(r->step_us > elapsed_us) { + next_us = r->step_us - elapsed_us; + return (next_us + 999) / 1000; /* in milliseconds */ + } + } + return 0; +} + void Curl_rlimit_block(struct Curl_rlimit *r, bool activate, const struct curltime *pts) @@ -188,7 +285,7 @@ if(!r->blocked) { /* Start rate limiting fresh. The amount of time this was blocked * does not generate extra tokens. */ - Curl_rlimit_start(r, pts); + Curl_rlimit_start(r, pts, -1); } else { r->tokens = 0;
diff --git a/Utilities/cmcurl/lib/ratelimit.h b/Utilities/cmcurl/lib/ratelimit.h index 07f11b8..3c3e38b 100644 --- a/Utilities/cmcurl/lib/ratelimit.h +++ b/Utilities/cmcurl/lib/ratelimit.h
@@ -1,5 +1,5 @@ -#ifndef HEADER_Curl_rlimit_H -#define HEADER_Curl_rlimit_H +#ifndef HEADER_CURL_RLIMIT_H +#define HEADER_CURL_RLIMIT_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -28,8 +28,10 @@ struct Curl_easy; /* This is a rate limiter that provides "tokens" to be consumed - * per second with a "burst" rate limitation. Example: - * A rate limit of 1 megabyte per second with a burst rate of 1.5MB. + * per second. In the literature, this is referred to as a + * "token bucket" (https://en.wikipedia.org/wiki/Token_bucket). + * Example: + * A rate limit of 1 megabyte per second. * - initially 1 million tokens are available. * - these are drained in the first second. * - checking available tokens before the 2nd second will return 0. @@ -43,44 +45,57 @@ * - setting "burst" to the same value as "rate" would make a * download always try to stay *at/below* the rate and slow times will * not generate extra tokens. + * * A rate limit can be blocked, causing the available tokens to become * always 0 until unblocked. After unblocking, the rate limiting starts * again with no history of the past. + * * Finally, a rate limiter with rate 0 will always have CURL_OFF_T_MAX * tokens available, unless blocked. */ struct Curl_rlimit { - curl_off_t rate_per_step; /* rate tokens are generated per step us */ - curl_off_t burst_per_step; /* burst rate of tokens per step us */ + int64_t rate_per_sec; /* rate tokens generated per second */ + int64_t burst_per_sec; /* burst rate of tokens per second */ + int64_t rate_per_step; /* rate tokens generated per step us */ + int64_t burst_per_step; /* burst rate of tokens per step us */ timediff_t step_us; /* microseconds between token increases */ - curl_off_t tokens; /* tokens available in the next second */ + int64_t tokens; /* tokens available in the next second */ timediff_t spare_us; /* microseconds unaffecting tokens */ struct curltime ts; /* time of the last update */ BIT(blocked); /* blocking sets available tokens to 0 */ }; void Curl_rlimit_init(struct Curl_rlimit *r, - curl_off_t rate_per_s, - curl_off_t burst_per_s, + int64_t rate_per_sec, + int64_t burst_per_sec, const struct curltime *pts); -/* Start ratelimiting with the given timestamp. Resets available tokens. */ -void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts); +/* Start ratelimiting with the given timestamp. Resets available tokens. + * `total_tokens` is either -1 or the number of total tokens expected + * to be consumed. */ +void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts, + int64_t total_tokens); /* How many milliseconds to wait until token are available again. */ timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r, const struct curltime *pts); +/* When the rate limit will update its tokens again */ +timediff_t Curl_rlimit_next_step_ms(struct Curl_rlimit *r, + const struct curltime *pts); + /* Return if rate limiting of tokens is active */ bool Curl_rlimit_active(struct Curl_rlimit *r); bool Curl_rlimit_is_blocked(struct Curl_rlimit *r); +int64_t Curl_rlimit_per_step(struct Curl_rlimit *r); /* Return how many tokens are available to spend, may be negative */ -curl_off_t Curl_rlimit_avail(struct Curl_rlimit *r, - const struct curltime *pts); +int64_t Curl_rlimit_avail(struct Curl_rlimit *r, + const struct curltime *pts); -/* Drain tokens from the ratelimit, return how many are now available. */ +/* Drain tokens from the ratelimit, give an estimate of how many tokens + * remain to be drained in the future (-1 for unknown). */ void Curl_rlimit_drain(struct Curl_rlimit *r, size_t tokens, const struct curltime *pts); @@ -90,4 +105,4 @@ bool activate, const struct curltime *pts); -#endif /* HEADER_Curl_rlimit_H */ +#endif /* HEADER_CURL_RLIMIT_H */
diff --git a/Utilities/cmcurl/lib/request.c b/Utilities/cmcurl/lib/request.c index ad7f628..c414383 100644 --- a/Utilities/cmcurl/lib/request.c +++ b/Utilities/cmcurl/lib/request.c
@@ -65,6 +65,11 @@ req->httpversion = 0; req->sendbuf_hds_len = 0; + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif + result = Curl_client_start(data); if(result) return result; @@ -102,9 +107,6 @@ if(!aborted) (void)req_flush(data); Curl_client_reset(data); -#ifndef CURL_DISABLE_DOH - Curl_doh_close(data); -#endif return CURLE_OK; } @@ -112,14 +114,20 @@ { struct curltime t0 = { 0, 0 }; - Curl_safefree(req->newurl); + curlx_safefree(req->newurl); + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif +#ifndef CURL_DISABLE_COOKIES + curlx_safefree(req->cookiehost); +#endif Curl_client_reset(data); if(req->sendbuf_init) Curl_bufq_reset(&req->sendbuf); -#ifndef CURL_DISABLE_DOH - Curl_doh_close(data); -#endif + /* clear any resolve data */ + Curl_resolv_destroy_all(data); /* Can no longer memset() this struct as we need to keep some state */ req->size = -1; req->maxdownload = -1; @@ -132,7 +140,7 @@ req->headerline = 0; req->offset = 0; req->httpcode = 0; - req->keepon = 0; + req->io_flags = 0; req->upgr101 = UPGR101_NONE; req->sendbuf_hds_len = 0; req->timeofdoc = 0; @@ -153,16 +161,24 @@ req->ignorebody = FALSE; req->http_bodyless = FALSE; req->chunk = FALSE; + req->resp_trailer = FALSE; req->ignore_cl = FALSE; req->upload_chunky = FALSE; req->no_body = data->set.opt_no_body; req->authneg = FALSE; req->shutdown = FALSE; + /* Unpause all directions */ + Curl_rlimit_block(&data->progress.dl.rlimit, FALSE, &t0); + Curl_rlimit_block(&data->progress.ul.rlimit, FALSE, &t0); } void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) { - Curl_safefree(req->newurl); + curlx_safefree(req->newurl); + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif if(req->sendbuf_init) Curl_bufq_free(&req->sendbuf); Curl_client_cleanup(data); @@ -253,7 +269,7 @@ { DEBUGASSERT(!data->req.upload_done); data->req.upload_done = TRUE; - data->req.keepon &= ~KEEP_SEND; /* we are done sending */ + CURL_REQ_CLEAR_SEND(data); Curl_pgrsTime(data, TIMER_POSTRANSFER); Curl_creader_done(data, data->req.upload_aborted); @@ -292,7 +308,7 @@ return result; if(!Curl_bufq_is_empty(&data->req.sendbuf)) { DEBUGF(infof(data, "Curl_req_flush(len=%zu) -> EAGAIN", - Curl_bufq_len(&data->req.sendbuf))); + Curl_bufq_len(&data->req.sendbuf))); return CURLE_AGAIN; } } @@ -416,22 +432,22 @@ bool Curl_req_want_send(struct Curl_easy *data) { /* Not done and upload not blocked and either one of - * - KEEP_SEND + * - REQ_IO_SEND * - request has buffered data to send * - connection has pending data to send */ return !data->req.done && !Curl_rlimit_is_blocked(&data->progress.ul.rlimit) && - ((data->req.keepon & KEEP_SEND) || + (CURL_REQ_WANT_SEND(data) || !Curl_req_sendbuf_empty(data) || Curl_xfer_needs_flush(data)); } bool Curl_req_want_recv(struct Curl_easy *data) { - /* Not done and download not blocked and KEEP_RECV */ + /* Not done and download not blocked and want RECV */ return !data->req.done && !Curl_rlimit_is_blocked(&data->progress.dl.rlimit) && - (data->req.keepon & KEEP_RECV); + CURL_REQ_WANT_RECV(data); } bool Curl_req_done_sending(struct Curl_easy *data) @@ -467,7 +483,7 @@ if(!data->req.upload_done) { Curl_bufq_reset(&data->req.sendbuf); data->req.upload_aborted = TRUE; - data->req.keepon &= ~KEEP_SEND; + CURL_REQ_CLEAR_SEND(data); return req_set_upload_done(data); } return CURLE_OK; @@ -479,8 +495,8 @@ * We might still be paused on receive client writes though, so * keep those bits around. */ CURLcode result = CURLE_OK; - if(data->req.keepon & KEEP_SEND) + if(CURL_REQ_WANT_SEND(data)) result = Curl_req_abort_sending(data); - data->req.keepon &= ~(KEEP_RECV | KEEP_SEND); + CURL_REQ_CLEAR_IO(data); return result; }
diff --git a/Utilities/cmcurl/lib/request.h b/Utilities/cmcurl/lib/request.h index 389ae4d..6948d79 100644 --- a/Utilities/cmcurl/lib/request.h +++ b/Utilities/cmcurl/lib/request.h
@@ -31,8 +31,27 @@ /* forward declarations */ struct UserDefined; +/* Bits on the io_flags member of SingleRequest */ +#define REQ_IO_RECV (1 << 0) /* there is or may be data to read */ +#define REQ_IO_SEND (1 << 1) /* there is or may be data to write */ + +/* Low level request receive/send io_flags checks. */ +#define CURL_REQ_WANT_SEND(d) ((d)->req.io_flags & REQ_IO_SEND) +#define CURL_REQ_WANT_RECV(d) ((d)->req.io_flags & REQ_IO_RECV) +#define CURL_REQ_WANT_IO(d) \ + ((d)->req.io_flags & (REQ_IO_RECV | REQ_IO_SEND)) +/* Low level request receive/send io_flags manipulations. */ +#define CURL_REQ_SET_SEND(d) ((d)->req.io_flags |= REQ_IO_SEND) +#define CURL_REQ_SET_RECV(d) ((d)->req.io_flags |= REQ_IO_RECV) +#define CURL_REQ_CLEAR_SEND(d) \ + ((d)->req.io_flags &= (uint8_t)~REQ_IO_SEND) +#define CURL_REQ_CLEAR_RECV(d) \ + ((d)->req.io_flags &= (uint8_t)~REQ_IO_RECV) +#define CURL_REQ_CLEAR_IO(d) \ + ((d)->req.io_flags &= (uint8_t)~(REQ_IO_RECV | REQ_IO_SEND)) + enum expect100 { - EXP100_SEND_DATA, /* enough waiting, just send the body now */ + EXP100_SEND_DATA, /* enough waiting, send the body now */ EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */ EXP100_SENDING_REQUEST, /* still sending the request but will wait for the 100 header once done with the request */ @@ -59,6 +78,8 @@ -1 means unlimited */ curl_off_t bytecount; /* total number of bytes read */ curl_off_t writebytecount; /* number of bytes written */ + curl_off_t offset; /* possible resume offset read from the + Content-Range: header */ struct curltime start; /* transfer started at this time */ unsigned int headerbytecount; /* received server headers (not CONNECT @@ -72,11 +93,8 @@ in a CURLE_GOT_NOTHING error code */ int headerline; /* counts header lines to better track the first one */ - curl_off_t offset; /* possible resume offset read from the - Content-Range: header */ int httpcode; /* error code from the 'HTTP/1.? XXX' or 'RTSP/1.? XXX' line */ - int keepon; unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */ unsigned char httpversion; /* Version in response (09, 10, 11, etc.) */ enum upgrade101 upgr101; /* 101 upgrade state */ @@ -94,7 +112,15 @@ header data */ char *newurl; /* Set to the new URL to use when a redirect or a retry is wanted */ + uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */ + char *userpwd; /* auth header */ +#ifndef CURL_DISABLE_PROXY + char *proxyuserpwd; /* proxy auth header */ +#endif +#ifndef CURL_DISABLE_COOKIES + char *cookiehost; +#endif #ifndef CURL_DISABLE_COOKIES unsigned char setcookies; #endif @@ -109,8 +135,8 @@ BIT(eos_sent); /* iff EOS has been sent to the server */ BIT(rewind_read); /* iff reader needs rewind at next start */ BIT(upload_done); /* set to TRUE when all request data has been sent */ - BIT(upload_aborted); /* set to TRUE when upload was aborted. Will also - * show `upload_done` as TRUE. */ + BIT(upload_aborted); /* set to TRUE when upload was aborted. Also + * shows `upload_done` as TRUE. */ BIT(ignorebody); /* we read a response-body but we ignore it! */ BIT(http_bodyless); /* HTTP response status code is between 100 and 199, 204 or 304 */ @@ -174,11 +200,11 @@ * they will be buffered. Use `Curl_req_flush()` to make sure * bytes are really send. * @param data the transfer making the request - * @param buf the complete header bytes, no body + * @param req the complete header bytes, no body * @param httpversion version used in request (09, 10, 11, etc.) * @return CURLE_OK (on blocking with *pnwritten == 0) or error. */ -CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf, +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req, unsigned char httpversion); /** @@ -207,13 +233,13 @@ /** * Stop sending any more request data to the server. - * Will clear the send buffer and mark request sending as done. + * Clear the send buffer and mark request sending as done. */ CURLcode Curl_req_abort_sending(struct Curl_easy *data); /** * Stop sending and receiving any more request data. - * Will abort sending if not done. + * Abort sending if not done. */ CURLcode Curl_req_stop_send_recv(struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index 93bcbeb..b08767f 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c
@@ -22,10 +22,11 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "rtsp.h" #ifndef CURL_DISABLE_RTSP -#include "urldata.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" @@ -33,12 +34,11 @@ #include "http.h" #include "url.h" #include "progress.h" -#include "rtsp.h" #include "strcase.h" #include "select.h" #include "connect.h" #include "cfilters.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "bufref.h" #include "curlx/strparse.h" @@ -66,45 +66,13 @@ /* RTSP transfer data */ struct RTSP { - long CSeq_sent; /* CSeq of this request */ - long CSeq_recv; /* CSeq received */ + uint32_t CSeq_sent; /* CSeq of this request */ + uint32_t CSeq_recv; /* CSeq received */ }; #define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \ ((unsigned int)((unsigned char)((p)[3])))) -/* protocol-specific functions set up to be called by the main engine */ -static CURLcode rtsp_do(struct Curl_easy *data, bool *done); -static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature); -static CURLcode rtsp_connect(struct Curl_easy *data, bool *done); -static CURLcode rtsp_do_pollset(struct Curl_easy *data, - struct easy_pollset *ps); - -/* - * Parse and write out an RTSP response. - * @param data the transfer - * @param conn the connection - * @param buf data read from connection - * @param blen amount of data in buf - * @param is_eos TRUE iff this is the last write - * @param readmore out, TRUE iff complete buf was consumed and more data - * is needed - */ -static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, - const char *buf, - size_t blen, - bool is_eos); -static CURLcode rtsp_rtp_write_resp_hd(struct Curl_easy *data, - const char *buf, - size_t blen, - bool is_eos); - -static CURLcode rtsp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static unsigned int rtsp_conncheck(struct Curl_easy *data, - struct connectdata *check, - unsigned int checks_to_perform); - /* this returns the socket to wait for in the DO and DOING state for the multi interface and then we are always _sending_ a request and thus we wait for the single socket to become writable only */ @@ -115,39 +83,6 @@ return Curl_pollset_add_out(data, ps, data->conn->sock[FIRSTSOCKET]); } -static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, - size_t len); -static CURLcode rtsp_parse_transport(struct Curl_easy *data, - const char *transport); - -/* - * RTSP handler interface. - */ -const struct Curl_handler Curl_handler_rtsp = { - "rtsp", /* scheme */ - rtsp_setup_connection, /* setup_connection */ - rtsp_do, /* do_it */ - rtsp_done, /* done */ - ZERO_NULL, /* do_more */ - rtsp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - rtsp_do_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - Curl_http_perform_pollset, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - rtsp_rtp_write_resp, /* write_resp */ - rtsp_rtp_write_resp_hd, /* write_resp_hd */ - rtsp_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - Curl_http_follow, /* follow */ - PORT_RTSP, /* defport */ - CURLPROTO_RTSP, /* protocol */ - CURLPROTO_RTSP, /* family */ - PROTOPT_CONN_REUSE /* flags */ -}; - #define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */ static void rtsp_easy_dtor(void *key, size_t klen, void *entry) @@ -191,20 +126,13 @@ /* * Function to check on various aspects of a connection. */ -static unsigned int rtsp_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) +static bool rtsp_conn_is_dead(struct Curl_easy *data, + struct connectdata *conn) { - unsigned int ret_val = CONNRESULT_NONE; - (void)data; - - if(checks_to_perform & CONNCHECK_ISDEAD) { - bool input_pending; - if(!Curl_conn_is_alive(data, conn, &input_pending)) - ret_val |= CONNRESULT_DEAD; - } - - return ret_val; + bool input_pending; + /* Contrary to default handling, this protocol allows pending + * input on an unused connection. */ + return !Curl_conn_is_alive(data, conn, &input_pending); } static CURLcode rtsp_connect(struct Curl_easy *data, bool *done) @@ -232,7 +160,7 @@ struct rtsp_conn *rtspc = Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN); struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); - CURLcode httpStatus; + CURLcode result; if(!rtspc || !rtsp) return CURLE_FAILED_INIT; @@ -241,20 +169,20 @@ if(data->set.rtspreq == RTSPREQ_RECEIVE) premature = TRUE; - httpStatus = Curl_http_done(data, status, premature); + result = Curl_http_done(data, status, premature); - if(!status && !httpStatus) { + if(!status && !result) { /* Check the sequence numbers */ - long CSeq_sent = rtsp->CSeq_sent; - long CSeq_recv = rtsp->CSeq_recv; + uint32_t CSeq_sent = rtsp->CSeq_sent; + uint32_t CSeq_recv = rtsp->CSeq_recv; if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) { failf(data, - "The CSeq of this request %ld did not match the response %ld", + "The CSeq of this request %u did not match the response %u", CSeq_sent, CSeq_recv); return CURLE_RTSP_CSEQ_ERROR; } if(data->set.rtspreq == RTSPREQ_RECEIVE && (rtspc->rtp_channel == -1)) { - infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv); + infof(data, "Got an RTP Receive with a CSeq of %u", CSeq_recv); } if(data->set.rtspreq == RTSPREQ_RECEIVE && data->req.eos_written) { @@ -263,7 +191,7 @@ } } - return httpStatus; + return result; } static CURLcode rtsp_setup_body(struct Curl_easy *data, @@ -306,7 +234,7 @@ /* As stated in the http comments, it is probably not wise to * actually set a custom Content-Length in the headers */ if(!Curl_checkheaders(data, STRCONST("Content-Length"))) { - result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T"\r\n", + result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T "\r\n", req_clen); if(result) return result; @@ -346,11 +274,11 @@ { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - Curl_RtspReq rtspreq = data->set.rtspreq; + const Curl_RtspReq rtspreq = data->set.rtspreq; struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); struct dynbuf req_buffer; - unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort of... */ - + const unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort + of... */ const char *p_request = NULL; const char *p_session_id = NULL; const char *p_accept = NULL; @@ -382,7 +310,7 @@ return CURLE_OUT_OF_MEMORY; data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->handler->protocol; + data->state.first_remote_protocol = conn->scheme->protocol; } /* Setup the 'p_request' pointer to the proper p_request string @@ -505,13 +433,13 @@ } } - /* The User-Agent string might have been allocated in url.c already, because + /* The User-Agent string might have been allocated already, because it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ if(Curl_checkheaders(data, STRCONST("User-Agent")) && data->state.aptr.uagent) { - Curl_safefree(data->state.aptr.uagent); + curlx_safefree(data->state.aptr.uagent); } else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && data->set.str[STRING_USERAGENT]) { @@ -525,12 +453,12 @@ goto out; #ifndef CURL_DISABLE_PROXY - p_proxyuserpwd = data->state.aptr.proxyuserpwd; + p_proxyuserpwd = data->req.proxyuserpwd; #endif - p_userpwd = data->state.aptr.userpwd; + p_userpwd = data->req.userpwd; /* Referrer */ - Curl_safefree(data->state.aptr.ref); + curlx_safefree(data->state.aptr.ref); if(Curl_bufref_ptr(&data->state.referer) && !Curl_checkheaders(data, STRCONST("Referer"))) data->state.aptr.ref = @@ -573,7 +501,7 @@ result = curlx_dyn_addf(&req_buffer, "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ - "CSeq: %ld\r\n", /* CSeq */ + "CSeq: %u\r\n", /* CSeq */ p_request, p_stream_uri, rtsp->CSeq_sent); if(result) goto out; @@ -610,12 +538,6 @@ p_proxyuserpwd ? p_proxyuserpwd : "", p_userpwd ? p_userpwd : ""); - /* - * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM - * with basic and digest, it will be freed anyway by the next request - */ - Curl_safefree(data->state.aptr.userpwd); - if(result) goto out; @@ -685,6 +607,48 @@ return CURLE_OK; } +static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, + size_t len) +{ + size_t wrote; + curl_write_callback writeit; + void *user_ptr; + + if(len == 0) { + failf(data, "Cannot write a 0 size RTP packet."); + return CURLE_WRITE_ERROR; + } + + /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that + function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP + data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA + pointer to write out the RTP data. */ + if(data->set.fwrite_rtp) { + writeit = data->set.fwrite_rtp; + user_ptr = data->set.rtp_out; + } + else { + writeit = data->set.fwrite_func; + user_ptr = data->set.out; + } + + Curl_set_in_callback(data, TRUE); + wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr); + Curl_set_in_callback(data, FALSE); + + if(wrote == CURL_WRITEFUNC_PAUSE) { + failf(data, "Cannot pause RTP"); + return CURLE_WRITE_ERROR; + } + + if(wrote != len) { + failf(data, "Failed writing RTP data"); + return CURLE_WRITE_ERROR; + } + + return CURLE_OK; +} + static CURLcode rtsp_filter_rtp(struct Curl_easy *data, struct rtsp_conn *rtspc, const char *buf, @@ -710,7 +674,7 @@ /* This could be the next response, no consume and return */ if(*pconsumed) { DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " - "skipping %zd bytes of junk", *pconsumed)); + "skipping %zu bytes of junk", *pconsumed)); } rtspc->state = RTP_PARSE_SKIP; rtspc->in_header = TRUE; @@ -798,6 +762,18 @@ break; rtp_buf = curlx_dyn_ptr(&rtspc->buf); rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4; + if(rtspc->rtp_len == 4) { + /* zero-length payload, the 4-byte header is the complete RTP + message. Dispatch immediately without entering RTP_PARSE_DATA. */ + DEBUGF(infof(data, "RTP write channel %d rtp_len %zu (no payload)", + rtspc->rtp_channel, rtspc->rtp_len)); + result = rtp_client_write(data, rtp_buf, rtspc->rtp_len); + curlx_dyn_free(&rtspc->buf); + rtspc->state = RTP_PARSE_SKIP; + if(result) + goto out; + break; + } rtspc->state = RTP_PARSE_DATA; break; } @@ -848,6 +824,16 @@ return result; } +/* + * Parse and write out an RTSP response. + * @param data the transfer + * @param conn the connection + * @param buf data read from connection + * @param blen amount of data in buf + * @param is_eos TRUE iff this is the last write + * @param readmore out, TRUE iff complete buf was consumed and more data + * is needed + */ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, const char *buf, size_t blen, @@ -916,8 +902,8 @@ /* we SHOULD have consumed all bytes, unless the response is borked. * In which case we write out the left over bytes, letting the client * writer deal with it (it will report EXCESS and fail the transfer). */ - DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d " - " rtspc->state=%d, req.size=%" FMT_OFF_T ")", + DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d, " + "rtspc->state=%d, req.size=%" FMT_OFF_T ")", blen, rtspc->in_header, data->req.done, rtspc->state, data->req.size)); if(!result && (is_eos || blen)) { @@ -928,7 +914,7 @@ out: if((data->set.rtspreq == RTSPREQ_RECEIVE) && (rtspc->state == RTP_PARSE_SKIP)) { - /* In special mode RECEIVE, we just process one chunk of network + /* In special mode RECEIVE, we process one chunk of network * data, so we stop the transfer here, if we have no incomplete * RTP message pending. */ data->req.download_done = TRUE; @@ -944,118 +930,6 @@ return rtsp_rtp_write_resp(data, buf, blen, is_eos); } -static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, - size_t len) -{ - size_t wrote; - curl_write_callback writeit; - void *user_ptr; - - if(len == 0) { - failf(data, "Cannot write a 0 size RTP packet."); - return CURLE_WRITE_ERROR; - } - - /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that - function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP - data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA - pointer to write out the RTP data. */ - if(data->set.fwrite_rtp) { - writeit = data->set.fwrite_rtp; - user_ptr = data->set.rtp_out; - } - else { - writeit = data->set.fwrite_func; - user_ptr = data->set.out; - } - - Curl_set_in_callback(data, TRUE); - wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr); - Curl_set_in_callback(data, FALSE); - - if(CURL_WRITEFUNC_PAUSE == wrote) { - failf(data, "Cannot pause RTP"); - return CURLE_WRITE_ERROR; - } - - if(wrote != len) { - failf(data, "Failed writing RTP data"); - return CURLE_WRITE_ERROR; - } - - return CURLE_OK; -} - -CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) -{ - if(checkprefix("CSeq:", header)) { - curl_off_t CSeq = 0; - struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); - const char *p = &header[5]; - if(!rtsp) - return CURLE_FAILED_INIT; - curlx_str_passblanks(&p); - if(curlx_str_number(&p, &CSeq, LONG_MAX)) { - failf(data, "Unable to read the CSeq header: [%s]", header); - return CURLE_RTSP_CSEQ_ERROR; - } - rtsp->CSeq_recv = (long)CSeq; /* mark the request */ - data->state.rtsp_CSeq_recv = (long)CSeq; /* update the handle */ - } - else if(checkprefix("Session:", header)) { - const char *start, *end; - size_t idlen; - - /* Find the first non-space letter */ - start = header + 8; - curlx_str_passblanks(&start); - - if(!*start) { - failf(data, "Got a blank Session ID"); - return CURLE_RTSP_SESSION_ERROR; - } - - /* Find the end of Session ID - * - * Allow any non whitespace content, up to the field separator or end of - * line. RFC 2326 is not 100% clear on the session ID and for example - * gstreamer does url-encoded session ID's not covered by the standard. - */ - end = start; - while((*end > ' ') && (*end != ';')) - end++; - idlen = end - start; - - if(data->set.str[STRING_RTSP_SESSION_ID]) { - - /* If the Session ID is set, then compare */ - if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen || - strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) { - failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", - start, data->set.str[STRING_RTSP_SESSION_ID]); - return CURLE_RTSP_SESSION_ERROR; - } - } - else { - /* If the Session ID is not set, and we find it in a response, then set - * it. - */ - - /* Copy the id substring into a new buffer */ - data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen); - if(!data->set.str[STRING_RTSP_SESSION_ID]) - return CURLE_OUT_OF_MEMORY; - } - } - else if(checkprefix("Transport:", header)) { - CURLcode result; - result = rtsp_parse_transport(data, header + 10); - if(result) - return result; - } - return CURLE_OK; -} - static CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport) { @@ -1099,4 +973,96 @@ return CURLE_OK; } +CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) +{ + if(checkprefix("CSeq:", header)) { + curl_off_t CSeq = 0; + struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); + const char *p = &header[5]; + if(!rtsp) + return CURLE_FAILED_INIT; + curlx_str_passblanks(&p); + if(curlx_str_number(&p, &CSeq, UINT_MAX)) { + failf(data, "Unable to read the CSeq header: [%s]", header); + return CURLE_RTSP_CSEQ_ERROR; + } + data->state.rtsp_CSeq_recv = rtsp->CSeq_recv = (uint32_t)CSeq; + } + else if(checkprefix("Session:", header)) { + const char *start, *end; + size_t idlen; + + /* Find the first non-space letter */ + start = header + 8; + curlx_str_passblanks(&start); + + if(!*start) { + failf(data, "Got a blank Session ID"); + return CURLE_RTSP_SESSION_ERROR; + } + + /* Find the end of Session ID + * + * Allow any non whitespace content, up to the field separator or end of + * line. RFC 2326 is not 100% clear on the session ID and for example + * gstreamer does URL-encoded session ID's not covered by the standard. + */ + end = start; + while((*end > ' ') && (*end != ';')) + end++; + idlen = end - start; + + if(data->set.str[STRING_RTSP_SESSION_ID]) { + + /* If the Session ID is set, then compare */ + if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen || + strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) { + failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", + start, data->set.str[STRING_RTSP_SESSION_ID]); + return CURLE_RTSP_SESSION_ERROR; + } + } + else { + /* If the Session ID is not set, and we find it in a response, then set + * it. + */ + + /* Copy the id substring into a new buffer */ + data->set.str[STRING_RTSP_SESSION_ID] = curlx_memdup0(start, idlen); + if(!data->set.str[STRING_RTSP_SESSION_ID]) + return CURLE_OUT_OF_MEMORY; + } + } + else if(checkprefix("Transport:", header)) { + CURLcode result; + result = rtsp_parse_transport(data, header + 10); + if(result) + return result; + } + return CURLE_OK; +} + +/* + * RTSP handler interface. + */ +const struct Curl_protocol Curl_protocol_rtsp = { + rtsp_setup_connection, /* setup_connection */ + rtsp_do, /* do_it */ + rtsp_done, /* done */ + ZERO_NULL, /* do_more */ + rtsp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + rtsp_do_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + Curl_http_perform_pollset, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + rtsp_rtp_write_resp, /* write_resp */ + rtsp_rtp_write_resp_hd, /* write_resp_hd */ + rtsp_conn_is_dead, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ +}; + #endif /* CURL_DISABLE_RTSP */
diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index 70ca00f..dd5df3f 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h
@@ -24,8 +24,8 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_RTSP -extern const struct Curl_handler Curl_handler_rtsp; CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header); +extern const struct Curl_protocol Curl_protocol_rtsp; #else #define Curl_rtsp_parseheader(x, y) CURLE_NOT_BUILT_IN #endif
diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index cae5085..888490f 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c
@@ -66,7 +66,7 @@ if((!fds_read || fds_read->fd_count == 0) && (!fds_write || fds_write->fd_count == 0) && (!fds_err || fds_err->fd_count == 0)) { - /* no sockets, just wait */ + /* no sockets, wait */ return curlx_wait_ms(timeout_ms); } #endif @@ -75,17 +75,17 @@ #ifdef USE_WINSOCK /* Winsock select() must not be called with an fd_set that contains zero - fd flags, or it will return WSAEINVAL. But, it also cannot be called - with no fd_sets at all! From the documentation: + fd flags, or it will return WSAEINVAL. It also cannot be called with + no fd_sets at all! From the documentation: - Any two of the parameters, readfds, writefds, or exceptfds, can be - given as null. At least one must be non-null, and any non-null - descriptor set must contain at least one handle to a socket. + Any two of the parameters, readfds, writefds, or exceptfds, can be + given as null. At least one must be non-null, and any non-null + descriptor set must contain at least one handle to a socket. - It is unclear why Winsock does not just handle this for us instead of - calling this an error. Luckily, with Winsock, we can _also_ ask how - many bits are set on an fd_set. So, let's just check it beforehand. - */ + It is unclear why Winsock does not handle this for us instead of + calling this an error. Luckily, with Winsock, we can _also_ ask how + many bits are set on an fd_set. Therefore, let's check it beforehand. + */ return select((int)maxfd + 1, fds_read && fds_read->fd_count ? fds_read : NULL, fds_write && fds_write->fd_count ? fds_write : NULL, @@ -128,7 +128,7 @@ if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) { - /* no sockets, just wait */ + /* no sockets, wait */ return curlx_wait_ms(timeout_ms); } @@ -223,7 +223,7 @@ } } if(fds_none) { - /* no sockets, just wait */ + /* no sockets, wait */ return curlx_wait_ms(timeout_ms); } @@ -274,7 +274,7 @@ if(ufds[i].fd == CURL_SOCKET_BAD) continue; VERIFY_SOCK(ufds[i].fd); - if(ufds[i].events & (POLLIN |POLLOUT |POLLPRI | + if(ufds[i].events & (POLLIN | POLLOUT | POLLPRI | POLLRDNORM | POLLWRNORM | POLLRDBAND)) { if(ufds[i].fd > maxfd) maxfd = ufds[i].fd; @@ -568,7 +568,6 @@ DEBUGASSERT(ps->init == CURL_EASY_POLLSET_MAGIC); #endif - (void)data; DEBUGASSERT(VALID_SOCK(sock)); if(!VALID_SOCK(sock)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -637,19 +636,25 @@ bool do_in, bool do_out) { return Curl_pollset_change(data, ps, sock, - (do_in ? CURL_POLL_IN : 0)| + (do_in ? CURL_POLL_IN : 0) | (do_out ? CURL_POLL_OUT : 0), - (!do_in ? CURL_POLL_IN : 0)| + (!do_in ? CURL_POLL_IN : 0) | (!do_out ? CURL_POLL_OUT : 0)); } +/* + * Return values: + * -1 = error + * 0 = timeout + * N = number of structures with non zero revent fields + */ int Curl_pollset_poll(struct Curl_easy *data, struct easy_pollset *ps, timediff_t timeout_ms) { struct pollfd *pfds; unsigned int i, npfds; - int result; + int rc; (void)data; DEBUGASSERT(data); @@ -678,9 +683,9 @@ } } - result = Curl_poll(pfds, npfds, timeout_ms); + rc = Curl_poll(pfds, npfds, timeout_ms); curlx_free(pfds); - return result; + return rc; } void Curl_pollset_check(struct Curl_easy *data,
diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h index 268ae7c..87b6954 100644 --- a/Utilities/cmcurl/lib/select.h +++ b/Utilities/cmcurl/lib/select.h
@@ -35,9 +35,7 @@ * Definition of pollfd struct and constants for platforms lacking them. */ -#if !defined(HAVE_SYS_POLL_H) && \ - !defined(HAVE_POLL_H) && \ - !defined(POLLIN) +#if !defined(HAVE_SYS_POLL_H) && !defined(HAVE_POLL_H) && !defined(POLLIN) #define POLLIN 0x01 #define POLLPRI 0x02 @@ -71,7 +69,8 @@ therefore defined here */ #define CURL_CSELECT_IN2 (CURL_CSELECT_ERR << 1) -int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2, +int Curl_socket_check(curl_socket_t readfd0, + curl_socket_t readfd1, curl_socket_t writefd, timediff_t timeout_ms); #define SOCKET_READABLE(x, z) \ @@ -160,22 +159,19 @@ bool do_in, bool do_out) WARN_UNUSED_RESULT; #define Curl_pollset_add_in(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0) + Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0) #define Curl_pollset_remove_in(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), 0, CURL_POLL_IN) + Curl_pollset_change(data, ps, sock, 0, CURL_POLL_IN) #define Curl_pollset_add_out(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0) + Curl_pollset_change(data, ps, sock, CURL_POLL_OUT, 0) #define Curl_pollset_remove_out(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), 0, CURL_POLL_OUT) + Curl_pollset_change(data, ps, sock, 0, CURL_POLL_OUT) #define Curl_pollset_add_inout(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_IN | CURL_POLL_OUT, 0) + Curl_pollset_change(data, ps, sock, CURL_POLL_IN | CURL_POLL_OUT, 0) #define Curl_pollset_set_in_only(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_IN, CURL_POLL_OUT) + Curl_pollset_change(data, ps, sock, CURL_POLL_IN, CURL_POLL_OUT) #define Curl_pollset_set_out_only(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_OUT, CURL_POLL_IN) + Curl_pollset_change(data, ps, sock, CURL_POLL_OUT, CURL_POLL_IN) /* return < = on error, 0 on timeout or how many sockets are ready */ int Curl_pollset_poll(struct Curl_easy *data,
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 2d66ed5..7c977d3 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c
@@ -42,45 +42,8 @@ #include "cw-out.h" #include "cw-pause.h" #include "multiif.h" -#include "strerror.h" #include "progress.h" - -static CURLcode do_init_writer_stack(struct Curl_easy *data); - -/* Curl_client_write() sends data to the write callback(s) - - The bit pattern defines to what "streams" to write to. Body and/or header. - The defines are in sendf.h of course. - */ -CURLcode Curl_client_write(struct Curl_easy *data, - int type, const char *buf, size_t blen) -{ - CURLcode result; - - /* it is one of those, at least */ - DEBUGASSERT(type & - (CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO)); - /* BODY is only BODY (with optional EOS) */ - DEBUGASSERT(!(type & CLIENTWRITE_BODY) || - ((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0)); - /* INFO is only INFO (with optional EOS) */ - DEBUGASSERT(!(type & CLIENTWRITE_INFO) || - ((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0)); - - if(!data->req.writer_stack) { - result = do_init_writer_stack(data); - if(result) - return result; - DEBUGASSERT(data->req.writer_stack); - } - - result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen); - CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", - type, blen, result); - return result; -} - static void cl_reset_writer(struct Curl_easy *data) { struct Curl_cwriter *writer = data->req.writer_stack; @@ -153,7 +116,7 @@ bool Curl_creader_will_rewind(struct Curl_easy *data) { - return data->req.rewind_read; + return (bool)data->req.rewind_read; } void Curl_creader_set_rewind(struct Curl_easy *data, bool enable) @@ -205,7 +168,9 @@ struct cw_download_ctx { struct Curl_cwriter super; BIT(started_response); + BIT(started_body); }; + /* Download client writer in phase CURL_CW_PROTOCOL that * sees the "real" download body data. */ static CURLcode cw_download_write(struct Curl_easy *data, @@ -220,7 +185,6 @@ if(!ctx->started_response && !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) { Curl_pgrsTime(data, TIMER_STARTTRANSFER); - Curl_rlimit_start(&data->progress.dl.rlimit, Curl_pgrs_now(data)); ctx->started_response = TRUE; } @@ -233,6 +197,13 @@ return result; } + if(!ctx->started_body && + !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) { + Curl_rlimit_start(&data->progress.dl.rlimit, Curl_pgrs_now(data), + data->req.size); + ctx->started_body = TRUE; + } + /* Here, we deal with REAL BODY bytes. All filtering and transfer * encodings have been applied and only the true content, e.g. BODY, * bytes are passed here. @@ -351,6 +322,84 @@ sizeof(struct Curl_cwriter) }; +static CURLcode do_init_writer_stack(struct Curl_easy *data) +{ + struct Curl_cwriter *writer; + CURLcode result; + + DEBUGASSERT(!data->req.writer_stack); + result = Curl_cwriter_create(&data->req.writer_stack, + data, &Curl_cwt_out, CURL_CW_CLIENT); + if(result) + return result; + + /* This places the "pause" writer behind the "download" writer that + * is added below. Meaning the "download" can do checks on content length + * and other things *before* write outs are buffered for paused transfers. */ + result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause, + CURL_CW_PROTOCOL); + if(!result) { + result = Curl_cwriter_add(data, writer); + if(result) + Curl_cwriter_free(data, writer); + } + if(result) + return result; + + result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); + if(!result) { + result = Curl_cwriter_add(data, writer); + if(result) + Curl_cwriter_free(data, writer); + } + if(result) + return result; + + result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); + if(!result) { + result = Curl_cwriter_add(data, writer); + if(result) + Curl_cwriter_free(data, writer); + } + if(result) + return result; + + return result; +} + +/* Curl_client_write() sends data to the write callback(s) + + The bit pattern defines to what "streams" to write to. Body and/or header. + The defines are in sendf.h of course. + */ +CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, + size_t len) +{ + CURLcode result; + + /* it is one of those, at least */ + DEBUGASSERT(type & + (CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO)); + /* BODY is only BODY (with optional EOS) */ + DEBUGASSERT(!(type & CLIENTWRITE_BODY) || + ((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0)); + /* INFO is only INFO (with optional EOS) */ + DEBUGASSERT(!(type & CLIENTWRITE_INFO) || + ((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0)); + + if(!data->req.writer_stack) { + result = do_init_writer_stack(data); + if(result) + return result; + DEBUGASSERT(data->req.writer_stack); + } + + result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, len); + CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", + type, len, result); + return result; +} + /* Create an unencoding writer stage using the given handler. */ CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, struct Curl_easy *data, @@ -400,51 +449,6 @@ return n; } -static CURLcode do_init_writer_stack(struct Curl_easy *data) -{ - struct Curl_cwriter *writer; - CURLcode result; - - DEBUGASSERT(!data->req.writer_stack); - result = Curl_cwriter_create(&data->req.writer_stack, - data, &Curl_cwt_out, CURL_CW_CLIENT); - if(result) - return result; - - /* This places the "pause" writer behind the "download" writer that - * is added below. Meaning the "download" can do checks on content length - * and other things *before* write outs are buffered for paused transfers. */ - result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause, - CURL_CW_PROTOCOL); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } - if(result) - return result; - - result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } - if(result) - return result; - - result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } - if(result) - return result; - - return result; -} - CURLcode Curl_cwriter_add(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -625,7 +629,6 @@ static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader) { struct cr_in_ctx *ctx = reader->ctx; - (void)data; ctx->read_cb = data->state.fread_func; ctx->cb_user_data = data->state.in; ctx->total_len = -1; @@ -672,7 +675,7 @@ case 0: if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { failf(data, "client read function EOF fail, " - "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read", + "only %" FMT_OFF_T "/%" FMT_OFF_T " of needed bytes read", ctx->read_len, ctx->total_len); result = CURLE_READ_ERROR; break; @@ -692,9 +695,9 @@ break; case CURL_READFUNC_PAUSE: - if(data->conn->handler->flags & PROTOPT_NONETWORK) { + if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it cannot pause since the transfer + actually only FILE:// now, and it cannot pause since the transfer is not done using the "normal" procedure. */ failf(data, "Read callback asked for PAUSE when not supported"); result = CURLE_READ_ERROR; @@ -723,11 +726,11 @@ if(ctx->total_len >= 0) ctx->seen_eos = (ctx->read_len >= ctx->total_len); *pnread = nread; - *peos = ctx->seen_eos; + *peos = (bool)ctx->seen_eos; break; } - CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"FMT_OFF_T - ", read=%"FMT_OFF_T") -> %d, nread=%zu, eos=%d", + CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%" FMT_OFF_T + ", read=%" FMT_OFF_T ") -> %d, nread=%zu, eos=%d", blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); return result; @@ -738,7 +741,7 @@ { struct cr_in_ctx *ctx = reader->ctx; (void)data; - return ctx->has_used_cb; + return (bool)ctx->has_used_cb; } static curl_off_t cr_in_total_length(struct Curl_easy *data, @@ -899,7 +902,7 @@ { struct cr_in_ctx *ctx = reader->ctx; (void)data; - return ctx->is_paused; + return (bool)ctx->is_paused; } static const struct Curl_crtype cr_in = { @@ -1006,10 +1009,12 @@ if(!nread || !memchr(buf, '\n', nread)) { /* nothing to convert, return this right away */ + if(nread) + ctx->prev_cr = (buf[nread - 1] == '\r'); if(ctx->read_eos) ctx->eos = TRUE; *pnread = nread; - *peos = ctx->eos; + *peos = (bool)ctx->eos; goto out; } @@ -1125,7 +1130,7 @@ struct cr_in_ctx *ctx; result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT); - if(result) + if(result || !r) goto out; ctx = r->ctx; ctx->total_len = len; @@ -1133,7 +1138,7 @@ cl_reset_reader(data); result = do_init_reader_stack(data, r); out: - CURL_TRC_READ(data, "add fread reader, len=%"FMT_OFF_T " -> %d", + CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d", len, result); return result; } @@ -1192,7 +1197,7 @@ DEBUGASSERT(data->req.reader_stack); } if(!data->req.reader_started) { - Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data)); + Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data), -1); data->req.reader_started = TRUE; } @@ -1294,7 +1299,6 @@ struct cr_buf_ctx *ctx = reader->ctx; size_t nread = ctx->blen - ctx->index; - (void)data; if(!nread || !ctx->buf) { *pnread = 0; *peos = TRUE;
diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h index 1521ccb..75c6e24 100644 --- a/Utilities/cmcurl/lib/sendf.h +++ b/Utilities/cmcurl/lib/sendf.h
@@ -55,10 +55,10 @@ struct Curl_easy; /** - * Write `len` bytes at `prt` to the client. `type` indicates what + * Write `len` bytes at `buf` to the client. `type` indicates what * kind of data is being written. */ -CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *ptr, +CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, size_t len) WARN_UNUSED_RESULT; /** @@ -140,7 +140,7 @@ */ CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, struct Curl_easy *data, - const struct Curl_cwtype *ce_handler, + const struct Curl_cwtype *cwt, Curl_cwriter_phase phase); /** @@ -298,7 +298,7 @@ */ CURLcode Curl_creader_create(struct Curl_creader **preader, struct Curl_easy *data, - const struct Curl_crtype *cr_handler, + const struct Curl_crtype *crt, Curl_creader_phase phase); /**
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index 0c1da5b..b8a6327 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c
@@ -46,9 +46,10 @@ #include "altsvc.h" #include "hsts.h" #include "tftp.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "escape.h" #include "bufref.h" +#include "vauth/vauth.h" static CURLcode setopt_set_timeout_sec(timediff_t *ptimeout_ms, long secs) { @@ -83,7 +84,7 @@ /* Release the previous storage at `charp' and replace by a dynamic storage copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ - Curl_safefree(*charp); + curlx_safefree(*charp); if(s) { if(strlen(s) > CURL_MAX_INPUT_LENGTH) @@ -103,11 +104,11 @@ /* free the previous storage at `blobp' and replace by a dynamic storage copy of blob. If CURL_BLOB_COPY is set, the data is copied. */ - Curl_safefree(*blobp); + curlx_safefree(*blobp); if(blob) { struct curl_blob *nblob; - if(blob->len > CURL_MAX_INPUT_LENGTH) + if(!blob->len || (blob->len > CURL_MAX_INPUT_LENGTH)) return CURLE_BAD_FUNCTION_ARGUMENT; nblob = (struct curl_blob *) curlx_malloc(sizeof(struct curl_blob) + @@ -128,7 +129,8 @@ return CURLE_OK; } -static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) +static CURLcode setstropt_userpwd(const char *option, char **userp, + char **passwdp) { char *user = NULL; char *passwd = NULL; @@ -136,8 +138,8 @@ DEBUGASSERT(userp); DEBUGASSERT(passwdp); - /* Parse the login details if specified. It not then we treat NULL as a hint - to clear the existing data */ + /* Parse the login details if specified. If not, then we treat NULL as a + hint to clear the existing data */ if(option) { size_t len = strlen(option); CURLcode result; @@ -188,8 +190,10 @@ return CURLE_OK; } -#define C_SSLVERSION_VALUE(x) (x & 0xffff) -#define C_SSLVERSION_MAX_VALUE(x) ((unsigned long)x & 0xffff0000) +#ifdef USE_SSL +#define C_SSLVERSION_VALUE(x) ((x) & 0xffff) +#define C_SSLVERSION_MAX_VALUE(x) ((unsigned long)(x) & 0xffff0000) +#endif static CURLcode protocol2num(const char *str, curl_prot_t *val) { @@ -214,9 +218,9 @@ str = strchr(str, ','); tlen = str ? (size_t)(str - token) : strlen(token); if(tlen) { - const struct Curl_handler *h = Curl_getn_scheme_handler(token, tlen); + const struct Curl_scheme *h = Curl_getn_scheme(token, tlen); - if(!h) + if(!h || !h->run) return CURLE_UNSUPPORTED_PROTOCOL; *val |= h->protocol; @@ -269,9 +273,9 @@ return CURLE_NOT_BUILT_IN; /* no supported types left! */ } if(proxy) - data->set.proxyauth = auth; + data->set.proxyauth = (uint32_t)auth; else - data->set.httpauth = auth; + data->set.httpauth = (uint32_t)auth; return CURLE_OK; } #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ @@ -356,51 +360,41 @@ static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg) { /* - * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) - * Would this be better if the RTSPREQ_* were just moved into here? + * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) Would this be + * better if the RTSPREQ_* were moved into here? */ Curl_RtspReq rtspreq = RTSPREQ_NONE; switch(arg) { case CURL_RTSPREQ_OPTIONS: rtspreq = RTSPREQ_OPTIONS; break; - case CURL_RTSPREQ_DESCRIBE: rtspreq = RTSPREQ_DESCRIBE; break; - case CURL_RTSPREQ_ANNOUNCE: rtspreq = RTSPREQ_ANNOUNCE; break; - case CURL_RTSPREQ_SETUP: rtspreq = RTSPREQ_SETUP; break; - case CURL_RTSPREQ_PLAY: rtspreq = RTSPREQ_PLAY; break; - case CURL_RTSPREQ_PAUSE: rtspreq = RTSPREQ_PAUSE; break; - case CURL_RTSPREQ_TEARDOWN: rtspreq = RTSPREQ_TEARDOWN; break; - case CURL_RTSPREQ_GET_PARAMETER: rtspreq = RTSPREQ_GET_PARAMETER; break; - case CURL_RTSPREQ_SET_PARAMETER: rtspreq = RTSPREQ_SET_PARAMETER; break; - case CURL_RTSPREQ_RECORD: rtspreq = RTSPREQ_RECORD; break; - case CURL_RTSPREQ_RECEIVE: rtspreq = RTSPREQ_RECEIVE; break; @@ -429,8 +423,8 @@ } #endif -static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, - long arg, bool *set) +static CURLcode setopt_long_bool(struct Curl_easy *data, CURLoption option, + long arg) { bool enabled = !!arg; int ok = 1; @@ -495,7 +489,7 @@ case CURLOPT_UPLOAD: case CURLOPT_PUT: /* - * We want to sent data to the remote host. If this is HTTP, that equals + * We want to send data to the remote host. If this is HTTP, that equals * using the PUT request. */ if(enabled) { @@ -536,7 +530,6 @@ */ s->http_auto_referer = enabled; break; - case CURLOPT_TRANSFER_ENCODING: s->http_transfer_encoding = enabled; break; @@ -547,21 +540,18 @@ */ s->allow_auth_to_other_hosts = enabled; break; - case CURLOPT_HTTP_TRANSFER_DECODING: /* * disable libcurl transfer encoding is used */ s->http_te_skip = !enabled; /* reversed */ break; - case CURLOPT_HTTP_CONTENT_DECODING: /* * raw data passed to the application when content encoding is used */ s->http_ce_skip = !enabled; /* reversed */ break; - case CURLOPT_HTTPGET: /* * Set to force us do HTTP GET @@ -624,7 +614,7 @@ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) case CURLOPT_SOCKS5_GSSAPI_NEC: /* - * Set flag for NEC SOCK5 support + * Set flag for NEC SOCKS5 support */ s->socks5_gssapi_nec = enabled; break; @@ -649,11 +639,9 @@ case CURLOPT_FTP_USE_EPRT: s->ftp_use_eprt = enabled; break; - case CURLOPT_FTP_USE_EPSV: s->ftp_use_epsv = enabled; break; - case CURLOPT_FTP_USE_PRET: s->ftp_use_pret = enabled; break; @@ -674,7 +662,6 @@ */ s->crlf = enabled; break; - #ifndef CURL_DISABLE_TFTP case CURLOPT_TFTP_NO_OPTIONS: /* @@ -775,7 +762,6 @@ */ s->tcp_nodelay = enabled; break; - case CURLOPT_IGNORE_CONTENT_LENGTH: s->ignorecl = enabled; break; @@ -835,14 +821,13 @@ s->quick_exit = enabled; break; default: - return CURLE_OK; + return CURLE_UNKNOWN_OPTION; } if((arg > ok) || (arg < 0)) /* reserve other values for future use */ infof(data, "boolean setopt(%d) got unsupported argument %ld," " treated as %d", option, arg, enabled); - *set = TRUE; return CURLE_OK; } @@ -857,20 +842,11 @@ return CURLE_OK; } -static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, - long arg) +static CURLcode setopt_long_net(struct Curl_easy *data, CURLoption option, + long arg) { -#if !defined(CURL_DISABLE_PROXY) || \ - !defined(CURL_DISABLE_HTTP) || \ - defined(HAVE_GSSAPI) || \ - defined(USE_IPV6) - unsigned long uarg = (unsigned long)arg; -#endif - bool set = FALSE; - CURLcode result = setopt_bool(data, option, arg, &set); + CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; - if(set || result) - return result; switch(option) { case CURLOPT_DNS_CACHE_TIMEOUT: @@ -878,181 +854,25 @@ return setopt_set_timeout_sec(&s->dns_cache_timeout_ms, arg); s->dns_cache_timeout_ms = -1; break; - - case CURLOPT_CA_CACHE_TIMEOUT: - if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) { - result = value_range(&arg, -1, -1, INT_MAX); - if(result) - return result; - - s->general_ssl.ca_cache_timeout = (int)arg; - } - else - return CURLE_NOT_BUILT_IN; - break; case CURLOPT_MAXCONNECTS: - result = value_range(&arg, 1, 1, UINT_MAX); - if(result) - return result; - s->maxconnects = (unsigned int)arg; + result = value_range(&arg, 1, 1, INT_MAX); + if(!result) + s->maxconnects = (uint32_t)arg; break; case CURLOPT_SERVER_RESPONSE_TIMEOUT: return setopt_set_timeout_sec(&s->server_response_timeout, arg); - case CURLOPT_SERVER_RESPONSE_TIMEOUT_MS: return setopt_set_timeout_ms(&s->server_response_timeout, arg); - -#ifndef CURL_DISABLE_TFTP - case CURLOPT_TFTP_BLKSIZE: - result = value_range(&arg, 0, TFTP_BLKSIZE_MIN, TFTP_BLKSIZE_MAX); - if(result) - return result; - s->tftp_blksize = (unsigned short)arg; - break; -#endif -#ifndef CURL_DISABLE_NETRC - case CURLOPT_NETRC: - if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->use_netrc = (unsigned char)arg; - break; -#endif - case CURLOPT_TIMECONDITION: - if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->timecondition = (unsigned char)arg; - break; - case CURLOPT_TIMEVALUE: - s->timevalue = (time_t)arg; - break; - case CURLOPT_SSLVERSION: -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLVERSION: -#endif - return Curl_setopt_SSLVERSION(data, option, arg); - - case CURLOPT_POSTFIELDSIZE: - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(s->postfieldsize < arg && - s->postfields == s->str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); - s->postfields = NULL; - } - - s->postfieldsize = arg; - break; -#ifndef CURL_DISABLE_HTTP - case CURLOPT_FOLLOWLOCATION: - if(uarg > 3) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->http_follow_mode = (unsigned char)uarg; - break; - - case CURLOPT_MAXREDIRS: - result = value_range(&arg, -1, -1, 0x7fff); - if(result) - return result; - s->maxredirs = (short)arg; - break; - - case CURLOPT_POSTREDIR: - if(arg < CURL_REDIR_GET_ALL) - /* no return error on too high numbers since the bitmask could be - extended in a future */ - return CURLE_BAD_FUNCTION_ARGUMENT; - s->keep_post = arg & CURL_REDIR_POST_ALL; - break; - - case CURLOPT_HEADEROPT: - s->sep_headers = !!(arg & CURLHEADER_SEPARATE); - break; - case CURLOPT_HTTPAUTH: - return httpauth(data, FALSE, uarg); - - case CURLOPT_HTTP_VERSION: - return setopt_HTTP_VERSION(data, arg); - - case CURLOPT_EXPECT_100_TIMEOUT_MS: - result = value_range(&arg, 0, 0, 0xffff); - if(result) - return result; - s->expect_100_timeout = (unsigned short)arg; - break; - -#endif /* !CURL_DISABLE_HTTP */ - -#ifndef CURL_DISABLE_MIME - case CURLOPT_MIME_OPTIONS: - s->mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); - break; -#endif -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYPORT: - if((arg < 0) || (arg > UINT16_MAX)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->proxyport = (uint16_t)arg; - break; - - case CURLOPT_PROXYAUTH: - return httpauth(data, TRUE, uarg); - - case CURLOPT_PROXYTYPE: - if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->proxytype = (unsigned char)arg; - break; - - case CURLOPT_SOCKS5_AUTH: - if(uarg & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) - return CURLE_NOT_BUILT_IN; - s->socks5auth = (unsigned char)uarg; - break; -#endif /* !CURL_DISABLE_PROXY */ - -#ifndef CURL_DISABLE_FTP - case CURLOPT_FTP_FILEMETHOD: - if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ftp_filemethod = (unsigned char)arg; - break; - case CURLOPT_FTP_SSL_CCC: - if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ftp_ccc = (unsigned char)arg; - break; - - case CURLOPT_FTPSSLAUTH: - if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ftpsslauth = (unsigned char)arg; - break; - case CURLOPT_ACCEPTTIMEOUT_MS: - return setopt_set_timeout_ms(&s->accepttimeout, arg); -#endif /* !CURL_DISABLE_FTP */ -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) - case CURLOPT_FTP_CREATE_MISSING_DIRS: - if((arg < CURLFTP_CREATE_DIR_NONE) || (arg > CURLFTP_CREATE_DIR_RETRY)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ftp_create_missing_dirs = (unsigned char)arg; - break; -#endif /* !CURL_DISABLE_FTP || USE_SSH */ - case CURLOPT_INFILESIZE: - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->filesize = arg; - break; case CURLOPT_LOW_SPEED_LIMIT: if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->low_speed_limit = arg; + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->low_speed_limit = arg; break; case CURLOPT_LOW_SPEED_TIME: - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->low_speed_time = arg; + result = value_range(&arg, 0, 0, USHRT_MAX); + if(!result) + s->low_speed_time = (uint16_t)arg; break; case CURLOPT_PORT: if((arg < 0) || (arg > 65535)) @@ -1061,22 +881,12 @@ break; case CURLOPT_TIMEOUT: return setopt_set_timeout_sec(&s->timeout, arg); - case CURLOPT_TIMEOUT_MS: return setopt_set_timeout_ms(&s->timeout, arg); - case CURLOPT_CONNECTTIMEOUT: return setopt_set_timeout_sec(&s->connecttimeout, arg); - case CURLOPT_CONNECTTIMEOUT_MS: return setopt_set_timeout_ms(&s->connecttimeout, arg); - - case CURLOPT_RESUME_FROM: - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->set_resume_from = arg; - break; - #ifndef CURL_DISABLE_BINDLOCAL case CURLOPT_LOCALPORT: if((arg < 0) || (arg > 65535)) @@ -1089,158 +899,374 @@ s->localportrange = curlx_sltous(arg); break; #endif - -#ifdef HAVE_GSSAPI - case CURLOPT_GSSAPI_DELEGATION: - s->gssapi_delegation = (unsigned char)uarg & - (CURLGSSAPI_DELEGATION_POLICY_FLAG | CURLGSSAPI_DELEGATION_FLAG); - break; -#endif - - case CURLOPT_SSL_FALSESTART: - return CURLE_NOT_BUILT_IN; case CURLOPT_BUFFERSIZE: result = value_range(&arg, 0, READBUFFER_MIN, READBUFFER_MAX); - if(result) - return result; - s->buffer_size = (unsigned int)arg; + if(!result) + s->buffer_size = (unsigned int)arg; break; - case CURLOPT_UPLOAD_BUFFERSIZE: result = value_range(&arg, 0, UPLOADBUFFER_MIN, UPLOADBUFFER_MAX); - if(result) - return result; - s->upload_buffer_size = (unsigned int)arg; + if(!result) + s->upload_buffer_size = (unsigned int)arg; break; - case CURLOPT_MAXFILESIZE: if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->max_filesize = arg; + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->max_filesize = arg; break; + case CURLOPT_IPRESOLVE: + if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ipver = (unsigned char)arg; + break; + case CURLOPT_CONNECT_ONLY: + if(arg < 0 || arg > 2) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else { + s->connect_only = !!arg; + s->connect_only_ws = (arg == 2); + } + break; +#ifdef USE_IPV6 + case CURLOPT_ADDRESS_SCOPE: +#if SIZEOF_LONG > 4 + if((unsigned long)arg > UINT_MAX) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else +#endif + s->scope_id = (unsigned int)arg; + break; +#endif + case CURLOPT_TCP_KEEPIDLE: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepidle = (int)arg; + break; + case CURLOPT_TCP_KEEPINTVL: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepintvl = (int)arg; + break; + case CURLOPT_TCP_KEEPCNT: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepcnt = (int)arg; + break; + case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: + return setopt_set_timeout_ms(&s->happy_eyeballs_timeout, arg); + case CURLOPT_UPKEEP_INTERVAL_MS: + return setopt_set_timeout_ms(&s->upkeep_interval_ms, arg); + case CURLOPT_MAXAGE_CONN: + return setopt_set_timeout_sec(&s->conn_max_idle_ms, arg); + case CURLOPT_MAXLIFETIME_CONN: + return setopt_set_timeout_sec(&s->conn_max_age_ms, arg); + case CURLOPT_DNS_USE_GLOBAL_CACHE: + /* deprecated */ + break; + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} +static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, + long arg) +{ #ifdef USE_SSL + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_CA_CACHE_TIMEOUT: + if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) { + result = value_range(&arg, -1, -1, INT_MAX); + if(!result) + s->general_ssl.ca_cache_timeout = (int)arg; + } + else + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_SSLVERSION: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLVERSION: +#endif + return Curl_setopt_SSLVERSION(data, option, arg); + case CURLOPT_SSL_FALSESTART: + result = CURLE_NOT_BUILT_IN; + break; case CURLOPT_USE_SSL: if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->use_ssl = (unsigned char)arg; + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->use_ssl = (unsigned char)arg; break; case CURLOPT_SSL_OPTIONS: set_ssl_options(&s->ssl, &s->ssl.primary, arg); break; - #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSL_OPTIONS: set_ssl_options(&s->proxy_ssl, &s->proxy_ssl.primary, arg); break; #endif - -#endif /* USE_SSL */ - case CURLOPT_IPRESOLVE: - if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ipver = (unsigned char)arg; - break; - - case CURLOPT_CONNECT_ONLY: - if(arg < 0 || arg > 2) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->connect_only = !!arg; - s->connect_only_ws = (arg == 2); - break; - -#ifdef USE_SSH - case CURLOPT_SSH_AUTH_TYPES: - s->ssh_auth_types = (int)arg; - break; -#endif - -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) - case CURLOPT_NEW_FILE_PERMS: - if((arg < 0) || (arg > 0777)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->new_file_perms = (unsigned int)arg; - break; -#endif -#ifdef USE_SSH - case CURLOPT_NEW_DIRECTORY_PERMS: - if((arg < 0) || (arg > 0777)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->new_directory_perms = (unsigned int)arg; - break; -#endif -#ifdef USE_IPV6 - case CURLOPT_ADDRESS_SCOPE: -#if SIZEOF_LONG > 4 - if(uarg > UINT_MAX) - return CURLE_BAD_FUNCTION_ARGUMENT; -#endif - s->scope_id = (unsigned int)uarg; - break; -#endif - case CURLOPT_PROTOCOLS: - s->allowed_protocols = (curl_prot_t)arg; - break; - - case CURLOPT_REDIR_PROTOCOLS: - s->redir_protocols = (curl_prot_t)arg; - break; - -#ifndef CURL_DISABLE_RTSP - case CURLOPT_RTSP_REQUEST: - return setopt_RTSP_REQUEST(data, arg); - case CURLOPT_RTSP_CLIENT_CSEQ: - data->state.rtsp_next_client_CSeq = arg; - break; - - case CURLOPT_RTSP_SERVER_CSEQ: - data->state.rtsp_next_server_CSeq = arg; - break; - -#endif /* !CURL_DISABLE_RTSP */ - - case CURLOPT_TCP_KEEPIDLE: - result = value_range(&arg, 0, 0, INT_MAX); - if(result) - return result; - s->tcp_keepidle = (int)arg; - break; - case CURLOPT_TCP_KEEPINTVL: - result = value_range(&arg, 0, 0, INT_MAX); - if(result) - return result; - s->tcp_keepintvl = (int)arg; - break; - case CURLOPT_TCP_KEEPCNT: - result = value_range(&arg, 0, 0, INT_MAX); - if(result) - return result; - s->tcp_keepcnt = (int)arg; - break; case CURLOPT_SSL_ENABLE_NPN: break; + case CURLOPT_SSLENGINE_DEFAULT: + curlx_safefree(s->str[STRING_SSL_ENGINE]); + result = Curl_ssl_set_engine_default(data); + break; + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +#else /* USE_SSL */ + (void)data; + (void)option; + (void)arg; + return CURLE_UNKNOWN_OPTION; +#endif /* !USE_SSL */ +} + +static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, + long arg) +{ +#ifndef CURL_DISABLE_PROXY + struct UserDefined *s = &data->set; + + switch(option) { + case CURLOPT_PROXYPORT: + if((arg < 0) || (arg > UINT16_MAX)) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->proxyport = (uint16_t)arg; + break; + case CURLOPT_PROXYAUTH: + return httpauth(data, TRUE, (unsigned long)arg); + case CURLOPT_PROXYTYPE: + if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->proxytype = (unsigned char)arg; + break; + case CURLOPT_SOCKS5_AUTH: + if(arg & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) + return CURLE_NOT_BUILT_IN; + s->socks5auth = (unsigned char)arg; + break; + default: + return CURLE_UNKNOWN_OPTION; + } + return CURLE_OK; +#else + (void)data; + (void)option; + (void)arg; + return CURLE_UNKNOWN_OPTION; +#endif +} + +static CURLcode setopt_long_http(struct Curl_easy *data, CURLoption option, + long arg) +{ +#ifndef CURL_DISABLE_HTTP + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { + case CURLOPT_FOLLOWLOCATION: + if((unsigned long)arg > 3) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->http_follow_mode = (unsigned char)arg; + break; + case CURLOPT_MAXREDIRS: + result = value_range(&arg, -1, -1, 0x7fff); + if(!result) + s->maxredirs = (short)arg; + break; + case CURLOPT_POSTREDIR: + if(arg < CURL_REDIR_GET_ALL) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else { + s->post301 = !!(arg & CURL_REDIR_POST_301); + s->post302 = !!(arg & CURL_REDIR_POST_302); + s->post303 = !!(arg & CURL_REDIR_POST_303); + } + break; + case CURLOPT_HEADEROPT: + s->sep_headers = !!(arg & CURLHEADER_SEPARATE); + break; + case CURLOPT_HTTPAUTH: + return httpauth(data, FALSE, (unsigned long)arg); + case CURLOPT_HTTP_VERSION: + return setopt_HTTP_VERSION(data, arg); + case CURLOPT_EXPECT_100_TIMEOUT_MS: + result = value_range(&arg, 0, 0, 0xffff); + if(!result) + s->expect_100_timeout = (unsigned short)arg; + break; case CURLOPT_STREAM_WEIGHT: #if defined(USE_HTTP2) || defined(USE_HTTP3) if((arg >= 1) && (arg <= 256)) s->priority.weight = (int)arg; break; #else - return CURLE_NOT_BUILT_IN; -#endif - case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: - return setopt_set_timeout_ms(&s->happy_eyeballs_timeout, arg); - - case CURLOPT_UPKEEP_INTERVAL_MS: - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->upkeep_interval_ms = arg; + result = CURLE_NOT_BUILT_IN; break; - case CURLOPT_MAXAGE_CONN: - return setopt_set_timeout_sec(&s->conn_max_idle_ms, arg); +#endif + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +#else + (void)data; + (void)option; + (void)arg; + return CURLE_UNKNOWN_OPTION; +#endif +} - case CURLOPT_MAXLIFETIME_CONN: - return setopt_set_timeout_sec(&s->conn_max_age_ms, arg); +static CURLcode setopt_long_proto(struct Curl_easy *data, CURLoption option, + long arg) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + switch(option) { +#ifndef CURL_DISABLE_TFTP + case CURLOPT_TFTP_BLKSIZE: + result = value_range(&arg, 0, TFTP_BLKSIZE_MIN, TFTP_BLKSIZE_MAX); + if(!result) + s->tftp_blksize = (unsigned short)arg; + break; +#endif +#ifndef CURL_DISABLE_NETRC + case CURLOPT_NETRC: + if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->use_netrc = (unsigned char)arg; + break; +#endif +#ifndef CURL_DISABLE_FTP + case CURLOPT_FTP_FILEMETHOD: + if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ftp_filemethod = (unsigned char)arg; + break; + case CURLOPT_FTP_SSL_CCC: + if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ftp_ccc = (unsigned char)arg; + break; + case CURLOPT_FTPSSLAUTH: + if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ftpsslauth = (unsigned char)arg; + break; + case CURLOPT_ACCEPTTIMEOUT_MS: + return setopt_set_timeout_ms(&s->accepttimeout, arg); +#endif +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + case CURLOPT_FTP_CREATE_MISSING_DIRS: + if((arg < CURLFTP_CREATE_DIR_NONE) || (arg > CURLFTP_CREATE_DIR_RETRY)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ftp_create_missing_dirs = (unsigned char)arg; + break; + case CURLOPT_NEW_FILE_PERMS: + if((arg < 0) || (arg > 0777)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->new_file_perms = (unsigned int)arg; + break; +#endif +#ifndef CURL_DISABLE_RTSP + case CURLOPT_RTSP_REQUEST: + return setopt_RTSP_REQUEST(data, arg); + case CURLOPT_RTSP_CLIENT_CSEQ: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + data->state.rtsp_next_client_CSeq = (uint32_t)arg; + break; + case CURLOPT_RTSP_SERVER_CSEQ: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + data->state.rtsp_next_server_CSeq = (uint32_t)arg; + break; +#endif +#ifdef USE_SSH + case CURLOPT_SSH_AUTH_TYPES: + s->ssh_auth_types = (uint32_t)arg; + break; + case CURLOPT_NEW_DIRECTORY_PERMS: + if((arg < 0) || (arg > 0777)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->new_directory_perms = (unsigned int)arg; + break; +#endif + case CURLOPT_PROTOCOLS: + s->allowed_protocols = (curl_prot_t)arg; + break; + case CURLOPT_REDIR_PROTOCOLS: + s->redir_protocols = (curl_prot_t)arg; + break; +#ifndef CURL_DISABLE_WEBSOCKETS + case CURLOPT_WS_OPTIONS: + s->ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE); + s->ws_no_auto_pong = (bool)(arg & CURLWS_NOAUTOPONG); + break; +#endif + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} + +static CURLcode setopt_long_misc(struct Curl_easy *data, CURLoption option, + long arg) +{ + struct UserDefined *s = &data->set; + + switch(option) { + case CURLOPT_TIMECONDITION: + if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->timecondition = (unsigned char)arg; + break; + case CURLOPT_TIMEVALUE: + s->timevalue = (time_t)arg; + break; + case CURLOPT_POSTFIELDSIZE: + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + if(s->postfieldsize < arg && + s->postfields == s->str[STRING_COPYPOSTFIELDS]) { + curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); + s->postfields = NULL; + } + s->postfieldsize = arg; + break; + case CURLOPT_INFILESIZE: + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->filesize = arg; + break; + case CURLOPT_RESUME_FROM: + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->set_resume_from = arg; + break; + case CURLOPT_UPLOAD_FLAGS: + s->upload_flags = (unsigned char)arg; + break; +#ifndef CURL_DISABLE_MIME + case CURLOPT_MIME_OPTIONS: + s->mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); + break; +#endif #ifndef CURL_DISABLE_HSTS case CURLOPT_HSTS_CTRL: if(arg & CURLHSTS_ENABLE) { @@ -1253,41 +1279,47 @@ else Curl_hsts_cleanup(&data->hsts); break; -#endif /* !CURL_DISABLE_HSTS */ +#endif #ifndef CURL_DISABLE_ALTSVC case CURLOPT_ALTSVC_CTRL: - if(!arg) { - DEBUGF(infof(data, "bad CURLOPT_ALTSVC_CTRL input")); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - if(!data->asi) { - data->asi = Curl_altsvc_init(); - if(!data->asi) - return CURLE_OUT_OF_MEMORY; - } - return Curl_altsvc_ctrl(data->asi, arg); -#endif /* !CURL_DISABLE_ALTSVC */ -#ifndef CURL_DISABLE_WEBSOCKETS - case CURLOPT_WS_OPTIONS: - s->ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE); - s->ws_no_auto_pong = (bool)(arg & CURLWS_NOAUTOPONG); + return Curl_altsvc_ctrl(data, arg); +#endif +#ifdef HAVE_GSSAPI + case CURLOPT_GSSAPI_DELEGATION: + s->gssapi_delegation = (unsigned char)arg & + (CURLGSSAPI_DELEGATION_POLICY_FLAG | CURLGSSAPI_DELEGATION_FLAG); break; #endif - case CURLOPT_DNS_USE_GLOBAL_CACHE: - /* deprecated */ - break; - case CURLOPT_SSLENGINE_DEFAULT: - Curl_safefree(s->str[STRING_SSL_ENGINE]); - return Curl_ssl_set_engine_default(data); - case CURLOPT_UPLOAD_FLAGS: - s->upload_flags = (unsigned char)arg; - break; default: return CURLE_UNKNOWN_OPTION; } return CURLE_OK; } +static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, + long arg) +{ + typedef CURLcode (*setoptfunc)(struct Curl_easy *data, + CURLoption option, long arg); + static const setoptfunc setopt_call[] = { + setopt_long_bool, + setopt_long_net, + setopt_long_http, + setopt_long_proxy, + setopt_long_ssl, + setopt_long_proto, + setopt_long_misc + }; + size_t i; + + for(i = 0; i < CURL_ARRAYSIZE(setopt_call); i++) { + CURLcode result = setopt_call[i](data, option, arg); + if(result != CURLE_UNKNOWN_OPTION) + return result; + } + return CURLE_UNKNOWN_OPTION; +} + static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option, struct curl_slist *slist) { @@ -1343,7 +1375,7 @@ * Entries added this way will remain in the cache until explicitly * removed or the handle is cleaned up. * - * Prefix the HOST with plus sign (+) to have the entry expire just like + * Prefix the HOST with plus sign (+) to have the entry expire like * automatically added entries. * * Prefix the HOST with dash (-) to _remove_ the entry from the cache. @@ -1385,6 +1417,38 @@ return result; } +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP) +#ifndef CURL_DISABLE_MIME +static CURLcode setopt_mimepost(struct Curl_easy *data, curl_mime *mimep) +{ + /* + * Set to make us do MIME POST + */ + CURLcode result; + struct UserDefined *s = &data->set; + if(!s->mimepostp) { + s->mimepostp = curlx_malloc(sizeof(*s->mimepostp)); + if(!s->mimepostp) + return CURLE_OUT_OF_MEMORY; + Curl_mime_initpart(s->mimepostp); + } + + result = Curl_mime_set_subparts(s->mimepostp, mimep, FALSE); + if(!result) { + s->method = HTTPREQ_POST_MIME; + s->opt_no_body = FALSE; /* this is implied */ +#ifndef CURL_DISABLE_FORM_API + Curl_mime_cleanpart(data->state.formp); + curlx_safefree(data->state.formp); + data->state.mimepost = NULL; +#endif + } + return result; +} +#endif /* !CURL_DISABLE_MIME */ +#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ + /* assorted pointer type arguments */ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, va_list param) @@ -1392,6 +1456,14 @@ CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; switch(option) { + case CURLOPT_CURLU: + /* + * pass CURLU to set URL + */ + Curl_bufref_free(&data->state.url); + curlx_safefree(s->str[STRING_SET_URL]); + s->uh = va_arg(param, CURLU *); + break; #ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_FORM_API case CURLOPT_HTTPPOST: @@ -1402,30 +1474,16 @@ s->method = HTTPREQ_POST_FORM; s->opt_no_body = FALSE; /* this is implied */ Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); data->state.mimepost = NULL; break; #endif /* !CURL_DISABLE_FORM_API */ #endif /* !CURL_DISABLE_HTTP */ -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_IMAP) -# ifndef CURL_DISABLE_MIME +#ifndef CURL_DISABLE_MIME case CURLOPT_MIMEPOST: - /* - * Set to make us do MIME POST - */ - result = Curl_mime_set_subparts(&s->mimepost, - va_arg(param, curl_mime *), - FALSE); - if(!result) { - s->method = HTTPREQ_POST_MIME; - s->opt_no_body = FALSE; /* this is implied */ -#ifndef CURL_DISABLE_FORM_API - Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); - data->state.mimepost = NULL; -#endif - } + result = setopt_mimepost(data, va_arg(param, curl_mime *)); break; #endif /* !CURL_DISABLE_MIME */ #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ @@ -1441,67 +1499,17 @@ case CURLOPT_SHARE: { struct Curl_share *set = va_arg(param, struct Curl_share *); - /* disconnect from old share, if any */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + /* disconnect from old share, if any and possible */ + result = Curl_share_easy_unlink(data); + if(result) + return result; -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies == data->cookies) - data->cookies = NULL; -#endif - -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts == data->hsts) - data->hsts = NULL; -#endif -#ifdef USE_LIBPSL - if(data->psl == &data->share->psl) - data->psl = data->multi ? &data->multi->psl : NULL; -#endif - if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) { - Curl_resolv_unlink(data, &data->state.dns[0]); - Curl_resolv_unlink(data, &data->state.dns[1]); - } - - data->share->dirty--; - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - data->share = NULL; + /* use new share if it set */ + if(GOOD_SHARE_HANDLE(set)) { + result = Curl_share_easy_link(data, set); + if(result) + return result; } - - if(GOOD_SHARE_HANDLE(set)) - /* use new share if it set */ - data->share = set; - if(data->share) { - - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - data->share->dirty++; - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies) { - /* use shared cookie list, first free own one if any */ - Curl_cookie_cleanup(data->cookies); - /* enable cookies since we now use a share that uses cookies! */ - data->cookies = data->share->cookies; - } -#endif /* CURL_DISABLE_HTTP */ -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts) { - /* first free the private one if any */ - Curl_hsts_cleanup(&data->hsts); - data->hsts = data->share->hsts; - } -#endif -#ifdef USE_LIBPSL - if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) - data->psl = &data->share->psl; -#endif - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } - /* check for host cache not needed, - * it will be done by curl_easy_perform */ break; } @@ -1614,19 +1622,90 @@ } #endif -static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, - char *ptr) +#ifndef CURL_DISABLE_PROXY +static CURLcode setproxy(struct Curl_easy *data, const char *proxy) +{ + if((data->set.str[STRING_PROXY] && proxy) && + /* there was one set, is this a new one? */ + !strcmp(data->set.str[STRING_PROXY], proxy)) + return CURLE_OK; /* same one as before */ + + Curl_auth_digest_cleanup(&data->state.proxydigest); + memset(&data->state.authproxy, 0, sizeof(data->state.authproxy)); + return Curl_setstropt(&data->set.str[STRING_PROXY], proxy); +} + +static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, + const char *ptr) { CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; switch(option) { - case CURLOPT_SSL_CIPHER_LIST: - if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) - /* set a list of cipher we want to use in the SSL connection */ - return Curl_setstropt(&s->str[STRING_SSL_CIPHER_LIST], ptr); - else - return CURLE_NOT_BUILT_IN; -#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXYUSERPWD: { + /* + * user:password needed to use the proxy + */ + char *u = NULL; + char *p = NULL; + result = setstropt_userpwd(ptr, &u, &p); + + /* URL decode the components */ + if(!result && u) { + curlx_safefree(s->str[STRING_PROXYUSERNAME]); + result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + } + if(!result && p) { + curlx_safefree(s->str[STRING_PROXYPASSWORD]); + result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, + REJECT_ZERO); + } + curlx_free(u); + curlx_free(p); + break; + } + case CURLOPT_PROXYUSERNAME: + /* + * authentication username to use in the operation + */ + return Curl_setstropt(&s->str[STRING_PROXYUSERNAME], ptr); + + case CURLOPT_PROXYPASSWORD: + /* + * authentication password to use in the operation + */ + return Curl_setstropt(&s->str[STRING_PROXYPASSWORD], ptr); + + case CURLOPT_NOPROXY: + /* + * proxy exception list + */ + return Curl_setstropt(&s->str[STRING_NOPROXY], ptr); + case CURLOPT_PROXY_SSLCERT: + /* + * String that holds filename of the SSL certificate to use for proxy + */ + return Curl_setstropt(&s->str[STRING_CERT_PROXY], ptr); + case CURLOPT_PROXY_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use for proxy + */ + return Curl_setstropt(&s->str[STRING_CERT_TYPE_PROXY], ptr); + case CURLOPT_PROXY_SSLKEY: + /* + * String that holds filename of the SSL key to use for proxy + */ + return Curl_setstropt(&s->str[STRING_KEY_PROXY], ptr); + case CURLOPT_PROXY_KEYPASSWD: + /* + * String that holds the SSL private key password for proxy. + */ + return Curl_setstropt(&s->str[STRING_KEY_PASSWD_PROXY], ptr); + case CURLOPT_PROXY_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use for proxy + */ + return Curl_setstropt(&s->str[STRING_KEY_TYPE_PROXY], ptr); case CURLOPT_PROXY_SSL_CIPHER_LIST: if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { /* set a list of cipher we want to use in the SSL connection for proxy */ @@ -1634,7 +1713,183 @@ } else return CURLE_NOT_BUILT_IN; + case CURLOPT_PROXY_TLS13_CIPHERS: + if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) + /* set preferred list of TLS 1.3 cipher suites for proxy */ + return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST_PROXY], ptr); + else + return CURLE_NOT_BUILT_IN; + case CURLOPT_PROXY: + /* + * Set proxy server:port to use as proxy. + * + * If the proxy is set to "" (and CURLOPT_PRE_PROXY is set to "" or NULL) + * we explicitly say that we do not want to use a proxy (even though there + * might be environment variables saying so). + * + * Setting it to NULL, means no proxy but allows the environment variables + * to decide for us (if CURLOPT_PRE_PROXY setting it to NULL). + */ + return setproxy(data, ptr); + case CURLOPT_PRE_PROXY: + /* + * Set proxy server:port to use as SOCKS proxy. + * + * If the proxy is set to "" or NULL we explicitly say that we do not want + * to use the socks proxy. + */ + return Curl_setstropt(&s->str[STRING_PRE_PROXY], ptr); + case CURLOPT_SOCKS5_GSSAPI_SERVICE: + case CURLOPT_PROXY_SERVICE_NAME: + /* + * Set proxy authentication service name for Kerberos 5 and SPNEGO + */ + return Curl_setstropt(&s->str[STRING_PROXY_SERVICE_NAME], ptr); + case CURLOPT_PROXY_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY_PROXY], ptr); #endif + return CURLE_NOT_BUILT_IN; + + case CURLOPT_HAPROXY_CLIENT_IP: + /* + * Set the client IP to send through HAProxy PROXY protocol + */ + result = Curl_setstropt(&s->str[STRING_HAPROXY_CLIENT_IP], ptr); + + /* enable the HAProxy protocol if an IP is provided */ + s->haproxyprotocol = !!s->str[STRING_HAPROXY_CLIENT_IP]; + break; + case CURLOPT_PROXY_CAINFO: + /* + * Set CA info SSL connection for proxy. Specify filename of the + * CA certificate + */ + s->proxy_ssl.custom_cafile = TRUE; + return Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); + case CURLOPT_PROXY_CRLFILE: + /* + * Set CRL file info for SSL connection for proxy. Specify filename of the + * CRL to check certificates revocation + */ + return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); + case CURLOPT_PROXY_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + case CURLOPT_PROXY_CAPATH: + /* + * Set CA path info for SSL connection proxy. Specify directory name of the + * CA certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { + /* This does not work on Windows. */ + s->proxy_ssl.custom_capath = TRUE; + return Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); + } +#endif + return CURLE_NOT_BUILT_IN; + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} +#endif + +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) +/* + * A string with POST data. Makes curl HTTP POST. Even if it is NULL. If + * needed, CURLOPT_POSTFIELDSIZE must have been set prior to + * CURLOPT_COPYPOSTFIELDS and not altered later. + */ +static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) +{ + CURLcode result = CURLE_OK; + if(!ptr || s->postfieldsize == -1) + result = Curl_setstropt(&s->str[STRING_COPYPOSTFIELDS], ptr); + else { + size_t pflen; + + if(s->postfieldsize < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); + if(pflen == SIZE_MAX) + return CURLE_OUT_OF_MEMORY; + else { + /* Allocate even when size == 0. This satisfies the need of possible + later address compare to detect the COPYPOSTFIELDS mode, and to mark + that postfields is used rather than read function or form data. + */ + char *p = curlx_memdup0(ptr, pflen); + if(!p) + return CURLE_OUT_OF_MEMORY; + else { + curlx_free(s->str[STRING_COPYPOSTFIELDS]); + s->str[STRING_COPYPOSTFIELDS] = p; + } + } + } + + s->postfields = s->str[STRING_COPYPOSTFIELDS]; + s->method = HTTPREQ_POST; + return result; +} +#endif + +static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result; + struct UserDefined *s = &data->set; +#ifndef CURL_DISABLE_PROXY + result = setopt_cptr_proxy(data, option, ptr); + if(result != CURLE_UNKNOWN_OPTION) + return result; +#endif + result = CURLE_OK; + + switch(option) { + case CURLOPT_CAINFO: + /* + * Set CA info for SSL connection. Specify filename of the CA certificate + */ + s->ssl.custom_cafile = TRUE; + return Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); + case CURLOPT_CAPATH: + /* + * Set CA path info for SSL connection. Specify directory name of the CA + * certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { + /* This does not work on Windows. */ + s->ssl.custom_capath = TRUE; + return Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); + } +#endif + return CURLE_NOT_BUILT_IN; + case CURLOPT_CRLFILE: + /* + * Set CRL file info for SSL connection. Specify filename of the CRL + * to check certificates revocation + */ + if(Curl_ssl_supports(data, SSLSUPP_CRLFILE)) + return Curl_setstropt(&s->str[STRING_SSL_CRLFILE], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSL_CIPHER_LIST: + if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) + /* set a list of cipher we want to use in the SSL connection */ + return Curl_setstropt(&s->str[STRING_SSL_CIPHER_LIST], ptr); + else + return CURLE_NOT_BUILT_IN; case CURLOPT_TLS13_CIPHERS: if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { /* set preferred list of TLS 1.3 cipher suites */ @@ -1642,14 +1897,6 @@ } else return CURLE_NOT_BUILT_IN; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) - /* set preferred list of TLS 1.3 cipher suites for proxy */ - return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST_PROXY], ptr); - else - return CURLE_NOT_BUILT_IN; -#endif case CURLOPT_RANDOM_FILE: break; case CURLOPT_EGDSOCKET: @@ -1666,40 +1913,7 @@ #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) case CURLOPT_COPYPOSTFIELDS: - /* - * A string with POST data. Makes curl HTTP POST. Even if it is NULL. - * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to - * CURLOPT_COPYPOSTFIELDS and not altered later. - */ - if(!ptr || s->postfieldsize == -1) - result = Curl_setstropt(&s->str[STRING_COPYPOSTFIELDS], ptr); - else { - size_t pflen; - - if(s->postfieldsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); - if(pflen == SIZE_MAX) - return CURLE_OUT_OF_MEMORY; - else { - /* Allocate even when size == 0. This satisfies the need of possible - later address compare to detect the COPYPOSTFIELDS mode, and to - mark that postfields is used rather than read function or form - data. - */ - char *p = Curl_memdup0(ptr, pflen); - if(!p) - return CURLE_OUT_OF_MEMORY; - else { - curlx_free(s->str[STRING_COPYPOSTFIELDS]); - s->str[STRING_COPYPOSTFIELDS] = p; - } - } - } - - s->postfields = s->str[STRING_COPYPOSTFIELDS]; - s->method = HTTPREQ_POST; - break; + return setopt_copypostfields(ptr, s); case CURLOPT_POSTFIELDS: /* @@ -1707,12 +1921,15 @@ */ s->postfields = ptr; /* Release old copied data. */ - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->method = HTTPREQ_POST; break; #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ #ifndef CURL_DISABLE_HTTP + case CURLOPT_TRAILERDATA: + s->trailer_data = ptr; + break; case CURLOPT_ACCEPT_ENCODING: /* * String to use at the value of Accept-Encoding header. @@ -1754,7 +1971,6 @@ * String to set in the HTTP Referer: field. */ result = Curl_setstropt(&s->str[STRING_SET_REFERER], ptr); - Curl_bufref_set(&data->state.referer, s->str[STRING_SET_REFERER], 0, NULL); break; case CURLOPT_USERAGENT: @@ -1804,43 +2020,9 @@ */ return Curl_setstropt(&s->str[STRING_CUSTOMREQUEST], ptr); - /* we do not set - s->method = HTTPREQ_CUSTOM; - here, we continue as if we were using the already set type - and this just changes the actual request keyword */ - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY: - /* - * Set proxy server:port to use as proxy. - * - * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) - * we explicitly say that we do not want to use a proxy - * (even though there might be environment variables saying so). - * - * Setting it to NULL, means no proxy but allows the environment variables - * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). - */ - return Curl_setstropt(&s->str[STRING_PROXY], ptr); - - case CURLOPT_PRE_PROXY: - /* - * Set proxy server:port to use as SOCKS proxy. - * - * If the proxy is set to "" or NULL we explicitly say that we do not want - * to use the socks proxy. - */ - return Curl_setstropt(&s->str[STRING_PRE_PROXY], ptr); -#endif /* CURL_DISABLE_PROXY */ - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_SOCKS5_GSSAPI_SERVICE: - case CURLOPT_PROXY_SERVICE_NAME: - /* - * Set proxy authentication service name for Kerberos 5 and SPNEGO - */ - return Curl_setstropt(&s->str[STRING_PROXY_SERVICE_NAME], ptr); -#endif + /* we do not set s->method = HTTPREQ_CUSTOM; here, we continue as if we + were using the already set type and this changes the actual request + keyword */ case CURLOPT_SERVICE_NAME: /* * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO @@ -1928,15 +2110,9 @@ */ s->closesocket_client = ptr; break; - case CURLOPT_TRAILERDATA: -#ifndef CURL_DISABLE_HTTP - s->trailer_data = ptr; -#endif - break; case CURLOPT_PREREQDATA: s->prereq_userp = ptr; break; - case CURLOPT_ERRORBUFFER: /* * Error buffer provided by the caller to get the human readable error @@ -1962,6 +2138,12 @@ case CURLOPT_KRBLEVEL: return CURLE_NOT_BUILT_IN; /* removed in 8.17.0 */ + case CURLOPT_CHUNK_DATA: + s->wildcardptr = ptr; + break; + case CURLOPT_FNMATCH_DATA: + s->fnmatch_data = ptr; + break; #endif case CURLOPT_URL: /* @@ -2001,134 +2183,36 @@ * OAuth 2.0 bearer token to use in the operation */ return Curl_setstropt(&s->str[STRING_BEARER], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYUSERPWD: { - /* - * user:password needed to use the proxy - */ - char *u = NULL; - char *p = NULL; - result = setstropt_userpwd(ptr, &u, &p); - - /* URL decode the components */ - if(!result && u) { - Curl_safefree(s->str[STRING_PROXYUSERNAME]); - result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, - REJECT_ZERO); - } - if(!result && p) { - Curl_safefree(s->str[STRING_PROXYPASSWORD]); - result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, - REJECT_ZERO); - } - curlx_free(u); - curlx_free(p); - break; - } - case CURLOPT_PROXYUSERNAME: - /* - * authentication username to use in the operation - */ - return Curl_setstropt(&s->str[STRING_PROXYUSERNAME], ptr); - - case CURLOPT_PROXYPASSWORD: - /* - * authentication password to use in the operation - */ - return Curl_setstropt(&s->str[STRING_PROXYPASSWORD], ptr); - - case CURLOPT_NOPROXY: - /* - * proxy exception list - */ - return Curl_setstropt(&s->str[STRING_NOPROXY], ptr); -#endif /* !CURL_DISABLE_PROXY */ - case CURLOPT_RANGE: /* * What range of the file you want to transfer */ return Curl_setstropt(&s->str[STRING_SET_RANGE], ptr); - - case CURLOPT_CURLU: - /* - * pass CURLU to set URL - */ - Curl_bufref_free(&data->state.url); - Curl_safefree(s->str[STRING_SET_URL]); - s->uh = (CURLU *)ptr; - break; case CURLOPT_SSLCERT: /* * String that holds filename of the SSL certificate to use */ return Curl_setstropt(&s->str[STRING_CERT], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLCERT: - /* - * String that holds filename of the SSL certificate to use for proxy - */ - return Curl_setstropt(&s->str[STRING_CERT_PROXY], ptr); - -#endif case CURLOPT_SSLCERTTYPE: /* * String that holds file type of the SSL certificate to use */ return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use for proxy - */ - return Curl_setstropt(&s->str[STRING_CERT_TYPE_PROXY], ptr); - -#endif case CURLOPT_SSLKEY: /* * String that holds filename of the SSL key to use */ return Curl_setstropt(&s->str[STRING_KEY], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLKEY: - /* - * String that holds filename of the SSL key to use for proxy - */ - return Curl_setstropt(&s->str[STRING_KEY_PROXY], ptr); - -#endif case CURLOPT_SSLKEYTYPE: /* * String that holds file type of the SSL key to use */ return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use for proxy - */ - return Curl_setstropt(&s->str[STRING_KEY_TYPE_PROXY], ptr); - -#endif case CURLOPT_KEYPASSWD: /* * String that holds the SSL or SSH private key password. */ return Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_KEYPASSWD: - /* - * String that holds the SSL private key password for proxy. - */ - return Curl_setstropt(&s->str[STRING_KEY_PASSWD_PROXY], ptr); - -#endif case CURLOPT_SSLENGINE: /* * String that holds the SSL crypto engine. @@ -2140,19 +2224,6 @@ } } break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_HAPROXY_CLIENT_IP: - /* - * Set the client IP to send through HAProxy PROXY protocol - */ - result = Curl_setstropt(&s->str[STRING_HAPROXY_CLIENT_IP], ptr); - - /* enable the HAProxy protocol if an IP is provided */ - s->haproxyprotocol = !!s->str[STRING_HAPROXY_CLIENT_IP]; - break; - -#endif case CURLOPT_INTERFACE: /* * Set what interface or address/hostname to bind the socket to when @@ -2162,122 +2233,29 @@ &s->str[STRING_DEVICE], &s->str[STRING_INTERFACE], &s->str[STRING_BINDHOST]); - - case CURLOPT_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); -#endif - return CURLE_NOT_BUILT_IN; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY_PROXY], ptr); -#endif - return CURLE_NOT_BUILT_IN; -#endif - case CURLOPT_CAINFO: - /* - * Set CA info for SSL connection. Specify filename of the CA certificate - */ - s->ssl.custom_cafile = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CAINFO: - /* - * Set CA info SSL connection for proxy. Specify filename of the - * CA certificate - */ - s->proxy_ssl.custom_cafile = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); - -#endif - case CURLOPT_CAPATH: - /* - * Set CA path info for SSL connection. Specify directory name of the CA - * certificates which have been prepared using openssl c_rehash utility. - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { - /* This does not work on Windows. */ - s->ssl.custom_capath = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); - } -#endif - return CURLE_NOT_BUILT_IN; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CAPATH: - /* - * Set CA path info for SSL connection proxy. Specify directory name of the - * CA certificates which have been prepared using openssl c_rehash utility. - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { - /* This does not work on Windows. */ - s->proxy_ssl.custom_capath = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); - } -#endif - return CURLE_NOT_BUILT_IN; -#endif - case CURLOPT_CRLFILE: - /* - * Set CRL file info for SSL connection. Specify filename of the CRL - * to check certificates revocation - */ - return Curl_setstropt(&s->str[STRING_SSL_CRLFILE], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CRLFILE: - /* - * Set CRL file info for SSL connection for proxy. Specify filename of the - * CRL to check certificates revocation - */ - return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); - -#endif case CURLOPT_ISSUERCERT: /* * Set Issuer certificate file * to check certificates issuer */ - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); - -#endif + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_PRIVATE: /* * Set private data pointer. */ s->private_data = ptr; break; - #ifdef USE_SSL case CURLOPT_SSL_EC_CURVES: /* * Set accepted curves in SSL connection setup. * Specify colon-delimited list of curve algorithm names. */ - return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); - + if(Curl_ssl_supports(data, SSLSUPP_SSL_EC_CURVES)) + return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_SSL_SIGNATURE_ALGORITHMS: /* * Set accepted signature algorithms. @@ -2286,6 +2264,14 @@ if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) return Curl_setstropt(&s->str[STRING_SSL_SIGNATURE_ALGORITHMS], ptr); return CURLE_NOT_BUILT_IN; + case CURLOPT_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. + */ + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); + return CURLE_NOT_BUILT_IN; #endif #ifdef USE_SSH case CURLOPT_SSH_PUBLIC_KEYFILE: @@ -2293,13 +2279,17 @@ * Use this file instead of the $HOME/.ssh/id_dsa.pub file */ return Curl_setstropt(&s->str[STRING_SSH_PUBLIC_KEY], ptr); - case CURLOPT_SSH_PRIVATE_KEYFILE: /* * Use this file instead of the $HOME/.ssh/id_dsa file */ return Curl_setstropt(&s->str[STRING_SSH_PRIVATE_KEY], ptr); - + case CURLOPT_SSH_KEYDATA: + /* + * Custom client data to pass to the SSH keyfunc callback + */ + s->ssh_keyfunc_userp = ptr; + break; #if defined(USE_LIBSSH2) || defined(USE_LIBSSH) case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: /* @@ -2307,19 +2297,12 @@ * for validation purposes. */ return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); - case CURLOPT_SSH_KNOWNHOSTS: /* * Store the filename to read known hosts from. */ return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); #endif - case CURLOPT_SSH_KEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - s->ssh_keyfunc_userp = ptr; - break; #ifdef USE_LIBSSH2 case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: /* @@ -2327,7 +2310,6 @@ * for validation purposes. */ return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); - case CURLOPT_SSH_HOSTKEYDATA: /* * Custom client data to pass to the SSH keyfunc callback @@ -2345,9 +2327,8 @@ } else /* make a NULL argument reset to default */ - s->allowed_protocols = (curl_prot_t)CURLPROTO_ALL; + s->allowed_protocols = (curl_prot_t)CURLPROTO_64ALL; break; - case CURLOPT_REDIR_PROTOCOLS_STR: if(ptr) { curl_prot_t protos; @@ -2359,16 +2340,13 @@ /* make a NULL argument reset to default */ s->redir_protocols = (curl_prot_t)CURLPROTO_REDIR; break; - case CURLOPT_DEFAULT_PROTOCOL: /* Set the protocol to use when the URL does not include any protocol */ return Curl_setstropt(&s->str[STRING_DEFAULT_PROTOCOL], ptr); - #ifndef CURL_DISABLE_SMTP case CURLOPT_MAIL_FROM: /* Set the SMTP mail originator */ return Curl_setstropt(&s->str[STRING_MAIL_FROM], ptr); - case CURLOPT_MAIL_AUTH: /* Set the SMTP auth originator */ return Curl_setstropt(&s->str[STRING_MAIL_AUTH], ptr); @@ -2376,7 +2354,6 @@ case CURLOPT_SASL_AUTHZID: /* Authorization identity (identity to act as) */ return Curl_setstropt(&s->str[STRING_SASL_AUTHZID], ptr); - #ifndef CURL_DISABLE_RTSP case CURLOPT_RTSP_SESSION_ID: /* @@ -2384,83 +2361,53 @@ * resuming a previously established RTSP session */ return Curl_setstropt(&s->str[STRING_RTSP_SESSION_ID], ptr); - case CURLOPT_RTSP_STREAM_URI: /* * Set the Stream URI for the RTSP request. Unless the request is * for generic server options, the application will need to set this. */ return Curl_setstropt(&s->str[STRING_RTSP_STREAM_URI], ptr); - case CURLOPT_RTSP_TRANSPORT: /* * The content of the Transport: header for the RTSP request */ return Curl_setstropt(&s->str[STRING_RTSP_TRANSPORT], ptr); - case CURLOPT_INTERLEAVEDATA: s->rtp_out = ptr; break; #endif /* !CURL_DISABLE_RTSP */ -#ifndef CURL_DISABLE_FTP - case CURLOPT_CHUNK_DATA: - s->wildcardptr = ptr; - break; - case CURLOPT_FNMATCH_DATA: - s->fnmatch_data = ptr; - break; -#endif #ifdef USE_TLS_SRP case CURLOPT_TLSAUTH_USERNAME: return Curl_setstropt(&s->str[STRING_TLSAUTH_USERNAME], ptr); - + case CURLOPT_TLSAUTH_PASSWORD: + return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD], ptr); + case CURLOPT_TLSAUTH_TYPE: + if(ptr && !curl_strequal(ptr, "SRP")) + result = CURLE_BAD_FUNCTION_ARGUMENT; + break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_USERNAME: return Curl_setstropt(&s->str[STRING_TLSAUTH_USERNAME_PROXY], ptr); - -#endif - case CURLOPT_TLSAUTH_PASSWORD: - return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD], ptr); - -#ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_PASSWORD: return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD_PROXY], ptr); -#endif - case CURLOPT_TLSAUTH_TYPE: - if(ptr && !curl_strequal(ptr, "SRP")) - return CURLE_BAD_FUNCTION_ARGUMENT; - break; -#ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_TYPE: if(ptr && !curl_strequal(ptr, "SRP")) - return CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; break; #endif #endif -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES case CURLOPT_DNS_SERVERS: - result = Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); - if(result) - return result; - return Curl_async_ares_set_dns_servers(data); + return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); case CURLOPT_DNS_INTERFACE: - result = Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); - if(result) - return result; - return Curl_async_ares_set_dns_interface(data); + return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); case CURLOPT_DNS_LOCAL_IP4: - result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); - if(result) - return result; - return Curl_async_ares_set_dns_local_ip4(data); + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); case CURLOPT_DNS_LOCAL_IP6: - result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); - if(result) - return result; - return Curl_async_ares_set_dns_local_ip6(data); + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); #endif #ifdef USE_UNIX_SOCKETS @@ -2529,7 +2476,7 @@ } result = Curl_setstropt(&s->str[STRING_ALTSVC], ptr); if(result) - return result; + break; if(ptr) return Curl_altsvc_load(data->asi, ptr); break; @@ -2540,7 +2487,7 @@ if(!ptr) { s->tls_ech = CURLECH_DISABLE; - return CURLE_OK; + break; } plen = strlen(ptr); if(plen > CURL_MAX_INPUT_LENGTH) { @@ -2558,15 +2505,11 @@ s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_HARD; else if(plen > 5 && !strncmp(ptr, "ecl:", 4)) { result = Curl_setstropt(&s->str[STRING_ECH_CONFIG], ptr + 4); - if(result) - return result; - s->tls_ech |= CURLECH_CLA_CFG; + if(!result) + s->tls_ech |= CURLECH_CLA_CFG; } - else if(plen > 4 && !strncmp(ptr, "pn:", 3)) { + else if(plen > 4 && !strncmp(ptr, "pn:", 3)) result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); - if(result) - return result; - } break; } #endif @@ -2790,7 +2733,7 @@ if(s->postfieldsize < offt && s->postfields == s->str[STRING_COPYPOSTFIELDS]) { /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->postfields = NULL; } s->postfieldsize = offt; @@ -2907,7 +2850,9 @@ /* * Blob that holds Issuer certificate to check certificates issuer */ - return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT], blob); + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT_BLOB)) + return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT], blob); + return CURLE_NOT_BUILT_IN; default: return CURLE_UNKNOWN_OPTION; @@ -2917,7 +2862,7 @@ /* * Do not make Curl_vsetopt() static: it is called from - * packages/OS400/ccsidcurl.c. + * projects/OS400/ccsidcurl.c. */ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) { @@ -2944,6 +2889,7 @@ case CURLOPT_SHARE: /* CURLSH * */ case CURLOPT_STREAM_DEPENDS: /* CURL * */ case CURLOPT_STREAM_DEPENDS_E: /* CURL * */ + case CURLOPT_CURLU: /* CURLU * */ return setopt_pointers(data, option, param); default: break; @@ -2967,21 +2913,21 @@ */ #undef curl_easy_setopt -CURLcode curl_easy_setopt(CURL *d, CURLoption tag, ...) +CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) { va_list arg; CURLcode result; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; - va_start(arg, tag); + va_start(arg, option); - result = Curl_vsetopt(data, tag, arg); + result = Curl_vsetopt(data, option, arg); va_end(arg); if(result == CURLE_BAD_FUNCTION_ARGUMENT) - failf(data, "setopt 0x%x got bad argument", tag); + failf(data, "setopt 0x%x got bad argument", option); return result; }
diff --git a/Utilities/cmcurl/lib/setopt.h b/Utilities/cmcurl/lib/setopt.h index 0dd60c7..c421f5c 100644 --- a/Utilities/cmcurl/lib/setopt.h +++ b/Utilities/cmcurl/lib/setopt.h
@@ -34,7 +34,7 @@ CURLcode Curl_setstropt(char **charp, const char *s) WARN_UNUSED_RESULT; CURLcode Curl_setblobopt(struct curl_blob **blobp, const struct curl_blob *blob) WARN_UNUSED_RESULT; -CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg) +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) WARN_UNUSED_RESULT; #endif /* HEADER_CURL_SETOPT_H */
diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h index 4cb0d89..b5bd733 100644 --- a/Utilities/cmcurl/lib/setup-os400.h +++ b/Utilities/cmcurl/lib/setup-os400.h
@@ -30,9 +30,6 @@ /* OS/400 netdb.h does not define NI_MAXSERV. */ #define NI_MAXSERV 32 -/* 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 @@ -42,7 +39,7 @@ #include <stdio.h> /* Be sure it is loaded. */ #undef puts -#define puts(s) (fputs((s), stdout) == EOF ? EOF : putchar('\n')) +#define puts(s) (fputs(s, stdout) == EOF ? EOF : putchar('\n')) /* System API wrapper prototypes & definitions to support ASCII parameters. */ @@ -70,22 +67,22 @@ /* GSSAPI wrappers. */ -extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, gss_OID in_name_type, - gss_name_t * out_name); + gss_name_t *out_name); #define gss_import_name Curl_gss_import_name_a -extern OM_uint32 Curl_gss_display_status_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_display_status_a(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, gss_OID mech_type, - gss_msg_ctx_t * message_context, + gss_msg_ctx_t *message_context, gss_buffer_t status_string); #define gss_display_status Curl_gss_display_status_a -extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 *minor_status, gss_cred_id_t cred_handle, - gss_ctx_id_t * context_handle, + gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, gss_flags_t req_flags, @@ -93,10 +90,10 @@ gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, - gss_OID * actual_mech_type, + gss_OID *actual_mech_type, gss_buffer_t output_token, - gss_flags_t * ret_flags, - OM_uint32 * time_rec); + gss_flags_t *ret_flags, + OM_uint32 *time_rec); #define gss_init_sec_context Curl_gss_init_sec_context_a extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 *minor_status,
diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h index dd46022..35d12f0 100644 --- a/Utilities/cmcurl/lib/setup-vms.h +++ b/Utilities/cmcurl/lib/setup-vms.h
@@ -24,11 +24,10 @@ * ***************************************************************************/ -/* */ -/* JEM, 12/30/12, VMS now generates config.h, so only define wrappers for */ -/* getenv(), getpwuid() and provide is_vms_shell() */ -/* Also need upper case symbols for system services, and */ -/* OpenSSL, and some Kerberos image */ +/* JEM, 2012-12-30, VMS now generates config.h, so only define wrappers for */ +/* getenv(), getpwuid() and provide is_vms_shell() */ +/* Also need upper case symbols for system services, and */ +/* OpenSSL, and some Kerberos image */ #ifdef __DECC #pragma message save @@ -87,7 +86,7 @@ char *test_str; /* See if the result is in VMS format, if not, we are done */ - /* Assume that this is a PATH, not just some data */ + /* Assume that this is a PATH, not some data */ test_str = strpbrk(path, ":[<^"); if(!test_str) { return (char *)path; @@ -166,7 +165,7 @@ return my_passwd; } - /* If no changes needed just return it */ + /* If no changes needed, return it */ if(unix_path == my_passwd->pw_dir) { return my_passwd; }
diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h index 02177a7..6a89b96 100644 --- a/Utilities/cmcurl/lib/setup-win32.h +++ b/Utilities/cmcurl/lib/setup-win32.h
@@ -45,31 +45,32 @@ /* Define to use BSD-style lwIP TCP/IP stack. */ /* #define USE_LWIPSOCK 1 */ # undef HAVE_GETHOSTNAME -# undef LWIP_POSIX_SOCKETS_IO_NAMES -# undef RECV_TYPE_ARG1 -# undef RECV_TYPE_ARG3 -# undef SEND_TYPE_ARG1 -# undef SEND_TYPE_ARG3 # define HAVE_GETHOSTBYNAME_R # define HAVE_GETHOSTBYNAME_R_6 +# undef LWIP_POSIX_SOCKETS_IO_NAMES # define LWIP_POSIX_SOCKETS_IO_NAMES 0 +# undef RECV_TYPE_ARG1 # define RECV_TYPE_ARG1 int +# undef RECV_TYPE_ARG3 # define RECV_TYPE_ARG3 size_t +# undef SEND_TYPE_ARG1 # define SEND_TYPE_ARG1 int +# undef SEND_TYPE_ARG3 # define SEND_TYPE_ARG3 size_t #elif defined(_WIN32) # define USE_WINSOCK 2 +# include <winsock2.h> +# include <ws2tcpip.h> #endif /* - * Include header files for Windows builds before redefining anything. - * Use this preprocessor block only to include or exclude windows.h, - * winsock2.h or ws2tcpip.h. Any other Windows thing belongs - * to any other further and independent block. Under Cygwin things work - * just as under Linux (e.g. <sys/socket.h>) and the Winsock headers should - * never be included when __CYGWIN__ is defined. + * Include header files for Windows builds before redefining anything. Use + * this preprocessor block only to include or exclude windows.h, winsock2.h or + * ws2tcpip.h. Any other Windows thing belongs to any other further and + * independent block. Under Cygwin things work as under Linux (e.g. + * <sys/socket.h>) and the Winsock headers should never be included when + * __CYGWIN__ is defined. */ - #ifdef _WIN32 # if defined(UNICODE) && !defined(_UNICODE) # error "UNICODE is defined but _UNICODE is not defined" @@ -77,8 +78,6 @@ # if defined(_UNICODE) && !defined(UNICODE) # error "_UNICODE is defined but UNICODE is not defined" # endif -# include <winsock2.h> -# include <ws2tcpip.h> # include <windows.h> # include <winerror.h> # include <tchar.h> @@ -89,13 +88,6 @@ * those symbols to compare against, and even those that do may be missing * newer symbols. */ - -#ifndef _WIN32_WINNT_WINXP -#define _WIN32_WINNT_WINXP 0x0501 /* Windows XP */ -#endif -#ifndef _WIN32_WINNT_WS03 -#define _WIN32_WINNT_WS03 0x0502 /* Windows Server 2003 */ -#endif #ifndef _WIN32_WINNT_VISTA #define _WIN32_WINNT_VISTA 0x0600 /* Windows Vista */ #endif
diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c index 7c3cad3..d97f45f 100644 --- a/Utilities/cmcurl/lib/sha256.c +++ b/Utilities/cmcurl/lib/sha256.c
@@ -30,45 +30,28 @@ #include "curl_sha256.h" #ifdef USE_MBEDTLS - #include <mbedtls/version.h> - #if MBEDTLS_VERSION_NUMBER < 0x03020000 - #error "mbedTLS 3.2.0 or later required" - #endif - #include <psa/crypto_config.h> - #if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ - #define USE_MBEDTLS_SHA256 - #endif +#include <mbedtls/version.h> +#if MBEDTLS_VERSION_NUMBER < 0x03020000 +#error "mbedTLS 3.2.0 or later required" #endif - -#ifdef USE_OPENSSL -#include <openssl/evp.h> -#elif defined(USE_GNUTLS) -#include <nettle/sha.h> -#elif defined(USE_MBEDTLS_SHA256) -#include <psa/crypto.h> -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) -#include <CommonCrypto/CommonDigest.h> -#define AN_APPLE_OS -#elif defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> +#include <psa/crypto_config.h> #endif /* Please keep the SSL backend-specific #if branches in this order: * * 1. USE_OPENSSL - * 2. USE_GNUTLS - * 3. USE_MBEDTLS - * 4. USE_COMMON_CRYPTO - * 5. USE_WIN32_CRYPTO + * 2. USE_WOLFSSL + * 3. USE_GNUTLS + * 4. USE_MBEDTLS + * 5. USE_COMMON_CRYPTO + * 6. USE_WIN32_CRYPTO * * This ensures that the same SSL branch gets activated throughout this source * file even if multiple backends are enabled at the same time. */ #ifdef USE_OPENSSL +#include <openssl/evp.h> struct ossl_sha256_ctx { EVP_MD_CTX *openssl_ctx; @@ -104,7 +87,33 @@ EVP_MD_CTX_destroy(ctx->openssl_ctx); } +#elif defined(USE_WOLFSSL) +#include <wolfssl/options.h> +#include <wolfssl/wolfcrypt/sha256.h> + +typedef struct wc_Sha256 my_sha256_ctx; + +static CURLcode my_sha256_init(void *in) +{ + if(wc_InitSha256(in)) + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static void my_sha256_update(void *in, + const unsigned char *data, + unsigned int length) +{ + (void)wc_Sha256Update(in, data, (word32)length); +} + +static void my_sha256_final(unsigned char *digest, void *in) +{ + (void)wc_Sha256Final(in, digest); +} + #elif defined(USE_GNUTLS) +#include <nettle/sha.h> typedef struct sha256_ctx my_sha256_ctx; @@ -126,7 +135,9 @@ sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); } -#elif defined(USE_MBEDTLS_SHA256) +#elif defined(USE_MBEDTLS) && \ + defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ +#include <psa/crypto.h> typedef psa_hash_operation_t my_sha256_ctx; @@ -152,7 +163,12 @@ &actual_length); } -#elif defined(AN_APPLE_OS) +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) +#include <CommonCrypto/CommonDigest.h> + typedef CC_SHA256_CTX my_sha256_ctx; static CURLcode my_sha256_init(void *ctx) @@ -174,6 +190,7 @@ } #elif defined(USE_WIN32_CRYPTO) +#include <wincrypt.h> struct sha256_ctx { HCRYPTPROV hCryptProv; @@ -181,11 +198,6 @@ }; typedef struct sha256_ctx my_sha256_ctx; -/* Offered when targeting Vista (XP SP2+) */ -#ifndef CALG_SHA_256 -#define CALG_SHA_256 0x0000800c -#endif - static CURLcode my_sha256_init(void *in) { my_sha256_ctx *ctx = (my_sha256_ctx *)in; @@ -243,41 +255,23 @@ (a)[0] = (unsigned char)((((unsigned long)(val)) >> 24) & 0xff); \ (a)[1] = (unsigned char)((((unsigned long)(val)) >> 16) & 0xff); \ (a)[2] = (unsigned char)((((unsigned long)(val)) >> 8) & 0xff); \ - (a)[3] = (unsigned char) (((unsigned long)(val)) & 0xff); \ + (a)[3] = (unsigned char)(((unsigned long)(val)) & 0xff); \ } while(0) -#ifdef HAVE_LONGLONG -#define WPA_PUT_BE64(a, val) \ - do { \ - (a)[0] = (unsigned char)(((unsigned long long)(val)) >> 56); \ - (a)[1] = (unsigned char)(((unsigned long long)(val)) >> 48); \ - (a)[2] = (unsigned char)(((unsigned long long)(val)) >> 40); \ - (a)[3] = (unsigned char)(((unsigned long long)(val)) >> 32); \ - (a)[4] = (unsigned char)(((unsigned long long)(val)) >> 24); \ - (a)[5] = (unsigned char)(((unsigned long long)(val)) >> 16); \ - (a)[6] = (unsigned char)(((unsigned long long)(val)) >> 8); \ - (a)[7] = (unsigned char)(((unsigned long long)(val)) & 0xff); \ +#define WPA_PUT_BE64(a, val) \ + do { \ + (a)[0] = (unsigned char)(((uint64_t)(val)) >> 56); \ + (a)[1] = (unsigned char)(((uint64_t)(val)) >> 48); \ + (a)[2] = (unsigned char)(((uint64_t)(val)) >> 40); \ + (a)[3] = (unsigned char)(((uint64_t)(val)) >> 32); \ + (a)[4] = (unsigned char)(((uint64_t)(val)) >> 24); \ + (a)[5] = (unsigned char)(((uint64_t)(val)) >> 16); \ + (a)[6] = (unsigned char)(((uint64_t)(val)) >> 8); \ + (a)[7] = (unsigned char)(((uint64_t)(val)) & 0xff); \ } while(0) -#else -#define WPA_PUT_BE64(a, val) \ - do { \ - (a)[0] = (unsigned char)(((unsigned __int64)(val)) >> 56); \ - (a)[1] = (unsigned char)(((unsigned __int64)(val)) >> 48); \ - (a)[2] = (unsigned char)(((unsigned __int64)(val)) >> 40); \ - (a)[3] = (unsigned char)(((unsigned __int64)(val)) >> 32); \ - (a)[4] = (unsigned char)(((unsigned __int64)(val)) >> 24); \ - (a)[5] = (unsigned char)(((unsigned __int64)(val)) >> 16); \ - (a)[6] = (unsigned char)(((unsigned __int64)(val)) >> 8); \ - (a)[7] = (unsigned char)(((unsigned __int64)(val)) & 0xff); \ - } while(0) -#endif struct sha256_state { -#ifdef HAVE_LONGLONG - unsigned long long length; -#else - unsigned __int64 length; -#endif + uint64_t length; unsigned long state[8], curlen; unsigned char buf[64]; }; @@ -307,7 +301,7 @@ #define Sha256_Ch(x, y, z) (z ^ (x & (y ^ z))) #define Sha256_Maj(x, y, z) (((x | y) & z) | (x & y)) -#define Sha256_S(x, n) RORc((x), (n)) +#define Sha256_S(x, n) RORc(x, n) #define Sha256_R(x, n) (((x) & 0xFFFFFFFFUL) >> (n)) #define Sigma0(x) (Sha256_S(x, 2) ^ Sha256_S(x, 13) ^ Sha256_S(x, 22)) @@ -315,7 +309,7 @@ #define Gamma0(x) (Sha256_S(x, 7) ^ Sha256_S(x, 18) ^ Sha256_R(x, 3)) #define Gamma1(x) (Sha256_S(x, 17) ^ Sha256_S(x, 19) ^ Sha256_R(x, 10)) -/* Compress 512-bits */ +/* Compress 512 bits */ static int sha256_compress(struct sha256_state *md, const unsigned char *buf) { unsigned long S[8], W[64]; @@ -325,7 +319,7 @@ for(i = 0; i < 8; i++) { S[i] = md->state[i]; } - /* copy the state into 512-bits into W[0..15] */ + /* copy the state into 512 bits into W[0..15] */ for(i = 0; i < 16; i++) W[i] = WPA_GET_BE32(buf + (4 * i)); /* fill W[16..63] */ @@ -448,7 +442,7 @@ */ if(md->curlen > 56) { while(md->curlen < 64) { - md->buf[md->curlen++] = (unsigned char)0; + md->buf[md->curlen++] = 0; } sha256_compress(md, md->buf); md->curlen = 0; @@ -456,7 +450,7 @@ /* Pad up to 56 bytes of zeroes */ while(md->curlen < 56) { - md->buf[md->curlen++] = (unsigned char)0; + md->buf[md->curlen++] = 0; } /* Store length */ @@ -484,14 +478,19 @@ * Returns CURLE_OK on success. */ CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, - const size_t length) + size_t len) { CURLcode result; my_sha256_ctx ctx; result = my_sha256_init(&ctx); if(!result) { - my_sha256_update(&ctx, input, curlx_uztoui(length)); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + my_sha256_update(&ctx, input, ilen); + len -= ilen; + input += ilen; + } while(len); my_sha256_final(output, &ctx); } return result;
diff --git a/Utilities/cmcurl/lib/sigpipe.h b/Utilities/cmcurl/lib/sigpipe.h index 5d2c78a..b489796 100644 --- a/Utilities/cmcurl/lib/sigpipe.h +++ b/Utilities/cmcurl/lib/sigpipe.h
@@ -25,19 +25,15 @@ ***************************************************************************/ #include "curl_setup.h" -#if defined(HAVE_SIGACTION) && \ - (defined(USE_OPENSSL) || defined(USE_MBEDTLS) || defined(USE_WOLFSSL)) +#if defined(HAVE_SIGACTION) && !defined(USE_SO_NOSIGPIPE) #include <signal.h> -struct sigpipe_ignore { +struct Curl_sigpipe_ctx { struct sigaction old_pipe_act; BIT(no_signal); }; -#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x -#define SIGPIPE_MEMBER(x) struct sigpipe_ignore x - -static void sigpipe_init(struct sigpipe_ignore *ig) +static CURL_INLINE void sigpipe_init(struct Curl_sigpipe_ctx *ig) { memset(ig, 0, sizeof(*ig)); ig->no_signal = TRUE; @@ -48,8 +44,8 @@ * internals, and then sigpipe_restore() will restore the situation when we * return from libcurl again. */ -static void sigpipe_ignore(struct Curl_easy *data, - struct sigpipe_ignore *ig) +static CURL_INLINE void sigpipe_ignore(struct Curl_easy *data, + struct Curl_sigpipe_ctx *ig) { /* get a local copy of no_signal because the Curl_easy might not be around when we restore */ @@ -61,6 +57,10 @@ action = ig->old_pipe_act; /* ignore this signal */ action.sa_handler = SIG_IGN; +#ifdef SA_SIGINFO + /* clear SA_SIGINFO flag since we are using sa_handler */ + action.sa_flags &= ~SA_SIGINFO; +#endif sigaction(SIGPIPE, &action, NULL); } } @@ -70,30 +70,33 @@ * and SIGPIPE handling. It MUST only be called after a corresponding * sigpipe_ignore() was used. */ -static void sigpipe_restore(struct sigpipe_ignore *ig) +static CURL_INLINE void sigpipe_restore(struct Curl_sigpipe_ctx *ig) { if(!ig->no_signal) /* restore the outside state */ sigaction(SIGPIPE, &ig->old_pipe_act, NULL); } -static void sigpipe_apply(struct Curl_easy *data, - struct sigpipe_ignore *ig) +static CURL_INLINE void sigpipe_apply(struct Curl_easy *data, + struct Curl_sigpipe_ctx *ig) { - if(data->set.no_signal != ig->no_signal) { + if(data && (data->set.no_signal != ig->no_signal)) { sigpipe_restore(ig); sigpipe_ignore(data, ig); } } -#else -/* for systems without sigaction */ -#define sigpipe_ignore(x, y) Curl_nop_stmt -#define sigpipe_apply(x, y) Curl_nop_stmt -#define sigpipe_init(x) Curl_nop_stmt -#define sigpipe_restore(x) Curl_nop_stmt -#define SIGPIPE_VARIABLE(x) -#define SIGPIPE_MEMBER(x) bool x -#endif +#else /* !HAVE_SIGACTION || USE_SO_NOSIGPIPE */ +/* for systems without sigaction or where SO_NOSIGPIPE is used. */ +#define sigpipe_ignore(x, y) do { (void)(x); (void)(y); } while(0) +#define sigpipe_apply(x, y) do { (void)(x); (void)(y); } while(0) +#define sigpipe_init(x) do { (void)(x); } while(0) +#define sigpipe_restore(x) do { (void)(x); } while(0) + +struct Curl_sigpipe_ctx { + bool dummy; +}; + +#endif /* else HAVE_SIGACTION && !USE_SO_NOSIGPIPE */ #endif /* HEADER_CURL_SIGPIPE_H */
diff --git a/Utilities/cmcurl/lib/slist.c b/Utilities/cmcurl/lib/slist.c index 9bfd08a..83fd291 100644 --- a/Utilities/cmcurl/lib/slist.c +++ b/Utilities/cmcurl/lib/slist.c
@@ -79,7 +79,7 @@ * curl_slist_append() appends a string to the linked list. It always returns * the address of the first record, so that you can use this function as an * initialization function as well as an append function. If you find this - * bothersome, then simply create a separate _init function and call it + * bothersome, then create a separate _init function and call it * appropriately from within the program. */ struct curl_slist *curl_slist_append(struct curl_slist *list, const char *data) @@ -132,7 +132,7 @@ item = list; do { next = item->next; - Curl_safefree(item->data); + curlx_safefree(item->data); curlx_free(item); item = next; } while(next);
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index 003a38f..6a97d2e 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c
@@ -23,11 +23,15 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> /* for htons() */ +#endif #include "smb.h" -#include "urldata.h" #include "url.h" #include "sendf.h" #include "curl_trc.h" @@ -285,77 +289,6 @@ # pragma pack(pop) #endif -/* Local API functions */ -static CURLcode smb_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode smb_connect(struct Curl_easy *data, bool *done); -static CURLcode smb_connection_state(struct Curl_easy *data, bool *done); -static CURLcode smb_do(struct Curl_easy *data, bool *done); -static CURLcode smb_request_state(struct Curl_easy *data, bool *done); -static CURLcode smb_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode smb_parse_url_path(struct Curl_easy *data, - struct smb_conn *smbc, - struct smb_request *req); - -/* - * SMB handler interface - */ -const struct Curl_handler Curl_handler_smb = { - "smb", /* scheme */ - smb_setup_connection, /* setup_connection */ - smb_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - smb_connect, /* connect_it */ - smb_connection_state, /* connecting */ - smb_request_state, /* doing */ - smb_pollset, /* proto_pollset */ - smb_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SMB, /* defport */ - CURLPROTO_SMB, /* protocol */ - CURLPROTO_SMB, /* family */ - PROTOPT_CONN_REUSE /* flags */ -}; - -#ifdef USE_SSL -/* - * SMBS handler interface - */ -const struct Curl_handler Curl_handler_smbs = { - "smbs", /* scheme */ - smb_setup_connection, /* setup_connection */ - smb_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - smb_connect, /* connect_it */ - smb_connection_state, /* connecting */ - smb_request_state, /* doing */ - smb_pollset, /* proto_pollset */ - smb_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SMBS, /* defport */ - CURLPROTO_SMBS, /* protocol */ - CURLPROTO_SMB, /* family */ - PROTOPT_SSL | PROTOPT_CONN_REUSE /* flags */ -}; -#endif - #define MAX_PAYLOAD_SIZE 0x8000 #define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000) #define CLIENTNAME "curl" @@ -390,7 +323,7 @@ static void conn_state(struct Curl_easy *data, struct smb_conn *smbc, enum smb_conn_state newstate) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* For debug purposes */ static const char * const names[] = { "SMB_NOT_CONNECTED", @@ -404,8 +337,9 @@ if(smbc->state != newstate) infof(data, "SMB conn %p state change from %s to %s", (void *)smbc, names[smbc->state], names[newstate]); -#endif +#else (void)data; +#endif smbc->state = newstate; } @@ -414,7 +348,7 @@ { struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY); if(req) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* For debug purposes */ static const char * const names[] = { "SMB_REQUESTING", @@ -442,9 +376,7 @@ struct smb_request *req = entry; (void)key; (void)klen; - /* `req->path` points to somewhere in `struct smb_conn` which is - * kept at the connection meta. If the connection is destroyed first, - * req->path points to free'd memory. */ + curlx_safefree(req->path); curlx_free(req); } @@ -453,13 +385,60 @@ struct smb_conn *smbc = entry; (void)key; (void)klen; - Curl_safefree(smbc->share); - Curl_safefree(smbc->domain); - Curl_safefree(smbc->recv_buf); - Curl_safefree(smbc->send_buf); + curlx_safefree(smbc->share); + curlx_safefree(smbc->domain); + curlx_safefree(smbc->recv_buf); + curlx_safefree(smbc->send_buf); curlx_free(smbc); } +static CURLcode smb_parse_url_path(struct Curl_easy *data, + struct smb_conn *smbc, + struct smb_request *req) +{ + char *path; + char *slash, *s; + CURLcode result; + + /* URL decode the path */ + result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); + if(result) + return result; + + /* Parse the path for the share */ + curlx_safefree(smbc->share); + smbc->share = curlx_strdup((*path == '/' || *path == '\\') + ? path + 1 : path); + curlx_free(path); + if(!smbc->share) + return CURLE_OUT_OF_MEMORY; + + slash = strchr(smbc->share, '/'); + if(!slash) + slash = strchr(smbc->share, '\\'); + + /* The share must be present */ + if(!slash) { + curlx_safefree(smbc->share); + failf(data, "missing share in URL path for SMB"); + return CURLE_URL_MALFORMAT; + } + + /* Parse the path for the file path converting any forward slashes into + backslashes */ + *slash++ = 0; + for(s = slash; *s; s++) { + if(*s == '/') + *s = '\\'; + } + /* keep a copy at easy struct to not share this with connection state */ + req->path = curlx_strdup(slash); + if(!req->path) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + /* this should setup things in the connection, not in the easy handle */ static CURLcode smb_setup_connection(struct Curl_easy *data, @@ -571,7 +550,7 @@ if(nbt_size >= msg_size + 1) { /* Add the word count */ - msg_size += 1 + ((unsigned char)buf[msg_size]) * sizeof(unsigned short); + msg_size += 1 + (((unsigned char)buf[msg_size]) * sizeof(unsigned short)); if(nbt_size >= msg_size + sizeof(unsigned short)) { /* Add the byte count */ msg_size += sizeof(unsigned short) + @@ -1226,47 +1205,27 @@ return CURLE_URL_MALFORMAT; } -static CURLcode smb_parse_url_path(struct Curl_easy *data, - struct smb_conn *smbc, - struct smb_request *req) -{ - char *path; - char *slash; - CURLcode result; +/* + * SMB handler interface + */ +const struct Curl_protocol Curl_protocol_smb = { + smb_setup_connection, /* setup_connection */ + smb_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + smb_connect, /* connect_it */ + smb_connection_state, /* connecting */ + smb_request_state, /* doing */ + smb_pollset, /* proto_pollset */ + smb_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; - /* URL decode the path */ - result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); - if(result) - return result; - - /* Parse the path for the share */ - smbc->share = curlx_strdup((*path == '/' || *path == '\\') - ? path + 1 : path); - curlx_free(path); - if(!smbc->share) - return CURLE_OUT_OF_MEMORY; - - slash = strchr(smbc->share, '/'); - if(!slash) - slash = strchr(smbc->share, '\\'); - - /* The share must be present */ - if(!slash) { - Curl_safefree(smbc->share); - failf(data, "missing share in URL path for SMB"); - return CURLE_URL_MALFORMAT; - } - - /* Parse the path for the file path converting any forward slashes into - backslashes */ - *slash++ = 0; - req->path = slash; - - for(; *slash; slash++) { - if(*slash == '/') - *slash = '\\'; - } - return CURLE_OK; -} - -#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ +#endif /* CURL_ENABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */
diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h index 474f8d6..5a92ad1 100644 --- a/Utilities/cmcurl/lib/smb.h +++ b/Utilities/cmcurl/lib/smb.h
@@ -24,12 +24,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - -extern const struct Curl_handler Curl_handler_smb; -extern const struct Curl_handler Curl_handler_smbs; - -#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) +extern const struct Curl_protocol Curl_protocol_smb; +#endif #endif /* HEADER_CURL_SMB_H */
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index 471c823..b5c425c 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c
@@ -39,6 +39,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "smtp.h" #ifndef CURL_DISABLE_SMTP @@ -56,7 +58,6 @@ #include <inet.h> #endif -#include "urldata.h" #include "sendf.h" #include "curl_trc.h" #include "hostip.h" @@ -65,7 +66,6 @@ #include "escape.h" #include "pingpong.h" #include "mime.h" -#include "smtp.h" #include "vtls/vtls.h" #include "cfilters.h" #include "connect.h" @@ -136,116 +136,343 @@ BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */ }; -/* Local API functions */ -static CURLcode smtp_regular_transfer(struct Curl_easy *data, - struct smtp_conn *smtpc, - struct SMTP *smtp, - bool *done); -static CURLcode smtp_do(struct Curl_easy *data, bool *done); -static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode smtp_connect(struct Curl_easy *data, bool *done); -static CURLcode smtp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode smtp_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode smtp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); +/*********************************************************************** + * + * smtp_parse_url_options() + * + * Parse the URL login options. + */ static CURLcode smtp_parse_url_options(struct connectdata *conn, - struct smtp_conn *smtpc); + struct smtp_conn *smtpc) +{ + CURLcode result = CURLE_OK; + const char *ptr = conn->options; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(curl_strnequal(key, "AUTH=", 5)) + result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, + value, ptr - value); + else + result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; + } + + return result; +} + +/*********************************************************************** + * + * smtp_parse_url_path() + * + * Parse the URL path into separate path components. + */ static CURLcode smtp_parse_url_path(struct Curl_easy *data, - struct smtp_conn *smtpc); + struct smtp_conn *smtpc) +{ + /* The SMTP struct is already initialised in smtp_connect() */ + const char *path = &data->state.up.path[1]; /* skip leading path */ + char localhost[HOSTNAME_MAX + 1]; + + /* Calculate the path if necessary */ + if(!*path) { + if(!Curl_gethostname(localhost, sizeof(localhost))) + path = localhost; + else + path = "localhost"; + } + + /* URL decode the path and use it as the domain in our EHLO */ + return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL); +} + +/*********************************************************************** + * + * smtp_parse_custom_request() + * + * Parse the custom request. + */ static CURLcode smtp_parse_custom_request(struct Curl_easy *data, - struct SMTP *smtp); -static CURLcode smtp_parse_address(const char *fqma, - char **address, struct hostname *host, - const char **suffix); -static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out); -static CURLcode cr_eob_add(struct Curl_easy *data); + struct SMTP *smtp) +{ + CURLcode result = CURLE_OK; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; -/* - * SMTP protocol handler. + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL); + + return result; +} + +/*********************************************************************** + * + * smtp_parse_address() + * + * Parse the fully qualified mailbox address into a local address part and the + * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if + * necessary. + * + * Parameters: + * + * fqma [in] - The fully qualified mailbox address (which may or + * may not contain UTF-8 characters). + * address [in/out] - A new allocated buffer which holds the local + * address part of the mailbox. This buffer must be + * free'ed by the caller. + * host [in/out] - The hostname structure that holds the original, + * and optionally encoded, hostname. + * Curl_free_idnconverted_hostname() must be called + * once the caller has finished with the structure. + * + * Returns CURLE_OK on success. + * + * Notes: + * + * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor + * that conversion then we shall return success. This allow the caller to send + * the data to the server as a U-label (as per RFC-6531 sect. 3.2). + * + * If an mailbox '@' separator cannot be located then the mailbox is considered + * to be either a local mailbox or an invalid mailbox (depending on what the + * calling function deems it to be) then the input will be returned in + * the address part with the hostname being NULL. */ +static CURLcode smtp_parse_address(const char *fqma, char **address, + struct hostname *host, const char **suffix) +{ + CURLcode result = CURLE_OK; + size_t length; + char *addressend; -const struct Curl_handler Curl_handler_smtp = { - "smtp", /* scheme */ - smtp_setup_connection, /* setup_connection */ - smtp_do, /* do_it */ - smtp_done, /* done */ - ZERO_NULL, /* do_more */ - smtp_connect, /* connect_it */ - smtp_multi_statemach, /* connecting */ - smtp_doing, /* doing */ - smtp_pollset, /* proto_pollset */ - smtp_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - smtp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SMTP, /* defport */ - CURLPROTO_SMTP, /* protocol */ - CURLPROTO_SMTP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE + /* Duplicate the fully qualified email address so we can manipulate it, + ensuring it does not contain the delimiters if specified */ + char *dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma); + if(!dup) + return CURLE_OUT_OF_MEMORY; + + if(fqma[0] != '<') { + length = strlen(dup); + if(length) { + if(dup[length - 1] == '>') + dup[length - 1] = '\0'; + } + } + else { + addressend = strrchr(dup, '>'); + if(addressend) { + *addressend = '\0'; + *suffix = addressend + 1; + } + } + + /* Extract the hostname from the address (if we can) */ + host->name = strpbrk(dup, "@"); + if(host->name) { + *host->name = '\0'; + host->name = host->name + 1; + + /* Attempt to convert the hostname to IDN ACE */ + (void)Curl_idnconvert_hostname(host); + + /* If Curl_idnconvert_hostname() fails then we shall attempt to continue + and send the hostname using UTF-8 rather than as 7-bit ACE (which is + our preference) */ + } + + /* Extract the local address from the mailbox */ + *address = dup; + + return result; +} + +struct cr_eob_ctx { + struct Curl_creader super; + struct bufq buf; + size_t n_eob; /* how many EOB bytes we matched so far */ + size_t eob; /* Number of bytes of the EOB (End Of Body) that + have been received so far */ + BIT(read_eos); /* we read an EOS from the next reader */ + BIT(processed_eos); /* we read and processed an EOS */ + BIT(eos); /* we have returned an EOS */ }; -#ifdef USE_SSL -/* - * SMTPS protocol handler. - */ +static CURLcode cr_eob_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_eob_ctx *ctx = reader->ctx; + (void)data; + /* The first char we read is the first on a line, as if we had + * read CRLF before */ + ctx->n_eob = 2; + Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT); + return CURLE_OK; +} -const struct Curl_handler Curl_handler_smtps = { - "smtps", /* scheme */ - smtp_setup_connection, /* setup_connection */ - smtp_do, /* do_it */ - smtp_done, /* done */ - ZERO_NULL, /* do_more */ - smtp_connect, /* connect_it */ - smtp_multi_statemach, /* connecting */ - smtp_doing, /* doing */ - smtp_pollset, /* proto_pollset */ - smtp_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - smtp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SMTPS, /* defport */ - CURLPROTO_SMTPS, /* protocol */ - CURLPROTO_SMTP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE -}; -#endif +static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader) +{ + struct cr_eob_ctx *ctx = reader->ctx; + (void)data; + Curl_bufq_free(&ctx->buf); +} -/* SASL parameters for the smtp protocol */ -static const struct SASLproto saslsmtp = { - "smtp", /* The service name */ - smtp_perform_auth, /* Send authentication command */ - smtp_continue_auth, /* Send authentication continuation */ - smtp_cancel_auth, /* Cancel authentication */ - smtp_get_message, /* Get SASL response message */ - 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ - 334, /* Code received when continuation is expected */ - 235, /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ +/* this is the 5-bytes End-Of-Body marker for SMTP */ +#define SMTP_EOB "\r\n.\r\n" +#define SMTP_EOB_FIND_LEN 3 + +/* client reader doing SMTP End-Of-Body escaping. */ +static CURLcode cr_eob_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_eob_ctx *ctx = reader->ctx; + CURLcode result = CURLE_OK; + size_t nread, i, start, n; + bool eos; + + if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* Get more and convert it when needed */ + result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); + CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d", + blen, result, nread, eos); + if(result) + return result; + + ctx->read_eos = eos; + if(nread) { + if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) { + /* not in the middle of a match, no EOB start found, pass */ + *pnread = nread; + *peos = FALSE; + return CURLE_OK; + } + /* scan for EOB (continuation) and convert */ + for(i = start = 0; i < nread; ++i) { + if(ctx->n_eob >= SMTP_EOB_FIND_LEN) { + /* matched the EOB prefix and seeing additional char, add '.' */ + result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); + if(result) + return result; + result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n); + if(result) + return result; + ctx->n_eob = 0; + start = i; + if(data->state.infilesize > 0) + data->state.infilesize++; + } + + if(buf[i] != SMTP_EOB[ctx->n_eob]) + ctx->n_eob = 0; + + if(buf[i] == SMTP_EOB[ctx->n_eob]) { + /* matching another char of the EOB */ + ++ctx->n_eob; + } + } + + /* add any remainder to buf */ + if(start < nread) { + result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n); + if(result) + return result; + } + } + } + + *peos = FALSE; + + if(ctx->read_eos && !ctx->processed_eos) { + /* if we last matched a CRLF or if the data was empty, add ".\r\n" + * to end the body. If we sent something and it did not end with "\r\n", + * add "\r\n.\r\n" to end the body */ + const char *eob = SMTP_EOB; + CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'"); + switch(ctx->n_eob) { + case 2: + /* seen a CRLF at the end, add the remainder */ + eob = &SMTP_EOB[2]; + break; + case 3: + /* ended with '\r\n.', we should escape the last '.' */ + eob = "." SMTP_EOB; + break; + default: + break; + } + result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n); + if(result) + return result; + ctx->processed_eos = TRUE; + } + + if(!Curl_bufq_is_empty(&ctx->buf)) { + result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); + } + else + *pnread = 0; + + if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* no more data, read all, done. */ + CURL_TRC_SMTP(data, "mail body complete, returning EOS"); + ctx->eos = TRUE; + } + *peos = (bool)ctx->eos; + DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d", + blen, result, *pnread, *peos)); + return result; +} + +static curl_off_t cr_eob_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + /* this reader changes length depending on input */ + (void)data; + (void)reader; + return -1; +} + +static const struct Curl_crtype cr_eob = { + "cr-smtp-eob", + cr_eob_init, + cr_eob_read, + cr_eob_close, + Curl_creader_def_needs_rewind, + cr_eob_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_cntrl, + Curl_creader_def_is_paused, + Curl_creader_def_done, + sizeof(struct cr_eob_ctx) }; +static CURLcode cr_eob_add(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + /*********************************************************************** * * smtp_endofresp() @@ -258,7 +485,7 @@ const char *line, size_t len, int *resp) { struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); - bool result = FALSE; + bool end = FALSE; (void)data; DEBUGASSERT(smtpc); @@ -277,7 +504,7 @@ char tmpline[6]; curl_off_t code; const char *p = tmpline; - result = TRUE; + end = TRUE; memcpy(tmpline, line, (len == 5 ? 5 : 3)); tmpline[len == 5 ? 5 : 3] = 0; if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999)) @@ -291,11 +518,11 @@ /* Do we have a multiline (continuation) response? */ else if(line[3] == '-' && (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { - result = TRUE; + end = TRUE; *resp = 1; /* Internal response code */ } - return result; + return end; } /*********************************************************************** @@ -319,13 +546,12 @@ if(len > 4) { /* Find the start of the message */ len -= 4; - for(message += 4; *message == ' ' || *message == '\t'; message++, len--) + for(message += 4; ISBLANK(*message); message++, len--) ; /* Find the end of the message */ while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') + if(!ISNEWLINE(message[len]) && !ISBLANK(message[len])) break; /* Terminate the message */ @@ -349,7 +575,7 @@ struct smtp_conn *smtpc, smtpstate newstate) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE /* for debug purposes */ static const char * const names[] = { "STOP", @@ -467,13 +693,13 @@ if(result) goto out; /* Change the connection handler and SMTP state */ - conn->handler = &Curl_handler_smtps; + conn->scheme = &Curl_scheme_smtps; } DEBUGASSERT(!smtpc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + result, ssldone)); if(!result && ssldone) { smtpc->ssldone = ssldone; /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */ @@ -608,7 +834,7 @@ whether the hostname is encoded using IDN ACE */ bool utf8 = FALSE; - if((!smtp->custom) || (!smtp->custom[0])) { + if(!smtp->custom || !smtp->custom[0]) { char *address = NULL; struct hostname host = { NULL, NULL, NULL, NULL }; const char *suffix = ""; @@ -622,9 +848,10 @@ /* Establish whether we should report SMTPUTF8 to the server for this mailbox as per RFC-6531 sect. 3.1 point 6 */ - utf8 = (smtpc->utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name))); + utf8 = smtpc->utf8_supported && + (host.encalloc || + !Curl_is_ASCII_name(address) || + !Curl_is_ASCII_name(host.name)); /* Send the VRFY command (Note: The hostname part may be absent when the host is a local system) */ @@ -697,9 +924,10 @@ /* Establish whether we should report SMTPUTF8 to the server for this mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ - utf8 = (smtpc->utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name))); + utf8 = smtpc->utf8_supported && + (host.encalloc || + !Curl_is_ASCII_name(address) || + !Curl_is_ASCII_name(host.name)); if(host.name) { from = curl_maprintf("<%s@%s>%s", address, host.name, suffix); @@ -707,8 +935,8 @@ Curl_free_idnconverted_hostname(&host); } else - /* An invalid mailbox was provided but we will simply let the server - worry about that and reply with a 501 error */ + /* An invalid mailbox was provided but we let the server worry + about that and reply with a 501 error */ from = curl_maprintf("<%s>%s", address, suffix); curlx_free(address); @@ -738,9 +966,10 @@ /* Establish whether we should report SMTPUTF8 to the server for this mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ - if((!utf8) && (smtpc->utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name)))) + if(!utf8 && smtpc->utf8_supported && + (host.encalloc || + !Curl_is_ASCII_name(address) || + !Curl_is_ASCII_name(host.name))) utf8 = TRUE; if(host.name) { @@ -749,8 +978,8 @@ Curl_free_idnconverted_hostname(&host); } else - /* An invalid mailbox was provided but we will simply let the server - worry about it */ + /* An invalid mailbox was provided but we let the server worry + about it */ auth = curl_maprintf("<%s>%s", address, suffix); curlx_free(address); } @@ -766,22 +995,24 @@ #ifndef CURL_DISABLE_MIME /* Prepare the mime data if some. */ - if(data->set.mimepost.kind != MIMEKIND_NONE) { + if(IS_MIME_POST(data)) { + curl_mimepart *postp = data->set.mimepostp; + /* Use the whole structure as data. */ - data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; + postp->flags &= ~(unsigned int)MIME_BODY_ONLY; /* Add external headers and mime version. */ - curl_mime_headers(&data->set.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, + curl_mime_headers(postp, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, postp, NULL, NULL, MIMESTRATEGY_MAIL); if(!result) if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) - result = Curl_mime_add_header(&data->set.mimepost.curlheaders, + result = Curl_mime_add_header(&postp->curlheaders, "Mime-Version: 1.0"); if(!result) - result = Curl_creader_set_mime(data, &data->set.mimepost); + result = Curl_creader_set_mime(data, postp); if(result) goto out; data->state.infilesize = Curl_creader_total_length(data); @@ -830,9 +1061,9 @@ "MAIL FROM:%s%s%s%s%s%s", from, /* Mandatory */ auth ? " AUTH=" : "", /* Optional on AUTH support */ - auth ? auth : "", /* */ + auth ? auth : "", size ? " SIZE=" : "", /* Optional on SIZE support */ - size ? size : "", /* */ + size ? size : "", utf8 ? " SMTPUTF8" /* Internationalised mailbox */ : ""); /* included in our envelope */ @@ -875,8 +1106,8 @@ result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s@%s>%s", address, host.name, suffix); else - /* An invalid mailbox was provided but we will simply let the server worry - about that and reply with a 501 error */ + /* An invalid mailbox was provided but we let the server worry about + that and reply with a 501 error */ result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s>%s", address, suffix); @@ -1004,10 +1235,7 @@ size_t wordlen; unsigned short mechbit; - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - + while(len && (ISBLANK(*line) || ISNEWLINE(*line))) { line++; len--; } @@ -1016,9 +1244,8 @@ break; /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) + for(wordlen = 0; wordlen < len && !ISBLANK(line[wordlen]) && + !ISNEWLINE(line[wordlen]);) wordlen++; /* Test the word for a matching authentication mechanism */ @@ -1113,7 +1340,7 @@ smtpstate instate) { CURLcode result = CURLE_OK; - char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf); + const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf); size_t len = smtpc->pp.nfinal; (void)instate; @@ -1411,6 +1638,20 @@ return smtpc ? Curl_pp_pollset(data, &smtpc->pp, ps) : CURLE_OK; } +/* SASL parameters for the smtp protocol */ +static const struct SASLproto saslsmtp = { + "smtp", /* The service name */ + smtp_perform_auth, /* Send authentication command */ + smtp_continue_auth, /* Send authentication continuation */ + smtp_cancel_auth, /* Cancel authentication */ + smtp_get_message, /* Get SASL response message */ + 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + 334, /* Code received when continuation is expected */ + 235, /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + /*********************************************************************** * * smtp_connect() @@ -1483,7 +1724,7 @@ return CURLE_OK; /* Cleanup our per-request based variables */ - Curl_safefree(smtp->custom); + curlx_safefree(smtp->custom); if(status) { connclose(conn, "SMTP done with bad status"); /* marked for closure */ @@ -1566,6 +1807,55 @@ return result; } +/* Call this when the DO phase has completed */ +static CURLcode smtp_dophase_done(struct Curl_easy *data, + struct SMTP *smtp, + bool connected) +{ + (void)connected; + + if(smtp->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + return CURLE_OK; +} + +/*********************************************************************** + * + * smtp_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + */ +static CURLcode smtp_regular_transfer(struct Curl_easy *data, + struct smtp_conn *smtpc, + struct SMTP *smtp, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + + /* Make sure size is unknown at this point */ + data->req.size = -1; + + /* Set the progress data */ + Curl_pgrsReset(data); + + /* Carry out the perform */ + result = smtp_perform(data, smtpc, smtp, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = smtp_dophase_done(data, smtp, connected); + + CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", + result, *dophase_done); + return result; +} + /*********************************************************************** * * smtp_do() @@ -1611,7 +1901,6 @@ { struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); - (void)data; if(!smtpc) return CURLE_FAILED_INIT; @@ -1629,20 +1918,6 @@ return CURLE_OK; } -/* Call this when the DO phase has completed */ -static CURLcode smtp_dophase_done(struct Curl_easy *data, - struct SMTP *smtp, - bool connected) -{ - (void)connected; - - if(smtp->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_xfer_setup_nop(data); - - return CURLE_OK; -} - /* Called from multi.c while DOing */ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) { @@ -1664,41 +1939,6 @@ return result; } -/*********************************************************************** - * - * smtp_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - */ -static CURLcode smtp_regular_transfer(struct Curl_easy *data, - struct smtp_conn *smtpc, - struct SMTP *smtp, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - - /* Make sure size is unknown at this point */ - data->req.size = -1; - - /* Set the progress data */ - Curl_pgrsReset(data); - - /* Carry out the perform */ - result = smtp_perform(data, smtpc, smtp, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = smtp_dophase_done(data, smtp, connected); - - CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", - result, *dophase_done); - return result; -} - static void smtp_easy_dtor(void *key, size_t klen, void *entry) { struct SMTP *smtp = entry; @@ -1713,7 +1953,7 @@ (void)key; (void)klen; Curl_pp_disconnect(&smtpc->pp); - Curl_safefree(smtpc->domain); + curlx_safefree(smtpc->domain); curlx_free(smtpc); } @@ -1741,342 +1981,27 @@ return result; } -/*********************************************************************** - * - * smtp_parse_url_options() - * - * Parse the URL login options. +/* + * SMTP protocol handler. */ -static CURLcode smtp_parse_url_options(struct connectdata *conn, - struct smtp_conn *smtpc) -{ - CURLcode result = CURLE_OK; - const char *ptr = conn->options; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(curl_strnequal(key, "AUTH=", 5)) - result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, - value, ptr - value); - else - result = CURLE_URL_MALFORMAT; - - if(*ptr == ';') - ptr++; - } - - return result; -} - -/*********************************************************************** - * - * smtp_parse_url_path() - * - * Parse the URL path into separate path components. - */ -static CURLcode smtp_parse_url_path(struct Curl_easy *data, - struct smtp_conn *smtpc) -{ - /* The SMTP struct is already initialised in smtp_connect() */ - const char *path = &data->state.up.path[1]; /* skip leading path */ - char localhost[HOSTNAME_MAX + 1]; - - /* Calculate the path if necessary */ - if(!*path) { - if(!Curl_gethostname(localhost, sizeof(localhost))) - path = localhost; - else - path = "localhost"; - } - - /* URL decode the path and use it as the domain in our EHLO */ - return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL); -} - -/*********************************************************************** - * - * smtp_parse_custom_request() - * - * Parse the custom request. - */ -static CURLcode smtp_parse_custom_request(struct Curl_easy *data, - struct SMTP *smtp) -{ - CURLcode result = CURLE_OK; - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - - /* URL decode the custom request */ - if(custom) - result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL); - - return result; -} - -/*********************************************************************** - * - * smtp_parse_address() - * - * Parse the fully qualified mailbox address into a local address part and the - * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if - * necessary. - * - * Parameters: - * - * conn [in] - The connection handle. - * fqma [in] - The fully qualified mailbox address (which may or - * may not contain UTF-8 characters). - * address [in/out] - A new allocated buffer which holds the local - * address part of the mailbox. This buffer must be - * free'ed by the caller. - * host [in/out] - The hostname structure that holds the original, - * and optionally encoded, hostname. - * Curl_free_idnconverted_hostname() must be called - * once the caller has finished with the structure. - * - * Returns CURLE_OK on success. - * - * Notes: - * - * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor - * that conversion then we shall return success. This allow the caller to send - * the data to the server as a U-label (as per RFC-6531 sect. 3.2). - * - * If an mailbox '@' separator cannot be located then the mailbox is considered - * to be either a local mailbox or an invalid mailbox (depending on what the - * calling function deems it to be) then the input will simply be returned in - * the address part with the hostname being NULL. - */ -static CURLcode smtp_parse_address(const char *fqma, char **address, - struct hostname *host, const char **suffix) -{ - CURLcode result = CURLE_OK; - size_t length; - char *addressend; - - /* Duplicate the fully qualified email address so we can manipulate it, - ensuring it does not contain the delimiters if specified */ - char *dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma); - if(!dup) - return CURLE_OUT_OF_MEMORY; - - if(fqma[0] != '<') { - length = strlen(dup); - if(length) { - if(dup[length - 1] == '>') - dup[length - 1] = '\0'; - } - } - else { - addressend = strrchr(dup, '>'); - if(addressend) { - *addressend = '\0'; - *suffix = addressend + 1; - } - } - - /* Extract the hostname from the address (if we can) */ - host->name = strpbrk(dup, "@"); - if(host->name) { - *host->name = '\0'; - host->name = host->name + 1; - - /* Attempt to convert the hostname to IDN ACE */ - (void)Curl_idnconvert_hostname(host); - - /* If Curl_idnconvert_hostname() fails then we shall attempt to continue - and send the hostname using UTF-8 rather than as 7-bit ACE (which is - our preference) */ - } - - /* Extract the local address from the mailbox */ - *address = dup; - - return result; -} - -struct cr_eob_ctx { - struct Curl_creader super; - struct bufq buf; - size_t n_eob; /* how many EOB bytes we matched so far */ - size_t eob; /* Number of bytes of the EOB (End Of Body) that - have been received so far */ - BIT(read_eos); /* we read an EOS from the next reader */ - BIT(processed_eos); /* we read and processed an EOS */ - BIT(eos); /* we have returned an EOS */ +const struct Curl_protocol Curl_protocol_smtp = { + smtp_setup_connection, /* setup_connection */ + smtp_do, /* do_it */ + smtp_done, /* done */ + ZERO_NULL, /* do_more */ + smtp_connect, /* connect_it */ + smtp_multi_statemach, /* connecting */ + smtp_doing, /* doing */ + smtp_pollset, /* proto_pollset */ + smtp_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + smtp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ }; -static CURLcode cr_eob_init(struct Curl_easy *data, - struct Curl_creader *reader) -{ - struct cr_eob_ctx *ctx = reader->ctx; - (void)data; - /* The first char we read is the first on a line, as if we had - * read CRLF just before */ - ctx->n_eob = 2; - Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT); - return CURLE_OK; -} - -static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader) -{ - struct cr_eob_ctx *ctx = reader->ctx; - (void)data; - Curl_bufq_free(&ctx->buf); -} - -/* this is the 5-bytes End-Of-Body marker for SMTP */ -#define SMTP_EOB "\r\n.\r\n" -#define SMTP_EOB_FIND_LEN 3 - -/* client reader doing SMTP End-Of-Body escaping. */ -static CURLcode cr_eob_read(struct Curl_easy *data, - struct Curl_creader *reader, - char *buf, size_t blen, - size_t *pnread, bool *peos) -{ - struct cr_eob_ctx *ctx = reader->ctx; - CURLcode result = CURLE_OK; - size_t nread, i, start, n; - bool eos; - - if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { - /* Get more and convert it when needed */ - result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); - CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d", - blen, result, nread, eos); - if(result) - return result; - - ctx->read_eos = eos; - if(nread) { - if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) { - /* not in the middle of a match, no EOB start found, just pass */ - *pnread = nread; - *peos = FALSE; - return CURLE_OK; - } - /* scan for EOB (continuation) and convert */ - for(i = start = 0; i < nread; ++i) { - if(ctx->n_eob >= SMTP_EOB_FIND_LEN) { - /* matched the EOB prefix and seeing additional char, add '.' */ - result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); - if(result) - return result; - result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n); - if(result) - return result; - ctx->n_eob = 0; - start = i; - if(data->state.infilesize > 0) - data->state.infilesize++; - } - - if(buf[i] != SMTP_EOB[ctx->n_eob]) - ctx->n_eob = 0; - - if(buf[i] == SMTP_EOB[ctx->n_eob]) { - /* matching another char of the EOB */ - ++ctx->n_eob; - } - } - - /* add any remainder to buf */ - if(start < nread) { - result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n); - if(result) - return result; - } - } - } - - *peos = FALSE; - - if(ctx->read_eos && !ctx->processed_eos) { - /* if we last matched a CRLF or if the data was empty, add ".\r\n" - * to end the body. If we sent something and it did not end with "\r\n", - * add "\r\n.\r\n" to end the body */ - const char *eob = SMTP_EOB; - CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'"); - switch(ctx->n_eob) { - case 2: - /* seen a CRLF at the end, just add the remainder */ - eob = &SMTP_EOB[2]; - break; - case 3: - /* ended with '\r\n.', we should escape the last '.' */ - eob = "." SMTP_EOB; - break; - default: - break; - } - result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n); - if(result) - return result; - ctx->processed_eos = TRUE; - } - - if(!Curl_bufq_is_empty(&ctx->buf)) { - result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); - } - else - *pnread = 0; - - if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { - /* no more data, read all, done. */ - CURL_TRC_SMTP(data, "mail body complete, returning EOS"); - ctx->eos = TRUE; - } - *peos = ctx->eos; - DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d", - blen, result, *pnread, *peos)); - return result; -} - -static curl_off_t cr_eob_total_length(struct Curl_easy *data, - struct Curl_creader *reader) -{ - /* this reader changes length depending on input */ - (void)data; - (void)reader; - return -1; -} - -static const struct Curl_crtype cr_eob = { - "cr-smtp-eob", - cr_eob_init, - cr_eob_read, - cr_eob_close, - Curl_creader_def_needs_rewind, - cr_eob_total_length, - Curl_creader_def_resume_from, - Curl_creader_def_cntrl, - Curl_creader_def_is_paused, - Curl_creader_def_done, - sizeof(struct cr_eob_ctx) -}; - -static CURLcode cr_eob_add(struct Curl_easy *data) -{ - struct Curl_creader *reader = NULL; - CURLcode result; - - result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE); - if(!result) - result = Curl_creader_add(data, reader); - - if(result && reader) - Curl_creader_free(data, reader); - return result; -} - #endif /* CURL_DISABLE_SMTP */
diff --git a/Utilities/cmcurl/lib/smtp.h b/Utilities/cmcurl/lib/smtp.h index 7e3cf77..75b8197 100644 --- a/Utilities/cmcurl/lib/smtp.h +++ b/Utilities/cmcurl/lib/smtp.h
@@ -23,7 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_handler Curl_handler_smtp; -extern const struct Curl_handler Curl_handler_smtps; +#ifndef CURL_DISABLE_SMTP +extern const struct Curl_protocol Curl_protocol_smtp; +#endif #endif /* HEADER_CURL_SMTP_H */
diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c index bdbe956..76b959d 100644 --- a/Utilities/cmcurl/lib/socketpair.c +++ b/Utilities/cmcurl/lib/socketpair.c
@@ -28,11 +28,14 @@ #include "rand.h" #include "curlx/nonblock.h" +#ifndef CURL_DISABLE_SOCKETPAIR + +/* choose implementation */ #ifdef USE_EVENTFD #include <sys/eventfd.h> -int Curl_eventfd(curl_socket_t socks[2], bool nonblocking) +static int wakeup_eventfd(curl_socket_t socks[2], bool nonblocking) { int efd = eventfd(0, nonblocking ? EFD_CLOEXEC | EFD_NONBLOCK : EFD_CLOEXEC); if(efd == -1) { @@ -49,7 +52,7 @@ #include <fcntl.h> #endif -int Curl_pipe(curl_socket_t socks[2], bool nonblocking) +static int wakeup_pipe(curl_socket_t socks[2], bool nonblocking) { #ifdef HAVE_PIPE2 int flags = nonblocking ? O_NONBLOCK | O_CLOEXEC : O_CLOEXEC; @@ -61,8 +64,8 @@ #ifdef HAVE_FCNTL if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || fcntl(socks[1], F_SETFD, FD_CLOEXEC)) { - close(socks[0]); - close(socks[1]); + sclose(socks[0]); + sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } @@ -70,8 +73,8 @@ if(nonblocking) { if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) { - close(socks[0]); - close(socks[1]); + sclose(socks[0]); + sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } @@ -81,33 +84,49 @@ return 0; } -#endif /* USE_EVENTFD */ +#elif defined(HAVE_SOCKETPAIR) /* !USE_EVENTFD && !HAVE_PIPE */ -#ifndef CURL_DISABLE_SOCKETPAIR -#ifdef HAVE_SOCKETPAIR -#ifdef USE_SOCKETPAIR -int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2], bool nonblocking) -{ -#ifdef SOCK_NONBLOCK - type = nonblocking ? type | SOCK_NONBLOCK : type; +#ifndef USE_UNIX_SOCKETS +#error "unsupported Unix domain and socketpair build combo" #endif - if(CURL_SOCKETPAIR(domain, type, protocol, socks)) + +static int wakeup_socketpair(curl_socket_t socks[2], bool nonblocking) +{ + int type = SOCK_STREAM; +#ifdef SOCK_CLOEXEC + type |= SOCK_CLOEXEC; +#endif +#ifdef SOCK_NONBLOCK + if(nonblocking) + type |= SOCK_NONBLOCK; +#endif + + if(CURL_SOCKETPAIR(AF_UNIX, type, 0, socks)) return -1; #ifndef SOCK_NONBLOCK if(nonblocking) { if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) { - close(socks[0]); - close(socks[1]); + sclose(socks[0]); + sclose(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } } #endif +#ifdef USE_SO_NOSIGPIPE + if(Curl_sock_nosigpipe(socks[1]) < 0) { + sclose(socks[0]); + sclose(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; + return -1; + } +#endif /* USE_SO_NOSIGPIPE */ + return 0; } -#endif /* USE_SOCKETPAIR */ -#else /* !HAVE_SOCKETPAIR */ + +#else /* !USE_EVENTFD && !HAVE_PIPE && !HAVE_SOCKETPAIR */ #ifdef HAVE_NETDB_H #include <netdb.h> @@ -125,8 +144,7 @@ #include "select.h" /* for Curl_poll */ -int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2], bool nonblocking) +static int wakeup_inet(curl_socket_t socks[2], bool nonblocking) { union { struct sockaddr_in inaddr; @@ -136,9 +154,6 @@ curl_socklen_t addrlen = sizeof(a.inaddr); int reuse = 1; struct pollfd pfd[1]; - (void)domain; - (void)type; - (void)protocol; listener = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(listener == CURL_SOCKET_BAD) @@ -248,6 +263,10 @@ if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) goto error; +#ifdef USE_SO_NOSIGPIPE + if(Curl_sock_nosigpipe(socks[1]) < 0) + goto error; +#endif sclose(listener); return 0; @@ -255,7 +274,103 @@ sclose(listener); sclose(socks[0]); sclose(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } + +#endif /* choose implementation */ + +int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking) +{ +#ifdef USE_EVENTFD + return wakeup_eventfd(socks, nonblocking); +#elif defined(HAVE_PIPE) + return wakeup_pipe(socks, nonblocking); +#elif defined(HAVE_SOCKETPAIR) + return wakeup_socketpair(socks, nonblocking); +#else + return wakeup_inet(socks, nonblocking); #endif +} + +#if defined(USE_EVENTFD) || defined(HAVE_PIPE) + +#define wakeup_write write +#define wakeup_read read +#define wakeup_close close + +#else /* !USE_EVENTFD && !HAVE_PIPE */ + +#define wakeup_write swrite +#define wakeup_read sread +#define wakeup_close sclose + +#endif + +int Curl_wakeup_signal(curl_socket_t socks[2]) +{ + int err = 0; +#ifdef USE_EVENTFD + const uint64_t buf[1] = { 1 }; +#else + const char buf[1] = { 1 }; +#endif + + while(1) { + err = 0; + if(wakeup_write(socks[1], buf, sizeof(buf)) < 0) { + err = SOCKERRNO; +#ifdef USE_WINSOCK + if(err == SOCKEWOULDBLOCK) + err = 0; /* wakeup is already ongoing */ +#else + if(SOCKEINTR == err) + continue; + if((err == SOCKEWOULDBLOCK) || (err == EAGAIN)) + err = 0; /* wakeup is already ongoing */ +#endif + } + break; + } + return err; +} + +CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) +{ + char buf[64]; + ssize_t rc; + CURLcode result = CURLE_OK; + + do { + rc = wakeup_read(socks[0], buf, sizeof(buf)); + if(!rc) + break; + else if(rc < 0) { +#ifdef USE_WINSOCK + if(SOCKERRNO == SOCKEWOULDBLOCK) + break; +#else + if(SOCKEINTR == SOCKERRNO) + continue; + if((SOCKERRNO == SOCKEWOULDBLOCK) || (SOCKERRNO == EAGAIN)) + break; +#endif + result = CURLE_READ_ERROR; + break; + } + } while(all); + return result; +} + +void Curl_wakeup_destroy(curl_socket_t socks[2]) +{ +#ifndef USE_EVENTFD + if(socks[1] != CURL_SOCKET_BAD) + wakeup_close(socks[1]); +#endif + if(socks[0] != CURL_SOCKET_BAD) + wakeup_close(socks[0]); + socks[0] = socks[1] = CURL_SOCKET_BAD; +} + #endif /* !CURL_DISABLE_SOCKETPAIR */
diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h index 36685e7..0427e72 100644 --- a/Utilities/cmcurl/lib/socketpair.h +++ b/Utilities/cmcurl/lib/socketpair.h
@@ -25,53 +25,19 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef USE_EVENTFD - -#define wakeup_write write -#define wakeup_read read -#define wakeup_close close -#define wakeup_create(p, nb) Curl_eventfd(p, nb) - -int Curl_eventfd(curl_socket_t socks[2], bool nonblocking); - -#elif defined(HAVE_PIPE) - -#define wakeup_write write -#define wakeup_read read -#define wakeup_close close -#define wakeup_create(p, nb) Curl_pipe(p, nb) - -int Curl_pipe(curl_socket_t socks[2], bool nonblocking); - -#else /* !USE_EVENTFD && !HAVE_PIPE */ - -#define wakeup_write swrite -#define wakeup_read sread -#define wakeup_close sclose - -#if defined(USE_UNIX_SOCKETS) && defined(HAVE_SOCKETPAIR) -#define SOCKETPAIR_FAMILY AF_UNIX -#elif !defined(HAVE_SOCKETPAIR) -#define SOCKETPAIR_FAMILY 0 /* not used */ -#else -#error "unsupported Unix domain and socketpair build combo" -#endif - -#ifdef SOCK_CLOEXEC -#define SOCKETPAIR_TYPE (SOCK_STREAM | SOCK_CLOEXEC) -#else -#define SOCKETPAIR_TYPE SOCK_STREAM -#endif - -#define USE_SOCKETPAIR -#define wakeup_create(p, nb) \ - Curl_socketpair(SOCKETPAIR_FAMILY, SOCKETPAIR_TYPE, 0, p, nb) - -#endif /* USE_EVENTFD */ - #ifndef CURL_DISABLE_SOCKETPAIR -int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2], bool nonblocking); + +/* return < 0 for failure to initialise */ +int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking); +void Curl_wakeup_destroy(curl_socket_t socks[2]); + +/* return 0 on success or errno on failure */ +int Curl_wakeup_signal(curl_socket_t socks[2]); + +CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all); + +#else +#define Curl_wakeup_destroy(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_SOCKETPAIR_H */
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index e49e8c8..b3da5be 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c
@@ -34,17 +34,14 @@ #include "urldata.h" #include "bufq.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "select.h" #include "cfilters.h" +#include "cf-dns.h" #include "connect.h" #include "socks.h" #include "curlx/inet_pton.h" -#include "url.h" - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) -#define DEBUG_AND_VERBOSE -#endif /* for the (SOCKS) connect state machine */ enum socks_state_t { @@ -71,7 +68,7 @@ SOCKS_ST_FAILED }; -#ifdef DEBUG_AND_VERBOSE +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) static const char * const cf_socks_statename[] = { "SOCKS_INIT", "SOCKS4_START", @@ -98,18 +95,21 @@ #define SOCKS_CHUNKS 1 -struct socks_state { +struct socks_ctx { enum socks_state_t state; struct bufq iobuf; - const char *hostname; - int remote_port; - const char *proxy_user; - const char *proxy_password; + uint16_t remote_port; + const char *user; + const char *passwd; CURLproxycode presult; + uint32_t resolv_id; + uint8_t ip_version; + uint8_t proxy_type; unsigned char version; BIT(resolve_local); BIT(start_resolving); BIT(socks4a); + char hostname[1]; }; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -130,17 +130,19 @@ *pnread = 0; for(;;) { - timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE); + timediff_t timeout_ms = Curl_timeleft_ms(data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + if(timeout_ms < 0) { /* we already got the timeout */ return CURLE_OPERATION_TIMEDOUT; } if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) + if(SOCKET_READABLE(sock, timeout_ms) <= 0) return CURLE_OPERATION_TIMEDOUT; result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) continue; else if(result) return result; @@ -159,40 +161,41 @@ } #endif -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) -#define DEBUG_AND_VERBOSE +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) #define sxstate(x, c, d, y) socksstate(x, c, d, y, __LINE__) #else #define sxstate(x, c, d, y) socksstate(x, c, d, y) #endif /* always use this function to change state, to make debugging easier */ -static void socksstate(struct socks_state *sx, +static void socksstate(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, enum socks_state_t state -#ifdef DEBUG_AND_VERBOSE +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) , int lineno #endif ) { enum socks_state_t oldstate = sx->state; - (void)cf; - (void)data; + if(oldstate == state) /* do not bother when the new state is the same as the old state */ return; sx->state = state; -#ifdef DEBUG_AND_VERBOSE +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) CURL_TRC_CF(data, cf, "[%s] -> [%s] (line %d)", cf_socks_statename[oldstate], cf_socks_statename[sx->state], lineno); +#else + (void)cf; + (void)data; #endif } -static CURLproxycode socks_failed(struct socks_state *sx, +static CURLproxycode socks_failed(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, CURLproxycode presult) @@ -202,7 +205,7 @@ return presult; } -static CURLproxycode socks_flush(struct socks_state *sx, +static CURLproxycode socks_flush(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -226,7 +229,7 @@ return CURLPX_OK; } -static CURLproxycode socks_recv(struct socks_state *sx, +static CURLproxycode socks_recv(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, size_t min_bytes, @@ -260,7 +263,7 @@ return CURLPX_OK; } -static CURLproxycode socks4_req_add_hd(struct socks_state *sx, +static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx, struct Curl_easy *data) { unsigned char buf[4]; @@ -270,8 +273,8 @@ (void)data; buf[0] = 4; /* version (SOCKS4) */ buf[1] = 1; /* connect */ - buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffu); /* MSB */ - buf[3] = (unsigned char)(sx->remote_port & 0xffu); /* LSB */ + buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffU); /* MSB */ + buf[3] = (unsigned char)(sx->remote_port & 0xffU); /* LSB */ result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten); if(result || (nwritten != 4)) @@ -279,14 +282,14 @@ return CURLPX_OK; } -static CURLproxycode socks4_req_add_user(struct socks_state *sx, +static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, struct Curl_easy *data) { CURLcode result; size_t nwritten; - if(sx->proxy_user) { - size_t plen = strlen(sx->proxy_user); + if(sx->user) { + size_t plen = strlen(sx->user); if(plen > 255) { /* there is no real size limit to this field in the protocol, but SOCKS5 limits the proxy user field to 255 bytes and it seems likely @@ -295,7 +298,7 @@ return CURLPX_LONG_USER; } /* add proxy name WITH trailing zero */ - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, plen + 1, + result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1, &nwritten); if(result || (nwritten != (plen + 1))) return CURLPX_SEND_REQUEST; @@ -310,70 +313,58 @@ return CURLPX_OK; } -static CURLproxycode socks4_resolving(struct socks_state *sx, +static CURLproxycode socks4_resolving(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *hp = NULL; + const struct Curl_addrinfo *ai = NULL; CURLcode result; size_t nwritten; + bool dns_done; *done = FALSE; if(sx->start_resolving) { /* need to resolve hostname to add destination address */ sx->start_resolving = FALSE; - DEBUGASSERT(sx->hostname && *sx->hostname); - - result = Curl_resolv(data, sx->hostname, sx->remote_port, - cf->conn->ip_version, TRUE, &dns); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "SOCKS4 non-blocking resolve of %s", sx->hostname); - return CURLPX_OK; + result = Curl_cf_dns_insert_after( + cf, data, Curl_resolv_dns_queries(data, sx->ip_version), + sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + if(result) { + failf(data, "unable to create DNS filter for socks"); + return CURLPX_UNKNOWN_FAIL; } - else if(result) - return CURLPX_RESOLVE_HOST; - } - else { - /* check if we have the name resolved by now */ - result = Curl_resolv_check(data, &dns); - if(!result && !dns) - return CURLPX_OK; } - if(result || !dns) { - failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", sx->hostname); + /* resolve the hostname by connecting the DNS filter */ + result = Curl_conn_cf_connect(cf->next, data, &dns_done); + if(result) { + failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", + sx->hostname); return CURLPX_RESOLVE_HOST; } + else if(!dns_done) + return CURLPX_OK; - /* - * We cannot use 'hostent' as a struct that Curl_resolv() returns. It - * returns a Curl_addrinfo pointer that may not always look the same. - */ - /* scan for the first IPv4 address */ - hp = dns->addr; - while(hp && (hp->ai_family != AF_INET)) - hp = hp->ai_next; - - if(hp) { + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + if(ai) { struct sockaddr_in *saddr_in; char ipbuf[64]; - Curl_printable_address(hp, ipbuf, sizeof(ipbuf)); + Curl_printable_address(ai, ipbuf, sizeof(ipbuf)); CURL_TRC_CF(data, cf, "SOCKS4 connect to IPv4 %s (locally resolved)", ipbuf); - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; + saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; result = Curl_bufq_write(&sx->iobuf, (unsigned char *)&saddr_in->sin_addr.s_addr, 4, &nwritten); - Curl_resolv_unlink(data, &dns); /* not used anymore from now on */ if(result || (nwritten != 4)) return CURLPX_SEND_REQUEST; } else { + /* No ipv4 address resolved */ failf(data, "SOCKS4 connection to %s not supported", sx->hostname); return CURLPX_RESOLVE_HOST; } @@ -382,7 +373,7 @@ return CURLPX_OK; } -static CURLproxycode socks4_check_resp(struct socks_state *sx, +static CURLproxycode socks4_check_resp(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -431,7 +422,7 @@ "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)" ", request rejected or failed.", resp[4], resp[5], resp[6], resp[7], - ((resp[2] << 8) | resp[3]), resp[1]); + (unsigned int)((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_REQUEST_FAILED; case 92: failf(data, @@ -439,7 +430,7 @@ ", request rejected because SOCKS server cannot connect to " "identd on the client.", resp[4], resp[5], resp[6], resp[7], - ((resp[2] << 8) | resp[3]), resp[1]); + (unsigned int)((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_IDENTD; case 93: failf(data, @@ -447,14 +438,14 @@ ", request rejected because the client program and identd " "report different user-ids.", resp[4], resp[5], resp[6], resp[7], - ((resp[2] << 8) | resp[3]), resp[1]); + (unsigned int)((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_IDENTD_DIFFER; default: failf(data, "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)" ", Unknown.", resp[4], resp[5], resp[6], resp[7], - ((resp[2] << 8) | resp[3]), resp[1]); + (unsigned int)((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_UNKNOWN_FAIL; } } @@ -471,7 +462,7 @@ * Nonsupport "Identification Protocol (RFC1413)" */ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { size_t nwritten; @@ -489,16 +480,14 @@ case SOCKS4_ST_START: Curl_bufq_reset(&sx->iobuf); sx->start_resolving = FALSE; - sx->socks4a = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A); + sx->socks4a = (sx->proxy_type == CURLPROXY_SOCKS4A); sx->resolve_local = !sx->socks4a; sx->presult = CURLPX_OK; /* SOCKS4 can only do IPv4, insist! */ - cf->conn->ip_version = CURL_IPRESOLVE_V4; - CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%d", - sx->socks4a ? "a" : "", - cf->conn->bits.httpproxy ? " HTTP proxy" : "", - sx->hostname, sx->remote_port); + sx->ip_version = CURL_IPRESOLVE_V4; + CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u", + sx->socks4a ? "a" : "", sx->hostname, sx->remote_port); /* * Compose socks4 request @@ -591,7 +580,7 @@ } static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { const unsigned char auth = data->set.socks5auth; @@ -613,7 +602,7 @@ "CURLOPT_SOCKS5_AUTH: %u", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - sx->proxy_user = NULL; + sx->user = NULL; req[0] = 5; /* version */ nauths = 1; @@ -624,7 +613,7 @@ req[1 + nauths] = 1; /* GSS-API */ } #endif - if(sx->proxy_user) { + if(sx->user) { ++nauths; req[1 + nauths] = 2; /* username/password */ } @@ -637,7 +626,7 @@ return CURLPX_OK; } -static CURLproxycode socks5_check_resp0(struct socks_state *sx, +static CURLproxycode socks5_check_resp0(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -689,7 +678,7 @@ } static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { /* Needs username and password */ @@ -697,9 +686,9 @@ unsigned char buf[2]; CURLcode result; - if(sx->proxy_user && sx->proxy_password) { - ulen = strlen(sx->proxy_user); - plen = strlen(sx->proxy_password); + if(sx->user && sx->passwd) { + ulen = strlen(sx->user); + plen = strlen(sx->passwd); /* the lengths must fit in a single byte */ if(ulen > 255) { failf(data, "Excessive username length for proxy auth"); @@ -724,7 +713,7 @@ if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; if(ulen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, ulen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten); if(result || (nwritten != ulen)) return CURLPX_SEND_REQUEST; } @@ -733,7 +722,7 @@ if(result || (nwritten != 1)) return CURLPX_SEND_REQUEST; if(plen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_password, plen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten); if(result || (nwritten != plen)) return CURLPX_SEND_REQUEST; } @@ -741,7 +730,7 @@ return CURLPX_OK; } -static CURLproxycode socks5_check_auth_resp(struct socks_state *sx, +static CURLproxycode socks5_check_auth_resp(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -766,7 +755,7 @@ return CURLPX_OK; } -static CURLproxycode socks5_req1_init(struct socks_state *sx, +static CURLproxycode socks5_req1_init(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -790,7 +779,7 @@ /* remote resolving, send what type+addr/string to resolve */ #ifdef USE_IPV6 - if(cf->conn->bits.ipv6_ip) { + if(strchr(sx->hostname, ':')) { desttype = 4; destination = ipbuf; destlen = 16; @@ -806,6 +795,10 @@ } else { const size_t hostname_len = strlen(sx->hostname); + /* socks5_req0_init() already rejects hostnames longer than 255 bytes, so + this cast to unsigned char is safe. Assert to guard against future + refactoring that might remove or reorder that earlier check. */ + DEBUGASSERT(hostname_len <= 255); desttype = 3; destination = (const unsigned char *)sx->hostname; destlen = (unsigned char)hostname_len; /* one byte length */ @@ -826,90 +819,80 @@ result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; - CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (remotely resolved)", + CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)", sx->hostname, sx->remote_port); return CURLPX_OK; } -static CURLproxycode socks5_resolving(struct socks_state *sx, +static CURLproxycode socks5_resolving(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *hp = NULL; + const struct Curl_addrinfo *ai = NULL; char dest[MAX_IPADR_LEN]; /* printable address */ - unsigned char *destination = NULL; + const unsigned char *destination = NULL; unsigned char desttype = 1, destlen = 4; unsigned char req[2]; CURLcode result; CURLproxycode presult = CURLPX_OK; size_t nwritten; + bool dns_done; *done = FALSE; if(sx->start_resolving) { /* need to resolve hostname to add destination address */ sx->start_resolving = FALSE; - DEBUGASSERT(sx->hostname && *sx->hostname); - - result = Curl_resolv(data, sx->hostname, sx->remote_port, - cf->conn->ip_version, TRUE, &dns); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "SOCKS5 non-blocking resolve of %s", sx->hostname); - return CURLPX_OK; + result = Curl_cf_dns_insert_after( + cf, data, Curl_resolv_dns_queries(data, sx->ip_version), + sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + if(result) { + failf(data, "unable to create DNS filter for socks"); + return CURLPX_UNKNOWN_FAIL; } - else if(result) - return CURLPX_RESOLVE_HOST; - } - else { - /* check if we have the name resolved by now */ - result = Curl_resolv_check(data, &dns); - if(!result && !dns) - return CURLPX_OK; } - if(result || !dns) { + /* resolve the hostname by connecting the DNS filter */ + result = Curl_conn_cf_connect(cf->next, data, &dns_done); + if(result) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); - presult = CURLPX_RESOLVE_HOST; - goto out; + return CURLPX_RESOLVE_HOST; } + else if(!dns_done) + return CURLPX_OK; - if(dns) - hp = dns->addr; #ifdef USE_IPV6 - if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) { - int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ? - AF_INET : AF_INET6; - /* scan for the first proper address */ - while(hp && (hp->ai_family != wanted_family)) - hp = hp->ai_next; - } + if(data->set.ipver != CURL_IPRESOLVE_V4) + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET6, 0); #endif - if(!hp) { + if(!ai) + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + + if(!ai) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); presult = CURLPX_RESOLVE_HOST; goto out; } - Curl_printable_address(hp, dest, sizeof(dest)); + Curl_printable_address(ai, dest, sizeof(dest)); - if(hp->ai_family == AF_INET) { + if(ai->ai_family == AF_INET) { struct sockaddr_in *saddr_in; desttype = 1; /* ATYP: IPv4 = 1 */ destlen = 4; - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; - destination = (unsigned char *)&saddr_in->sin_addr.s_addr; - CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (locally resolved)", + saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; + destination = (const unsigned char *)&saddr_in->sin_addr.s_addr; + CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)", dest, sx->remote_port); } #ifdef USE_IPV6 - else if(hp->ai_family == AF_INET6) { + else if(ai->ai_family == AF_INET6) { struct sockaddr_in6 *saddr_in6; desttype = 4; /* ATYP: IPv6 = 4 */ destlen = 16; - saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; - destination = (unsigned char *)&saddr_in6->sin6_addr.s6_addr; - CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%d (locally resolved)", + saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; + destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr; + CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)", dest, sx->remote_port); } #endif @@ -932,8 +915,8 @@ goto out; } /* PORT MSB+LSB */ - req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffu); - req[1] = (unsigned char)(sx->remote_port & 0xffu); + req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffU); + req[1] = (unsigned char)(sx->remote_port & 0xffU); result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) { presult = CURLPX_SEND_REQUEST; @@ -941,13 +924,11 @@ } out: - if(dns) - Curl_resolv_unlink(data, &dns); *done = (presult == CURLPX_OK); return presult; } -static CURLproxycode socks5_recv_resp1(struct socks_state *sx, +static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1046,7 +1027,7 @@ * destination server. */ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { CURLproxycode presult; @@ -1056,14 +1037,13 @@ switch(sx->state) { case SOCKS_ST_INIT: sx->version = 5; - sx->resolve_local = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS5); + sx->resolve_local = (sx->proxy_type == CURLPROXY_SOCKS5); sxstate(sx, cf, data, SOCKS5_ST_START); FALLTHROUGH(); case SOCKS5_ST_START: - if(cf->conn->bits.httpproxy) - CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %d", - sx->hostname, sx->remote_port); + CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u", + sx->hostname, sx->remote_port); presult = socks5_req0_init(cf, sx, data); if(presult) return socks_failed(sx, cf, data, presult); @@ -1198,13 +1178,11 @@ } } -static void socks_proxy_cf_free(struct Curl_cfilter *cf) +static void socks_proxy_ctx_free(struct socks_ctx *ctx) { - struct socks_state *sxstate = cf->ctx; - if(sxstate) { - Curl_bufq_free(&sxstate->iobuf); - curlx_free(sxstate); - cf->ctx = NULL; + if(ctx) { + Curl_bufq_free(&ctx->iobuf); + curlx_free(ctx); } } @@ -1219,11 +1197,9 @@ struct Curl_easy *data, bool *done) { - CURLcode result; - struct connectdata *conn = cf->conn; - int sockindex = cf->sockindex; - struct socks_state *sx = cf->ctx; + struct socks_ctx *ctx = cf->ctx; CURLproxycode pxresult = CURLPX_OK; + CURLcode result; if(cf->connected) { *done = TRUE; @@ -1234,47 +1210,19 @@ if(result || !*done) return result; - if(!sx) { - cf->ctx = sx = curlx_calloc(1, sizeof(*sx)); - if(!sx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - sx->hostname = - conn->bits.httpproxy ? - conn->http_proxy.host.name : - conn->bits.conn_to_host ? - conn->conn_to_host.name : - sockindex == SECONDARYSOCKET ? - conn->secondaryhostname : conn->host.name; - sx->remote_port = - conn->bits.httpproxy ? (int)conn->http_proxy.port : - sockindex == SECONDARYSOCKET ? conn->secondary_port : - conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; - sx->proxy_user = conn->socks_proxy.user; - sx->proxy_password = conn->socks_proxy.passwd; - Curl_bufq_init2(&sx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, - BUFQ_OPT_SOFT_LIMIT); - } - - switch(conn->socks_proxy.proxytype) { + switch(ctx->proxy_type) { case CURLPROXY_SOCKS5: case CURLPROXY_SOCKS5_HOSTNAME: - pxresult = socks5_connect(cf, sx, data); + pxresult = socks5_connect(cf, ctx, data); break; case CURLPROXY_SOCKS4: case CURLPROXY_SOCKS4A: - pxresult = socks4_connect(cf, sx, data); + pxresult = socks4_connect(cf, ctx, data); break; default: - failf(data, "unknown proxytype option given"); + DEBUGASSERT(0); /* should not come here, checked it at creation time */ result = CURLE_COULDNT_CONNECT; goto out; } @@ -1284,30 +1232,33 @@ data->info.pxcode = pxresult; goto out; } - else if(sx->state != SOCKS_ST_SUCCESS) + else if(ctx->state != SOCKS_ST_SUCCESS) goto out; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { struct ip_quadruple ipquad; bool is_ipv6; if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) - infof(data, "Opened %sSOCKS connection from %s port %u to %s port %u " + infof(data, "Opened %sSOCKS connection from %s port %d to %s port %d " "(via %s port %u)", - (sockindex == SECONDARYSOCKET) ? "2nd " : "", + (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", ipquad.local_ip, ipquad.local_port, - sx->hostname, sx->remote_port, + ctx->hostname, ctx->remote_port, ipquad.remote_ip, ipquad.remote_port); else infof(data, "Opened %sSOCKS connection", - (sockindex == SECONDARYSOCKET) ? "2nd " : ""); + (cf->sockindex == SECONDARYSOCKET) ? "2nd " : ""); } #endif - socks_proxy_cf_free(cf); cf->connected = TRUE; out: - *done = cf->connected; + *done = (bool)cf->connected; + if(*done || result) { + ctx->user = NULL; + ctx->passwd = NULL; + } return result; } @@ -1315,7 +1266,7 @@ struct Curl_easy *data, struct easy_pollset *ps) { - struct socks_state *sx = cf->ctx; + struct socks_ctx *sx = cf->ctx; CURLcode result = CURLE_OK; if(!cf->connected && sx) { @@ -1342,25 +1293,24 @@ static void socks_proxy_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - - DEBUGASSERT(cf->next); cf->connected = FALSE; - socks_proxy_cf_free(cf); - cf->next->cft->do_close(cf->next, data); + if(cf->next) + cf->next->cft->do_close(cf->next, data); } static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { (void)data; - socks_proxy_cf_free(cf); + socks_proxy_ctx_free(cf->ctx); + cf->ctx = NULL; } static CURLcode socks_cf_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) { - struct socks_state *sx = cf->ctx; + struct socks_ctx *sx = cf->ctx; switch(query) { case CF_QUERY_HOST_PORT: @@ -1403,15 +1353,53 @@ }; CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + const char *hostname, + uint16_t port, + uint8_t ip_version, + uint8_t proxy_type, + const char *user, + const char *passwd) { struct Curl_cfilter *cf; + struct socks_ctx *ctx; + size_t hostlen = hostname ? strlen(hostname) : 0; CURLcode result; - (void)data; - result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL); + if(!hostlen) + return CURLE_FAILED_INIT; + + switch(proxy_type) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + break; /* all supported */ + default: + failf(data, "unknown proxytype %d option given", proxy_type); + return CURLE_COULDNT_CONNECT; + } + + /* NUL byte already part of struct size */ + ctx = curlx_calloc(1, sizeof(*ctx) + hostlen); + if(!ctx) { + return CURLE_OUT_OF_MEMORY; + } + + memcpy(ctx->hostname, hostname, hostlen); + ctx->remote_port = port; + ctx->ip_version = ip_version; + ctx->proxy_type = proxy_type; + ctx->user = user; + ctx->passwd = passwd; + Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + + result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, ctx); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else + socks_proxy_ctx_free(ctx); return result; }
diff --git a/Utilities/cmcurl/lib/socks.h b/Utilities/cmcurl/lib/socks.h index 520fb75..ea36832 100644 --- a/Utilities/cmcurl/lib/socks.h +++ b/Utilities/cmcurl/lib/socks.h
@@ -46,8 +46,19 @@ struct Curl_easy *data); #endif +/* Insert a SOCKS filter after `cf_at` for connecting to `hostname` + * and `port` with optional credentials. + * Credentials are NOT duplicated and are + * expected to exist during connect phase. + */ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + const char *hostname, + uint16_t port, + uint8_t ip_version, + uint8_t proxy_type, + const char *user, + const char *passwd); extern struct Curl_cftype Curl_cft_socks_proxy;
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c index 2da1171..32db070 100644 --- a/Utilities/cmcurl/lib/socks_gssapi.c +++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -33,9 +33,9 @@ #include "connect.h" #include "curlx/nonblock.h" #include "socks.h" -#include "strdup.h" +#include "curlx/strdup.h" -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -106,7 +106,7 @@ CURLcode code; size_t actualread; size_t nwritten; - int result; + CURLcode result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; int gss_conf_state, gss_enc; @@ -135,7 +135,7 @@ /* prepare service name */ if(strchr(serviceptr, '/')) { service.length = serviceptr_length; - service.value = Curl_memdup(serviceptr, service.length); + service.value = curlx_memdup(serviceptr, service.length); if(!service.value) return CURLE_OUT_OF_MEMORY; @@ -156,7 +156,7 @@ GSS_C_NT_HOSTBASED_SERVICE, &server); } - Curl_safefree(service.value); + curlx_safefree(service.value); service.length = 0; if(check_gss_err(data, gss_major_status, @@ -168,8 +168,8 @@ (void)curlx_nonblock(sock, FALSE); - /* As long as we need to keep sending some context info, and there is no */ - /* errors, keep sending it... */ + /* As long as we need to keep sending some context info, and there is no + * errors, keep sending it... */ for(;;) { gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, @@ -183,12 +183,12 @@ &gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) { - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; } if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_init_sec_context") || - /* the size needs to fit in a 16 bit field */ + /* the size needs to fit in a 16-bit field */ (gss_send_token.length > 0xffff)) { gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_send_token); @@ -228,7 +228,7 @@ if(gss_major_status != GSS_S_CONTINUE_NEEDED) break; - /* analyse response */ + /* analyze response */ /* GSS-API response looks like * +----+------+-----+----------------+ @@ -266,6 +266,13 @@ memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length GSS-API authentication token."); + gss_release_name(&gss_status, &server); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + gss_recv_token.length = us_length; gss_recv_token.value = curlx_malloc(gss_recv_token.length); if(!gss_recv_token.value) { @@ -283,7 +290,7 @@ if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API authentication token."); gss_release_name(&gss_status, &server); - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; @@ -317,7 +324,7 @@ } infof(data, "SOCKS5 server authenticated user %.*s with GSS-API.", - (int)gss_send_token.length, (char *)gss_send_token.value); + (int)gss_send_token.length, (const char *)gss_send_token.value); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); @@ -374,7 +381,7 @@ } else { gss_send_token.length = 1; - gss_send_token.value = Curl_memdup(&gss_enc, gss_send_token.length); + gss_send_token.value = curlx_memdup(&gss_enc, gss_send_token.length); if(!gss_send_token.value) { Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; @@ -385,14 +392,14 @@ &gss_conf_state, &gss_w_token); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { - Curl_safefree(gss_send_token.value); + curlx_safefree(gss_send_token.value); gss_send_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to wrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } - Curl_safefree(gss_send_token.value); + curlx_safefree(gss_send_token.value); gss_send_token.length = 0; us_length = htons((unsigned short)gss_w_token.length); @@ -453,6 +460,12 @@ memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length GSS-API encryption token."); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + gss_recv_token.length = us_length; gss_recv_token.value = curlx_malloc(gss_recv_token.length); if(!gss_recv_token.value) { @@ -464,7 +477,7 @@ if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API encryption type."); - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; @@ -476,14 +489,14 @@ 0, GSS_C_QOP_DEFAULT); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to unwrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; if(gss_w_token.length != 1) { @@ -501,21 +514,21 @@ if(gss_recv_token.length != 1) { failf(data, "Invalid GSS-API encryption response length (%zu).", gss_recv_token.length); - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq, gss_recv_token.value, gss_recv_token.length); - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; } (void)curlx_nonblock(sock, TRUE); infof(data, "SOCKS5 access with%s protection granted.", - (socksreq[0] == 0) ? "out GSS-API data": + (socksreq[0] == 0) ? "out GSS-API data" : ((socksreq[0] == 1) ? " GSS-API integrity" : " GSS-API confidentiality")); @@ -526,7 +539,7 @@ return CURLE_OK; } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c index 0f3cfce..385312a 100644 --- a/Utilities/cmcurl/lib/socks_sspi.c +++ b/Utilities/cmcurl/lib/socks_sspi.c
@@ -56,52 +56,70 @@ } /* This is the SSPI-using version of this function */ -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + CredHandle *cred_handle, + char **service_namep) { struct connectdata *conn = cf->conn; - curl_socket_t sock = conn->sock[cf->sockindex]; - CURLcode code; - size_t actualread; - size_t written; - CURLcode result; - /* Needs GSS-API authentication */ - SECURITY_STATUS status; - unsigned long sspi_ret_flags = 0; - unsigned char gss_enc; - SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3]; - SecBufferDesc input_desc, output_desc, wrap_desc; - SecPkgContext_Sizes sspi_sizes; - CredHandle cred_handle; - CtxtHandle sspi_context; - PCtxtHandle context_handle = NULL; - SecPkgCredentials_Names names; - char *service_name = NULL; - unsigned short us_length; - unsigned long qop; - unsigned char socksreq[4]; /* room for GSS-API exchange header only */ const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; - uint8_t *etbuf; - size_t etbuf_size; - - /* GSS-API request looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ + data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + SECURITY_STATUS status; /* prepare service name */ if(strchr(service, '/')) - service_name = curlx_strdup(service); + *service_namep = curlx_strdup(service); else - service_name = curl_maprintf("%s/%s", - service, conn->socks_proxy.host.name); - if(!service_name) + *service_namep = curl_maprintf("%s/%s", + service, conn->socks_proxy.host.name); + if(!*service_namep) return CURLE_OUT_OF_MEMORY; + status = + Curl_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *)CURL_UNCONST(TEXT("Kerberos")), + SECPKG_CRED_OUTBOUND, + NULL, NULL, NULL, NULL, + cred_handle, NULL); + + if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { + failf(data, "Failed to acquire credentials."); + return CURLE_COULDNT_CONNECT; + } + + return CURLE_OK; +} + +static CURLcode socks5_free_token(SecBuffer *send_token, + CURLcode result) +{ + if(send_token->pvBuffer) { + Curl_pSecFn->FreeContextBuffer(send_token->pvBuffer); + send_token->pvBuffer = NULL; + } + return result; +} + +static CURLcode socks5_sspi_loop(struct Curl_cfilter *cf, + struct Curl_easy *data, + CredHandle *cred_handle, + CtxtHandle *sspi_context, + char *service_name, + unsigned long *sspi_ret_flagsp) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result = CURLE_OK; + CURLcode code; + SECURITY_STATUS status; + SecBuffer sspi_send_token, sspi_recv_token; + SecBufferDesc input_desc, output_desc; + PCtxtHandle context_handle = NULL; + unsigned short us_length; + size_t actualread; + size_t written; + unsigned char socksreq[4]; + input_desc.cBuffers = 1; input_desc.pBuffers = &sspi_recv_token; input_desc.ulVersion = SECBUFFER_VERSION; @@ -118,6 +136,158 @@ sspi_send_token.cbBuffer = 0; sspi_send_token.pvBuffer = NULL; + (void)curlx_nonblock(sock, FALSE); + + for(;;) { + TCHAR *sname = curlx_convert_UTF8_to_tchar(service_name); + if(!sname) { + curlx_free(sspi_recv_token.pvBuffer); + return socks5_free_token(&sspi_send_token, CURLE_OUT_OF_MEMORY); + } + + status = + Curl_pSecFn->InitializeSecurityContext(cred_handle, context_handle, + sname, + ISC_REQ_MUTUAL_AUTH | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_CONFIDENTIALITY | + ISC_REQ_REPLAY_DETECT, + 0, SECURITY_NATIVE_DREP, + &input_desc, 0, + sspi_context, + &output_desc, + sspi_ret_flagsp, NULL); + + curlx_free(sname); + curlx_safefree(sspi_recv_token.pvBuffer); + sspi_recv_token.cbBuffer = 0; + + if(check_sspi_err(data, status, "InitializeSecurityContext")) { + failf(data, "Failed to initialise security context."); + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); + } + + if(sspi_send_token.cbBuffer) { + socksreq[0] = 1; /* GSS-API subnegotiation version */ + socksreq[1] = 1; /* authentication message type */ + if(sspi_send_token.cbBuffer > 0xffff) { + /* needs to fit in an unsigned 16-bit field */ + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); + } + us_length = htons((unsigned short)sspi_send_token.cbBuffer); + memcpy(socksreq + 2, &us_length, sizeof(short)); + + code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); + if(code || (written != 4)) { + failf(data, "Failed to send SSPI authentication request."); + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); + } + + code = Curl_conn_cf_send(cf->next, data, + sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, FALSE, &written); + if(code || (sspi_send_token.cbBuffer != written)) { + failf(data, "Failed to send SSPI authentication token."); + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); + } + } + + if(sspi_send_token.pvBuffer) + socks5_free_token(&sspi_send_token, CURLE_OK); + sspi_send_token.cbBuffer = 0; + + curlx_safefree(sspi_recv_token.pvBuffer); + sspi_recv_token.cbBuffer = 0; + + if(status != SEC_I_CONTINUE_NEEDED) + break; + + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + if(result || (actualread != 4)) { + failf(data, "Failed to receive SSPI authentication response."); + return result ? result : CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] == 255) { + failf(data, "User was rejected by the SOCKS5 server (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + return CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] != 1) { + failf(data, "Invalid SSPI authentication response type (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + return CURLE_COULDNT_CONNECT; + } + + memcpy(&us_length, socksreq + 2, sizeof(short)); + us_length = ntohs(us_length); + + if(!us_length) { + failf(data, "Invalid zero-length SSPI authentication token."); + return CURLE_COULDNT_CONNECT; + } + + sspi_recv_token.cbBuffer = us_length; + sspi_recv_token.pvBuffer = curlx_malloc(us_length); + + if(!sspi_recv_token.pvBuffer) + return CURLE_OUT_OF_MEMORY; + + result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, + sspi_recv_token.cbBuffer, &actualread); + + if(result || (actualread != us_length)) { + failf(data, "Failed to receive SSPI authentication token."); + curlx_free(sspi_recv_token.pvBuffer); + return result ? result : CURLE_COULDNT_CONNECT; + } + + context_handle = sspi_context; + } + + return CURLE_OK; +} + +static CURLcode socks5_free(SecBuffer *sspi_w_token, + CURLcode result) +{ + curlx_safefree(sspi_w_token[0].pvBuffer); + curlx_safefree(sspi_w_token[1].pvBuffer); + curlx_safefree(sspi_w_token[2].pvBuffer); + return result; +} + +static CURLcode socks5_sspi_encrypt(struct Curl_cfilter *cf, + struct Curl_easy *data, + CtxtHandle *sspi_context, + unsigned long sspi_ret_flags) +{ + CURLcode result = CURLE_OK; + CURLcode code; + SECURITY_STATUS status; + unsigned char gss_enc; + SecBuffer sspi_w_token[3]; + SecBufferDesc wrap_desc; + SecPkgContext_Sizes sspi_sizes; + unsigned short us_length; + unsigned long qop; + unsigned char socksreq[4]; + uint8_t *etbuf = NULL; + size_t etbuf_size = 0; + size_t actualread; + size_t written; + + gss_enc = 0; + if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY) + gss_enc = 2; + else if(sspi_ret_flags & ISC_REQ_INTEGRITY) + gss_enc = 1; + + infof(data, "SOCKS5 server supports GSS-API %s data protection.", + (gss_enc == 0) ? "no" : + ((gss_enc == 1) ? "integrity" : "confidentiality")); + sspi_w_token[0].pvBuffer = sspi_w_token[1].pvBuffer = sspi_w_token[2].pvBuffer = NULL; @@ -126,288 +296,56 @@ wrap_desc.pBuffers = sspi_w_token; wrap_desc.ulVersion = SECBUFFER_VERSION; - memset(&cred_handle, 0, sizeof(cred_handle)); - memset(&sspi_context, 0, sizeof(sspi_context)); - - names.sUserName = NULL; - - etbuf = NULL; - etbuf_size = 0; - - status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT("Kerberos")), - SECPKG_CRED_OUTBOUND, - NULL, NULL, NULL, NULL, - &cred_handle, NULL); - - if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { - failf(data, "Failed to acquire credentials."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - (void)curlx_nonblock(sock, FALSE); - - /* As long as we need to keep sending some context info, and there is no */ - /* errors, keep sending it... */ - for(;;) { - TCHAR *sname; - - sname = curlx_convert_UTF8_to_tchar(service_name); - if(!sname) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - status = - Curl_pSecFn->InitializeSecurityContext(&cred_handle, context_handle, - sname, - ISC_REQ_MUTUAL_AUTH | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_CONFIDENTIALITY | - ISC_REQ_REPLAY_DETECT, - 0, SECURITY_NATIVE_DREP, - &input_desc, 0, - &sspi_context, - &output_desc, - &sspi_ret_flags, NULL); - - curlx_free(sname); - - Curl_safefree(sspi_recv_token.pvBuffer); - sspi_recv_token.cbBuffer = 0; - - if(check_sspi_err(data, status, "InitializeSecurityContext")) { - failf(data, "Failed to initialise security context."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - if(sspi_send_token.cbBuffer) { - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 1; /* authentication message type */ - if(sspi_send_token.cbBuffer > 0xffff) { - /* needs to fit in an unsigned 16 bit field */ - result = CURLE_COULDNT_CONNECT; - goto error; - } - us_length = htons((unsigned short)sspi_send_token.cbBuffer); - memcpy(socksreq + 2, &us_length, sizeof(short)); - - code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); - if(code || (written != 4)) { - failf(data, "Failed to send SSPI authentication request."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - code = Curl_conn_cf_send(cf->next, data, - sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, FALSE, &written); - if(code || (sspi_send_token.cbBuffer != written)) { - failf(data, "Failed to send SSPI authentication token."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - } - - if(sspi_send_token.pvBuffer) { - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - sspi_send_token.pvBuffer = NULL; - } - sspi_send_token.cbBuffer = 0; - - Curl_safefree(sspi_recv_token.pvBuffer); - sspi_recv_token.cbBuffer = 0; - - if(status != SEC_I_CONTINUE_NEEDED) - break; - - /* analyse response */ - - /* GSS-API response looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ - - result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); - if(result || (actualread != 4)) { - failf(data, "Failed to receive SSPI authentication response."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; - } - - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - if(socksreq[1] != 1) { /* status / message type */ - failf(data, "Invalid SSPI authentication response type (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - memcpy(&us_length, socksreq + 2, sizeof(short)); - us_length = ntohs(us_length); - - sspi_recv_token.cbBuffer = us_length; - sspi_recv_token.pvBuffer = curlx_malloc(us_length); - - if(!sspi_recv_token.pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, - sspi_recv_token.cbBuffer, &actualread); - - if(result || (actualread != us_length)) { - failf(data, "Failed to receive SSPI authentication token."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; - } - - context_handle = &sspi_context; - } - - Curl_safefree(service_name); - - /* Everything is good so far, user was authenticated! */ - status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle, - SECPKG_CRED_ATTR_NAMES, - &names); - if(check_sspi_err(data, status, "QueryCredentialAttributes")) { - failf(data, "Failed to determine username."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - else { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char *user_utf8 = curlx_convert_tchar_to_UTF8(names.sUserName); - infof(data, "SOCKS5 server authenticated user %s with GSS-API.", - (user_utf8 ? user_utf8 : "(unknown)")); - curlx_free(user_utf8); -#endif - Curl_pSecFn->FreeContextBuffer(names.sUserName); - names.sUserName = NULL; - } - - /* Do encryption */ - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 2; /* encryption message type */ - - gss_enc = 0; /* no data protection */ - /* do confidentiality protection if supported */ - if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY) - gss_enc = 2; - /* else do integrity protection */ - else if(sspi_ret_flags & ISC_REQ_INTEGRITY) - gss_enc = 1; - - infof(data, "SOCKS5 server supports GSS-API %s data protection.", - (gss_enc == 0) ? "no" : - ((gss_enc == 1) ? "integrity" : "confidentiality") ); - - /* - * Sending the encryption type in clear seems wrong. It should be - * protected with gss_seal()/gss_wrap(). See RFC1961 extract below - * The NEC reference implementations on which this is based is - * therefore at fault - * - * +------+------+------+.......................+ - * + ver | mtyp | len | token | - * +------+------+------+.......................+ - * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | - * +------+------+------+.......................+ - * - * Where: - * - * - "ver" is the protocol version number, here 1 to represent the - * first version of the SOCKS/GSS-API protocol - * - * - "mtyp" is the message type, here 2 to represent a protection - * -level negotiation message - * - * - "len" is the length of the "token" field in octets - * - * - "token" is the GSS-API encapsulated protection level - * - * The token is produced by encapsulating an octet containing the - * required protection level using gss_seal()/gss_wrap() with conf_req - * set to FALSE. The token is verified using gss_unseal()/ - * gss_unwrap(). - * - */ + socksreq[0] = 1; + socksreq[1] = 2; if(data->set.socks5_gssapi_nec) { us_length = htons((unsigned short)1); memcpy(socksreq + 2, &us_length, sizeof(short)); } else { - status = Curl_pSecFn->QueryContextAttributes(&sspi_context, + status = Curl_pSecFn->QueryContextAttributes(sspi_context, SECPKG_ATTR_SIZES, &sspi_sizes); if(check_sspi_err(data, status, "QueryContextAttributes")) { failf(data, "Failed to query security context attributes."); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer; sspi_w_token[0].BufferType = SECBUFFER_TOKEN; sspi_w_token[0].pvBuffer = curlx_malloc(sspi_sizes.cbSecurityTrailer); - if(!sspi_w_token[0].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!sspi_w_token[0].pvBuffer) + return CURLE_OUT_OF_MEMORY; sspi_w_token[1].cbBuffer = 1; + sspi_w_token[1].BufferType = SECBUFFER_DATA; sspi_w_token[1].pvBuffer = curlx_malloc(1); - if(!sspi_w_token[1].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!sspi_w_token[1].pvBuffer) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1); sspi_w_token[2].BufferType = SECBUFFER_PADDING; sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize; sspi_w_token[2].pvBuffer = curlx_malloc(sspi_sizes.cbBlockSize); - if(!sspi_w_token[2].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - status = Curl_pSecFn->EncryptMessage(&sspi_context, + if(!sspi_w_token[2].pvBuffer) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); + + status = Curl_pSecFn->EncryptMessage(sspi_context, KERB_WRAP_NO_ENCRYPT, &wrap_desc, 0); - if(check_sspi_err(data, status, "EncryptMessage")) { - failf(data, "Failed to query security context attributes."); - result = CURLE_COULDNT_CONNECT; - goto error; - } + if(check_sspi_err(data, status, "EncryptMessage")) + return socks5_free(sspi_w_token, CURLE_COULDNT_CONNECT); etbuf_size = sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer + - sspi_w_token[2].cbBuffer; - if(etbuf_size > 0xffff) { - /* needs to fit in an unsigned 16 bit field */ - result = CURLE_COULDNT_CONNECT; - goto error; - } + sspi_w_token[2].cbBuffer; + if(etbuf_size > 0xffff) + return socks5_free(sspi_w_token, CURLE_COULDNT_CONNECT); + etbuf = curlx_malloc(etbuf_size); - if(!etbuf) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!etbuf) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); memcpy(etbuf, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); memcpy(etbuf + sspi_w_token[0].cbBuffer, @@ -415,12 +353,7 @@ memcpy(etbuf + sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer, sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer); - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; - Curl_safefree(sspi_w_token[1].pvBuffer); - sspi_w_token[1].cbBuffer = 0; - Curl_safefree(sspi_w_token[2].pvBuffer); - sspi_w_token[2].cbBuffer = 0; + (void)socks5_free(sspi_w_token, CURLE_OK); us_length = htons((unsigned short)etbuf_size); memcpy(socksreq + 2, &us_length, sizeof(short)); @@ -429,8 +362,8 @@ code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); if(code || (written != 4)) { failf(data, "Failed to send SSPI encryption request."); - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(etbuf); + return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { @@ -438,62 +371,57 @@ code = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &written); if(code || (written != 1)) { failf(data, "Failed to send SSPI encryption type."); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } } else { - code = Curl_conn_cf_send(cf->next, data, etbuf, etbuf_size, - FALSE, &written); + code = Curl_conn_cf_send(cf->next, data, etbuf, etbuf_size, FALSE, + &written); + curlx_free(etbuf); if(code || (etbuf_size != written)) { failf(data, "Failed to send SSPI encryption type."); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } - Curl_safefree(etbuf); } result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI encryption response."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; + return result ? result : CURLE_COULDNT_CONNECT; } - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ + if(socksreq[1] == 255) { failf(data, "User was rejected by the SOCKS5 server (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } - if(socksreq[1] != 2) { /* status / message type */ + if(socksreq[1] != 2) { failf(data, "Invalid SSPI encryption response type (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length SSPI encryption token."); + return CURLE_COULDNT_CONNECT; + } + sspi_w_token[0].cbBuffer = us_length; sspi_w_token[0].pvBuffer = curlx_malloc(us_length); - if(!sspi_w_token[0].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!sspi_w_token[0].pvBuffer) + return CURLE_OUT_OF_MEMORY; result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer, &actualread); if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI encryption type."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_w_token[0].pvBuffer); + return result ? result : CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { @@ -503,86 +431,108 @@ sspi_w_token[1].cbBuffer = 0; sspi_w_token[1].pvBuffer = NULL; - status = Curl_pSecFn->DecryptMessage(&sspi_context, &wrap_desc, + status = Curl_pSecFn->DecryptMessage(sspi_context, &wrap_desc, 0, &qop); - /* since sspi_w_token[1].pvBuffer is allocated by the SSPI in this case, it - must be freed in this block using FreeContextBuffer() instead of - potentially in error cleanup using curlx_free(). */ - if(check_sspi_err(data, status, "DecryptMessage")) { - failf(data, "Failed to query security context attributes."); - if(sspi_w_token[1].pvBuffer) { + if(sspi_w_token[1].pvBuffer) Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - } - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } if(sspi_w_token[1].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[1].cbBuffer); - if(sspi_w_token[1].pvBuffer) { + if(sspi_w_token[1].pvBuffer) Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - } - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - sspi_w_token[1].cbBuffer = 0; } else { if(sspi_w_token[0].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[0].cbBuffer); - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; } - (void)curlx_nonblock(sock, TRUE); + curlx_free(sspi_w_token[0].pvBuffer); infof(data, "SOCKS5 access with%s protection granted BUT NOT USED.", - (socksreq[0] == 0) ? "out GSS-API data": + (socksreq[0] == 0) ? "out GSS-API data" : ((socksreq[0] == 1) ? " GSS-API integrity" : " GSS-API confidentiality")); - /* For later use if encryption is required - conn->socks5_gssapi_enctype = socksreq[0]; - if(socksreq[0] != 0) - conn->socks5_sspi_context = sspi_context; - else { - Curl_pSecFn->DeleteSecurityContext(&sspi_context); - conn->socks5_sspi_context = sspi_context; - } - */ + return CURLE_OK; +} +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result; + SECURITY_STATUS status; + CredHandle cred_handle; + CtxtHandle sspi_context; + SecPkgCredentials_Names names; + char *service_name = NULL; + unsigned long sspi_ret_flags = 0; + + memset(&cred_handle, 0, sizeof(cred_handle)); + memset(&sspi_context, 0, sizeof(sspi_context)); + names.sUserName = NULL; + + result = socks5_sspi_setup(cf, data, &cred_handle, &service_name); + if(result) + goto error; + + result = socks5_sspi_loop(cf, data, &cred_handle, &sspi_context, + service_name, &sspi_ret_flags); + if(result) + goto error; + + curlx_safefree(service_name); + + status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle, + SECPKG_CRED_ATTR_NAMES, + &names); + if(check_sspi_err(data, status, "QueryCredentialAttributes")) { + failf(data, "Failed to determine username."); + result = CURLE_COULDNT_CONNECT; + goto error; + } + else { + VERBOSE(char *user_utf8 = curlx_convert_tchar_to_UTF8(names.sUserName)); + infof(data, "SOCKS5 server authenticated user %s with GSS-API.", + (user_utf8 ? user_utf8 : "(unknown)")); + VERBOSE(curlx_free(user_utf8)); + Curl_pSecFn->FreeContextBuffer(names.sUserName); + names.sUserName = NULL; + } + + result = socks5_sspi_encrypt(cf, data, &sspi_context, sspi_ret_flags); + if(result) + goto error; + + (void)curlx_nonblock(sock, TRUE); Curl_pSecFn->DeleteSecurityContext(&sspi_context); Curl_pSecFn->FreeCredentialsHandle(&cred_handle); return CURLE_OK; + error: (void)curlx_nonblock(sock, TRUE); curlx_free(service_name); Curl_pSecFn->DeleteSecurityContext(&sspi_context); Curl_pSecFn->FreeCredentialsHandle(&cred_handle); - curlx_free(sspi_recv_token.pvBuffer); - if(sspi_send_token.pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); if(names.sUserName) Curl_pSecFn->FreeContextBuffer(names.sUserName); - curlx_free(sspi_w_token[0].pvBuffer); - curlx_free(sspi_w_token[1].pvBuffer); - curlx_free(sspi_w_token[2].pvBuffer); - curlx_free(etbuf); return result; } #endif
diff --git a/Utilities/cmcurl/lib/splay.c b/Utilities/cmcurl/lib/splay.c index 5e4cd54..ddab7a4 100644 --- a/Utilities/cmcurl/lib/splay.c +++ b/Utilities/cmcurl/lib/splay.c
@@ -241,7 +241,7 @@ to remove, as otherwise we might be trying to remove a node that is not actually in the tree. - We cannot just compare the keys here as a double remove in quick + We cannot compare the keys here as a double remove in quick succession of a node with key != SPLAY_SUBNODE && same != NULL could return the same key but a different node. */ DEBUGASSERT(t == removenode); @@ -252,7 +252,7 @@ remove the root node of a list of nodes with identical keys. */ x = t->samen; if(x != t) { - /* 'x' is the new root node, we just make it use the root node's + /* 'x' is the new root node, we make it use the root node's smaller/larger links */ x->key = t->key;
diff --git a/Utilities/cmcurl/lib/splay.h b/Utilities/cmcurl/lib/splay.h index 9ed96f6..c6623f2 100644 --- a/Utilities/cmcurl/lib/splay.h +++ b/Utilities/cmcurl/lib/splay.h
@@ -42,7 +42,7 @@ struct Curl_tree *Curl_splayinsert(const struct curltime *pkey, struct Curl_tree *t, - struct Curl_tree *newnode); + struct Curl_tree *node); struct Curl_tree *Curl_splaygetbest(const struct curltime *pkey, struct Curl_tree *t,
diff --git a/Utilities/cmcurl/lib/strcase.c b/Utilities/cmcurl/lib/strcase.c index 34ccae3..9f70f41 100644 --- a/Utilities/cmcurl/lib/strcase.c +++ b/Utilities/cmcurl/lib/strcase.c
@@ -116,7 +116,7 @@ /* Compare case-sensitive null-terminated strings, taking care of possible * null pointers. Return true if arguments match. */ -bool Curl_safecmp(char *a, char *b) +bool Curl_safecmp(const char *a, const char *b) { if(a && b) return !strcmp(a, b);
diff --git a/Utilities/cmcurl/lib/strcase.h b/Utilities/cmcurl/lib/strcase.h index 3a7f126..5429981 100644 --- a/Utilities/cmcurl/lib/strcase.h +++ b/Utilities/cmcurl/lib/strcase.h
@@ -35,7 +35,7 @@ void Curl_strntoupper(char *dest, const char *src, size_t n); void Curl_strntolower(char *dest, const char *src, size_t n); -bool Curl_safecmp(char *a, char *b); -int Curl_timestrcmp(const char *first, const char *second); +bool Curl_safecmp(const char *a, const char *b); +int Curl_timestrcmp(const char *a, const char *b); #endif /* HEADER_CURL_STRCASE_H */
diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c deleted file mode 100644 index f666571..0000000 --- a/Utilities/cmcurl/lib/strdup.c +++ /dev/null
@@ -1,139 +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 _WIN32 -#include <wchar.h> -#endif - -#include "strdup.h" - -#ifndef HAVE_STRDUP -char *Curl_strdup(const char *str) -{ - size_t len; - char *newstr; - - if(!str) - return (char *)NULL; - - len = strlen(str) + 1; - - newstr = curlx_malloc(len); - if(!newstr) - return (char *)NULL; - - memcpy(newstr, str, len); - return newstr; -} -#endif - -#ifdef _WIN32 -/*************************************************************************** - * - * Curl_wcsdup(source) - * - * Copies the 'source' wchar string to a newly allocated buffer (that is - * returned). - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -wchar_t *Curl_wcsdup(const wchar_t *src) -{ - size_t length = wcslen(src); - - if(length > (SIZE_MAX / sizeof(wchar_t)) - 1) - return (wchar_t *)NULL; /* integer overflow */ - - return (wchar_t *)Curl_memdup(src, (length + 1) * sizeof(wchar_t)); -} -#endif - -/*************************************************************************** - * - * Curl_memdup(source, length) - * - * Copies the 'source' data to a newly allocated buffer (that is - * returned). Copies 'length' bytes. - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -void *Curl_memdup(const void *src, size_t length) -{ - void *buffer = curlx_malloc(length); - if(!buffer) - return NULL; /* fail */ - - memcpy(buffer, src, length); - - return buffer; -} - -/*************************************************************************** - * - * Curl_memdup0(source, length) - * - * Copies the 'source' string to a newly allocated buffer (that is returned). - * Copies 'length' bytes then adds a null-terminator. - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -void *Curl_memdup0(const char *src, size_t length) -{ - char *buf = (length < SIZE_MAX) ? curlx_malloc(length + 1) : NULL; - if(!buf) - return NULL; - if(length) { - DEBUGASSERT(src); /* must never be NULL */ - memcpy(buf, src, length); - } - buf[length] = 0; - return buf; -} - -/*************************************************************************** - * - * Curl_saferealloc(ptr, size) - * - * Does a normal curlx_realloc(), but will free the data pointer if the realloc - * fails. If 'size' is non-zero, it will free the data and return a failure. - * - * This convenience function is provided and used to help us avoid a common - * mistake pattern when we could pass in a zero, catch the NULL return and end - * up free'ing the memory twice. - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -void *Curl_saferealloc(void *ptr, size_t size) -{ - void *datap = curlx_realloc(ptr, size); - if(size && !datap) - /* only free 'ptr' if size was non-zero */ - curlx_free(ptr); - return datap; -}
diff --git a/Utilities/cmcurl/lib/strdup.h b/Utilities/cmcurl/lib/strdup.h deleted file mode 100644 index cd01ffb..0000000 --- a/Utilities/cmcurl/lib/strdup.h +++ /dev/null
@@ -1,37 +0,0 @@ -#ifndef HEADER_CURL_STRDUP_H -#define HEADER_CURL_STRDUP_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" - -#ifndef HAVE_STRDUP -char *Curl_strdup(const char *str); -#endif -#ifdef _WIN32 -wchar_t *Curl_wcsdup(const wchar_t *src); -#endif -void *Curl_memdup(const void *src, size_t buffer_length); -void *Curl_saferealloc(void *ptr, size_t size); -void *Curl_memdup0(const char *src, size_t length); -#endif /* HEADER_CURL_STRDUP_H */
diff --git a/Utilities/cmcurl/lib/strequal.c b/Utilities/cmcurl/lib/strequal.c index 98cecdc..a352fda 100644 --- a/Utilities/cmcurl/lib/strequal.c +++ b/Utilities/cmcurl/lib/strequal.c
@@ -42,7 +42,7 @@ second++; } /* If we are here either the strings are the same or the length is different. - We can just test if the "current" character is non-zero for one and zero + We can test if the "current" character is non-zero for one and zero for the other. Note that the characters may not be exactly the same even if they match, we only want to compare zero-ness. */ return !*first == !*second; @@ -73,23 +73,23 @@ */ /* --- public function --- */ -int curl_strequal(const char *first, const char *second) +int curl_strequal(const char *s1, const char *s2) { - if(first && second) + if(s1 && s2) /* both pointers point to something then compare them */ - return casecompare(first, second); + return casecompare(s1, s2); /* if both pointers are NULL then treat them as equal */ - return NULL == first && NULL == second; + return NULL == s1 && NULL == s2; } /* --- public function --- */ -int curl_strnequal(const char *first, const char *second, size_t max) +int curl_strnequal(const char *s1, const char *s2, size_t n) { - if(first && second) + if(s1 && s2) /* both pointers point to something then compare them */ - return ncasecompare(first, second, max); + return ncasecompare(s1, s2, n); /* if both pointers are NULL then treat them as equal if max is non-zero */ - return NULL == first && NULL == second && max; + return NULL == s1 && NULL == s2 && n; }
diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index b281b18..1e97c28 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c
@@ -33,7 +33,7 @@ const char *curl_easy_strerror(CURLcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLE_OK: return "No error"; @@ -325,7 +325,7 @@ const char *curl_multi_strerror(CURLMcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLM_CALL_MULTI_PERFORM: return "Please call curl_multi_perform() soon"; @@ -384,7 +384,7 @@ const char *curl_share_strerror(CURLSHcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLSHE_OK: return "No error"; @@ -419,7 +419,7 @@ const char *curl_url_strerror(CURLUcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLUE_OK: return "No error"; @@ -541,15 +541,14 @@ DWORD old_win_err = GetLastError(); #endif int old_errno = errno; - const char *txt; + VERBOSE(const char *txt); if(!buflen) return NULL; *buf = '\0'; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - +#ifdef CURLVERBOSE switch(err) { case SEC_E_OK: txt = "No error"; @@ -657,13 +656,11 @@ else curl_msnprintf(buf, buflen, "%s (0x%08lx)", txt, err); } - -#else +#else /* CURLVERBOSE */ if(err == SEC_E_OK) - txt = "No error"; + curlx_strcopy(buf, buflen, STRCONST("No error")); else - txt = "Error"; - curlx_strcopy(buf, buflen, txt, strlen(txt)); + curlx_strcopy(buf, buflen, STRCONST("Error")); #endif if(errno != old_errno)
diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c index 0e03e7c..1b05235 100644 --- a/Utilities/cmcurl/lib/system_win32.c +++ b/Utilities/cmcurl/lib/system_win32.c
@@ -26,25 +26,15 @@ #ifdef _WIN32 #include "system_win32.h" -#include "curlx/version_win32.h" #include "curl_sspi.h" - -#ifndef HAVE_IF_NAMETOINDEX -/* Handle of iphlpapp.dll */ -static HMODULE s_hIpHlpApiDll = NULL; - -/* Pointer to the if_nametoindex function */ -IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL; - -/* This is used to dynamically load DLLs */ -static HMODULE curl_load_library(LPCTSTR filename); -#endif +#include "curlx/timeval.h" +#include "curlx/version_win32.h" /* for curlx_verify_windows_init() */ /* Curl_win32_init() performs Win32 global initialization */ CURLcode Curl_win32_init(long flags) { /* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which - is just for Winsock at the moment. Any required Win32 initialization + is for Winsock at the moment. Any required Win32 initialization should take place after this block. */ if(flags & CURL_GLOBAL_WIN32) { #ifdef USE_WINSOCK @@ -57,7 +47,7 @@ if(res) /* Tell the user that we could not find a usable */ - /* winsock.dll. */ + /* winsock.dll. */ return CURLE_FAILED_INIT; /* Confirm that the Windows Sockets DLL supports what we need.*/ @@ -88,43 +78,14 @@ } #endif -#ifndef HAVE_IF_NAMETOINDEX - s_hIpHlpApiDll = curl_load_library(TEXT("iphlpapi.dll")); - if(s_hIpHlpApiDll) { - /* Get the address of the if_nametoindex function */ - IF_NAMETOINDEX_FN pIfNameToIndex = - CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN, - GetProcAddress(s_hIpHlpApiDll, "if_nametoindex")); - - if(pIfNameToIndex) - Curl_if_nametoindex = pIfNameToIndex; - } -#endif - - /* curlx_verify_windows_version must be called during init at least once - because it has its own initialization routine. */ - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - Curl_isVistaOrGreater = TRUE; - } - else - Curl_isVistaOrGreater = FALSE; - - QueryPerformanceFrequency(&Curl_freq); + curlx_verify_windows_init(); + curlx_now_init(); return CURLE_OK; } /* Curl_win32_cleanup() is the opposite of Curl_win32_init() */ void Curl_win32_cleanup(long init_flags) { -#ifndef HAVE_IF_NAMETOINDEX - if(s_hIpHlpApiDll) { - FreeLibrary(s_hIpHlpApiDll); - s_hIpHlpApiDll = NULL; - Curl_if_nametoindex = NULL; - } -#endif - #ifdef USE_WINDOWS_SSPI Curl_sspi_global_cleanup(); #endif @@ -136,104 +97,4 @@ } } -#ifndef HAVE_IF_NAMETOINDEX - -#ifndef LOAD_WITH_ALTERED_SEARCH_PATH -#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 -#endif - -#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 -#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 -#endif - -/* We use our own typedef here since some headers might lack these */ -typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD); - -/* See function definitions in winbase.h */ -#ifdef UNICODE -# define LOADLIBARYEX "LoadLibraryExW" -#else -# define LOADLIBARYEX "LoadLibraryExA" -#endif - -/* - * curl_load_library() - * - * This is used to dynamically load DLLs using the most secure method available - * for the version of Windows that we are running on. - * - * Parameters: - * - * filename [in] - The filename or full path of the DLL to load. If only the - * filename is passed then the DLL will be loaded from the - * Windows system directory. - * - * Returns the handle of the module on success; otherwise NULL. - */ -static HMODULE curl_load_library(LPCTSTR filename) -{ -#ifndef CURL_WINDOWS_UWP - HMODULE hModule = NULL; - LOADLIBRARYEX_FN pLoadLibraryEx = NULL; - - /* Get a handle to kernel32 so we can access its functions at runtime */ - HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32")); - if(!hKernel32) - return NULL; - - /* Attempt to find LoadLibraryEx() which is only available on Windows 2000 - and above */ - pLoadLibraryEx = - CURLX_FUNCTION_CAST(LOADLIBRARYEX_FN, - GetProcAddress(hKernel32, LOADLIBARYEX)); - - /* Detect if there is already a path in the filename and load the library if - there is. Note: Both back slashes and forward slashes have been supported - since the earlier days of DOS at an API level although they are not - supported by command prompt */ - if(_tcspbrk(filename, TEXT("\\/"))) { - /** !checksrc! disable BANNEDFUNC 1 **/ - hModule = pLoadLibraryEx ? - pLoadLibraryEx(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : - LoadLibrary(filename); - } - /* Detect if KB2533623 is installed, as LOAD_LIBRARY_SEARCH_SYSTEM32 is only - supported on Windows Vista, Windows Server 2008, Windows 7 and Windows - Server 2008 R2 with this patch or natively on Windows 8 and above */ - else if(pLoadLibraryEx && GetProcAddress(hKernel32, "AddDllDirectory")) { - /* Load the DLL from the Windows system directory */ - hModule = pLoadLibraryEx(filename, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); - } - else { - /* Attempt to get the Windows system path */ - UINT systemdirlen = GetSystemDirectory(NULL, 0); - if(systemdirlen) { - /* Allocate space for the full DLL path (Room for the null-terminator - is included in systemdirlen) */ - size_t filenamelen = _tcslen(filename); - TCHAR *path = curlx_malloc(sizeof(TCHAR) * - (systemdirlen + 1 + filenamelen)); - if(path && GetSystemDirectory(path, systemdirlen)) { - /* Calculate the full DLL path */ - _tcscpy(path + _tcslen(path), TEXT("\\")); - _tcscpy(path + _tcslen(path), filename); - - /* Load the DLL from the Windows system directory */ - /** !checksrc! disable BANNEDFUNC 1 **/ - hModule = pLoadLibraryEx ? - pLoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : - LoadLibrary(path); - } - curlx_free(path); - } - } - return hModule; -#else - /* the Universal Windows Platform (UWP) cannot do this */ - (void)filename; - return NULL; -#endif -} -#endif /* !HAVE_IF_NAMETOINDEX */ - #endif /* _WIN32 */
diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h index c7df2a5..8a51f09 100644 --- a/Utilities/cmcurl/lib/system_win32.h +++ b/Utilities/cmcurl/lib/system_win32.h
@@ -26,21 +26,11 @@ #include "curl_setup.h" #ifdef _WIN32 - extern LARGE_INTEGER Curl_freq; -extern bool Curl_isVistaOrGreater; CURLcode Curl_win32_init(long flags); void Curl_win32_cleanup(long init_flags); - -#ifndef HAVE_IF_NAMETOINDEX -/* We use our own typedef here since some headers might lack this */ -typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *); - -/* This is used instead of if_nametoindex if available on Windows */ -extern IF_NAMETOINDEX_FN Curl_if_nametoindex; -#endif -#else /* !_WIN32 */ +#else #define Curl_win32_init(x) CURLE_OK #endif /* _WIN32 */
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index cca97cf..c5ce9c2 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c
@@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "telnet.h" #ifndef CURL_DISABLE_TELNET @@ -45,13 +47,10 @@ #include <sys/param.h> #endif -#include "urldata.h" #include "url.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" -#include "telnet.h" -#include "connect.h" #include "progress.h" #include "arpa_telnet.h" #include "select.h" @@ -59,29 +58,25 @@ #define SUBBUFSIZE 512 -#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer -#define CURL_SB_TERM(x) \ - do { \ - x->subend = x->subpointer; \ - CURL_SB_CLEAR(x); \ +#define CURL_SB_CLEAR(x) x->subpointer = (x)->subbuffer +#define CURL_SB_TERM(x) \ + do { \ + (x)->subend = (x)->subpointer; \ + CURL_SB_CLEAR(x); \ } while(0) -#define CURL_SB_ACCUM(x, c) \ - do { \ - if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \ - *x->subpointer++ = (c); \ +#define CURL_SB_ACCUM(x, c) \ + do { \ + if((x)->subpointer < ((x)->subbuffer + sizeof((x)->subbuffer))) \ + *(x)->subpointer++ = (c); \ } while(0) -#define CURL_SB_GET(x) ((*x->subpointer++) & 0xff) -#define CURL_SB_LEN(x) (x->subend - x->subpointer) +#define CURL_SB_GET(x) ((*(x)->subpointer++) & 0xff) +#define CURL_SB_LEN(x) ((x)->subend - (x)->subpointer) /* For posterity: #define CURL_SB_PEEK(x) ((*x->subpointer)&0xff) #define CURL_SB_EOF(x) (x->subpointer >= x->subend) */ -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define printoption(a, b, c, d) Curl_nop_stmt -#endif - /* For negotiation compliant to RFC 1143 */ #define CURL_NO 0 #define CURL_YES 1 @@ -119,8 +114,8 @@ int himq[256]; int him_preferred[256]; int subnegotiation[256]; - char *subopt_ttype; /* Set with suboption TTYPE */ - char *subopt_xdisploc; /* Set with suboption XDISPLOC */ + const char *subopt_ttype; /* Set with suboption TTYPE */ + const char *subopt_xdisploc; /* Set with suboption XDISPLOC */ unsigned short subopt_wsx; /* Set with suboption NAWS */ unsigned short subopt_wsy; /* Set with suboption NAWS */ TelnetReceive telrcv_state; @@ -132,67 +127,44 @@ unsigned char *subpointer, *subend; /* buffer for sub-options */ }; -static CURLcode telrcv(struct Curl_easy *data, - struct TELNET *tn, - const unsigned char *inbuf, /* Data received from - socket */ - ssize_t count); /* Number of bytes - received */ - -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifndef CURLVERBOSE +#define printoption(a, b, c, d) Curl_nop_stmt +#else static void printoption(struct Curl_easy *data, - const char *direction, - int cmd, int option); -#endif + const char *direction, int cmd, int option) +{ + if(data->set.verbose) { + if(cmd == CURL_IAC) { + if(CURL_TELCMD_OK(option)) + infof(data, "%s IAC %s", direction, CURL_TELCMD(option)); + else + infof(data, "%s IAC %d", direction, option); + } + else { + const char *fmt = (cmd == CURL_WILL) ? "WILL" : + (cmd == CURL_WONT) ? "WONT" : + (cmd == CURL_DO) ? "DO" : + (cmd == CURL_DONT) ? "DONT" : 0; + if(fmt) { + const char *opt; + if(CURL_TELOPT_OK(option)) + opt = CURL_TELOPT(option); + else if(option == CURL_TELOPT_EXOPL) + opt = "EXOPL"; + else + opt = NULL; -static void send_negotiation(struct Curl_easy *data, int cmd, int option); -static void set_local_option(struct Curl_easy *data, struct TELNET *tn, - int option, int newstate); -static void set_remote_option(struct Curl_easy *data, struct TELNET *tn, - int option, int newstate); - -static void printsub(struct Curl_easy *data, - int direction, unsigned char *pointer, - size_t length); -static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn); -static void sendsuboption(struct Curl_easy *data, - struct TELNET *tn, int option); - -static CURLcode telnet_do(struct Curl_easy *data, bool *done); -static CURLcode telnet_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode send_telnet_data(struct Curl_easy *data, - struct TELNET *tn, - char *buffer, ssize_t nread); - -/* - * TELNET protocol handler. - */ - -const struct Curl_handler Curl_handler_telnet = { - "telnet", /* scheme */ - ZERO_NULL, /* setup_connection */ - telnet_do, /* do_it */ - telnet_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_TELNET, /* defport */ - CURLPROTO_TELNET, /* protocol */ - CURLPROTO_TELNET, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ -}; + if(opt) + infof(data, "%s %s %s", direction, fmt, opt); + else + infof(data, "%s %s %d", direction, fmt, option); + } + else + infof(data, "%s %d %d", direction, cmd, option); + } + } +} +#endif /* !CURLVERBOSE */ static void telnet_easy_dtor(void *key, size_t klen, void *entry) { @@ -239,8 +211,8 @@ */ tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES; - /* Set the subnegotiation fields to send information - just after negotiation passed (do/will) + /* Set the subnegotiation fields to send information after negotiation + passed (do/will) Default values are (0,0) initialized by calloc. According to the RFC1013 it is valid: @@ -255,59 +227,6 @@ return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor); } -static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn) -{ - int i; - - for(i = 0; i < CURL_NTELOPTS; i++) { - if(i == CURL_TELOPT_ECHO) - continue; - - if(tn->us_preferred[i] == CURL_YES) - set_local_option(data, tn, i, CURL_YES); - - if(tn->him_preferred[i] == CURL_YES) - set_remote_option(data, tn, i, CURL_YES); - } -} - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void printoption(struct Curl_easy *data, - const char *direction, int cmd, int option) -{ - if(data->set.verbose) { - if(cmd == CURL_IAC) { - if(CURL_TELCMD_OK(option)) - infof(data, "%s IAC %s", direction, CURL_TELCMD(option)); - else - infof(data, "%s IAC %d", direction, option); - } - else { - const char *fmt = (cmd == CURL_WILL) ? "WILL" : - (cmd == CURL_WONT) ? "WONT" : - (cmd == CURL_DO) ? "DO" : - (cmd == CURL_DONT) ? "DONT" : 0; - if(fmt) { - const char *opt; - if(CURL_TELOPT_OK(option)) - opt = CURL_TELOPT(option); - else if(option == CURL_TELOPT_EXOPL) - opt = "EXOPL"; - else - opt = NULL; - - if(opt) - infof(data, "%s %s %s", direction, fmt, opt); - else - infof(data, "%s %s %d", direction, fmt, option); - } - else - infof(data, "%s %d %d", direction, cmd, option); - } - } -} -#endif - static void send_negotiation(struct Curl_easy *data, int cmd, int option) { unsigned char buf[3]; @@ -400,6 +319,95 @@ } } +static void set_local_option(struct Curl_easy *data, struct TELNET *tn, + int option, int newstate) +{ + if(newstate == CURL_YES) { + switch(tn->us[option]) { + case CURL_NO: + tn->us[option] = CURL_WANTYES; + send_negotiation(data, CURL_WILL, option); + break; + + case CURL_YES: + /* Already enabled */ + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Already negotiating for CURL_YES, queue the request */ + tn->usq[option] = CURL_OPPOSITE; + break; + case CURL_OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Error: already negotiating for enable */ + break; + case CURL_OPPOSITE: + tn->usq[option] = CURL_EMPTY; + break; + } + break; + } + } + else { /* NO */ + switch(tn->us[option]) { + case CURL_NO: + /* Already disabled */ + break; + + case CURL_YES: + tn->us[option] = CURL_WANTNO; + send_negotiation(data, CURL_WONT, option); + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Already negotiating for NO */ + break; + case CURL_OPPOSITE: + tn->usq[option] = CURL_EMPTY; + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->usq[option] = CURL_OPPOSITE; + break; + case CURL_OPPOSITE: + break; + } + break; + } + } +} + +static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn) +{ + int i; + + for(i = 0; i < CURL_NTELOPTS; i++) { + if(i == CURL_TELOPT_ECHO) + continue; + + if(tn->us_preferred[i] == CURL_YES) + set_local_option(data, tn, i, CURL_YES); + + if(tn->him_preferred[i] == CURL_YES) + set_remote_option(data, tn, i, CURL_YES); + } +} + static void rec_will(struct Curl_easy *data, struct TELNET *tn, int option) { switch(tn->him[option]) { @@ -486,181 +494,10 @@ } } -static void set_local_option(struct Curl_easy *data, struct TELNET *tn, - int option, int newstate) -{ - if(newstate == CURL_YES) { - switch(tn->us[option]) { - case CURL_NO: - tn->us[option] = CURL_WANTYES; - send_negotiation(data, CURL_WILL, option); - break; - - case CURL_YES: - /* Already enabled */ - break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Already negotiating for CURL_YES, queue the request */ - tn->usq[option] = CURL_OPPOSITE; - break; - case CURL_OPPOSITE: - /* Error: already queued an enable request */ - break; - } - break; - - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Error: already negotiating for enable */ - break; - case CURL_OPPOSITE: - tn->usq[option] = CURL_EMPTY; - break; - } - break; - } - } - else { /* NO */ - switch(tn->us[option]) { - case CURL_NO: - /* Already disabled */ - break; - - case CURL_YES: - tn->us[option] = CURL_WANTNO; - send_negotiation(data, CURL_WONT, option); - break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Already negotiating for NO */ - break; - case CURL_OPPOSITE: - tn->usq[option] = CURL_EMPTY; - break; - } - break; - - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->usq[option] = CURL_OPPOSITE; - break; - case CURL_OPPOSITE: - break; - } - break; - } - } -} - -static void rec_do(struct Curl_easy *data, struct TELNET *tn, int option) -{ - switch(tn->us[option]) { - case CURL_NO: - if(tn->us_preferred[option] == CURL_YES) { - tn->us[option] = CURL_YES; - send_negotiation(data, CURL_WILL, option); - if(tn->subnegotiation[option] == CURL_YES) - /* transmission of data option */ - sendsuboption(data, tn, option); - } - else if(tn->subnegotiation[option] == CURL_YES) { - /* send information to achieve this option */ - tn->us[option] = CURL_YES; - send_negotiation(data, CURL_WILL, option); - sendsuboption(data, tn, option); - } - else - send_negotiation(data, CURL_WONT, option); - break; - - case CURL_YES: - /* Already enabled */ - break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Error: DONT answered by WILL */ - tn->us[option] = CURL_NO; - break; - case CURL_OPPOSITE: - /* Error: DONT answered by WILL */ - tn->us[option] = CURL_YES; - tn->usq[option] = CURL_EMPTY; - break; - } - break; - - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->us[option] = CURL_YES; - if(tn->subnegotiation[option] == CURL_YES) { - /* transmission of data option */ - sendsuboption(data, tn, option); - } - break; - case CURL_OPPOSITE: - tn->us[option] = CURL_WANTNO; - tn->himq[option] = CURL_EMPTY; - send_negotiation(data, CURL_WONT, option); - break; - } - break; - } -} - -static void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option) -{ - switch(tn->us[option]) { - case CURL_NO: - /* Already disabled */ - break; - - case CURL_YES: - tn->us[option] = CURL_NO; - send_negotiation(data, CURL_WONT, option); - break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->us[option] = CURL_NO; - break; - - case CURL_OPPOSITE: - tn->us[option] = CURL_WANTYES; - tn->usq[option] = CURL_EMPTY; - send_negotiation(data, CURL_WILL, option); - break; - } - break; - - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->us[option] = CURL_NO; - break; - case CURL_OPPOSITE: - tn->us[option] = CURL_NO; - tn->usq[option] = CURL_EMPTY; - break; - } - break; - } -} - static void printsub(struct Curl_easy *data, - int direction, /* '<' or '>' */ - unsigned char *pointer, /* where suboption data is */ - size_t length) /* length of suboption data */ + int direction, /* '<' or '>' */ + const unsigned char *pointer, /* ptr to suboption data */ + size_t length) /* suboption data length */ { if(data->set.verbose) { unsigned int i = 0; @@ -770,6 +607,220 @@ } } +/* Escape and send a telnet data block */ +static CURLcode send_telnet_data(struct Curl_easy *data, + struct TELNET *tn, + const char *buffer, ssize_t nread) +{ + size_t i, outlen; + const unsigned char *outbuf; + CURLcode result = CURLE_OK; + size_t bytes_written; + size_t total_written = 0; + struct connectdata *conn = data->conn; + + DEBUGASSERT(tn); + DEBUGASSERT(nread > 0); + if(nread < 0) + return CURLE_TOO_LARGE; + + if(memchr(buffer, CURL_IAC, nread)) { + /* only use the escape buffer when necessary */ + curlx_dyn_reset(&tn->out); + + for(i = 0; i < (size_t)nread && !result; i++) { + result = curlx_dyn_addn(&tn->out, &buffer[i], 1); + if(!result && ((unsigned char)buffer[i] == CURL_IAC)) + /* IAC is FF in hex */ + result = curlx_dyn_addn(&tn->out, "\xff", 1); + } + + outlen = curlx_dyn_len(&tn->out); + outbuf = curlx_dyn_uptr(&tn->out); + } + else { + outlen = (size_t)nread; + outbuf = (const unsigned char *)buffer; + } + while(!result && total_written < outlen) { + /* Make sure socket is writable to avoid EWOULDBLOCK condition */ + struct pollfd pfd[1]; + pfd[0].fd = conn->sock[FIRSTSOCKET]; + pfd[0].events = POLLOUT; + switch(Curl_poll(pfd, 1, -1)) { + case -1: /* error, abort writing */ + case 0: /* timeout (will never happen) */ + result = CURLE_SEND_ERROR; + break; + default: /* write! */ + bytes_written = 0; + result = Curl_xfer_send(data, outbuf + total_written, + outlen - total_written, FALSE, &bytes_written); + total_written += bytes_written; + break; + } + } + + return result; +} + +/* + * sendsuboption() + * + * Send suboption information to the server side. + */ +static void sendsuboption(struct Curl_easy *data, + struct TELNET *tn, int option) +{ + ssize_t bytes_written; + int err; + unsigned short x, y; + const unsigned char *uc1, *uc2; + struct connectdata *conn = data->conn; + + switch(option) { + case CURL_TELOPT_NAWS: + /* We prepare data to be sent */ + CURL_SB_CLEAR(tn); + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SB); + CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS); + /* We must deal either with little or big endian processors */ + /* Window size must be sent according to the 'network order' */ + x = htons(tn->subopt_wsx); + y = htons(tn->subopt_wsy); + uc1 = (const unsigned char *)&x; + uc2 = (const unsigned char *)&y; + CURL_SB_ACCUM(tn, uc1[0]); + CURL_SB_ACCUM(tn, uc1[1]); + CURL_SB_ACCUM(tn, uc2[0]); + CURL_SB_ACCUM(tn, uc2[1]); + + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SE); + CURL_SB_TERM(tn); + /* data suboption is now ready */ + + printsub(data, '>', (const unsigned char *)tn->subbuffer + 2, + CURL_SB_LEN(tn) - 2); + + /* we send the header of the suboption... */ + bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + } + /* ... then the window size with the send_telnet_data() function + to deal with 0xFF cases ... */ + send_telnet_data(data, tn, (const char *)tn->subbuffer + 3, 4); + /* ... and the footer */ + bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + } + break; + } +} + +static void rec_do(struct Curl_easy *data, struct TELNET *tn, int option) +{ + switch(tn->us[option]) { + case CURL_NO: + if(tn->us_preferred[option] == CURL_YES) { + tn->us[option] = CURL_YES; + send_negotiation(data, CURL_WILL, option); + if(tn->subnegotiation[option] == CURL_YES) + /* transmission of data option */ + sendsuboption(data, tn, option); + } + else if(tn->subnegotiation[option] == CURL_YES) { + /* send information to achieve this option */ + tn->us[option] = CURL_YES; + send_negotiation(data, CURL_WILL, option); + sendsuboption(data, tn, option); + } + else + send_negotiation(data, CURL_WONT, option); + break; + + case CURL_YES: + /* Already enabled */ + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Error: DONT answered by WILL */ + tn->us[option] = CURL_NO; + break; + case CURL_OPPOSITE: + /* Error: DONT answered by WILL */ + tn->us[option] = CURL_YES; + tn->usq[option] = CURL_EMPTY; + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->us[option] = CURL_YES; + if(tn->subnegotiation[option] == CURL_YES) { + /* transmission of data option */ + sendsuboption(data, tn, option); + } + break; + case CURL_OPPOSITE: + tn->us[option] = CURL_WANTNO; + tn->himq[option] = CURL_EMPTY; + send_negotiation(data, CURL_WONT, option); + break; + } + break; + } +} + +static void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option) +{ + switch(tn->us[option]) { + case CURL_NO: + /* Already disabled */ + break; + + case CURL_YES: + tn->us[option] = CURL_NO; + send_negotiation(data, CURL_WONT, option); + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->us[option] = CURL_NO; + break; + + case CURL_OPPOSITE: + tn->us[option] = CURL_WANTYES; + tn->usq[option] = CURL_EMPTY; + send_negotiation(data, CURL_WILL, option); + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->us[option] = CURL_NO; + break; + case CURL_OPPOSITE: + tn->us[option] = CURL_NO; + tn->usq[option] = CURL_EMPTY; + break; + } + break; + } +} + static bool str_is_nonascii(const char *str) { char c; @@ -808,9 +859,9 @@ for(head = data->set.telnet_options; head && !result; head = head->next) { size_t olen; - char *option = head->data; - char *arg; - char *sep = strchr(option, '='); + const char *option = head->data; + const char *arg; + const char *sep = strchr(option, '='); if(sep) { olen = sep - option; arg = ++sep; @@ -910,7 +961,7 @@ /* if the option contains an IAC code, it should be escaped in the output, but as we cannot think of any legit way to send that as part of the content we - rather just ban its use instead */ + rather ban its use instead */ static bool bad_option(const char *data) { return !data || !!strchr(data, CURL_IAC); @@ -934,7 +985,8 @@ if(!CURL_SB_LEN(tn)) /* ignore empty suboption */ return CURLE_OK; - printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2); + printsub(data, '<', (const unsigned char *)tn->subbuffer, + CURL_SB_LEN(tn) + 2); switch(CURL_SB_GET(tn)) { case CURL_TELOPT_TTYPE: if(bad_option(tn->subopt_ttype)) @@ -985,7 +1037,7 @@ return CURLE_BAD_FUNCTION_ARGUMENT; /* Add the variable if it fits */ if(len + tmplen < (int)sizeof(temp) - 6) { - char *s = strchr(v->data, ','); + const char *s = strchr(v->data, ','); if(!s) len += curl_msnprintf((char *)&temp[len], sizeof(temp) - len, "%c%s", CURL_NEW_ENV_VAR, v->data); @@ -1011,65 +1063,6 @@ return CURLE_OK; } -/* - * sendsuboption() - * - * Send suboption information to the server side. - */ -static void sendsuboption(struct Curl_easy *data, - struct TELNET *tn, int option) -{ - ssize_t bytes_written; - int err; - unsigned short x, y; - unsigned char *uc1, *uc2; - struct connectdata *conn = data->conn; - - switch(option) { - case CURL_TELOPT_NAWS: - /* We prepare data to be sent */ - CURL_SB_CLEAR(tn); - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, CURL_SB); - CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS); - /* We must deal either with little or big endian processors */ - /* Window size must be sent according to the 'network order' */ - x = htons(tn->subopt_wsx); - y = htons(tn->subopt_wsy); - uc1 = (unsigned char *)&x; - uc2 = (unsigned char *)&y; - CURL_SB_ACCUM(tn, uc1[0]); - CURL_SB_ACCUM(tn, uc1[1]); - CURL_SB_ACCUM(tn, uc2[0]); - CURL_SB_ACCUM(tn, uc2[1]); - - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, CURL_SE); - CURL_SB_TERM(tn); - /* data suboption is now ready */ - - printsub(data, '>', (unsigned char *)tn->subbuffer + 2, - CURL_SB_LEN(tn) - 2); - - /* we send the header of the suboption... */ - bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } - /* ... then the window size with the send_telnet_data() function - to deal with 0xFF cases ... */ - send_telnet_data(data, tn, (char *)tn->subbuffer + 3, 4); - /* ... and the footer */ - bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } - break; - } -} - static CURLcode telrcv(struct Curl_easy *data, struct TELNET *tn, const unsigned char *inbuf, /* Data received from @@ -1224,63 +1217,6 @@ return CURLE_OK; } -/* Escape and send a telnet data block */ -static CURLcode send_telnet_data(struct Curl_easy *data, - struct TELNET *tn, - char *buffer, ssize_t nread) -{ - size_t i, outlen; - unsigned char *outbuf; - CURLcode result = CURLE_OK; - size_t bytes_written; - size_t total_written = 0; - struct connectdata *conn = data->conn; - - DEBUGASSERT(tn); - DEBUGASSERT(nread > 0); - if(nread < 0) - return CURLE_TOO_LARGE; - - if(memchr(buffer, CURL_IAC, nread)) { - /* only use the escape buffer when necessary */ - curlx_dyn_reset(&tn->out); - - for(i = 0; i < (size_t)nread && !result; i++) { - result = curlx_dyn_addn(&tn->out, &buffer[i], 1); - if(!result && ((unsigned char)buffer[i] == CURL_IAC)) - /* IAC is FF in hex */ - result = curlx_dyn_addn(&tn->out, "\xff", 1); - } - - outlen = curlx_dyn_len(&tn->out); - outbuf = curlx_dyn_uptr(&tn->out); - } - else { - outlen = (size_t)nread; - outbuf = (unsigned char *)buffer; - } - while(!result && total_written < outlen) { - /* Make sure socket is writable to avoid EWOULDBLOCK condition */ - struct pollfd pfd[1]; - pfd[0].fd = conn->sock[FIRSTSOCKET]; - pfd[0].events = POLLOUT; - switch(Curl_poll(pfd, 1, -1)) { - case -1: /* error, abort writing */ - case 0: /* timeout (will never happen) */ - result = CURLE_SEND_ERROR; - break; - default: /* write! */ - bytes_written = 0; - result = Curl_xfer_send(data, outbuf + total_written, - outlen - total_written, FALSE, &bytes_written); - total_written += bytes_written; - break; - } - } - - return result; -} - static CURLcode telnet_done(struct Curl_easy *data, CURLcode status, bool premature) { @@ -1330,9 +1266,9 @@ #ifdef USE_WINSOCK /* We want to wait for both stdin and the socket. Since - ** the select() function in Winsock only works on sockets - ** we have to use the WaitForMultipleObjects() call. - */ + * the select() function in Winsock only works on sockets + * we have to use the WaitForMultipleObjects() call. + */ /* First, create a sockets event object */ event_handle = WSACreateEvent(); @@ -1342,8 +1278,7 @@ } /* Tell Winsock what events we want to listen to */ - if(WSAEventSelect(sockfd, event_handle, FD_READ | FD_CLOSE) == - SOCKET_ERROR) { + if(WSAEventSelect(sockfd, event_handle, FD_READ | FD_CLOSE) != 0) { WSACloseEvent(event_handle); return CURLE_RECV_ERROR; } @@ -1358,7 +1293,7 @@ /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it, else use the old WaitForMultipleObjects() way */ if(GetFileType(stdin_handle) == FILE_TYPE_PIPE || data->set.is_fread_set) { - /* Do not wait for stdin_handle, just wait for event_handle */ + /* Do not wait for stdin_handle, wait for event_handle */ obj_count = 1; /* Check stdin_handle per 100 milliseconds */ wait_timeout = 100; @@ -1441,7 +1376,7 @@ case WAIT_OBJECT_0: { events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) { + if(WSAEnumNetworkEvents(sockfd, event_handle, &events) != 0) { err = SOCKERRNO; if(err != SOCKEINPROGRESS) { infof(data, "WSAEnumNetworkEvents failed (%d)", err); @@ -1630,4 +1565,28 @@ return result; } -#endif + +/* + * TELNET protocol handler. + */ +const struct Curl_protocol Curl_protocol_telnet = { + ZERO_NULL, /* setup_connection */ + telnet_do, /* do_it */ + telnet_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif /* !CURL_DISABLE_TELNET */
diff --git a/Utilities/cmcurl/lib/telnet.h b/Utilities/cmcurl/lib/telnet.h index 30782d8..3848fff 100644 --- a/Utilities/cmcurl/lib/telnet.h +++ b/Utilities/cmcurl/lib/telnet.h
@@ -24,7 +24,7 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_TELNET -extern const struct Curl_handler Curl_handler_telnet; +extern const struct Curl_protocol Curl_protocol_telnet; #endif #endif /* HEADER_CURL_TELNET_H */
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index 69571bb..6cc672d 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c
@@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "tftp.h" #ifndef CURL_DISABLE_TFTP @@ -45,13 +47,11 @@ #include <sys/param.h> #endif -#include "urldata.h" #include "cfilters.h" #include "cf-socket.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" -#include "tftp.h" #include "progress.h" #include "connect.h" #include "sockaddr.h" /* required for Curl_sockaddr_storage */ @@ -142,49 +142,6 @@ BIT(remote_pinned); }; -/* Forward declarations */ -static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event); -static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event); -static CURLcode tftp_connect(struct Curl_easy *data, bool *done); -static CURLcode tftp_do(struct Curl_easy *data, bool *done); -static CURLcode tftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode tftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode tftp_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode tftp_translate_code(tftp_error_t error); - -/* - * TFTP protocol handler. - */ -const struct Curl_handler Curl_handler_tftp = { - "tftp", /* scheme */ - tftp_setup_connection, /* setup_connection */ - tftp_do, /* do_it */ - tftp_done, /* done */ - ZERO_NULL, /* do_more */ - tftp_connect, /* connect_it */ - tftp_multi_statemach, /* connecting */ - tftp_doing, /* doing */ - tftp_pollset, /* proto_pollset */ - tftp_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_TFTP, /* defport */ - CURLPROTO_TFTP, /* protocol */ - CURLPROTO_TFTP, /* family */ - PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */ -}; - /********************************************************** * * tftp_set_timeouts - @@ -199,10 +156,9 @@ { time_t timeout; timediff_t timeout_ms; - bool start = (state->state == TFTP_STATE_START); /* Compute drop-dead time */ - timeout_ms = Curl_timeleft_ms(state->data, start); + timeout_ms = Curl_timeleft_ms(state->data); if(timeout_ms < 0) { /* time-out, bail out, go home */ @@ -219,7 +175,7 @@ /* Average reposting an ACK after 5 seconds */ state->retry_max = (int)timeout / 5; - /* But bound the total number */ + /* Bound the total number */ if(state->retry_max < 3) state->retry_max = 3; @@ -344,8 +300,8 @@ return CURLE_TFTP_ILLEGAL; } - state->blksize = (int)blksize; - infof(data, "blksize parsed from OACK (%d) requested (%d)", + state->blksize = (unsigned int)blksize; + infof(data, "blksize parsed from OACK (%u) requested (%u)", state->blksize, state->requested_blksize); } else if(checkprefix(TFTP_OPTION_TSIZE, option)) { @@ -383,15 +339,167 @@ return CURLE_OK; } +/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16-bit + boundary */ +#define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff) + +/********************************************************** + * + * tftp_tx + * + * Event handler for the TX state + * + **********************************************************/ +static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) +{ + struct Curl_easy *data = state->data; + ssize_t sbytes; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + size_t cb; /* Bytes currently read */ + char buffer[STRERROR_LEN]; + char *bufptr; + bool eos; + + switch(event) { + + case TFTP_EVENT_ACK: + case TFTP_EVENT_OACK: + if(event == TFTP_EVENT_ACK) { + /* Ack the packet */ + int rblock = getrpacketblock(&state->rpacket); + + if(rblock != state->block && + /* There is a bug in tftpd-hpa that causes it to send us an ACK for + * 65535 when the block number wraps to 0. To handle it, when we are + * expecting 0, also accept 65535. See + * https://www.syslinux.org/archives/2010-September/015612.html + * */ + !(state->block == 0 && rblock == 65535)) { + /* This is not the expected block. Log it and up the retry counter */ + infof(data, "Received ACK for block %d, expecting %d", + rblock, state->block); + state->retries++; + /* Bail out if over the maximum */ + if(state->retries > state->retry_max) { + failf(data, "tftp_tx: giving up waiting for block %d ack", + state->block); + result = CURLE_SEND_ERROR; + } + else { + /* Re-send the data packet */ + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes < 0) { + failf(data, "%s", + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + result = CURLE_SEND_ERROR; + } + } + + return result; + } + /* This is the expected packet. Reset the counters and send the next + block */ + state->rx_time = time(NULL); + state->block++; + } + else + state->block = 1; /* first data block is 1 when using OACK */ + + state->retries = 0; + setpacketevent(&state->spacket, TFTP_EVENT_DATA); + setpacketblock(&state->spacket, state->block); + if(state->block > 1 && state->sbytes < state->blksize) { + state->state = TFTP_STATE_FIN; + return CURLE_OK; + } + + /* TFTP considers data block size < 512 bytes as an end of session, so + * in some cases we must wait for additional data to build full (512 bytes) + * data block. + * */ + state->sbytes = 0; + bufptr = (char *)state->spacket.data + 4; + do { + result = Curl_client_read(data, bufptr, state->blksize - state->sbytes, + &cb, &eos); + if(result) + return result; + state->sbytes += cb; + bufptr += cb; + } while(state->sbytes < state->blksize && cb); + + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes < 0) { + failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + /* Update the progress meter */ + k->writebytecount += state->sbytes; + Curl_pgrs_upload_inc(data, state->sbytes); + break; + + case TFTP_EVENT_TIMEOUT: + /* Increment the retry counter and log the timeout */ + state->retries++; + infof(data, "Timeout waiting for block %d ACK. " + "Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); + /* Decide if we have had enough */ + if(state->retries > state->retry_max) { + state->error = TFTP_ERR_TIMEOUT; + state->state = TFTP_STATE_FIN; + } + else { + /* Re-send the data packet */ + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes < 0) { + failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + /* since this was a re-send, we remain at the still byte position */ + Curl_pgrsSetUploadCounter(data, k->writebytecount); + } + break; + + case TFTP_EVENT_ERROR: + state->state = TFTP_STATE_FIN; + setpacketevent(&state->spacket, TFTP_EVENT_ERROR); + setpacketblock(&state->spacket, state->block); + (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* do not bother with the return code, but if the socket is still up we + * should be a good TFTP client and let the server know we are done */ + state->state = TFTP_STATE_FIN; + break; + + default: + failf(data, "tftp_tx: internal error, event: %i", (int)event); + break; + } + + return result; +} + static CURLcode tftp_connect_for_tx(struct tftp_conn *state, tftp_event_t event) { CURLcode result; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - struct Curl_easy *data = state->data; - infof(data, "%s", "Connected for transmit"); -#endif + infof(state->data, "%s", "Connected for transmit"); + state->state = TFTP_STATE_TX; result = tftp_set_timeouts(state); if(result) @@ -399,170 +507,6 @@ return tftp_tx(state, event); } -static CURLcode tftp_connect_for_rx(struct tftp_conn *state, - tftp_event_t event) -{ - CURLcode result; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - struct Curl_easy *data = state->data; - - infof(data, "%s", "Connected for receive"); -#endif - state->state = TFTP_STATE_RX; - result = tftp_set_timeouts(state); - if(result) - return result; - return tftp_rx(state, event); -} - -static CURLcode tftp_send_first(struct tftp_conn *state, - tftp_event_t event) -{ - size_t sbytes; - ssize_t senddata; - const char *mode = "octet"; - char *filename; - struct Curl_easy *data = state->data; - const struct Curl_sockaddr_ex *remote_addr = NULL; - CURLcode result = CURLE_OK; - - /* Set ASCII mode if -B flag was used */ - if(data->state.prefer_ascii) - mode = "netascii"; - - switch(event) { - - case TFTP_EVENT_INIT: /* Send the first packet out */ - case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ - /* Increment the retry counter, quit if over the limit */ - state->retries++; - if(state->retries > state->retry_max) { - state->error = TFTP_ERR_NORESPONSE; - state->state = TFTP_STATE_FIN; - return result; - } - - if(data->state.upload) { - /* If we are uploading, send an WRQ */ - setpacketevent(&state->spacket, TFTP_EVENT_WRQ); - if(data->state.infilesize != -1) - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - else { - /* If we are downloading, send an RRQ */ - setpacketevent(&state->spacket, TFTP_EVENT_RRQ); - } - /* As RFC3617 describes the separator slash is not actually part of the - filename so we skip the always-present first letter of the path - string. */ - if(!state->data->state.up.path[1]) { - failf(data, "Missing filename"); - return CURLE_TFTP_ILLEGAL; - } - result = Curl_urldecode(&state->data->state.up.path[1], 0, - &filename, NULL, REJECT_ZERO); - if(result) - return result; - - if(strlen(filename) > (state->blksize - strlen(mode) - 4)) { - failf(data, "TFTP filename too long"); - curlx_free(filename); - return CURLE_TFTP_ILLEGAL; /* too long filename field */ - } - - curl_msnprintf((char *)state->spacket.data + 2, - state->blksize, - "%s%c%s%c", filename, '\0', mode, '\0'); - sbytes = 4 + strlen(filename) + strlen(mode); - curlx_free(filename); - - /* optional addition of TFTP options */ - if(!data->set.tftp_no_options) { - char buf[64]; - /* add tsize option */ - curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, - data->state.upload && (data->state.infilesize != -1) ? - data->state.infilesize : 0); - - result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, buf); - - /* add blksize option */ - curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, buf); - - /* add timeout option */ - curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, buf); - - if(result != CURLE_OK) { - failf(data, "TFTP buffer too small for options"); - return CURLE_TFTP_ILLEGAL; - } - } - - /* the typecase for the 3rd argument is mostly for systems that do - not have a size_t argument, like older unixes that want an 'int' */ -#ifdef __AMIGA__ -#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) -#else -#define CURL_SENDTO_ARG5(x) (x) -#endif - remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET); - if(!remote_addr) - return CURLE_FAILED_INIT; - - senddata = sendto(state->sockfd, (void *)state->spacket.data, - (SEND_TYPE_ARG3)sbytes, 0, - CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr), - (curl_socklen_t)remote_addr->addrlen); - if(senddata != (ssize_t)sbytes) { - char buffer[STRERROR_LEN]; - failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - break; - - case TFTP_EVENT_OACK: - if(data->state.upload) { - result = tftp_connect_for_tx(state, event); - } - else { - result = tftp_connect_for_rx(state, event); - } - break; - - case TFTP_EVENT_ACK: /* Connected for transmit */ - result = tftp_connect_for_tx(state, event); - break; - - case TFTP_EVENT_DATA: /* Connected for receive */ - result = tftp_connect_for_rx(state, event); - break; - - case TFTP_EVENT_ERROR: - state->state = TFTP_STATE_FIN; - break; - - default: - failf(state->data, "tftp_send_first: internal error"); - return CURLE_TFTP_ILLEGAL; - } - - return result; -} - -/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit - boundary */ -#define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff) - /********************************************************** * * tftp_rx @@ -592,7 +536,7 @@ infof(data, "Received last DATA packet block %d again.", rblock); } else { - /* totally unexpected, just log it */ + /* totally unexpected, log it */ infof(data, "Received unexpected DATA packet block %d, expecting block %d", rblock, NEXT_BLOCKNUM(state->block)); @@ -685,151 +629,159 @@ return CURLE_OK; } -/********************************************************** - * - * tftp_tx - * - * Event handler for the TX state - * - **********************************************************/ -static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) +static CURLcode tftp_connect_for_rx(struct tftp_conn *state, + tftp_event_t event) { + CURLcode result; + + infof(state->data, "%s", "Connected for receive"); + + state->state = TFTP_STATE_RX; + result = tftp_set_timeouts(state); + if(result) + return result; + return tftp_rx(state, event); +} + +static CURLcode tftp_send_first(struct tftp_conn *state, + tftp_event_t event) +{ + size_t sbytes; + ssize_t senddata; + const char *mode = "octet"; + char *filename; struct Curl_easy *data = state->data; - ssize_t sbytes; + const struct Curl_sockaddr_ex *remote_addr = NULL; CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - size_t cb; /* Bytes currently read */ - char buffer[STRERROR_LEN]; - char *bufptr; - bool eos; + + /* Set ASCII mode if -B flag was used */ + if(data->state.prefer_ascii) + mode = "netascii"; switch(event) { - case TFTP_EVENT_ACK: - case TFTP_EVENT_OACK: - if(event == TFTP_EVENT_ACK) { - /* Ack the packet */ - int rblock = getrpacketblock(&state->rpacket); - - if(rblock != state->block && - /* There is a bug in tftpd-hpa that causes it to send us an ack for - * 65535 when the block number wraps to 0. So when we are expecting - * 0, also accept 65535. See - * https://www.syslinux.org/archives/2010-September/015612.html - * */ - !(state->block == 0 && rblock == 65535)) { - /* This is not the expected block. Log it and up the retry counter */ - infof(data, "Received ACK for block %d, expecting %d", - rblock, state->block); - state->retries++; - /* Bail out if over the maximum */ - if(state->retries > state->retry_max) { - failf(data, "tftp_tx: giving up waiting for block %d ack", - state->block); - result = CURLE_SEND_ERROR; - } - else { - /* Re-send the data packet */ - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* Check all sbytes were sent */ - if(sbytes < 0) { - failf(data, "%s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - result = CURLE_SEND_ERROR; - } - } - - return result; - } - /* This is the expected packet. Reset the counters and send the next - block */ - state->rx_time = time(NULL); - state->block++; - } - else - state->block = 1; /* first data block is 1 when using OACK */ - - state->retries = 0; - setpacketevent(&state->spacket, TFTP_EVENT_DATA); - setpacketblock(&state->spacket, state->block); - if(state->block > 1 && state->sbytes < state->blksize) { + case TFTP_EVENT_INIT: /* Send the first packet out */ + case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ + /* Increment the retry counter, quit if over the limit */ + state->retries++; + if(state->retries > state->retry_max) { + state->error = TFTP_ERR_NORESPONSE; state->state = TFTP_STATE_FIN; - return CURLE_OK; + return result; } - /* TFTP considers data block size < 512 bytes as an end of session. So - * in some cases we must wait for additional data to build full (512 bytes) - * data block. - * */ - state->sbytes = 0; - bufptr = (char *)state->spacket.data + 4; - do { - result = Curl_client_read(data, bufptr, state->blksize - state->sbytes, - &cb, &eos); - if(result) - return result; - state->sbytes += cb; - bufptr += cb; - } while(state->sbytes < state->blksize && cb); + if(data->state.upload) { + /* If we are uploading, send an WRQ */ + setpacketevent(&state->spacket, TFTP_EVENT_WRQ); + if(data->state.infilesize != -1) + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + else { + /* If we are downloading, send an RRQ */ + setpacketevent(&state->spacket, TFTP_EVENT_RRQ); + } + /* As RFC3617 describes the separator slash is not actually part of the + filename so we skip the always-present first letter of the path + string. */ + if(!state->data->state.up.path[1]) { + failf(data, "Missing filename"); + return CURLE_TFTP_ILLEGAL; + } + result = Curl_urldecode(&state->data->state.up.path[1], 0, + &filename, NULL, REJECT_ZERO); + if(result) + return result; - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* Check all sbytes were sent */ - if(sbytes < 0) { + if(strlen(filename) + strlen(mode) + 4 > state->blksize) { + failf(data, "TFTP filename too long"); + curlx_free(filename); + return CURLE_TFTP_ILLEGAL; /* too long filename field */ + } + + sbytes = 2 + + curl_msnprintf((char *)state->spacket.data + 2, + state->blksize, + "%s%c%s%c", filename, '\0', mode, '\0'); + curlx_free(filename); + + /* optional addition of TFTP options */ + if(!data->set.tftp_no_options) { + char buf[64]; + /* add tsize option */ + curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, + data->state.upload && (data->state.infilesize != -1) ? + data->state.infilesize : 0); + + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, buf); + + /* add blksize option */ + curl_msnprintf(buf, sizeof(buf), "%u", state->requested_blksize); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, buf); + + /* add timeout option */ + curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, buf); + + if(result != CURLE_OK) { + failf(data, "TFTP buffer too small for options"); + return CURLE_TFTP_ILLEGAL; + } + } + + /* the typecase for the 3rd argument is mostly for systems that do + not have a size_t argument, like older unixes that want an 'int' */ +#ifdef __AMIGA__ +#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) +#else +#define CURL_SENDTO_ARG5(x) (x) +#endif + remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET); + if(!remote_addr) + return CURLE_FAILED_INIT; + + senddata = sendto(state->sockfd, (void *)state->spacket.data, + (SEND_TYPE_ARG3)sbytes, 0, + CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr), + (curl_socklen_t)remote_addr->addrlen); + if(senddata != (ssize_t)sbytes) { + char buffer[STRERROR_LEN]; failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); return CURLE_SEND_ERROR; } - /* Update the progress meter */ - k->writebytecount += state->sbytes; - Curl_pgrs_upload_inc(data, state->sbytes); break; - case TFTP_EVENT_TIMEOUT: - /* Increment the retry counter and log the timeout */ - state->retries++; - infof(data, "Timeout waiting for block %d ACK. " - " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); - /* Decide if we have had enough */ - if(state->retries > state->retry_max) { - state->error = TFTP_ERR_TIMEOUT; - state->state = TFTP_STATE_FIN; + case TFTP_EVENT_OACK: + if(data->state.upload) { + result = tftp_connect_for_tx(state, event); } else { - /* Re-send the data packet */ - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* Check all sbytes were sent */ - if(sbytes < 0) { - failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - /* since this was a re-send, we remain at the still byte position */ - Curl_pgrsSetUploadCounter(data, k->writebytecount); + result = tftp_connect_for_rx(state, event); } break; + case TFTP_EVENT_ACK: /* Connected for transmit */ + result = tftp_connect_for_tx(state, event); + break; + + case TFTP_EVENT_DATA: /* Connected for receive */ + result = tftp_connect_for_rx(state, event); + break; + case TFTP_EVENT_ERROR: state->state = TFTP_STATE_FIN; - setpacketevent(&state->spacket, TFTP_EVENT_ERROR); - setpacketblock(&state->spacket, state->block); - (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* do not bother with the return code, but if the socket is still up we - * should be a good TFTP client and let the server know we are done */ - state->state = TFTP_STATE_FIN; break; default: - failf(data, "tftp_tx: internal error, event: %i", (int)(event)); - break; + failf(state->data, "tftp_send_first: internal error"); + return CURLE_TFTP_ILLEGAL; } return result; @@ -931,8 +883,8 @@ struct tftp_conn *state = entry; (void)key; (void)klen; - Curl_safefree(state->rpacket.data); - Curl_safefree(state->spacket.data); + curlx_safefree(state->rpacket.data); + curlx_safefree(state->spacket.data); curlx_free(state); } @@ -983,8 +935,8 @@ return CURLE_OUT_OF_MEMORY; } - /* we do not keep TFTP connections up basically because there is none or - * little gain for UDP */ + /* we do not keep TFTP connections up because there is none or little gain + * for UDP */ connclose(conn, "TFTP"); state->data = data; @@ -1126,7 +1078,7 @@ if(state->rbytes > 4 && (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) { result = Curl_client_write(data, CLIENTWRITE_BODY, - (char *)state->rpacket.data + 4, + (const char *)state->rpacket.data + 4, state->rbytes - 4); if(result) { tftp_state_machine(state, TFTP_EVENT_ERROR); @@ -1136,7 +1088,7 @@ break; case TFTP_EVENT_ERROR: { unsigned short error = getrpacketblock(&state->rpacket); - char *str = (char *)state->rpacket.data + 4; + const char *str = (const char *)state->rpacket.data + 4; size_t strn = state->rbytes - 4; state->error = (tftp_error_t)error; if(tftp_strnlen(str, strn) < strn) @@ -1185,8 +1137,7 @@ if(event) *event = TFTP_EVENT_NONE; - timeout_ms = Curl_timeleft_ms(state->data, - (state->state == TFTP_STATE_START)); + timeout_ms = Curl_timeleft_ms(state->data); if(timeout_ms < 0) { state->error = TFTP_ERR_TIMEOUT; state->state = TFTP_STATE_FIN; @@ -1377,4 +1328,28 @@ return CURLE_OK; } + +/* + * TFTP protocol handler. + */ +const struct Curl_protocol Curl_protocol_tftp = { + tftp_setup_connection, /* setup_connection */ + tftp_do, /* do_it */ + tftp_done, /* done */ + ZERO_NULL, /* do_more */ + tftp_connect, /* connect_it */ + tftp_multi_statemach, /* connecting */ + tftp_doing, /* doing */ + tftp_pollset, /* proto_pollset */ + tftp_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif
diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h index 12404bf..3231010 100644 --- a/Utilities/cmcurl/lib/tftp.h +++ b/Utilities/cmcurl/lib/tftp.h
@@ -24,10 +24,10 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_TFTP -extern const struct Curl_handler Curl_handler_tftp; +extern const struct Curl_protocol Curl_protocol_tftp; +#endif #define TFTP_BLKSIZE_MIN 8 #define TFTP_BLKSIZE_MAX 65464 -#endif #endif /* HEADER_CURL_TFTP_H */
diff --git a/Utilities/cmcurl/lib/thrdpool.c b/Utilities/cmcurl/lib/thrdpool.c new file mode 100644 index 0000000..22faa39 --- /dev/null +++ b/Utilities/cmcurl/lib/thrdpool.c
@@ -0,0 +1,481 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_THREADS + +#include "llist.h" +#include "curl_threads.h" +#include "curlx/timeval.h" +#include "thrdpool.h" +#ifdef CURLVERBOSE +#include "curl_trc.h" +#include "urldata.h" +#endif + + +struct thrdslot { + struct Curl_llist_node node; + struct curl_thrdpool *tpool; + curl_thread_t thread; + curl_cond_t await; + struct curltime starttime; + const char *work_description; + timediff_t work_timeout_ms; + uint32_t id; + BIT(running); + BIT(idle); +}; + +struct curl_thrdpool { + char *name; + uint64_t refcount; + curl_mutex_t lock; + curl_cond_t await; + struct Curl_llist slots; + struct Curl_llist zombies; + Curl_thrdpool_take_item_cb *fn_take; + Curl_thrdpool_process_item_cb *fn_process; + Curl_thrdpool_return_item_cb *fn_return; + void *fn_user_data; + CURLcode fatal_err; + uint32_t min_threads; + uint32_t max_threads; + uint32_t idle_time_ms; + uint32_t next_id; + BIT(aborted); + BIT(detached); +}; + +static void thrdpool_join_zombies(struct curl_thrdpool *tpool); +static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked); + +static void thrdslot_destroy(struct thrdslot *tslot) +{ + DEBUGASSERT(tslot->thread == curl_thread_t_null); + DEBUGASSERT(!tslot->running); + Curl_cond_destroy(&tslot->await); + curlx_free(tslot); +} + +static void thrdslot_done(struct thrdslot *tslot) +{ + struct curl_thrdpool *tpool = tslot->tpool; + + DEBUGASSERT(Curl_node_llist(&tslot->node) == &tpool->slots); + Curl_node_remove(&tslot->node); + tslot->running = FALSE; + Curl_llist_append(&tpool->zombies, tslot, &tslot->node); + Curl_cond_signal(&tpool->await); +} + +static CURL_THREAD_RETURN_T CURL_STDCALL thrdslot_run(void *arg) +{ + struct thrdslot *tslot = arg; + struct curl_thrdpool *tpool = tslot->tpool; + void *item; + + Curl_mutex_acquire(&tpool->lock); + DEBUGASSERT(Curl_node_llist(&tslot->node) == &tpool->slots); + for(;;) { + while(!tpool->aborted) { + tslot->work_description = NULL; + tslot->work_timeout_ms = 0; + item = tpool->fn_take(tpool->fn_user_data, &tslot->work_description, + &tslot->work_timeout_ms); + if(!item) + break; + tslot->starttime = curlx_now(); + tslot->idle = FALSE; + Curl_mutex_release(&tpool->lock); + + tpool->fn_process(item); + + Curl_mutex_acquire(&tpool->lock); + tslot->work_description = NULL; + tpool->fn_return(item, tpool->aborted ? NULL : tpool->fn_user_data); + } + + if(tpool->aborted || + (Curl_llist_count(&tpool->slots) > tpool->max_threads)) + goto out; + + tslot->idle = TRUE; + tslot->starttime = curlx_now(); + thrdpool_join_zombies(tpool); + Curl_cond_signal(&tpool->await); + /* Only wait with idle timeout when we are above the minimum + * number of threads. Otherwise short idle timeouts will keep + * on activating threads that have no means to shut down. */ + if((tpool->idle_time_ms > 0) && + (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { + CURLcode r = Curl_cond_timedwait(&tslot->await, &tpool->lock, + tpool->idle_time_ms); + if((r == CURLE_OPERATION_TIMEDOUT) && + (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { + goto out; + } + } + else { + Curl_cond_wait(&tslot->await, &tpool->lock); + } + } + +out: + thrdslot_done(tslot); + if(!thrdpool_unlink(tslot->tpool, TRUE)) { + /* tpool not destroyed */ + Curl_mutex_release(&tpool->lock); + } + return 0; +} + +static CURLcode thrdslot_start(struct curl_thrdpool *tpool) +{ + struct thrdslot *tslot; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tslot = curlx_calloc(1, sizeof(*tslot)); + if(!tslot) + goto out; + tslot->id = tpool->next_id++; + tslot->tpool = tpool; + tslot->thread = curl_thread_t_null; + Curl_cond_init(&tslot->await); + + tpool->refcount++; + tslot->running = TRUE; + tslot->thread = Curl_thread_create(thrdslot_run, tslot); + if(tslot->thread == curl_thread_t_null) { /* never started */ + tslot->running = FALSE; + thrdpool_unlink(tpool, TRUE); + result = CURLE_FAILED_INIT; + goto out; + } + + Curl_llist_append(&tpool->slots, tslot, &tslot->node); + tslot = NULL; + result = CURLE_OK; + +out: + if(tslot) + thrdslot_destroy(tslot); + return result; +} + +static void thrdpool_wake_all(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + Curl_cond_signal(&tslot->await); + } +} + +static void thrdpool_join_zombies(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + + for(e = Curl_llist_head(&tpool->zombies); e; + e = Curl_llist_head(&tpool->zombies)) { + struct thrdslot *tslot = Curl_node_elem(e); + + Curl_node_remove(&tslot->node); + if(tslot->thread != curl_thread_t_null) { + Curl_mutex_release(&tpool->lock); + Curl_thread_join(&tslot->thread); + Curl_mutex_acquire(&tpool->lock); + tslot->thread = curl_thread_t_null; + } + thrdslot_destroy(tslot); + } +} + +static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked) +{ + DEBUGASSERT(tpool->refcount); + if(tpool->refcount) + tpool->refcount--; + if(tpool->refcount) + return FALSE; + + /* no more references, free */ + DEBUGASSERT(tpool->aborted); + thrdpool_join_zombies(tpool); + if(locked) + Curl_mutex_release(&tpool->lock); + curlx_free(tpool->name); + Curl_cond_destroy(&tpool->await); + Curl_mutex_destroy(&tpool->lock); + curlx_free(tpool); + return TRUE; +} + +static CURLcode thrdpool_signal(struct curl_thrdpool *tpool, + uint32_t nthreads) +{ + struct Curl_llist_node *e, *n; + CURLcode result = CURLE_OK; + + DEBUGASSERT(!tpool->aborted); + thrdpool_join_zombies(tpool); + + for(e = Curl_llist_head(&tpool->slots); e && nthreads; e = n) { + struct thrdslot *tslot = Curl_node_elem(e); + n = Curl_node_next(e); + if(tslot->idle) { + Curl_cond_signal(&tslot->await); + --nthreads; + } + else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) { + /* starting thread, queries for work soon. */ + --nthreads; + } + } + + while(nthreads && !result && + Curl_llist_count(&tpool->slots) < tpool->max_threads) { + result = thrdslot_start(tpool); + if(result) + break; + --nthreads; + } + + return result; +} + +CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + CURLcode result = CURLE_OK; + size_t running; + + if(!max_threads || (min_threads > max_threads)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + Curl_mutex_acquire(&tpool->lock); + tpool->min_threads = min_threads; + tpool->max_threads = max_threads; + tpool->idle_time_ms = idle_time_ms; + running = Curl_llist_count(&tpool->slots); + if(tpool->min_threads > running) { + result = thrdpool_signal(tpool, tpool->min_threads - (uint32_t)running); + } + Curl_mutex_release(&tpool->lock); + + return result; +} + +CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, + const char *name, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdpool_take_item_cb *fn_take, + Curl_thrdpool_process_item_cb *fn_process, + Curl_thrdpool_return_item_cb *fn_return, + void *user_data) +{ + struct curl_thrdpool *tpool; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tpool = curlx_calloc(1, sizeof(*tpool)); + if(!tpool) + goto out; + tpool->refcount = 1; + + Curl_mutex_init(&tpool->lock); + Curl_cond_init(&tpool->await); + Curl_llist_init(&tpool->slots, NULL); + Curl_llist_init(&tpool->zombies, NULL); + tpool->fn_take = fn_take; + tpool->fn_process = fn_process; + tpool->fn_return = fn_return; + tpool->fn_user_data = user_data; + + tpool->name = curlx_strdup(name); + if(!tpool->name) + goto out; + + result = Curl_thrdpool_set_props(tpool, min_threads, max_threads, + idle_time_ms); + +out: + if(result && tpool) { + tpool->aborted = TRUE; + thrdpool_unlink(tpool, FALSE); + tpool = NULL; + } + *ptpool = tpool; + return result; +} + +void Curl_thrdpool_destroy(struct curl_thrdpool *tpool, bool join) +{ + Curl_mutex_acquire(&tpool->lock); + + tpool->aborted = TRUE; + + while(join && Curl_llist_count(&tpool->slots)) { + thrdpool_wake_all(tpool); + Curl_cond_wait(&tpool->await, &tpool->lock); + } + + thrdpool_join_zombies(tpool); + + /* detach all still running threads */ + if(Curl_llist_count(&tpool->slots)) { + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + if(tslot->thread != curl_thread_t_null) + Curl_thread_destroy(&tslot->thread); + } + tpool->detached = TRUE; + } + + if(!thrdpool_unlink(tpool, TRUE)) { + /* tpool not destroyed */ + Curl_mutex_release(&tpool->lock); + } +} + +CURLcode Curl_thrdpool_signal(struct curl_thrdpool *tpool, uint32_t nthreads) +{ + CURLcode result; + + Curl_mutex_acquire(&tpool->lock); + result = thrdpool_signal(tpool, nthreads); + Curl_mutex_release(&tpool->lock); + return result; +} + +static bool thrdpool_all_idle(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + if(!tslot->idle) + return FALSE; + } + return TRUE; +} + +CURLcode Curl_thrdpool_await_idle(struct curl_thrdpool *tpool, + uint32_t timeout_ms) +{ + CURLcode result = CURLE_OK; + struct curltime end = { 0 }; + + Curl_mutex_acquire(&tpool->lock); + DEBUGASSERT(!tpool->aborted); + if(tpool->aborted) { + result = CURLE_FAILED_INIT; + goto out; + } + + while(!thrdpool_all_idle(tpool)) { + if(timeout_ms) { + timediff_t remain_ms; + CURLcode r; + + if(!end.tv_sec && !end.tv_usec) { + end = curlx_now(); + end.tv_sec += (time_t)(timeout_ms / 1000); + end.tv_usec += (int)(timeout_ms % 1000) * 1000; + if(end.tv_usec >= 1000000) { + end.tv_sec++; + end.tv_usec -= 1000000; + } + } + remain_ms = curlx_timediff_ms(curlx_now(), end); + if(remain_ms <= 0) + r = CURLE_OPERATION_TIMEDOUT; + else + r = Curl_cond_timedwait(&tpool->await, &tpool->lock, + (uint32_t)remain_ms); + if(r == CURLE_OPERATION_TIMEDOUT) { + result = r; + break; + } + } + else { + Curl_cond_wait(&tpool->await, &tpool->lock); + } + } + +out: + thrdpool_join_zombies(tpool); + Curl_mutex_release(&tpool->lock); + return result; +} + +#ifdef CURLVERBOSE +void Curl_thrdpool_trace(struct curl_thrdpool *tpool, + struct Curl_easy *data) +{ + struct curl_trc_feat *feat = &Curl_trc_feat_threads; + if(Curl_trc_ft_is_verbose(data, feat)) { + struct Curl_llist_node *e; + struct curltime now = curlx_now(); + + Curl_mutex_acquire(&tpool->lock); + if(!Curl_llist_count(&tpool->slots)) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] no threads running", + tpool->name); + } + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + timediff_t elapsed_ms = curlx_ptimediff_ms(&now, &tslot->starttime); + if(!tslot->running) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: not running", + tpool->name, tslot->id); + } + else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: starting...", + tpool->name, tslot->id); + } + else if(tslot->idle) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: idle for %" + FMT_TIMEDIFF_T "ms", + tpool->name, tslot->id, elapsed_ms); + } + else { + timediff_t remain_ms = tslot->work_timeout_ms ? + (tslot->work_timeout_ms - elapsed_ms) : 0; + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: busy %" + FMT_TIMEDIFF_T "ms, timeout in %" FMT_TIMEDIFF_T + "ms: %s", + tpool->name, tslot->id, elapsed_ms, remain_ms, + tslot->work_description); + } + } + Curl_mutex_release(&tpool->lock); + } +} +#endif + +#endif /* USE_THREADS */
diff --git a/Utilities/cmcurl/lib/thrdpool.h b/Utilities/cmcurl/lib/thrdpool.h new file mode 100644 index 0000000..cc81fb6 --- /dev/null +++ b/Utilities/cmcurl/lib/thrdpool.h
@@ -0,0 +1,106 @@ +#ifndef HEADER_CURL_THRDPOOL_H +#define HEADER_CURL_THRDPOOL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "curlx/timediff.h" + +#ifdef USE_THREADS + +struct curl_thrdpool; +struct Curl_easy; + +/* Invoked under thread pool lock to get an "item" to work on. Must + * return NULL if there is nothing to do. + * Caller might return a descriptive string about the "item", where + * available. The string needs to have the same lifetime as the + * item itself. */ +typedef void *Curl_thrdpool_take_item_cb(void *user_data, + const char **pdescription, + timediff_t *ptimeout_ms); + +/* Invoked outside thread pool lock to process the item taken. */ +typedef void Curl_thrdpool_process_item_cb(void *item); + +/* Invoked under thread pool lock to return a processed item back + * to the producer. + * If the thread pool has been destroyed, `user_data` will be NULL + * and the callback is responsible to release all `item` resources. */ +typedef void Curl_thrdpool_return_item_cb(void *item, void *user_data); + +/* Create a new thread pool. + * @param name name of pool for tracing purposes + * @param min_threads minimum number of threads to have always running + * @param max_threads maximum number of threads running, ever. + * @param idle_time_ms maximum time a thread should wait for tasks to + * process before shutting down (unless the pool is + * already at minimum thread count), use 0 for + * infinite wait. + * @param fn_take take the next item to process + * @param fn_process process the item taken + * @param fn_return return the processed item + * @param user_data parameter passed to take/return callbacks + */ +CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, + const char *name, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdpool_take_item_cb *fn_take, + Curl_thrdpool_process_item_cb *fn_process, + Curl_thrdpool_return_item_cb *fn_return, + void *user_data); + +/* Destroy the thread pool, release its resources. + * With `join` being TRUE, the call will wait for all threads to finish + * processing before returning. On FALSE, it will detach all threads + * running. Ongoing item processing will continue to run and + * `fn_return` will be invoked with NULL user_data before the thread exits. + */ +void Curl_thrdpool_destroy(struct curl_thrdpool *tpool, bool join); + +/* Signal the pool to wake up `nthreads` idle worker threads, possible + * creating new threads up to the max limit. The number should reflect + * the items that can actually be taken for processing right away, e.g. + * the producers "queue" length of outstanding items. + */ +CURLcode Curl_thrdpool_signal(struct curl_thrdpool *tpool, uint32_t nthreads); + +CURLcode Curl_thrdpool_await_idle(struct curl_thrdpool *tpool, + uint32_t timeout_ms); + +/* Change the properties of a threadpool. */ +CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#ifdef CURLVERBOSE +void Curl_thrdpool_trace(struct curl_thrdpool *tpool, + struct Curl_easy *data); +#endif + +#endif /* USE_THREADS */ + +#endif /* HEADER_CURL_THRDPOOL_H */
diff --git a/Utilities/cmcurl/lib/thrdqueue.c b/Utilities/cmcurl/lib/thrdqueue.c new file mode 100644 index 0000000..1521ccf --- /dev/null +++ b/Utilities/cmcurl/lib/thrdqueue.c
@@ -0,0 +1,411 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_THREADS + +#include "llist.h" +#include "curl_threads.h" +#include "thrdpool.h" +#include "thrdqueue.h" +#include "curlx/timeval.h" +#ifdef CURLVERBOSE +#include "curl_trc.h" +#include "urldata.h" +#endif + + +struct curl_thrdq { + char *name; + curl_mutex_t lock; + curl_cond_t await; + struct Curl_llist sendq; + struct Curl_llist recvq; + struct curl_thrdpool *tpool; + Curl_thrdq_item_free_cb *fn_free; + Curl_thrdq_item_process_cb *fn_process; + Curl_thrdq_ev_cb *fn_event; + void *fn_user_data; + uint32_t send_max_len; + BIT(aborted); +}; + +struct thrdq_item { + struct Curl_llist_node node; + Curl_thrdq_item_free_cb *fn_free; + Curl_thrdq_item_process_cb *fn_process; + void *item; + struct curltime start; + timediff_t timeout_ms; + const char *description; +}; + +static struct thrdq_item *thrdq_item_create(struct curl_thrdq *tqueue, + void *item, + const char *description, + timediff_t timeout_ms) +{ + struct thrdq_item *qitem; + + qitem = curlx_calloc(1, sizeof(*qitem)); + if(!qitem) + return NULL; + qitem->item = item; + qitem->description = description; + qitem->fn_free = tqueue->fn_free; + qitem->fn_process = tqueue->fn_process; + if(timeout_ms) { + qitem->start = curlx_now(); + qitem->timeout_ms = timeout_ms; + } + return qitem; +} + +static void thrdq_item_destroy(struct thrdq_item *qitem) +{ + if(qitem->item) + qitem->fn_free(qitem->item); + curlx_free(qitem); +} + +static void thrdq_item_list_dtor(void *user_data, void *elem) +{ + (void)user_data; + thrdq_item_destroy(elem); +} + +static void *thrdq_tpool_take(void *user_data, const char **pdescription, + timediff_t *ptimeout_ms) +{ + struct curl_thrdq *tqueue = user_data; + struct thrdq_item *qitem = NULL; + struct Curl_llist_node *e; + Curl_thrdq_ev_cb *fn_event = NULL; + void *fn_user_data = NULL; + + Curl_mutex_acquire(&tqueue->lock); + *pdescription = NULL; + *ptimeout_ms = 0; + if(!tqueue->aborted) { + e = Curl_llist_head(&tqueue->sendq); + if(e) { + struct curltime now = curlx_now(); + timediff_t timeout_ms; + while(e) { + qitem = Curl_node_take_elem(e); + timeout_ms = (!qitem->timeout_ms) ? 0 : + (qitem->timeout_ms - curlx_ptimediff_ms(&now, &qitem->start)); + if(timeout_ms < 0) { + /* timed out while queued, place on receive queue */ + Curl_llist_append(&tqueue->recvq, qitem, &qitem->node); + fn_event = tqueue->fn_event; + fn_user_data = tqueue->fn_user_data; + qitem = NULL; + e = Curl_llist_head(&tqueue->sendq); + continue; + } + else { + *pdescription = qitem->description; + *ptimeout_ms = timeout_ms; + break; + } + } + } + } + Curl_mutex_release(&tqueue->lock); + /* avoiding deadlocks */ + if(fn_event) + fn_event(tqueue, CURL_THRDQ_EV_ITEM_DONE, fn_user_data); + return qitem; +} + +static void thrdq_tpool_return(void *item, void *user_data) +{ + struct curl_thrdq *tqueue = user_data; + struct thrdq_item *qitem = item; + Curl_thrdq_ev_cb *fn_event = NULL; + void *fn_user_data = NULL; + + if(!tqueue) { + thrdq_item_destroy(item); + return; + } + + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + thrdq_item_destroy(qitem); + } + else { + DEBUGASSERT(!Curl_node_llist(&qitem->node)); + Curl_llist_append(&tqueue->recvq, qitem, &qitem->node); + fn_event = tqueue->fn_event; + fn_user_data = tqueue->fn_user_data; + } + Curl_mutex_release(&tqueue->lock); + /* avoiding deadlocks */ + if(fn_event) + fn_event(tqueue, CURL_THRDQ_EV_ITEM_DONE, fn_user_data); +} + +static void thrdq_tpool_process(void *item) +{ + struct thrdq_item *qitem = item; + qitem->fn_process(qitem->item); +} + +static void thrdq_unlink(struct curl_thrdq *tqueue, bool locked, bool join) +{ + DEBUGASSERT(tqueue->aborted); + if(tqueue->tpool) { + if(locked) + Curl_mutex_release(&tqueue->lock); + Curl_thrdpool_destroy(tqueue->tpool, join); + tqueue->tpool = NULL; + if(locked) + Curl_mutex_acquire(&tqueue->lock); + } + + Curl_llist_destroy(&tqueue->sendq, NULL); + Curl_llist_destroy(&tqueue->recvq, NULL); + curlx_free(tqueue->name); + Curl_cond_destroy(&tqueue->await); + if(locked) + Curl_mutex_release(&tqueue->lock); + Curl_mutex_destroy(&tqueue->lock); + curlx_free(tqueue); +} + +CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, + const char *name, + uint32_t max_len, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdq_item_free_cb *fn_free, + Curl_thrdq_item_process_cb *fn_process, + Curl_thrdq_ev_cb *fn_event, + void *user_data) +{ + struct curl_thrdq *tqueue; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tqueue = curlx_calloc(1, sizeof(*tqueue)); + if(!tqueue) + goto out; + + Curl_mutex_init(&tqueue->lock); + Curl_cond_init(&tqueue->await); + Curl_llist_init(&tqueue->sendq, thrdq_item_list_dtor); + Curl_llist_init(&tqueue->recvq, thrdq_item_list_dtor); + tqueue->fn_free = fn_free; + tqueue->fn_process = fn_process; + tqueue->fn_event = fn_event; + tqueue->fn_user_data = user_data; + tqueue->send_max_len = max_len; + + tqueue->name = curlx_strdup(name); + if(!tqueue->name) + goto out; + + result = Curl_thrdpool_create(&tqueue->tpool, name, + min_threads, max_threads, idle_time_ms, + thrdq_tpool_take, + thrdq_tpool_process, + thrdq_tpool_return, + tqueue); + +out: + if(result && tqueue) { + tqueue->aborted = TRUE; + thrdq_unlink(tqueue, FALSE, TRUE); + tqueue = NULL; + } + *ptqueue = tqueue; + return result; +} + +void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join) +{ + Curl_mutex_acquire(&tqueue->lock); + DEBUGASSERT(!tqueue->aborted); + tqueue->aborted = TRUE; + thrdq_unlink(tqueue, TRUE, join); +} + +CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, + const char *description, timediff_t timeout_ms) +{ + CURLcode result = CURLE_AGAIN; + size_t signals = 0; + + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + result = CURLE_SEND_ERROR; + goto out; + } + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + + if(!tqueue->send_max_len || + (Curl_llist_count(&tqueue->sendq) < tqueue->send_max_len)) { + struct thrdq_item *qitem = thrdq_item_create(tqueue, item, description, + timeout_ms); + if(!qitem) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + item = NULL; + Curl_llist_append(&tqueue->sendq, qitem, &qitem->node); + signals = Curl_llist_count(&tqueue->sendq); + result = CURLE_OK; + } + +out: + Curl_mutex_release(&tqueue->lock); + /* Signal thread pool unlocked to avoid deadlocks. Since we added + * item to the queue already, it might have been taken for processing + * already. Any error in signalling the pool cannot be reported to + * the caller since it needs to give up ownership of item. */ + if(!result && signals) + (void)Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals); + return result; +} + +CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem) +{ + CURLcode result = CURLE_AGAIN; + struct Curl_llist_node *e; + + *pitem = NULL; + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + result = CURLE_RECV_ERROR; + goto out; + } + + e = Curl_llist_head(&tqueue->recvq); + if(e) { + struct thrdq_item *qitem = Curl_node_take_elem(e); + *pitem = qitem->item; + qitem->item = NULL; + thrdq_item_destroy(qitem); + result = CURLE_OK; + } +out: + Curl_mutex_release(&tqueue->lock); + return result; +} + +static void thrdq_llist_clean_matches(struct Curl_llist *llist, + Curl_thrdq_item_match_cb *fn_match, + void *match_data) +{ + struct Curl_llist_node *e, *n; + struct thrdq_item *qitem; + + for(e = Curl_llist_head(llist); e; e = n) { + n = Curl_node_next(e); + qitem = Curl_node_elem(e); + if(fn_match(qitem->item, match_data)) + Curl_node_remove(e); + } +} + +void Curl_thrdq_clear(struct curl_thrdq *tqueue, + Curl_thrdq_item_match_cb *fn_match, + void *match_data) +{ + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + goto out; + } + thrdq_llist_clean_matches(&tqueue->sendq, fn_match, match_data); + thrdq_llist_clean_matches(&tqueue->recvq, fn_match, match_data); +out: + Curl_mutex_release(&tqueue->lock); +} + +CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms) +{ + return Curl_thrdpool_await_idle(tqueue->tpool, timeout_ms); +} + +CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, + uint32_t max_len, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + CURLcode result; + size_t signals; + + Curl_mutex_acquire(&tqueue->lock); + tqueue->send_max_len = max_len; + signals = Curl_llist_count(&tqueue->sendq); + Curl_mutex_release(&tqueue->lock); + + result = Curl_thrdpool_set_props(tqueue->tpool, min_threads, + max_threads, idle_time_ms); + if(!result && signals) + result = Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals); + return result; +} + +#ifdef CURLVERBOSE +void Curl_thrdq_trace(struct curl_thrdq *tqueue, + struct Curl_easy *data) +{ + struct curl_trc_feat *feat = &Curl_trc_feat_threads; + if(Curl_trc_ft_is_verbose(data, feat)) { + struct Curl_llist_node *e; + struct thrdq_item *qitem; + + Curl_thrdpool_trace(tqueue->tpool, data); + Curl_mutex_acquire(&tqueue->lock); + if(!Curl_llist_count(&tqueue->sendq) && + !Curl_llist_count(&tqueue->recvq)) { + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] empty", tqueue->name); + } + for(e = Curl_llist_head(&tqueue->sendq); e; e = Curl_node_next(e)) { + qitem = Curl_node_elem(e); + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] in: %s", + tqueue->name, qitem->description); + } + for(e = Curl_llist_head(&tqueue->recvq); e; e = Curl_node_next(e)) { + qitem = Curl_node_elem(e); + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] out: %s", + tqueue->name, qitem->description); + } + Curl_mutex_release(&tqueue->lock); + } +} +#endif + +#endif /* USE_THREADS */
diff --git a/Utilities/cmcurl/lib/thrdqueue.h b/Utilities/cmcurl/lib/thrdqueue.h new file mode 100644 index 0000000..d267f5d --- /dev/null +++ b/Utilities/cmcurl/lib/thrdqueue.h
@@ -0,0 +1,118 @@ +#ifndef HEADER_CURL_THRDQUEUE_H +#define HEADER_CURL_THRDQUEUE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "curlx/timediff.h" + +#ifdef USE_THREADS + +struct Curl_easy; +struct curl_thrdq; + +typedef enum { + CURL_THRDQ_EV_ITEM_DONE /* an item has been processed and is ready */ +} Curl_thrdq_event; + +/* Notification callback when "events" happen in the queue. May be + * call from any thread, queue is not locked. */ +typedef void Curl_thrdq_ev_cb(const struct curl_thrdq *tqueue, + Curl_thrdq_event ev, + void *user_data); + +/* Process a queued item. Maybe call from any thread. Queue is + * not locked. */ +typedef void Curl_thrdq_item_process_cb(void *item); + +/* Free an item. May be called from any thread at any time for an + * item that is in the queue (either before or after processing). */ +typedef void Curl_thrdq_item_free_cb(void *item); + +/* Create a new queue processing "items" by a thread pool. + */ +CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, + const char *name, + uint32_t max_len, /* 0 for unlimited */ + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdq_item_free_cb *fn_free, + Curl_thrdq_item_process_cb *fn_process, + Curl_thrdq_ev_cb *fn_event, /* optional */ + void *user_data); + +/* Destroy the queue, free all queued items unprocessed and destroy + * the thread pool used. + * @param join TRUE when thread pool shall be joined. FALSE for + * detaching any running threads. + */ +void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join); + +/* Send "item" onto the queue. The caller needs to clear any reference + * to "item" on success, e.g. the queue takes ownership. + * `description` is an optional string describing the item for tracing + * purposes. It needs to have the same lifetime as `item`. + * Returns CURLE_AGAIN when the queue has already been full. + * + * With`timeout_ms` != 0, items that get stuck that long in the send + * queue are removed and added to the receive queue right away. + */ +CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, + const char *description, timediff_t timeout_ms); + +/* Receive the oldest, processed item from the queue again, if there is one. + * The caller takes ownership of the item received, e.g. the queue + * relinquishes all references to item. + * Returns CURLE_AGAIN when there is no processed item, setting `pitem` + * to NULL. + */ +CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem); + +/* Return TRUE if the passed "item" matches. */ +typedef bool Curl_thrdq_item_match_cb(void *item, void *match_data); + +/* Clear all scheduled/processed items that match from the queue. This + * will *not* be able to clear items that are being processed. + */ +void Curl_thrdq_clear(struct curl_thrdq *tqueue, + Curl_thrdq_item_match_cb *fn_match, + void *match_data); + +CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms); + +CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, + uint32_t max_len, /* 0 for unlimited */ + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#ifdef CURLVERBOSE +void Curl_thrdq_trace(struct curl_thrdq *tqueue, + struct Curl_easy *data); +#endif + +#endif /* USE_THREADS */ + +#endif /* HEADER_CURL_THRDQUEUE_H */
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index fed6a17..fd1a903 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c
@@ -59,6 +59,7 @@ #include "hostip.h" #include "cfilters.h" #include "cw-out.h" +#include "dnscache.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" @@ -68,7 +69,6 @@ #include "getinfo.h" #include "multiif.h" #include "connect.h" -#include "mime.h" #include "hsts.h" #include "setopt.h" #include "headers.h" @@ -104,13 +104,13 @@ { struct connectdata *conn = data->conn; - if(conn->handler->protocol & PROTO_FAMILY_FTP) + if(conn->scheme->protocol & PROTO_FAMILY_FTP) return Curl_conn_data_pending(data, SECONDARYSOCKET); /* in the case of libssh2, we can never be really sure that we have emptied its internal buffers so we MUST always try until we get EAGAIN back */ return (!rcvd_eagain && - conn->handler->protocol & (CURLPROTO_SCP | CURLPROTO_SFTP)) || + conn->scheme->protocol & (CURLPROTO_SCP | CURLPROTO_SFTP)) || Curl_conn_data_pending(data, FIRSTSOCKET); } @@ -127,16 +127,14 @@ case CURL_TIMECOND_IFMODSINCE: default: if(timeofdoc <= data->set.timevalue) { - infof(data, - "The requested document is not new enough"); + infof(data, "The requested document is not new enough"); data->info.timecond = TRUE; return FALSE; } break; case CURL_TIMECOND_IFUNMODSINCE: if(timeofdoc >= data->set.timevalue) { - infof(data, - "The requested document is not old enough"); + infof(data, "The requested document is not old enough"); data->info.timecond = TRUE; return FALSE; } @@ -254,12 +252,13 @@ if(bytestoread && Curl_rlimit_active(&data->progress.dl.rlimit)) { curl_off_t dl_avail = Curl_rlimit_avail(&data->progress.dl.rlimit, Curl_pgrs_now(data)); - /* DEBUGF(infof(data, "dl_rlimit, available=%" FMT_OFF_T, dl_avail)); - */ - /* In case of rate limited downloads: if this loop already got - * data and less than 16k is left in the limit, break out. - * We want to stutter a bit to keep in the limit, but too small - * receives will just cost cpu unnecessarily. */ +#if 0 + DEBUGF(infof(data, "dl_rlimit, available=%" FMT_OFF_T, dl_avail)); +#endif + /* In case of rate limited downloads: if this loop already got data and + * less than 16k is left in the limit, break out. We want to stutter a + * bit to keep in the limit, but too small receives will cost cpu + * unnecessarily. */ if(dl_avail <= 0) { rate_limited = TRUE; break; @@ -278,7 +277,7 @@ if(data->req.download_done && data->req.no_body && !data->req.resp_trailer) { DEBUGF(infof(data, "EAGAIN, download done, no trailer announced, " - "not waiting for EOS")); + "not waiting for EOS")); blen = 0; /* continue as if we received the EOS */ } @@ -305,15 +304,15 @@ * we should read the EOS. Which may arrive as meta data after * the bytes. Not taking it in might lead to RST of streams. */ if((!is_multiplex && data->req.download_done) || is_eos) { - data->req.keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); } /* if we stopped receiving, leave the loop */ - if(!(k->keepon & KEEP_RECV)) + if(!CURL_REQ_WANT_RECV(data)) break; } while(maxloops--); - if(!is_eos && !rate_limited && CURL_WANT_RECV(data) && + if(!is_eos && !rate_limited && CURL_REQ_WANT_RECV(data) && (!rcvd_eagain || data_pending(data, rcvd_eagain))) { /* Did not read until EAGAIN/EOS or there is still data pending * in buffers. Mark as read-again via simulated SELECT results. */ @@ -321,7 +320,7 @@ CURL_TRC_M(data, "sendrecv_dl() no EAGAIN/pending data, mark as dirty"); } - if(((k->keepon & (KEEP_RECV | KEEP_SEND)) == KEEP_SEND) && + if(!CURL_REQ_WANT_RECV(data) && CURL_REQ_WANT_SEND(data) && (conn->bits.close || is_multiplex)) { /* When we have read the entire thing and the close bit is set, the server may now close the connection. If there is now any kind of sending going @@ -342,9 +341,7 @@ */ static CURLcode sendrecv_ul(struct Curl_easy *data) { - /* We should not get here when the sending is already done. It - * probably means that someone set `data-req.keepon |= KEEP_SEND` - * when it should not. */ + /* We should not get here when the sending is already done. */ DEBUGASSERT(!Curl_req_done_sending(data)); if(!Curl_req_done_sending(data)) @@ -368,7 +365,7 @@ /* We go ahead and do a read if we have a readable socket or if the stream was rewound (in which case we have data in a buffer) */ - if(k->keepon & KEEP_RECV) { + if(CURL_REQ_WANT_RECV(data)) { result = sendrecv_dl(data, k); if(result || data->req.done) goto out; @@ -385,8 +382,8 @@ if(result) goto out; - if(k->keepon) { - if(Curl_timeleft_ms(data, FALSE) < 0) { + if(CURL_REQ_WANT_IO(data)) { + if(Curl_timeleft_ms(data) < 0) { if(k->size != -1) { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " out of %" @@ -408,7 +405,7 @@ } else { /* - * The transfer has been performed. Just make some general checks before + * The transfer has been performed. Make some general checks before * returning. */ if(!(data->req.no_body) && (k->size != -1) && @@ -421,7 +418,7 @@ } /* If there is nothing more to send/recv, the request is done */ - if((k->keepon & (KEEP_RECV | KEEP_SEND)) == 0) + if(!CURL_REQ_WANT_IO(data)) data->req.done = TRUE; result = Curl_pgrsUpdate(data); @@ -442,6 +439,40 @@ } /* + * Restore the user credentials to those set in options. + */ +CURLcode Curl_reset_userpwd(struct Curl_easy *data) +{ + CURLcode result; + if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) + data->state.creds_from = CREDS_OPTION; + result = Curl_setstropt(&data->state.aptr.user, + data->set.str[STRING_USERNAME]); + if(!result) + result = Curl_setstropt(&data->state.aptr.passwd, + data->set.str[STRING_PASSWORD]); + return result; +} + +/* + * Restore the proxy credentials to those set in options. + */ +CURLcode Curl_reset_proxypwd(struct Curl_easy *data) +{ +#ifndef CURL_DISABLE_PROXY + CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, + data->set.str[STRING_PROXYUSERNAME]); + if(!result) + result = Curl_setstropt(&data->state.aptr.proxypasswd, + data->set.str[STRING_PROXYPASSWORD]); + return result; +#else + (void)data; + return CURLE_OK; +#endif +} + +/* * Curl_pretransfer() is called immediately before a transfer starts, and only * once for one transfer no matter if it has redirects or do multi-pass * authentication etc. @@ -472,6 +503,8 @@ uc = curl_url_get(data->set.uh, CURLUPART_URL, &data->set.str[STRING_SET_URL], 0); if(uc) { + /* clear the pointer to not point to freed memory anymore */ + Curl_bufref_set(&data->state.url, NULL, 0, NULL); failf(data, "No URL set"); return CURLE_URL_MALFORMAT; } @@ -494,6 +527,7 @@ data->state.requests = 0; data->state.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ + data->state.http_ignorecustom = FALSE; /* use custom HTTP method */ data->state.errorbuf = FALSE; /* no error has occurred */ #ifndef CURL_DISABLE_HTTP Curl_http_neg_init(data, &data->state.http_neg); @@ -501,8 +535,13 @@ data->state.authproblem = FALSE; data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; - Curl_safefree(data->info.wouldredirect); + curlx_safefree(data->info.wouldredirect); Curl_data_priority_clear_state(data); + if(data->set.http_auto_referer) + Curl_bufref_free(&data->state.referer); + if(data->set.str[STRING_SET_REFERER]) + Curl_bufref_set(&data->state.referer, data->set.str[STRING_SET_REFERER], + 0, NULL); if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; @@ -534,7 +573,7 @@ * different ports! */ data->state.allow_port = TRUE; -#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(MSG_NOSIGNAL) /************************************************************* * Tell signal handler to ignore SIGPIPE *************************************************************/ @@ -565,8 +604,8 @@ if(wc->state < CURLWC_INIT) { if(wc->ftpwc) wc->dtor(wc->ftpwc); - Curl_safefree(wc->pattern); - Curl_safefree(wc->path); + curlx_safefree(wc->pattern); + curlx_safefree(wc->path); Curl_wildcard_init(wc); /* init wildcard structures */ } } @@ -576,8 +615,7 @@ /* * Set user-agent. Used for HTTP, but since we can attempt to tunnel - * basically anything through an HTTP proxy we cannot limit this based on - * protocol. + * anything through an HTTP proxy we cannot limit this based on protocol. */ if(!result && data->set.str[STRING_USERAGENT]) { curlx_free(data->state.aptr.uagent); @@ -587,23 +625,10 @@ return CURLE_OUT_OF_MEMORY; } - if(data->set.str[STRING_USERNAME] || - data->set.str[STRING_PASSWORD]) - data->state.creds_from = CREDS_OPTION; if(!result) - result = Curl_setstropt(&data->state.aptr.user, - data->set.str[STRING_USERNAME]); + result = Curl_reset_userpwd(data); if(!result) - result = Curl_setstropt(&data->state.aptr.passwd, - data->set.str[STRING_PASSWORD]); -#ifndef CURL_DISABLE_PROXY - if(!result) - result = Curl_setstropt(&data->state.aptr.proxyuser, - data->set.str[STRING_PROXYUSERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - data->set.str[STRING_PROXYPASSWORD]); -#endif + result = Curl_reset_proxypwd(data); data->req.headerbytecount = 0; Curl_headers_cleanup(data); @@ -623,13 +648,13 @@ protocol is HTTP as when uploading over HTTP we will still get a response */ if(data->state.upload && - !(conn->handler->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_RTSP))) + !(conn->scheme->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_RTSP))) return CURLE_OK; if(conn->bits.reuse && (data->req.bytecount + data->req.headerbytecount == 0) && ((!data->req.no_body && !data->req.done) || - (conn->handler->protocol & PROTO_FAMILY_HTTP)) + (conn->scheme->protocol & PROTO_FAMILY_HTTP)) #ifndef CURL_DISABLE_RTSP && (data->set.rtspreq != RTSPREQ_RECEIVE) #endif @@ -668,11 +693,10 @@ return CURLE_OUT_OF_MEMORY; connclose(conn, "retry"); /* close this connection */ - conn->bits.retry = TRUE; /* mark this as a connection we are about - to retry. Marking it this way should - prevent i.e HTTP transfers to return - error just because nothing has been - transferred! */ + conn->bits.retry = TRUE; /* mark this as a connection we are about to + retry. Marking it this way should prevent i.e + HTTP transfers to return error because nothing + has been transferred! */ Curl_creader_set_rewind(data, TRUE); } return CURLE_OK; @@ -701,27 +725,24 @@ /* without receiving, there should be not recv_size */ DEBUGASSERT((conn->recv_idx >= 0) || (recv_size == -1)); k->size = recv_size; - k->header = !!conn->handler->write_resp_hd; + k->header = !!conn->scheme->run->write_resp_hd; /* by default, we do not shutdown at the end of the transfer */ k->shutdown = FALSE; k->shutdown_err_ignore = FALSE; - /* The code sequence below is placed in this function just because all - necessary input is not always known in do_complete() as this function may - be called after that */ + /* The code sequence below is placed in this function because all necessary + input is not always known in do_complete() as this function may be called + after that */ if(!k->header && (recv_size > 0)) Curl_pgrsSetDownloadSize(data, recv_size); /* we want header and/or body, if neither then do not do this! */ - if(conn->handler->write_resp_hd || !data->req.no_body) { - + if(conn->scheme->run->write_resp_hd || !data->req.no_body) { if(conn->recv_idx != -1) - k->keepon |= KEEP_RECV; - + CURL_REQ_SET_RECV(data); if(conn->send_idx != -1) - k->keepon |= KEEP_SEND; + CURL_REQ_SET_SEND(data); } - CURL_TRC_M(data, "xfer_setup: recv_idx=%d, send_idx=%d", conn->recv_idx, conn->send_idx); } @@ -768,10 +789,10 @@ { CURLcode result = CURLE_OK; - if(data->conn->handler->write_resp) { + if(data->conn->scheme->run->write_resp) { /* protocol handlers offering this function take full responsibility * for writing all received download data to the client. */ - result = data->conn->handler->write_resp(data, buf, blen, is_eos); + result = data->conn->scheme->run->write_resp(data, buf, blen, is_eos); } else { /* No special handling by protocol handler, write all received data @@ -802,11 +823,11 @@ CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data, const char *hd0, size_t hdlen, bool is_eos) { - if(data->conn->handler->write_resp_hd) { - DEBUGASSERT(!hd0[hdlen]); /* null terminated */ + if(data->conn->scheme->run->write_resp_hd) { + DEBUGASSERT(!hd0[hdlen]); /* null-terminated */ /* protocol handlers offering this function take full responsibility * for writing all received download data to the client. */ - return data->conn->handler->write_resp_hd(data, hd0, hdlen, is_eos); + return data->conn->scheme->run->write_resp_hd(data, hd0, hdlen, is_eos); } /* No special handling by protocol handler, write as response bytes */ return Curl_xfer_write_resp(data, hd0, hdlen, is_eos); @@ -872,8 +893,8 @@ bool Curl_xfer_is_blocked(struct Curl_easy *data) { - bool want_send = ((data)->req.keepon & KEEP_SEND); - bool want_recv = ((data)->req.keepon & KEEP_RECV); + bool want_send = CURL_REQ_WANT_SEND(data); + bool want_recv = CURL_REQ_WANT_RECV(data); if(!want_send) return want_recv && Curl_xfer_recv_is_paused(data); else if(!want_recv) @@ -912,3 +933,25 @@ Curl_pgrsRecvPause(data, enable); return result; } + +bool Curl_xfer_is_secure(struct Curl_easy *data) +{ + const struct Curl_scheme *scheme = NULL; + + if(data->conn) { + scheme = data->conn->scheme; + /* if we are connected, but not use SSL, the transfer is not secure. + * This covers an insecure http:// proxy that is not tunneling. + * We enforce tunneling for such cases, but better be sure here. */ + if(Curl_conn_is_connected(data->conn, FIRSTSOCKET) && + !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) + return FALSE; + } + else if(data->info.conn_scheme) { /* was connected once */ + scheme = Curl_get_scheme(data->info.conn_scheme); + } + else { /* never connected (yet?) */ + DEBUGASSERT(0); /* not implemented, would need to parse URL */ + } + return scheme ? (scheme->flags & PROTOPT_SSL) : FALSE; +}
diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h index 05a5f89..b29e70b 100644 --- a/Utilities/cmcurl/lib/transfer.h +++ b/Utilities/cmcurl/lib/transfer.h
@@ -31,6 +31,8 @@ void Curl_init_CONNECT(struct Curl_easy *data); +CURLcode Curl_reset_userpwd(struct Curl_easy *data); +CURLcode Curl_reset_proxypwd(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); CURLcode Curl_sendrecv(struct Curl_easy *data); @@ -39,8 +41,8 @@ /** * Write the transfer raw response bytes, as received from the connection. - * Will handle all passed bytes or return an error. By default, this will - * write the bytes as BODY to the client. Protocols may provide a + * Handle all passed bytes or return an error. By default, this writes + * the bytes as BODY to the client. Protocols may provide a * "write_resp" callback in their handler to add specific treatment. E.g. * HTTP parses response headers and passes them differently to the client. * @param data the transfer @@ -112,7 +114,7 @@ /** * Send data on the socket/connection filter designated * for transfer's outgoing data. - * Will return CURLE_OK on blocking with (*pnwritten == 0). + * Return CURLE_OK on blocking with (*pnwritten == 0). */ CURLcode Curl_xfer_send(struct Curl_easy *data, const void *buf, size_t blen, bool eos, @@ -121,7 +123,7 @@ /** * Receive data on the socket/connection filter designated * for transfer's incoming data. - * Will return CURLE_AGAIN on blocking with (*pnrcvd == 0). + * Return CURLE_AGAIN on blocking with (*pnrcvd == 0). */ CURLcode Curl_xfer_recv(struct Curl_easy *data, char *buf, size_t blen, @@ -143,4 +145,8 @@ CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable); CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable); +/* TRUE if the transfer is secure (e.g. TLS) from libcurl to the + * URL's host. */ +bool Curl_xfer_is_secure(struct Curl_easy *data); + #endif /* HEADER_CURL_TRANSFER_H */
diff --git a/Utilities/cmcurl/lib/uint-bset.c b/Utilities/cmcurl/lib/uint-bset.c index 5efdbea..5469174 100644 --- a/Utilities/cmcurl/lib/uint-bset.c +++ b/Utilities/cmcurl/lib/uint-bset.c
@@ -68,7 +68,9 @@ } #ifdef UNITTESTS -UNITTEST uint32_t Curl_uint32_bset_capacity(struct uint32_bset *bset) +/* @unittest 3211 */ +UNITTEST uint32_t uint32_bset_capacity(struct uint32_bset *bset); +UNITTEST uint32_t uint32_bset_capacity(struct uint32_bset *bset) { return bset->nslots * 64; }
diff --git a/Utilities/cmcurl/lib/uint-bset.h b/Utilities/cmcurl/lib/uint-bset.h index 7455c42..ce0450c 100644 --- a/Utilities/cmcurl/lib/uint-bset.h +++ b/Utilities/cmcurl/lib/uint-bset.h
@@ -56,9 +56,6 @@ /* Destroy the bitset, freeing all resources. */ void Curl_uint32_bset_destroy(struct uint32_bset *bset); -/* Get the bitset capacity, e.g. can hold numbers from 0 to capacity - 1. */ -uint32_t Curl_uint32_bset_capacity(struct uint32_bset *bset); - /* Get the cardinality of the bitset, e.g. numbers present in the set. */ uint32_t Curl_uint32_bset_count(struct uint32_bset *bset);
diff --git a/Utilities/cmcurl/lib/uint-hash.c b/Utilities/cmcurl/lib/uint-hash.c index e23d249..c37448f 100644 --- a/Utilities/cmcurl/lib/uint-hash.c +++ b/Utilities/cmcurl/lib/uint-hash.c
@@ -107,7 +107,7 @@ ++h->size; } -#define CURL_UINT32_HASH_SLOT(h, id) h->table[uint32_hash_hash(id, h->slots)] +#define CURL_UINT32_HASH_SLOT(h, id) h->table[uint32_hash_hash(id, (h)->slots)] #define CURL_UINT32_HASH_SLOT_ADDR(h, id) &CURL_UINT32_HASH_SLOT(h, id) bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value) @@ -181,7 +181,9 @@ return NULL; } -static void uint_hash_clear(struct uint_hash *h) +/* @unittest 1616 */ +UNITTEST void uint_hash_clear(struct uint_hash *h); +UNITTEST void uint_hash_clear(struct uint_hash *h) { if(h && h->table) { struct uint_hash_entry *he, **he_anchor; @@ -198,19 +200,12 @@ } } -#ifdef UNITTESTS -UNITTEST void Curl_uint32_hash_clear(struct uint_hash *h) -{ - uint_hash_clear(h); -} -#endif - void Curl_uint32_hash_destroy(struct uint_hash *h) { DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); if(h->table) { uint_hash_clear(h); - Curl_safefree(h->table); + curlx_safefree(h->table); } DEBUGASSERT(h->size == 0); h->slots = 0;
diff --git a/Utilities/cmcurl/lib/uint-hash.h b/Utilities/cmcurl/lib/uint-hash.h index 56d7104..3cbcf8f 100644 --- a/Utilities/cmcurl/lib/uint-hash.h +++ b/Utilities/cmcurl/lib/uint-hash.h
@@ -44,8 +44,6 @@ uint32_t slots, Curl_uint32_hash_dtor *dtor); void Curl_uint32_hash_destroy(struct uint_hash *h); -void Curl_uint32_hash_clear(struct uint_hash *h); - bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value); bool Curl_uint32_hash_remove(struct uint_hash *h, uint32_t id); void *Curl_uint32_hash_get(struct uint_hash *h, uint32_t id);
diff --git a/Utilities/cmcurl/lib/uint-spbset.c b/Utilities/cmcurl/lib/uint-spbset.c index 58dcbda..3daa2ee 100644 --- a/Utilities/cmcurl/lib/uint-spbset.c +++ b/Utilities/cmcurl/lib/uint-spbset.c
@@ -30,9 +30,6 @@ #define CURL_UINT32_SPBSET_MAGIC 0x70737362 #endif -/* Clear the bitset, making it empty. */ -UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset); - void Curl_uint32_spbset_init(struct uint32_spbset *bset) { memset(bset, 0, sizeof(*bset)); @@ -41,10 +38,26 @@ #endif } +/* Clear the bitset, making it empty. + + @unittest 3213 + */ +UNITTEST void uint32_spbset_clear(struct uint32_spbset *bset); +UNITTEST void uint32_spbset_clear(struct uint32_spbset *bset) +{ + struct uint32_spbset_chunk *next, *chunk; + + for(chunk = bset->head.next; chunk; chunk = next) { + next = chunk->next; + curlx_free(chunk); + } + memset(&bset->head, 0, sizeof(bset->head)); +} + void Curl_uint32_spbset_destroy(struct uint32_spbset *bset) { DEBUGASSERT(bset->init == CURL_UINT32_SPBSET_MAGIC); - Curl_uint32_spbset_clear(bset); + uint32_spbset_clear(bset); } uint32_t Curl_uint32_spbset_count(struct uint32_spbset *bset) @@ -61,19 +74,8 @@ return n; } -UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset) -{ - struct uint32_spbset_chunk *next, *chunk; - - for(chunk = bset->head.next; chunk; chunk = next) { - next = chunk->next; - curlx_free(chunk); - } - memset(&bset->head, 0, sizeof(bset->head)); -} - -static struct uint32_spbset_chunk * -uint32_spbset_get_chunk(struct uint32_spbset *bset, uint32_t i, bool grow) +static struct uint32_spbset_chunk *uint32_spbset_get_chunk( + struct uint32_spbset *bset, uint32_t i, bool grow) { struct uint32_spbset_chunk *chunk, **panchor = NULL; uint32_t i_offset = (i & ~CURL_UINT32_SPBSET_CH_MASK);
diff --git a/Utilities/cmcurl/lib/uint-table.c b/Utilities/cmcurl/lib/uint-table.c index b9c1990..addd2c9 100644 --- a/Utilities/cmcurl/lib/uint-table.c +++ b/Utilities/cmcurl/lib/uint-table.c
@@ -29,9 +29,6 @@ #define CURL_UINT32_TBL_MAGIC 0x62757473 #endif -/* Clear the table, making it empty. */ -UNITTEST void Curl_uint32_tbl_clear(struct uint32_tbl *tbl); - void Curl_uint32_tbl_init(struct uint32_tbl *tbl, Curl_uint32_tbl_entry_dtor *entry_dtor) { @@ -82,15 +79,12 @@ return CURLE_OK; } -void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl) -{ - DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); - Curl_uint32_tbl_clear(tbl); - curlx_free(tbl->rows); - memset(tbl, 0, sizeof(*tbl)); -} +/* Clear the table, making it empty. -UNITTEST void Curl_uint32_tbl_clear(struct uint32_tbl *tbl) + @unittest 3212 + */ +UNITTEST void uint32_tbl_clear(struct uint32_tbl *tbl); +UNITTEST void uint32_tbl_clear(struct uint32_tbl *tbl) { DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); uint32_tbl_clear_rows(tbl, 0, tbl->nrows); @@ -98,6 +92,14 @@ tbl->last_key_added = UINT32_MAX; } +void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl) +{ + DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); + uint32_tbl_clear(tbl); + curlx_free(tbl->rows); + memset(tbl, 0, sizeof(*tbl)); +} + uint32_t Curl_uint32_tbl_capacity(struct uint32_tbl *tbl) { return tbl->nrows;
diff --git a/Utilities/cmcurl/lib/uint-table.h b/Utilities/cmcurl/lib/uint-table.h index 3fc7e34..398a26a 100644 --- a/Utilities/cmcurl/lib/uint-table.h +++ b/Utilities/cmcurl/lib/uint-table.h
@@ -47,7 +47,7 @@ /* Resize the table to change capacity `nmax`. When `nmax` is reduced, * all present entries with key equal or larger to `nmax` are removed. */ -CURLcode Curl_uint32_tbl_resize(struct uint32_tbl *tbl, uint32_t nmax); +CURLcode Curl_uint32_tbl_resize(struct uint32_tbl *tbl, uint32_t nrows); /* Destroy the table, freeing all entries. */ void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl);
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index 130be19..5fe6803 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c
@@ -58,21 +58,21 @@ #error "We cannot compile without socket() support!" #endif -#if defined(HAVE_IF_NAMETOINDEX) && defined(_WIN32) +#if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK) #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 5) #include <wincrypt.h> /* workaround for old mingw-w64 missing to include it */ #endif #include <iphlpapi.h> #endif -#include "doh.h" #include "urldata.h" -#include "formdata.h" #include "mime.h" #include "bufref.h" #include "vtls/vtls.h" +#include "vssh/vssh.h" #include "hostip.h" #include "transfer.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "progress.h" #include "cookie.h" @@ -92,7 +92,7 @@ #include "http_proxy.h" #include "conncache.h" #include "multihandle.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "setopt.h" #include "altsvc.h" #include "curlx/dynbuf.h" @@ -100,7 +100,7 @@ #include "curlx/strerr.h" #include "curlx/strparse.h" -/* And now for the protocols */ +/* Now for the protocols */ #include "ftp.h" #include "dict.h" #include "telnet.h" @@ -112,7 +112,6 @@ #include "imap.h" #include "url.h" #include "connect.h" -#include "curl_rtmp.h" #include "gopher.h" #include "mqtt.h" #include "rtsp.h" @@ -125,18 +124,14 @@ #define data_priority_cleanup(x) #endif -/* Some parts of the code (e.g. chunked encoding) assume this buffer has at - * more than just a few bytes to play with. Do not let it become too small or - * bad things will happen. +/* Some parts of the code (e.g. chunked encoding) assume this buffer has more + * than a few bytes to play with. Do not let it become too small or bad things + * will happen. */ #if READBUFFER_SIZE < READBUFFER_MIN # error READBUFFER_SIZE is too small #endif -#ifdef USE_UNIX_SOCKETS -#define UNIX_SOCKET_PREFIX "localhost" -#endif - /* Reject URLs exceeding this length */ #define MAX_URL_LEN 0xffff @@ -147,15 +142,15 @@ * * Parameters: * - * 'h' [in] - struct Curl_handler pointer. + * 's' [in] - struct Curl_scheme pointer. * * Returns the family as a single bit protocol identifier. */ -static curl_prot_t get_protocol_family(const struct Curl_handler *h) +static curl_prot_t get_protocol_family(const struct Curl_scheme *s) { - DEBUGASSERT(h); - DEBUGASSERT(h->family); - return h->family; + DEBUGASSERT(s); + DEBUGASSERT(s->family); + return s->family; } void Curl_freeset(struct Curl_easy *data) @@ -165,17 +160,20 @@ enum dupblob j; for(i = (enum dupstring)0; i < STRING_LAST; i++) { - Curl_safefree(data->set.str[i]); + curlx_safefree(data->set.str[i]); } for(j = (enum dupblob)0; j < BLOB_LAST; j++) { - Curl_safefree(data->set.blobs[j]); + curlx_safefree(data->set.blobs[j]); } Curl_bufref_free(&data->state.referer); Curl_bufref_free(&data->state.url); - Curl_mime_cleanpart(&data->set.mimepost); +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + Curl_mime_cleanpart(data->set.mimepostp); + curlx_safefree(data->set.mimepostp); +#endif #ifndef CURL_DISABLE_COOKIES curl_slist_free_all(data->state.cookielist); @@ -187,14 +185,14 @@ static void up_free(struct Curl_easy *data) { struct urlpieces *up = &data->state.up; - Curl_safefree(up->scheme); - Curl_safefree(up->hostname); - Curl_safefree(up->port); - Curl_safefree(up->user); - Curl_safefree(up->password); - Curl_safefree(up->options); - Curl_safefree(up->path); - Curl_safefree(up->query); + curlx_safefree(up->scheme); + curlx_safefree(up->hostname); + curlx_safefree(up->port); + curlx_safefree(up->user); + curlx_safefree(up->password); + curlx_safefree(up->options); + curlx_safefree(up->path); + curlx_safefree(up->query); curl_url_cleanup(data->state.uh); data->state.uh = NULL; } @@ -241,9 +239,7 @@ curlx_free(data->state.range); /* release any resolve information this transfer kept */ - Curl_async_destroy(data); - Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */ - Curl_resolv_unlink(data, &data->state.dns[1]); + Curl_resolv_destroy_all(data); data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */ data->magic = 0; /* force a clear AFTER the possibly enforced removal from @@ -251,12 +247,12 @@ * handle might check the magic and so might any * DEBUGFUNCTION invoked for tracing */ - /* freed here just in case DONE was not called */ + /* freed here in case DONE was not called */ Curl_req_free(&data->req, data); /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); - Curl_safefree(data->state.first_host); + curlx_safefree(data->state.first_host); Curl_ssl_free_certinfo(data); Curl_bufref_free(&data->state.referer); @@ -277,45 +273,38 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif - Curl_safefree(data->state.most_recent_ftp_entrypath); - Curl_safefree(data->info.contenttype); - Curl_safefree(data->info.wouldredirect); + curlx_safefree(data->state.most_recent_ftp_entrypath); + curlx_safefree(data->info.contenttype); + curlx_safefree(data->info.wouldredirect); data_priority_cleanup(data); /* No longer a dirty share, if it exists */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - data->share->dirty--; - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } + if(Curl_share_easy_unlink(data)) + DEBUGASSERT(0); Curl_hash_destroy(&data->meta_hash); -#ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuserpwd); -#endif - Curl_safefree(data->state.aptr.uagent); - Curl_safefree(data->state.aptr.userpwd); - Curl_safefree(data->state.aptr.accept_encoding); - Curl_safefree(data->state.aptr.rangeline); - Curl_safefree(data->state.aptr.ref); - Curl_safefree(data->state.aptr.host); + curlx_safefree(data->state.aptr.uagent); + curlx_safefree(data->state.aptr.accept_encoding); + curlx_safefree(data->state.aptr.rangeline); + curlx_safefree(data->state.aptr.ref); + curlx_safefree(data->state.aptr.host); #ifndef CURL_DISABLE_COOKIES - Curl_safefree(data->state.aptr.cookiehost); + curlx_safefree(data->req.cookiehost); #endif #ifndef CURL_DISABLE_RTSP - Curl_safefree(data->state.aptr.rtsp_transport); + curlx_safefree(data->state.aptr.rtsp_transport); #endif - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); #ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuser); - Curl_safefree(data->state.aptr.proxypasswd); + curlx_safefree(data->state.aptr.proxyuser); + curlx_safefree(data->state.aptr.proxypasswd); #endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); #endif /* destruct wildcard structures if it is needed */ @@ -385,8 +374,6 @@ set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; #endif - Curl_mime_initpart(&set->mimepost); - Curl_ssl_easy_config_init(data); #ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; @@ -399,7 +386,7 @@ #endif set->new_file_perms = 0644; /* Default permissions */ - set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; + set->allowed_protocols = (curl_prot_t)CURLPROTO_64ALL; set->redir_protocols = CURLPROTO_REDIR; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -420,8 +407,8 @@ #endif #ifndef CURL_DISABLE_FTP set->wildcard_enabled = FALSE; - set->chunk_bgn = ZERO_NULL; - set->chunk_end = ZERO_NULL; + set->chunk_bgn = ZERO_NULL; + set->chunk_end = ZERO_NULL; set->fnmatch = ZERO_NULL; #endif set->tcp_keepalive = FALSE; @@ -435,6 +422,7 @@ set->sep_headers = TRUE; /* separated header lists by default */ set->buffer_size = READBUFFER_SIZE; set->upload_buffer_size = UPLOADBUFFER_DEFAULT; + set->upload_flags = CURLULFLAG_SEEN; set->happy_eyeballs_timeout = CURL_HET_DEFAULT; set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ @@ -457,7 +445,7 @@ static void easy_meta_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -517,9 +505,9 @@ DEBUGASSERT(conn); - if(conn->handler && conn->handler->disconnect && + if(conn->scheme && conn->scheme->run->disconnect && !conn->bits.shutdown_handler) - conn->handler->disconnect(data, conn, TRUE); + conn->scheme->run->disconnect(data, conn, TRUE); for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) { Curl_conn_cf_discard_all(data, conn, (int)i); @@ -530,29 +518,28 @@ #ifndef CURL_DISABLE_PROXY Curl_free_idnconverted_hostname(&conn->http_proxy.host); Curl_free_idnconverted_hostname(&conn->socks_proxy.host); - Curl_safefree(conn->http_proxy.user); - Curl_safefree(conn->socks_proxy.user); - Curl_safefree(conn->http_proxy.passwd); - Curl_safefree(conn->socks_proxy.passwd); - Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ - Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ + curlx_safefree(conn->http_proxy.user); + curlx_safefree(conn->socks_proxy.user); + curlx_safefree(conn->http_proxy.passwd); + curlx_safefree(conn->socks_proxy.passwd); + curlx_safefree(conn->http_proxy.host.rawalloc); /* http proxy name */ + curlx_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name */ #endif - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - Curl_safefree(conn->sasl_authzid); - Curl_safefree(conn->options); - Curl_safefree(conn->oauth_bearer); - Curl_safefree(conn->host.rawalloc); /* hostname buffer */ - Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ - Curl_safefree(conn->hostname_resolve); - Curl_safefree(conn->secondaryhostname); - Curl_safefree(conn->localdev); + curlx_safefree(conn->user); + curlx_safefree(conn->passwd); + curlx_safefree(conn->sasl_authzid); + curlx_safefree(conn->options); + curlx_safefree(conn->oauth_bearer); + curlx_safefree(conn->host.rawalloc); /* hostname buffer */ + curlx_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ + curlx_safefree(conn->secondaryhostname); + curlx_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); #ifdef USE_UNIX_SOCKETS - Curl_safefree(conn->unix_domain_socket); + curlx_safefree(conn->unix_domain_socket); #endif - Curl_safefree(conn->destination); + curlx_safefree(conn->destination); Curl_hash_destroy(&conn->meta_hash); curlx_free(conn); /* free all the connection oriented data */ @@ -569,7 +556,7 @@ { #ifndef CURL_DISABLE_HTTP /* If an HTTP protocol and multiplexing is enabled */ - if((conn->handler->protocol & PROTO_FAMILY_HTTP) && + if((conn->scheme->protocol & PROTO_FAMILY_HTTP) && (!conn->bits.protoconnstart || !conn->bits.close)) { if(Curl_multiplex_wanted(data->multi) && @@ -590,37 +577,19 @@ { if((data->proxytype == needle->proxytype) && (data->port == needle->port) && - curl_strequal(data->host.name, needle->host.name)) - return TRUE; + curl_strequal(data->host.name, needle->host.name)) { + if(Curl_timestrcmp(data->user, needle->user) || + Curl_timestrcmp(data->passwd, needle->passwd)) + return FALSE; + return TRUE; + } return FALSE; } - -static bool socks_proxy_info_matches(const struct proxy_info *data, - const struct proxy_info *needle) -{ - if(!proxy_info_matches(data, needle)) - return FALSE; - - /* the user information is case-sensitive - or at least it is not defined as case-insensitive - see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 */ - - /* curl_strequal does a case insensitive comparison, - so do not use it here! */ - if(Curl_timestrcmp(data->user, needle->user) || - Curl_timestrcmp(data->passwd, needle->passwd)) - return FALSE; - return TRUE; -} -#else -/* disabled, will not get called */ -#define proxy_info_matches(x, y) FALSE -#define socks_proxy_info_matches(x, y) FALSE #endif /* A connection has to have been idle for less than 'conn_max_idle_ms' - (the success rate is just too low after this), or created less than + (the success rate is too low after this), or created less than 'conn_max_age_ms' ago, to be subject for reuse. */ static bool conn_maxage(struct Curl_easy *data, struct connectdata *conn, @@ -668,18 +637,12 @@ /* avoid check if already too old */ dead = TRUE; } - else if(conn->handler->connection_check) { + else if(conn->scheme->run->connection_is_dead) { /* The protocol has a special method for checking the state of the connection. Use it to check if the connection is dead. */ - unsigned int state; - - /* briefly attach the connection to this transfer for the purpose of - checking it */ + /* briefly attach the connection for the check */ Curl_attach_connection(data, conn); - - state = conn->handler->connection_check(data, conn, CONNCHECK_ISDEAD); - dead = (state & CONNRESULT_DEAD); - /* detach the connection again */ + dead = conn->scheme->run->connection_is_dead(data, conn); Curl_detach_connection(data); } else { @@ -722,17 +685,7 @@ /* briefly attach for action */ Curl_attach_connection(data, conn); - if(conn->handler->connection_check) { - /* Do a protocol-specific keepalive check on the connection. */ - unsigned int rc; - rc = conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); - if(rc & CONNRESULT_DEAD) - result = CURLE_RECV_ERROR; - } - else { - /* Do the generic action on the FIRSTSOCKET filter chain */ - result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET); - } + result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET); Curl_detach_connection(data); conn->keepalive = *Curl_pgrs_now(data); @@ -747,8 +700,8 @@ sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN); sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN); - return (sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && - Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub)); + return sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && + Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub); } #endif @@ -759,7 +712,9 @@ BIT(may_multiplex); BIT(want_ntlm_http); BIT(want_proxy_ntlm_http); - + BIT(want_nego_http); + BIT(want_proxy_nego_http); + BIT(req_tls); /* require TLS use from a clear-text start */ BIT(wait_pipe); BIT(force_reuse); BIT(seen_pending_conn); @@ -771,7 +726,7 @@ struct url_conn_match *m) { /* connect-only or to-be-closed connections will not be reused */ - if(conn->connect_only || conn->bits.close || conn->bits.no_reuse) + if(conn->bits.connect_only || conn->bits.close || conn->bits.no_reuse) return FALSE; /* ip_version must match */ @@ -899,7 +854,7 @@ static bool url_match_ssl_use(struct connectdata *conn, struct url_conn_match *m) { - if(m->needle->handler->flags & PROTOPT_SSL) { + if(m->needle->scheme->flags & PROTOPT_SSL) { /* We are looking for SSL, if `conn` does not do it, not a match. */ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) return FALSE; @@ -907,10 +862,13 @@ else if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* If the protocol does not allow reuse of SSL connections OR is of another protocol family, not a match. */ - if(!(m->needle->handler->flags & PROTOPT_SSL_REUSE) || - (get_protocol_family(conn->handler) != m->needle->handler->protocol)) + if(!(m->needle->scheme->flags & PROTOPT_SSL_REUSE) || + (get_protocol_family(conn->scheme) != m->needle->scheme->protocol)) return FALSE; } + else if(m->req_tls) + /* a clear-text STARTTLS protocol with required TLS */ + return FALSE; return TRUE; } @@ -923,7 +881,7 @@ return FALSE; if(m->needle->bits.socksproxy && - !socks_proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) + !proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) return FALSE; if(m->needle->bits.httpproxy) { @@ -952,7 +910,7 @@ return TRUE; } #else -#define url_match_proxy_use(c, m) ((void)c, (void)m, TRUE) +#define url_match_proxy_use(c, m) ((void)(c), (void)(m), TRUE) #endif #ifndef CURL_DISABLE_HTTP @@ -961,7 +919,7 @@ { if(m->may_multiplex && (m->data->state.http_neg.allowed & (CURL_HTTP_V2x | CURL_HTTP_V3x)) && - (m->needle->handler->protocol & CURLPROTO_HTTP) && + (m->needle->scheme->protocol & CURLPROTO_HTTP) && !conn->httpversion_seen) { if(m->data->set.pipewait) { infof(m->data, "Server upgrade does not support multiplex yet, wait"); @@ -980,26 +938,26 @@ { /* If looking for HTTP and the HTTP versions allowed do not include * the HTTP version of conn, continue looking. */ - if((m->needle->handler->protocol & PROTO_FAMILY_HTTP)) { + if((m->needle->scheme->protocol & PROTO_FAMILY_HTTP)) { switch(Curl_conn_http_version(m->data, conn)) { case 30: if(!(m->data->state.http_neg.allowed & CURL_HTTP_V3x)) { DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T - ", we do not want h3", conn->connection_id)); + ", we do not want h3", conn->connection_id)); return FALSE; } break; case 20: if(!(m->data->state.http_neg.allowed & CURL_HTTP_V2x)) { DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T - ", we do not want h2", conn->connection_id)); + ", we do not want h2", conn->connection_id)); return FALSE; } break; default: if(!(m->data->state.http_neg.allowed & CURL_HTTP_V1x)) { DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T - ", we do not want h1", conn->connection_id)); + ", we do not want h1", conn->connection_id)); return FALSE; } break; @@ -1008,8 +966,8 @@ return TRUE; } #else -#define url_match_http_multiplex(c, m) ((void)c, (void)m, TRUE) -#define url_match_http_version(c, m) ((void)c, (void)m, TRUE) +#define url_match_http_multiplex(c, m) ((void)(c), (void)(m), TRUE) +#define url_match_http_version(c, m) ((void)(c), (void)(m), TRUE) #endif static bool url_match_proto_config(struct connectdata *conn, @@ -1019,13 +977,13 @@ return FALSE; #ifdef USE_SSH - if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_SSH) { + if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_SSH) { if(!ssh_config_matches(m->needle, conn)) return FALSE; } #endif #ifndef CURL_DISABLE_FTP - else if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_FTP) { + else if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_FTP) { if(!ftp_conns_match(m->needle, conn)) return FALSE; } @@ -1036,7 +994,7 @@ static bool url_match_auth(struct connectdata *conn, struct url_conn_match *m) { - if(!(m->needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { + if(!(m->needle->scheme->flags & PROTOPT_CREDSPERREQUEST)) { /* This protocol requires credentials per connection, so verify that we are using the same name and password as well */ if(Curl_timestrcmp(m->needle->user, conn->user) || @@ -1062,23 +1020,23 @@ { /* Additional match requirements if talking TLS OR * not talking to an HTTP proxy OR using a tunnel through a proxy */ - if((m->needle->handler->flags & PROTOPT_SSL) + if((m->needle->scheme->flags & PROTOPT_SSL) #ifndef CURL_DISABLE_PROXY || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy #endif ) { - if(!curl_strequal(m->needle->handler->scheme, conn->handler->scheme)) { + if(!curl_strequal(m->needle->scheme->name, conn->scheme->name)) { /* `needle` and `conn` do not have the same scheme... */ - if(get_protocol_family(conn->handler) != m->needle->handler->protocol) { + if(get_protocol_family(conn->scheme) != m->needle->scheme->protocol) { /* and `conn`s protocol family is not the protocol `needle` wants. * IMAPS would work for IMAP, but no vice versa. */ return FALSE; } /* We are in an IMAPS vs IMAP like case. We expect `conn` to have SSL */ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - DEBUGF(infof(m->data, - "Connection #%" FMT_OFF_T " has compatible protocol family, " - "but no SSL, no match", conn->connection_id)); + DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T + " has compatible protocol family, but no SSL, no match", + conn->connection_id)); return FALSE; } } @@ -1102,10 +1060,9 @@ struct url_conn_match *m) { /* If talking TLS, conn needs to use the same SSL options. */ - if((m->needle->handler->flags & PROTOPT_SSL) && + if((m->needle->scheme->flags & PROTOPT_SSL) && !Curl_ssl_conn_config_match(m->data, conn, FALSE)) { - DEBUGF(infof(m->data, - "Connection #%" FMT_OFF_T + DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T " has different SSL parameters, cannot reuse", conn->connection_id)); return FALSE; @@ -1125,11 +1082,17 @@ if(m->want_ntlm_http) { if(Curl_timestrcmp(m->needle->user, conn->user) || Curl_timestrcmp(m->needle->passwd, conn->passwd)) { - /* we prefer a credential match, but this is at least a connection - that can be reused and "upgraded" to NTLM */ - if(conn->http_ntlm_state == NTLMSTATE_NONE) + that can be reused and "upgraded" to NTLM if it does + not have any auth ongoing. */ +#ifdef USE_SPNEGO + if((conn->http_ntlm_state == NTLMSTATE_NONE) + && (conn->http_negotiate_state == GSS_AUTHNONE)) { +#else + if(conn->http_ntlm_state == NTLMSTATE_NONE) { +#endif m->found = conn; + } return FALSE; } } @@ -1178,7 +1141,64 @@ return TRUE; } #else -#define url_match_auth_ntlm(c, m) ((void)c, (void)m, TRUE) +#define url_match_auth_ntlm(c, m) ((void)(c), (void)(m), TRUE) +#endif + +#ifdef USE_SPNEGO +static bool url_match_auth_nego(struct connectdata *conn, + struct url_conn_match *m) +{ + /* If we are looking for an HTTP+Negotiate connection, check if this is + already authenticating with the right credentials. If not, keep looking + so that we can reuse Negotiate connections if possible. */ + if(m->want_nego_http) { + if(Curl_timestrcmp(m->needle->user, conn->user) || + Curl_timestrcmp(m->needle->passwd, conn->passwd)) + return FALSE; + } + else if(conn->http_negotiate_state != GSS_AUTHNONE) { + /* Connection is using Negotiate auth but we do not want Negotiate */ + return FALSE; + } + +#ifndef CURL_DISABLE_PROXY + /* Same for Proxy Negotiate authentication */ + if(m->want_proxy_nego_http) { + /* Both conn->http_proxy.user and conn->http_proxy.passwd can be + * NULL */ + if(!conn->http_proxy.user || !conn->http_proxy.passwd) + return FALSE; + + if(Curl_timestrcmp(m->needle->http_proxy.user, + conn->http_proxy.user) || + Curl_timestrcmp(m->needle->http_proxy.passwd, + conn->http_proxy.passwd)) + return FALSE; + } + else if(conn->proxy_negotiate_state != GSS_AUTHNONE) { + /* Proxy connection is using Negotiate auth but we do not want Negotiate */ + return FALSE; + } +#endif + if(m->want_nego_http || m->want_proxy_nego_http) { + /* Credentials are already checked, we may use this connection. We MUST + * use a connection where it has already been fully negotiated. If it has + * not, we keep on looking for a better one. */ + m->found = conn; + if((m->want_nego_http && + (conn->http_negotiate_state != GSS_AUTHNONE)) || + (m->want_proxy_nego_http && + (conn->proxy_negotiate_state != GSS_AUTHNONE))) { + /* We must use this connection, no other */ + m->force_reuse = TRUE; + return TRUE; + } + return FALSE; /* get another */ + } + return TRUE; +} +#else +#define url_match_auth_nego(c, m) ((void)(c), (void)(m), TRUE) #endif static bool url_match_conn(struct connectdata *conn, void *userdata) @@ -1224,6 +1244,11 @@ else if(m->force_reuse) return TRUE; + if(!url_match_auth_nego(conn, m)) + return FALSE; + else if(m->force_reuse) + return TRUE; + if(!url_match_multiplex_limits(conn, m)) return FALSE; @@ -1238,10 +1263,9 @@ return TRUE; } -static bool url_match_result(bool result, void *userdata) +static bool url_match_result(void *userdata) { struct url_conn_match *match = userdata; - (void)result; if(match->found) { /* Attach it now while still under lock, so the connection does * no longer appear idle and can be reaped. */ @@ -1264,52 +1288,61 @@ } /* - * Given one filled in connection struct (named needle), this function should - * detect if there already is one that has all the significant details - * exactly the same and thus should be used instead. + * Given a transfer and a prototype connection (needle), + * find and attach an existing connection that matches. * - * If there is a match, this function returns TRUE - and has marked the - * connection as 'in-use'. It must later be called with ConnectionDone() to - * return back to 'idle' (unused) state. - * - * The force_reuse flag is set if the connection must be used. + * Return TRUE if an existing connection was attached. + * `waitpipe` is TRUE if no existing connection matched, but there + * might be suitable one in the near future (common cause: multiplexing + * capability has not been determined yet, e.g. ALPN handshake). */ -static bool ConnectionExists(struct Curl_easy *data, - struct connectdata *needle, - struct connectdata **usethis, - bool *force_reuse, - bool *waitpipe) +static bool url_attach_existing(struct Curl_easy *data, + struct connectdata *needle, + bool *waitpipe) { struct url_conn_match match; - bool result; + bool success; + DEBUGASSERT(!data->conn); memset(&match, 0, sizeof(match)); match.data = data; match.needle = needle; match.may_multiplex = xfer_may_multiplex(data, needle); #ifdef USE_NTLM - match.want_ntlm_http = ((data->state.authhost.want & CURLAUTH_NTLM) && - (needle->handler->protocol & PROTO_FAMILY_HTTP)); + match.want_ntlm_http = + (data->state.authhost.want & CURLAUTH_NTLM) && + (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_ntlm_http = - (needle->bits.proxy_user_passwd && - (data->state.authproxy.want & CURLAUTH_NTLM) && - (needle->handler->protocol & PROTO_FAMILY_HTTP)); + needle->bits.proxy_user_passwd && + (data->state.authproxy.want & CURLAUTH_NTLM) && + (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif #endif +#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) + match.want_nego_http = + (data->state.authhost.want & CURLAUTH_NEGOTIATE) && + (needle->scheme->protocol & PROTO_FAMILY_HTTP); +#ifndef CURL_DISABLE_PROXY + match.want_proxy_nego_http = + needle->bits.proxy_user_passwd && + (data->state.authproxy.want & CURLAUTH_NEGOTIATE) && + (needle->scheme->protocol & PROTO_FAMILY_HTTP); +#endif +#endif + match.req_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; + /* Find a connection in the pool that matches what "data + needle" * requires. If a suitable candidate is found, it is attached to "data". */ - result = Curl_cpool_find(data, needle->destination, - url_match_conn, url_match_result, &match); + success = Curl_cpool_find(data, needle->destination, + url_match_conn, url_match_result, &match); /* wait_pipe is TRUE if we encounter a bundle that is undecided. There * is no matching connection then, yet. */ - *usethis = match.found; - *force_reuse = match.force_reuse; - *waitpipe = match.wait_pipe; - return result; + *waitpipe = (bool)match.wait_pipe; + return success; } /* @@ -1329,7 +1362,7 @@ conn->send_idx = 0; /* default for sending transfer data */ conn->connection_id = -1; /* no ID */ conn->attached_xfers = 0; - conn->remote_port = -1; /* unknown at this point */ + conn->remote_port = 0; /* unknown at this point */ /* Store creation time to help future close decision making */ conn->created = *Curl_pgrs_now(data); @@ -1341,7 +1374,7 @@ conn->http_proxy.proxytype = data->set.proxytype; conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; - /* note that these two proxy bits are now just on what looks to be + /* note that these two proxy bits are set on what looks to be requested, they may be altered down the road */ conn->bits.proxy = (data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]); @@ -1365,7 +1398,7 @@ conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; #endif conn->ip_version = data->set.ipver; - conn->connect_only = data->set.connect_only; + conn->bits.connect_only = (bool)data->set.connect_only; conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ /* Store the local bind parameters that will be used for this connection */ @@ -1396,233 +1429,13 @@ return NULL; } -const struct Curl_handler *Curl_get_scheme_handler(const char *scheme) -{ - return Curl_getn_scheme_handler(scheme, strlen(scheme)); -} - -/* returns the handler if the given scheme is built-in */ -const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, - size_t len) -{ - /* table generated by schemetable.c: - 1. gcc schemetable.c && ./a.out - 2. check how small the table gets - 3. tweak the hash algorithm, then rerun from 1 - 4. when the table is good enough - 5. copy the table into this source code - 6. make sure this function uses the same hash function that worked for - schemetable.c - 7. if needed, adjust the #ifdefs in schemetable.c and rerun - */ - static const struct Curl_handler * const protocols[67] = { -#ifndef CURL_DISABLE_FILE - &Curl_handler_file, -#else - NULL, -#endif - NULL, NULL, -#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER) - &Curl_handler_gophers, -#else - NULL, -#endif - NULL, -#ifdef USE_LIBRTMP - &Curl_handler_rtmpe, -#else - NULL, -#endif -#ifndef CURL_DISABLE_SMTP - &Curl_handler_smtp, -#else - NULL, -#endif -#ifdef USE_SSH - &Curl_handler_sftp, -#else - NULL, -#endif -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - &Curl_handler_smb, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP) - &Curl_handler_smtps, -#else - NULL, -#endif -#ifndef CURL_DISABLE_TELNET - &Curl_handler_telnet, -#else - NULL, -#endif -#ifndef CURL_DISABLE_GOPHER - &Curl_handler_gopher, -#else - NULL, -#endif -#ifndef CURL_DISABLE_TFTP - &Curl_handler_tftp, -#else - NULL, -#endif - NULL, NULL, NULL, -#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) - &Curl_handler_ftps, -#else - NULL, -#endif -#ifndef CURL_DISABLE_HTTP - &Curl_handler_http, -#else - NULL, -#endif -#ifndef CURL_DISABLE_IMAP - &Curl_handler_imap, -#else - NULL, -#endif -#ifdef USE_LIBRTMP - &Curl_handler_rtmps, -#else - NULL, -#endif -#ifdef USE_LIBRTMP - &Curl_handler_rtmpt, -#else - NULL, -#endif - NULL, NULL, NULL, -#if !defined(CURL_DISABLE_LDAP) && \ - !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) - &Curl_handler_ldaps, -#else - NULL, -#endif -#if !defined(CURL_DISABLE_WEBSOCKETS) && \ - defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_wss, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_https, -#else - NULL, -#endif - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -#ifndef CURL_DISABLE_RTSP - &Curl_handler_rtsp, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_SMB) && \ - defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4) - &Curl_handler_smbs, -#else - NULL, -#endif -#ifdef USE_SSH - &Curl_handler_scp, -#else - NULL, -#endif - NULL, NULL, NULL, -#ifndef CURL_DISABLE_POP3 - &Curl_handler_pop3, -#else - NULL, -#endif - NULL, NULL, -#ifdef USE_LIBRTMP - &Curl_handler_rtmp, -#else - NULL, -#endif - NULL, NULL, NULL, -#ifdef USE_LIBRTMP - &Curl_handler_rtmpte, -#else - NULL, -#endif - NULL, NULL, NULL, -#ifndef CURL_DISABLE_DICT - &Curl_handler_dict, -#else - NULL, -#endif - NULL, NULL, NULL, -#ifndef CURL_DISABLE_MQTT - &Curl_handler_mqtt, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) - &Curl_handler_pop3s, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP) - &Curl_handler_imaps, -#else - NULL, -#endif - NULL, -#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_ws, -#else - NULL, -#endif - NULL, -#ifdef USE_LIBRTMP - &Curl_handler_rtmpts, -#else - NULL, -#endif -#ifndef CURL_DISABLE_LDAP - &Curl_handler_ldap, -#else - NULL, -#endif - NULL, NULL, -#ifndef CURL_DISABLE_FTP - &Curl_handler_ftp, -#else - NULL, -#endif - }; - - if(len && (len <= 7)) { - const char *s = scheme; - size_t l = len; - const struct Curl_handler *h; - unsigned int c = 978; - while(l) { - c <<= 5; - c += (unsigned int)Curl_raw_tolower(*s); - s++; - l--; - } - - h = protocols[c % 67]; - if(h && curl_strnequal(scheme, h->scheme, len) && !h->scheme[len]) - return h; - } - return NULL; -} - static CURLcode findprotocol(struct Curl_easy *data, struct connectdata *conn, const char *protostr) { - const struct Curl_handler *p = Curl_get_scheme_handler(protostr); + const struct Curl_scheme *p = Curl_get_scheme(protostr); - if(p && /* Protocol found in table. Check if allowed */ + if(p && p->run && /* Protocol found supported. Check if allowed */ (data->set.allowed_protocols & p->protocol)) { /* it is allowed for "normal" request, now do an extra check if this is @@ -1633,7 +1446,7 @@ ; else { /* Perform setup complement if some. */ - conn->handler = conn->given = p; + conn->scheme = conn->given = p; /* 'port' and 'remote_port' are set in setup_connection_internals() */ return CURLE_OK; } @@ -1674,7 +1487,7 @@ { char *zoneid; CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); -#ifdef CURL_DISABLE_VERBOSE_STRINGS +#if !defined(HAVE_IF_NAMETOINDEX) || !defined(CURLVERBOSE) (void)data; #endif @@ -1686,20 +1499,11 @@ conn->scope_id = (unsigned int)scope; #ifdef HAVE_IF_NAMETOINDEX else { -#elif defined(_WIN32) - else if(Curl_if_nametoindex) { -#endif - -#if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32) /* Zone identifier is not numeric */ unsigned int scopeidx = 0; -#ifdef HAVE_IF_NAMETOINDEX scopeidx = if_nametoindex(zoneid); -#else - scopeidx = Curl_if_nametoindex(zoneid); -#endif if(!scopeidx) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE char buffer[STRERROR_LEN]; infof(data, "Invalid zoneid: %s; %s", zoneid, curlx_strerror(errno, buffer, sizeof(buffer))); @@ -1708,7 +1512,7 @@ else conn->scope_id = scopeidx; } -#endif /* HAVE_IF_NAMETOINDEX || _WIN32 */ +#endif /* HAVE_IF_NAMETOINDEX */ curlx_free(zoneid); } @@ -1727,6 +1531,7 @@ CURLU *uh; CURLUcode uc; char *hostname; + size_t hlen; bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); up_free(data); /* cleanup previous leftovers first */ @@ -1785,17 +1590,16 @@ failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); return CURLE_URL_MALFORMAT; } + hostname = data->state.up.hostname; + hlen = hostname ? strlen(hostname) : 0; if(hostname && hostname[0] == '[') { /* This looks like an IPv6 address literal. See if there is an address scope. */ - size_t hlen; - conn->bits.ipv6_ip = TRUE; - /* cut off the brackets! */ + /* cut off the brackets after copying this! */ hostname++; - hlen = strlen(hostname); - hostname[hlen - 1] = 0; + hlen -= 2; zonefrom_url(uh, data, conn); } @@ -1804,6 +1608,7 @@ conn->host.rawalloc = curlx_strdup(hostname ? hostname : ""); if(!conn->host.rawalloc) return CURLE_OUT_OF_MEMORY; + conn->host.rawalloc[hlen] = 0; /* cut off for ipv6 case */ conn->host.name = conn->host.rawalloc; /************************************************************* @@ -1819,7 +1624,7 @@ /* This MUST use the IDN decoded name */ if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { char *url; - Curl_safefree(data->state.up.scheme); + curlx_safefree(data->state.up.scheme); uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); if(uc) return Curl_uc_to_curlcode(uc); @@ -1852,7 +1657,7 @@ if(!uc) { char *decoded; result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, - conn->handler->flags&PROTOPT_USERPWDCTRL ? + conn->scheme->flags&PROTOPT_USERPWDCTRL ? REJECT_ZERO : REJECT_CTRL); if(result) return result; @@ -1874,7 +1679,7 @@ if(!uc) { char *decoded; result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, - conn->handler->flags&PROTOPT_USERPWDCTRL ? + conn->scheme->flags&PROTOPT_USERPWDCTRL ? REJECT_ZERO : REJECT_CTRL); if(result) return result; @@ -1929,7 +1734,7 @@ #ifdef USE_IPV6 if(data->set.scope_id) - /* Override any scope that was set above. */ + /* Override any scope that was set above. */ conn->scope_id = data->set.scope_id; #endif @@ -1980,12 +1785,12 @@ struct connectdata *conn) { const char *hostname; - int port; + uint16_t port; CURLcode result; DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); - if(conn->handler->setup_connection) { - result = conn->handler->setup_connection(data, conn); + if(conn->scheme->run->setup_connection) { + result = conn->scheme->run->setup_connection(data, conn); if(result) return result; } @@ -2000,19 +1805,21 @@ else #endif { - port = conn->remote_port; - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; + port = conn->bits.conn_to_port ? + conn->conn_to_port : conn->remote_port; + hostname = conn->bits.conn_to_host ? + conn->conn_to_host.name : conn->host.name; } #ifdef USE_IPV6 - conn->destination = curl_maprintf("%u/%d/%s", conn->scope_id, port, - hostname); -#else - conn->destination = curl_maprintf("%d/%s", port, hostname); + /* IPv6 addresses with a scope_id (0 is default == global) have a + * printable representation with a '%<scope_id>' suffix. */ + if(conn->scope_id) + conn->destination = curl_maprintf("[%s:%u]%%%u", hostname, port, + conn->scope_id); + else #endif + conn->destination = curl_maprintf("%s:%u", hostname, port); if(!conn->destination) return CURLE_OUT_OF_MEMORY; @@ -2053,13 +1860,11 @@ * checked if the lowercase versions do not exist. */ char proxy_env[20]; - const char *envp = proxy_env; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif + const char *envp; + VERBOSE(envp = proxy_env); curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", - conn->handler->scheme); + conn->scheme->name); /* read the protocol proxy: */ proxy = curl_getenv(proxy_env); @@ -2118,7 +1923,7 @@ * that may exist registered to the same proxy host. */ static CURLcode parse_proxy(struct Curl_easy *data, - struct connectdata *conn, char *proxy, + struct connectdata *conn, const char *proxy, long proxytype) { char *portptr = NULL; @@ -2184,15 +1989,13 @@ goto error; } -#ifdef USE_SSL - if(!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) -#endif - if(IS_HTTPS_PROXY(proxytype)) { - failf(data, "Unsupported proxy \'%s\', libcurl is built without the " - "HTTPS-proxy support.", proxy); - result = CURLE_NOT_BUILT_IN; - goto error; - } + if(IS_HTTPS_PROXY(proxytype) && + !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { + failf(data, "Unsupported proxy \'%s\', libcurl is built without the " + "HTTPS-proxy support.", proxy); + result = CURLE_NOT_BUILT_IN; + goto error; + } sockstype = proxytype == CURLPROXY_SOCKS5_HOSTNAME || @@ -2203,7 +2006,7 @@ proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; proxyinfo->proxytype = (unsigned char)proxytype; - /* Is there a username and password given in this proxy url? */ + /* Is there a username and password given in this proxy URL? */ uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); if(uc && (uc != CURLUE_NO_USER)) { result = Curl_uc_to_curlcode(uc); @@ -2222,7 +2025,7 @@ proxyuser = NULL; if(result) goto error; - Curl_safefree(proxyinfo->passwd); + curlx_safefree(proxyinfo->passwd); if(!proxypasswd) { proxypasswd = curlx_strdup(""); if(!proxypasswd) { @@ -2341,7 +2144,7 @@ if(conn->http_proxy.passwd) result = CURLE_OK; else - Curl_safefree(conn->http_proxy.user); + curlx_safefree(conn->http_proxy.user); } return result; } @@ -2402,15 +2205,15 @@ if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? data->set.str[STRING_NOPROXY] : no_proxy)) { - Curl_safefree(proxy); - Curl_safefree(socksproxy); + curlx_safefree(proxy); + curlx_safefree(socksproxy); } #ifndef CURL_DISABLE_HTTP else if(!proxy && !socksproxy) /* if the host is not in the noproxy list, detect proxy. */ proxy = detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ - Curl_safefree(no_proxy); + curlx_safefree(no_proxy); #ifdef USE_UNIX_SOCKETS /* For the time being do not mix proxy and Unix domain sockets. See #1274 */ @@ -2420,13 +2223,13 @@ } #endif - if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { + if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { curlx_free(proxy); /* Do not bother with an empty proxy string or if the protocol does not work with network */ proxy = NULL; } if(socksproxy && (!*socksproxy || - (conn->handler->flags & PROTOPT_NONETWORK))) { + (conn->scheme->flags & PROTOPT_NONETWORK))) { curlx_free(socksproxy); /* Do not bother with an empty socks proxy string or if the protocol does not work with network */ @@ -2439,18 +2242,18 @@ * connection that may exist registered to the same proxy host. ***********************************************************************/ if(proxy || socksproxy) { - long ptype = conn->http_proxy.proxytype; if(proxy) { - result = parse_proxy(data, conn, proxy, ptype); - Curl_safefree(proxy); /* parse_proxy copies the proxy string */ + result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype); + curlx_safefree(proxy); /* parse_proxy copies the proxy string */ if(result) goto out; } if(socksproxy) { - result = parse_proxy(data, conn, socksproxy, ptype); + result = parse_proxy(data, conn, socksproxy, + conn->socks_proxy.proxytype); /* parse_proxy copies the socks proxy string */ - Curl_safefree(socksproxy); + curlx_safefree(socksproxy); if(result) goto out; } @@ -2462,10 +2265,10 @@ goto out; #else /* force this connection's protocol to become HTTP if compatible */ - if(!(conn->handler->protocol & PROTO_FAMILY_HTTP)) { - if((conn->handler->flags & PROTOPT_PROXY_AS_HTTP) && + if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { + if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) && !conn->bits.tunnel_proxy) - conn->handler = &Curl_handler_http; + conn->scheme = &Curl_scheme_http; else /* if not converting to HTTP over the proxy, enforce tunneling */ conn->bits.tunnel_proxy = TRUE; @@ -2584,13 +2387,13 @@ (size_t)(login + len - osep)) - 1 : 0); /* Clone the user portion buffer, which can be zero length */ - ubuf = Curl_memdup0(login, ulen); + ubuf = curlx_memdup0(login, ulen); if(!ubuf) goto error; /* Clone the password portion buffer */ if(psep) { - pbuf = Curl_memdup0(&psep[1], plen); + pbuf = curlx_memdup0(&psep[1], plen); if(!pbuf) goto error; } @@ -2599,7 +2402,7 @@ if(optionsp) { char *obuf = NULL; if(olen) { - obuf = Curl_memdup0(&osep[1], olen); + obuf = curlx_memdup0(&osep[1], olen); if(!obuf) goto error; } @@ -2625,7 +2428,6 @@ static CURLcode parse_remote_port(struct Curl_easy *data, struct connectdata *conn) { - if(data->set.use_port && data->state.allow_port) { /* if set, we use this instead of the port possibly given in the URL */ char portbuf[16]; @@ -2676,8 +2478,8 @@ #ifndef CURL_DISABLE_NETRC if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - Curl_safefree(*userp); - Curl_safefree(*passwdp); + curlx_safefree(*userp); + curlx_safefree(*passwdp); } conn->bits.netrc = FALSE; if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { @@ -2710,7 +2512,7 @@ return CURLE_READ_ERROR; } else { - if(!(conn->handler->flags & PROTOPT_USERPWDCTRL)) { + if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { /* if the protocol cannot handle control codes in credentials, make sure there are none */ if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) { @@ -2793,7 +2595,7 @@ const char *setpasswd = CURL_DEFAULT_PASSWORD; /* If our protocol needs a password and we have none, use the defaults */ - if((conn->handler->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) + if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) ; else { setuser = ""; @@ -2833,10 +2635,6 @@ int port = -1; CURLcode result = CURLE_OK; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - *hostname_result = NULL; *port_result = -1; @@ -2942,19 +2740,15 @@ ptr++; } else { - /* check whether the URL's hostname matches */ - size_t hostname_to_match_len; - char *hostname_to_match = curl_maprintf("%s%s%s", - conn->bits.ipv6_ip ? "[" : "", - conn->host.name, - conn->bits.ipv6_ip ? "]" : ""); - if(!hostname_to_match) - return CURLE_OUT_OF_MEMORY; - hostname_to_match_len = strlen(hostname_to_match); - host_match = curl_strnequal(ptr, hostname_to_match, - hostname_to_match_len); - curlx_free(hostname_to_match); - ptr += hostname_to_match_len; + /* check whether the URL's hostname matches. Use the URL hostname + * when it was an IPv6 address. Otherwise use the connection's hostname + * that has IDN conversion. */ + char *hostname_to_match = + (data->state.up.hostname && data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name; + size_t hlen = strlen(hostname_to_match); + host_match = curl_strnequal(ptr, hostname_to_match, hlen); + ptr += hlen; host_match = host_match && *ptr == ':'; ptr++; @@ -2968,7 +2762,7 @@ } else { /* check whether the URL's port matches */ - char *ptr_next = strchr(ptr, ':'); + const char *ptr_next = strchr(ptr, ':'); if(ptr_next) { curl_off_t port_to_match; if(!curlx_str_number(&ptr, &port_to_match, 0xffff) && @@ -3015,13 +2809,13 @@ else { /* no "connect to host" */ conn->bits.conn_to_host = FALSE; - Curl_safefree(host); + curlx_safefree(host); } if(port >= 0) { - conn->conn_to_port = port; + conn->conn_to_port = (uint16_t)port; conn->bits.conn_to_port = TRUE; - infof(data, "Connecting to port: %d", port); + infof(data, "Connecting to port: %u", conn->conn_to_port); } else { /* no "connect to port" */ @@ -3034,7 +2828,7 @@ #ifndef CURL_DISABLE_ALTSVC if(data->asi && !host && (port == -1) && - ((conn->handler->protocol == CURLPROTO_HTTPS) || + ((conn->scheme->protocol == CURLPROTO_HTTPS) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") @@ -3114,7 +2908,7 @@ } } else if(hit) { - char *hostd = curlx_strdup((char *)as->dst.host); + char *hostd = curlx_strdup(as->dst.host); if(!hostd) return CURLE_OUT_OF_MEMORY; conn->conn_to_host.rawalloc = hostd; @@ -3123,7 +2917,7 @@ conn->conn_to_port = as->dst.port; conn->bits.conn_to_port = TRUE; conn->bits.altused = TRUE; - infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d", + infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u", Curl_alpnid2str(srcalpnid), host, conn->remote_port, Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); if(srcalpnid != as->dst.alpnid) { @@ -3151,166 +2945,50 @@ return result; } -#ifdef USE_UNIX_SOCKETS -static CURLcode resolve_unix(struct Curl_easy *data, - struct connectdata *conn, - char *unix_path, - struct Curl_dns_entry **pdns) -{ - struct Curl_dns_entry *hostaddr; - bool longpath = FALSE; - - DEBUGASSERT(unix_path); - *pdns = NULL; - - /* Unix domain sockets are local. The host gets ignored, just use the - * specified domain socket address. Do not cache "DNS entries". There is - * no DNS involved and we already have the file system path available. */ - hostaddr = curlx_calloc(1, sizeof(struct Curl_dns_entry)); - if(!hostaddr) - return CURLE_OUT_OF_MEMORY; - - hostaddr->addr = Curl_unix2addr(unix_path, &longpath, - conn->bits.abstract_unix_socket); - if(!hostaddr->addr) { - if(longpath) - /* Long paths are not supported for now */ - failf(data, "Unix socket path too long: '%s'", unix_path); - curlx_free(hostaddr); - return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY; - } - - hostaddr->refcount = 1; /* connection is the only one holding this */ - *pdns = hostaddr; - return CURLE_OK; -} -#endif - -/************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ -static CURLcode resolve_server(struct Curl_easy *data, - struct connectdata *conn, - bool *async, - struct Curl_dns_entry **pdns) -{ - struct hostname *ehost; - int eport; - timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE); - const char *peertype = "host"; - CURLcode result; - - *pdns = NULL; - -#ifdef USE_UNIX_SOCKETS - { - char *unix_path = conn->unix_domain_socket; - -#ifndef CURL_DISABLE_PROXY - if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name && - !strncmp(UNIX_SOCKET_PREFIX "/", - conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) - unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; -#endif - - if(unix_path) { - /* This only works if previous transport is TRNSPRT_TCP. Check it? */ - conn->transport_wanted = TRNSPRT_UNIX; - return resolve_unix(data, conn, unix_path, pdns); - } - } -#endif - -#ifndef CURL_DISABLE_PROXY - if(CONN_IS_PROXIED(conn)) { - ehost = conn->bits.socksproxy ? &conn->socks_proxy.host : - &conn->http_proxy.host; - eport = conn->bits.socksproxy ? conn->socks_proxy.port : - conn->http_proxy.port; - peertype = "proxy"; - } - else -#endif - { - ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; - /* If not connecting via a proxy, extract the port from the URL, if it is - * there, thus overriding any defaults that might have been set above. */ - eport = conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port; - } - - /* Resolve target host right on */ - conn->hostname_resolve = curlx_strdup(ehost->name); - if(!conn->hostname_resolve) - return CURLE_OUT_OF_MEMORY; - - result = Curl_resolv_timeout(data, conn->hostname_resolve, - eport, conn->ip_version, - pdns, timeout_ms); - DEBUGASSERT(!result || !*pdns); - if(result == CURLE_AGAIN) { - *async = TRUE; - return CURLE_OK; - } - else if(result == CURLE_OPERATION_TIMEDOUT) { - failf(data, "Failed to resolve %s '%s' with timeout after %" - FMT_TIMEDIFF_T " ms", peertype, ehost->dispname, - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle)); - return CURLE_OPERATION_TIMEDOUT; - } - else if(result) { - failf(data, "Could not resolve %s: %s", peertype, ehost->dispname); - return result; - } - DEBUGASSERT(*pdns); - return CURLE_OK; -} - static void url_move_hostname(struct hostname *dest, struct hostname *src) { - Curl_safefree(dest->rawalloc); + curlx_safefree(dest->rawalloc); Curl_free_idnconverted_hostname(dest); *dest = *src; memset(src, 0, sizeof(*src)); } /* - * Cleanup the connection `temp`, just allocated for `data`, before using the - * previously `existing` one for `data`. All relevant info is copied over - * and `temp` is freed. + * Adjust reused connection settings to the transfer/needle. */ -static void reuse_conn(struct Curl_easy *data, - struct connectdata *temp, - struct connectdata *existing) +static void url_conn_reuse_adjust(struct Curl_easy *data, + struct connectdata *needle) { - /* get the user+password information from the temp struct since it may - * be new for this request even when we reuse an existing connection */ - if(temp->user) { + struct connectdata *conn = data->conn; + + /* get the user+password information from the needle since it may + * be new for this request even when we reuse conn */ + if(needle->user) { /* use the new username and password though */ - curlx_free(existing->user); - curlx_free(existing->passwd); - existing->user = temp->user; - existing->passwd = temp->passwd; - temp->user = NULL; - temp->passwd = NULL; + curlx_free(conn->user); + curlx_free(conn->passwd); + conn->user = needle->user; + conn->passwd = needle->passwd; + needle->user = NULL; + needle->passwd = NULL; } #ifndef CURL_DISABLE_PROXY - existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd; - if(existing->bits.proxy_user_passwd) { + conn->bits.proxy_user_passwd = needle->bits.proxy_user_passwd; + if(conn->bits.proxy_user_passwd) { /* use the new proxy username and proxy password though */ - curlx_free(existing->http_proxy.user); - curlx_free(existing->socks_proxy.user); - curlx_free(existing->http_proxy.passwd); - curlx_free(existing->socks_proxy.passwd); - existing->http_proxy.user = temp->http_proxy.user; - existing->socks_proxy.user = temp->socks_proxy.user; - existing->http_proxy.passwd = temp->http_proxy.passwd; - existing->socks_proxy.passwd = temp->socks_proxy.passwd; - temp->http_proxy.user = NULL; - temp->socks_proxy.user = NULL; - temp->http_proxy.passwd = NULL; - temp->socks_proxy.passwd = NULL; + curlx_free(conn->http_proxy.user); + curlx_free(conn->socks_proxy.user); + curlx_free(conn->http_proxy.passwd); + curlx_free(conn->socks_proxy.passwd); + conn->http_proxy.user = needle->http_proxy.user; + conn->socks_proxy.user = needle->socks_proxy.user; + conn->http_proxy.passwd = needle->http_proxy.passwd; + conn->socks_proxy.passwd = needle->socks_proxy.passwd; + needle->http_proxy.user = NULL; + needle->socks_proxy.user = NULL; + needle->http_proxy.passwd = NULL; + needle->socks_proxy.passwd = NULL; } #endif @@ -3323,64 +3001,29 @@ * a new request to the same remote endpoint of an existing conn. * We want to reuse an existing conn to the remote endpoint. * Since connection reuse does not match on conn->host necessarily, we - * switch `existing` conn to `temp` conn's host settings. - * Is this correct in the case of TLS connections that have - * used the original hostname in SNI to negotiate? Do we send - * requests for another host through the different SNI? + * switch conn to needle's host settings. */ - url_move_hostname(&existing->host, &temp->host); - url_move_hostname(&existing->conn_to_host, &temp->conn_to_host); + url_move_hostname(&conn->host, &needle->host); + url_move_hostname(&conn->conn_to_host, &needle->conn_to_host); - existing->conn_to_port = temp->conn_to_port; - existing->remote_port = temp->remote_port; - curlx_free(existing->hostname_resolve); - existing->hostname_resolve = temp->hostname_resolve; - temp->hostname_resolve = NULL; - - /* reuse init */ - existing->bits.reuse = TRUE; /* yes, we are reusing here */ - - Curl_conn_free(data, temp); + conn->conn_to_port = needle->conn_to_port; + conn->remote_port = needle->remote_port; } static void conn_meta_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); } -/** - * create_conn() sets up a new connectdata struct, or reuses an already - * existing one, and resolves hostname. - * - * if this function returns CURLE_OK and *async is set to TRUE, the resolve - * response will be coming asynchronously. If *async is FALSE, the name is - * already resolved. - * - * @param data The sessionhandle pointer - * @param in_connect is set to the next connection data pointer - * @param reusedp is set to to TRUE if connection was reused - * @see Curl_setup_conn() - * - */ - -static CURLcode create_conn(struct Curl_easy *data, - struct connectdata **in_connect, - bool *reusedp) +static CURLcode url_create_needle(struct Curl_easy *data, + struct connectdata **pneedle) { + struct connectdata *needle = NULL; CURLcode result = CURLE_OK; - struct connectdata *conn; - struct connectdata *existing = NULL; - bool reuse; - bool connections_available = TRUE; - bool force_reuse = FALSE; - bool waitpipe = FALSE; - - *reusedp = FALSE; - *in_connect = NULL; /************************************************************* * Check input data @@ -3394,37 +3037,31 @@ parts for checking against the already present connections. In order to not have to modify everything at once, we allocate a temporary connection data struct and fill in for comparison purposes. */ - conn = allocate_conn(data); - - if(!conn) { + needle = allocate_conn(data); + if(!needle) { result = CURLE_OUT_OF_MEMORY; goto out; } - /* We must set the return variable as soon as possible, so that our - parent can cleanup any possible allocs we may have done before - any failure */ - *in_connect = conn; - /* Do the unfailable inits first, before checks that may early return */ - Curl_hash_init(&conn->meta_hash, 23, + Curl_hash_init(&needle->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry); - result = parseurlandfillconn(data, conn); + result = parseurlandfillconn(data, needle); if(result) goto out; if(data->set.str[STRING_SASL_AUTHZID]) { - conn->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); - if(!conn->sasl_authzid) { + needle->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); + if(!needle->sasl_authzid) { result = CURLE_OUT_OF_MEMORY; goto out; } } if(data->set.str[STRING_BEARER]) { - conn->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]); - if(!conn->oauth_bearer) { + needle->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]); + if(!needle->oauth_bearer) { result = CURLE_OUT_OF_MEMORY; goto out; } @@ -3432,20 +3069,20 @@ #ifdef USE_UNIX_SOCKETS if(data->set.str[STRING_UNIX_SOCKET_PATH]) { - conn->unix_domain_socket = + needle->unix_domain_socket = curlx_strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); - if(!conn->unix_domain_socket) { + if(!needle->unix_domain_socket) { result = CURLE_OUT_OF_MEMORY; goto out; } - conn->bits.abstract_unix_socket = data->set.abstract_unix_socket; + needle->bits.abstract_unix_socket = data->set.abstract_unix_socket; } #endif /* After the Unix socket init but before the proxy vars are used, parse and initialize the proxy vars */ #ifndef CURL_DISABLE_PROXY - result = create_conn_helper_init_proxy(data, conn); + result = create_conn_helper_init_proxy(data, needle); if(result) goto out; @@ -3453,24 +3090,24 @@ * If the protocol is using SSL and HTTP proxy is used, we set * the tunnel_proxy bit. *************************************************************/ - if((conn->given->flags & PROTOPT_SSL) && conn->bits.httpproxy) - conn->bits.tunnel_proxy = TRUE; + if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy) + needle->bits.tunnel_proxy = TRUE; #endif /************************************************************* * Figure out the remote port number and fix it in the URL *************************************************************/ - result = parse_remote_port(data, conn); + result = parse_remote_port(data, needle); if(result) goto out; /* Check for overridden login details and set them accordingly so that they are known when protocol->setup_connection is called! */ - result = override_login(data, conn); + result = override_login(data, needle); if(result) goto out; - result = set_login(data, conn); /* default credentials */ + result = set_login(data, needle); /* default credentials */ if(result) goto out; @@ -3478,7 +3115,7 @@ * Process the "connect to" linked list of hostname/port mappings. * Do this after the remote port number has been fixed in the URL. *************************************************************/ - result = parse_connect_to_slist(data, conn, data->set.connect_to); + result = parse_connect_to_slist(data, needle, data->set.connect_to); if(result) goto out; @@ -3486,38 +3123,39 @@ * IDN-convert the proxy hostnames *************************************************************/ #ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy) { - result = Curl_idnconvert_hostname(&conn->http_proxy.host); + if(needle->bits.httpproxy) { + result = Curl_idnconvert_hostname(&needle->http_proxy.host); if(result) - return result; + goto out; } - if(conn->bits.socksproxy) { - result = Curl_idnconvert_hostname(&conn->socks_proxy.host); + if(needle->bits.socksproxy) { + result = Curl_idnconvert_hostname(&needle->socks_proxy.host); if(result) - return result; + goto out; } #endif - if(conn->bits.conn_to_host) { - result = Curl_idnconvert_hostname(&conn->conn_to_host); + if(needle->bits.conn_to_host) { + result = Curl_idnconvert_hostname(&needle->conn_to_host); if(result) - return result; + goto out; } /************************************************************* * Check whether the host and the "connect to host" are equal. * Do this after the hostnames have been IDN-converted. *************************************************************/ - if(conn->bits.conn_to_host && - curl_strequal(conn->conn_to_host.name, conn->host.name)) { - conn->bits.conn_to_host = FALSE; + if(needle->bits.conn_to_host && + curl_strequal(needle->conn_to_host.name, needle->host.name)) { + needle->bits.conn_to_host = FALSE; } /************************************************************* * Check whether the port and the "connect to port" are equal. * Do this after the remote port number has been fixed in the URL. *************************************************************/ - if(conn->bits.conn_to_port && conn->conn_to_port == conn->remote_port) { - conn->bits.conn_to_port = FALSE; + if(needle->bits.conn_to_port && + needle->conn_to_port == needle->remote_port) { + needle->bits.conn_to_port = FALSE; } #ifndef CURL_DISABLE_PROXY @@ -3525,105 +3163,145 @@ * If the "connect to" feature is used with an HTTP proxy, * we set the tunnel_proxy bit. *************************************************************/ - if((conn->bits.conn_to_host || conn->bits.conn_to_port) && - conn->bits.httpproxy) - conn->bits.tunnel_proxy = TRUE; + if((needle->bits.conn_to_host || needle->bits.conn_to_port) && + needle->bits.httpproxy) + needle->bits.tunnel_proxy = TRUE; #endif /************************************************************* * Setup internals depending on protocol. Needs to be done after * we figured out what/if proxy to use. *************************************************************/ - result = setup_connection_internals(data, conn); + result = setup_connection_internals(data, needle); if(result) goto out; + if(needle->scheme->flags & PROTOPT_ALPN) { + /* The protocol wants it, so set the bits if enabled in the easy handle + (default) */ + if(data->set.ssl_enable_alpn) + needle->bits.tls_enable_alpn = TRUE; + } + + if(!(needle->scheme->flags & PROTOPT_NONETWORK)) { + /* Setup callbacks for network connections */ + needle->recv[FIRSTSOCKET] = Curl_cf_recv; + needle->send[FIRSTSOCKET] = Curl_cf_send; + needle->recv[SECONDARYSOCKET] = Curl_cf_recv; + needle->send[SECONDARYSOCKET] = Curl_cf_send; + needle->bits.tcp_fastopen = data->set.tcp_fastopen; +#ifdef USE_UNIX_SOCKETS + if(Curl_conn_get_unix_path(needle)) + needle->transport_wanted = TRNSPRT_UNIX; +#endif + } + +out: + if(!result) { + DEBUGASSERT(needle); + *pneedle = needle; + } + else { + *pneedle = NULL; + if(needle) + Curl_conn_free(data, needle); + } + return result; +} + +/** + * Find an existing connection for the transfer or create a new one. + * Returns + * - CURLE_OK on success with a connection attached to data + * - CURLE_NO_CONNECTION_AVAILABLE when connection limits apply or when + * a suitable connection has not determined its multiplex capability. + * - a fatal error + */ +static CURLcode url_find_or_create_conn(struct Curl_easy *data) +{ + struct connectdata *needle = NULL; + bool waitpipe = FALSE; + CURLcode result; + + /* create the template connection for transfer data. Use this needle to + * find an existing connection or, if none exists, convert needle + * to a full connection and attach it to data. */ + result = url_create_needle(data, &needle); + if(result) + goto out; + DEBUGASSERT(needle); + /*********************************************************************** * file: is a special case in that it does not need a network connection ***********************************************************************/ #ifndef CURL_DISABLE_FILE - if(conn->handler->flags & PROTOPT_NONETWORK) { + if(needle->scheme->flags & PROTOPT_NONETWORK) { bool done; /* this is supposed to be the connect function so we better at least check that the file is present here! */ - DEBUGASSERT(conn->handler->connect_it); - data->info.conn_scheme = conn->handler->scheme; + DEBUGASSERT(needle->scheme->run->connect_it); + data->info.conn_scheme = needle->scheme->name; /* conn_protocol can only provide "old" protocols */ - data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; - result = conn->handler->connect_it(data, &done); + data->info.conn_protocol = (needle->scheme->protocol) & CURLPROTO_MASK; + result = needle->scheme->run->connect_it(data, &done); if(result) goto out; /* Setup a "faked" transfer that will do nothing */ - Curl_attach_connection(data, conn); - result = Curl_cpool_add(data, conn); + Curl_attach_connection(data, needle); + needle = NULL; + result = Curl_cpool_add(data, data->conn); if(!result) { /* Setup whatever necessary for a resumed transfer */ result = setup_range(data); if(!result) { Curl_xfer_setup_nop(data); - result = Curl_init_do(data, conn); + result = Curl_init_do(data, data->conn); } } if(result) { - DEBUGASSERT(conn->handler->done); + DEBUGASSERT(data->conn->scheme->run->done); /* we ignore the return code for the protocol-specific DONE */ - (void)conn->handler->done(data, result, FALSE); + (void)data->conn->scheme->run->done(data, result, FALSE); } goto out; } #endif - /* Setup filter for network connections */ - conn->recv[FIRSTSOCKET] = Curl_cf_recv; - conn->send[FIRSTSOCKET] = Curl_cf_send; - conn->recv[SECONDARYSOCKET] = Curl_cf_recv; - conn->send[SECONDARYSOCKET] = Curl_cf_send; - conn->bits.tcp_fastopen = data->set.tcp_fastopen; - /* Complete the easy's SSL configuration for connection cache matching */ result = Curl_ssl_easy_config_complete(data); if(result) goto out; + /* Get rid of any dead connections so limit are easier kept. */ Curl_cpool_prune_dead(data); /************************************************************* - * Check the current list of connections to see if we can - * reuse an already existing one or if we have to create a - * new one. + * Reuse of existing connection is not allowed when + * - connect_only is set or + * - reuse_fresh is set and this is not a follow-up request + * (like with HTTP followlocation) *************************************************************/ + if((!data->set.reuse_fresh || data->state.followlocation) && + !data->set.connect_only) { + /* Ok, try to find and attach an existing one */ + url_attach_existing(data, needle, &waitpipe); + } - DEBUGASSERT(conn->user); - DEBUGASSERT(conn->passwd); + if(data->conn) { + /* We attached an existing connection for this transfer. Copy + * over transfer specific properties over from needle. */ + struct connectdata *conn = data->conn; + VERBOSE(bool tls_upgraded = (!(needle->given->flags & PROTOPT_SSL) && + Curl_conn_is_ssl(conn, FIRSTSOCKET))); - /* 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 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) || - data->set.connect_only) - reuse = FALSE; - else - reuse = ConnectionExists(data, conn, &existing, &force_reuse, &waitpipe); - - if(reuse) { - /* - * We already have a connection for this, we got the former connection in - * `existing` and thus we need to cleanup the one we just - * allocated before we can move along and use `existing`. - */ - bool tls_upgraded = (!(conn->given->flags & PROTOPT_SSL) && - Curl_conn_is_ssl(conn, FIRSTSOCKET)); - - reuse_conn(data, conn, existing); - conn = existing; - *in_connect = conn; + conn->bits.reuse = TRUE; + url_conn_reuse_adjust(data, needle); #ifndef CURL_DISABLE_PROXY infof(data, "Reusing existing %s: connection%s with %s %s", - conn->given->scheme, + conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", conn->bits.proxy ? "proxy" : "host", conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : @@ -3631,36 +3309,30 @@ conn->host.dispname); #else infof(data, "Reusing existing %s: connection%s with host %s", - conn->given->scheme, + conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", conn->host.dispname); #endif } else { - /* We have decided that we want a new connection. However, we may not - be able to do that if we have reached the limit of how many - connections we are allowed to open. */ - DEBUGF(infof(data, "new connection, bits.close=%d", conn->bits.close)); - - if(conn->handler->flags & PROTOPT_ALPN) { - /* The protocol wants it, so set the bits if enabled in the easy handle - (default) */ - if(data->set.ssl_enable_alpn) - conn->bits.tls_enable_alpn = TRUE; - } + /* We have decided that we want a new connection. We may not be able to do + that if we have reached the limit of how many connections we are + allowed to open. */ + DEBUGF(infof(data, "new connection, bits.close=%d", needle->bits.close)); if(waitpipe) { /* There is a connection that *might* become usable for multiplexing "soon", and we wait for that */ infof(data, "Waiting on connection to negotiate possible multiplexing."); - connections_available = FALSE; + result = CURLE_NO_CONNECTION_AVAILABLE; + goto out; } else { - switch(Curl_cpool_check_limits(data, conn)) { + switch(Curl_cpool_check_limits(data, needle)) { case CPOOL_LIMIT_DEST: infof(data, "No more connections allowed to host"); - connections_available = FALSE; - break; + result = CURLE_NO_CONNECTION_AVAILABLE; + goto out; case CPOOL_LIMIT_TOTAL: if(data->master_mid != UINT32_MAX) CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override " @@ -3668,7 +3340,8 @@ else { infof(data, "No connections available, total of %zu reached.", data->multi->max_total_connections); - connections_available = FALSE; + result = CURLE_NO_CONNECTION_AVAILABLE; + goto out; } break; default: @@ -3676,29 +3349,20 @@ } } - if(!connections_available) { - Curl_conn_free(data, conn); - *in_connect = NULL; - - result = CURLE_NO_CONNECTION_AVAILABLE; + /* Convert needle into a full connection by filling in all the + * remaining parts like the cloned SSL configuration. */ + result = Curl_ssl_conn_config_init(data, needle); + if(result) { + DEBUGF(curl_mfprintf(stderr, "Error: init connection ssl config\n")); goto out; } - else { - /* - * This is a brand new connection, so let's store it in the connection - * cache of ours! - */ - result = Curl_ssl_conn_config_init(data, conn); - if(result) { - DEBUGF(curl_mfprintf(stderr, "Error: init connection ssl config\n")); - goto out; - } + /* attach it and no longer own it */ + Curl_attach_connection(data, needle); + needle = NULL; - Curl_attach_connection(data, conn); - result = Curl_cpool_add(data, conn); - if(result) - goto out; - } + result = Curl_cpool_add(data, data->conn); + if(result) + goto out; #ifdef USE_NTLM /* If NTLM is requested in a part of this connection, make sure we do not @@ -3721,123 +3385,76 @@ } /* Setup and init stuff before DO starts, in preparing for the transfer. */ - result = Curl_init_do(data, conn); + result = Curl_init_do(data, data->conn); if(result) goto out; - /* - * Setup whatever necessary for a resumed transfer - */ + /* Setup whatever necessary for a resumed transfer */ result = setup_range(data); if(result) goto out; - /* Continue connectdata initialization here. */ - - if(conn->bits.reuse) { - /* We are reusing the connection - no need to resolve anything, and - idnconvert_hostname() was called already in create_conn() for the reuse - case. */ - *reusedp = TRUE; - } - /* persist the scheme and handler the transfer is using */ - data->info.conn_scheme = conn->handler->scheme; + data->info.conn_scheme = data->conn->scheme->name; /* conn_protocol can only provide "old" protocols */ - data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; + data->info.conn_protocol = (data->conn->scheme->protocol) & CURLPROTO_MASK; data->info.used_proxy = #ifdef CURL_DISABLE_PROXY 0 #else - conn->bits.proxy + data->conn->bits.proxy #endif ; - /* Everything general done, inform filters that they need - * to prepare for a data transfer. */ + /* Lastly, inform connection filters that a new transfer is attached */ result = Curl_conn_ev_data_setup(data); out: + if(needle) + Curl_conn_free(data, needle); + DEBUGASSERT(result || data->conn); return result; } -/* Curl_setup_conn() is called after the name resolve initiated in - * create_conn() is all done. - * - * Curl_setup_conn() also handles reused connections - */ -CURLcode Curl_setup_conn(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - DEBUGASSERT(dns); - Curl_pgrsTime(data, TIMER_NAMELOOKUP); - - if(!conn->bits.reuse) - result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns, - CURL_CF_SSL_DEFAULT); - if(!result) - result = Curl_headers_init(data); - - /* not sure we need this flag to be passed around any more */ - *protocol_done = FALSE; - return result; -} - -CURLcode Curl_connect(struct Curl_easy *data, - bool *asyncp, - bool *protocol_done) +CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) { CURLcode result; struct connectdata *conn; - bool reused = FALSE; - *asyncp = FALSE; /* assume synchronous resolves by default */ - *protocol_done = FALSE; + *pconnected = FALSE; /* Set the request to virgin state based on transfer settings */ Curl_req_hard_reset(&data->req, data); - /* call the stuff that needs to be called */ - result = create_conn(data, &conn, &reused); + /* Get or create a connection for the transfer. */ + result = url_find_or_create_conn(data); + conn = data->conn; - if(result == CURLE_NO_CONNECTION_AVAILABLE) { + if(result) + goto out; + + DEBUGASSERT(conn); + Curl_pgrsTime(data, TIMER_POSTQUEUE); + if(conn->bits.reuse) { + if(conn->attached_xfers > 1) + /* multiplexed */ + *pconnected = TRUE; + } + else if(conn->scheme->flags & PROTOPT_NONETWORK) { + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + *pconnected = TRUE; + } + else { + result = Curl_conn_setup(data, conn, FIRSTSOCKET, NULL, + CURL_CF_SSL_DEFAULT); + if(!result) + result = Curl_headers_init(data); + CURL_TRC_M(data, "Curl_conn_setup() -> %d", result); + } + +out: + if(result == CURLE_NO_CONNECTION_AVAILABLE) DEBUGASSERT(!conn); - return result; - } - - if(!result) { - DEBUGASSERT(conn); - Curl_pgrsTime(data, TIMER_POSTQUEUE); - if(reused) { - if(conn->attached_xfers > 1) - /* multiplexed */ - *protocol_done = TRUE; - } - else if(conn->handler->flags & PROTOPT_NONETWORK) { - *asyncp = FALSE; - Curl_pgrsTime(data, TIMER_NAMELOOKUP); - *protocol_done = TRUE; - } - else { - /************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ - struct Curl_dns_entry *dns; - result = resolve_server(data, conn, asyncp, &dns); - if(!result) { - *asyncp = !dns; - if(dns) - /* DNS resolution is done: that is either because this is a reused - connection, in which case DNS was unnecessary, or because DNS - really did finish already (synch resolver/fast async resolve) */ - result = Curl_setup_conn(data, dns, protocol_done); - } - } - } if(result && conn) { /* We are not allowed to return failure with memory left allocated in the @@ -3868,12 +3485,13 @@ use */ /* if the protocol used does not support wildcards, switch it off */ if(data->state.wildcardmatch && - !(conn->handler->flags & PROTOPT_WILDCARD)) + !(conn->scheme->flags & PROTOPT_WILDCARD)) data->state.wildcardmatch = FALSE; } data->state.done = FALSE; /* *_done() is not called yet */ + data->req.no_body = data->set.opt_no_body; if(data->req.no_body) /* in HTTP lingo, no body means using the HEAD request... */ data->state.httpreq = HTTPREQ_HEAD; @@ -4005,11 +3623,6 @@ return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1); } -CURLcode Curl_1st_err(CURLcode r1, CURLcode r2) -{ - return r1 ? r1 : r2; -} - CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2) { if(r1 && (r1 != CURLE_AGAIN))
diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index 3a8d57c..66baf70 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h
@@ -26,7 +26,7 @@ #include "curl_setup.h" /* - * Prototypes for library-wide functions provided by url.c + * Prototypes for library-wide functions */ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn); @@ -35,15 +35,12 @@ void Curl_freeset(struct Curl_easy *data); CURLcode Curl_uc_to_curlcode(CURLUcode uc); -CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */ -CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect); -CURLcode Curl_setup_conn(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_done); +CURLcode Curl_close(struct Curl_easy **datap); /* opposite of Curl_open() */ +CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected); void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_parse_login_details(const char *login, const size_t len, - char **userptr, char **passwdptr, - char **optionsptr); + char **userp, char **passwdp, + char **optionsp); /* Attach/Clear/Get meta data for an easy handle. Needs to provide * a destructor, will be automatically called when the easy handle @@ -67,21 +64,12 @@ void Curl_conn_meta_remove(struct connectdata *conn, const char *key); void *Curl_conn_meta_get(struct connectdata *conn, const char *key); -/* Get protocol handler for a URI scheme - * @param scheme URI scheme, case-insensitive - * @return NULL of handler not found - */ -const struct Curl_handler *Curl_get_scheme_handler(const char *scheme); -const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, - size_t len); - #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless specified */ /** * Return TRUE iff the given connection is considered dead. - * @param nowp NULL or pointer to time being checked against. */ bool Curl_conn_seems_dead(struct connectdata *conn, struct Curl_easy *data); @@ -93,15 +81,8 @@ struct connectdata *conn); /** - * Always eval all arguments, return the first result != CURLE_OK. - * A non-short-circuit evaluation. - */ -CURLcode Curl_1st_err(CURLcode r1, CURLcode r2); - -/** * Always eval all arguments, return the first - * result != (CURLE_OK|CURLE_AGAIN) or `r1`. - * A non-short-circuit evaluation. + * result != (CURLE_OK | CURLE_AGAIN) or `r1`. */ CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2);
diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h index fbce183..4d8f2c1 100644 --- a/Utilities/cmcurl/lib/urlapi-int.h +++ b/Utilities/cmcurl/lib/urlapi-int.h
@@ -24,6 +24,34 @@ * ***************************************************************************/ #include "curl_setup.h" +#include <curl/urlapi.h> + +/* Internal representation of CURLU. Point to URL-encoded strings. */ +struct Curl_URL { + char *scheme; + char *user; + char *password; + char *options; /* IMAP only? */ + char *host; + char *zoneid; /* for numerical IPv6 addresses */ + char *port; + char *path; + char *query; + char *fragment; + unsigned short portnum; /* the numerical version (if 'port' is set) */ + BIT(query_present); /* to support blank */ + BIT(fragment_present); /* to support blank */ + BIT(guessed_scheme); /* when a URL without scheme is parsed */ +}; + +#define HOST_ERROR (-1) /* out of memory */ +#define HOST_NAME 1 +#define HOST_IPV4 2 +#define HOST_IPV6 3 + +#define QUERY_NO 2 +#define QUERY_NOT_YET 3 /* allow to change to query */ +#define QUERY_YES 4 size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, bool guess_scheme); @@ -32,9 +60,9 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace); -#ifdef UNITTESTS -UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, - bool has_scheme); -#endif +#define U_CURLU_URLDECODE (unsigned int)CURLU_URLDECODE +#define U_CURLU_PATH_AS_IS (unsigned int)CURLU_PATH_AS_IS + +bool Curl_url_same_origin(CURLU *base, CURLU *href); #endif /* HEADER_CURL_URLAPI_INT_H */
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index e974783..103e0a6 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c
@@ -30,17 +30,17 @@ #include "escape.h" #include "curlx/inet_pton.h" #include "curlx/inet_ntop.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "idn.h" #include "curlx/strparse.h" #include "curl_memrchr.h" #ifdef _WIN32 /* MS-DOS/Windows style drive prefix, eg c: in c:foo */ -#define STARTS_WITH_DRIVE_PREFIX(str) \ - ((('a' <= str[0] && str[0] <= 'z') || \ - ('A' <= str[0] && str[0] <= 'Z')) && \ - (str[1] == ':')) +#define STARTS_WITH_DRIVE_PREFIX(str) \ + ((('a' <= (str)[0] && (str)[0] <= 'z') || \ + ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ + ((str)[1] == ':')) #endif /* MS-DOS/Windows style drive prefix, optionally with @@ -63,29 +63,8 @@ #define AF_INET6 (AF_INET + 1) #endif -/* Internal representation of CURLU. Point to URL-encoded strings. */ -struct Curl_URL { - char *scheme; - char *user; - char *password; - char *options; /* IMAP only? */ - char *host; - char *zoneid; /* for numerical IPv6 addresses */ - char *port; - char *path; - char *query; - char *fragment; - unsigned short portnum; /* the numerical version (if 'port' is set) */ - BIT(query_present); /* to support blank */ - BIT(fragment_present); /* to support blank */ - BIT(guessed_scheme); /* when a URL without scheme is parsed */ -}; - #define DEFAULT_SCHEME "https" -static CURLUcode parseurl_and_replace(const char *url, CURLU *u, - unsigned int flags); - static void free_urlhandle(struct Curl_URL *u) { curlx_free(u->scheme); @@ -121,25 +100,40 @@ } /* convert CURLcode to CURLUcode */ -#define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : \ - CURLUE_OUT_OF_MEMORY) +#define cc2cu(x) \ + ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : CURLUE_OUT_OF_MEMORY) /* urlencode_str() writes data into an output dynbuf and URL-encodes the * spaces in the source URL accordingly. * + * This function re-encodes the string, meaning that it leaves already encoded + * bytes as-is and works by encoding only what *has* to be encoded - unless it + * has to uppercase the hex to normalize. + * + * Illegal percent-encoding sequences are left as-is. + * * URL encoding should be skipped for hostnames, otherwise IDN resolution * will fail. + * + * 'query' tells if it is a query part or not, or if it is allowed to + * "transition" into a query part with a question mark. + * + * @unittest 1675 */ -static CURLUcode urlencode_str(struct dynbuf *o, const char *url, - size_t len, bool relative, - bool query) +UNITTEST CURLUcode urlencode_str(struct dynbuf *o, const char *url, + size_t len, bool relative, + unsigned int query); +UNITTEST CURLUcode urlencode_str(struct dynbuf *o, const char *url, + size_t len, bool relative, + unsigned int query) { /* we must add this with whitespace-replacing */ - bool left = !query; const unsigned char *iptr; const unsigned char *host_sep = (const unsigned char *)url; CURLcode result = CURLE_OK; + DEBUGASSERT((query >= QUERY_NO) && (query <= QUERY_YES)); + if(!relative) { size_t n; host_sep = (const unsigned char *)find_host_sep(url); @@ -152,7 +146,7 @@ for(iptr = host_sep; len && !result; iptr++, len--) { if(*iptr == ' ') { - if(left) + if(query != QUERY_YES) result = curlx_dyn_addn(o, "%20", 3); else result = curlx_dyn_addn(o, "+", 1); @@ -162,10 +156,22 @@ Curl_hexbyte(&out[1], *iptr); result = curlx_dyn_addn(o, out, 3); } + else if(*iptr == '%' && (len >= 3) && + ISXDIGIT(iptr[1]) && ISXDIGIT(iptr[2]) && + (ISLOWER(iptr[1]) || ISLOWER(iptr[2]))) { + /* uppercase it */ + unsigned char hex = (unsigned char)((curlx_hexval(iptr[1]) << 4) | + curlx_hexval(iptr[2])); + unsigned char out[3] = { '%' }; + Curl_hexbyte(&out[1], hex); + result = curlx_dyn_addn(o, out, 3); + iptr += 2; + len -= 2; + } else { result = curlx_dyn_addn(o, iptr, 1); - if(*iptr == '?') - left = FALSE; + if(*iptr == '?' && (query == QUERY_NOT_YET)) + query = QUERY_YES; } } @@ -222,81 +228,6 @@ return 0; } -/* - * Concatenate a relative URL onto a base URL making it absolute. - */ -static CURLUcode redirect_url(const char *base, const char *relurl, - CURLU *u, unsigned int flags) -{ - struct dynbuf urlbuf; - bool host_changed = FALSE; - const char *useurl = relurl; - const char *cutoff = NULL; - size_t prelen; - CURLUcode uc; - - /* protsep points to the start of the hostname, after [scheme]:// */ - const char *protsep = base + strlen(u->scheme) + 3; - DEBUGASSERT(base && relurl && u); /* all set here */ - if(!base) - return CURLUE_MALFORMED_INPUT; /* should never happen */ - - /* handle different relative URL types */ - switch(relurl[0]) { - case '/': - if(relurl[1] == '/') { - /* protocol-relative URL: //example.com/path */ - cutoff = protsep; - useurl = &relurl[2]; - host_changed = TRUE; - } - else - /* absolute /path */ - cutoff = strchr(protsep, '/'); - break; - - case '#': - /* fragment-only change */ - if(u->fragment) - cutoff = strchr(protsep, '#'); - break; - - default: - /* path or query-only change */ - if(u->query && u->query[0]) - /* remove existing query */ - cutoff = strchr(protsep, '?'); - else if(u->fragment && u->fragment[0]) - /* Remove existing fragment */ - cutoff = strchr(protsep, '#'); - - if(relurl[0] != '?') { - /* append a relative path after the last slash */ - cutoff = memrchr(protsep, '/', - cutoff ? (size_t)(cutoff - protsep) : strlen(protsep)); - if(cutoff) - cutoff++; /* truncate after last slash */ - } - break; - } - - prelen = cutoff ? (size_t)(cutoff - base) : strlen(base); - - /* build new URL */ - curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH); - - if(!curlx_dyn_addn(&urlbuf, base, prelen) && - !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) { - uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u, - flags & ~CURLU_PATH_AS_IS); - } - else - uc = CURLUE_OUT_OF_MEMORY; - - curlx_dyn_free(&urlbuf); - return uc; -} - /* scan for byte values <= 31, 127 and sometimes space */ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace) { @@ -329,12 +260,12 @@ unsigned int flags, size_t *offset) /* to the hostname */ { - CURLUcode result = CURLUE_OK; - CURLcode ccode; + CURLUcode ures = CURLUE_OK; + CURLcode result; char *userp = NULL; char *passwdp = NULL; char *optionsp = NULL; - const struct Curl_handler *h = NULL; + const struct Curl_scheme *h = NULL; /* At this point, we assume all the other special cases have been taken * care of, so the host is at most @@ -343,7 +274,7 @@ * * We need somewhere to put the embedded details, so do that first. */ - char *ptr; + const char *ptr; DEBUGASSERT(login); @@ -359,25 +290,25 @@ /* if this is a known scheme, get some details */ if(u->scheme) - h = Curl_get_scheme_handler(u->scheme); + h = Curl_get_scheme(u->scheme); /* We could use the login information in the URL so extract it. Only parse options if the handler says we should. Note that 'h' might be NULL! */ - ccode = Curl_parse_login_details(login, ptr - login - 1, - &userp, &passwdp, - (h && (h->flags & PROTOPT_URLOPTIONS)) ? - &optionsp : NULL); - if(ccode) { + result = Curl_parse_login_details(login, ptr - login - 1, + &userp, &passwdp, + (h && (h->flags & PROTOPT_URLOPTIONS)) ? + &optionsp : NULL); + if(result) { /* the only possible error from Curl_parse_login_details is out of memory: */ - result = CURLUE_OUT_OF_MEMORY; + ures = CURLUE_OUT_OF_MEMORY; goto out; } if(userp) { if(flags & CURLU_DISALLOW_USER) { /* Option DISALLOW_USER is set and URL contains username. */ - result = CURLUE_USER_NOT_ALLOWED; + ures = CURLUE_USER_NOT_ALLOWED; goto out; } curlx_free(u->user); @@ -407,14 +338,17 @@ u->password = NULL; u->options = NULL; - return result; + return ures; } -UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, - bool has_scheme) +/* @unittest 1653 */ +UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme); +UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme) { const char *portptr; - char *hostname = curlx_dyn_ptr(host); + const char *hostname = curlx_dyn_ptr(host); /* * Find the end of an IPv6 address on the ']' ending bracket. */ @@ -439,8 +373,8 @@ size_t keep = portptr - hostname; /* Browser behavior adaptation. If there is a colon with no digits after, - just cut off the name there which makes us ignore the colon and just - use the default port. Firefox, Chrome and Safari all do that. + cut off the name there which makes us ignore the colon and use the + default port. Firefox, Chrome and Safari all do that. Do not do it if the URL has no scheme, to make something that looks like a scheme not work! @@ -464,9 +398,15 @@ return CURLUE_OK; } -/* this assumes 'hostname' now starts with [ */ -static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, - size_t hlen) /* length of hostname */ +/* This function assumes 'hostname' now starts with [. It trims 'hostname' in + * place and it sets u->zoneid if present. + * + * @unittest 1675 + */ +UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, + size_t hlen); +UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, + size_t hlen) /* length of hostname */ { size_t len; DEBUGASSERT(*hostname == '['); @@ -550,15 +490,12 @@ * integers. * * Returns the host type. + * + * @unittest 1675 */ -#define HOST_ERROR -1 /* out of memory */ - -#define HOST_NAME 1 -#define HOST_IPV4 2 -#define HOST_IPV6 3 - -static int ipv4_normalize(struct dynbuf *host) +UNITTEST int ipv4_normalize(struct dynbuf *host); +UNITTEST int ipv4_normalize(struct dynbuf *host) { bool done = FALSE; int n = 0; @@ -655,7 +592,7 @@ /* if necessary, replace the host content with a URL decoded version */ static CURLUcode urldecode_host(struct dynbuf *host) { - char *per = NULL; + const char *per; const char *hostname = curlx_dyn_ptr(host); per = strchr(hostname, '%'); if(!per) @@ -702,7 +639,7 @@ goto out; } - uc = Curl_parse_port(u, host, has_scheme); + uc = parse_port(u, host, has_scheme); if(uc) goto out; @@ -735,21 +672,21 @@ /* used for HTTP/2 server push */ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority) { - CURLUcode result; + CURLUcode ures; struct dynbuf host; DEBUGASSERT(authority); curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - result = parse_authority(u, authority, strlen(authority), - CURLU_DISALLOW_USER, &host, !!u->scheme); - if(result) + ures = parse_authority(u, authority, strlen(authority), + CURLU_DISALLOW_USER, &host, !!u->scheme); + if(ures) curlx_dyn_free(&host); else { curlx_free(u->host); u->host = curlx_dyn_ptr(&host); } - return result; + return ures; } /* @@ -778,7 +715,6 @@ /* * dedotdotify() - * @unittest: 1395 * * This function gets a null-terminated path with dot and dotdot sequences * passed in and strips them off according to the rules in RFC 3986 section @@ -789,6 +725,8 @@ * RETURNS * * Zero for success and 'out' set to an allocated dedotdotified string. + * + * @unittest 1395 */ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp); UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) @@ -796,38 +734,36 @@ struct dynbuf out; CURLcode result = CURLE_OK; + /* variables for leading dot checks */ + const char *dinput = input; + size_t dlen = clen; + *outp = NULL; - /* the path always starts with a slash, and a slash has not dot */ + /* a single byte path cannot be cleaned up */ if(clen < 2) return 0; curlx_dyn_init(&out, clen + 1); - /* A. If the input buffer begins with a prefix of "../" or "./", then - remove that prefix from the input buffer; otherwise, */ - if(is_dot(&input, &clen)) { - const char *p = input; - size_t blen = clen; - - if(!clen) - /* . [end] */ - goto end; - else if(ISSLASH(*p)) { + /* if the input buffer begins with a prefix of "../" or "./", then remove + that prefix from the input buffer; otherwise, */ + if(is_dot(&dinput, &dlen)) { + if(ISSLASH(*dinput)) { /* one dot followed by a slash */ - input = p + 1; - clen--; + input = dinput + 1; + clen = dlen - 1; } - /* D. if the input buffer consists only of "." or "..", then remove - that from the input buffer; otherwise, */ - else if(is_dot(&p, &blen)) { - if(!blen) + /* if the input buffer consists only of "." or "..", then remove + that from the input buffer; otherwise, */ + else if(is_dot(&dinput, &dlen)) { + if(!dlen) /* .. [end] */ goto end; - else if(ISSLASH(*p)) { + else if(ISSLASH(*dinput)) { /* ../ */ - input = p + 1; - clen = blen - 1; + input = dinput + 1; + clen = dlen - 1; } } } @@ -836,9 +772,9 @@ if(ISSLASH(*input)) { const char *p = &input[1]; size_t blen = clen - 1; - /* B. if the input buffer begins with a prefix of "/./" or "/.", where - "." is a complete path segment, then replace that prefix with "/" in - the input buffer; otherwise, */ + /* if the input buffer begins with a prefix of "/./" or "/.", where "." + is a complete path segment, then replace that prefix with "/" in the + input buffer; otherwise, */ if(is_dot(&p, &blen)) { if(!blen) { /* /. */ result = curlx_dyn_addn(&out, "/", 1); @@ -850,16 +786,16 @@ continue; } - /* C. if the input buffer begins with a prefix of "/../" or "/..", - where ".." is a complete path segment, then replace that prefix - with "/" in the input buffer and remove the last segment and its - preceding "/" (if any) from the output buffer; otherwise, */ + /* if the input buffer begins with a prefix of "/../" or "/..", where + ".." is a complete path segment, then replace that prefix with "/" + in the input buffer and remove the last segment and its preceding + "/" (if any) from the output buffer; otherwise, */ else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) { /* remove the last segment from the output buffer */ size_t len = curlx_dyn_len(&out); if(len) { - char *ptr = curlx_dyn_ptr(&out); - char *last = memrchr(ptr, '/', len); + const char *ptr = curlx_dyn_ptr(&out); + const char *last = memrchr(ptr, '/', len); if(last) /* trim the output at the slash */ curlx_dyn_setlen(&out, last - ptr); @@ -876,10 +812,10 @@ } } - /* E. move the first path segment in the input buffer to the end of - the output buffer, including the initial "/" character (if any) and - any subsequent characters up to, but not including, the next "/" - character or the end of the input buffer. */ + /* move the first path segment in the input buffer to the end of the + output buffer, including the initial "/" character (if any) and any + subsequent characters up to, but not including, the next "/" character + or the end of the input buffer. */ result = curlx_dyn_addn(&out, input, 1); input++; @@ -898,25 +834,285 @@ return result ? 1 : 0; /* success */ } +/* + * @unittest 1675 + */ +UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, + const char **pathp, size_t *pathlenp); +UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, + const char **pathp, size_t *pathlenp) +{ + const char *path; + size_t pathlen; + + *pathp = NULL; + *pathlenp = 0; + if(urllen <= 6) + /* file:/ is not enough to actually be a complete file: URL */ + return CURLUE_BAD_FILE_URL; + + /* path has been allocated large enough to hold this */ + path = &url[5]; + pathlen = urllen - 5; + + /* Extra handling URLs with an authority component (i.e. that start with + * "file://") + * + * We allow omitted hostname (e.g. file:/<path>) -- valid according to + * RFC 8089, but not the (current) WHAT-WG URL spec. + */ + if(path[0] == '/' && path[1] == '/') { + /* swallow the two slashes */ + const char *ptr = &path[2]; + + /* + * According to RFC 8089, a file: URL can be reliably dereferenced if: + * + * o it has no/blank hostname, or + * + * o the hostname matches "localhost" (case-insensitively), or + * + * o the hostname is a FQDN that resolves to this machine, or + * + * For brevity, we only consider URLs with empty, "localhost", or + * "127.0.0.1" hostnames as local, otherwise as an UNC String. + * + * Additionally, there is an exception for URLs with a Windows drive + * letter in the authority (which was accidentally omitted from RFC 8089 + * Appendix E, but believe me, it was meant to be there. --MK) + */ + if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { + /* the URL includes a hostname, it must match "localhost" or + "127.0.0.1" to be valid */ + if(checkprefix("localhost/", ptr) || + checkprefix("127.0.0.1/", ptr)) { + ptr += 9; /* now points to the slash after the host */ + } + else + /* Invalid file://hostname/, expected localhost or 127.0.0.1 or + none */ + return CURLUE_BAD_FILE_URL; + } + + path = ptr; + pathlen = urllen - (ptr - url); + } + +#if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__) + /* Do not allow Windows drive letters when not in Windows. + * This catches both "file:/c:" and "file:c:" */ + if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || + STARTS_WITH_URL_DRIVE_PREFIX(path)) { + /* File drive letters are only accepted in MS-DOS/Windows */ + return CURLUE_BAD_FILE_URL; + } +#else + /* If the path starts with a slash and a drive letter, ditch the slash */ + if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { + /* This cannot be done with strcpy, as the memory chunks overlap! */ + path++; + pathlen--; + } +#endif + u->scheme = curlx_strdup("file"); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + + *pathp = path; + *pathlenp = pathlen; + return CURLUE_OK; +} + +static CURLUcode parse_scheme(const char *url, CURLU *u, char *schemebuf, + size_t schemelen, unsigned int flags, + const char **hostpp) +{ + /* clear path */ + const char *schemep = NULL; + + if(schemelen) { + int i = 0; + const char *p = &url[schemelen + 1]; + while((*p == '/') && (i < 4)) { + p++; + i++; + } + + schemep = schemebuf; + if(!Curl_get_scheme(schemep) && + !(flags & CURLU_NON_SUPPORT_SCHEME)) + return CURLUE_UNSUPPORTED_SCHEME; + + if((i < 1) || (i > 3)) + /* less than one or more than three slashes */ + return CURLUE_BAD_SLASHES; + + *hostpp = p; /* hostname starts here */ + } + else { + /* no scheme! */ + + if(!(flags & (CURLU_DEFAULT_SCHEME | CURLU_GUESS_SCHEME))) + return CURLUE_BAD_SCHEME; + + if(flags & CURLU_DEFAULT_SCHEME) + schemep = DEFAULT_SCHEME; + + /* + * The URL was badly formatted, let's try without scheme specified. + */ + *hostpp = url; + } + + if(schemep) { + u->scheme = curlx_strdup(schemep); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + } + return CURLUE_OK; +} + +static CURLUcode guess_scheme(CURLU *u, struct dynbuf *host) +{ + const char *hostname = curlx_dyn_ptr(host); + const char *schemep = NULL; + /* legacy curl-style guess based on hostname */ + if(checkprefix("ftp.", hostname)) + schemep = "ftp"; + else if(checkprefix("dict.", hostname)) + schemep = "dict"; + else if(checkprefix("ldap.", hostname)) + schemep = "ldap"; + else if(checkprefix("imap.", hostname)) + schemep = "imap"; + else if(checkprefix("smtp.", hostname)) + schemep = "smtp"; + else if(checkprefix("pop3.", hostname)) + schemep = "pop3"; + else + schemep = "http"; + + u->scheme = curlx_strdup(schemep); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + + u->guessed_scheme = TRUE; + return CURLUE_OK; +} + +static CURLUcode handle_fragment(CURLU *u, const char *fragment, + size_t fraglen, unsigned int flags) +{ + CURLUcode ures; + u->fragment_present = TRUE; + if(fraglen > 1) { + /* skip the leading '#' in the copy but include the terminating null */ + if(flags & CURLU_URLENCODE) { + struct dynbuf enc; + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + ures = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, QUERY_NO); + if(ures) + return ures; + u->fragment = curlx_dyn_ptr(&enc); + } + else { + u->fragment = curlx_memdup0(fragment + 1, fraglen - 1); + if(!u->fragment) + return CURLUE_OUT_OF_MEMORY; + } + } + return CURLUE_OK; +} + +static CURLUcode handle_query(CURLU *u, const char *query, + size_t qlen, unsigned int flags) +{ + u->query_present = TRUE; + if(qlen > 1) { + if(flags & CURLU_URLENCODE) { + struct dynbuf enc; + CURLUcode ures; + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + /* skip the leading question mark */ + ures = urlencode_str(&enc, query + 1, qlen - 1, TRUE, QUERY_YES); + if(ures) + return ures; + u->query = curlx_dyn_ptr(&enc); + } + else { + u->query = curlx_memdup0(query + 1, qlen - 1); + if(!u->query) + return CURLUE_OUT_OF_MEMORY; + } + } + else { + /* single byte query */ + u->query = curlx_strdup(""); + if(!u->query) + return CURLUE_OUT_OF_MEMORY; + } + return CURLUE_OK; +} + +static CURLUcode handle_path(CURLU *u, const char *path, + size_t pathlen, unsigned int flags, + bool is_file) +{ + CURLUcode ures; + if(pathlen && (flags & CURLU_URLENCODE)) { + struct dynbuf enc; + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + ures = urlencode_str(&enc, path, pathlen, TRUE, QUERY_NO); + if(ures) + return ures; + pathlen = curlx_dyn_len(&enc); + path = u->path = curlx_dyn_ptr(&enc); + } + + if(pathlen >= (size_t)(1 + !is_file)) { + /* paths for file:// scheme can be one byte, others need to be two */ + if(!u->path) { + u->path = curlx_memdup0(path, pathlen); + if(!u->path) + return CURLUE_OUT_OF_MEMORY; + path = u->path; + } + else if(flags & CURLU_URLENCODE) + /* it might have encoded more than the path so cut it */ + u->path[pathlen] = 0; + + if(!(flags & CURLU_PATH_AS_IS)) { + /* remove ../ and ./ sequences according to RFC3986 */ + char *dedot; + int err = dedotdotify(path, pathlen, &dedot); + if(err) + return CURLUE_OUT_OF_MEMORY; + if(dedot) { + curlx_free(u->path); + u->path = dedot; + } + } + } + return CURLUE_OK; +} + static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) { const char *path; size_t pathlen; - char *query = NULL; - char *fragment = NULL; char schemebuf[MAX_SCHEME_LEN + 1]; size_t schemelen = 0; size_t urllen; - CURLUcode result = CURLUE_OK; - size_t fraglen = 0; + CURLUcode ures = CURLUE_OK; struct dynbuf host; + bool is_file = FALSE; DEBUGASSERT(url); curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - result = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE)); - if(result) + ures = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE)); + if(ures) goto fail; schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf), @@ -925,169 +1121,15 @@ /* handle the file: scheme */ if(schemelen && !strcmp(schemebuf, "file")) { - bool uncpath = FALSE; - if(urllen <= 6) { - /* file:/ is not enough to actually be a complete file: URL */ - result = CURLUE_BAD_FILE_URL; - goto fail; - } - - /* path has been allocated large enough to hold this */ - path = &url[5]; - pathlen = urllen - 5; - - u->scheme = curlx_strdup("file"); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - - /* Extra handling URLs with an authority component (i.e. that start with - * "file://") - * - * We allow omitted hostname (e.g. file:/<path>) -- valid according to - * RFC 8089, but not the (current) WHAT-WG URL spec. - */ - if(path[0] == '/' && path[1] == '/') { - /* swallow the two slashes */ - const char *ptr = &path[2]; - - /* - * According to RFC 8089, a file: URL can be reliably dereferenced if: - * - * o it has no/blank hostname, or - * - * o the hostname matches "localhost" (case-insensitively), or - * - * o the hostname is a FQDN that resolves to this machine, or - * - * o it is an UNC String transformed to an URI (Windows only, RFC 8089 - * Appendix E.3). - * - * For brevity, we only consider URLs with empty, "localhost", or - * "127.0.0.1" hostnames as local, otherwise as an UNC String. - * - * Additionally, there is an exception for URLs with a Windows drive - * letter in the authority (which was accidentally omitted from RFC 8089 - * Appendix E, but believe me, it was meant to be there. --MK) - */ - if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { - /* the URL includes a hostname, it must match "localhost" or - "127.0.0.1" to be valid */ - if(checkprefix("localhost/", ptr) || - checkprefix("127.0.0.1/", ptr)) { - ptr += 9; /* now points to the slash after the host */ - } - else { -#ifdef _WIN32 - size_t len; - - /* the hostname, NetBIOS computer name, can not contain disallowed - chars, and the delimiting slash character must be appended to the - hostname */ - path = strpbrk(ptr, "/\\:*?\"<>|"); - if(!path || *path != '/') { - result = CURLUE_BAD_FILE_URL; - goto fail; - } - - len = path - ptr; - if(len) { - CURLcode code = curlx_dyn_addn(&host, ptr, len); - if(code) { - result = cc2cu(code); - goto fail; - } - uncpath = TRUE; - } - - ptr -= 2; /* now points to the // before the host in UNC */ -#else - /* Invalid file://hostname/, expected localhost or 127.0.0.1 or - none */ - result = CURLUE_BAD_FILE_URL; - goto fail; -#endif - } - } - - path = ptr; - pathlen = urllen - (ptr - url); - } - - if(!uncpath) - /* no host for file: URLs by default */ - curlx_dyn_reset(&host); - -#if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__) - /* Do not allow Windows drive letters when not in Windows. - * This catches both "file:/c:" and "file:c:" */ - if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || - STARTS_WITH_URL_DRIVE_PREFIX(path)) { - /* File drive letters are only accepted in MS-DOS/Windows */ - result = CURLUE_BAD_FILE_URL; - goto fail; - } -#else - /* If the path starts with a slash and a drive letter, ditch the slash */ - if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { - /* This cannot be done with strcpy, as the memory chunks overlap! */ - path++; - pathlen--; - } -#endif + is_file = TRUE; + ures = parse_file(url, urllen, u, &path, &pathlen); } else { - /* clear path */ - const char *schemep = NULL; - const char *hostp; + const char *hostp = NULL; size_t hostlen; - - if(schemelen) { - int i = 0; - const char *p = &url[schemelen + 1]; - while((*p == '/') && (i < 4)) { - p++; - i++; - } - - schemep = schemebuf; - if(!Curl_get_scheme_handler(schemep) && - !(flags & CURLU_NON_SUPPORT_SCHEME)) { - result = CURLUE_UNSUPPORTED_SCHEME; - goto fail; - } - - if((i < 1) || (i > 3)) { - /* less than one or more than three slashes */ - result = CURLUE_BAD_SLASHES; - goto fail; - } - hostp = p; /* hostname starts here */ - } - else { - /* no scheme! */ - - if(!(flags & (CURLU_DEFAULT_SCHEME | CURLU_GUESS_SCHEME))) { - result = CURLUE_BAD_SCHEME; - goto fail; - } - if(flags & CURLU_DEFAULT_SCHEME) - schemep = DEFAULT_SCHEME; - - /* - * The URL was badly formatted, let's try without scheme specified. - */ - hostp = url; - } - - if(schemep) { - u->scheme = curlx_strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } + ures = parse_scheme(url, u, schemebuf, schemelen, flags, &hostp); + if(ures) + goto fail; /* find the end of the hostname + port number */ hostlen = strcspn(hostp, "/?#"); @@ -1096,159 +1138,49 @@ /* this pathlen also contains the query and the fragment */ pathlen = urllen - (path - url); if(hostlen) { - - result = parse_authority(u, hostp, hostlen, flags, &host, schemelen); - if(result) - goto fail; - - if((flags & CURLU_GUESS_SCHEME) && !schemep) { - const char *hostname = curlx_dyn_ptr(&host); - /* legacy curl-style guess based on hostname */ - if(checkprefix("ftp.", hostname)) - schemep = "ftp"; - else if(checkprefix("dict.", hostname)) - schemep = "dict"; - else if(checkprefix("ldap.", hostname)) - schemep = "ldap"; - else if(checkprefix("imap.", hostname)) - schemep = "imap"; - else if(checkprefix("smtp.", hostname)) - schemep = "smtp"; - else if(checkprefix("pop3.", hostname)) - schemep = "pop3"; - else - schemep = "http"; - - u->scheme = curlx_strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - u->guessed_scheme = TRUE; - } + ures = parse_authority(u, hostp, hostlen, flags, &host, + u->scheme != NULL); + if(!ures && (flags & CURLU_GUESS_SCHEME) && !u->scheme) + ures = guess_scheme(u, &host); } else if(flags & CURLU_NO_AUTHORITY) { /* allowed to be empty. */ - if(curlx_dyn_add(&host, "")) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + if(curlx_dyn_add(&host, "")) + ures = CURLUE_OUT_OF_MEMORY; } - else { - result = CURLUE_NO_HOST; - goto fail; + else + ures = CURLUE_NO_HOST; + } + if(!ures) { + /* The path might at this point contain a fragment and/or a query to + handle */ + const char *fragment = strchr(path, '#'); + if(fragment) { + size_t fraglen = pathlen - (fragment - path); + ures = handle_fragment(u, fragment, fraglen, flags); + /* after this, pathlen still contains the query */ + pathlen -= fraglen; } } - - fragment = strchr(path, '#'); - if(fragment) { - fraglen = pathlen - (fragment - path); - u->fragment_present = TRUE; - if(fraglen > 1) { - /* skip the leading '#' in the copy but include the terminating null */ - if(flags & CURLU_URLENCODE) { - struct dynbuf enc; - curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE); - if(result) - goto fail; - u->fragment = curlx_dyn_ptr(&enc); - } - else { - u->fragment = Curl_memdup0(fragment + 1, fraglen - 1); - if(!u->fragment) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } - } - /* after this, pathlen still contains the query */ - pathlen -= fraglen; - } - - query = memchr(path, '?', pathlen); - if(query) { - size_t qlen = fragment ? (size_t)(fragment - query) : - pathlen - (query - path); - pathlen -= qlen; - u->query_present = TRUE; - if(qlen > 1) { - if(flags & CURLU_URLENCODE) { - struct dynbuf enc; - curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - /* skip the leading question mark */ - result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE); - if(result) - goto fail; - u->query = curlx_dyn_ptr(&enc); - } - else { - u->query = Curl_memdup0(query + 1, qlen - 1); - if(!u->query) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } - } - else { - /* single byte query */ - u->query = curlx_strdup(""); - if(!u->query) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + if(!ures) { + const char *query = memchr(path, '?', pathlen); + if(query) { + size_t qlen = pathlen - (query - path); + ures = handle_query(u, query, qlen, flags); + pathlen -= qlen; } } - - if(pathlen && (flags & CURLU_URLENCODE)) { - struct dynbuf enc; - curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - result = urlencode_str(&enc, path, pathlen, TRUE, FALSE); - if(result) - goto fail; - pathlen = curlx_dyn_len(&enc); - path = u->path = curlx_dyn_ptr(&enc); + if(!ures) + /* the fragment and query parts are trimmed off from the path */ + ures = handle_path(u, path, pathlen, flags, is_file); + if(!ures) { + u->host = curlx_dyn_ptr(&host); + return CURLUE_OK; } - - if(pathlen <= 1) { - /* there is no path left or just the slash, unset */ - path = NULL; - } - else { - if(!u->path) { - u->path = Curl_memdup0(path, pathlen); - if(!u->path) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - path = u->path; - } - else if(flags & CURLU_URLENCODE) - /* it might have encoded more than just the path so cut it */ - u->path[pathlen] = 0; - - if(!(flags & CURLU_PATH_AS_IS)) { - /* remove ../ and ./ sequences according to RFC3986 */ - char *dedot; - int err = dedotdotify(path, pathlen, &dedot); - if(err) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - if(dedot) { - curlx_free(u->path); - u->path = dedot; - } - } - } - - u->host = curlx_dyn_ptr(&host); - - return result; fail: curlx_dyn_free(&host); free_urlhandle(u); - return result; + return ures; } /* @@ -1257,15 +1189,91 @@ static CURLUcode parseurl_and_replace(const char *url, CURLU *u, unsigned int flags) { - CURLUcode result; + CURLUcode ures; CURLU tmpurl; memset(&tmpurl, 0, sizeof(tmpurl)); - result = parseurl(url, &tmpurl, flags); - if(!result) { + ures = parseurl(url, &tmpurl, flags); + if(!ures) { free_urlhandle(u); *u = tmpurl; } - return result; + return ures; +} + +/* + * Concatenate a relative URL onto a base URL making it absolute. + */ +static CURLUcode redirect_url(const char *base, const char *relurl, + CURLU *u, unsigned int flags) +{ + struct dynbuf urlbuf; + bool host_changed = FALSE; + const char *useurl = relurl; + const char *cutoff = NULL; + size_t prelen; + CURLUcode uc; + + /* protsep points to the start of the hostname, after [scheme]:// */ + const char *protsep = base + strlen(u->scheme) + 3; + DEBUGASSERT(base && relurl && u); /* all set here */ + if(!base) + return CURLUE_MALFORMED_INPUT; /* should never happen */ + + /* handle different relative URL types */ + switch(relurl[0]) { + case '/': + if(relurl[1] == '/') { + /* protocol-relative URL: //example.com/path */ + cutoff = protsep; + useurl = &relurl[2]; + host_changed = TRUE; + } + else + /* absolute /path */ + cutoff = strchr(protsep, '/'); + break; + + case '#': + /* fragment-only change */ + if(u->fragment) + cutoff = strchr(protsep, '#'); + break; + + default: + /* path or query-only change */ + if(u->query && u->query[0]) + /* remove existing query */ + cutoff = strchr(protsep, '?'); + else if(u->fragment && u->fragment[0]) + /* Remove existing fragment */ + cutoff = strchr(protsep, '#'); + + if(relurl[0] != '?') { + /* append a relative path after the last slash */ + cutoff = memrchr(protsep, '/', + cutoff ? (size_t)(cutoff - protsep) : strlen(protsep)); + if(cutoff) + cutoff++; /* truncate after last slash */ + } + break; + } + + prelen = cutoff ? (size_t)(cutoff - base) : strlen(base); + + /* build new URL */ + curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH); + + if(!curlx_dyn_addn(&urlbuf, base, prelen) && + !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, + QUERY_NOT_YET)) { + uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u, + flags & ~U_CURLU_PATH_AS_IS); + } + else + uc = CURLUE_OUT_OF_MEMORY; + + curlx_dyn_free(&urlbuf); + return uc; } /* @@ -1285,9 +1293,9 @@ #define DUP(dest, src, name) \ do { \ - if(src->name) { \ - dest->name = curlx_strdup(src->name); \ - if(!dest->name) \ + if((src)->name) { \ + (dest)->name = curlx_strdup((src)->name); \ + if(!(dest)->name) \ goto fail; \ } \ } while(0) @@ -1349,7 +1357,7 @@ bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; bool punycode = (flags & CURLU_PUNYCODE) && (what == CURLUPART_HOST); bool depunyfy = (flags & CURLU_PUNY2IDN) && (what == CURLUPART_HOST); - char *part = Curl_memdup0(ptr, partlen); + char *part = curlx_memdup0(ptr, partlen); *partp = NULL; if(!part) return CURLUE_OUT_OF_MEMORY; @@ -1365,11 +1373,12 @@ if(urldecode) { char *decoded; size_t dlen; - /* this unconditional rejection of control bytes is documented - API behavior */ - CURLcode res = Curl_urldecode(part, partlen, &decoded, &dlen, REJECT_CTRL); + /* this unconditional rejection of control bytes is documented API + behavior */ + CURLcode result = Curl_urldecode(part, partlen, &decoded, &dlen, + REJECT_CTRL); curlx_free(part); - if(res) + if(result) return CURLUE_URLDECODE; part = decoded; partlen = dlen; @@ -1377,7 +1386,8 @@ if(urlencode) { struct dynbuf enc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - uc = urlencode_str(&enc, part, partlen, TRUE, what == CURLUPART_QUERY); + uc = urlencode_str(&enc, part, partlen, TRUE, what == CURLUPART_QUERY ? + QUERY_YES : QUERY_NO); curlx_free(part); if(uc) return uc; @@ -1410,30 +1420,26 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) { char *url; - const char *scheme; - char *options = u->options; - char *port = u->port; char *allochost = NULL; - bool show_fragment = - u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY); - bool show_query = (u->query && u->query[0]) || - (u->query_present && flags & CURLU_GET_EMPTY); - bool punycode = (flags & CURLU_PUNYCODE) ? 1 : 0; - bool depunyfy = (flags & CURLU_PUNY2IDN) ? 1 : 0; - bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; + const char *fragmentsep = + (u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY)) ? + "#" : ""; + const char *querysep = ((u->query && u->query[0]) || + (u->query_present && flags & CURLU_GET_EMPTY)) ? + "?" : ""; char portbuf[7]; if(u->scheme && curl_strequal("file", u->scheme)) { url = curl_maprintf("file://%s%s%s%s%s", - u->path, - show_query ? "?": "", - u->query ? u->query : "", - show_fragment ? "#": "", - u->fragment ? u->fragment : ""); + u->path, querysep, u->query ? u->query : "", + fragmentsep, u->fragment ? u->fragment : ""); } else if(!u->host) return CURLUE_NO_HOST; else { - const struct Curl_handler *h = NULL; + const char *scheme; + char *options = u->options; + char *port = u->port; + const struct Curl_scheme *h = NULL; char schemebuf[MAX_SCHEME_LEN + 5]; if(u->scheme) scheme = u->scheme; @@ -1442,7 +1448,7 @@ else return CURLUE_NO_SCHEME; - h = Curl_get_scheme_handler(scheme); + h = Curl_get_scheme(scheme); if(!port && (flags & CURLU_DEFAULT_PORT)) { /* there is no stored port number, but asked to deliver a default one for the scheme */ @@ -1474,19 +1480,19 @@ allochost = curlx_dyn_ptr(&enc); } } - else if(urlencode) { + else if(flags & CURLU_URLENCODE) { allochost = curl_easy_escape(NULL, u->host, 0); if(!allochost) return CURLUE_OUT_OF_MEMORY; } - else if(punycode) { + else if(flags & CURLU_PUNYCODE) { if(!Curl_is_ASCII_name(u->host)) { CURLUcode ret = host_decode(u->host, &allochost); if(ret) return ret; } } - else if(depunyfy) { + else if(flags & CURLU_PUNY2IDN) { if(Curl_is_ASCII_name(u->host)) { CURLUcode ret = host_encode(u->host, &allochost); if(ret) @@ -1502,18 +1508,18 @@ url = curl_maprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", schemebuf, u->user ? u->user : "", - u->password ? ":": "", + u->password ? ":" : "", u->password ? u->password : "", options ? ";" : "", options ? options : "", - (u->user || u->password || options) ? "@": "", + (u->user || u->password || options) ? "@" : "", allochost ? allochost : u->host, - port ? ":": "", + port ? ":" : "", port ? port : "", u->path ? u->path : "/", - show_query ? "?": "", + querysep, u->query ? u->query : "", - show_fragment ? "#": "", + fragmentsep, u->fragment ? u->fragment : ""); curlx_free(allochost); } @@ -1540,7 +1546,7 @@ case CURLUPART_SCHEME: ptr = u->scheme; ifmissing = CURLUE_NO_SCHEME; - flags &= ~CURLU_URLDECODE; /* never for schemes */ + flags &= ~U_CURLU_URLDECODE; /* never for schemes */ if((flags & CURLU_NO_GUESS_SCHEME) && u->guessed_scheme) return CURLUE_NO_SCHEME; break; @@ -1567,11 +1573,11 @@ case CURLUPART_PORT: ptr = u->port; ifmissing = CURLUE_NO_PORT; - flags &= ~CURLU_URLDECODE; /* never for port */ + flags &= ~U_CURLU_URLDECODE; /* never for port */ if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { /* there is no stored port number, but asked to deliver a default one for the scheme */ - const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); + const struct Curl_scheme *h = Curl_get_scheme(u->scheme); if(h) { curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); ptr = portbuf; @@ -1580,7 +1586,7 @@ else if(ptr && u->scheme) { /* there is a stored port number, but ask to inhibit if it matches the default one for the scheme */ - const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); + const struct Curl_scheme *h = Curl_get_scheme(u->scheme); if(h && (h->defport == u->portnum) && (flags & CURLU_NO_DEFAULT_PORT)) ptr = NULL; @@ -1622,18 +1628,19 @@ unsigned int flags) { size_t plen = strlen(scheme); - const struct Curl_handler *h = NULL; + const struct Curl_scheme *h = NULL; if((plen > MAX_SCHEME_LEN) || (plen < 1)) /* too long or too short */ return CURLUE_BAD_SCHEME; /* verify that it is a fine scheme */ - h = Curl_get_scheme_handler(scheme); + h = Curl_get_scheme(scheme); + if(!(flags & CURLU_NON_SUPPORT_SCHEME) && (!h || !h->run)) + return CURLUE_UNSUPPORTED_SCHEME; if(!h) { const char *s = scheme; - if(!(flags & CURLU_NON_SUPPORT_SCHEME)) - return CURLUE_UNSUPPORTED_SCHEME; if(ISALPHA(*s)) { /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ + s++; while(--plen) { if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.')) s++; /* fine */ @@ -1722,37 +1729,37 @@ memset(u, 0, sizeof(struct Curl_URL)); break; case CURLUPART_SCHEME: - Curl_safefree(u->scheme); + curlx_safefree(u->scheme); u->guessed_scheme = FALSE; break; case CURLUPART_USER: - Curl_safefree(u->user); + curlx_safefree(u->user); break; case CURLUPART_PASSWORD: - Curl_safefree(u->password); + curlx_safefree(u->password); break; case CURLUPART_OPTIONS: - Curl_safefree(u->options); + curlx_safefree(u->options); break; case CURLUPART_HOST: - Curl_safefree(u->host); + curlx_safefree(u->host); break; case CURLUPART_ZONEID: - Curl_safefree(u->zoneid); + curlx_safefree(u->zoneid); break; case CURLUPART_PORT: u->portnum = 0; - Curl_safefree(u->port); + curlx_safefree(u->port); break; case CURLUPART_PATH: - Curl_safefree(u->path); + curlx_safefree(u->path); break; case CURLUPART_QUERY: - Curl_safefree(u->query); + curlx_safefree(u->query); u->query_present = FALSE; break; case CURLUPART_FRAGMENT: - Curl_safefree(u->fragment); + curlx_safefree(u->fragment); u->fragment_present = FALSE; break; default: @@ -1830,7 +1837,7 @@ break; case CURLUPART_HOST: storep = &u->host; - Curl_safefree(u->zoneid); + curlx_safefree(u->zoneid); break; case CURLUPART_ZONEID: storep = &u->zoneid; @@ -1862,7 +1869,7 @@ { const char *newp; struct dynbuf enc; - curlx_dyn_init(&enc, nalloc * 3 + 1 + leadingslash); + curlx_dyn_init(&enc, (nalloc * 3) + 1 + leadingslash); if(leadingslash && (part[0] != '/')) { CURLcode result = curlx_dyn_addn(&enc, "/", 1); @@ -1981,3 +1988,36 @@ } return CURLUE_OK; } + +bool Curl_url_same_origin(CURLU *base, CURLU *href) +{ + const struct Curl_scheme *s = NULL; + + /* base must be an absolute URL */ + if(!base->scheme || !base->host) + return FALSE; + if(href->scheme && !curl_strequal(base->scheme, href->scheme)) + return FALSE; + if(href->host) { + if(!curl_strequal(base->host, href->host)) + return FALSE; + if(!curl_strequal(base->port, href->port)) { + /* This may still match if only one has an explicit port + * and it is the default for the scheme. */ + if(base->port && href->port) + return FALSE; + + s = Curl_get_scheme(base->scheme); + if(!s) /* Cannot match default port for unknown scheme */ + return FALSE; + + /* The port which is set must be the default one */ + if((base->port && (base->portnum != s->defport)) || + (href->port && (href->portnum != s->defport))) + return FALSE; + } + } + else if(href->port) /* no host in href, then there must be no port */ + return FALSE; + return TRUE; +}
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index aba1693..f35bfee 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h
@@ -26,74 +26,7 @@ /* This file is for lib internal stuff */ #include "curl_setup.h" -#define PORT_FTP 21 -#define PORT_FTPS 990 -#define PORT_TELNET 23 -#define PORT_HTTP 80 -#define PORT_HTTPS 443 -#define PORT_DICT 2628 -#define PORT_LDAP 389 -#define PORT_LDAPS 636 -#define PORT_TFTP 69 -#define PORT_SSH 22 -#define PORT_IMAP 143 -#define PORT_IMAPS 993 -#define PORT_POP3 110 -#define PORT_POP3S 995 -#define PORT_SMB 445 -#define PORT_SMBS 445 -#define PORT_SMTP 25 -#define PORT_SMTPS 465 /* sometimes called SSMTP */ -#define PORT_RTSP 554 -#define PORT_RTMP 1935 -#define PORT_RTMPT PORT_HTTP -#define PORT_RTMPS PORT_HTTPS -#define PORT_GOPHER 70 -#define PORT_MQTT 1883 - -struct curl_trc_featt; - -#ifdef USE_ECH -/* CURLECH_ bits for the tls_ech option */ -# define CURLECH_DISABLE (1 << 0) -# define CURLECH_GREASE (1 << 1) -# define CURLECH_ENABLE (1 << 2) -# define CURLECH_HARD (1 << 3) -# define CURLECH_CLA_CFG (1 << 4) -#endif - -#ifndef CURL_DISABLE_WEBSOCKETS -/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, - * the rest are internal information. If we use higher bits we only do this on - * platforms that have a >= 64-bit type and then we use such a type for the - * protocol fields in the protocol handler. - */ -#define CURLPROTO_WS (1L << 30) -#define CURLPROTO_WSS ((curl_prot_t)1 << 31) -#else -#define CURLPROTO_WS 0L -#define CURLPROTO_WSS 0L -#endif - -/* the default protocols accepting a redirect to */ -#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \ - CURLPROTO_FTPS) - -/* This should be undefined once we need bit 32 or higher */ -#define PROTO_TYPE_SMALL - -#ifndef PROTO_TYPE_SMALL -typedef curl_off_t curl_prot_t; -#else -typedef unsigned int curl_prot_t; -#endif - -/* This mask is for all the old protocols that are provided and defined in the - public header and shall exclude protocols added since which are not exposed - in the API */ -#define CURLPROTO_MASK (0x3ffffff) - -#define CURL_DEFAULT_USER "anonymous" +#define CURL_DEFAULT_USER "anonymous" #define CURL_DEFAULT_PASSWORD "ftp@example.com" #if !defined(_WIN32) && !defined(MSDOS) @@ -102,39 +35,15 @@ #define CURL_PREFER_LF_LINEENDS #endif -/* Convenience defines for checking protocols or their SSL based version. Each - protocol handler should only ever have a single CURLPROTO_ in its protocol - field. */ -#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_WS | \ - CURLPROTO_WSS) -#define PROTO_FAMILY_FTP (CURLPROTO_FTP | CURLPROTO_FTPS) -#define PROTO_FAMILY_POP3 (CURLPROTO_POP3 | CURLPROTO_POP3S) -#define PROTO_FAMILY_SMB (CURLPROTO_SMB | CURLPROTO_SMBS) -#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 */ #define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") -/* Default FTP/IMAP etc response timeout in milliseconds */ -#define RESP_TIMEOUT (60 * 1000) - /* Max string input length is a precaution against abuse and to detect junk input easier and better. */ #define CURL_MAX_INPUT_LENGTH 8000000 -#include "cookie.h" -#include "psl.h" -#include "formdata.h" - #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -144,6 +53,10 @@ #include "curlx/timeval.h" +#include "asyn.h" +#include "cookie.h" +#include "psl.h" +#include "formdata.h" #include "http_chunks.h" /* for the structs and enum stuff */ #include "hostip.h" #include "hash.h" @@ -171,6 +84,7 @@ size_t *pnread); /* how much received */ #include "mime.h" +#include "protocol.h" #include "ftp.h" #include "http.h" #include "smb.h" @@ -180,14 +94,14 @@ #include "cf-socket.h" #ifdef HAVE_GSSAPI -# ifdef HAVE_GSSGNU -# include <gss.h> -# elif defined(HAVE_GSSAPI_H) -# include <gssapi.h> -# else /* MIT Kerberos */ -# include <gssapi/gssapi.h> -# include <gssapi/gssapi_krb5.h> /* for GSS_C_CHANNEL_BOUND_FLAG, in 1.19+ */ -# endif +# ifdef HAVE_GSSGNU +# include <gss.h> +# elif defined(HAVE_GSSAPI_H) +# include <gssapi.h> +# else /* MIT Kerberos */ +# include <gssapi/gssapi.h> +# include <gssapi/gssapi_krb5.h> /* for GSS_C_CHANNEL_BOUND_FLAG in 1.19+ */ +# endif #endif #ifdef USE_LIBSSH2 @@ -218,16 +132,12 @@ * us early warning on things only discovered by valgrind otherwise. */ #define GOOD_EASY_HANDLE(x) \ (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \ - (DEBUGASSERT(!(x)), FALSE)) + (DEBUGASSERT(!(x)), FALSE)) #else #define GOOD_EASY_HANDLE(x) \ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) #endif -/* SSL backend-specific data; declared differently by each SSL backend */ -struct ssl_backend_data; -struct Curl_ssl_scache_entry; - struct ssl_primary_config { char *CApath; /* certificate directory (does not work on Windows) */ char *CAfile; /* certificate to verify peer against */ @@ -246,9 +156,9 @@ char *password; /* TLS password (for, e.g., SRP) */ #endif char *curves; /* list of curves to use */ - unsigned int version_max; /* max supported version the client wants to use */ - unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ - unsigned char version; /* what version the client wants to use */ + uint32_t version_max; /* max supported version the client wants to use */ + uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ + uint8_t version; /* what version the client wants to use */ BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ BIT(verifystatus); /* set TRUE if certificate status must be checked */ @@ -307,7 +217,7 @@ char *qop; char *algorithm; int nc; /* nonce count */ - unsigned char algo; + uint8_t algo; BIT(stale); /* set true for re-negotiation */ BIT(userhash); #endif @@ -330,16 +240,11 @@ GSS_AUTHSUCC } curlnegotiate; -#ifdef CURL_DISABLE_PROXY -#define CONN_IS_PROXIED(x) 0 -#else -#define CONN_IS_PROXIED(x) x->bits.proxy -#endif - /* * Boolean values that concerns this connection. */ struct ConnectBits { + BIT(connect_only); #ifndef CURL_DISABLE_PROXY BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ @@ -358,8 +263,6 @@ that overrides the host in the URL */ BIT(conn_to_port); /* if set, this connection has a "connect to port" that overrides the port in the URL (remote port) */ - BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6 - IP address */ BIT(ipv6); /* we communicate with a site using an IPv6 address */ BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is supposed to be called, after ->curl_do() */ @@ -386,9 +289,6 @@ BIT(multiplex); /* connection is multiplexed */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ -#ifndef CURL_DISABLE_DOH - BIT(doh); -#endif #ifdef USE_UNIX_SOCKETS BIT(abstract_unix_socket); #endif @@ -401,6 +301,7 @@ BIT(shutdown_handler); /* connection shutdown: handler shut down */ BIT(shutdown_filters); /* connection shutdown: filters shut down */ BIT(in_cpool); /* connection is kept in a connection pool */ + BIT(dns_resolved); /* DNS records for connection were resolved */ }; struct hostname { @@ -410,164 +311,9 @@ const char *dispname; /* name to display, as 'name' might be encoded */ }; -/* - * Flags on the keepon member of the Curl_transfer_keeper - */ - -#define KEEP_NONE 0 -#define KEEP_RECV (1 << 0) /* there is or may be data to read */ -#define KEEP_SEND (1 << 1) /* there is or may be data to write */ - -/* transfer wants to send */ -#define CURL_WANT_SEND(data) ((data)->req.keepon & KEEP_SEND) -/* transfer wants to receive */ -#define CURL_WANT_RECV(data) ((data)->req.keepon & KEEP_RECV) - #define FIRSTSOCKET 0 #define SECONDARYSOCKET 1 -/* - * Specific protocol handler. - */ - -struct Curl_handler { - const char *scheme; /* URL scheme name in lowercase */ - - /* Complement to setup_connection_internals(). This is done before the - transfer "owns" the connection. */ - CURLcode (*setup_connection)(struct Curl_easy *data, - struct connectdata *conn); - - /* These two functions MUST be set to be protocol dependent */ - CURLcode (*do_it)(struct Curl_easy *data, bool *done); - CURLcode (*done)(struct Curl_easy *, CURLcode, bool); - - /* If the curl_do() function is better made in two halves, this - * curl_do_more() function will be called afterwards, if set. For example - * for doing the FTP stuff after the PASV/PORT command. - */ - CURLcode (*do_more)(struct Curl_easy *, int *); - - /* This function *MAY* be set to a protocol-dependent function that is run - * after the connect() and everything is done, as a step in the connection. - * The 'done' pointer points to a bool that should be set to TRUE if the - * function completes before return. If it does not complete, the caller - * should call the ->connecting() function until it is. - */ - CURLcode (*connect_it)(struct Curl_easy *data, bool *done); - - /* See above. */ - CURLcode (*connecting)(struct Curl_easy *data, bool *done); - CURLcode (*doing)(struct Curl_easy *data, bool *done); - - /* Called from the multi interface during the PROTOCONNECT phase, and it - should then return a proper fd set */ - CURLcode (*proto_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DOING phase, and it should - then return a proper fd set */ - CURLcode (*doing_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DO_MORE phase, and it should - then return a proper fd set */ - CURLcode (*domore_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DO_DONE, PERFORM and - WAITPERFORM phases, and it should then return a proper fd set. Not setting - this will make libcurl use the generic default one. */ - CURLcode (*perform_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* This function *MAY* be set to a protocol-dependent function that is run - * by the curl_disconnect(), as a step in the disconnection. If the handler - * is called because the connection has been considered dead, - * dead_connection is set to TRUE. The connection is (again) associated with - * the transfer here. - */ - CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, - bool dead_connection); - - /* If used, this function gets called from transfer.c to - allow the protocol to do extra handling in writing response to - the client. */ - CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen, - bool is_eos); - - /* If used, this function gets called from transfer.c to - allow the protocol to do extra handling in writing a single response - header line to the client. */ - CURLcode (*write_resp_hd)(struct Curl_easy *data, - const char *hd, size_t hdlen, bool is_eos); - - /* This function can perform various checks on the connection. See - CONNCHECK_* for more information about the checks that can be performed, - and CONNRESULT_* for the results that can be returned. */ - unsigned int (*connection_check)(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform); - - /* attach() attaches this transfer to this connection */ - void (*attach)(struct Curl_easy *data, struct connectdata *conn); - - /* return CURLE_OK if a redirect to `newurl` should be followed, - CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change - the way the follow request is performed. */ - CURLcode (*follow)(struct Curl_easy *data, const char *newurl, - followtype type); - - uint16_t defport; /* Default port. */ - curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single - specific protocol bit */ - curl_prot_t family; /* single bit for protocol family; basically the - non-TLS name of the protocol this is */ - unsigned int flags; /* Extra particular characteristics, see PROTOPT_* */ - -}; - -#define PROTOPT_NONE 0 /* nothing extra */ -#define PROTOPT_SSL (1 << 0) /* uses SSL */ -#define PROTOPT_DUAL (1 << 1) /* this protocol uses two connections */ -#define PROTOPT_CLOSEACTION (1 << 2) /* need action before socket close */ -/* some protocols will have to call the underlying functions without regard to - what exact state the socket signals. IE even if the socket says "readable", - the send function might need to be called while uploading, or vice versa. -*/ -#define PROTOPT_DIRLOCK (1 << 3) -#define PROTOPT_NONETWORK (1 << 4) /* protocol does not use the network! */ -#define PROTOPT_NEEDSPWD (1 << 5) /* needs a password, and if none is set it - gets a default */ -#define PROTOPT_NOURLQUERY (1 << 6) /* protocol cannot handle - URL query strings (?foo=bar) ! */ -#define PROTOPT_CREDSPERREQUEST (1 << 7) /* requires login credentials per - request instead of per - connection */ -#define PROTOPT_ALPN (1 << 8) /* set ALPN for this */ -/* (1 << 9) was PROTOPT_STREAM, now free */ -#define PROTOPT_URLOPTIONS (1 << 10) /* allow options part in the userinfo - field of the URL */ -#define PROTOPT_PROXY_AS_HTTP (1 << 11) /* allow this non-HTTP scheme over a - HTTP proxy as HTTP proxies may know - this protocol and act as - a gateway */ -#define PROTOPT_WILDCARD (1 << 12) /* protocol supports wildcard matching */ -#define PROTOPT_USERPWDCTRL (1 << 13) /* Allow "control bytes" (< 32 ASCII) in - username and password */ -#define PROTOPT_NOTCPPROXY (1 << 14) /* this protocol cannot proxy over TCP */ -#define PROTOPT_SSL_REUSE (1 << 15) /* this protocol may reuse an existing - SSL connection in the same family - without having PROTOPT_SSL. */ -#define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ - -#define CONNCHECK_NONE 0 /* No checks */ -#define CONNCHECK_ISDEAD (1 << 0) /* Check if the connection is dead. */ -#define CONNCHECK_KEEPALIVE (1 << 1) /* Perform any keepalive function. */ - -#define CONNRESULT_NONE 0 /* No extra information. */ -#define CONNRESULT_DEAD (1 << 0) /* The connection is dead. */ - #define TRNSPRT_NONE 0 #define TRNSPRT_TCP 3 #define TRNSPRT_UDP 4 @@ -590,7 +336,7 @@ struct proxy_info { struct hostname host; uint16_t port; - unsigned char proxytype; /* what kind of proxy that is in use */ + uint8_t proxytype; /* what kind of proxy that is in use */ char *user; /* proxy username string, allocated */ char *passwd; /* proxy password string, allocated */ }; @@ -624,7 +370,6 @@ struct Curl_hash meta_hash; struct hostname host; - char *hostname_resolve; /* hostname to resolve to address, allocated */ char *secondaryhostname; /* secondary socket hostname (ftp) */ struct hostname conn_to_host; /* the host to connect to. valid only if bits.conn_to_host is set */ @@ -638,7 +383,7 @@ char *sasl_authzid; /* authorization identity string, allocated */ char *oauth_bearer; /* OAUTH2 bearer, allocated */ struct curltime created; /* creation time */ - struct curltime lastused; /* when returned to the connection poolas idle */ + struct curltime lastused; /* when returned to the connection pool as idle */ /* A connection can have one or two sockets and connection filters. * The protocol using the 2nd one is FTP for CONTROL+DATA sockets */ @@ -662,8 +407,8 @@ #endif struct ConnectBits bits; /* various state-flags for this connection */ - const struct Curl_handler *handler; /* Connection's protocol handler */ - const struct Curl_handler *given; /* The protocol first given */ + const struct Curl_scheme *scheme; /* Connection's protocol handler */ + const struct Curl_scheme *given; /* The protocol first given */ /* Protocols can use a custom keepalive mechanism to keep connections alive. This allows those protocols to track the last time the keepalive mechanism @@ -675,7 +420,7 @@ * for concurrency reasons. That multi might run in another thread. * `attached_multi` is set by the first transfer attached and cleared * when the last one is detached. - * NEVER call anything on this multi, just check for equality. */ + * NEVER call anything on this multi, check for equality. */ struct Curl_multi *attached_multi; /*************** Request - specific items ************/ @@ -703,33 +448,31 @@ that subsequent bound-requested connections are not accidentally reusing wrong connections. */ char *localdev; - unsigned short localportrange; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) int socks5_gssapi_enctype; #endif - /* The field below gets set in connect.c:connecthost() */ - int remote_port; /* the remote port, not the proxy port! */ - int conn_to_port; /* the remote port to connect to. valid only if - bits.conn_to_port is set */ - uint32_t attached_xfers; /* # of attached easy handles */ #ifdef USE_IPV6 - unsigned int scope_id; /* Scope id for IPv6 */ + uint32_t scope_id; /* Scope id for IPv6 */ #endif - unsigned short localport; - unsigned short secondary_port; /* secondary socket remote port to connect to + /* The field below gets set in connect.c:connecthost() */ + uint16_t remote_port; /* the remote port, not the proxy port! */ + uint16_t conn_to_port; /* the remote port to connect to. valid only if + bits.conn_to_port is set */ + uint16_t localportrange; + uint16_t localport; + uint16_t secondary_port; /* secondary socket remote port to connect to (ftp) */ - unsigned char transport_wanted; /* one of the TRNSPRT_* defines. Not - necessarily the transport the connection ends using due to Alt-Svc - and happy eyeballing. Use `Curl_conn_get_transport() for actual value - once the connection is set up. */ - unsigned char ip_version; /* copied from the Curl_easy at creation time */ + uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not necessarily + the transport the connection ends using due to Alt-Svc and happy + eyeballing. Use Curl_conn_get_transport() for actual value once the + connection is set up. */ + uint8_t ip_version; /* copied from the Curl_easy at creation time */ /* HTTP version last responded with by the server or negotiated via ALPN. * 0 at start, then one of 09, 10, 11, etc. */ - unsigned char httpversion_seen; - unsigned char connect_only; - unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ + uint8_t httpversion_seen; + uint8_t gssapi_delegation; /* inherited from set.gssapi_delegation */ }; #ifndef CURL_DISABLE_PROXY @@ -757,15 +500,15 @@ time_t filetime; /* If requested, this is might get set. Set to -1 if the time was unretrievable. */ 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 */ - unsigned long proxyauthpicked; /* selected proxy auth type */ - unsigned long httpauthpicked; /* selected host auth type */ - long numconnects; /* how many new connection did libcurl created */ + curl_off_t numconnects; /* how many new connections libcurl created */ + uint32_t proxyauthavail; /* what proxy auth types were announced */ + uint32_t httpauthavail; /* what host auth types were announced */ + uint32_t proxyauthpicked; /* selected proxy auth type */ + uint32_t httpauthpicked; /* selected host auth type */ char *contenttype; /* the content type of the object */ char *wouldredirect; /* URL this would have been redirected to if asked to */ curl_off_t retry_after; /* info from Retry-After: header */ - unsigned int header_size; /* size of read header(s) in bytes */ + uint32_t header_size; /* size of read header(s) in bytes */ /* PureInfo primary ip_quadruple is copied over from the connectdata struct in order to allow curl_easy_getinfo() to return this information @@ -778,7 +521,7 @@ number of the used URL, independent of proxy or not */ const char *conn_scheme; - unsigned int conn_protocol; + uint32_t conn_protocol; struct curl_certinfo certs; /* info about the certs. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ CURLproxycode pxcode; @@ -800,6 +543,7 @@ force redraw at next call */ struct pgrs_dir ul; struct pgrs_dir dl; + curl_off_t deliver; /* amount of data delivered to application */ curl_off_t current_speed; /* uses the currently fastest transfer */ curl_off_t earlydata_sent; @@ -825,7 +569,7 @@ curl_off_t speed_amount[CURL_SPEED_RECORDS]; struct curltime speed_time[CURL_SPEED_RECORDS]; - unsigned char speeder_c; + uint32_t speeder_c; BIT(hide); BIT(ul_size_known); BIT(dl_size_known); @@ -851,11 +595,11 @@ } Curl_RtspReq; struct auth { - unsigned long want; /* Bitmask set to the authentication methods wanted by - app (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */ - unsigned long picked; - unsigned long avail; /* Bitmask for what the server reports to support for - this resource */ + uint32_t want; /* Bitmask set to the authentication methods wanted by app + (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */ + uint32_t picked; + uint32_t avail; /* Bitmask for what the server reports to support for this + resource */ BIT(done); /* TRUE when the auth phase is done and ready to do the actual request */ BIT(multipass); /* TRUE if this is not yet authenticated but within the @@ -907,13 +651,6 @@ EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; -typedef enum { - TRAILERS_NONE, - TRAILERS_INITIALIZED, - TRAILERS_SENDING, - TRAILERS_DONE -} trailers_state; - /* * One instance for each timeout an easy handle can set. */ @@ -948,8 +685,10 @@ curl_off_t recent_conn_id; /* The most recent connection used, might no * longer exist */ struct dynbuf headerb; /* buffer to store headers in */ +#ifndef CURL_DISABLE_HSTS struct curl_slist *hstslist; /* list of HSTS files set by curl_easy_setopt(HSTS) calls */ +#endif curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ @@ -973,9 +712,8 @@ struct auth authhost; /* auth details for host */ struct auth authproxy; /* auth details for proxy */ - struct Curl_dns_entry *dns[2]; /* DNS to connect FIRST/SECONDARY */ #ifdef USE_CURL_ASYNC - struct Curl_async async; /* asynchronous name resolver data */ + struct Curl_resolv_async *async; /* asynchronous name resolver data */ #endif #ifdef USE_OPENSSL @@ -1002,11 +740,10 @@ #ifndef CURL_DISABLE_RTSP /* This RTSP state information survives requests and connections */ - long rtsp_next_client_CSeq; /* the session's next client CSeq */ - long rtsp_next_server_CSeq; /* the session's next server CSeq */ - long rtsp_CSeq_recv; /* most recent CSeq received */ - - unsigned char rtp_channel_mask[32]; /* for the correctness checking of the + uint32_t rtsp_next_client_CSeq; /* the session's next client CSeq */ + uint32_t rtsp_next_server_CSeq; /* the session's next server CSeq */ + uint32_t rtsp_CSeq_recv; /* most recent CSeq received */ + uint8_t rtp_channel_mask[32]; /* for the correctness checking of the interleaved data */ #endif @@ -1036,15 +773,13 @@ struct Curl_llist httphdrs; /* received headers */ struct curl_header headerout[2]; /* for external purposes */ struct Curl_header_store *prevhead; /* the latest added header */ - trailers_state trailers_state; /* whether we are sending trailers - and what stage are we at */ #endif #ifndef CURL_DISABLE_COOKIES struct curl_slist *cookielist; /* list of cookie files set by curl_easy_setopt(COOKIEFILE) calls */ #endif -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */ #endif @@ -1057,13 +792,9 @@ struct dynamically_allocated_data { char *uagent; char *accept_encoding; - char *userpwd; char *rangeline; char *ref; char *host; -#ifndef CURL_DISABLE_COOKIES - char *cookiehost; -#endif #ifndef CURL_DISABLE_RTSP char *rtsp_transport; #endif @@ -1072,7 +803,6 @@ char *user; char *passwd; #ifndef CURL_DISABLE_PROXY - char *proxyuserpwd; char *proxyuser; char *proxypasswd; #endif @@ -1080,10 +810,10 @@ #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; #endif - unsigned short followlocation; /* redirect counter */ - unsigned char retrycount; /* number of retries on a new connection, up to + uint16_t followlocation; /* redirect counter */ + uint8_t retrycount; /* number of retries on a new connection, up to CONN_MAX_RETRIES */ - unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) + uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ unsigned int creds_from:2; /* where is the server credentials originating from, see the CREDS_* defines above */ @@ -1273,7 +1003,7 @@ STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ - STRING_LAST /* not used, just an end-of-list marker */ + STRING_LAST /* not used, an end-of-list marker */ }; enum dupblob { @@ -1297,8 +1027,8 @@ void *out; /* CURLOPT_WRITEDATA */ void *in_set; /* CURLOPT_READDATA */ void *writeheader; /* write the header to this if non-NULL */ - unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */ - unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */ + uint32_t httpauth; /* kind of HTTP authentication to use (bitmask) */ + uint32_t proxyauth; /* kind of proxy authentication to use (bitmask) */ void *postfields; /* if POST, set the fields' values here */ curl_seek_callback seek_func; /* function that seeks the input */ curl_off_t postfieldsize; /* if POST, this might have a size to use instead @@ -1308,7 +1038,7 @@ curl_write_callback fwrite_header; /* function that stores headers */ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ curl_read_callback fread_func_set; /* function that reads the input */ - curl_progress_callback fprogress; /* OLD and deprecated progress callback */ + curl_progress_callback fprogress; /* OLD and deprecated progress callback */ curl_xferinfo_callback fxferinfo; /* progress callback */ curl_debug_callback fdebug; /* function that write informational data */ curl_ioctl_callback ioctl_func; /* function for I/O control */ @@ -1338,8 +1068,7 @@ timediff_t conn_max_age_ms; /* max time since creation to allow a connection that is to be reused */ curl_off_t filesize; /* size of file to upload, -1 means unknown */ - long low_speed_limit; /* bytes/second */ - long low_speed_time; /* number of seconds */ + curl_off_t low_speed_limit; /* bytes/second */ curl_off_t max_send_speed; /* high speed limit in bytes/second for upload */ curl_off_t max_recv_speed; /* high speed limit in bytes/second for download */ @@ -1347,7 +1076,7 @@ struct curl_slist *headers; /* linked list of extra headers */ struct curl_httppost *httppost; /* linked list of old POST data */ #if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) - curl_mimepart mimepost; /* MIME/POST data. */ + curl_mimepart *mimepostp; /* MIME/POST data. */ #endif #ifndef CURL_DISABLE_TELNET struct curl_slist *telnet_options; /* linked list of telnet options */ @@ -1364,14 +1093,14 @@ uint16_t proxyport; /* If non-zero, use this port number by default. If the proxy string features a ":[port]" that one will override this. */ - unsigned char proxytype; /* what kind of proxy */ - unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ + uint8_t proxytype; /* what kind of proxy */ + uint8_t socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ #endif struct ssl_general_config general_ssl; /* general user defined SSL stuff */ timediff_t dns_cache_timeout_ms; /* DNS cache timeout (milliseconds) */ - unsigned int buffer_size; /* size of receive buffer to use */ - unsigned int upload_buffer_size; /* size of upload buffer to use, - keep it >= CURL_MAX_WRITE_SIZE */ + uint32_t buffer_size; /* size of receive buffer to use */ + uint32_t upload_buffer_size; /* size of upload buffer to use, keep it >= + CURL_MAX_WRITE_SIZE */ void *private_data; /* application-private data */ #ifndef CURL_DISABLE_HTTP struct curl_slist *http200aliases; /* linked list of aliases for http200 */ @@ -1379,9 +1108,9 @@ curl_off_t max_filesize; /* Maximum file size to download */ #ifndef CURL_DISABLE_FTP timediff_t accepttimeout; /* in milliseconds, 0 means no timeout */ - unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile */ - unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ - unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */ + uint8_t ftp_filemethod; /* how to get to a file: curl_ftpfile */ + uint8_t ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ + uint8_t ftp_ccc; /* FTP CCC options: curl_ftpccc */ #endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) struct curl_slist *quote; /* after connection is established */ @@ -1395,14 +1124,14 @@ #ifdef USE_SSH curl_sshkeycallback ssh_keyfunc; /* key matching callback */ void *ssh_keyfunc_userp; /* custom pointer to callback */ - int ssh_auth_types; /* allowed SSH auth types */ - unsigned int new_directory_perms; /* when creating remote dirs */ + uint32_t ssh_auth_types; /* allowed SSH auth types */ + uint32_t new_directory_perms; /* when creating remote dirs */ #endif - unsigned int new_file_perms; /* when creating remote files */ + uint32_t new_file_perms; /* when creating remote files */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ struct curl_blob *blobs[BLOB_LAST]; #ifdef USE_IPV6 - unsigned int scope_id; /* Scope id for IPv6 */ + uint32_t scope_id; /* Scope id for IPv6 */ #endif curl_prot_t allowed_protocols; curl_prot_t redir_protocols; @@ -1437,7 +1166,8 @@ curl_resolver_start_callback resolver_start; /* optional callback called before resolver start */ void *resolver_start_client; /* pointer to pass to resolver start callback */ - long upkeep_interval_ms; /* Time between calls for connection upkeep. */ + timediff_t upkeep_interval_ms; /* Time between calls for connection + upkeep. */ CURLU *uh; /* URL handle for the current parsed URL */ #ifndef CURL_DISABLE_HTTP void *trailer_data; /* pointer to pass to trailer data callback */ @@ -1446,50 +1176,49 @@ #ifndef CURL_DISABLE_SMTP struct curl_slist *mail_rcpt; /* linked list of mail recipients */ #endif - unsigned int maxconnects; /* Max idle connections in the connection cache */ + uint32_t maxconnects; /* Max idle connections in the connection cache */ #ifdef USE_ECH - int tls_ech; /* TLS ECH configuration */ + int tls_ech; /* TLS ECH configuration */ #endif short maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ - unsigned short expect_100_timeout; /* in milliseconds */ - unsigned short use_port; /* which port to use (when not using default) */ + uint16_t expect_100_timeout; /* in milliseconds */ + uint16_t use_port; /* which port to use (when not using default) */ + uint16_t low_speed_time; /* number of seconds */ #ifndef CURL_DISABLE_BINDLOCAL - unsigned short localport; /* local port number to bind to */ - unsigned short localportrange; /* number of additional port numbers to test + uint16_t localport; /* local port number to bind to */ + uint16_t localportrange; /* number of additional port numbers to test in case the 'localport' one cannot be bind()ed */ #endif #ifndef CURL_DISABLE_TFTP - unsigned short tftp_blksize; /* in bytes, 0 means use default */ + uint16_t tftp_blksize; /* in bytes, 0 means use default */ #endif #ifndef CURL_DISABLE_NETRC - unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ + uint8_t use_netrc; /* enum CURL_NETRC_OPTION values */ #endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP 1 - create directories that do not exist 2 - the same but also allow MKD to fail once */ - unsigned char ftp_create_missing_dirs; + uint8_t ftp_create_missing_dirs; #endif - unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! (type: curl_usessl)*/ - char keep_post; /* keep POSTs as POSTs after a 30x request; each - bit represents a request, from 301 to 303 */ - unsigned char timecondition; /* kind of time comparison: curl_TimeCond */ - unsigned char method; /* what kind of HTTP request: Curl_HttpReq */ - unsigned char httpwant; /* when non-zero, a specific HTTP version requested + uint8_t use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or + POP3 or others! (type: curl_usessl)*/ + uint8_t timecondition; /* kind of time comparison: curl_TimeCond */ + uint8_t method; /* what kind of HTTP request: Curl_HttpReq */ + uint8_t httpwant; /* when non-zero, a specific HTTP version requested to be used in the library's request(s) */ - unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header + uint8_t ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ - unsigned char upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ + uint8_t upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ #ifdef HAVE_GSSAPI /* GSS-API credential delegation, see the documentation of CURLOPT_GSSAPI_DELEGATION */ - unsigned char gssapi_delegation; + uint8_t gssapi_delegation; #endif - unsigned char http_follow_mode; /* follow HTTP redirects */ + uint8_t http_follow_mode; /* follow HTTP redirects */ BIT(connect_only); /* make connection/request, then let application use the socket */ BIT(connect_only_ws); /* special websocket connect-only level */ @@ -1549,7 +1278,7 @@ BIT(opt_no_body); /* as set with CURLOPT_NOBODY */ BIT(verbose); /* output verbosity */ BIT(reuse_forbid); /* forbidden to be reused, close after use */ - BIT(reuse_fresh); /* do not reuse 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 */ @@ -1591,10 +1320,14 @@ BIT(ws_raw_mode); BIT(ws_no_auto_pong); #endif + BIT(post301); /* keep POSTs as POSTs after a 301 request */ + BIT(post302); /* keep POSTs as POSTs after a 302 request */ + BIT(post303); /* keep POSTs as POSTs after a 303 request */ }; #ifndef CURL_DISABLE_MIME -#define IS_MIME_POST(a) ((a)->set.mimepost.kind != MIMEKIND_NONE) +#define IS_MIME_POST(a) \ + ((a)->set.mimepostp && ((a)->set.mimepostp->kind != MIMEKIND_NONE)) #else #define IS_MIME_POST(a) FALSE #endif @@ -1618,7 +1351,7 @@ struct Curl_easy { /* First a simple identifier to easier detect if a user mix up this easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ - unsigned int magic; + uint32_t magic; /* once an easy handle is tied to a connection pool a non-negative number to distinguish this transfer from other using the same pool. For easier tracking in log output. This may wrap around after LONG_MAX to 0 again,
diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c index fc8a6eb..7976ade 100644 --- a/Utilities/cmcurl/lib/vauth/cleartext.c +++ b/Utilities/cmcurl/lib/vauth/cleartext.c
@@ -24,13 +24,13 @@ * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_POP3) || \ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) -#include "vauth.h" +#include "vauth/vauth.h" /* * Curl_auth_create_plain_message() @@ -81,14 +81,14 @@ * * Parameters: * - * valuep [in] - The username or user's password. + * value [in] - The username or user's password. * out [out] - The result storage. * * Returns void. */ -void Curl_auth_create_login_message(const char *valuep, struct bufref *out) +void Curl_auth_create_login_message(const char *value, struct bufref *out) { - Curl_bufref_set(out, valuep, strlen(valuep), NULL); + Curl_bufref_set(out, value, strlen(value), NULL); } /*
diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c index f539973..d3f2d13 100644 --- a/Utilities/cmcurl/lib/vauth/cram.c +++ b/Utilities/cmcurl/lib/vauth/cram.c
@@ -23,14 +23,13 @@ * RFC2195 CRAM-MD5 authentication * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH -#include "vauth.h" -#include "../curl_hmac.h" -#include "../curl_md5.h" - +#include "vauth/vauth.h" +#include "curl_hmac.h" +#include "curl_md5.h" /* * Curl_auth_create_cram_md5_message()
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index a1fd248..5ecfd60 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -24,18 +24,18 @@ * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH -#include "vauth.h" -#include "digest.h" -#include "../curlx/base64.h" -#include "../curl_md5.h" -#include "../curl_sha256.h" -#include "../curl_sha512_256.h" -#include "../curlx/strparse.h" -#include "../rand.h" +#include "vauth/vauth.h" +#include "vauth/digest.h" +#include "curlx/base64.h" +#include "curl_md5.h" +#include "curl_sha256.h" +#include "curl_sha512_256.h" +#include "curlx/strparse.h" +#include "rand.h" #ifndef USE_WINDOWS_SSPI #define SESSION_ALGO 1 /* for algos with this bit set */ @@ -130,8 +130,9 @@ #ifndef USE_WINDOWS_SSPI /* Convert MD5 chunk to RFC2617 (section 3.1.3) -suitable ASCII string */ -static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ - unsigned char *dest) /* 33 bytes */ +static void auth_digest_md5_to_ascii( + const unsigned char *source, /* 16 bytes */ + unsigned char *dest) /* 33 bytes */ { int i; for(i = 0; i < 16; i++) @@ -139,8 +140,9 @@ } /* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ASCII string */ -static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ - unsigned char *dest) /* 65 bytes */ +static void auth_digest_sha256_to_ascii( + const unsigned char *source, /* 32 bytes */ + unsigned char *dest) /* 65 bytes */ { int i; for(i = 0; i < 32; i++) @@ -148,35 +150,26 @@ } /* Perform quoted-string escaping as described in RFC2616 and its errata */ -static char *auth_digest_string_quoted(const char *source) +static char *auth_digest_string_quoted(const char *s) { - char *dest; - const char *s = source; - size_t n = 1; /* null-terminator */ - - /* Calculate size needed */ + struct dynbuf out; + curlx_dyn_init(&out, 2048); + if(!*s) /* for zero length input, make sure we return an empty string */ + return curlx_strdup(""); while(*s) { - ++n; + CURLcode result; if(*s == '"' || *s == '\\') { - ++n; + result = curlx_dyn_addn(&out, "\\", 1); + if(!result) + result = curlx_dyn_addn(&out, s, 1); } - ++s; + else + result = curlx_dyn_addn(&out, s, 1); + if(result) + return NULL; + s++; } - - dest = curlx_malloc(n); - if(dest) { - char *d = dest; - s = source; - while(*s) { - if(*s == '"' || *s == '\\') { - *d++ = '\\'; - } - *d++ = *s++; - } - *d = '\0'; - } - - return dest; + return curlx_dyn_ptr(&out); } /* Retrieves the value for a corresponding key from the challenge string @@ -262,13 +255,13 @@ * Parameters: * * chlgref [in] - The challenge message. - * nonce [in/out] - The buffer where the nonce will be stored. + * nonce [in/out] - The buffer where the nonce is stored. * nlen [in] - The length of the nonce buffer. - * realm [in/out] - The buffer where the realm will be stored. + * realm [in/out] - The buffer where the realm is stored. * rlen [in] - The length of the realm buffer. - * alg [in/out] - The buffer where the algorithm will be stored. + * alg [in/out] - The buffer where the algorithm is stored. * alen [in] - The length of the algorithm buffer. - * qop [in/out] - The buffer where the qop-options will be stored. + * qop [in/out] - The buffer where the qop-options is stored. * qlen [in] - The length of the qop buffer. * * Returns CURLE_OK on success. @@ -348,9 +341,9 @@ struct MD5_context *ctxt; char *response = NULL; unsigned char digest[MD5_DIGEST_LEN]; - char HA1_hex[2 * MD5_DIGEST_LEN + 1]; - char HA2_hex[2 * MD5_DIGEST_LEN + 1]; - char resp_hash_hex[2 * MD5_DIGEST_LEN + 1]; + char HA1_hex[(2 * MD5_DIGEST_LEN) + 1]; + char HA2_hex[(2 * MD5_DIGEST_LEN) + 1]; + char resp_hash_hex[(2 * MD5_DIGEST_LEN) + 1]; char nonce[64]; char realm[128]; char algorithm[64]; @@ -361,6 +354,9 @@ char method[] = "AUTHENTICATE"; char qop[] = DIGEST_QOP_VALUE_STRING_AUTH; char *spn = NULL; + char *qrealm; + char *qnonce; + char *quserp; /* Decode the challenge message */ CURLcode result = auth_decode_digest_md5_message(chlg, @@ -389,7 +385,7 @@ if(result) return result; - /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ + /* Good so far, now calculate A1 and H(A1) according to RFC 2831 */ ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -474,12 +470,22 @@ for(i = 0; i < MD5_DIGEST_LEN; i++) curl_msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); - /* Generate the response */ - response = curl_maprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\"," - "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\"," - "response=%s,qop=%s", - userp, realm, nonce, - cnonce, nonceCount, spn, resp_hash_hex, qop); + /* escape double quotes and backslashes in the username, realm and nonce as + necessary */ + qrealm = auth_digest_string_quoted(realm); + qnonce = auth_digest_string_quoted(nonce); + quserp = auth_digest_string_quoted(userp); + if(qrealm && qnonce && quserp) + /* Generate the response */ + response = curl_maprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\"," + "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\"," + "response=%s,qop=%s", + quserp, qrealm, qnonce, + cnonce, nonceCount, spn, resp_hash_hex, qop); + + curlx_free(qrealm); + curlx_free(qnonce); + curlx_free(quserp); curlx_free(spn); if(!response) return CURLE_OUT_OF_MEMORY; @@ -666,22 +672,21 @@ * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ static CURLcode auth_create_digest_http_message( - struct Curl_easy *data, - const char *userp, - const char *passwdp, - const unsigned char *request, - const unsigned char *uripath, - struct digestdata *digest, - char **outptr, size_t *outlen, - void (*convert_to_ascii)(unsigned char *, unsigned char *), - CURLcode (*hash)(unsigned char *, const unsigned char *, - const size_t)) + struct Curl_easy *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen, + void (*convert_to_ascii)(const unsigned char *, unsigned char *), + CURLcode (*hash)(unsigned char *, const unsigned char *, const size_t)) { CURLcode result; unsigned char hashbuf[32]; /* 32 bytes/256 bits */ @@ -691,12 +696,15 @@ char userh[65]; char *cnonce = NULL; size_t cnonce_sz = 0; - char *userp_quoted; - char *realm_quoted; - char *nonce_quoted; - char *response = NULL; + char *userp_quoted = NULL; + char *realm_quoted = NULL; + char *nonce_quoted = NULL; char *hashthis = NULL; - char *tmp = NULL; + char *uri_quoted = NULL; + struct dynbuf response; + *outptr = NULL; + + curlx_dyn_init(&response, 4096); /* arbitrary max */ memset(hashbuf, 0, sizeof(hashbuf)); if(!digest->nc) @@ -710,27 +718,27 @@ #endif (unsigned char *)cnoncebuf, sizeof(cnoncebuf)); + if(!result) + result = curlx_base64_encode((uint8_t *)cnoncebuf, sizeof(cnoncebuf), + &cnonce, &cnonce_sz); if(result) - return result; - - result = curlx_base64_encode((uint8_t *)cnoncebuf, sizeof(cnoncebuf), - &cnonce, &cnonce_sz); - if(result) - return result; + goto oom; digest->cnonce = cnonce; } if(digest->userhash) { - hashthis = curl_maprintf("%s:%s", userp, - digest->realm ? digest->realm : ""); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + char *hasht = curl_maprintf("%s:%s", userp, + digest->realm ? digest->realm : ""); + if(!hasht) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } - result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis)); - curlx_free(hashthis); + result = hash(hashbuf, (unsigned char *)hasht, strlen(hasht)); + curlx_free(hasht); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, (unsigned char *)userh); } @@ -745,27 +753,31 @@ unq(nonce-value) ":" unq(cnonce-value) */ - hashthis = curl_maprintf("%s:%s:%s", userp, - digest->realm ? digest->realm : "", passwdp); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + hashthis = curl_maprintf("%s:%s:%s", userp, digest->realm ? + digest->realm : "", passwdp); + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis)); curlx_free(hashthis); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, ha1); if(digest->algo & SESSION_ALGO) { /* nonce and cnonce are OUTSIDE the hash */ - tmp = curl_maprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); - if(!tmp) - return CURLE_OUT_OF_MEMORY; + char *tmp = curl_maprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); + if(!tmp) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } result = hash(hashbuf, (unsigned char *)tmp, strlen(tmp)); curlx_free(tmp); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, ha1); } @@ -782,9 +794,17 @@ 5.1.1 of RFC 2616) */ + uri_quoted = auth_digest_string_quoted((const char *)uripath); + if(!uri_quoted) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } + hashthis = curl_maprintf("%s:%s", request, uripath); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } if(digest->qop && curl_strequal(digest->qop, "auth-int")) { /* We do not support auth-int for PUT or POST */ @@ -794,39 +814,40 @@ result = hash(hashbuf, (const unsigned char *)"", 0); if(result) { curlx_free(hashthis); - return result; + goto oom; } convert_to_ascii(hashbuf, (unsigned char *)hashed); hashthis2 = curl_maprintf("%s:%s", hashthis, hashed); curlx_free(hashthis); hashthis = hashthis2; + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } } - if(!hashthis) - return CURLE_OUT_OF_MEMORY; - result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis)); curlx_free(hashthis); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, ha2); - if(digest->qop) { + if(digest->qop) hashthis = curl_maprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, digest->cnonce, digest->qop, ha2); - } - else { + else hashthis = curl_maprintf("%s:%s:%s", ha1, digest->nonce, ha2); - } - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis)); curlx_free(hashthis); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, request_digest); /* For test case 64 (snooped from a Mozilla 1.3a request) @@ -835,16 +856,18 @@ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" Digest parameters are all quoted strings. Username which is provided by - the user will need double quotes and backslashes within it escaped. - realm, nonce, and opaque will need backslashes as well as they were + the user needs double quotes and backslashes within it escaped. + realm, nonce, and opaque needs backslashes as well as they were de-escaped when copied from request header. cnonce is generated with web-safe characters. uri is already percent encoded. nc is 8 hex characters. algorithm and qop with standard values only contain web-safe characters. */ userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp); - if(!userp_quoted) - return CURLE_OUT_OF_MEMORY; + if(!userp_quoted) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } if(digest->realm) realm_quoted = auth_digest_string_quoted(digest->realm); else { @@ -853,98 +876,93 @@ realm_quoted[0] = 0; } if(!realm_quoted) { - curlx_free(userp_quoted); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto oom; } + nonce_quoted = auth_digest_string_quoted(digest->nonce); if(!nonce_quoted) { - curlx_free(realm_quoted); - curlx_free(userp_quoted); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto oom; } if(digest->qop) { - response = curl_maprintf("username=\"%s\", " - "realm=\"%s\", " - "nonce=\"%s\", " - "uri=\"%s\", " - "cnonce=\"%s\", " - "nc=%08x, " - "qop=%s, " - "response=\"%s\"", - userp_quoted, - realm_quoted, - nonce_quoted, - uripath, - digest->cnonce, - digest->nc, - digest->qop, - request_digest); + result = curlx_dyn_addf(&response, "username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "cnonce=\"%s\", " + "nc=%08x, " + "qop=%s, " + "response=\"%s\"", + userp_quoted, + realm_quoted, + nonce_quoted, + uri_quoted, + digest->cnonce, + digest->nc, + digest->qop, + request_digest); /* Increment nonce-count to use another nc value for the next request */ digest->nc++; } else { - response = curl_maprintf("username=\"%s\", " - "realm=\"%s\", " - "nonce=\"%s\", " - "uri=\"%s\", " - "response=\"%s\"", - userp_quoted, - realm_quoted, - nonce_quoted, - uripath, - request_digest); + result = curlx_dyn_addf(&response, "username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "response=\"%s\"", + userp_quoted, + realm_quoted, + nonce_quoted, + uri_quoted, + request_digest); } - curlx_free(nonce_quoted); - curlx_free(realm_quoted); - curlx_free(userp_quoted); - if(!response) - return CURLE_OUT_OF_MEMORY; + if(result) + goto oom; /* Add the optional fields */ if(digest->opaque) { - char *opaque_quoted; /* Append the opaque */ - opaque_quoted = auth_digest_string_quoted(digest->opaque); + char *opaque_quoted = auth_digest_string_quoted(digest->opaque); if(!opaque_quoted) { - curlx_free(response); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto oom; } - tmp = curl_maprintf("%s, opaque=\"%s\"", response, opaque_quoted); - curlx_free(response); + result = curlx_dyn_addf(&response, ", opaque=\"%s\"", opaque_quoted); curlx_free(opaque_quoted); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; + if(result) + goto oom; } if(digest->algorithm) { /* Append the algorithm */ - tmp = curl_maprintf("%s, algorithm=%s", response, digest->algorithm); - curlx_free(response); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; + result = curlx_dyn_addf(&response, ", algorithm=%s", digest->algorithm); + if(result) + goto oom; } if(digest->userhash) { /* Append the userhash */ - tmp = curl_maprintf("%s, userhash=true", response); - curlx_free(response); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; + result = curlx_dyn_add(&response, ", userhash=true"); + if(result) + goto oom; } /* Return the output */ - *outptr = response; - *outlen = strlen(response); + *outptr = curlx_dyn_ptr(&response); + *outlen = curlx_dyn_len(&response); + result = CURLE_OK; - return CURLE_OK; +oom: + curlx_free(nonce_quoted); + curlx_free(realm_quoted); + curlx_free(uri_quoted); + curlx_free(userp_quoted); + if(result) + curlx_dyn_free(&response); + return result; } /* @@ -962,7 +980,7 @@ * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -1013,12 +1031,12 @@ */ void Curl_auth_digest_cleanup(struct digestdata *digest) { - Curl_safefree(digest->nonce); - Curl_safefree(digest->cnonce); - Curl_safefree(digest->realm); - Curl_safefree(digest->opaque); - Curl_safefree(digest->qop); - Curl_safefree(digest->algorithm); + curlx_safefree(digest->nonce); + curlx_safefree(digest->cnonce); + curlx_safefree(digest->realm); + curlx_safefree(digest->opaque); + curlx_safefree(digest->qop); + curlx_safefree(digest->algorithm); digest->nc = 0; digest->algo = ALGO_MD5; /* default algorithm */
diff --git a/Utilities/cmcurl/lib/vauth/digest.h b/Utilities/cmcurl/lib/vauth/digest.h index 902ffab..5b8f3ae 100644 --- a/Utilities/cmcurl/lib/vauth/digest.h +++ b/Utilities/cmcurl/lib/vauth/digest.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c index 615b4bc..f0b6780 100644 --- a/Utilities/cmcurl/lib/vauth/digest_sspi.c +++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -24,17 +24,17 @@ * RFC2831 DIGEST-MD5 authentication * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) -#include "vauth.h" -#include "digest.h" -#include "../curlx/multibyte.h" -#include "../curl_trc.h" -#include "../strdup.h" -#include "../strcase.h" -#include "../strerror.h" +#include "vauth/vauth.h" +#include "vauth/digest.h" +#include "curlx/multibyte.h" +#include "curl_trc.h" +#include "curlx/strdup.h" +#include "strcase.h" +#include "strerror.h" /* * Curl_auth_is_digest_supported() @@ -193,9 +193,7 @@ status == SEC_I_COMPLETE_AND_CONTINUE) Curl_pSecFn->CompleteAuthToken(&credentials, &resp_desc); else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; -#endif + VERBOSE(char buffer[STRERROR_LEN]); Curl_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); @@ -205,10 +203,8 @@ if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; -#ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "schannel: InitializeSecurityContext failed: %s", Curl_sspi_strerror(status, buffer, sizeof(buffer))); -#endif return CURLE_AUTH_ERROR; } @@ -355,7 +351,7 @@ } /* Store the challenge for use later */ - digest->input_token = (BYTE *)Curl_memdup(chlg, chlglen + 1); + digest->input_token = (BYTE *)curlx_memdup(chlg, chlglen + 1); if(!digest->input_token) return CURLE_OUT_OF_MEMORY; @@ -379,7 +375,7 @@ * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -401,8 +397,6 @@ SecBufferDesc chlg_desc; SECURITY_STATUS status; - (void)data; - /* Query the security package for DigestSSP */ status = Curl_pSecFn->QuerySecurityPackageInfo( @@ -433,10 +427,10 @@ (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); } if(digest->http_context) { @@ -466,7 +460,7 @@ else { /* delete the context so a new one can be made */ infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", status); Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } } @@ -480,8 +474,8 @@ TCHAR *spn; /* free the copy of user/passwd used to make the previous identity */ - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); if(userp && *userp) { /* Populate our identity structure */ @@ -521,7 +515,7 @@ if(!digest->passwd) { curlx_free(output_token); Curl_sspi_free_identity(p_identity); - Curl_safefree(digest->user); + curlx_safefree(digest->user); return CURLE_OUT_OF_MEMORY; } } @@ -594,24 +588,20 @@ status == SEC_I_COMPLETE_AND_CONTINUE) Curl_pSecFn->CompleteAuthToken(&credentials, &resp_desc); else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; -#endif + VERBOSE(char buffer[STRERROR_LEN]); Curl_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); curlx_free(output_token); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; -#ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "schannel: InitializeSecurityContext failed: %s", Curl_sspi_strerror(status, buffer, sizeof(buffer))); -#endif return CURLE_AUTH_ERROR; } @@ -622,7 +612,7 @@ Curl_sspi_free_identity(p_identity); } - resp = Curl_memdup0((const char *)output_token, output_token_len); + resp = curlx_memdup0((const char *)output_token, output_token_len); curlx_free(output_token); if(!resp) { return CURLE_OUT_OF_MEMORY; @@ -647,7 +637,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) { /* Free the input token */ - Curl_safefree(digest->input_token); + curlx_safefree(digest->input_token); /* Reset any variables */ digest->input_token_len = 0; @@ -655,12 +645,12 @@ /* Delete security context */ if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } /* Free the copy of user/passwd used to make the identity for http_context */ - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */
diff --git a/Utilities/cmcurl/lib/vauth/gsasl.c b/Utilities/cmcurl/lib/vauth/gsasl.c index b95d6e3..958f4ff 100644 --- a/Utilities/cmcurl/lib/vauth/gsasl.c +++ b/Utilities/cmcurl/lib/vauth/gsasl.c
@@ -23,12 +23,12 @@ * RFC5802 SCRAM-SHA-1 authentication * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_GSASL -#include "vauth.h" -#include "../curl_trc.h" +#include "vauth/vauth.h" +#include "curl_trc.h" #include <gsasl.h>
diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c index 6a9f182..64c735b 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
@@ -24,16 +24,16 @@ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5) -#include "vauth.h" -#include "../curl_sasl.h" -#include "../curl_gssapi.h" -#include "../curl_trc.h" +#include "vauth/vauth.h" +#include "curl_sasl.h" +#include "curl_gssapi.h" +#include "curl_trc.h" -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -240,8 +240,8 @@ /* Process the maximum message size the server can receive */ if(max_size > 0) { - /* The server has told us it supports a maximum receive buffer, however, as - we do not require one unless we are encrypting data, we tell the server + /* The server has told us it supports a maximum receive buffer, but as we + do not require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } @@ -320,7 +320,7 @@ } } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c index 8bf2091..e7491be 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
@@ -23,12 +23,12 @@ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5) -#include "vauth.h" -#include "../curl_trc.h" +#include "vauth/vauth.h" +#include "curl_trc.h" /* * Curl_auth_is_gssapi_supported() @@ -259,10 +259,6 @@ SecPkgContext_Sizes sizes; SECURITY_STATUS status; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - /* Ensure we have a valid challenge message */ DEBUGASSERT(chlg); if(!Curl_bufref_len(chlg)) { @@ -322,8 +318,8 @@ /* Process the maximum message size the server can receive */ if(max_size > 0) { - /* The server has told us it supports a maximum receive buffer, however, as - we do not require one unless we are encrypting data, we tell the server + /* The server has told us it supports a maximum receive buffer, but as we + do not require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } @@ -448,8 +444,8 @@ krb5->p_identity = NULL; /* Free the SPN and output token */ - Curl_safefree(krb5->spn); - Curl_safefree(krb5->output_token); + curlx_safefree(krb5->spn); + curlx_safefree(krb5->output_token); /* Reset any variables */ krb5->token_max = 0;
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index 5475573..1e485ed 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI) @@ -34,12 +34,15 @@ #define DEBUG_ME 0 -#include "vauth.h" -#include "../curl_trc.h" -#include "../curl_ntlm_core.h" -#include "../rand.h" -#include "../strdup.h" -#include "../curl_endian.h" +#include "vauth/vauth.h" +#include "curl_trc.h" +#include "curl_ntlm_core.h" +#include "rand.h" +#include "curlx/strdup.h" +#include "curl_endian.h" + +/* "NTLMSSP" signature is always in ASCII regardless of the platform */ +#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" /* NTLM buffer fixed size, large enough for long user + host + domain */ #define NTLM_BUFSIZE 1024 @@ -58,6 +61,7 @@ /* Requests that the server's authentication realm be included in the Type 2 message. */ +#if DEBUG_ME /* unknown (1 << 3) */ #define NTLMFLAG_NEGOTIATE_SIGN (1 << 4) /* Specifies that authenticated communication between the client and server @@ -73,10 +77,12 @@ #define NTLMFLAG_NEGOTIATE_LM_KEY (1 << 7) /* Indicates that the LAN Manager session key should be used for signing and sealing authenticated communications. */ +#endif #define NTLMFLAG_NEGOTIATE_NTLM_KEY (1 << 9) /* Indicates that NTLM authentication is being used. */ +#if DEBUG_ME /* unknown (1 << 10) */ #define NTLMFLAG_NEGOTIATE_ANONYMOUS (1 << 11) @@ -95,11 +101,13 @@ /* Sent by the server to indicate that the server and client are on the same machine. Implies that the client may use a pre-established local security context rather than responding to the challenge. */ +#endif #define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1 << 15) /* Indicates that authenticated communication between the client and server should be signed with a "dummy" signature. */ +#if DEBUG_ME #define NTLMFLAG_TARGET_TYPE_DOMAIN (1 << 16) /* Sent by the server in the Type 2 message to indicate that the target authentication realm is a domain. */ @@ -112,11 +120,13 @@ /* Sent by the server in the Type 2 message to indicate that the target authentication realm is a share. Presumably, this is for share-level authentication. Usage is unclear. */ +#endif #define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1 << 19) /* Indicates that the NTLM2 signing and sealing scheme should be used for protecting authenticated communications. */ +#if DEBUG_ME #define NTLMFLAG_REQUEST_INIT_RESPONSE (1 << 20) /* unknown purpose */ @@ -125,11 +135,13 @@ #define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1 << 22) /* unknown purpose */ +#endif #define NTLMFLAG_NEGOTIATE_TARGET_INFO (1 << 23) /* Sent by the server in the Type 2 message to indicate that it is including a Target Information block in the message. */ +#if DEBUG_ME /* unknown (1<24) */ /* unknown (1<25) */ /* unknown (1<26) */ @@ -140,16 +152,12 @@ /* Indicates that 128-bit encryption is supported. */ #define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1 << 30) -/* Indicates that the client will provide an encrypted master key in +/* Indicates that the client provides an encrypted master key in the "Session Key" field of the Type 3 message. */ #define NTLMFLAG_NEGOTIATE_56 (1 << 31) /* Indicates that 56-bit encryption is supported. */ -/* "NTLMSSP" signature is always in ASCII regardless of the platform */ -#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" - -#if DEBUG_ME #define DEBUG_OUT(x) x static void ntlm_print_flags(FILE *handle, unsigned long flags) { @@ -254,10 +262,6 @@ const unsigned char *type2 = Curl_bufref_uptr(type2ref); size_t type2len = Curl_bufref_len(type2ref); -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - if(type2len >= 48) { target_info_len = Curl_read16_le(&type2[40]); target_info_offset = Curl_read32_le(&type2[44]); @@ -271,8 +275,8 @@ } curlx_free(ntlm->target_info); /* replace any previous data */ - ntlm->target_info = Curl_memdup(&type2[target_info_offset], - target_info_len); + ntlm->target_info = curlx_memdup(&type2[target_info_offset], + target_info_len); if(!ntlm->target_info) return CURLE_OUT_OF_MEMORY; } @@ -354,10 +358,6 @@ const unsigned char *type2 = Curl_bufref_uptr(type2ref); size_t type2len = Curl_bufref_len(type2ref); -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - ntlm->flags = 0; if((type2len < 32) || @@ -398,7 +398,7 @@ size_t i; for(i = 0; i < length; i++) { dest[2 * i] = (unsigned char)src[i]; - dest[2 * i + 1] = '\0'; + dest[(2 * i) + 1] = '\0'; } } @@ -424,7 +424,7 @@ const char *userp, const char *passwdp, const char *service, - const char *hostname, + const char *host, struct ntlmdata *ntlm, struct bufref *out) { @@ -445,7 +445,7 @@ size_t size; char *ntlmbuf; - const char *host = ""; /* empty */ + const char *hostname = ""; /* empty */ const char *domain = ""; /* empty */ size_t hostlen = 0; size_t domlen = 0; @@ -456,7 +456,7 @@ (void)userp; (void)passwdp; (void)service; - (void)hostname; + (void)host; /* Clean up any former leftovers and initialise to defaults */ Curl_auth_cleanup_ntlm(ntlm); @@ -490,7 +490,7 @@ SHORTPAIR(hostlen), SHORTPAIR(hostoff), 0, 0, - host, /* this is empty */ + hostname, /* this is empty */ domain /* this is empty */); if(!ntlmbuf) @@ -573,7 +573,7 @@ unsigned int ntrespoff; unsigned int ntresplen = 24; unsigned char ntresp[24]; /* fixed-size */ - unsigned char *ptr_ntresp = &ntresp[0]; + const unsigned char *ptr_ntresp = &ntresp[0]; unsigned char *ntlmv2resp = NULL; bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE); /* The fixed hostname we provide, in order to not leak our real local host @@ -850,7 +850,7 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) { /* Free the target info */ - Curl_safefree(ntlm->target_info); + curlx_safefree(ntlm->target_info); /* Reset any variables */ ntlm->target_info_len = 0;
diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c index 3197dd0..4c41eb2 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c +++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
@@ -21,14 +21,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM) -#include "vauth.h" -#include "../curl_ntlm_core.h" -#include "../curl_trc.h" -#include "../strdup.h" +#include "vauth/vauth.h" +#include "curl_ntlm_core.h" +#include "curl_trc.h" +#include "curlx/strdup.h" /* * Curl_auth_is_ntlm_supported() @@ -191,25 +191,21 @@ * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, - const struct bufref *type2, + const struct bufref *type2ref, struct ntlmdata *ntlm) { -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - /* Ensure we have a valid type-2 message */ - if(!Curl_bufref_len(type2)) { + if(!Curl_bufref_len(type2ref)) { infof(data, "NTLM handshake failure (empty type-2 message)"); return CURLE_BAD_CONTENT_ENCODING; } /* Store the challenge for later use */ - ntlm->input_token = Curl_memdup0(Curl_bufref_ptr(type2), - Curl_bufref_len(type2)); + ntlm->input_token = curlx_memdup0(Curl_bufref_ptr(type2ref), + Curl_bufref_len(type2ref)); if(!ntlm->input_token) return CURLE_OUT_OF_MEMORY; - ntlm->input_token_len = Curl_bufref_len(type2); + ntlm->input_token_len = Curl_bufref_len(type2ref); return CURLE_OK; } @@ -244,9 +240,6 @@ SECURITY_STATUS status; unsigned long attrs; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif (void)passwdp; (void)userp; @@ -262,7 +255,7 @@ /* ssl context comes from schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS will not pass the authentication (401 response). + * otherwise IIS does not pass the authentication (401 response). * Minimum supported version is Windows 7. * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 */ @@ -348,13 +341,13 @@ ntlm->p_identity = NULL; /* Free the input and output tokens */ - Curl_safefree(ntlm->input_token); - Curl_safefree(ntlm->output_token); + curlx_safefree(ntlm->input_token); + curlx_safefree(ntlm->output_token); /* Reset any variables */ ntlm->token_max = 0; - Curl_safefree(ntlm->spn); + curlx_safefree(ntlm->spn); } #endif /* USE_WINDOWS_SSPI && USE_NTLM */
diff --git a/Utilities/cmcurl/lib/vauth/oauth2.c b/Utilities/cmcurl/lib/vauth/oauth2.c index a7a0613..4541d1d 100644 --- a/Utilities/cmcurl/lib/vauth/oauth2.c +++ b/Utilities/cmcurl/lib/vauth/oauth2.c
@@ -23,13 +23,13 @@ * RFC6749 OAuth 2.0 Authorization Framework * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_POP3) || \ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) -#include "vauth.h" +#include "vauth/vauth.h" /* * Curl_auth_create_oauth_bearer_message()
diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c index b1ec37c..38bb4c1 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
@@ -23,16 +23,16 @@ * RFC4178 Simple and Protected GSS-API Negotiation Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(HAVE_GSSAPI) && defined(USE_SPNEGO) -#include "vauth.h" -#include "../curlx/base64.h" -#include "../curl_gssapi.h" -#include "../curl_trc.h" +#include "vauth/vauth.h" +#include "curlx/base64.h" +#include "curl_gssapi.h" +#include "curl_trc.h" -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -171,7 +171,7 @@ NULL); /* Free the decoded challenge as it is not required anymore */ - Curl_safefree(input_token.value); + curlx_safefree(input_token.value); nego->status = major_status; if(GSS_ERROR(major_status)) { @@ -211,7 +211,7 @@ * data [in] - The session handle. * nego [in/out] - The Negotiate data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -288,7 +288,7 @@ nego->havemultiplerequests = FALSE; } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c index 4211763..1baf593 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
@@ -23,14 +23,14 @@ * RFC4178 Simple and Protected GSS-API Negotiation Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO) -#include "vauth.h" -#include "../curlx/base64.h" -#include "../curl_trc.h" -#include "../strerror.h" +#include "vauth/vauth.h" +#include "curlx/base64.h" +#include "curl_trc.h" +#include "strerror.h" /* * Curl_auth_is_spnego_supported() @@ -95,10 +95,6 @@ SecBufferDesc resp_desc; unsigned long attrs; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - if(nego->context && nego->status == SEC_E_OK) { /* We finished successfully our part of authentication, but server * rejected it (since we are again here). Exit with an error since we @@ -196,7 +192,7 @@ /* ssl context comes from Schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS will not pass the authentication (401 response). + * otherwise IIS does not pass the authentication (401 response). * Minimum supported version is Windows 7. * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 */ @@ -282,7 +278,7 @@ * data [in] - The session handle. * nego [in/out] - The Negotiate data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -333,8 +329,8 @@ nego->p_identity = NULL; /* Free the SPN and output token */ - Curl_safefree(nego->spn); - Curl_safefree(nego->output_token); + curlx_safefree(nego->spn); + curlx_safefree(nego->output_token); /* Reset any variables */ nego->status = 0;
diff --git a/Utilities/cmcurl/lib/vauth/vauth.c b/Utilities/cmcurl/lib/vauth/vauth.c index a74c2a1..a00078f 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.c +++ b/Utilities/cmcurl/lib/vauth/vauth.c
@@ -21,11 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "vauth.h" -#include "../curlx/multibyte.h" -#include "../url.h" +#include "vauth/vauth.h" +#include "curlx/multibyte.h" +#include "url.h" /* * Curl_auth_build_spn() @@ -117,7 +117,7 @@ if(user && *user) { /* Check we have a domain name or UPN present */ - char *p = strpbrk(user, "\\/@"); + const char *p = strpbrk(user, "\\/@"); valid = (p != NULL && p > user && p < user + strlen(user) - 1); } @@ -143,11 +143,10 @@ (data->state.first_host && curl_strequal(data->state.first_host, conn->host.name) && (data->state.first_remote_port == conn->remote_port) && - (data->state.first_remote_protocol == conn->handler->protocol)); + (data->state.first_remote_protocol == conn->scheme->protocol)); } #ifdef USE_NTLM - static void ntlm_conn_dtor(void *key, size_t klen, void *entry) { struct ntlmdata *ntlm = entry; @@ -175,11 +174,9 @@ Curl_conn_meta_remove(conn, proxy ? CURL_META_NTLM_PROXY_CONN : CURL_META_NTLM_CONN); } - #endif /* USE_NTLM */ #ifdef USE_KERBEROS5 - static void krb5_conn_dtor(void *key, size_t klen, void *entry) { struct kerberos5data *krb5 = entry; @@ -201,11 +198,9 @@ } return krb5; } - #endif /* USE_KERBEROS5 */ #ifdef USE_GSASL - static void gsasl_conn_dtor(void *key, size_t klen, void *entry) { struct gsasldata *gsasl = entry; @@ -227,11 +222,9 @@ } return gsasl; } - #endif /* USE_GSASL */ #ifdef USE_SPNEGO - static void nego_conn_dtor(void *key, size_t klen, void *entry) { struct negotiatedata *nego = entry; @@ -253,5 +246,4 @@ } return nego; } - #endif /* USE_SPNEGO */
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h index 312bd8d..279da60 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.h +++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -23,11 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "../bufref.h" -#include "../curlx/dynbuf.h" -#include "../urldata.h" +#include "bufref.h" +#include "curlx/dynbuf.h" +#include "urldata.h" struct Curl_easy; struct connectdata; @@ -49,7 +49,7 @@ #endif #ifdef USE_WINDOWS_SSPI -#include "../curl_sspi.h" +#include "curl_sspi.h" #define GSS_ERROR(status) ((status) & 0x80000000) #endif @@ -110,13 +110,14 @@ const char *userp, const char *passwdp, const unsigned char *request, - const unsigned char *uri, + const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen); /* This is used to clean up the digest specific data */ void Curl_auth_digest_cleanup(struct digestdata *digest); #else +#define Curl_auth_digest_cleanup(x) #define Curl_auth_is_digest_supported() FALSE #endif /* !CURL_DISABLE_DIGEST_AUTH */ @@ -150,7 +151,7 @@ struct bufref *out); /* This is used to clean up the gsasl specific data */ -void Curl_auth_gsasl_cleanup(struct gsasldata *digest); +void Curl_auth_gsasl_cleanup(struct gsasldata *gsasl); #endif #ifdef USE_NTLM @@ -205,7 +206,7 @@ /* This is used to decode a base64 encoded NTLM type-2 message */ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, - const struct bufref *type2, + const struct bufref *type2ref, struct ntlmdata *ntlm); /* This is used to generate a base64 encoded NTLM type-3 message */ @@ -263,7 +264,7 @@ const char *passwdp, const char *service, const char *host, - const bool mutual, + const bool mutual_auth, const struct bufref *chlg, struct kerberos5data *krb5, struct bufref *out);
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index f8db42b..a3e69ac 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c
@@ -46,23 +46,18 @@ #include <libpsl.h> #endif -#ifdef USE_LIBRTMP -#include <librtmp/rtmp.h> -#include "curl_rtmp.h" -#endif - #ifdef HAVE_LIBZ #include <cm3p/zlib.h> #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG /* Ignore -Wvla warnings in brotli headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvla" #endif #include <brotli/decode.h> -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif #endif @@ -178,9 +173,6 @@ #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) char h3_version[30]; #endif -#ifdef USE_LIBRTMP - char rtmp_version[30]; -#endif #ifdef USE_GSASL char gsasl_buf[30]; #endif @@ -244,10 +236,6 @@ Curl_quic_ver(h3_version, sizeof(h3_version)); src[i++] = h3_version; #endif -#ifdef USE_LIBRTMP - Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); - src[i++] = rtmp_version; -#endif #ifdef USE_GSASL curl_msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s", gsasl_check_version(NULL)); @@ -255,11 +243,9 @@ #endif #ifdef HAVE_GSSAPI #ifdef HAVE_GSSGNU - curl_msnprintf(gss_buf, sizeof(gss_buf), "libgss/%s", - GSS_VERSION); + curl_msnprintf(gss_buf, sizeof(gss_buf), "libgss/%s", GSS_VERSION); #elif defined(CURL_KRB5_VERSION) - curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5/%s", - CURL_KRB5_VERSION); + curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5/%s", CURL_KRB5_VERSION); #else curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5"); #endif @@ -341,20 +327,15 @@ #ifndef CURL_DISABLE_MQTT "mqtt", #endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_MQTT) + "mqtts", +#endif #ifndef CURL_DISABLE_POP3 "pop3", #endif #if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) "pop3s", #endif -#ifdef USE_LIBRTMP - "rtmp", - "rtmpe", - "rtmps", - "rtmpt", - "rtmpte", - "rtmpts", -#endif #ifndef CURL_DISABLE_RTSP "rtsp", #endif @@ -362,7 +343,7 @@ "scp", "sftp", #endif -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) "smb", # ifdef USE_SSL "smbs", @@ -448,8 +429,8 @@ #ifndef CURL_DISABLE_ALTSVC FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC), #endif -#if defined(USE_ARES) && defined(CURLRES_THREADED) && defined(USE_HTTPSRR) - FEATURE("asyn-rr", NULL, 0), +#if defined(USE_ARES) && defined(USE_RESOLV_THREADED) && defined(USE_HTTPSRR) + FEATURE("asyn-rr", NULL, 0), #endif #ifdef CURLRES_ASYNCH FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS), @@ -513,9 +494,13 @@ #ifdef USE_LIBPSL FEATURE("PSL", NULL, CURL_VERSION_PSL), #endif +#ifdef USE_SSL #ifdef USE_APPLE_SECTRUST FEATURE("AppleSecTrust", NULL, 0), +#elif defined(CURL_CA_NATIVE) + FEATURE("NativeCA", NULL, 0), #endif +#endif /* USE_SSL */ #ifdef USE_SPNEGO FEATURE("SPNEGO", NULL, CURL_VERSION_SPNEGO), #endif @@ -534,9 +519,6 @@ #ifdef USE_TLS_SRP FEATURE("TLS-SRP", NULL, CURL_VERSION_TLSAUTH_SRP), #endif -#ifdef CURLDEBUG - FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG), -#endif #if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE) FEATURE("Unicode", NULL, CURL_VERSION_UNICODE), #endif @@ -648,7 +630,7 @@ #endif #ifdef HAVE_ZSTD - version_info.zstd_ver_num = (unsigned int)ZSTD_versionNumber(); + version_info.zstd_ver_num = ZSTD_versionNumber(); zstd_version(zstd_buffer, sizeof(zstd_buffer)); version_info.zstd_version = zstd_buffer; #endif @@ -683,16 +665,12 @@ feature_names[n++] = p->name; } +#ifdef DEBUGBUILD + features |= CURL_VERSION_CURLDEBUG; /* for compatibility */ +#endif + feature_names[n] = NULL; /* Terminate array. */ version_info.features = features; -#ifdef USE_LIBRTMP - { - static char rtmp_version[30]; - Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); - version_info.rtmp_version = rtmp_version; - } -#endif - return &version_info; }
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c index 97ef98b..8cf3886 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) #include <ngtcp2/ngtcp2.h> @@ -36,37 +36,38 @@ #else #include <ngtcp2/ngtcp2_crypto_quictls.h> #endif -#include "../vtls/openssl.h" +#include "vtls/openssl.h" #elif defined(USE_GNUTLS) #include <ngtcp2/ngtcp2_crypto_gnutls.h> -#include "../vtls/gtls.h" +#include "vtls/gtls.h" #elif defined(USE_WOLFSSL) #include <ngtcp2/ngtcp2_crypto_wolfssl.h> -#include "../vtls/wolfssl.h" +#include "vtls/wolfssl.h" #endif -#include "../urldata.h" -#include "../url.h" -#include "../uint-hash.h" -#include "../curl_trc.h" -#include "../rand.h" -#include "../multiif.h" -#include "../cfilters.h" -#include "../cf-socket.h" -#include "../connect.h" -#include "../progress.h" -#include "../curlx/dynbuf.h" -#include "../http1.h" -#include "../select.h" -#include "../transfer.h" -#include "../bufref.h" -#include "vquic.h" -#include "vquic_int.h" -#include "vquic-tls.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" -#include "../vtls/vtls_scache.h" -#include "curl_ngtcp2.h" +#include "urldata.h" +#include "url.h" +#include "uint-hash.h" +#include "curl_trc.h" +#include "rand.h" +#include "multiif.h" +#include "cfilters.h" +#include "cf-dns.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "curlx/fopen.h" +#include "curlx/dynbuf.h" +#include "http1.h" +#include "select.h" +#include "transfer.h" +#include "bufref.h" +#include "vquic/vquic.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" +#include "vtls/vtls.h" +#include "vtls/vtls_scache.h" +#include "vquic/curl_ngtcp2.h" #define QUIC_MAX_STREAMS (256 * 1024) @@ -182,7 +183,7 @@ struct cf_ngtcp2_ctx *ctx = cf->ctx; const ngtcp2_transport_params *rp; /* Peer should have sent us its transport parameters. If it - * announces a positive `max_idle_timeout` it will close the + * announces a positive `max_idle_timeout` it closes the * connection when it does not hear from us for that time. * * Some servers use this as a keep-alive timer at a rather low @@ -231,7 +232,8 @@ size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ - uint64_t download_unacked; /* bytes not acknowledged yet */ + uint64_t rx_offset; /* current receive offset */ + uint64_t rx_offset_max; /* allowed receive offset */ uint64_t window_size_max; /* max flow control window set for stream */ int status_code; /* HTTP status code */ CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ @@ -273,6 +275,9 @@ return CURLE_OUT_OF_MEMORY; stream->id = -1; + stream->rx_offset = 0; + stream->rx_offset_max = H3_STREAM_WINDOW_SIZE_INITIAL; + /* on send, we control how much we put into the buffer */ Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); @@ -371,10 +376,6 @@ } } -/* ngtcp2 default congestion controller does not perform pacing. Limit - the maximum packet burst to MAX_PKT_BURST packets. */ -#define MAX_PKT_BURST 10 - struct pkt_io_ctx { struct Curl_cfilter *cf; struct Curl_easy *data; @@ -390,8 +391,8 @@ const struct curltime *pnow = Curl_pgrs_now(data); vquic_ctx_update_time(&ctx->q, pnow); - pktx->ts = (ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS + - (ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS; + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); } static void pktx_init(struct pkt_io_ctx *pktx, @@ -405,8 +406,8 @@ pktx->data = data; ngtcp2_path_storage_zero(&pktx->ps); vquic_ctx_set_time(&ctx->q, pnow); - pktx->ts = (ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS + - (ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS; + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); } static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, @@ -445,7 +446,7 @@ ssize_t rc = write(ctx->qlogfd, data, datalen); if(rc == -1) { /* on write error, stop further write attempts */ - close(ctx->qlogfd); + curlx_close(ctx->qlogfd); ctx->qlogfd = -1; } } @@ -511,7 +512,7 @@ ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { const ngtcp2_transport_params *rp; rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); @@ -610,21 +611,15 @@ { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; - nghttp3_ssize nconsumed; + nghttp3_ssize rc; + uint64_t nconsumed; int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)offset; - (void)data; - nconsumed = - nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); - if(!data) - data = CF_DATA_CURRENT(cf); - if(data) - CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", - stream_id, buflen, nconsumed); - if(nconsumed < 0) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + rc = nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); + if(rc < 0) { if(data && stream) { CURL_TRC_CF(data, cf, "[%" PRId64 "] error on known stream, " "reset=%d, closed=%d", @@ -632,13 +627,18 @@ } return NGTCP2_ERR_CALLBACK_FAILURE; } - - /* number of bytes inside buflen which consists of framing overhead - * including QPACK HEADERS. In other words, it does not consume payload of - * DATA frame. */ - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, (uint64_t)nconsumed); - ngtcp2_conn_extend_max_offset(tconn, (uint64_t)nconsumed); - + nconsumed = (uint64_t)rc; + if(nconsumed) { + /* number of bytes inside buflen which consists of framing overhead + * including QPACK HEADERS. In other words, it does not consume payload of + * DATA frame. */ + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); + ngtcp2_conn_extend_max_offset(tconn, nconsumed); + if(stream) { + stream->rx_offset += nconsumed; + stream->rx_offset_max += nconsumed; + } + } return 0; } @@ -705,7 +705,6 @@ (void)tconn; (void)final_size; (void)app_error_code; - (void)data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); @@ -785,11 +784,12 @@ result = Curl_rand(NULL, dest, destlen); if(result) { /* cb_rand is only used for non-cryptographic context. If Curl_rand - failed, just fill 0 and call it *random*. */ + failed, fill 0 and call it *random*. */ memset(dest, 0, destlen); } } +/* for ngtcp2 <v1.22.0 */ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, uint8_t *token, size_t cidlen, void *user_data) @@ -810,6 +810,27 @@ return 0; } +#ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ +static int cb_get_new_connection_id2(ngtcp2_conn *tconn, ngtcp2_cid *cid, + struct ngtcp2_stateless_reset_token *token, size_t cidlen, void *user_data) +{ + CURLcode result; + (void)tconn; + (void)user_data; + + result = Curl_rand(NULL, cid->data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token->data, sizeof(token->data)); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} +#endif + static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, void *user_data) { @@ -853,7 +874,7 @@ cb_extend_max_local_streams_bidi, NULL, /* extend_max_local_streams_uni */ cb_rand, - cb_get_new_connection_id, + cb_get_new_connection_id, /* for ngtcp2 <v1.22.0 */ NULL, /* remove_connection_id */ ngtcp2_crypto_update_key_cb, /* update_key */ NULL, /* path_validation */ @@ -879,6 +900,12 @@ #ifdef NGTCP2_CALLBACKS_V2 /* ngtcp2 v1.14.0+ */ NULL, /* begin_path_validation */ #endif +#ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ + NULL, /* recv_stateless_reset2 */ + cb_get_new_connection_id2, /* get_new_connection_id2 */ + NULL, /* dcid_status2 */ + ngtcp2_crypto_get_path_challenge_data2_cb, /* get_path_challenge_data2 */ +#endif }; #if defined(_MSC_VER) && defined(_DLL) @@ -1046,57 +1073,50 @@ } } -static void cf_ngtcp2_stream_update_window(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream) -{ - /* stream receive max window size for flow control. We can only - * grow it from the initial window size */ - uint64_t swin_max = data->progress.dl.rlimit.rate_per_step ? - data->progress.dl.rlimit.rate_per_step : H3_STREAM_WINDOW_SIZE_MAX; - if(swin_max > stream->window_size_max) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rc = ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, - swin_max - stream->window_size_max); - if(rc) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] extend_max_stream_offset to %" - PRIu64 " -> %s (%d)", - stream->id, swin_max, ngtcp2_strerror(rc), rc); - DEBUGASSERT(0); - } - stream->window_size_max = swin_max; - } -} - -static void cf_ngtcp2_ack_stream(struct Curl_cfilter *cf, +static void cf_ngtcp2_upd_rx_win(struct Curl_cfilter *cf, struct Curl_easy *data, struct h3_stream_ctx *stream) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - curl_off_t avail; - uint64_t ack_len = 0; - - /* How many byte to ack on the stream? */ + uint64_t cur_win, wanted_win = H3_STREAM_WINDOW_SIZE_MAX; /* how much does rate limiting allow us to acknowledge? */ - avail = Curl_rlimit_avail(&data->progress.dl.rlimit, - Curl_pgrs_now(data)); - if(avail == CURL_OFF_T_MAX) { /* no rate limit, ack all */ - ack_len = stream->download_unacked; - } - else if(avail > 0) { - ack_len = CURLMIN(stream->download_unacked, (uint64_t)avail); + if(Curl_rlimit_active(&data->progress.dl.rlimit)) { + int64_t avail; + + /* start rate limit updates only after first bytes arrived */ + if(!stream->rx_offset) + return; + + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, + Curl_pgrs_now(data)); + if(avail <= 0) { + /* nothing available, do not extend the rx offset */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 + " tokens)", stream->id, avail); + return; + } + wanted_win = CURLMIN((uint64_t)avail, H3_STREAM_WINDOW_SIZE_MAX); } - if(ack_len) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %" PRIu64 - "/%" PRIu64 " bytes of DATA", stream->id, - ack_len, stream->download_unacked); - ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, ack_len); - stream->download_unacked -= ack_len; + if(stream->rx_offset_max < stream->rx_offset) { + DEBUGASSERT(0); + return; } + cur_win = stream->rx_offset_max - stream->rx_offset; - cf_ngtcp2_stream_update_window(cf, data, stream); + if(wanted_win > cur_win) { + uint64_t delta = wanted_win - cur_win; + + if(UINT64_MAX - delta < stream->rx_offset_max) + delta = UINT64_MAX - stream->rx_offset_max; + if(delta) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] rx window, extend by %" PRIu64 + " bytes", stream->id, delta); + stream->rx_offset_max += delta; + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, delta); + } + } } static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, @@ -1115,15 +1135,15 @@ return NGHTTP3_ERR_CALLBACK_FAILURE; h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE); - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, blen); ngtcp2_conn_extend_max_offset(ctx->qconn, blen); - if(UINT64_MAX - blen < stream->download_unacked) - stream->download_unacked = UINT64_MAX; /* unlikely */ - else - stream->download_unacked += blen; + stream->rx_offset += blen; + if(stream->rx_offset_max < stream->rx_offset) + stream->rx_offset_max = stream->rx_offset; - cf_ngtcp2_ack_stream(cf, data, stream); + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64, + stream->id, blen, stream->rx_offset_max - stream->rx_offset); + cf_ngtcp2_upd_rx_win(cf, data, stream); return 0; } @@ -1133,13 +1153,18 @@ { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)conn; - (void)stream_user_data; /* nghttp3 has consumed bytes on the QUIC stream and we need to * tell the QUIC connection to increase its flow control */ ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + if(stream) { + stream->rx_offset += consumed; + stream->rx_offset_max += consumed; + } return 0; } @@ -1158,7 +1183,8 @@ if(!stream) return 0; /* add a CRLF only if we have received some headers */ - h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); + h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), + (bool)stream->closed); CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", stream_id, stream->status_code); @@ -1263,7 +1289,6 @@ struct Curl_easy *data = stream_user_data; int rv; (void)conn; - (void)data; rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, app_error_code); @@ -1372,7 +1397,23 @@ (void)cf; *pnread = 0; if(stream->reset) { - failf(data, "HTTP/3 stream %" PRId64 " reset by server", stream->id); + if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { + infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " + "on a new connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + data->state.refused_stream = TRUE; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ + } + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " + "but we did not want a body anyway, ignore error 0x%" + PRIx64 " %s", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + return CURLE_OK; + } + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; } else if(!stream->resp_hds_complete) { @@ -1394,9 +1435,11 @@ struct cf_call_data save; struct pkt_io_ctx pktx; CURLcode result = CURLE_OK; + int i; (void)ctx; (void)buf; + NOVERBOSE((void)blen); CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); @@ -1418,28 +1461,36 @@ goto out; } - cf_ngtcp2_ack_stream(cf, data, stream); + cf_ngtcp2_upd_rx_win(cf, data, stream); - if(cf_progress_ingress(cf, data, &pktx)) { - result = CURLE_RECV_ERROR; - goto out; + /* first check for results/closed already known without touching + * the connection. For an already failed/closed stream, errors on + * the connection do not count. + * Then handle incoming data and check for failed/closed again. + */ + for(i = 0; i < 2; ++i) { + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); + cf_ngtcp2_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + result = recv_closed_stream(cf, data, stream, pnread); + goto out; + } + + if(!i && cf_progress_ingress(cf, data, &pktx)) { + result = CURLE_RECV_ERROR; + goto out; + } } - if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); - result = stream->xfer_result; - goto out; - } - else if(stream->closed) { - result = recv_closed_stream(cf, data, stream, pnread); - goto out; - } result = CURLE_AGAIN; out: - result = Curl_1st_err(result, cf_progress_egress(cf, data, &pktx)); - result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); + result = Curl_1st_fatal(result, cf_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(blen=%zu) -> %d, %zu", stream ? stream->id : -1, blen, result, *pnread); @@ -1536,7 +1587,7 @@ } CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", + "%d vecs%s with %zd (buffered=%zu, left=%" FMT_OFF_T ")", stream->id, (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", nwritten, Curl_bufq_len(&stream->sendbuf), @@ -1580,7 +1631,6 @@ 0, pnwritten); if(result) goto out; - if(!stream->h1.done) { /* need more data */ goto out; @@ -1661,7 +1711,7 @@ goto out; } - cf_ngtcp2_stream_update_window(cf, data, stream); + cf_ngtcp2_upd_rx_win(cf, data, stream); if(Curl_trc_is_verbose(data)) { infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", @@ -1718,7 +1768,7 @@ CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); goto out; } - stream = H3_STREAM_CTX(ctx, data); + VERBOSE(stream = H3_STREAM_CTX(ctx, data)); } else if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); @@ -1766,7 +1816,7 @@ result = cf_progress_egress(cf, data, &pktx); out: - result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); + result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : -1, len, result, *pnwritten); @@ -1802,7 +1852,7 @@ CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", buflen, gso_size, ecn); ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, - (socklen_t)ctx->q.local_addrlen); + ctx->q.local_addrlen); ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, remote_addrlen); pi.ecn = (uint8_t)ecn; @@ -1996,7 +2046,7 @@ * This is called PMTUD (Path Maximum Transmission Unit Discovery). * Since a PMTUD might be rejected right on send, we do not want it * be followed by other packets of lesser size. Because those would - * also fail then. So, if we detect a PMTUD while buffering, we flush. + * also fail then. If we detect a PMTUD while buffering, we flush. */ max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); path_max_payload_size = @@ -2021,13 +2071,13 @@ ++pktcnt; if(pktcnt == 1) { /* first packet in buffer. This is either of a known, "good" - * payload size or it is a PMTUD. We will see. */ + * payload size or it is a PMTUD. We shall see. */ gsolen = nread; } else if(nread > gsolen || (gsolen > path_max_payload_size && nread != gsolen)) { - /* The just added packet is a PMTUD *or* the one(s) before the - * just added were PMTUD and the last one is smaller. + /* The added packet is a PMTUD *or* the one(s) before the + * added were PMTUD and the last one is smaller. * Flush the buffer before the last add. */ curlcode = vquic_send_tail_split(cf, data, &ctx->q, gsolen, nread, nread); @@ -2041,8 +2091,7 @@ pktcnt = 0; } else if(nread < gsolen) { - /* Reached MAX_PKT_BURST *or* - * the capacity of our buffer *or* + /* Reached capacity of our buffer *or* * last add was shorter than the previous ones, flush */ break; } @@ -2129,7 +2178,7 @@ if(!ctx->initialized) return; if(ctx->qlogfd != -1) { - close(ctx->qlogfd); + curlx_close(ctx->qlogfd); } ctx->qlogfd = -1; Curl_vquic_tls_cleanup(&ctx->tls); @@ -2195,8 +2244,8 @@ (uint8_t *)buffer, sizeof(buffer), &ctx->last_error, pktx.ts); CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - PRIu64 ") -> %d", ctx->last_error.type, - ctx->last_error.error_code, (int)nwritten); + PRIu64 ") -> %zd", ctx->last_error.type, + ctx->last_error.error_code, (ssize_t)nwritten); /* there are cases listed in ngtcp2 documentation where this call * may fail. Since we are doing a connection shutdown as graceful * as we can, such an error is ignored here. */ @@ -2236,7 +2285,7 @@ if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { /* Sent everything off. ngtcp2 seems to have no support for graceful - * shutdowns. So, we are done. */ + * shutdowns. We are done. */ CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); *done = TRUE; result = CURLE_OK; @@ -2319,6 +2368,7 @@ #ifdef USE_GNUTLS +#ifdef CURLVERBOSE static const char *gtls_hs_msg_name(int mtype) { switch(mtype) { @@ -2345,6 +2395,7 @@ } return "Unknown"; } +#endif static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, unsigned when, unsigned int incoming, @@ -2361,7 +2412,7 @@ DEBUGASSERT(data); if(!data) return 0; - CURL_TRC_CF(data, cf, "SSL message: %s %s [%d]", + CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]", incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); switch(htype) { case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { @@ -2508,7 +2559,7 @@ #endif #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) - if((!ctx->earlydata_max)) { + if(!ctx->earlydata_max) { CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); } else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { @@ -2544,6 +2595,18 @@ return result; } +static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data) +{ +#ifdef USE_OPENSSL + return Curl_ossl_need_httpsrr(data); +#elif defined(USE_WOLFSSL) + return Curl_wssl_need_httpsrr(data); +#else + (void)data; + return FALSE; +#endif +} + /* * Might be called twice for happy eyeballs. */ @@ -2628,7 +2691,7 @@ #elif defined(USE_WOLFSSL) ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl); #else - #error "ngtcp2 TLS backend not defined" +#error "ngtcp2 TLS backend not defined" #endif ngtcp2_ccerr_default(&ctx->last_error); @@ -2658,8 +2721,14 @@ } *done = FALSE; - pktx_init(&pktx, cf, data); + if(cf_ngtcp2_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } + + pktx_init(&pktx, cf, data); CF_DATA_SAVE(save, cf, data); if(!ctx->qconn) { @@ -2710,7 +2779,7 @@ default: if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", - (unsigned int)(cerr->error_code & 0xffu)); + (unsigned int)(cerr->error_code & 0xffU)); } else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { CURL_TRC_CF(data, cf, "connection refused by server"); @@ -2723,7 +2792,7 @@ } } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(result) { struct ip_quadruple ip; @@ -2836,7 +2905,7 @@ goto out; /* We do not announce a max idle timeout, but when the peer does - * it will close the connection when it expires. */ + * it closes the connection when it expires. */ rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); if(rp && rp->max_idle_timeout) { timediff_t idletime_ms = @@ -2890,13 +2959,12 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai) + struct Curl_sockaddr_ex *addr) { struct cf_ngtcp2_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; - (void)data; ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; @@ -2909,7 +2977,7 @@ goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn;
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h index 1e9868e..185272a 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) @@ -46,14 +46,14 @@ struct Curl_cfilter; -#include "../urldata.h" +#include "urldata.h" void Curl_ngtcp2_ver(char *p, size_t len); CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai); + struct Curl_sockaddr_ex *addr); #endif #endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_osslq.c b/Utilities/cmcurl/lib/vquic/curl_osslq.c deleted file mode 100644 index 4648797..0000000 --- a/Utilities/cmcurl/lib/vquic/curl_osslq.c +++ /dev/null
@@ -1,2458 +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" - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_OPENSSL_QUIC) && \ - defined(USE_NGHTTP3) - -#include <openssl/ssl.h> -#include <openssl/bio.h> -#include <openssl/err.h> -#include <nghttp3/nghttp3.h> - -#include "../urldata.h" -#include "../curl_trc.h" -#include "../multiif.h" -#include "../cfilters.h" -#include "../cf-socket.h" -#include "../connect.h" -#include "../progress.h" -#include "../curlx/dynbuf.h" -#include "../http1.h" -#include "../select.h" -#include "../uint-hash.h" -#include "vquic.h" -#include "vquic_int.h" -#include "vquic-tls.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" -#include "../vtls/openssl.h" -#include "curl_osslq.h" -#include "../url.h" -#include "../bufref.h" -#include "../curlx/strerr.h" -#include "../curlx/strcopy.h" - -/* A stream window is the maximum amount we need to buffer for - * each active transfer. We use HTTP/3 flow control and only ACK - * when we take things out of the buffer. - * Chunk size is large enough to take a full DATA frame */ -#define H3_STREAM_WINDOW_SIZE (128 * 1024) -#define H3_STREAM_CHUNK_SIZE (16 * 1024) -/* The pool keeps spares around and half of a full stream window - * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffer to not keep - * spares. Memory consumption goes down when streams run empty, - * have a large upload done, etc. */ -#define H3_STREAM_POOL_SPARES \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) / 2 -/* Receive and Send max number of chunks just follows from the - * chunk size and window size */ -#define H3_STREAM_RECV_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) -#define H3_STREAM_SEND_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) - -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) -typedef uint32_t sslerr_t; -#else -typedef unsigned long sslerr_t; -#endif - - -/* How to access `call_data` from a cf_osslq filter */ -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) ((struct cf_osslq_ctx *)(cf)->ctx)->call_data - -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static const char *osslq_SSL_ERROR_to_str(int err) -{ - switch(err) { - case SSL_ERROR_NONE: - return "SSL_ERROR_NONE"; - case SSL_ERROR_SSL: - return "SSL_ERROR_SSL"; - case SSL_ERROR_WANT_READ: - return "SSL_ERROR_WANT_READ"; - case SSL_ERROR_WANT_WRITE: - return "SSL_ERROR_WANT_WRITE"; - case SSL_ERROR_WANT_X509_LOOKUP: - return "SSL_ERROR_WANT_X509_LOOKUP"; - case SSL_ERROR_SYSCALL: - return "SSL_ERROR_SYSCALL"; - case SSL_ERROR_ZERO_RETURN: - return "SSL_ERROR_ZERO_RETURN"; - case SSL_ERROR_WANT_CONNECT: - return "SSL_ERROR_WANT_CONNECT"; - case SSL_ERROR_WANT_ACCEPT: - return "SSL_ERROR_WANT_ACCEPT"; -#ifdef SSL_ERROR_WANT_ASYNC /* OpenSSL 1.1.0+, LibreSSL 3.6.0+ */ - case SSL_ERROR_WANT_ASYNC: - return "SSL_ERROR_WANT_ASYNC"; -#endif -#ifdef SSL_ERROR_WANT_ASYNC_JOB /* OpenSSL 1.1.0+, LibreSSL 3.6.0+ */ - case SSL_ERROR_WANT_ASYNC_JOB: - return "SSL_ERROR_WANT_ASYNC_JOB"; -#endif -#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB /* OpenSSL 1.1.1, LibreSSL 3.6.0+ */ - case SSL_ERROR_WANT_CLIENT_HELLO_CB: - return "SSL_ERROR_WANT_CLIENT_HELLO_CB"; -#endif - default: - return "SSL_ERROR unknown"; - } -} - -/* Return error string for last OpenSSL error */ -static char *osslq_strerror(unsigned long error, char *buf, size_t size) -{ - DEBUGASSERT(size); - *buf = '\0'; - -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) - ERR_error_string_n((uint32_t)error, buf, size); -#else - ERR_error_string_n(error, buf, size); -#endif - - if(!*buf) { - const char *msg = error ? "Unknown error" : "No error"; - if(strlen(msg) < size) - curlx_strcopy(buf, size, msg, strlen(msg)); - } - - return buf; -} - -static CURLcode make_bio_addr(BIO_ADDR **pbio_addr, - const struct Curl_sockaddr_ex *addr) -{ - BIO_ADDR *bio_addr; - CURLcode result = CURLE_FAILED_INIT; - - bio_addr = BIO_ADDR_new(); - if(!bio_addr) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - switch(addr->family) { - case AF_INET: { - struct sockaddr_in * const sin = - (struct sockaddr_in * const)CURL_UNCONST(&addr->curl_sa_addr); - if(!BIO_ADDR_rawmake(bio_addr, AF_INET, &sin->sin_addr, - sizeof(sin->sin_addr), sin->sin_port)) { - goto out; - } - result = CURLE_OK; - break; - } -#ifdef USE_IPV6 - case AF_INET6: { - struct sockaddr_in6 * const sin = - (struct sockaddr_in6 * const)CURL_UNCONST(&addr->curl_sa_addr); - if(!BIO_ADDR_rawmake(bio_addr, AF_INET6, &sin->sin6_addr, - sizeof(sin->sin6_addr), sin->sin6_port)) { - goto out; - } - result = CURLE_OK; - break; - } -#endif /* USE_IPV6 */ - default: - /* sunsupported */ - DEBUGASSERT(0); - break; - } - -out: - if(result && bio_addr) { - BIO_ADDR_free(bio_addr); - bio_addr = NULL; - } - *pbio_addr = bio_addr; - return result; -} - -/* QUIC stream (not necessarily H3) */ -struct cf_osslq_stream { - int64_t id; - SSL *ssl; - struct bufq recvbuf; /* QUIC war data recv buffer */ - BIT(recvd_eos); - BIT(closed); - BIT(reset); - BIT(send_blocked); -}; - -static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s, - SSL *conn, - uint64_t flags, - struct bufc_pool *bufcp, - void *user_data) -{ - DEBUGASSERT(!s->ssl); - Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE); - s->ssl = SSL_new_stream(conn, flags); - if(!s->ssl) { - return CURLE_FAILED_INIT; - } - s->id = SSL_get_stream_id(s->ssl); - SSL_set_app_data(s->ssl, user_data); - return CURLE_OK; -} - -static void cf_osslq_stream_cleanup(struct cf_osslq_stream *s) -{ - if(s->ssl) { - SSL_set_app_data(s->ssl, NULL); - SSL_free(s->ssl); - } - Curl_bufq_free(&s->recvbuf); - memset(s, 0, sizeof(*s)); -} - -static void cf_osslq_stream_close(struct cf_osslq_stream *s) -{ - if(s->ssl) { - SSL_free(s->ssl); - s->ssl = NULL; - } -} - -struct cf_osslq_h3conn { - nghttp3_conn *conn; - nghttp3_settings settings; - struct cf_osslq_stream s_ctrl; - struct cf_osslq_stream s_qpack_enc; - struct cf_osslq_stream s_qpack_dec; - struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */ - size_t remote_ctrl_n; /* number of peer streams opened */ -}; - -static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3) -{ - size_t i; - - if(h3->conn) - nghttp3_conn_del(h3->conn); - cf_osslq_stream_cleanup(&h3->s_ctrl); - cf_osslq_stream_cleanup(&h3->s_qpack_enc); - cf_osslq_stream_cleanup(&h3->s_qpack_dec); - for(i = 0; i < h3->remote_ctrl_n; ++i) { - cf_osslq_stream_cleanup(&h3->remote_ctrl[i]); - } -} - -struct cf_osslq_ctx { - struct cf_quic_ctx q; - struct ssl_peer peer; - struct curl_tls_ctx tls; - struct cf_call_data call_data; - struct cf_osslq_h3conn h3; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct curltime first_byte_at; /* when first byte was recvd */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct uint_hash streams; /* hash `data->mid` to `h3_stream_ctx` */ - size_t max_stream_window; /* max flow window for one stream */ - SSL_POLL_ITEM *poll_items; /* Array for polling on writable state */ - struct Curl_easy **curl_items; /* Array of easy objs */ - size_t items_max; /* max elements in poll/curl_items */ - BIT(initialized); - BIT(got_first_byte); /* if first byte was received */ - BIT(x509_store_setup); /* if x509 store has been set up */ - BIT(protocol_shutdown); /* QUIC connection is shut down */ - BIT(need_recv); /* QUIC connection needs to receive */ - BIT(need_send); /* QUIC connection needs to send */ -}; - -static void h3_stream_hash_free(unsigned int id, void *stream); - -static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx) -{ - DEBUGASSERT(!ctx->initialized); - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); - ctx->poll_items = NULL; - ctx->curl_items = NULL; - ctx->items_max = 0; - ctx->initialized = TRUE; -} - -static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx) -{ - if(ctx && ctx->initialized) { - Curl_bufcp_free(&ctx->stream_bufcp); - Curl_uint32_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - curlx_free(ctx->poll_items); - curlx_free(ctx->curl_items); - } - curlx_free(ctx); -} - -static void cf_osslq_ctx_close(struct cf_osslq_ctx *ctx) -{ - struct cf_call_data save = ctx->call_data; - - cf_osslq_h3conn_cleanup(&ctx->h3); - Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); - ctx->call_data = save; -} - -static CURLcode cf_osslq_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct cf_call_data save; - CURLcode result = CURLE_OK; - int rc; - - CF_DATA_SAVE(save, cf, data); - - if(cf->shutdown || ctx->protocol_shutdown) { - *done = TRUE; - return CURLE_OK; - } - - CF_DATA_SAVE(save, cf, data); - *done = FALSE; - ctx->need_send = FALSE; - ctx->need_recv = FALSE; - - rc = SSL_shutdown_ex(ctx->tls.ossl.ssl, - SSL_SHUTDOWN_FLAG_NO_BLOCK, NULL, 0); - if(rc == 0) { /* ongoing */ - CURL_TRC_CF(data, cf, "shutdown ongoing"); - ctx->need_recv = TRUE; - goto out; - } - else if(rc == 1) { /* done */ - CURL_TRC_CF(data, cf, "shutdown finished"); - *done = TRUE; - goto out; - } - else { - long sslerr; - char err_buffer[256]; - int err = SSL_get_error(ctx->tls.ossl.ssl, rc); - - switch(err) { - case SSL_ERROR_NONE: - case SSL_ERROR_ZERO_RETURN: - CURL_TRC_CF(data, cf, "shutdown not received, but closed"); - *done = TRUE; - goto out; - case SSL_ERROR_WANT_READ: - /* SSL has send its notify and now wants to read the reply - * from the server. We are not really interested in that. */ - CURL_TRC_CF(data, cf, "shutdown sent, want receive"); - ctx->need_recv = TRUE; - goto out; - case SSL_ERROR_WANT_WRITE: - CURL_TRC_CF(data, cf, "shutdown send blocked"); - ctx->need_send = TRUE; - goto out; - default: - /* We give up on this. */ - sslerr = ERR_get_error(); - CURL_TRC_CF(data, cf, "shutdown, ignore recv error: '%s', errno %d", - (sslerr ? - osslq_strerror(sslerr, err_buffer, sizeof(err_buffer)) : - osslq_SSL_ERROR_to_str(err)), - SOCKERRNO); - *done = TRUE; - result = CURLE_OK; - goto out; - } - } -out: - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->tls.ossl.ssl) { - CURL_TRC_CF(data, cf, "cf_osslq_close()"); - if(!cf->shutdown && !ctx->protocol_shutdown) { - /* last best effort, which OpenSSL calls a "rapid" shutdown. */ - SSL_shutdown_ex(ctx->tls.ossl.ssl, - (SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID), - NULL, 0); - } - cf_osslq_ctx_close(ctx); - } - - cf->connected = FALSE; - CF_DATA_RESTORE(cf, save); -} - -static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - CURL_TRC_CF(data, cf, "destroy"); - if(ctx) { - CURL_TRC_CF(data, cf, "cf_osslq_destroy()"); - if(ctx->tls.ossl.ssl) - cf_osslq_ctx_close(ctx); - cf_osslq_ctx_free(ctx); - } - cf->ctx = NULL; - /* No CF_DATA_RESTORE(cf, save) possible */ - (void)save; -} - -static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, - SSL *stream_ssl, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - int64_t stream_id = (int64_t)SSL_get_stream_id(stream_ssl); - int stype = SSL_get_stream_type(stream_ssl); - /* This could be a GREASE stream, e.g. HTTP/3 rfc9114 ch 6.2.3 - * reserved stream type that is supposed to be discarded silently. - * BUT OpenSSL does not offer this information to us. So, we silently - * ignore all such streams we do not expect. */ - switch(stype) { - case SSL_STREAM_TYPE_READ: { - struct cf_osslq_stream *nstream; - if(h3->remote_ctrl_n >= CURL_ARRAYSIZE(h3->remote_ctrl)) { - /* rejected, we are full */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] reject remote uni stream", - stream_id); - SSL_free(stream_ssl); - return CURLE_OK; - } - nstream = &h3->remote_ctrl[h3->remote_ctrl_n++]; - nstream->id = stream_id; - nstream->ssl = stream_ssl; - Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE); - CURL_TRC_CF(data, cf, "[%" PRId64 "] accepted remote uni stream", - stream_id); - return CURLE_OK; - } - default: - CURL_TRC_CF(data, cf, "[%" PRId64 "] reject remote %s" - " stream, type=%x", stream_id, - (stype == SSL_STREAM_TYPE_BIDI) ? "bidi" : "write", stype); - SSL_free(stream_ssl); - return CURLE_OK; - } -} - -static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, - struct Curl_easy *data, - int detail, CURLcode def_result) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = def_result; - sslerr_t errdetail; - char ebuf[256] = "unknown"; - const char *err_descr = ebuf; - long lerr; - int lib; - int reason; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - - errdetail = ERR_get_error(); - lib = ERR_GET_LIB(errdetail); - reason = ERR_GET_REASON(errdetail); - - if((lib == ERR_LIB_SSL) && - ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || - (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { - result = CURLE_PEER_FAILED_VERIFICATION; - - lerr = SSL_get_verify_result(ctx->tls.ossl.ssl); - if(lerr != X509_V_OK) { - ssl_config->certverifyresult = lerr; - curl_msnprintf(ebuf, sizeof(ebuf), "SSL certificate problem: %s", - X509_verify_cert_error_string(lerr)); - } - else - err_descr = "SSL certificate verification failed"; - } -#ifdef 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) && - (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { - /* If client certificate is required, communicate the - error to client */ - result = CURLE_SSL_CLIENTCERT; - osslq_strerror(errdetail, ebuf, sizeof(ebuf)); - } -#endif - else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) { - ctx->protocol_shutdown = TRUE; - err_descr = "QUIC connection has been shut down"; - result = def_result; - } - else { - result = def_result; - osslq_strerror(errdetail, ebuf, sizeof(ebuf)); - } - - /* detail is already set to the SSL error above */ - - /* If we e.g. use SSLv2 request-method and the server does not like us - * (RST connection, etc.), OpenSSL gives no explanation whatsoever and - * the SO_ERROR is also lost. - */ - if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { - char extramsg[80] = ""; - int sockerr = SOCKERRNO; - struct ip_quadruple ip; - - if(sockerr && detail == SSL_ERROR_SYSCALL) - curlx_strerror(sockerr, extramsg, sizeof(extramsg)); - if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) - failf(data, "QUIC connect: %s in connection to %s:%d (%s)", - extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail), - ctx->peer.dispname, ip.remote_port, ip.remote_ip); - } - else { - /* Could be a CERT problem */ - failf(data, "%s", err_descr); - } - return result; -} - -static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); -} - -/** - * All about the H3 internals of a stream - */ -struct h3_stream_ctx { - struct cf_osslq_stream s; - 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 recv_buf_nonflow; /* buffered bytes, - not counting for flow control */ - uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ - curl_off_t download_recvd; /* number of response DATA bytes received */ - int status_code; /* HTTP status code */ - BIT(resp_hds_complete); /* we have a complete, final response */ - BIT(closed); /* TRUE on stream close */ - BIT(reset); /* TRUE on stream reset */ - BIT(send_closed); /* stream is local closed */ - BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ -}; - -static void h3_stream_ctx_free(struct h3_stream_ctx *stream) -{ - cf_osslq_stream_cleanup(&stream->s); - Curl_bufq_free(&stream->sendbuf); - Curl_bufq_free(&stream->recvbuf); - Curl_h1_req_parse_free(&stream->h1); - curlx_free(stream); -} - -static void h3_stream_hash_free(unsigned int id, void *stream) -{ - (void)id; - DEBUGASSERT(stream); - h3_stream_ctx_free((struct h3_stream_ctx *)stream); -} - -static CURLcode h3_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - if(!data) - return CURLE_FAILED_INIT; - - if(stream) - return CURLE_OK; - - stream = curlx_calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - stream->s.id = -1; - /* on send, we control how much we put into the buffer */ - Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, - H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - stream->sendbuf_len_in_flight = 0; - /* on recv, we need a flexible buffer limit since we also write - * headers to it that are not counted against the nghttp3 flow limits. */ - 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); - - if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { - h3_stream_ctx_free(stream); - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - (void)cf; - if(stream) { - CURL_TRC_CF(data, cf, "[%" PRIu64 "] easy handle is done", stream->s.id); - if(ctx->h3.conn && (stream->s.id >= 0) && !stream->closed) { - nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id); - nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id, - NGHTTP3_H3_REQUEST_CANCELLED); - nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL); - stream->closed = TRUE; - } - - Curl_uint32_hash_remove(&ctx->streams, data->mid); - } -} - -struct cf_ossq_find_ctx { - int64_t stream_id; - struct h3_stream_ctx *stream; -}; - -static bool cf_osslq_find_stream(uint32_t mid, void *val, void *user_data) -{ - struct h3_stream_ctx *stream = val; - struct cf_ossq_find_ctx *fctx = user_data; - - (void)mid; - if(stream && stream->s.id == fctx->stream_id) { - fctx->stream = stream; - return FALSE; /* stop iterating */ - } - return TRUE; -} - -static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, - struct Curl_easy *data, - int64_t stream_id) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - if(stream && stream->s.id == stream_id) { - return &stream->s; - } - else if(ctx->h3.s_ctrl.id == stream_id) { - return &ctx->h3.s_ctrl; - } - else if(ctx->h3.s_qpack_enc.id == stream_id) { - return &ctx->h3.s_qpack_enc; - } - else if(ctx->h3.s_qpack_dec.id == stream_id) { - return &ctx->h3.s_qpack_dec; - } - else { - struct cf_ossq_find_ctx fctx; - fctx.stream_id = stream_id; - fctx.stream = NULL; - Curl_uint32_hash_visit(&ctx->streams, cf_osslq_find_stream, &fctx); - if(fctx.stream) - return &fctx.stream->s; - } - return NULL; -} - -static CURLcode h3_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) -{ - (void)cf; - if(!pause) { - /* unpaused. make it run again right away */ - Curl_multi_mark_dirty(data); - } - return CURLE_OK; -} - -static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)conn; - (void)stream_id; - - /* we might be called by nghttp3 after we already cleaned up */ - if(!stream) - return 0; - - stream->closed = TRUE; - stream->error3 = app_error_code; - if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - stream->reset = TRUE; - stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, - stream->s.id, stream->error3); - } - else { - CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id); - } - Curl_multi_mark_dirty(data); - return 0; -} - -/* - * write_resp_raw() copies response data in raw format to the `data`'s - * receive buffer. If not enough space is available, it appends to the - * `data`'s overflow buffer. - */ -static CURLcode write_resp_raw(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, size_t memlen, - bool flow) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURLcode result = CURLE_OK; - size_t nwritten; - - (void)cf; - if(!stream) { - return CURLE_RECV_ERROR; - } - result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten); - if(result) - return result; - - if(!flow) - stream->recv_buf_nonflow += nwritten; - - if(nwritten < memlen) { - /* This MUST not happen. Our recbuf is dimensioned to hold the - * full max_stream_window and then some for this reason. */ - DEBUGASSERT(0); - return CURLE_RECV_ERROR; - } - return result; -} - -static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(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 %d", - stream->s.id, buflen, result); - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - stream->download_recvd += (curl_off_t)buflen; - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, total=%" FMT_OFF_T, - stream->s.id, buflen, stream->download_recvd); - Curl_multi_mark_dirty(data); - return 0; -} - -static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, - size_t consumed, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - (void)conn; - (void)stream_id; - if(stream) - CURL_TRC_CF(data, cf, "[%" PRId64 "] deferred consume %zu bytes", - stream->s.id, consumed); - return 0; -} - -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, - int32_t token, nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); - nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)token; - (void)flags; - (void)cf; - - /* we might have cleaned up this transfer already */ - if(!stream) - return 0; - - if(token == NGHTTP3_QPACK_TOKEN__STATUS) { - char line[14]; /* status line is always 13 characters long */ - size_t ncopy; - - result = Curl_http_decode_status(&stream->status_code, - (const char *)h3val.base, h3val.len); - if(result) - return -1; - ncopy = curl_msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", - stream->status_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line); - result = write_resp_raw(cf, data, line, ncopy, FALSE); - if(result) { - return -1; - } - } - else { - /* store as an HTTP1-style header */ - 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; - } - result = write_resp_raw(cf, data, ": ", 2, FALSE); - if(result) { - return -1; - } - result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE); - if(result) { - return -1; - } - result = write_resp_raw(cf, data, "\r\n", 2, FALSE); - if(result) { - return -1; - } - } - return 0; -} - -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, - int fin, void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)fin; - (void)cf; - - if(!stream) - return 0; - /* add a CRLF only if we have received some headers */ - result = write_resp_raw(cf, data, "\r\n", 2, FALSE); - if(result) { - return -1; - } - - CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", - stream_id, stream->status_code); - if(stream->status_code / 100 != 1) { - stream->resp_hds_complete = TRUE; - } - Curl_multi_mark_dirty(data); - return 0; -} - -static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)conn; - (void)app_error_code; - - if(!stream || !stream->s.ssl) - return 0; - - CURL_TRC_CF(data, cf, "[%" PRId64 "] stop_sending", stream_id); - cf_osslq_stream_close(&stream->s); - return 0; -} - -static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - int rv; - (void)conn; - - if(stream && stream->s.ssl) { - SSL_STREAM_RESET_ARGS args = { 0 }; - args.quic_error_code = app_error_code; - rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args)); - CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(!rv) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static nghttp3_ssize cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, - nghttp3_vec *vec, size_t veccnt, - uint32_t *pflags, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nwritten = 0; - size_t nvecs = 0; - (void)cf; - (void)conn; - (void)stream_id; - (void)user_data; - (void)veccnt; - - if(!stream) - return NGHTTP3_ERR_CALLBACK_FAILURE; - /* nghttp3 keeps references to the sendbuf data until it is ACKed - * by the server (see `cb_h3_acked_req_body()` for updates). - * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` - * that we have already passed to nghttp3, but which have not been - * ACKed yet. - * Any amount beyond `sendbuf_len_in_flight` we need still to pass - * to nghttp3. Do that now, if we can. */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { - nvecs = 0; - while(nvecs < veccnt && - Curl_bufq_peek_at(&stream->sendbuf, - stream->sendbuf_len_in_flight, - CURL_UNCONST(&vec[nvecs].base), - &vec[nvecs].len)) { - stream->sendbuf_len_in_flight += vec[nvecs].len; - nwritten += vec[nvecs].len; - ++nvecs; - } - DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ - } - - if(nwritten > 0 && stream->upload_left != -1) - stream->upload_left -= nwritten; - - /* When we stopped sending and everything in `sendbuf` is "in flight", - * we are at the end of the request body. */ - if(stream->upload_left == 0) { - *pflags = NGHTTP3_DATA_FLAG_EOF; - stream->send_closed = TRUE; - } - else if(!nwritten) { - /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", - stream->s.id); - return NGHTTP3_ERR_WOULDBLOCK; - } - - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", - stream->s.id, (int)nvecs, - *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", - nwritten, Curl_bufq_len(&stream->sendbuf), - stream->upload_left); - return (nghttp3_ssize)nvecs; -} - -static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - size_t skiplen; - - (void)cf; - if(!stream) - return 0; - /* The server acknowledged `datalen` of bytes from our request body. - * This is a delta. We have kept this data in `sendbuf` for - * re-transmissions and can free it now. */ - if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) - skiplen = stream->sendbuf_len_in_flight; - else - skiplen = (size_t)datalen; - Curl_bufq_skip(&stream->sendbuf, skiplen); - stream->sendbuf_len_in_flight -= skiplen; - - /* Resume upload processing if we have more data to send */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { - int rv = nghttp3_conn_resume_stream(conn, stream_id); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static nghttp3_callbacks ngh3_callbacks = { - cb_h3_acked_stream_data, - cb_h3_stream_close, - cb_h3_recv_data, - cb_h3_deferred_consume, - NULL, /* begin_headers */ - cb_h3_recv_header, - cb_h3_end_headers, - NULL, /* begin_trailers */ - cb_h3_recv_header, - NULL, /* end_trailers */ - cb_h3_stop_sending, - NULL, /* end_stream */ - cb_h3_reset_stream, - NULL, /* shutdown */ - NULL, /* recv_settings (deprecated) */ -#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ - NULL, /* recv_origin */ - NULL, /* end_origin */ - NULL, /* rand */ -#endif -#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ - NULL, /* recv_settings2 */ -#endif -}; - -static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn, - void *user_data) -{ - struct cf_osslq_h3conn *h3 = &ctx->h3; - CURLcode result; - int rc; - - nghttp3_settings_default(&h3->settings); - rc = nghttp3_conn_client_new(&h3->conn, - &ngh3_callbacks, - &h3->settings, - Curl_nghttp3_mem(), - user_data); - if(rc) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - result = cf_osslq_stream_open(&h3->s_ctrl, conn, - SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI, - &ctx->stream_bufcp, NULL); - if(result) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - result = cf_osslq_stream_open(&h3->s_qpack_enc, conn, - SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI, - &ctx->stream_bufcp, NULL); - if(result) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - result = cf_osslq_stream_open(&h3->s_qpack_dec, conn, - SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI, - &ctx->stream_bufcp, NULL); - if(result) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - - rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id, - h3->s_qpack_dec.id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - - result = CURLE_OK; -out: - return result; -} - -static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result; - int rv; - const struct Curl_sockaddr_ex *peer_addr = NULL; - BIO *bio = NULL; - BIO_ADDR *baddr = NULL; - static const struct alpn_spec ALPN_SPEC_H3 = {{ "h3" }, 1}; - - DEBUGASSERT(ctx->initialized); - -#define H3_ALPN "\x2h3" - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, - &ALPN_SPEC_H3, NULL, NULL, NULL, NULL); - if(result) - goto out; - - result = vquic_ctx_init(data, &ctx->q); - if(result) - goto out; - - result = CURLE_QUIC_CONNECT_ERROR; - if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL) || - !peer_addr) - goto out; - - ctx->q.local_addrlen = sizeof(ctx->q.local_addr); - rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); - if(rv == -1) - goto out; - - result = make_bio_addr(&baddr, peer_addr); - if(result) { - failf(data, "error creating BIO_ADDR from sockaddr"); - goto out; - } - - /* Type conversions, see #12861: OpenSSL wants an `int`, but on 64-bit - * Win32 systems, Microsoft defines SOCKET as `unsigned long long`. - */ -#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) - if(ctx->q.sockfd > INT_MAX) { - failf(data, "Windows socket identifier larger than MAX_INT, " - "unable to set in OpenSSL dgram API."); - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - bio = BIO_new_dgram((int)ctx->q.sockfd, BIO_NOCLOSE); -#else - bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE); -#endif - if(!bio) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - if(!SSL_set1_initial_peer_addr(ctx->tls.ossl.ssl, baddr)) { - failf(data, "failed to set the initial peer address"); - result = CURLE_FAILED_INIT; - goto out; - } - if(!SSL_set_blocking_mode(ctx->tls.ossl.ssl, 0)) { - failf(data, "failed to turn off blocking mode"); - result = CURLE_FAILED_INIT; - goto out; - } - - SSL_set_bio(ctx->tls.ossl.ssl, bio, bio); - bio = NULL; - SSL_set_connect_state(ctx->tls.ossl.ssl); - SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl, - SSL_INCOMING_STREAM_POLICY_ACCEPT, 0); - /* from our side, there is no idle timeout */ - SSL_set_value_uint(ctx->tls.ossl.ssl, - SSL_VALUE_CLASS_FEATURE_REQUEST, - SSL_VALUE_QUIC_IDLE_TIMEOUT, 0); - /* setup the H3 things on top of the QUIC connection */ - result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf); - -out: - if(bio) - BIO_free(bio); - if(baddr) - BIO_ADDR_free(baddr); - CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result); - return result; -} - -struct h3_quic_recv_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - struct cf_osslq_stream *s; -}; - -static CURLcode h3_quic_recv(void *reader_ctx, - unsigned char *buf, size_t len, - size_t *pnread) -{ - struct h3_quic_recv_ctx *x = reader_ctx; - int rv; - - rv = SSL_read_ex(x->s->ssl, buf, len, pnread); - if(rv <= 0) { - int detail = SSL_get_error(x->s->ssl, rv); - if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) { - return CURLE_AGAIN; - } - else if(detail == SSL_ERROR_ZERO_RETURN) { - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS", - x->s->id); - x->s->recvd_eos = TRUE; - return CURLE_OK; - } - else if(SSL_get_stream_read_state(x->s->ssl) == - SSL_STREAM_STATE_RESET_REMOTE) { - uint64_t app_error_code = NGHTTP3_H3_NO_ERROR; - if(!SSL_get_stream_read_error_code(x->s->ssl, &app_error_code)) { - x->s->reset = TRUE; - return CURLE_RECV_ERROR; - } - else { - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, " - "rv=%d, app_err=%" PRIu64, - x->s->id, rv, app_error_code); - if(app_error_code != NGHTTP3_H3_NO_ERROR) - x->s->reset = TRUE; - } - x->s->recvd_eos = TRUE; - return CURLE_OK; - } - else { - return cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR); - } - } - return CURLE_OK; -} - -static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - ssize_t nread; - size_t n; - struct h3_quic_recv_ctx x; - bool eagain = FALSE; - size_t total_recv_len = 0; - - DEBUGASSERT(s); - if(s->closed) - return CURLE_OK; - - x.cf = cf; - x.data = data; - x.s = s; - while(s->ssl && !s->closed && !eagain && - (total_recv_len < H3_STREAM_CHUNK_SIZE)) { - if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) { - while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) { - result = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &n); - if(result) { - if(result != CURLE_AGAIN) - goto out; - result = CURLE_OK; - eagain = TRUE; - } - } - } - - /* Forward what we have to nghttp3 */ - if(!Curl_bufq_is_empty(&s->recvbuf)) { - const unsigned char *buf; - size_t blen; - - while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) { - nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id, - buf, blen, 0); - CURL_TRC_CF(data, cf, "[%" PRId64 "] forward %zu bytes " - "to nghttp3 -> %zd", s->id, blen, nread); - if(nread < 0) { - failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s", - blen, nghttp3_strerror((int)nread)); - result = CURLE_RECV_ERROR; - goto out; - } - /* success, `nread` is the flow for QUIC to count as "consumed", - * not sure how that will work with OpenSSL. Anyways, without error, - * all data that we passed is not owned by nghttp3. */ - Curl_bufq_skip(&s->recvbuf, blen); - total_recv_len += blen; - } - } - - /* When we forwarded everything, handle RESET/EOS */ - if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) { - int rv; - result = CURLE_OK; - if(s->reset) { - uint64_t app_error; - if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) { - failf(data, "SSL_get_stream_read_error_code returned error"); - result = CURLE_RECV_ERROR; - goto out; - } - rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error); - s->closed = TRUE; - if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_close_stream returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_RECV_ERROR; - goto out; - } - } - else if(s->recvd_eos) { - rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, - NGHTTP3_H3_NO_ERROR); - s->closed = TRUE; - CURL_TRC_CF(data, cf, "[%" PRId64 "] close nghttp3 stream -> %d", - s->id, rv); - if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_close_stream returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_RECV_ERROR; - goto out; - } - } - } - } -out: - if(result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d", - s->id, result); - return result; -} - -struct cf_ossq_recv_ctx { - struct Curl_cfilter *cf; - struct Curl_multi *multi; - CURLcode result; -}; - -static bool cf_osslq_iter_recv(uint32_t mid, void *val, void *user_data) -{ - struct h3_stream_ctx *stream = val; - struct cf_ossq_recv_ctx *rctx = user_data; - - (void)mid; - if(stream && !stream->closed && !Curl_bufq_is_full(&stream->recvbuf)) { - struct Curl_easy *sdata = Curl_multi_get_easy(rctx->multi, mid); - if(sdata) { - rctx->result = cf_osslq_stream_recv(&stream->s, rctx->cf, sdata); - if(rctx->result) - return FALSE; /* abort iteration */ - } - } - return TRUE; -} - -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(!ctx->tls.ossl.ssl) - goto out; - - ERR_clear_error(); - - /* 1. Check for new incoming streams */ - while(1) { - SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl, - SSL_ACCEPT_STREAM_NO_BLOCK); - if(!snew) - break; - - result = cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data); - if(result) - goto out; - } - - if(!SSL_handle_events(ctx->tls.ossl.ssl)) { - int detail = SSL_get_error(ctx->tls.ossl.ssl, 0); - result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR); - if(result) - goto out; - } - - if(ctx->h3.conn) { - size_t i; - for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) { - result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data); - if(result) - goto out; - } - } - - if(ctx->h3.conn) { - struct cf_ossq_recv_ctx rctx; - - DEBUGASSERT(data->multi); - rctx.cf = cf; - rctx.multi = data->multi; - rctx.result = CURLE_OK; - Curl_uint32_hash_visit(&ctx->streams, cf_osslq_iter_recv, &rctx); - result = rctx.result; - } - -out: - CURL_TRC_CF(data, cf, "progress_ingress -> %d", result); - return result; -} - -struct cf_ossq_fill_ctx { - struct cf_osslq_ctx *ctx; - struct Curl_multi *multi; - size_t n; -}; - -static bool cf_osslq_collect_block_send(uint32_t mid, void *val, - void *user_data) -{ - struct h3_stream_ctx *stream = val; - struct cf_ossq_fill_ctx *fctx = user_data; - struct cf_osslq_ctx *ctx = fctx->ctx; - - if(fctx->n >= ctx->items_max) /* should not happen, prevent mayhem */ - return FALSE; - - if(stream && stream->s.ssl && stream->s.send_blocked) { - struct Curl_easy *sdata = Curl_multi_get_easy(fctx->multi, mid); - if(sdata) { - ctx->poll_items[fctx->n].desc = SSL_as_poll_descriptor(stream->s.ssl); - ctx->poll_items[fctx->n].events = SSL_POLL_EVENT_W; - ctx->curl_items[fctx->n] = sdata; - fctx->n++; - } - } - return TRUE; -} - -/* Iterate over all streams and check if blocked can be unblocked */ -static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream; - size_t poll_count; - size_t result_count = 0; - size_t idx_count = 0; - CURLcode res = CURLE_OK; - struct timeval timeout; - void *tmpptr; - - if(ctx->h3.conn) { - struct cf_ossq_fill_ctx fill_ctx; - - if(ctx->items_max < Curl_uint32_hash_count(&ctx->streams)) { - size_t nmax = Curl_uint32_hash_count(&ctx->streams); - ctx->items_max = 0; - tmpptr = curlx_realloc(ctx->poll_items, nmax * sizeof(SSL_POLL_ITEM)); - if(!tmpptr) { - curlx_free(ctx->poll_items); - ctx->poll_items = NULL; - res = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->poll_items = tmpptr; - - tmpptr = curlx_realloc(ctx->curl_items, - nmax * sizeof(struct Curl_easy *)); - if(!tmpptr) { - curlx_free(ctx->curl_items); - ctx->curl_items = NULL; - res = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->curl_items = tmpptr; - ctx->items_max = nmax; - } - - fill_ctx.ctx = ctx; - fill_ctx.multi = data->multi; - fill_ctx.n = 0; - Curl_uint32_hash_visit(&ctx->streams, cf_osslq_collect_block_send, - &fill_ctx); - poll_count = fill_ctx.n; - if(poll_count) { - CURL_TRC_CF(data, cf, "polling %zu blocked streams", poll_count); - - memset(&timeout, 0, sizeof(struct timeval)); - res = CURLE_UNRECOVERABLE_POLL; - if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM), - &timeout, 0, &result_count)) - goto out; - - res = CURLE_OK; - - for(idx_count = 0; idx_count < poll_count && result_count > 0; - idx_count++) { - if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) { - stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]); - DEBUGASSERT(stream); /* should still exist */ - if(stream) { - nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id); - stream->s.send_blocked = FALSE; - Curl_multi_mark_dirty(ctx->curl_items[idx_count]); - CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked"); - } - result_count--; - } - } - } - } - -out: - return res; -} - -static CURLcode h3_send_streams(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(!ctx->tls.ossl.ssl || !ctx->h3.conn) - goto out; - - for(;;) { - struct cf_osslq_stream *s = NULL; - nghttp3_vec vec[16]; - nghttp3_ssize n, i; - int64_t stream_id; - size_t written; - int eos, ok, rv; - size_t total_len, acked_len = 0; - bool blocked = FALSE, eos_written = FALSE; - - n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos, - vec, CURL_ARRAYSIZE(vec)); - if(n < 0) { - failf(data, "nghttp3_conn_writev_stream returned error: %s", - nghttp3_strerror((int)n)); - result = CURLE_SEND_ERROR; - goto out; - } - if(stream_id < 0) { - result = CURLE_OK; - goto out; - } - - /* Get the stream for this data */ - s = cf_osslq_get_qstream(cf, data, stream_id); - if(!s) { - failf(data, "nghttp3_conn_writev_stream gave unknown stream %" - PRId64, stream_id); - result = CURLE_SEND_ERROR; - goto out; - } - /* Now write the data to the stream's SSL*, it may not all fit! */ - DEBUGASSERT(s->id == stream_id); - for(i = 0, total_len = 0; i < n; ++i) { - total_len += vec[i].len; - } - for(i = 0; (i < n) && !blocked; ++i) { - /* Without stream->s.ssl, we closed that already, so - * pretend the write did succeed. */ - uint64_t flags = (eos && ((i + 1) == n)) ? SSL_WRITE_FLAG_CONCLUDE : 0; - written = vec[i].len; - ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags, - &written); - if(ok && flags & SSL_WRITE_FLAG_CONCLUDE) - eos_written = TRUE; - if(ok) { - /* As OpenSSL buffers the data, we count this as acknowledged - * from nghttp3's point of view */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] send %zu bytes to QUIC ok", - s->id, vec[i].len); - acked_len += vec[i].len; - } - else { - int detail = SSL_get_error(s->ssl, 0); - switch(detail) { - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_READ: - /* QUIC blocked us from writing more */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] send %zu bytes to " - "QUIC blocked", s->id, vec[i].len); - written = 0; - nghttp3_conn_block_stream(ctx->h3.conn, s->id); - s->send_blocked = blocked = TRUE; - break; - default: - failf(data, "[%" PRId64 "] send %zu bytes to QUIC, SSL error %d", - s->id, vec[i].len, detail); - result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3); - goto out; - } - } - } - - if(acked_len > 0 || (eos && !s->send_blocked)) { - /* Since QUIC buffers the data written internally, we can tell - * nghttp3 that it can move forward on it */ - ctx->q.last_io = *Curl_pgrs_now(data); - rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_add_write_offset returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_SEND_ERROR; - goto out; - } - rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_add_ack_offset returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_SEND_ERROR; - goto out; - } - CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes " - "to QUIC, eos=%d", s->id, acked_len, total_len, eos); - } - - if(eos && !s->send_blocked && !eos_written) { - /* wrote everything and H3 indicates end of stream */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id); - SSL_stream_conclude(s->ssl, 0); - } - } - -out: - CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result); - return result; -} - -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(!ctx->tls.ossl.ssl) - goto out; - - ERR_clear_error(); - result = h3_send_streams(cf, data); - if(result) - goto out; - - if(!SSL_handle_events(ctx->tls.ossl.ssl)) { - int detail = SSL_get_error(ctx->tls.ossl.ssl, 0); - result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR); - } - - result = cf_osslq_check_and_unblock(cf, data); - -out: - CURL_TRC_CF(data, cf, "progress_egress -> %d", result); - return result; -} - -static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct timeval tv; - timediff_t timeoutms; - int is_infinite = 1; - - if(ctx->tls.ossl.ssl && - SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite) && - !is_infinite) { - timeoutms = curlx_tvtoms(&tv); - /* QUIC want to be called again latest at the returned timeout */ - if(timeoutms <= 0) { - result = cf_progress_ingress(cf, data); - if(result) - goto out; - result = cf_progress_egress(cf, data); - if(result) - goto out; - if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) { - timeoutms = curlx_tvtoms(&tv); - } - } - if(!is_infinite) { - Curl_expire(data, timeoutms, EXPIRE_QUIC); - CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms); - } - } -out: - return result; -} - -static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - int err; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* Connect the UDP filter first */ - if(!cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; - } - - *done = FALSE; - CF_DATA_SAVE(save, cf, data); - - if(!ctx->tls.ossl.ssl) { - ctx->started_at = *Curl_pgrs_now(data); - result = cf_osslq_ctx_start(cf, data); - if(result) - goto out; - } - - if(!ctx->got_first_byte) { - int readable = SOCKET_READABLE(ctx->q.sockfd, 0); - if(readable > 0 && (readable & CURL_CSELECT_IN)) { - ctx->got_first_byte = TRUE; - ctx->first_byte_at = *Curl_pgrs_now(data); - } - } - - /* Since OpenSSL does its own send/recv internally, we may miss the - * moment to populate the x509 store right before the server response. - * Do it instead before we start the handshake, at the loss of the - * time to set this up. */ - result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); - if(result) - goto out; - - ERR_clear_error(); - err = SSL_do_handshake(ctx->tls.ossl.ssl); - - if(err == 1) { - /* connected */ - if(!ctx->got_first_byte) { - /* if not recorded yet, take the timestamp before we called - * SSL_do_handshake() as the time we received the first packet. */ - ctx->got_first_byte = TRUE; - ctx->first_byte_at = *Curl_pgrs_now(data); - } - /* Record the handshake complete with a new time stamp. */ - ctx->handshake_at = *Curl_pgrs_now(data); - ctx->q.last_io = *Curl_pgrs_now(data); - CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms", - curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started_at)); - result = cf_osslq_verify_peer(cf, data); - if(!result) { - CURL_TRC_CF(data, cf, "peer verified"); - cf->connected = TRUE; - *done = TRUE; - } - } - else { - int detail = SSL_get_error(ctx->tls.ossl.ssl, err); - switch(detail) { - case SSL_ERROR_WANT_READ: - ctx->q.last_io = *Curl_pgrs_now(data); - CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV"); - goto out; - case SSL_ERROR_WANT_WRITE: - ctx->q.last_io = *Curl_pgrs_now(data); - CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND"); - result = CURLE_OK; - goto out; -#ifdef SSL_ERROR_WANT_ASYNC - case SSL_ERROR_WANT_ASYNC: - ctx->q.last_io = *Curl_pgrs_now(data); - CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC"); - result = CURLE_OK; - goto out; -#endif -#ifdef SSL_ERROR_WANT_RETRY_VERIFY - case SSL_ERROR_WANT_RETRY_VERIFY: - result = CURLE_OK; - goto out; -#endif - default: - result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT); - goto out; - } - } - -out: - if(result == CURLE_RECV_ERROR && ctx->tls.ossl.ssl && - ctx->protocol_shutdown) { - /* When a QUIC server instance is shutting down, it may send us a - * CONNECTION_CLOSE right away. Our connection then enters the DRAINING - * state. The CONNECT may work in the near future again. Indicate - * that as a "weird" reply. */ - result = CURLE_WEIRD_SERVER_REPLY; - } - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(result) { - struct ip_quadruple ip; - - if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) - infof(data, "QUIC connect to %s port %u failed: %s", - ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); - } -#endif - if(!result) - result = check_and_set_expiry(cf, data); - if(result || *done) - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode h3_stream_open(struct Curl_cfilter *cf, - struct Curl_easy *data, - const uint8_t *buf, size_t len, - size_t *pnwritten) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = NULL; - struct dynhds h2_headers; - size_t nheader; - nghttp3_nv *nva = NULL; - int rc = 0; - unsigned int i; - nghttp3_data_reader reader; - nghttp3_data_reader *preader = NULL; - CURLcode result; - - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - - result = h3_data_setup(cf, data); - if(result) - goto out; - stream = H3_STREAM_CTX(ctx, data); - DEBUGASSERT(stream); - if(!stream) { - result = CURLE_FAILED_INIT; - goto out; - } - - result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, - !data->state.http_ignorecustom ? - data->set.str[STRING_CUSTOMREQUEST] : NULL, - 0, pnwritten); - if(result) - goto out; - if(!stream->h1.done) { - /* need more data */ - goto out; - } - DEBUGASSERT(stream->h1.req); - - result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); - if(result) - goto out; - /* no longer needed */ - Curl_h1_req_parse_free(&stream->h1); - - nheader = Curl_dynhds_count(&h2_headers); - nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP3_NV_FLAG_NONE; - } - - DEBUGASSERT(stream->s.id == -1); - result = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0, - &ctx->stream_bufcp, data); - if(result) { - failf(data, "cannot get bidi streams"); - result = CURLE_SEND_ERROR; - goto out; - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - /* known request body size or -1 */ - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown */ - break; - default: - /* there is not request body */ - stream->upload_left = 0; /* no request body */ - break; - } - - stream->send_closed = (stream->upload_left == 0); - if(!stream->send_closed) { - reader.read_data = cb_h3_read_req_body; - preader = &reader; - } - - rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id, - nva, nheader, preader, data); - if(rc) { - switch(rc) { - case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " - "connection is closing", stream->s.id); - break; - default: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)", - stream->s.id, rc, nghttp3_strerror(rc)); - break; - } - result = CURLE_SEND_ERROR; - goto out; - } - - if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", - stream->s.id, Curl_bufref_ptr(&data->state.url)); - for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", - stream->s.id, - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); - } - } - -out: - curlx_free(nva); - Curl_dynhds_free(&h2_headers); - return result; -} - -static CURLcode cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const uint8_t *buf, size_t len, bool eos, - size_t *pnwritten) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = NULL; - struct cf_call_data save; - CURLcode result = CURLE_OK; - - (void)eos; /* use to end stream */ - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->tls.ossl.ssl); - DEBUGASSERT(ctx->h3.conn); - *pnwritten = 0; - - result = cf_progress_ingress(cf, data); - if(result) - goto out; - - result = cf_progress_egress(cf, data); - if(result) - goto out; - - stream = H3_STREAM_CTX(ctx, data); - if(!stream || stream->s.id < 0) { - result = h3_stream_open(cf, data, buf, len, pnwritten); - if(result) { - CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); - goto out; - } - stream = H3_STREAM_CTX(ctx, data); - } - 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->s.id); - result = CURLE_OK; - *pnwritten = len; - goto out; - } - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " - "-> stream closed", stream->s.id, len); - result = CURLE_HTTP3; - goto out; - } - else { - result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %d, %zu", - stream->s.id, len, result, *pnwritten); - if(result) - goto out; - (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); - } - - result = Curl_1st_err(result, cf_progress_egress(cf, data)); - -out: - result = Curl_1st_err(result, check_and_set_expiry(cf, data)); - - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", - stream ? stream->s.id : -1, len, result, *pnwritten); - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - size_t *pnread) -{ - (void)cf; - *pnread = 0; - if(stream->reset) { - failf(data, - "HTTP/3 stream %" PRId64 " reset by server", - stream->s.id); - return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; - } - 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->s.id); - return CURLE_HTTP3; - } - return CURLE_OK; -} - -static CURLcode cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, size_t *pnread) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream; - struct cf_call_data save; - CURLcode result = CURLE_OK; - - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->tls.ossl.ssl); - DEBUGASSERT(ctx->h3.conn); - *pnread = 0; - - stream = H3_STREAM_CTX(ctx, data); - if(!stream) { - result = CURLE_RECV_ERROR; - goto out; - } - - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); - if(result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) -> %d, %zu", - stream->s.id, len, result, *pnread); - goto out; - } - } - - result = Curl_1st_err(result, cf_progress_ingress(cf, data)); - if(result) - goto out; - - /* recvbuf had nothing before, maybe after progressing ingress? */ - if(!*pnread && !Curl_bufq_is_empty(&stream->recvbuf)) { - result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); - if(result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) -> %d, %zu", - stream->s.id, len, result, *pnread); - goto out; - } - } - - if(*pnread) { - Curl_multi_mark_dirty(data); - } - else { - if(stream->closed) { - result = recv_closed_stream(cf, data, stream, pnread); - goto out; - } - result = CURLE_AGAIN; - } - -out: - result = Curl_1st_err(result, cf_progress_egress(cf, data)); - result = Curl_1st_err(result, check_and_set_expiry(cf, data)); - - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu", - stream ? stream->s.id : -1, len, result, *pnread); - CF_DATA_RESTORE(cf, save); - return result; -} - -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -static bool cf_osslq_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - const struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)cf; - return stream && !Curl_bufq_is_empty(&stream->recvbuf); -} - -static CURLcode cf_osslq_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_DATA_SETUP: - break; - case CF_CTRL_DATA_PAUSE: - result = h3_data_pause(cf, data, (arg1 != 0)); - break; - case CF_CTRL_DATA_DONE: - h3_data_done(cf, data); - break; - case CF_CTRL_DATA_DONE_SEND: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(stream && !stream->send_closed) { - stream->send_closed = TRUE; - stream->upload_left = Curl_bufq_len(&stream->sendbuf) - - stream->sendbuf_len_in_flight; - (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); - } - break; - } - case CF_CTRL_CONN_INFO_UPDATE: - if(!cf->sockindex && cf->connected) { - cf->conn->httpversion_seen = 30; - Curl_conn_set_multiplex(cf->conn); - } - break; - default: - break; - } - CF_DATA_RESTORE(cf, save); - return result; -} - -static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - bool alive = FALSE; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - *input_pending = FALSE; - if(!ctx->tls.ossl.ssl) - goto out; - -#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT - /* Added in OpenSSL v3.3.x */ - { - timediff_t idletime; - uint64_t idle_ms = 0; - if(!SSL_get_value_uint(ctx->tls.ossl.ssl, - SSL_VALUE_CLASS_FEATURE_NEGOTIATED, - SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) { - CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, " - "assume connection is dead."); - goto out; - } - CURL_TRC_CF(data, cf, "negotiated idle timeout: %" PRIu64 "ms", idle_ms); - idletime = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->q.last_io); - if(idle_ms && idletime > 0 && (uint64_t)idletime > idle_ms) - goto out; - } - -#endif - - if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - goto out; - - alive = TRUE; - if(*input_pending) { - CURLcode result; - /* This happens before we have sent off a request and the connection is - not in use by any other transfer, there should not be any data here, - only "protocol frames" */ - *input_pending = FALSE; - result = cf_progress_ingress(cf, data); - CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); - alive = result ? FALSE : TRUE; - } - -out: - CF_DATA_RESTORE(cf, save); - return alive; -} - -static CURLcode cf_osslq_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(!ctx->tls.ossl.ssl) { - /* NOP */ - } - else if(!cf->connected) { - /* during handshake, transfer has not started yet. we always - * add our socket for polling if SSL wants to send/recv */ - result = Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ossl.ssl), - SSL_net_write_desired(ctx->tls.ossl.ssl)); - } - else { - /* once connected, we only modify the socket if it is present. - * this avoids adding it for paused transfers. */ - bool want_recv, want_send; - Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); - if(want_recv || want_send) { - result = Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ossl.ssl), - SSL_net_write_desired(ctx->tls.ossl.ssl)); - } - else if(ctx->need_recv || ctx->need_send) { - result = Curl_pollset_set(data, ps, ctx->q.sockfd, - ctx->need_recv, ctx->need_send); - } - } - return result; -} - -static CURLcode cf_osslq_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - - switch(query) { - case CF_QUERY_MAX_CONCURRENT: { -#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL - /* Added in OpenSSL v3.3.x */ - uint64_t v = 0; - if(ctx->tls.ossl.ssl && - !SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC, - SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) { - CURL_TRC_CF(data, cf, "error getting available local bidi streams"); - return CURLE_HTTP3; - } - /* we report avail + in_use */ - v += cf->conn->attached_xfers; - *pres1 = (v > INT_MAX) ? INT_MAX : (int)v; -#else - *pres1 = 100; -#endif - CURL_TRC_CF(data, cf, "query max_concurrent -> %d", *pres1); - return CURLE_OK; - } - case CF_QUERY_CONNECT_REPLY_MS: - if(ctx->got_first_byte) { - timediff_t ms = curlx_ptimediff_ms(&ctx->first_byte_at, - &ctx->started_at); - *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; - } - else - *pres1 = -1; - return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - if(ctx->got_first_byte) - *when = ctx->first_byte_at; - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } - case CF_QUERY_HTTP_VERSION: - *pres1 = 30; - return CURLE_OK; - case CF_QUERY_SSL_INFO: - case CF_QUERY_SSL_CTX_INFO: { - struct curl_tlssessioninfo *info = pres2; - if(Curl_vquic_tls_get_ssl_info(&ctx->tls, - (query == CF_QUERY_SSL_CTX_INFO), info)) - return CURLE_OK; - break; - } - case CF_QUERY_ALPN_NEGOTIATED: { - const char **palpn = pres2; - DEBUGASSERT(palpn); - *palpn = cf->connected ? "h3" : NULL; - return CURLE_OK; - } - default: - break; - } - return cf->next ? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -struct Curl_cftype Curl_cft_http3 = { - "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, - 0, - cf_osslq_destroy, - cf_osslq_connect, - cf_osslq_close, - cf_osslq_shutdown, - cf_osslq_adjust_pollset, - cf_osslq_data_pending, - cf_osslq_send, - cf_osslq_recv, - cf_osslq_cntrl, - cf_osslq_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_osslq_query, -}; - -CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai) -{ - struct cf_osslq_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL; - CURLcode result; - - (void)data; - ctx = curlx_calloc(1, sizeof(*ctx)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - cf_osslq_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); - if(result) - goto out; - cf->conn = conn; - - result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC); - if(result) - goto out; - - cf->next->conn = cf->conn; - cf->next->sockindex = cf->sockindex; - -out: - *pcf = (!result) ? cf : NULL; - if(result) { - if(cf) - Curl_conn_cf_discard_chain(&cf, data); - else if(ctx) - cf_osslq_ctx_free(ctx); - } - return result; -} - -bool Curl_conn_is_osslq(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL; - - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_http3) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -/* - * Store ngtcp2 version info in this buffer. - */ -void Curl_osslq_ver(char *p, size_t len) -{ - const nghttp3_info *ht3 = nghttp3_version(0); - (void)curl_msnprintf(p, len, "nghttp3/%s", ht3->version_str); -} - -#endif /* !CURL_DISABLE_HTTP && USE_OPENSSL_QUIC && USE_NGHTTP3 */
diff --git a/Utilities/cmcurl/lib/vquic/curl_osslq.h b/Utilities/cmcurl/lib/vquic/curl_osslq.h deleted file mode 100644 index 7aa93d6..0000000 --- a/Utilities/cmcurl/lib/vquic/curl_osslq.h +++ /dev/null
@@ -1,51 +0,0 @@ -#ifndef HEADER_CURL_VQUIC_CURL_OSSLQ_H -#define HEADER_CURL_VQUIC_CURL_OSSLQ_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "../curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_OPENSSL_QUIC) && \ - defined(USE_NGHTTP3) - -#ifdef HAVE_NETINET_UDP_H -#include <netinet/udp.h> -#endif - -struct Curl_cfilter; - -#include "../urldata.h" - -void Curl_osslq_ver(char *p, size_t len); - -CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai); - -bool Curl_conn_is_osslq(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); -#endif - -#endif /* HEADER_CURL_VQUIC_CURL_OSSLQ_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c index e4140e7..73f664a 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.c +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -21,49 +21,46 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_QUICHE) #include <quiche.h> #include <openssl/err.h> #include <openssl/ssl.h> -#include "../bufq.h" -#include "../uint-hash.h" -#include "../urldata.h" -#include "../cfilters.h" -#include "../cf-socket.h" -#include "../curl_trc.h" -#include "../rand.h" -#include "../multiif.h" -#include "../connect.h" -#include "../progress.h" -#include "../select.h" -#include "../http1.h" -#include "vquic.h" -#include "vquic_int.h" -#include "vquic-tls.h" -#include "curl_quiche.h" -#include "../transfer.h" -#include "../url.h" -#include "../bufref.h" -#include "../vtls/openssl.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" + +#include "bufq.h" +#include "uint-hash.h" +#include "urldata.h" +#include "cfilters.h" +#include "cf-dns.h" +#include "cf-socket.h" +#include "curl_trc.h" +#include "rand.h" +#include "multiif.h" +#include "connect.h" +#include "progress.h" +#include "select.h" +#include "http1.h" +#include "vquic/vquic.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" +#include "vquic/curl_quiche.h" +#include "transfer.h" +#include "url.h" +#include "bufref.h" +#include "vtls/openssl.h" +#include "vtls/vtls.h" /* HTTP/3 error values defined in RFC 9114, ch. 8.1 */ -#define CURL_H3_NO_ERROR (0x0100) +#define CURL_H3_NO_ERROR 0x0100 -#define QUIC_MAX_STREAMS (100) +#define MAX_PKT_BURST 10 -#define H3_STREAM_WINDOW_SIZE (128 * 1024) -#define H3_STREAM_CHUNK_SIZE (16 * 1024) -/* The pool keeps spares around and half of a full stream windows seems good. - * More does not seem to improve performance. The benefit of the pool is that - * stream buffer to not keep spares. Memory consumption goes down when streams - * run empty, have a large upload done, etc. */ -#define H3_STREAM_POOL_SPARES \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) / 2 -/* Receive and Send max number of chunks just follows from the +#define QUIC_MAX_STREAMS 100 + +#define H3_STREAM_WINDOW_SIZE (1024 * 128) +#define H3_STREAM_CHUNK_SIZE (1024 * 16) +/* Receive and Send max number of chunks follows from the * chunk size and window size */ #define H3_STREAM_RECV_CHUNKS \ (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) @@ -129,7 +126,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) { if(ctx && ctx->initialized) { - /* quiche just freed it */ + /* quiche freed it */ ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); Curl_ssl_peer_cleanup(&ctx->peer); @@ -340,7 +337,7 @@ if(!stream->xfer_result) { stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); if(stream->xfer_result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu " + CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu " "bytes of headers", stream->id, stream->xfer_result, blen); } } @@ -392,14 +389,14 @@ if(!result) cf_quiche_write_hd(cf, data, stream, curlx_dyn_ptr(&ctx->h1hdr), curlx_dyn_len(&ctx->h1hdr), FALSE); - CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", + CURL_TRC_CF(data, cf, "[%" PRIu64 "] status: %s", stream->id, curlx_dyn_ptr(&ctx->h1hdr)); } else { if(is_valid_h3_header(value, value_len) && is_valid_h3_header(name, name_len)) { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" PRIu64 "] header: %.*s: %.*s", stream->id, (int)name_len, name, (int)value_len, value); curlx_dyn_reset(&ctx->h1hdr); @@ -461,7 +458,7 @@ data, (const char *)buf, blen, FALSE); Curl_bufq_skip(&ctx->writebuf, blen); if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu bytes" + CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu bytes" " of data", stream->id, stream->xfer_result, blen); } } @@ -484,7 +481,7 @@ return; /* Even when the transfer has already errored, we need to receive - * the data from quiche, as quiche will otherwise get stuck and + * the data from quiche, as quiche otherwise gets stuck and * raise events to receive over and over again. */ cb_ctx.cf = cf; cb_ctx.data = data; @@ -782,7 +779,7 @@ else failf(data, "connection closed by server"); /* Connection timed out, expire all transfers belonging to it - * as will not get any more POLL events here. */ + * as it does not get any more POLL events here. */ cf_quiche_expire_conn_closed(cf, data); return CURLE_SEND_ERROR; } @@ -855,7 +852,23 @@ DEBUGASSERT(stream); *pnread = 0; if(stream->reset) { - failf(data, "HTTP/3 stream %" PRIu64 " reset by server", stream->id); + if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { + infof(data, "HTTP/3 stream %" PRIu64 " refused by server, try again " + "on a new connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + data->state.refused_stream = TRUE; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ + } + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%" PRIu64 "] error after response headers, " + "but we did not want a body anyway, ignore error 0x%" + PRIx64 " %s", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + return CURLE_OK; + } + failf(data, "HTTP/3 stream %" PRIu64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv, was reset -> %d", stream->id, result); @@ -905,7 +918,7 @@ result = CURLE_AGAIN; out: - result = Curl_1st_err(result, cf_flush_egress(cf, data)); + result = Curl_1st_fatal(result, cf_flush_egress(cf, data)); if(*pnread > 0) ctx->data_recvd += *pnread; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv(len=%zu) -> %d, %zu, total=%" @@ -926,8 +939,8 @@ rv = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, (uint8_t *)CURL_UNCONST(buf), len, eos); if(rv == QUICHE_H3_ERR_DONE || (rv == 0 && len > 0)) { - /* Blocked on flow control and should HOLD sending. But when do we open - * again? */ + /* Blocked on flow control and should HOLD sending. + When do we open again? */ if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { CURL_TRC_CF(data, cf, "[%" PRIu64 "] send_body(len=%zu) " "-> window exhausted", stream->id, len); @@ -1038,7 +1051,7 @@ goto out; } else { - CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRIu64, + CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64, Curl_bufref_ptr(&data->state.url), rv); } result = CURLE_SEND_ERROR; @@ -1066,7 +1079,7 @@ CURLcode r2 = CURLE_OK; r2 = cf_quiche_send_body(cf, data, stream, buf, blen, eos, &nwritten); - if(r2 && (CURLE_AGAIN != r2)) { /* real error, fail */ + if(r2 && (r2 != CURLE_AGAIN)) { /* real error, fail */ result = r2; } else if(nwritten > 0) { @@ -1111,7 +1124,7 @@ * 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 + * of reading the full request body 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- */ @@ -1131,7 +1144,7 @@ } out: - result = Curl_1st_err(result, cf_flush_egress(cf, data)); + result = Curl_1st_fatal(result, cf_flush_egress(cf, data)); CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : (uint64_t)~0, len, @@ -1139,8 +1152,8 @@ return result; } -static bool stream_is_writeable(struct Curl_cfilter *cf, - struct Curl_easy *data) +static bool stream_is_writable(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1168,7 +1181,7 @@ c_exhaust = FALSE; /* Have not found any call in quiche that tells us if the connection itself is blocked */ s_exhaust = want_send && stream && stream->opened && - (stream->quic_flow_blocked || !stream_is_writeable(cf, data)); + (stream->quic_flow_blocked || !stream_is_writable(cf, data)); want_recv = (want_recv || c_exhaust || s_exhaust); want_send = (!s_exhaust && want_send) || !Curl_bufq_is_empty(&ctx->q.sendbuf); @@ -1366,6 +1379,12 @@ } *done = FALSE; + if(Curl_ossl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } + vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); if(!ctx->qconn) { @@ -1418,7 +1437,7 @@ } out: -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(result && result != CURLE_AGAIN) { struct ip_quadruple ip; @@ -1621,14 +1640,12 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai) + struct Curl_sockaddr_ex *addr) { struct cf_quiche_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; - (void)data; - (void)conn; ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; @@ -1641,7 +1658,7 @@ goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn;
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.h b/Utilities/cmcurl/lib/vquic/curl_quiche.h index 6c0fb9e..c2c88dd 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.h +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_QUICHE) @@ -38,7 +38,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai); + struct Curl_sockaddr_ex *addr); #endif
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.c b/Utilities/cmcurl/lib/vquic/vquic-tls.c index 3391d98..d81f2a9 100644 --- a/Utilities/cmcurl/lib/vquic/vquic-tls.c +++ b/Utilities/cmcurl/lib/vquic/vquic-tls.c
@@ -21,34 +21,33 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) #ifdef USE_OPENSSL #include <openssl/err.h> -#include "../vtls/openssl.h" +#include "vtls/openssl.h" #elif defined(USE_GNUTLS) #include <gnutls/abstract.h> #include <gnutls/gnutls.h> #include <gnutls/x509.h> #include <gnutls/crypto.h> #include <nettle/sha2.h> -#include "../vtls/gtls.h" +#include "vtls/gtls.h" #elif defined(USE_WOLFSSL) #include <wolfssl/options.h> #include <wolfssl/ssl.h> #include <wolfssl/quic.h> -#include "../vtls/wolfssl.h" +#include "vtls/wolfssl.h" #endif -#include "../urldata.h" -#include "../cfilters.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" -#include "../vtls/vtls_scache.h" -#include "vquic-tls.h" +#include "urldata.h" +#include "cfilters.h" +#include "vtls/vtls.h" +#include "vtls/vtls_scache.h" +#include "vquic/vquic-tls.h" CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf,
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.h b/Utilities/cmcurl/lib/vquic/vquic-tls.h index cff7654..c461c15 100644 --- a/Utilities/cmcurl/lib/vquic/vquic-tls.h +++ b/Utilities/cmcurl/lib/vquic/vquic-tls.h
@@ -23,17 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) -#include "../bufq.h" -#include "../vtls/vtls.h" -#include "../vtls/vtls_int.h" +#include "bufq.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" -#include "../vtls/openssl.h" -#include "../vtls/wolfssl.h" +#include "vtls/openssl.h" +#include "vtls/wolfssl.h" struct ssl_peer; struct Curl_ssl_session; @@ -53,7 +53,7 @@ * Callback passed to `Curl_vquic_tls_init()` that can * do early initializations on the not otherwise configured TLS * instances created. This varies by TLS backend: - * - openssl/wolfssl: SSL_CTX* has just been created + * - openssl/wolfssl: SSL_CTX* has been created * - gnutls: gtls_client_init() has run */ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, @@ -72,7 +72,7 @@ * @param ctx the TLS context to initialize * @param cf the connection filter involved * @param data the transfer involved - * @param peer the peer that will be connected to + * @param peer the peer to be connected to * @param alpns the ALPN specifications to negotiate, may be NULL * @param cb_setup optional callback for early TLS config * @param cb_user_data user_data param for callback
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index d7bfa99..0040ad6 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -21,33 +21,35 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" +#include "urldata.h" +#include "vquic/vquic.h" + +#include "curl_trc.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) #ifdef HAVE_NETINET_UDP_H #include <netinet/udp.h> #endif + #ifdef USE_NGHTTP3 #include <nghttp3/nghttp3.h> #endif -#include "../urldata.h" -#include "../bufq.h" -#include "../curlx/dynbuf.h" -#include "../curlx/fopen.h" -#include "../cfilters.h" -#include "../curl_trc.h" -#include "curl_ngtcp2.h" -#include "curl_osslq.h" -#include "curl_quiche.h" -#include "../multiif.h" -#include "../progress.h" -#include "../rand.h" -#include "vquic.h" -#include "vquic_int.h" -#include "../curlx/strerr.h" -#include "../curlx/strparse.h" +#include "bufq.h" +#include "curlx/dynbuf.h" +#include "curlx/fopen.h" +#include "cfilters.h" +#include "vquic/curl_ngtcp2.h" +#include "vquic/curl_quiche.h" +#include "multiif.h" +#include "progress.h" +#include "rand.h" +#include "vquic/vquic_int.h" +#include "curlx/strerr.h" +#include "curlx/strparse.h" -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) #define NW_CHUNK_SIZE (64 * 1024) #define NW_SEND_CHUNKS 1 @@ -66,8 +68,6 @@ { #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) Curl_ngtcp2_ver(p, len); -#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) - Curl_osslq_ver(p, len); #elif defined(USE_QUICHE) Curl_quiche_ver(p, len); #endif @@ -170,7 +170,8 @@ #endif return CURLE_AGAIN; case SOCKEMSGSIZE: - /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ + /* UDP datagram is too large; caused by PMTUD. Let it be lost. */ + *psent = pktlen; break; case EIO: if(pktlen > gsolen) { @@ -198,8 +199,7 @@ *psent = 0; - while((rv = send(qctx->sockfd, (const char *)pkt, - (SEND_TYPE_ARG3)pktlen, 0)) == -1 && + while((rv = swrite(qctx->sockfd, pkt, pktlen)) == -1 && SOCKERRNO == SOCKEINTR) ; @@ -209,13 +209,13 @@ goto out; } else { - failf(data, "send() returned %zd (errno %d)", rv, SOCKERRNO); if(SOCKERRNO != SOCKEMSGSIZE) { + failf(data, "send() returned %zd (errno %d)", rv, SOCKERRNO); result = CURLE_SEND_ERROR; goto out; } - /* UDP datagram is too large; caused by PMTUD. Just let it be - lost. */ + /* UDP datagram is too large; caused by PMTUD. Let it be lost. */ + *psent = pktlen; } } #endif @@ -225,11 +225,13 @@ return result; } +#ifdef CURLVERBOSE #ifdef HAVE_SENDMSG #define VQUIC_SEND_METHOD "sendmsg" #else #define VQUIC_SEND_METHOD "send" #endif +#endif static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -238,8 +240,9 @@ size_t gsolen, size_t *psent) { const uint8_t *p, *end = pkt + pktlen; - size_t sent, len, calls = 0; + size_t sent, len; CURLcode result = CURLE_OK; + VERBOSE(size_t calls = 0); *psent = 0; @@ -249,7 +252,7 @@ if(result) goto out; *psent += sent; - ++calls; + VERBOSE(++calls); } out: CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=%zu)" @@ -392,7 +395,10 @@ struct mmsghdr mmsg[MMSG_NUM]; uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))]; struct sockaddr_storage remote_addr[MMSG_NUM]; - size_t total_nread = 0, pkts = 0, calls = 0; + size_t total_nread = 0, pkts = 0; +#ifdef CURLVERBOSE + size_t calls = 0; +#endif int mcount, i, n; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; @@ -445,7 +451,7 @@ goto out; } - ++calls; + VERBOSE(++calls); for(i = 0; i < mcount; ++i) { /* A zero-length UDP packet is no QUIC packet. Ignore. */ if(!mmsg[i].msg_len) @@ -696,22 +702,20 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { (void)transport; DEBUGASSERT(transport == TRNSPRT_QUIC); #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_create(pcf, data, conn, ai); -#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) - return Curl_cf_osslq_create(pcf, data, conn, ai); + return Curl_cf_ngtcp2_create(pcf, data, conn, addr); #elif defined(USE_QUICHE) - return Curl_cf_quiche_create(pcf, data, conn, ai); + return Curl_cf_quiche_create(pcf, data, conn, addr); #else *pcf = NULL; (void)data; (void)conn; - (void)ai; + (void)addr; return CURLE_NOT_BUILT_IN; #endif } @@ -721,10 +725,10 @@ unsigned char transport) { if(transport == TRNSPRT_UNIX) { - /* cannot do QUIC over a Unix domain socket */ + failf(data, "HTTP/3 cannot be used over UNIX domain sockets"); return CURLE_QUIC_CONNECT_ERROR; } - if(!(conn->handler->flags & PROTOPT_SSL)) { + if(!(conn->scheme->flags & PROTOPT_SSL)) { failf(data, "HTTP/3 requested for non-HTTPS URL"); return CURLE_URL_MALFORMAT; } @@ -742,6 +746,56 @@ return CURLE_OK; } +#ifdef CURLVERBOSE +const char *vquic_h3_err_str(uint64_t error_code) +{ + if(error_code <= UINT_MAX) { + switch((unsigned int)error_code) { + case CURL_H3_ERR_NO_ERROR: + return "NO_ERROR"; + case CURL_H3_ERR_GENERAL_PROTOCOL_ERROR: + return "GENERAL_PROTOCOL_ERROR"; + case CURL_H3_ERR_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case CURL_H3_ERR_STREAM_CREATION_ERROR: + return "STREAM_CREATION_ERROR"; + case CURL_H3_ERR_CLOSED_CRITICAL_STREAM: + return "CLOSED_CRITICAL_STREAM"; + case CURL_H3_ERR_FRAME_UNEXPECTED: + return "FRAME_UNEXPECTED"; + case CURL_H3_ERR_FRAME_ERROR: + return "FRAME_ERROR"; + case CURL_H3_ERR_EXCESSIVE_LOAD: + return "EXCESSIVE_LOAD"; + case CURL_H3_ERR_ID_ERROR: + return "ID_ERROR"; + case CURL_H3_ERR_SETTINGS_ERROR: + return "SETTINGS_ERROR"; + case CURL_H3_ERR_MISSING_SETTINGS: + return "MISSING_SETTINGS"; + case CURL_H3_ERR_REQUEST_REJECTED: + return "REQUEST_REJECTED"; + case CURL_H3_ERR_REQUEST_CANCELLED: + return "REQUEST_CANCELLED"; + case CURL_H3_ERR_REQUEST_INCOMPLETE: + return "REQUEST_INCOMPLETE"; + case CURL_H3_ERR_MESSAGE_ERROR: + return "MESSAGE_ERROR"; + case CURL_H3_ERR_CONNECT_ERROR: + return "CONNECT_ERROR"; + case CURL_H3_ERR_VERSION_FALLBACK: + return "VERSION_FALLBACK"; + default: + break; + } + } + /* RFC 9114 ch. 8.1 + 9, reserved future error codes that are NO_ERROR */ + if((error_code >= 0x21) && !((error_code - 0x21) % 0x1f)) + return "NO_ERROR"; + return "unknown"; +} +#endif /* CURLVERBOSE */ + #if defined(USE_NGTCP2) || defined(USE_NGHTTP3) static void *vquic_ngtcp2_malloc(size_t size, void *user_data) @@ -804,8 +858,8 @@ const struct connectdata *conn, unsigned char transport) { - (void)conn; (void)data; + (void)conn; (void)transport; DEBUGF(infof(data, "QUIC is not supported in this build")); return CURLE_NOT_BUILT_IN;
diff --git a/Utilities/cmcurl/lib/vquic/vquic.h b/Utilities/cmcurl/lib/vquic/vquic.h index f5392eb..1f0a1ab 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.h +++ b/Utilities/cmcurl/lib/vquic/vquic.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) struct Curl_cfilter; @@ -42,7 +42,7 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); extern struct Curl_cftype Curl_cft_http3;
diff --git a/Utilities/cmcurl/lib/vquic/vquic_int.h b/Utilities/cmcurl/lib/vquic/vquic_int.h index e602b7e..82bd5b0 100644 --- a/Utilities/cmcurl/lib/vquic/vquic_int.h +++ b/Utilities/cmcurl/lib/vquic/vquic_int.h
@@ -23,15 +23,41 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" - -#include "../bufq.h" +#include "curl_setup.h" #ifdef USE_HTTP3 -#define MAX_PKT_BURST 10 +#include "bufq.h" + #define MAX_UDP_PAYLOAD_SIZE 1452 +/* definitions from RFC 9114, ch 8.1 */ +typedef enum { + CURL_H3_ERR_NO_ERROR = 0x0100, + CURL_H3_ERR_GENERAL_PROTOCOL_ERROR = 0x0101, + CURL_H3_ERR_INTERNAL_ERROR = 0x0102, + CURL_H3_ERR_STREAM_CREATION_ERROR = 0x0103, + CURL_H3_ERR_CLOSED_CRITICAL_STREAM = 0x0104, + CURL_H3_ERR_FRAME_UNEXPECTED = 0x0105, + CURL_H3_ERR_FRAME_ERROR = 0x0106, + CURL_H3_ERR_EXCESSIVE_LOAD = 0x0107, + CURL_H3_ERR_ID_ERROR = 0x0108, + CURL_H3_ERR_SETTINGS_ERROR = 0x0109, + CURL_H3_ERR_MISSING_SETTINGS = 0x010a, + CURL_H3_ERR_REQUEST_REJECTED = 0x010b, + CURL_H3_ERR_REQUEST_CANCELLED = 0x010c, + CURL_H3_ERR_REQUEST_INCOMPLETE = 0x010d, + CURL_H3_ERR_MESSAGE_ERROR = 0x010e, + CURL_H3_ERR_CONNECT_ERROR = 0x010f, + CURL_H3_ERR_VERSION_FALLBACK = 0x0110, +} vquic_h3_error; + +#ifdef CURLVERBOSE +const char *vquic_h3_err_str(uint64_t error_code); +#else +#define vquic_h3_err_str(x) "" +#endif /* CURLVERBOSE */ + struct cf_quic_ctx { curl_socket_t sockfd; /* connected UDP socket */ struct sockaddr_storage local_addr; /* address socket is bound to */ @@ -52,7 +78,7 @@ }; #define H3_STREAM_CTX(ctx, data) \ - (data ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) + ((data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) CURLcode vquic_ctx_init(struct Curl_easy *data, struct cf_quic_ctx *qctx); @@ -94,8 +120,6 @@ size_t max_pkts, vquic_recv_pkts_cb *recv_cb, void *userp); -#endif /* !USE_HTTP3 */ - #ifdef USE_NGTCP2 struct ngtcp2_mem; struct ngtcp2_mem *Curl_ngtcp2_mem(void); @@ -105,4 +129,6 @@ struct nghttp3_mem *Curl_nghttp3_mem(void); #endif +#endif /* !USE_HTTP3 */ + #endif /* HEADER_CURL_VQUIC_QUIC_INT_H */
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c index 3e6cb3a..44094e4 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -24,7 +24,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_LIBSSH @@ -42,21 +42,21 @@ #include <inet.h> #endif -#include "../urldata.h" -#include "../sendf.h" -#include "../curl_trc.h" -#include "../hostip.h" -#include "../progress.h" -#include "../transfer.h" -#include "ssh.h" -#include "../url.h" -#include "../cfilters.h" -#include "../connect.h" -#include "../parsedate.h" /* for the week day and month names */ -#include "../curlx/strparse.h" -#include "../multiif.h" -#include "../select.h" -#include "vssh.h" +#include "urldata.h" +#include "sendf.h" +#include "curl_trc.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "vssh/ssh.h" +#include "url.h" +#include "cfilters.h" +#include "connect.h" +#include "parsedate.h" /* for the week day and month names */ +#include "curlx/strparse.h" +#include "multiif.h" +#include "select.h" +#include "vssh/vssh.h" #ifdef HAVE_UNISTD_H #include <unistd.h> @@ -84,100 +84,6 @@ #define SSH_S_IFLNK 0120000 #endif -/* Local functions: */ -static CURLcode myssh_connect(struct Curl_easy *data, bool *done); -static CURLcode myssh_multi_statemach(struct Curl_easy *data, - bool *done); -static CURLcode myssh_do_it(struct Curl_easy *data, bool *done); - -static CURLcode scp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode scp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); - -static CURLcode sftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode sftp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode sftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead); -static CURLcode sftp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done); - -static CURLcode myssh_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static void myssh_block2waitfor(struct connectdata *conn, - struct ssh_conn *sshc, - bool block); - -static CURLcode myssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static void sshc_cleanup(struct ssh_conn *sshc); - -/* - * SCP protocol handler. - */ - -const struct Curl_handler Curl_handler_scp = { - "SCP", /* scheme */ - myssh_setup_connection, /* setup_connection */ - myssh_do_it, /* do_it */ - scp_done, /* done */ - ZERO_NULL, /* do_more */ - myssh_connect, /* connect_it */ - myssh_multi_statemach, /* connecting */ - scp_doing, /* doing */ - myssh_pollset, /* proto_pollset */ - myssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - myssh_pollset, /* perform_pollset */ - scp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SSH, /* defport */ - CURLPROTO_SCP, /* protocol */ - CURLPROTO_SCP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE -}; - -/* - * SFTP protocol handler. - */ - -const struct Curl_handler Curl_handler_sftp = { - "SFTP", /* scheme */ - myssh_setup_connection, /* setup_connection */ - myssh_do_it, /* do_it */ - sftp_done, /* done */ - ZERO_NULL, /* do_more */ - myssh_connect, /* connect_it */ - myssh_multi_statemach, /* connecting */ - sftp_doing, /* doing */ - myssh_pollset, /* proto_pollset */ - myssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - myssh_pollset, /* perform_pollset */ - sftp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SSH, /* defport */ - CURLPROTO_SFTP, /* protocol */ - CURLPROTO_SFTP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE -}; - static CURLcode sftp_error_to_CURLE(int err) { switch(err) { @@ -202,101 +108,6 @@ return CURLE_SSH; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static const char *myssh_statename(sshstate state) -{ - static const char * const names[] = { - "SSH_STOP", - "SSH_INIT", - "SSH_S_STARTUP", - "SSH_HOSTKEY", - "SSH_AUTHLIST", - "SSH_AUTH_PKEY_INIT", - "SSH_AUTH_PKEY", - "SSH_AUTH_PASS_INIT", - "SSH_AUTH_PASS", - "SSH_AUTH_AGENT_INIT", - "SSH_AUTH_AGENT_LIST", - "SSH_AUTH_AGENT", - "SSH_AUTH_HOST_INIT", - "SSH_AUTH_HOST", - "SSH_AUTH_KEY_INIT", - "SSH_AUTH_KEY", - "SSH_AUTH_GSSAPI", - "SSH_AUTH_DONE", - "SSH_SFTP_INIT", - "SSH_SFTP_REALPATH", - "SSH_SFTP_QUOTE_INIT", - "SSH_SFTP_POSTQUOTE_INIT", - "SSH_SFTP_QUOTE", - "SSH_SFTP_NEXT_QUOTE", - "SSH_SFTP_QUOTE_STAT", - "SSH_SFTP_QUOTE_SETSTAT", - "SSH_SFTP_QUOTE_SYMLINK", - "SSH_SFTP_QUOTE_MKDIR", - "SSH_SFTP_QUOTE_RENAME", - "SSH_SFTP_QUOTE_RMDIR", - "SSH_SFTP_QUOTE_UNLINK", - "SSH_SFTP_QUOTE_STATVFS", - "SSH_SFTP_GETINFO", - "SSH_SFTP_FILETIME", - "SSH_SFTP_TRANS_INIT", - "SSH_SFTP_UPLOAD_INIT", - "SSH_SFTP_CREATE_DIRS_INIT", - "SSH_SFTP_CREATE_DIRS", - "SSH_SFTP_CREATE_DIRS_MKDIR", - "SSH_SFTP_READDIR_INIT", - "SSH_SFTP_READDIR", - "SSH_SFTP_READDIR_LINK", - "SSH_SFTP_READDIR_BOTTOM", - "SSH_SFTP_READDIR_DONE", - "SSH_SFTP_DOWNLOAD_INIT", - "SSH_SFTP_DOWNLOAD_STAT", - "SSH_SFTP_CLOSE", - "SSH_SFTP_SHUTDOWN", - "SSH_SCP_TRANS_INIT", - "SSH_SCP_UPLOAD_INIT", - "SSH_SCP_DOWNLOAD_INIT", - "SSH_SCP_DOWNLOAD", - "SSH_SCP_DONE", - "SSH_SCP_SEND_EOF", - "SSH_SCP_WAIT_EOF", - "SSH_SCP_WAIT_CLOSE", - "SSH_SCP_CHANNEL_FREE", - "SSH_SESSION_DISCONNECT", - "SSH_SESSION_FREE", - "QUIT" - }; - /* a precaution to make sure the lists are in sync */ - DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST); - return ((size_t)state < CURL_ARRAYSIZE(names)) ? names[state] : ""; -} -#else -#define myssh_statename(x) "" -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ - -#define myssh_to(x, y, z) myssh_set_state(x, y, z) - -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void myssh_set_state(struct Curl_easy *data, - struct ssh_conn *sshc, - sshstate nowstate) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(sshc->state != nowstate) { - CURL_TRC_SSH(data, "[%s] -> [%s]", - myssh_statename(sshc->state), - myssh_statename(nowstate)); - } -#else - (void)data; -#endif - sshc->state = nowstate; -} - /* Multiple options: * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 * hash (90s style auth, not sure we should have it here) @@ -336,18 +147,18 @@ rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen); if(rc != SSH_OK || hlen != 16) { failf(data, - "Denied establishing ssh session: md5 fingerprint not available"); + "Denied establishing ssh session: MD5 fingerprint not available"); goto cleanup; } for(i = 0; i < 16; i++) - curl_msnprintf(&md5buffer[i * 2], 3, "%02x", (unsigned char)hash[i]); + curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash[i]); infof(data, "SSH MD5 fingerprint: %s", md5buffer); if(!curl_strequal(md5buffer, pubkey_md5)) { failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " + "Denied establishing ssh session: mismatch MD5 fingerprint. " "Remote %s is not equal to %s", md5buffer, pubkey_md5); rc = SSH_ERROR; goto cleanup; @@ -546,6 +357,8 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); Curl_pgrsSetDownloadSize(data, -1); if(data->req.no_body) { myssh_to(data, sshc, SSH_STOP); @@ -573,6 +386,8 @@ { CURLcode result = CURLE_OK; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); curlx_dyn_reset(&sshc->readdir_buf); if(sshc->readdir_attrs) sftp_attributes_free(sshc->readdir_attrs); @@ -669,7 +484,7 @@ sshc->readdir_longentry = sshc->readdir_link_attrs->longname; } - Curl_safefree(sshc->readdir_linkPath); + curlx_safefree(sshc->readdir_linkPath); if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s", sshc->readdir_filename)) { /* Not using: @@ -708,8 +523,10 @@ ssh_string_free_char(sshc->readdir_tmp); sshc->readdir_tmp = NULL; - if(result) + if(result) { myssh_to(data, sshc, SSH_STOP); + sshc->actualcode = result; + } else myssh_to(data, sshc, SSH_SFTP_READDIR); return SSH_NO_ERROR; @@ -727,6 +544,19 @@ return SSH_NO_ERROR; } +static void myssh_quote_error(struct Curl_easy *data, struct ssh_conn *sshc, + const char *cmd) +{ + if(cmd) + failf(data, "%s command failed: %s", cmd, + ssh_get_error(sshc->ssh_session)); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; +} + static int myssh_in_SFTP_QUOTE_STATVFS(struct Curl_easy *data, struct ssh_conn *sshc) { @@ -734,20 +564,17 @@ statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); if(!statvfs && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "statvfs command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "statvfs"); return SSH_OK; } else if(statvfs) { - #ifdef _MSC_VER - #define CURL_LIBSSH_VFS_SIZE_MASK "I64u" - #else - #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 - #endif +#ifdef _MSC_VER +#define CURL_LIBSSH_VFS_SIZE_MASK "I64u" +#elif defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 6) +#define CURL_LIBSSH_VFS_SIZE_MASK "llu" +#else +#define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 +#endif CURLcode result = CURLE_OK; char *tmp = curl_maprintf("statvfs:\n" "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" @@ -868,6 +695,22 @@ myssh_to(data, sshc, SSH_S_STARTUP); } +static void myssh_block2waitfor(struct connectdata *conn, + struct ssh_conn *sshc, + bool block) +{ + (void)conn; + if(block) { + int dir = ssh_get_poll_flags(sshc->ssh_session); + /* translate the libssh define bits into our own bit defines */ + sshc->waitfor = + ((dir & SSH_READ_PENDING) ? REQ_IO_RECV : 0) | + ((dir & SSH_WRITE_PENDING) ? REQ_IO_SEND : 0); + } + else + sshc->waitfor = 0; +} + static int myssh_in_S_STARTUP(struct Curl_easy *data, struct ssh_conn *sshc) { @@ -914,13 +757,13 @@ if(sshc->auth_methods) infof(data, "SSH authentication methods available: %s%s%s%s", sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ? - "public key, ": "", + "public key, " : "", sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ? "GSSAPI, " : "", sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ? "keyboard-interactive, " : "", sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ? - "password": ""); + "password" : ""); /* For public key auth we need either the private key or CURLSSH_AUTH_AGENT. */ if((sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) && @@ -1104,7 +947,7 @@ conn->recv_idx = FIRSTSOCKET; conn->send_idx = -1; - if(conn->handler->protocol == CURLPROTO_SFTP) { + if(conn->scheme->protocol == CURLPROTO_SFTP) { myssh_to(data, sshc, SSH_SFTP_INIT); return SSH_NO_ERROR; } @@ -1120,6 +963,9 @@ int flags; int rc = 0; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + if(data->state.resume_from) { sftp_attributes attrs; @@ -1165,10 +1011,11 @@ if(!sshc->sftp_file) { int err = sftp_get_error(sshc->sftp_session); - if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE || - err == SSH_FX_NO_SUCH_PATH)) && - (data->set.ftp_create_missing_dirs && - (strlen(sshp->path) > 1))) { + if((err == SSH_FX_NO_SUCH_FILE || + err == SSH_FX_FAILURE || + err == SSH_FX_NO_SUCH_PATH) && + data->set.ftp_create_missing_dirs && + (strlen(sshp->path) > 1)) { /* try to create the path remotely */ rc = 0; sshc->secondCreateDirs = 1; @@ -1248,7 +1095,7 @@ /* upload data */ Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->recv_idx = FIRSTSOCKET; /* since we do not really wait for anything at this point, we want the @@ -1265,6 +1112,8 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); /* Work on getting the specified file */ if(sshc->sftp_file) sftp_close(sshc->sftp_file); @@ -1293,7 +1142,7 @@ (attrs->size == 0)) { /* * sftp_fstat did not return an error, so maybe the server - * just does not support stat() + * does not support stat() * OR the server does not return a file size with a stat() * OR file size is 0 */ @@ -1333,7 +1182,7 @@ if(data->state.resume_from) { if(data->state.resume_from < 0) { /* We are supposed to download the last abs(from) bytes */ - if((curl_off_t)size < -data->state.resume_from) { + if(size < -data->state.resume_from) { failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, size); return myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); @@ -1342,7 +1191,7 @@ data->state.resume_from += size; } else { - if((curl_off_t)size < data->state.resume_from) { + if(size < data->state.resume_from) { failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, size); @@ -1369,7 +1218,7 @@ } Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; sshc->sftp_recv_state = 0; @@ -1382,17 +1231,19 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); if(sshc->sftp_file) { sftp_close(sshc->sftp_file); sshc->sftp_file = NULL; } - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); CURL_TRC_SSH(data, "SFTP DONE done"); /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT After nextstate is executed, the control should come back to - SSH_SFTP_CLOSE to pass the correct result back */ + SSH_SFTP_CLOSE to pass the correct result back */ if(sshc->nextstate != SSH_NO_STATE && sshc->nextstate != SSH_SFTP_CLOSE) { myssh_to(data, sshc, sshc->nextstate); @@ -1470,8 +1321,8 @@ /* This is the last step in the SFTP connect phase. Do note that while we get the homedir here, we get the "workingpath" in the DO action - since the homedir will remain the same between request but the - working path will not. */ + since the homedir remains the same between request but the + working path does not. */ CURL_TRC_SSH(data, "CONNECT phase done"); myssh_to(data, sshc, SSH_STOP); return SSH_NO_ERROR; @@ -1481,7 +1332,12 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { - CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + CURLcode result; + + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); if(result) { sshc->actualcode = result; myssh_to(data, sshc, SSH_STOP); @@ -1510,12 +1366,12 @@ return SSH_NO_ERROR; } -static int return_quote_error(struct Curl_easy *data, - struct ssh_conn *sshc) +static int quote_error(struct Curl_easy *data, + struct ssh_conn *sshc) { failf(data, "Suspicious data after the command line"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1532,12 +1388,15 @@ /* * Support some of the "FTP" commands */ - char *cmd = sshc->quote_item->data; + const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1624,14 +1483,14 @@ else failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: " "Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); sshc->quote_attrs = NULL; myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT); return SSH_NO_ERROR; @@ -1647,20 +1506,20 @@ failf(data, "Out of memory"); else failf(data, "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK); return SSH_NO_ERROR; } else if(!strncmp(cmd, "mkdir ", 6)) { if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); /* create directory */ myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR); return SSH_NO_ERROR; @@ -1675,42 +1534,42 @@ failf(data, "Out of memory"); else failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME); return SSH_NO_ERROR; } else if(!strncmp(cmd, "rmdir ", 6)) { /* delete directory */ if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR); return SSH_NO_ERROR; } else if(!strncmp(cmd, "rm ", 3)) { if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK); return SSH_NO_ERROR; } #ifdef HAS_STATVFS_SUPPORT else if(!strncmp(cmd, "statvfs ", 8)) { if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS); return SSH_NO_ERROR; } #endif failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1720,8 +1579,8 @@ static int myssh_in_SFTP_NEXT_QUOTE(struct Curl_easy *data, struct ssh_conn *sshc) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); sshc->quote_item = sshc->quote_item->next; @@ -1743,12 +1602,12 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, struct ssh_conn *sshc) { - char *cmd = sshc->quote_item->data; + const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1765,13 +1624,9 @@ sftp_attributes_free(sshc->quote_attrs); sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); if(!sshc->quote_attrs) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Attempt to get SFTP stats failed: %d", sftp_get_error(sshc->sftp_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } @@ -1780,12 +1635,8 @@ const char *p = sshc->quote_path1; curl_off_t gid; if(curlx_str_number(&p, &gid, UINT_MAX)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Syntax error: chgrp gid not a number"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->gid = (uint32_t)gid; @@ -1795,12 +1646,8 @@ curl_off_t perms; const char *p = sshc->quote_path1; if(curlx_str_octal(&p, &perms, 07777)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Syntax error: chmod permissions not a number"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->permissions = (mode_t)perms; @@ -1810,12 +1657,8 @@ const char *p = sshc->quote_path1; curl_off_t uid; if(curlx_str_number(&p, &uid, UINT_MAX)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Syntax error: chown uid not a number"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->uid = (uint32_t)uid; @@ -1836,16 +1679,9 @@ } #endif if(fail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } - if(date > UINT_MAX) - /* because the liubssh API cannot deal with a larger value */ - date = UINT_MAX; if(!strncmp(cmd, "atime", 5)) sshc->quote_attrs->atime = (uint32_t)date; else /* mtime */ @@ -1871,21 +1707,491 @@ } } +static void myssh_SESSION_DISCONNECT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + /* during weird times when we have been prematurely aborted, the channel + is still alive when we reach this state and we MUST kill the channel + properly first */ + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + if(sshc->sftp_session) { + sftp_free(sshc->sftp_session); + sshc->sftp_session = NULL; + } + + ssh_disconnect(sshc->ssh_session); + if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { + /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, + tell the connection to forget about it. This libssh + bug is fixed in 0.10.0. */ + conn_forget_socket(data, FIRSTSOCKET); + } + + SSH_STRING_FREE_CHAR(sshc->homedir); + + myssh_to(data, sshc, SSH_SESSION_FREE); +} + +static int myssh_SSH_SCP_DOWNLOAD(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + curl_off_t bytecount; + int rc = ssh_scp_pull_request(sshc->scp_session); + if(rc != SSH_SCP_REQUEST_NEWFILE) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND); + } + + /* download data */ + bytecount = ssh_scp_request_get_size(sshc->scp_session); + data->req.maxdownload = bytecount; + Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); + + /* not set by Curl_xfer_setup to preserve io_flags */ + data->conn->send_idx = 0; + + myssh_to(data, sshc, SSH_STOP); + return 0; +} + +static int myssh_in_TRANS_INIT(struct Curl_easy *data, struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + int rc = 0; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + if(result) { + sshc->actualcode = result; + myssh_to(data, sshc, SSH_STOP); + return 0; + } + + /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ + ssh_set_blocking(sshc->ssh_session, 1); + + if(data->state.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path); + myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); + } + else { + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path); + myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); + } + + if(!sshc->scp_session) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + return rc; +} + +static void sshc_cleanup(struct ssh_conn *sshc) +{ + if(sshc->initialised) { + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + if(sshc->sftp_session) { + sftp_free(sshc->sftp_session); + sshc->sftp_session = NULL; + } + if(sshc->ssh_session) { + ssh_free(sshc->ssh_session); + sshc->ssh_session = NULL; + } + + /* worst-case scenario cleanup */ + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->scp_session == NULL); + + if(sshc->readdir_tmp) { + ssh_string_free_char(sshc->readdir_tmp); + sshc->readdir_tmp = NULL; + } + if(sshc->quote_attrs) { + sftp_attributes_free(sshc->quote_attrs); + sshc->quote_attrs = NULL; + } + if(sshc->readdir_attrs) { + sftp_attributes_free(sshc->readdir_attrs); + sshc->readdir_attrs = NULL; + } + if(sshc->readdir_link_attrs) { + sftp_attributes_free(sshc->readdir_link_attrs); + sshc->readdir_link_attrs = NULL; + } + if(sshc->privkey) { + ssh_key_free(sshc->privkey); + sshc->privkey = NULL; + } + if(sshc->pubkey) { + ssh_key_free(sshc->pubkey); + sshc->pubkey = NULL; + } + + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + curlx_dyn_free(&sshc->readdir_buf); + curlx_safefree(sshc->readdir_linkPath); + SSH_STRING_FREE_CHAR(sshc->homedir); + sshc->initialised = FALSE; + } +} + +static int myssh_in_SFTP_QUOTE_SETSTAT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, + sshc->quote_attrs); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "setstat"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_SYMLINK(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, + sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "symlink"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_MKDIR(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, + (mode_t)data->set.new_directory_perms); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "mkdir"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_RENAME(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, + sshc->quote_path2); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rename"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_RMDIR(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rmdir"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_UNLINK(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rm"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_GETINFO(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->set.get_filetime) + myssh_to(data, sshc, SSH_SFTP_FILETIME); + else + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_FILETIME(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + sftp_attributes attrs; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + attrs = sftp_stat(sshc->sftp_session, sshp->path); + if(attrs) { + data->info.filetime = attrs->mtime; + sftp_attributes_free(attrs); + } + + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_TRANS_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + if(data->state.upload) + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + else { + size_t path_len = strlen(sshp->path); + + if(path_len && sshp->path[path_len - 1] == '/') + myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); + else + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); + } + + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + if(strlen(sshp->path) > 1) { + sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + } + else { + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + } + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + + infof(data, "Creating directory '%s'", sshp->path); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); + return SSH_NO_ERROR; + } + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS_MKDIR(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + int rc; + int err; + /* 'mode' - parameter is preliminary - default to 0644 */ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + rc = sftp_mkdir(sshc->sftp_session, sshp->path, + (mode_t)data->set.new_directory_perms); + if(rc == SSH_AGAIN) + return rc; + *sshc->slash_pos = '/'; + ++sshc->slash_pos; + if(rc < 0) { + /* + * Abort if failure was not that the directory already + * exists or the permission was denied (creation might + * succeed further down the path) - retry on unspecific + * FAILURE also + */ + err = sftp_get_error(sshc->sftp_session); + if((err != SSH_FX_FILE_ALREADY_EXISTS) && + (err != SSH_FX_FAILURE) && + (err != SSH_FX_PERMISSION_DENIED)) { + return myssh_to_SFTP_CLOSE(data, sshc); + } + rc = 0; /* clear rc and continue */ + } + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + return rc; +} + +static int myssh_in_SCP_UPLOAD_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + int rc; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + rc = ssh_scp_push_file64(sshc->scp_session, sshp->path, + (uint64_t)data->state.infilesize, + (int)data->set.new_file_perms); + + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + /* upload data */ + Curl_xfer_setup_send(data, FIRSTSOCKET); + + /* not set by Curl_xfer_setup to preserve io_flags */ + data->conn->recv_idx = FIRSTSOCKET; + + myssh_to(data, sshc, SSH_STOP); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_DOWNLOAD_INIT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, + CURLE_COULDNT_CONNECT); + } + myssh_to(data, sshc, SSH_SCP_DOWNLOAD); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_DONE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SCP_SEND_EOF); + else + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_SEND_EOF(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->scp_session) { + int rc = ssh_scp_close(sshc->scp_session); + if(rc == SSH_AGAIN) { + /* Currently the ssh_scp_close handles waiting for + * EOF in blocking way. + */ + return SSH_AGAIN; + } + if(rc != SSH_OK) { + infof(data, + "Failed to close libssh scp channel: %s", + ssh_get_error(sshc->ssh_session)); + } + } + + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_CHANNEL_FREE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + CURL_TRC_SSH(data, "SCP DONE phase complete"); + + ssh_set_blocking(sshc->ssh_session, 0); + + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); + return SSH_NO_ERROR; +} + +static CURLcode myssh_in_SESSION_FREE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + CURLcode result; + sshc_cleanup(sshc); + /* the code we are about to return */ + result = sshc->actualcode; + memset(sshc, 0, sizeof(struct ssh_conn)); + connclose(data->conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + sshc->nextstate = SSH_NO_STATE; + myssh_to(data, sshc, SSH_STOP); + return result; +} + /* - * ssh_statemach_act() runs the SSH state machine as far as it can without + * myssh_statemachine() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points - * to will be set to TRUE if the libssh function returns SSH_AGAIN + * to is set to TRUE if the libssh function returns SSH_AGAIN * meaning it wants to be called again when the socket is ready */ -static CURLcode myssh_statemach_act(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp, - bool *block) +static CURLcode myssh_statemachine(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - int rc = SSH_NO_ERROR, err; - const char *err_msg; + int rc = SSH_NO_ERROR; *block = FALSE; /* we are not blocking by default */ do { @@ -1941,15 +2247,13 @@ rc = myssh_in_SFTP_REALPATH(data, sshc); break; case SSH_SFTP_QUOTE_INIT: - rc = sshp ? myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp); break; case SSH_SFTP_POSTQUOTE_INIT: rc = myssh_in_SFTP_POSTQUOTE_INIT(data, sshc); break; case SSH_SFTP_QUOTE: - rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_QUOTE(data, sshc, sshp); break; case SSH_SFTP_NEXT_QUOTE: rc = myssh_in_SFTP_NEXT_QUOTE(data, sshc); @@ -1957,224 +2261,53 @@ case SSH_SFTP_QUOTE_STAT: rc = myssh_in_SFTP_QUOTE_STAT(data, sshc); break; - case SSH_SFTP_QUOTE_SETSTAT: - rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, - sshc->quote_attrs); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to set SFTP stats failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - /* sshc->actualcode = sftp_error_to_CURLE(err); - * we do not send the actual error; we return - * the error the libssh2 backend is returning */ - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_SETSTAT(data, sshc); break; - case SSH_SFTP_QUOTE_SYMLINK: - rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, - sshc->quote_path1); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "symlink command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_SYMLINK(data, sshc); break; - case SSH_SFTP_QUOTE_MKDIR: - rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, - (mode_t)data->set.new_directory_perms); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "mkdir command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_MKDIR(data, sshc); break; - case SSH_SFTP_QUOTE_RENAME: - rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, - sshc->quote_path2); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "rename command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_RENAME(data, sshc); break; - case SSH_SFTP_QUOTE_RMDIR: - rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "rmdir command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_RMDIR(data, sshc); break; - case SSH_SFTP_QUOTE_UNLINK: - rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "rm command failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_UNLINK(data, sshc); break; - case SSH_SFTP_QUOTE_STATVFS: rc = myssh_in_SFTP_QUOTE_STATVFS(data, sshc); break; - case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - myssh_to(data, sshc, SSH_SFTP_FILETIME); - } - else { - myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); - } + rc = myssh_in_SFTP_GETINFO(data, sshc); break; - - case SSH_SFTP_FILETIME: { - sftp_attributes attrs; - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - attrs = sftp_stat(sshc->sftp_session, sshp->path); - if(attrs) { - data->info.filetime = attrs->mtime; - sftp_attributes_free(attrs); - } - - myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + case SSH_SFTP_FILETIME: + rc = myssh_in_SFTP_FILETIME(data, sshc, sshp); break; - } - case SSH_SFTP_TRANS_INIT: - if(data->state.upload) - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); - else if(sshp) { - if(sshp->path[strlen(sshp->path) - 1] == '/') - myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); - else - myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); - } - else - result = CURLE_FAILED_INIT; + rc = myssh_in_SFTP_TRANS_INIT(data, sshc, sshp); break; - case SSH_SFTP_UPLOAD_INIT: - rc = sshp ? myssh_in_UPLOAD_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_UPLOAD_INIT(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS_INIT: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - else if(strlen(sshp->path) > 1) { - sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); - } - else { - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); - } + rc = myssh_in_SFTP_CREATE_DIRS_INIT(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS: - sshc->slash_pos = strchr(sshc->slash_pos, '/'); - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - else if(sshc->slash_pos) { - *sshc->slash_pos = 0; - - infof(data, "Creating directory '%s'", sshp->path); - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); - break; - } - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + rc = myssh_in_SFTP_CREATE_DIRS(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS_MKDIR: - /* 'mode' - parameter is preliminary - default to 0644 */ - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - rc = sftp_mkdir(sshc->sftp_session, sshp->path, - (mode_t)data->set.new_directory_perms); - *sshc->slash_pos = '/'; - ++sshc->slash_pos; - if(rc < 0) { - /* - * Abort if failure was not that the directory already exists or - * the permission was denied (creation might succeed further down - * the path) - retry on unspecific FAILURE also - */ - err = sftp_get_error(sshc->sftp_session); - if((err != SSH_FX_FILE_ALREADY_EXISTS) && - (err != SSH_FX_FAILURE) && - (err != SSH_FX_PERMISSION_DENIED)) { - rc = myssh_to_SFTP_CLOSE(data, sshc); - break; - } - rc = 0; /* clear rc and continue */ - } - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + rc = myssh_in_SFTP_CREATE_DIRS_MKDIR(data, sshc, sshp); break; - case SSH_SFTP_READDIR_INIT: - rc = sshp ? myssh_in_SFTP_READDIR_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_READDIR_INIT(data, sshc, sshp); break; case SSH_SFTP_READDIR: - rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_READDIR(data, sshc, sshp); break; case SSH_SFTP_READDIR_LINK: rc = myssh_in_SFTP_READDIR_LINK(data, sshc); @@ -2186,206 +2319,46 @@ rc = myssh_in_SFTP_READDIR_DONE(data, sshc); break; case SSH_SFTP_DOWNLOAD_INIT: - rc = sshp ? myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp); break; case SSH_SFTP_DOWNLOAD_STAT: rc = myssh_in_SFTP_DOWNLOAD_STAT(data, sshc); break; case SSH_SFTP_CLOSE: - rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_CLOSE(data, sshc, sshp); break; case SSH_SFTP_SHUTDOWN: rc = myssh_in_SFTP_SHUTDOWN(data, sshc); break; - case SSH_SCP_TRANS_INIT: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); - if(result) { - sshc->actualcode = result; - myssh_to(data, sshc, SSH_STOP); - break; - } - - /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ - ssh_set_blocking(sshc->ssh_session, 1); - - if(data->state.upload) { - if(data->state.infilesize < 0) { - failf(data, "SCP requires a known file size for upload"); - sshc->actualcode = CURLE_UPLOAD_FAILED; - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - sshc->scp_session = - ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path); - myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); - } - else { - sshc->scp_session = - ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path); - myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); - } - - if(!sshc->scp_session) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - } + rc = myssh_in_TRANS_INIT(data, sshc, sshp); break; - case SSH_SCP_UPLOAD_INIT: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - rc = ssh_scp_init(sshc->scp_session); - if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - rc = ssh_scp_push_file64(sshc->scp_session, sshp->path, - (uint64_t)data->state.infilesize, - (int)data->set.new_file_perms); - - if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - /* upload data */ - Curl_xfer_setup_send(data, FIRSTSOCKET); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - data->conn->recv_idx = FIRSTSOCKET; - - myssh_to(data, sshc, SSH_STOP); - + rc = myssh_in_SCP_UPLOAD_INIT(data, sshc, sshp); break; - case SSH_SCP_DOWNLOAD_INIT: - - rc = ssh_scp_init(sshc->scp_session); - if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); + rc = myssh_in_SCP_DOWNLOAD_INIT(data, sshc); + if(rc) break; - } - myssh_to(data, sshc, SSH_SCP_DOWNLOAD); FALLTHROUGH(); - - case SSH_SCP_DOWNLOAD: { - curl_off_t bytecount; - - rc = ssh_scp_pull_request(sshc->scp_session); - if(rc != SSH_SCP_REQUEST_NEWFILE) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND); - break; - } - - /* download data */ - bytecount = ssh_scp_request_get_size(sshc->scp_session); - data->req.maxdownload = (curl_off_t)bytecount; - Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - conn->send_idx = 0; - - myssh_to(data, sshc, SSH_STOP); + case SSH_SCP_DOWNLOAD: + rc = myssh_SSH_SCP_DOWNLOAD(data, sshc); break; - } case SSH_SCP_DONE: - if(data->state.upload) - myssh_to(data, sshc, SSH_SCP_SEND_EOF); - else - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + rc = myssh_in_SCP_DONE(data, sshc); break; - case SSH_SCP_SEND_EOF: - if(sshc->scp_session) { - rc = ssh_scp_close(sshc->scp_session); - if(rc == SSH_AGAIN) { - /* Currently the ssh_scp_close handles waiting for EOF in - * blocking way. - */ - break; - } - if(rc != SSH_OK) { - infof(data, "Failed to close libssh scp channel: %s", - ssh_get_error(sshc->ssh_session)); - } - } - - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + rc = myssh_in_SCP_SEND_EOF(data, sshc); break; - case SSH_SCP_CHANNEL_FREE: - if(sshc->scp_session) { - ssh_scp_free(sshc->scp_session); - sshc->scp_session = NULL; - } - CURL_TRC_SSH(data, "SCP DONE phase complete"); - - ssh_set_blocking(sshc->ssh_session, 0); - - myssh_to(data, sshc, SSH_SESSION_DISCONNECT); + myssh_in_SCP_CHANNEL_FREE(data, sshc); FALLTHROUGH(); - case SSH_SESSION_DISCONNECT: - /* during weird times when we have been prematurely aborted, the channel - is still alive when we reach this state and we MUST kill the channel - properly first */ - if(sshc->scp_session) { - ssh_scp_free(sshc->scp_session); - sshc->scp_session = NULL; - } - - if(sshc->sftp_file) { - sftp_close(sshc->sftp_file); - sshc->sftp_file = NULL; - } - if(sshc->sftp_session) { - sftp_free(sshc->sftp_session); - sshc->sftp_session = NULL; - } - - ssh_disconnect(sshc->ssh_session); - if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { - /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, - tell the connection to forget about it. This libssh - bug is fixed in 0.10.0. */ - conn_forget_socket(data, FIRSTSOCKET); - } - - SSH_STRING_FREE_CHAR(sshc->homedir); - - myssh_to(data, sshc, SSH_SESSION_FREE); + myssh_SESSION_DISCONNECT(data, sshc); FALLTHROUGH(); case SSH_SESSION_FREE: - sshc_cleanup(sshc); - /* the code we are about to return */ - result = sshc->actualcode; - memset(sshc, 0, sizeof(struct ssh_conn)); - connclose(conn, "SSH session free"); - sshc->state = SSH_SESSION_FREE; /* current */ - sshc->nextstate = SSH_NO_STATE; - myssh_to(data, sshc, SSH_STOP); + result = myssh_in_SESSION_FREE(data, sshc); break; - case SSH_QUIT: default: /* internal error */ @@ -2405,7 +2378,7 @@ if(!result && (sshc->state == SSH_STOP)) result = sshc->actualcode; CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - myssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), result, *block); return result; } @@ -2422,12 +2395,12 @@ if(!sshc || (sock == CURL_SOCKET_BAD)) return CURLE_FAILED_INIT; - waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon; + waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; if(waitfor) { int flags = 0; - if(waitfor & KEEP_RECV) + if(waitfor & REQ_IO_RECV) flags |= CURL_POLL_IN; - if(waitfor & KEEP_SEND) + if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); CURL_TRC_SSH(data, "pollset, flags=%x", flags); @@ -2439,22 +2412,6 @@ return CURLE_OK; } -static void myssh_block2waitfor(struct connectdata *conn, - struct ssh_conn *sshc, - bool block) -{ - (void)conn; - if(block) { - int dir = ssh_get_poll_flags(sshc->ssh_session); - /* translate the libssh define bits into our own bit defines */ - sshc->waitfor = - ((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) | - ((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0); - } - else - sshc->waitfor = 0; -} - /* called repeatedly until done from multi.c */ static CURLcode myssh_multi_statemach(struct Curl_easy *data, bool *done) @@ -2468,7 +2425,7 @@ if(!sshc || !sshp) return CURLE_FAILED_INIT; - result = myssh_statemach_act(data, sshc, sshp, &block); + result = myssh_statemachine(data, sshc, sshp, &block); *done = (sshc->state == SSH_STOP); myssh_block2waitfor(conn, sshc, block); @@ -2487,7 +2444,7 @@ bool block; timediff_t left_ms = 1000; - result = myssh_statemach_act(data, sshc, sshp, &block); + result = myssh_statemachine(data, sshc, sshp, &block); if(result) break; @@ -2496,7 +2453,7 @@ if(result) break; - left_ms = Curl_timeleft_ms(data, FALSE); + left_ms = Curl_timeleft_ms(data); if(left_ms < 0) { failf(data, "Operation timed out"); return CURLE_OPERATION_TIMEDOUT; @@ -2518,7 +2475,7 @@ struct SSHPROTO *sshp = entry; (void)key; (void)klen; - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); curlx_free(sshp); } @@ -2577,7 +2534,7 @@ return CURLE_FAILED_INIT; CURL_TRC_SSH(data, "myssh_connect"); - if(conn->handler->protocol & CURLPROTO_SCP) { + if(conn->scheme->protocol & CURLPROTO_SCP) { conn->recv[FIRSTSOCKET] = scp_recv; conn->send[FIRSTSOCKET] = scp_send; } @@ -2592,13 +2549,9 @@ return CURLE_FAILED_INIT; } - if(conn->bits.ipv6_ip) { - char ipv6[MAX_IPADR_LEN]; - curl_msnprintf(ipv6, sizeof(ipv6), "[%s]", conn->host.name); - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, ipv6); - } - else - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->host.name); + rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, + (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name); if(rc != SSH_OK) { failf(data, "Could not set remote host"); @@ -2730,89 +2683,6 @@ return result; } -static CURLcode myssh_do_it(struct Curl_easy *data, bool *done) -{ - CURLcode result; - bool connected = FALSE; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - - *done = FALSE; /* default to false */ - if(!sshc) - return CURLE_FAILED_INIT; - - data->req.size = -1; /* make sure this is unknown at this point */ - - sshc->actualcode = CURLE_OK; /* reset error code */ - sshc->secondCreateDirs = 0; /* reset the create directory attempt state - variable */ - - Curl_pgrsReset(data); - - if(conn->handler->protocol & CURLPROTO_SCP) - result = scp_perform(data, &connected, done); - else - result = sftp_perform(data, &connected, done); - - return result; -} - -static void sshc_cleanup(struct ssh_conn *sshc) -{ - if(sshc->initialised) { - if(sshc->sftp_file) { - sftp_close(sshc->sftp_file); - sshc->sftp_file = NULL; - } - if(sshc->sftp_session) { - sftp_free(sshc->sftp_session); - sshc->sftp_session = NULL; - } - if(sshc->ssh_session) { - ssh_free(sshc->ssh_session); - sshc->ssh_session = NULL; - } - - /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->scp_session == NULL); - - if(sshc->readdir_tmp) { - ssh_string_free_char(sshc->readdir_tmp); - sshc->readdir_tmp = NULL; - } - if(sshc->quote_attrs) { - sftp_attributes_free(sshc->quote_attrs); - sshc->quote_attrs = NULL; - } - if(sshc->readdir_attrs) { - sftp_attributes_free(sshc->readdir_attrs); - sshc->readdir_attrs = NULL; - } - if(sshc->readdir_link_attrs) { - sftp_attributes_free(sshc->readdir_link_attrs); - sshc->readdir_link_attrs = NULL; - } - if(sshc->privkey) { - ssh_key_free(sshc->privkey); - sshc->privkey = NULL; - } - if(sshc->pubkey) { - ssh_key_free(sshc->pubkey); - sshc->pubkey = NULL; - } - - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - curlx_dyn_free(&sshc->readdir_buf); - Curl_safefree(sshc->readdir_linkPath); - SSH_STRING_FREE_CHAR(sshc->homedir); - sshc->initialised = FALSE; - } -} - /* BLOCKING, but the function is using the state machine so the only reason this is still blocking is that the multi interface code has no support for disconnecting operations that takes a while */ @@ -2853,7 +2723,7 @@ if(Curl_pgrsDone(data)) return CURLE_ABORTED_BY_CALLBACK; - data->req.keepon = 0; /* clear all bits */ + CURL_REQ_CLEAR_IO(data); return result; } @@ -2890,7 +2760,7 @@ #if 0 /* The following code is misleading, mostly added as wishful thinking - * that libssh at some point will implement non-blocking ssh_scp_write/read. + * that libssh at some point would implement non-blocking ssh_scp_write/read. * Currently rc can only be number of bytes read or SSH_ERROR. */ myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN)); @@ -2924,7 +2794,7 @@ return CURLE_SSH; #if 0 /* The following code is misleading, mostly added as wishful thinking - * that libssh at some point will implement non-blocking ssh_scp_write/read. + * that libssh at some point would implement non-blocking ssh_scp_write/read. * Currently rc can only be SSH_OK or SSH_ERROR. */ myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN)); @@ -3174,6 +3044,33 @@ } } +static CURLcode myssh_do_it(struct Curl_easy *data, bool *done) +{ + CURLcode result; + bool connected = FALSE; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + + *done = FALSE; /* default to false */ + if(!sshc) + return CURLE_FAILED_INIT; + + data->req.size = -1; /* make sure this is unknown at this point */ + + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs = 0; /* reset the create directory attempt state + variable */ + + Curl_pgrsReset(data); + + if(conn->scheme->protocol & CURLPROTO_SCP) + result = scp_perform(data, &connected, done); + else + result = sftp_perform(data, &connected, done); + + return result; +} + CURLcode Curl_ssh_init(void) { if(ssh_init()) { @@ -3193,4 +3090,50 @@ (void)curl_msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); } +/* + * SCP. + */ +const struct Curl_protocol Curl_protocol_scp = { + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + myssh_pollset, /* proto_pollset */ + myssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + myssh_pollset, /* perform_pollset */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +/* + * SFTP. + */ +const struct Curl_protocol Curl_protocol_sftp = { + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + sftp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + sftp_doing, /* doing */ + myssh_pollset, /* proto_pollset */ + myssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + myssh_pollset, /* perform_pollset */ + sftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* USE_LIBSSH */
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index 5b2b337..4e2a722 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_LIBSSH2 @@ -41,110 +41,89 @@ #include <inet.h> #endif -#include "../urldata.h" -#include "../sendf.h" -#include "../curl_trc.h" -#include "../hostip.h" -#include "../progress.h" -#include "../transfer.h" -#include "ssh.h" -#include "../url.h" -#include "../cfilters.h" -#include "../connect.h" -#include "../parsedate.h" /* for the week day and month names */ -#include "../multiif.h" -#include "../select.h" -#include "../curlx/fopen.h" -#include "vssh.h" -#include "../curlx/strparse.h" -#include "../curlx/base64.h" /* for base64 encoding/decoding */ +#include "urldata.h" +#include "sendf.h" +#include "curl_trc.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "vssh/ssh.h" +#include "url.h" +#include "cfilters.h" +#include "connect.h" +#include "parsedate.h" /* for the week day and month names */ +#include "multiif.h" +#include "select.h" +#include "curlx/fopen.h" +#include "vssh/vssh.h" +#include "curlx/strparse.h" +#include "curlx/base64.h" /* for base64 encoding/decoding */ -/* Local functions: */ -static const char *sftp_libssh2_strerror(unsigned long err); -static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); -static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); -static LIBSSH2_FREE_FUNC(my_libssh2_free); -static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, - struct ssh_conn *sshc); -static CURLcode ssh_connect(struct Curl_easy *data, bool *done); -static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode ssh_do(struct Curl_easy *data, bool *done); -static CURLcode scp_done(struct Curl_easy *data, CURLcode c, bool premature); -static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode scp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection); -static CURLcode sftp_done(struct Curl_easy *data, CURLcode, bool premature); -static CURLcode sftp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode sftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode sftp_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done); -static CURLcode ssh_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode ssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static void ssh_attach(struct Curl_easy *data, struct connectdata *conn); -static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, - bool block); -/* - * SCP protocol handler. - */ +static const char *sftp_libssh2_strerror(unsigned long err) +{ + switch(err) { + case LIBSSH2_FX_NO_SUCH_FILE: + return "No such file or directory"; -const struct Curl_handler Curl_handler_scp = { - "SCP", /* scheme */ - ssh_setup_connection, /* setup_connection */ - ssh_do, /* do_it */ - scp_done, /* done */ - ZERO_NULL, /* do_more */ - ssh_connect, /* connect_it */ - ssh_multi_statemach, /* connecting */ - scp_doing, /* doing */ - ssh_pollset, /* proto_pollset */ - ssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ssh_pollset, /* perform_pollset */ - scp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ssh_attach, /* attach */ - ZERO_NULL, /* follow */ - PORT_SSH, /* defport */ - CURLPROTO_SCP, /* protocol */ - CURLPROTO_SCP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE -}; + case LIBSSH2_FX_PERMISSION_DENIED: + return "Permission denied"; -/* - * SFTP protocol handler. - */ + case LIBSSH2_FX_FAILURE: + return "Operation failed"; -const struct Curl_handler Curl_handler_sftp = { - "SFTP", /* scheme */ - ssh_setup_connection, /* setup_connection */ - ssh_do, /* do_it */ - sftp_done, /* done */ - ZERO_NULL, /* do_more */ - ssh_connect, /* connect_it */ - ssh_multi_statemach, /* connecting */ - sftp_doing, /* doing */ - ssh_pollset, /* proto_pollset */ - ssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ssh_pollset, /* perform_pollset */ - sftp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ssh_attach, /* attach */ - ZERO_NULL, /* follow */ - PORT_SSH, /* defport */ - CURLPROTO_SFTP, /* protocol */ - CURLPROTO_SFTP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE -}; + case LIBSSH2_FX_BAD_MESSAGE: + return "Bad message from SFTP server"; + + case LIBSSH2_FX_NO_CONNECTION: + return "Not connected to SFTP server"; + + case LIBSSH2_FX_CONNECTION_LOST: + return "Connection to SFTP server lost"; + + case LIBSSH2_FX_OP_UNSUPPORTED: + return "Operation not supported by SFTP server"; + + case LIBSSH2_FX_INVALID_HANDLE: + return "Invalid handle"; + + case LIBSSH2_FX_NO_SUCH_PATH: + return "No such file or directory"; + + case LIBSSH2_FX_FILE_ALREADY_EXISTS: + return "File already exists"; + + case LIBSSH2_FX_WRITE_PROTECT: + return "File is write protected"; + + case LIBSSH2_FX_NO_MEDIA: + return "No media"; + + case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: + return "Disk full"; + + case LIBSSH2_FX_QUOTA_EXCEEDED: + return "User quota exceeded"; + + case LIBSSH2_FX_UNKNOWN_PRINCIPLE: + return "Unknown principle"; + + case LIBSSH2_FX_LOCK_CONFlICT: + return "File lock conflict"; + + case LIBSSH2_FX_DIR_NOT_EMPTY: + return "Directory not empty"; + + case LIBSSH2_FX_NOT_A_DIRECTORY: + return "Not a directory"; + + case LIBSSH2_FX_INVALID_FILENAME: + return "Invalid filename"; + + case LIBSSH2_FX_LINK_LOOP: + return "Link points to itself"; + } + return "Unknown error in libssh2"; +} static void kbd_callback(const char *name, int name_len, const char *instruction, int instruction_len, @@ -169,7 +148,9 @@ #endif /* CURL_LIBSSH2_DEBUG */ if(num_prompts == 1) { struct connectdata *conn = data->conn; - responses[0].text = curlx_strdup(conn->passwd); + /* this function must allocate memory that can be freed by libssh2, which + uses the LIBSSH2_FREE_FUNC callback */ + responses[0].text = Curl_cstrdup(conn->passwd); responses[0].length = responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); } @@ -272,101 +253,6 @@ Curl_cfree(ptr); } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static const char *myssh_statename(sshstate state) -{ - static const char * const names[] = { - "SSH_STOP", - "SSH_INIT", - "SSH_S_STARTUP", - "SSH_HOSTKEY", - "SSH_AUTHLIST", - "SSH_AUTH_PKEY_INIT", - "SSH_AUTH_PKEY", - "SSH_AUTH_PASS_INIT", - "SSH_AUTH_PASS", - "SSH_AUTH_AGENT_INIT", - "SSH_AUTH_AGENT_LIST", - "SSH_AUTH_AGENT", - "SSH_AUTH_HOST_INIT", - "SSH_AUTH_HOST", - "SSH_AUTH_KEY_INIT", - "SSH_AUTH_KEY", - "SSH_AUTH_GSSAPI", - "SSH_AUTH_DONE", - "SSH_SFTP_INIT", - "SSH_SFTP_REALPATH", - "SSH_SFTP_QUOTE_INIT", - "SSH_SFTP_POSTQUOTE_INIT", - "SSH_SFTP_QUOTE", - "SSH_SFTP_NEXT_QUOTE", - "SSH_SFTP_QUOTE_STAT", - "SSH_SFTP_QUOTE_SETSTAT", - "SSH_SFTP_QUOTE_SYMLINK", - "SSH_SFTP_QUOTE_MKDIR", - "SSH_SFTP_QUOTE_RENAME", - "SSH_SFTP_QUOTE_RMDIR", - "SSH_SFTP_QUOTE_UNLINK", - "SSH_SFTP_QUOTE_STATVFS", - "SSH_SFTP_GETINFO", - "SSH_SFTP_FILETIME", - "SSH_SFTP_TRANS_INIT", - "SSH_SFTP_UPLOAD_INIT", - "SSH_SFTP_CREATE_DIRS_INIT", - "SSH_SFTP_CREATE_DIRS", - "SSH_SFTP_CREATE_DIRS_MKDIR", - "SSH_SFTP_READDIR_INIT", - "SSH_SFTP_READDIR", - "SSH_SFTP_READDIR_LINK", - "SSH_SFTP_READDIR_BOTTOM", - "SSH_SFTP_READDIR_DONE", - "SSH_SFTP_DOWNLOAD_INIT", - "SSH_SFTP_DOWNLOAD_STAT", - "SSH_SFTP_CLOSE", - "SSH_SFTP_SHUTDOWN", - "SSH_SCP_TRANS_INIT", - "SSH_SCP_UPLOAD_INIT", - "SSH_SCP_DOWNLOAD_INIT", - "SSH_SCP_DOWNLOAD", - "SSH_SCP_DONE", - "SSH_SCP_SEND_EOF", - "SSH_SCP_WAIT_EOF", - "SSH_SCP_WAIT_CLOSE", - "SSH_SCP_CHANNEL_FREE", - "SSH_SESSION_DISCONNECT", - "SSH_SESSION_FREE", - "QUIT" - }; - /* a precaution to make sure the lists are in sync */ - DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST); - return ((size_t)state < CURL_ARRAYSIZE(names)) ? names[state] : ""; -} -#else -#define myssh_statename(x) "" -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ - -#define myssh_state(x, y, z) myssh_set_state(x, y, z) - -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void myssh_set_state(struct Curl_easy *data, - struct ssh_conn *sshc, - sshstate nowstate) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(sshc->state != nowstate) { - CURL_TRC_SSH(data, "[%s] -> [%s]", - myssh_statename(sshc->state), - myssh_statename(nowstate)); - } -#else - (void)data; -#endif - sshc->state = nowstate; -} - static int sshkeycallback(CURL *easy, const struct curl_khkey *knownkey, /* known */ const struct curl_khkey *foundkey, /* found */ @@ -521,9 +407,9 @@ rc = CURLKHSTAT_REJECT; switch(rc) { - default: /* unknown return codes will equal reject */ + default: /* unknown return codes is the same as reject */ case CURLKHSTAT_REJECT: - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); FALLTHROUGH(); case CURLKHSTAT_DEFER: /* DEFER means bail out but keep the SSH_HOSTKEY state */ @@ -592,9 +478,9 @@ LIBSSH2_HOSTKEY_HASH_SHA256); if(!fingerprint) { failf(data, - "Denied establishing ssh session: sha256 fingerprint " + "Denied establishing ssh session: SHA256 fingerprint " "not available"); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } @@ -602,13 +488,13 @@ * See libssh2_hostkey_hash documentation. */ if(curlx_base64_encode((const uint8_t *)fingerprint, 32, &fingerprint_b64, &fingerprint_b64_len) != CURLE_OK) { - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } if(!fingerprint_b64) { - failf(data, "sha256 fingerprint could not be encoded"); - myssh_state(data, sshc, SSH_SESSION_FREE); + failf(data, "SHA256 fingerprint could not be encoded"); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } @@ -625,16 +511,16 @@ b64_pos++; } - /* Before we authenticate we check the hostkey's sha256 fingerprint + /* Before we authenticate we check the hostkey's SHA256 fingerprint * against a known fingerprint, if available. */ if((pub_pos != b64_pos) || strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { failf(data, - "Denied establishing ssh session: mismatch sha256 fingerprint. " + "Denied establishing ssh session: mismatch SHA256 fingerprint. " "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); curlx_free(fingerprint_b64); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } @@ -661,20 +547,20 @@ infof(data, "SSH MD5 fingerprint: %s", md5buffer); } - /* This does NOT verify the length of 'pubkey_md5' separately, which will - make the comparison below fail unless it is exactly 32 characters */ + /* This does NOT verify the length of 'pubkey_md5' separately, which + makes the comparison below fail unless it is exactly 32 characters */ if(!fingerprint || !curl_strequal(md5buffer, pubkey_md5)) { if(fingerprint) { failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " + "Denied establishing ssh session: mismatch MD5 fingerprint. " "Remote %s is not equal to %s", md5buffer, pubkey_md5); } else { failf(data, - "Denied establishing ssh session: md5 fingerprint " + "Denied establishing ssh session: MD5 fingerprint " "not available"); } - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } infof(data, "MD5 checksum match"); @@ -695,12 +581,12 @@ (int)keytype, remotekey, keylen); Curl_set_in_callback(data, FALSE); if(rc != CURLKHMATCH_OK) { - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } } else { - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } return CURLE_OK; @@ -716,7 +602,7 @@ } /* - * ssh_force_knownhost_key_type() will check the known hosts file and try to + * ssh_force_knownhost_key_type() checks the known hosts file and try to * force a specific public key type from the server if an entry is found. */ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, @@ -740,7 +626,7 @@ struct connectdata *conn = data->conn; /* lets try to find our host in the known hosts file */ while(!libssh2_knownhost_get(sshc->kh, &store, store)) { - /* For non-standard ports, the name will be enclosed in */ + /* For non-standard ports, the name is enclosed in */ /* square brackets, followed by a colon and the port */ if(store) { if(store->name) { @@ -815,10 +701,10 @@ rc = libssh2_session_method_pref(sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method); if(rc) { - char *errmsg = NULL; + char *err_msg = NULL; int errlen; - libssh2_session_last_error(sshc->ssh_session, &errmsg, &errlen, 0); - failf(data, "libssh2 method '%s' failed: %s", hostkey_method, errmsg); + libssh2_session_last_error(sshc->ssh_session, &err_msg, &errlen, 0); + failf(data, "libssh2 method '%s' failed: %s", hostkey_method, err_msg); result = libssh2_session_error_to_CURLE(rc); } } @@ -831,12 +717,12 @@ return result; } -static CURLcode return_quote_error(struct Curl_easy *data, - struct ssh_conn *sshc) +static CURLcode quote_error(struct Curl_easy *data, + struct ssh_conn *sshc) { failf(data, "Suspicious data after the command line"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); return CURLE_QUOTE_ERROR; } @@ -853,12 +739,12 @@ * 'sshc->quote_item' is already verified to be non-NULL before it * switched to this state. */ - char *cmd = sshc->quote_item->data; + const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -881,7 +767,7 @@ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); curlx_free(tmp); if(!result) - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return result; } @@ -924,14 +810,14 @@ if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in %s: Bad second parameter", cmd); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT); + myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT); return result; } if(!strncmp(cmd, "ln ", 3) || @@ -943,19 +829,19 @@ if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } if(*cp) - return_quote_error(data, sshc); - myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK); + return quote_error(data, sshc); + myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK); return result; } else if(!strncmp(cmd, "mkdir ", 6)) { if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); /* create directory */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR); + myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR); return result; } else if(!strncmp(cmd, "rename ", 7)) { @@ -966,37 +852,37 @@ if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } if(*cp) - return_quote_error(data, sshc); - myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME); + return quote_error(data, sshc); + myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME); return result; } else if(!strncmp(cmd, "rmdir ", 6)) { if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); /* delete directory */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR); + myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR); return result; } else if(!strncmp(cmd, "rm ", 3)) { if(*cp) - return_quote_error(data, sshc); - myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK); + return quote_error(data, sshc); + myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK); return result; } else if(!strncmp(cmd, "statvfs ", 8)) { if(*cp) - return_quote_error(data, sshc); - myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS); + return quote_error(data, sshc); + myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS); return result; } failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); return CURLE_QUOTE_ERROR; } @@ -1010,7 +896,7 @@ /* * NOTE!!! libssh2 requires that the destination path is a full path * that includes the destination file and name OR ends in a "/" - * If this is not done the destination file will be named the + * If this is not done the destination file is named the * same name as the last directory in the path. */ @@ -1078,22 +964,22 @@ sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */ if(sshc->secondCreateDirs) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); failf(data, "Creating the dir/file failed: %s", sftp_libssh2_strerror(sftperr)); return sftp_libssh2_error_to_CURLE(sftperr); } - if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) || - (sftperr == LIBSSH2_FX_FAILURE) || - (sftperr == LIBSSH2_FX_NO_SUCH_PATH)) && - (data->set.ftp_create_missing_dirs && - (strlen(sshp->path) > 1))) { + if((sftperr == LIBSSH2_FX_NO_SUCH_FILE || + sftperr == LIBSSH2_FX_FAILURE || + sftperr == LIBSSH2_FX_NO_SUCH_PATH) && + data->set.ftp_create_missing_dirs && + (strlen(sshp->path) > 1)) { /* try to create the path remotely */ sshc->secondCreateDirs = 1; - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_INIT); return CURLE_OK; } - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); result = sftp_libssh2_error_to_CURLE(sftperr); if(!result) { /* Sometimes, for some reason libssh2_sftp_last_error() returns zero @@ -1170,14 +1056,14 @@ /* upload data */ Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->recv_idx = FIRSTSOCKET; /* since we do not really wait for anything at this point, we want the state machine to move on as soon as possible so mark this as dirty */ Curl_multi_mark_dirty(data); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1205,7 +1091,7 @@ /* To ponder about: should really the lib be messing about with the HOME environment variable etc? */ char *home = curl_getenv("HOME"); - struct_stat sbuf; + curlx_struct_stat sbuf; /* If no private key file is specified, try some common paths. */ if(home) { @@ -1219,7 +1105,7 @@ if(!sshc->rsa) out_of_memory = TRUE; else if(curlx_stat(sshc->rsa, &sbuf)) { - Curl_safefree(sshc->rsa); + curlx_safefree(sshc->rsa); } } curlx_free(home); @@ -1243,20 +1129,20 @@ /* * Unless the user explicitly specifies a public key file, let * libssh2 extract the public key from the private key file. - * This is done by simply passing sshc->rsa_pub = NULL. + * This is done by passing sshc->rsa_pub = NULL. */ - if(!out_of_memory && data->set.str[STRING_SSH_PUBLIC_KEY] + if(!out_of_memory && data->set.str[STRING_SSH_PUBLIC_KEY] && /* treat empty string the same way as NULL */ - && data->set.str[STRING_SSH_PUBLIC_KEY][0]) { + data->set.str[STRING_SSH_PUBLIC_KEY][0]) { sshc->rsa_pub = curlx_strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); if(!sshc->rsa_pub) out_of_memory = TRUE; } if(out_of_memory || !sshc->rsa) { - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->rsa_pub); - myssh_state(data, sshc, SSH_SESSION_FREE); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->rsa_pub); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_OUT_OF_MEMORY; } @@ -1268,12 +1154,12 @@ infof(data, "Using SSH public key file '%s'", sshc->rsa_pub); infof(data, "Using SSH private key file '%s'", sshc->rsa); - myssh_state(data, sshc, SSH_AUTH_PKEY); + myssh_to(data, sshc, SSH_AUTH_PKEY); } else { - myssh_state(data, sshc, SSH_AUTH_PASS_INIT); + myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } - return 0; + return CURLE_OK; } static CURLcode sftp_quote_stat(struct Curl_easy *data, @@ -1281,12 +1167,12 @@ struct SSHPROTO *sshp, bool *blockp) { - char *cmd = sshc->quote_item->data; + const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any aborts or - cancels etc. It will cause libcurl to act as if the command is + can, the command is allowed to fail without it causing any aborts or + cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1379,11 +1265,11 @@ } /* Now send the completed structure... */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT); + myssh_to(data, sshc, SSH_SFTP_QUOTE_SETSTAT); return CURLE_OK; fail: - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); return CURLE_QUOTE_ERROR; } @@ -1405,14 +1291,14 @@ (attrs.filesize == 0)) { /* * libssh2_sftp_open() did not return an error, so maybe the server - * just does not support stat() + * does not support stat() * OR the server does not return a file size with a stat() * OR file size is 0 */ data->req.size = -1; data->req.maxdownload = -1; Curl_pgrsSetDownloadSize(data, -1); - attrs.filesize = 0; /* might be uninitialized but will be read below */ + attrs.filesize = 0; /* might be uninitialized but is read below */ } else { curl_off_t size = attrs.filesize; @@ -1469,15 +1355,15 @@ /* no data to transfer */ Curl_xfer_setup_nop(data); infof(data, "File already completely downloaded"); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1518,17 +1404,17 @@ LIBSSH2_SFTP_S_IFLNK)) { result = curlx_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path, sshp->readdir_filename); - myssh_state(data, sshc, SSH_SFTP_READDIR_LINK); + myssh_to(data, sshc, SSH_SFTP_READDIR_LINK); } else { - myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM); + myssh_to(data, sshc, SSH_SFTP_READDIR_BOTTOM); } } return result; } } else if(!rc) { - myssh_state(data, sshc, SSH_SFTP_READDIR_DONE); + myssh_to(data, sshc, SSH_SFTP_READDIR_DONE); } else { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); @@ -1536,7 +1422,7 @@ failf(data, "Could not open remote file for reading: %s :: %d", sftp_libssh2_strerror(sftperr), libssh2_session_last_errno(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); } return result; } @@ -1554,9 +1440,9 @@ result = ssh_force_knownhost_key_type(data, sshc); if(result) - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); else - myssh_state(data, sshc, SSH_S_STARTUP); + myssh_to(data, sshc, SSH_S_STARTUP); return result; } @@ -1574,11 +1460,11 @@ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_FAILED_INIT; } - myssh_state(data, sshc, SSH_HOSTKEY); + myssh_to(data, sshc, SSH_HOSTKEY); return CURLE_OK; } @@ -1592,7 +1478,7 @@ */ CURLcode result = ssh_check_fingerprint(data, sshc); if(!result) - myssh_state(data, sshc, SSH_AUTHLIST); + myssh_to(data, sshc, SSH_AUTHLIST); return result; } @@ -1605,9 +1491,9 @@ * must never change it later. Thus, always specify the correct username * here, even though the libssh2 docs kind of indicate that it should be * possible to get a 'generic' list (not user-specific) of authentication - * methods, presumably with a blank username. That will not work in my + * methods, presumably with a blank username. That does not work in my * experience. - * So always specify it here. + * Therefore always specify it here. */ struct connectdata *conn = data->conn; sshc->authlist = libssh2_userauth_list(sshc->ssh_session, @@ -1619,19 +1505,19 @@ if(libssh2_userauth_authenticated(sshc->ssh_session)) { sshc->authed = TRUE; infof(data, "SSH user accepted with no authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } rc = libssh2_session_last_errno(sshc->ssh_session); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return libssh2_session_error_to_CURLE(rc); } infof(data, "SSH authentication methods available: %s", sshc->authlist); - myssh_state(data, sshc, SSH_AUTH_PKEY_INIT); + myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); return CURLE_OK; } @@ -1651,13 +1537,13 @@ if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); if(rc == 0) { sshc->authed = TRUE; infof(data, "Initialized SSH public key authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); } else { char *err_msg = NULL; @@ -1671,7 +1557,7 @@ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); } infof(data, "SSH public key authentication failed: %s", err_msg); - myssh_state(data, sshc, SSH_AUTH_PASS_INIT); + myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } return CURLE_OK; } @@ -1681,10 +1567,10 @@ { if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) && (strstr(sshc->authlist, "password") != NULL)) { - myssh_state(data, sshc, SSH_AUTH_PASS); + myssh_to(data, sshc, SSH_AUTH_PASS); } else { - myssh_state(data, sshc, SSH_AUTH_HOST_INIT); + myssh_to(data, sshc, SSH_AUTH_HOST_INIT); } return CURLE_OK; } @@ -1705,10 +1591,10 @@ if(rc == 0) { sshc->authed = TRUE; infof(data, "Initialized password authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); } else { - myssh_state(data, sshc, SSH_AUTH_HOST_INIT); + myssh_to(data, sshc, SSH_AUTH_HOST_INIT); } return CURLE_OK; } @@ -1718,10 +1604,10 @@ { if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) && (strstr(sshc->authlist, "hostbased") != NULL)) { - myssh_state(data, sshc, SSH_AUTH_HOST); + myssh_to(data, sshc, SSH_AUTH_HOST); } else { - myssh_state(data, sshc, SSH_AUTH_AGENT_INIT); + myssh_to(data, sshc, SSH_AUTH_AGENT_INIT); } return CURLE_OK; } @@ -1741,7 +1627,7 @@ if(!sshc->ssh_agent) { infof(data, "Could not create agent object"); - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); return CURLE_OK; } } @@ -1751,14 +1637,14 @@ return CURLE_AGAIN; if(rc < 0) { infof(data, "Failure connecting to agent"); - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { - myssh_state(data, sshc, SSH_AUTH_AGENT_LIST); + myssh_to(data, sshc, SSH_AUTH_AGENT_LIST); } } else - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); return CURLE_OK; } @@ -1771,10 +1657,10 @@ return CURLE_AGAIN; if(rc < 0) { infof(data, "Failure requesting identities to agent"); - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { - myssh_state(data, sshc, SSH_AUTH_AGENT); + myssh_to(data, sshc, SSH_AUTH_AGENT); sshc->sshagent_prev_identity = NULL; } return CURLE_OK; @@ -1815,10 +1701,10 @@ if(rc == LIBSSH2_ERROR_NONE) { sshc->authed = TRUE; infof(data, "Agent based authentication successful"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); } else { - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } return CURLE_OK; } @@ -1828,10 +1714,10 @@ { if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) { - myssh_state(data, sshc, SSH_AUTH_KEY); + myssh_to(data, sshc, SSH_AUTH_KEY); } else { - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); } return CURLE_OK; } @@ -1853,7 +1739,7 @@ if(rc == 0) { sshc->authed = TRUE; infof(data, "Initialized keyboard interactive authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } return CURLE_LOGIN_DENIED; @@ -1865,7 +1751,7 @@ struct connectdata *conn = data->conn; if(!sshc->authed) { failf(data, "Authentication failure"); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_LOGIN_DENIED; } @@ -1879,12 +1765,12 @@ data->conn->recv_idx = FIRSTSOCKET; conn->send_idx = -1; - if(conn->handler->protocol == CURLPROTO_SFTP) { - myssh_state(data, sshc, SSH_SFTP_INIT); + if(conn->scheme->protocol == CURLPROTO_SFTP) { + myssh_to(data, sshc, SSH_SFTP_INIT); return CURLE_OK; } infof(data, "SSH CONNECT phase done"); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1903,10 +1789,10 @@ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); failf(data, "Failure initializing sftp session: %s", err_msg); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_FAILED_INIT; } - myssh_state(data, sshc, SSH_SFTP_REALPATH); + myssh_to(data, sshc, SSH_SFTP_REALPATH); return CURLE_OK; } @@ -1917,14 +1803,19 @@ /* * Get the "home" directory */ - int rc = libssh2_sftp_symlink_ex(sshc->sftp_session, - ".", curlx_uztoui(strlen(".")), - sshp->readdir_filename, CURL_PATH_MAX, - LIBSSH2_SFTP_REALPATH); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_symlink_ex(sshc->sftp_session, ".", + curlx_uztoui(strlen(".")), + sshp->readdir_filename, CURL_PATH_MAX, + LIBSSH2_SFTP_REALPATH); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); if(rc > 0) { curlx_free(sshc->homedir); sshc->homedir = curlx_strdup(sshp->readdir_filename); @@ -1951,8 +1842,7 @@ /* This is the last step in the SFTP connect phase. Do note that while we get the homedir here, we get the "workingpath" in the DO action since the - homedir will remain the same between request but the working path will - not. */ + homedir remains the same between request but the working path does not. */ CURL_TRC_SSH(data, "CONNECT phase done"); return CURLE_OK; } @@ -1961,19 +1851,24 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { - CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); if(result) { - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return result; } if(data->set.quote) { infof(data, "Sending quote commands"); sshc->quote_item = data->set.quote; - myssh_state(data, sshc, SSH_SFTP_QUOTE); + myssh_to(data, sshc, SSH_SFTP_QUOTE); } else { - myssh_state(data, sshc, SSH_SFTP_GETINFO); + myssh_to(data, sshc, SSH_SFTP_GETINFO); } return CURLE_OK; } @@ -1984,10 +1879,10 @@ if(data->set.postquote) { infof(data, "Sending quote commands"); sshc->quote_item = data->set.postquote; - myssh_state(data, sshc, SSH_SFTP_QUOTE); + myssh_to(data, sshc, SSH_SFTP_QUOTE); } else { - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); } return CURLE_OK; } @@ -1997,9 +1892,14 @@ struct SSHPROTO *sshp) { /* Send quote commands */ - CURLcode result = sftp_quote(data, sshc, sshp); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + result = sftp_quote(data, sshc, sshp); if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; } return result; @@ -2008,21 +1908,21 @@ static CURLcode ssh_state_sftp_next_quote(struct Curl_easy *data, struct ssh_conn *sshc) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); sshc->quote_item = sshc->quote_item->next; if(sshc->quote_item) { - myssh_state(data, sshc, SSH_SFTP_QUOTE); + myssh_to(data, sshc, SSH_SFTP_QUOTE); } else { if(sshc->nextstate != SSH_NO_STATE) { - myssh_state(data, sshc, sshc->nextstate); + myssh_to(data, sshc, sshc->nextstate); sshc->nextstate = SSH_NO_STATE; } else { - myssh_state(data, sshc, SSH_SFTP_GETINFO); + myssh_to(data, sshc, SSH_SFTP_GETINFO); } } return CURLE_OK; @@ -2033,9 +1933,14 @@ struct SSHPROTO *sshp, bool *blockp) { - CURLcode result = sftp_quote_stat(data, sshc, sshp, blockp); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + result = sftp_quote_stat(data, sshc, sshp, blockp); if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; } return result; @@ -2045,11 +1950,15 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { - int rc = - libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, - curlx_uztoui(strlen(sshc->quote_path2)), - LIBSSH2_SFTP_SETSTAT, - &sshp->quote_attrs); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, + curlx_uztoui(strlen(sshc->quote_path2)), + LIBSSH2_SFTP_SETSTAT, + &sshp->quote_attrs); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -2057,13 +1966,13 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "Attempt to set SFTP stats for \"%s\" failed: %s", sshc->quote_path2, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2084,13 +1993,13 @@ failf(data, "symlink \"%s\" to \"%s\" failed: %s", sshc->quote_path1, sshc->quote_path2, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2107,12 +2016,12 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "mkdir \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2136,13 +2045,13 @@ failf(data, "rename \"%s\" to \"%s\" failed: %s", sshc->quote_path1, sshc->quote_path2, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2158,12 +2067,12 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "rmdir \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2179,12 +2088,12 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "rm \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2203,8 +2112,8 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "statvfs \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } @@ -2234,7 +2143,7 @@ statvfs.f_fsid, statvfs.f_flag, statvfs.f_namemax); if(!tmp) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_OUT_OF_MEMORY; } @@ -2242,12 +2151,12 @@ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); curlx_free(tmp); if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return result; } } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2256,9 +2165,14 @@ struct SSHPROTO *sshp) { /* 'mode' - parameter is preliminary - default to 0644 */ - int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - (long)data->set.new_directory_perms); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + (long)data->set.new_directory_perms); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -2274,11 +2188,11 @@ if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) && (sftperr != LIBSSH2_FX_FAILURE) && (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); return sftp_libssh2_error_to_CURLE(sftperr); } } - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); return CURLE_OK; } @@ -2286,9 +2200,12 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; + Curl_pgrsSetDownloadSize(data, -1); if(data->req.no_body) { - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2308,10 +2225,10 @@ sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "Could not open directory for reading: %s", sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); return sftp_libssh2_error_to_CURLE(sftperr); } - myssh_state(data, sshc, SSH_SFTP_READDIR); + myssh_to(data, sshc, SSH_SFTP_READDIR); return CURLE_OK; } @@ -2320,13 +2237,16 @@ struct SSHPROTO *sshp) { CURLcode result; - int rc = - libssh2_sftp_symlink_ex(sshc->sftp_session, - curlx_dyn_ptr(&sshp->readdir_link), - (unsigned int) - curlx_dyn_len(&sshp->readdir_link), - sshp->readdir_filename, - CURL_PATH_MAX, LIBSSH2_SFTP_READLINK); + int rc; + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_symlink_ex(sshc->sftp_session, + curlx_dyn_ptr(&sshp->readdir_link), + (unsigned int) + curlx_dyn_len(&sshp->readdir_link), + sshp->readdir_filename, + CURL_PATH_MAX, LIBSSH2_SFTP_READLINK); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -2338,9 +2258,9 @@ /* append filename and extra output */ result = curlx_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename); if(result) - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); else - myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM); + myssh_to(data, sshc, SSH_SFTP_READDIR_BOTTOM); return result; } @@ -2349,9 +2269,12 @@ struct SSHPROTO *sshp) { curl_off_t bytecount; + libssh2_struct_stat sb; + if(!sshp) + return CURLE_FAILED_INIT; /* - * We must check the remote file; if it is a directory no values will + * We must check the remote file; if it is a directory no values are * be set in sb */ @@ -2360,7 +2283,6 @@ */ /* get a fresh new channel from the ssh layer */ - libssh2_struct_stat sb; memset(&sb, 0, sizeof(libssh2_struct_stat)); sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session, sshp->path, &sb); if(!sshc->ssh_channel) { @@ -2373,7 +2295,7 @@ ssh_err = libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); failf(data, "%s", err_msg); - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); return libssh2_session_error_to_CURLE(ssh_err); } @@ -2382,10 +2304,10 @@ data->req.maxdownload = (curl_off_t)sb.st_size; Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2394,6 +2316,8 @@ struct SSHPROTO *sshp) { int rc = 0; + if(!sshp) + return CURLE_FAILED_INIT; if(sshc->sftp_handle) { rc = libssh2_sftp_close(sshc->sftp_handle); if(rc == LIBSSH2_ERROR_EAGAIN) @@ -2407,20 +2331,20 @@ sshc->sftp_handle = NULL; } - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); CURL_TRC_SSH(data, "SFTP DONE done"); /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT After nextstate is executed, the control should come back to - SSH_SFTP_CLOSE to pass the correct result back */ + SSH_SFTP_CLOSE to pass the correct result back */ if(sshc->nextstate != SSH_NO_STATE && sshc->nextstate != SSH_SFTP_CLOSE) { - myssh_state(data, sshc, sshc->nextstate); + myssh_to(data, sshc, sshc->nextstate); sshc->nextstate = SSH_SFTP_CLOSE; } else - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2455,9 +2379,9 @@ sshc->sftp_session = NULL; } - Curl_safefree(sshc->homedir); + curlx_safefree(sshc->homedir); - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); return CURLE_OK; } @@ -2465,6 +2389,8 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; /* * Work on getting the specified file */ @@ -2482,10 +2408,10 @@ sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "Could not open remote file for reading: %s", sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); return sftp_libssh2_error_to_CURLE(sftperr); } - myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT); + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_STAT); return CURLE_OK; } @@ -2493,10 +2419,12 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; /* * libssh2 requires that the destination path is a full path that * includes the destination file and name OR ends in a "/" . If this is - * not done the destination file will be named the same name as the last + * not done the destination file is named the same name as the last * directory in the path. */ sshc->ssh_channel = @@ -2513,7 +2441,7 @@ ssh_err = libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); failf(data, "%s", err_msg); - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); result = libssh2_session_error_to_CURLE(ssh_err); /* Map generic errors to upload failed */ @@ -2528,10 +2456,10 @@ Curl_pgrsSetUploadSize(data, data->state.infilesize); Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->recv_idx = FIRSTSOCKET; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2568,25 +2496,424 @@ } } - Curl_safefree(sshc->homedir); + curlx_safefree(sshc->homedir); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_OK; } + +static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, + bool block) +{ + int rc; + + if(sshc->kh) { + libssh2_knownhost_free(sshc->kh); + sshc->kh = NULL; + } + + if(sshc->ssh_agent) { + rc = libssh2_agent_disconnect(sshc->ssh_agent); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to disconnect from libssh2 agent: %d %s", + rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + libssh2_agent_free(sshc->ssh_agent); + sshc->ssh_agent = NULL; + + /* NB: there is no need to free identities, they are part of internal + agent stuff */ + sshc->sshagent_identity = NULL; + sshc->sshagent_prev_identity = NULL; + } + + if(sshc->sftp_handle) { + rc = libssh2_sftp_close(sshc->sftp_handle); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + sshc->sftp_handle = NULL; + } + + if(sshc->ssh_channel) { + rc = libssh2_channel_free(sshc->ssh_channel); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 scp subsystem: %d %s", rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + sshc->ssh_channel = NULL; + } + + if(sshc->sftp_session) { + rc = libssh2_sftp_shutdown(sshc->sftp_session); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to stop libssh2 sftp subsystem: %d %s", rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + sshc->sftp_session = NULL; + } + + if(sshc->ssh_session) { + rc = libssh2_session_free(sshc->ssh_session); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + sshc->ssh_session = NULL; + } + + /* worst-case scenario cleanup */ + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->ssh_channel == NULL); + DEBUGASSERT(sshc->sftp_session == NULL); + DEBUGASSERT(sshc->sftp_handle == NULL); + DEBUGASSERT(sshc->kh == NULL); + DEBUGASSERT(sshc->ssh_agent == NULL); + + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + curlx_safefree(sshc->homedir); + + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_getinfo(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->set.get_filetime) + myssh_to(data, sshc, SSH_SFTP_FILETIME); + else + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_filetime(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + LIBSSH2_SFTP_ATTRIBUTES attrs; + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_SFTP_STAT, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + + if(rc == 0) + data->info.filetime = (time_t)attrs.mtime; + + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_trans_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + else if(sshp) { + size_t plen = strlen(sshp->path); + if(plen && (sshp->path[plen - 1] == '/')) + myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); + else + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); + } + else + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_upload_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_upload_init(data, sshc, sshp, block); + if(result) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + } + return result; +} + +static CURLcode ssh_state_sftp_create_dirs_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return CURLE_FAILED_INIT; + if(strlen(sshp->path) > 1) { + sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + } + else + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_create_dirs(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return CURLE_FAILED_INIT; + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + infof(data, "Creating directory '%s'", sshp->path); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); + return CURLE_OK; + } + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_readdir(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_readdir(data, sshc, sshp, block); + if(result) + myssh_to(data, sshc, SSH_SFTP_CLOSE); + return result; +} + +static CURLcode ssh_state_sftp_readdir_bottom(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + + result = curlx_dyn_addn(&sshp->readdir, "\n", 1); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, + curlx_dyn_ptr(&sshp->readdir), + curlx_dyn_len(&sshp->readdir)); + if(result) { + curlx_dyn_free(&sshp->readdir); + myssh_to(data, sshc, SSH_STOP); + } + else { + curlx_dyn_reset(&sshp->readdir); + myssh_to(data, sshc, SSH_SFTP_READDIR); + } + return result; +} + +static CURLcode ssh_state_sftp_readdir_done(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(libssh2_sftp_closedir(sshc->sftp_handle) == + LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + + sshc->sftp_handle = NULL; + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + myssh_to(data, sshc, SSH_STOP); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_download_stat(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_download_stat(data, sshc, sshp, block); + if(result) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + } + return result; +} + +static CURLcode ssh_state_scp_trans_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = Curl_getworkingpath(data, sshc->homedir, + &sshp->path); + if(result) { + myssh_to(data, sshc, SSH_STOP); + return result; + } + + if(data->state.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_UPLOAD_FAILED; + } + myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); + } + else + myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_done(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SCP_SEND_EOF); + else + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_send_eof(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_send_eof(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, + "Failed to send libssh2 channel EOF: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_WAIT_EOF); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_wait_eof(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_wait_eof(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to get channel EOF: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_wait_close(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = + libssh2_channel_wait_closed(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Channel failed to close: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_channel_free( + struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_free(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, + "Failed to free libssh2 scp subsystem: %d %s", + rc, err_msg); + } + sshc->ssh_channel = NULL; + } + CURL_TRC_SSH(data, "SCP DONE phase complete"); + myssh_to(data, sshc, SSH_STOP); + return CURLE_OK; +} + +static CURLcode ssh_state_session_free(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + CURLcode result = sshc_cleanup(sshc, data, FALSE); + struct connectdata *conn = data->conn; + if(result) + return result; + memset(sshc, 0, sizeof(struct ssh_conn)); + connclose(conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + myssh_to(data, sshc, SSH_STOP); + return CURLE_OK; +} + /* * ssh_statemachine() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points - * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN + * to is set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN * meaning it wants to be called again when the socket is ready */ - static CURLcode ssh_statemachine(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp, bool *block) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; *block = 0; /* we are not blocking by default */ do { @@ -2632,7 +2959,7 @@ break; case SSH_AUTH_HOST: - myssh_state(data, sshc, SSH_AUTH_AGENT_INIT); + myssh_to(data, sshc, SSH_AUTH_AGENT_INIT); break; case SSH_AUTH_AGENT_INIT: @@ -2664,13 +2991,11 @@ break; case SSH_SFTP_REALPATH: - result = sshp ? ssh_state_sftp_realpath(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_realpath(data, sshc, sshp); break; case SSH_SFTP_QUOTE_INIT: - result = sshp ? ssh_state_sftp_quote_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_quote_init(data, sshc, sshp); break; case SSH_SFTP_POSTQUOTE_INIT: @@ -2678,8 +3003,7 @@ break; case SSH_SFTP_QUOTE: - result = sshp ? ssh_state_sftp_quote(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_quote(data, sshc, sshp); break; case SSH_SFTP_NEXT_QUOTE: @@ -2687,13 +3011,11 @@ break; case SSH_SFTP_QUOTE_STAT: - result = sshp ? ssh_state_sftp_quote_stat(data, sshc, sshp, block) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_quote_stat(data, sshc, sshp, block); break; case SSH_SFTP_QUOTE_SETSTAT: - result = sshp ? ssh_state_sftp_quote_setstat(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_quote_setstat(data, sshc, sshp); break; case SSH_SFTP_QUOTE_SYMLINK: @@ -2721,160 +3043,63 @@ break; case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - myssh_state(data, sshc, SSH_SFTP_FILETIME); - } - else { - myssh_state(data, sshc, SSH_SFTP_TRANS_INIT); - } + result = ssh_state_sftp_getinfo(data, sshc); break; - case SSH_SFTP_FILETIME: { - LIBSSH2_SFTP_ATTRIBUTES attrs; - int rc; - - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_SFTP_STAT, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc == 0) { - data->info.filetime = (time_t)attrs.mtime; - } - - myssh_state(data, sshc, SSH_SFTP_TRANS_INIT); + case SSH_SFTP_FILETIME: + result = ssh_state_sftp_filetime(data, sshc, sshp); break; - } case SSH_SFTP_TRANS_INIT: - if(data->state.upload) - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); - else if(sshp) { - if(sshp->path[strlen(sshp->path) - 1] == '/') - myssh_state(data, sshc, SSH_SFTP_READDIR_INIT); - else - myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT); - } - else - result = CURLE_FAILED_INIT; + result = ssh_state_sftp_trans_init(data, sshc, sshp); break; case SSH_SFTP_UPLOAD_INIT: - result = sshp ? sftp_upload_init(data, sshc, sshp, block) : - CURLE_FAILED_INIT; - if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - } + result = ssh_state_sftp_upload_init(data, sshc, sshp, block); break; case SSH_SFTP_CREATE_DIRS_INIT: - if(!sshp) - result = CURLE_FAILED_INIT; - else if(strlen(sshp->path) > 1) { - sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS); - } - else { - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); - } + result = ssh_state_sftp_create_dirs_init(data, sshc, sshp); break; case SSH_SFTP_CREATE_DIRS: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - sshc->slash_pos = strchr(sshc->slash_pos, '/'); - if(sshc->slash_pos) { - *sshc->slash_pos = 0; - - infof(data, "Creating directory '%s'", sshp->path); - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); - break; - } - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); + result = ssh_state_sftp_create_dirs(data, sshc, sshp); break; case SSH_SFTP_CREATE_DIRS_MKDIR: - result = sshp ? ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp); break; case SSH_SFTP_READDIR_INIT: - result = sshp ? ssh_state_sftp_readdir_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_readdir_init(data, sshc, sshp); break; case SSH_SFTP_READDIR: - result = sshp ? sftp_readdir(data, sshc, sshp, block) : - CURLE_FAILED_INIT; - if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - } + result = ssh_state_sftp_readdir(data, sshc, sshp, block); break; case SSH_SFTP_READDIR_LINK: - result = sshp ? ssh_state_sftp_readdir_link(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_readdir_link(data, sshc, sshp); break; case SSH_SFTP_READDIR_BOTTOM: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - result = curlx_dyn_addn(&sshp->readdir, "\n", 1); - if(!result) - result = Curl_client_write(data, CLIENTWRITE_BODY, - curlx_dyn_ptr(&sshp->readdir), - curlx_dyn_len(&sshp->readdir)); - - if(result) { - curlx_dyn_free(&sshp->readdir); - myssh_state(data, sshc, SSH_STOP); - } - else { - curlx_dyn_reset(&sshp->readdir); - myssh_state(data, sshc, SSH_SFTP_READDIR); - } + result = ssh_state_sftp_readdir_bottom(data, sshc, sshp); break; case SSH_SFTP_READDIR_DONE: - if(libssh2_sftp_closedir(sshc->sftp_handle) == LIBSSH2_ERROR_EAGAIN) - result = CURLE_AGAIN; - else { - sshc->sftp_handle = NULL; - - /* no data to transfer */ - Curl_xfer_setup_nop(data); - myssh_state(data, sshc, SSH_STOP); - } + result = ssh_state_sftp_readdir_done(data, sshc); break; case SSH_SFTP_DOWNLOAD_INIT: - result = sshp ? ssh_state_sftp_download_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_download_init(data, sshc, sshp); break; case SSH_SFTP_DOWNLOAD_STAT: - result = sshp ? sftp_download_stat(data, sshc, sshp, block) : - CURLE_FAILED_INIT; - if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - } + result = ssh_state_sftp_download_stat(data, sshc, sshp, block); break; case SSH_SFTP_CLOSE: - result = sshp ? ssh_state_sftp_close(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_close(data, sshc, sshp); break; case SSH_SFTP_SHUTDOWN: @@ -2882,114 +3107,35 @@ break; case SSH_SCP_TRANS_INIT: - result = sshp ? Curl_getworkingpath(data, sshc->homedir, &sshp->path) : - CURLE_FAILED_INIT; - if(result) { - myssh_state(data, sshc, SSH_STOP); - break; - } - - if(data->state.upload) { - if(data->state.infilesize < 0) { - failf(data, "SCP requires a known file size for upload"); - result = CURLE_UPLOAD_FAILED; - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); - break; - } - myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT); - } - else { - myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT); - } + result = ssh_state_scp_trans_init(data, sshc, sshp); break; case SSH_SCP_UPLOAD_INIT: - result = sshp ? ssh_state_scp_upload_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_scp_upload_init(data, sshc, sshp); break; case SSH_SCP_DOWNLOAD_INIT: - result = sshp ? ssh_state_scp_download_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_scp_download_init(data, sshc, sshp); break; case SSH_SCP_DONE: - if(data->state.upload) - myssh_state(data, sshc, SSH_SCP_SEND_EOF); - else - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + result = ssh_state_scp_done(data, sshc); break; case SSH_SCP_SEND_EOF: - if(sshc->ssh_channel) { - int rc = libssh2_channel_send_eof(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to send libssh2 channel EOF: %d %s", - rc, err_msg); - } - } - myssh_state(data, sshc, SSH_SCP_WAIT_EOF); + result = ssh_state_scp_send_eof(data, sshc); break; case SSH_SCP_WAIT_EOF: - if(sshc->ssh_channel) { - int rc = libssh2_channel_wait_eof(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); - } - } - myssh_state(data, sshc, SSH_SCP_WAIT_CLOSE); + result = ssh_state_scp_wait_eof(data, sshc); break; case SSH_SCP_WAIT_CLOSE: - if(sshc->ssh_channel) { - int rc = libssh2_channel_wait_closed(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Channel failed to close: %d %s", rc, err_msg); - } - } - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + result = ssh_state_scp_wait_close(data, sshc); break; case SSH_SCP_CHANNEL_FREE: - if(sshc->ssh_channel) { - int rc = libssh2_channel_free(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 scp subsystem: %d %s", - rc, err_msg); - } - sshc->ssh_channel = NULL; - } - CURL_TRC_SSH(data, "SCP DONE phase complete"); - myssh_state(data, sshc, SSH_STOP); + result = ssh_state_scp_channel_free(data, sshc); break; case SSH_SESSION_DISCONNECT: @@ -2997,20 +3143,13 @@ break; case SSH_SESSION_FREE: - result = sshc_cleanup(sshc, data, FALSE); - if(result) - break; - /* the code we are about to return */ - memset(sshc, 0, sizeof(struct ssh_conn)); - connclose(conn, "SSH session free"); - sshc->state = SSH_SESSION_FREE; /* current */ - myssh_state(data, sshc, SSH_STOP); + result = ssh_state_session_free(data, sshc); break; case SSH_QUIT: default: /* internal error */ - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; } @@ -3023,7 +3162,7 @@ result = CURLE_OK; } CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - myssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), result, *block); return result; } @@ -3041,12 +3180,12 @@ if(!sshc || (sock == CURL_SOCKET_BAD)) return CURLE_FAILED_INIT; - waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon; + waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; if(waitfor) { int flags = 0; - if(waitfor & KEEP_RECV) + if(waitfor & REQ_IO_RECV) flags |= CURL_POLL_IN; - if(waitfor & KEEP_SEND) + if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); CURL_TRC_SSH(data, "pollset, flags=%x", flags); @@ -3075,8 +3214,9 @@ dir = libssh2_session_block_directions(sshc->ssh_session); if(dir) { /* translate the libssh2 define bits into our own bit defines */ - sshc->waitfor = ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? KEEP_RECV : 0) | - ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? KEEP_SEND : 0); + sshc->waitfor = + ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? REQ_IO_RECV : 0) | + ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? REQ_IO_SEND : 0); } } if(!dir) @@ -3127,7 +3267,7 @@ if(result) break; - left_ms = Curl_timeleft_ms(data, FALSE); + left_ms = Curl_timeleft_ms(data); if(left_ms < 0) { failf(data, "Operation timed out"); return CURLE_OPERATION_TIMEDOUT; @@ -3163,7 +3303,7 @@ struct SSHPROTO *sshp = entry; (void)key; (void)klen; - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); curlx_dyn_free(&sshp->readdir); curlx_dyn_free(&sshp->readdir_link); curlx_free(sshp); @@ -3235,7 +3375,7 @@ return -EAGAIN; /* magic return code for libssh2 */ else if(result) return -1; /* generic error */ - Curl_debug(data, CURLINFO_DATA_IN, (const char *)buffer, (size_t)nread); + Curl_debug(data, CURLINFO_DATA_IN, (const char *)buffer, nread); return (ssize_t)nread; } @@ -3400,7 +3540,7 @@ } #endif /* CURL_DISABLE_PROXY */ - if(conn->handler->protocol & CURLPROTO_SCP) { + if(conn->scheme->protocol & CURLPROTO_SCP) { conn->recv[FIRSTSOCKET] = scp_recv; conn->send[FIRSTSOCKET] = scp_send; } @@ -3437,7 +3577,7 @@ infof(data, "SSH socket: %d", (int)sock); #endif /* CURL_LIBSSH2_DEBUG */ - myssh_state(data, sshc, SSH_INIT); + myssh_to(data, sshc, SSH_INIT); result = ssh_multi_statemach(data, done); @@ -3467,7 +3607,7 @@ return CURLE_FAILED_INIT; /* start the first command in the DO phase */ - myssh_state(data, sshc, SSH_SCP_TRANS_INIT); + myssh_to(data, sshc, SSH_SCP_TRANS_INIT); /* run the state-machine */ result = ssh_multi_statemach(data, dophase_done); @@ -3494,135 +3634,6 @@ return result; } -/* - * The DO function is generic for both protocols. There was previously two - * separate ones but this way means less duplicated code. - */ - -static CURLcode ssh_do(struct Curl_easy *data, bool *done) -{ - CURLcode result; - bool connected = FALSE; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - - *done = FALSE; /* default to false */ - if(!sshc) - return CURLE_FAILED_INIT; - - data->req.size = -1; /* make sure this is unknown at this point */ - sshc->secondCreateDirs = 0; /* reset the create directory attempt state - variable */ - - Curl_pgrsReset(data); - - if(conn->handler->protocol & CURLPROTO_SCP) - result = scp_perform(data, &connected, done); - else - result = sftp_perform(data, &connected, done); - - return result; -} - -static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, - bool block) -{ - int rc; - - if(sshc->kh) { - libssh2_knownhost_free(sshc->kh); - sshc->kh = NULL; - } - - if(sshc->ssh_agent) { - rc = libssh2_agent_disconnect(sshc->ssh_agent); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to disconnect from libssh2 agent: %d %s", - rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - libssh2_agent_free(sshc->ssh_agent); - sshc->ssh_agent = NULL; - - /* NB: there is no need to free identities, they are part of internal - agent stuff */ - sshc->sshagent_identity = NULL; - sshc->sshagent_prev_identity = NULL; - } - - if(sshc->sftp_handle) { - rc = libssh2_sftp_close(sshc->sftp_handle); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - sshc->sftp_handle = NULL; - } - - if(sshc->ssh_channel) { - rc = libssh2_channel_free(sshc->ssh_channel); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 scp subsystem: %d %s", rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - sshc->ssh_channel = NULL; - } - - if(sshc->sftp_session) { - rc = libssh2_sftp_shutdown(sshc->sftp_session); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to stop libssh2 sftp subsystem: %d %s", rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - sshc->sftp_session = NULL; - } - - if(sshc->ssh_session) { - rc = libssh2_session_free(sshc->ssh_session); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - sshc->ssh_session = NULL; - } - - /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->ssh_channel == NULL); - DEBUGASSERT(sshc->sftp_session == NULL); - DEBUGASSERT(sshc->sftp_handle == NULL); - DEBUGASSERT(sshc->kh == NULL); - DEBUGASSERT(sshc->ssh_agent == NULL); - - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - Curl_safefree(sshc->homedir); - - return CURLE_OK; -} - /* BLOCKING, but the function is using the state machine so the only reason this is still blocking is that the multi interface code has no support for disconnecting operations that takes a while */ @@ -3637,7 +3648,7 @@ if(sshc && sshc->ssh_session) { /* only if there is a session still around to use! */ - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); result = ssh_block_statemach(data, sshc, sshp, TRUE); } @@ -3666,7 +3677,7 @@ if(Curl_pgrsDone(data)) return CURLE_ABORTED_BY_CALLBACK; - data->req.keepon = 0; /* clear all bits */ + CURL_REQ_CLEAR_IO(data); return result; } @@ -3677,7 +3688,7 @@ (void)premature; if(sshc && !status) - myssh_state(data, sshc, SSH_SCP_DONE); + myssh_to(data, sshc, SSH_SCP_DONE); return ssh_done(data, status); } @@ -3769,7 +3780,7 @@ return CURLE_FAILED_INIT; /* start the first command in the DO phase */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT); + myssh_to(data, sshc, SSH_SFTP_QUOTE_INIT); /* run the state-machine */ result = ssh_multi_statemach(data, dophase_done); @@ -3810,7 +3821,7 @@ if(sshc->ssh_session) { /* only if there is a session still around to use! */ CURL_TRC_SSH(data, "DISCONNECT starts now"); - myssh_state(data, sshc, SSH_SFTP_SHUTDOWN); + myssh_to(data, sshc, SSH_SFTP_SHUTDOWN); result = ssh_block_statemach(data, sshc, sshp, TRUE); CURL_TRC_SSH(data, "DISCONNECT is done -> %d", result); } @@ -3834,7 +3845,7 @@ operation */ if(!premature && data->set.postquote && !conn->bits.retry) sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); } return ssh_done(data, status); } @@ -3897,70 +3908,33 @@ return CURLE_OK; } -static const char *sftp_libssh2_strerror(unsigned long err) +/* + * The DO function is generic for both protocols. There was previously two + * separate ones but this way means less duplicated code. + */ +static CURLcode ssh_do(struct Curl_easy *data, bool *done) { - switch(err) { - case LIBSSH2_FX_NO_SUCH_FILE: - return "No such file or directory"; + CURLcode result; + bool connected = FALSE; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - case LIBSSH2_FX_PERMISSION_DENIED: - return "Permission denied"; + *done = FALSE; /* default to false */ + if(!sshc) + return CURLE_FAILED_INIT; - case LIBSSH2_FX_FAILURE: - return "Operation failed"; + data->req.size = -1; /* make sure this is unknown at this point */ + sshc->secondCreateDirs = 0; /* reset the create directory attempt state + variable */ - case LIBSSH2_FX_BAD_MESSAGE: - return "Bad message from SFTP server"; + Curl_pgrsReset(data); - case LIBSSH2_FX_NO_CONNECTION: - return "Not connected to SFTP server"; + if(conn->scheme->protocol & CURLPROTO_SCP) + result = scp_perform(data, &connected, done); + else + result = sftp_perform(data, &connected, done); - case LIBSSH2_FX_CONNECTION_LOST: - return "Connection to SFTP server lost"; - - case LIBSSH2_FX_OP_UNSUPPORTED: - return "Operation not supported by SFTP server"; - - case LIBSSH2_FX_INVALID_HANDLE: - return "Invalid handle"; - - case LIBSSH2_FX_NO_SUCH_PATH: - return "No such file or directory"; - - case LIBSSH2_FX_FILE_ALREADY_EXISTS: - return "File already exists"; - - case LIBSSH2_FX_WRITE_PROTECT: - return "File is write protected"; - - case LIBSSH2_FX_NO_MEDIA: - return "No media"; - - case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: - return "Disk full"; - - case LIBSSH2_FX_QUOTA_EXCEEDED: - return "User quota exceeded"; - - case LIBSSH2_FX_UNKNOWN_PRINCIPLE: - return "Unknown principle"; - - case LIBSSH2_FX_LOCK_CONFlICT: - return "File lock conflict"; - - case LIBSSH2_FX_DIR_NOT_EMPTY: - return "Directory not empty"; - - case LIBSSH2_FX_NOT_A_DIRECTORY: - return "Not a directory"; - - case LIBSSH2_FX_INVALID_FILENAME: - return "Invalid filename"; - - case LIBSSH2_FX_LINK_LOOP: - return "Link points to itself"; - } - return "Unknown error in libssh2"; + return result; } CURLcode Curl_ssh_init(void) @@ -3974,7 +3948,7 @@ void Curl_ssh_cleanup(void) { - (void)libssh2_exit(); + libssh2_exit(); } void Curl_ssh_version(char *buffer, size_t buflen) @@ -3990,7 +3964,7 @@ { DEBUGASSERT(data); DEBUGASSERT(conn); - if(conn->handler->protocol & PROTO_FAMILY_SSH) { + if(conn->scheme->protocol & PROTO_FAMILY_SSH) { struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); if(sshc && sshc->ssh_session) { /* only re-attach if the session already exists */ @@ -3999,4 +3973,51 @@ } } } + +/* + * SCP protocol handler. + */ +const struct Curl_protocol Curl_protocol_scp = { + ssh_setup_connection, /* setup_connection */ + ssh_do, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + ssh_connect, /* connect_it */ + ssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + ssh_pollset, /* proto_pollset */ + ssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ssh_pollset, /* perform_pollset */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ssh_attach, /* attach */ + ZERO_NULL, /* follow */ +}; + +/* + * SFTP protocol handler. + */ +const struct Curl_protocol Curl_protocol_sftp = { + ssh_setup_connection, /* setup_connection */ + ssh_do, /* do_it */ + sftp_done, /* done */ + ZERO_NULL, /* do_more */ + ssh_connect, /* connect_it */ + ssh_multi_statemach, /* connecting */ + sftp_doing, /* doing */ + ssh_pollset, /* proto_pollset */ + ssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ssh_pollset, /* perform_pollset */ + sftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ssh_attach, /* attach */ + ZERO_NULL, /* follow */ +}; + #endif /* USE_LIBSSH2 */
diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h index 6fdf3f7..44e72ab 100644 --- a/Utilities/cmcurl/lib/vssh/ssh.h +++ b/Utilities/cmcurl/lib/vssh/ssh.h
@@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_SSH_H -#define HEADER_CURL_SSH_H +#ifndef HEADER_CURL_VSSH_SSH_H +#define HEADER_CURL_VSSH_SSH_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,7 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" +#include "urldata.h" + +extern const struct Curl_protocol Curl_protocol_sftp; +extern const struct Curl_protocol Curl_protocol_scp; + +#ifdef USE_SSH #ifdef USE_LIBSSH2 #include <libssh2.h> @@ -153,7 +159,7 @@ int secondCreateDirs; /* counter use by the code to see if the second attempt has been made to change to/create a directory */ - int waitfor; /* KEEP_RECV/KEEP_SEND bits overriding + int waitfor; /* REQ_IO_RECV/REQ_IO_SEND bits overriding pollset given flags */ char *slash_pos; /* used by the SFTP_CREATE_DIRS state */ @@ -230,10 +236,16 @@ #endif /* USE_LIBSSH2 */ -#ifdef USE_SSH +#ifdef CURLVERBOSE +const char *Curl_ssh_statename(sshstate state); +#else +#define Curl_ssh_statename(x) "" +#endif +void Curl_ssh_set_state(struct Curl_easy *data, + struct ssh_conn *sshc, + sshstate nowstate); -extern const struct Curl_handler Curl_handler_scp; -extern const struct Curl_handler Curl_handler_sftp; +#define myssh_to(x, y, z) Curl_ssh_set_state(x, y, z) /* generic SSH backend functions */ CURLcode Curl_ssh_init(void); @@ -241,11 +253,10 @@ void Curl_ssh_version(char *buffer, size_t buflen); void Curl_ssh_attach(struct Curl_easy *data, struct connectdata *conn); -#else -/* for non-SSH builds */ +#else /* !USE_SSH */ #define Curl_ssh_cleanup() #define Curl_ssh_attach(x, y) #define Curl_ssh_init() 0 -#endif +#endif /* USE_SSH */ #endif /* HEADER_CURL_SSH_H */
diff --git a/Utilities/cmcurl/lib/vssh/vssh.c b/Utilities/cmcurl/lib/vssh/vssh.c index 77fa89b..0ba4a9e 100644 --- a/Utilities/cmcurl/lib/vssh/vssh.c +++ b/Utilities/cmcurl/lib/vssh/vssh.c
@@ -21,20 +21,112 @@ * SPDX-License-Identifier: curl AND ISC * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" +#include "vssh/ssh.h" #ifdef USE_SSH -#include "vssh.h" -#include "../curlx/strparse.h" -#include "../curl_trc.h" -#include "../escape.h" +#include "vssh/vssh.h" +#include "curlx/strparse.h" +#include "curl_trc.h" +#include "escape.h" + +#ifdef CURLVERBOSE +const char *Curl_ssh_statename(sshstate state) +{ + static const char * const names[] = { + "SSH_STOP", + "SSH_INIT", + "SSH_S_STARTUP", + "SSH_HOSTKEY", + "SSH_AUTHLIST", + "SSH_AUTH_PKEY_INIT", + "SSH_AUTH_PKEY", + "SSH_AUTH_PASS_INIT", + "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", + "SSH_AUTH_HOST_INIT", + "SSH_AUTH_HOST", + "SSH_AUTH_KEY_INIT", + "SSH_AUTH_KEY", + "SSH_AUTH_GSSAPI", + "SSH_AUTH_DONE", + "SSH_SFTP_INIT", + "SSH_SFTP_REALPATH", + "SSH_SFTP_QUOTE_INIT", + "SSH_SFTP_POSTQUOTE_INIT", + "SSH_SFTP_QUOTE", + "SSH_SFTP_NEXT_QUOTE", + "SSH_SFTP_QUOTE_STAT", + "SSH_SFTP_QUOTE_SETSTAT", + "SSH_SFTP_QUOTE_SYMLINK", + "SSH_SFTP_QUOTE_MKDIR", + "SSH_SFTP_QUOTE_RENAME", + "SSH_SFTP_QUOTE_RMDIR", + "SSH_SFTP_QUOTE_UNLINK", + "SSH_SFTP_QUOTE_STATVFS", + "SSH_SFTP_GETINFO", + "SSH_SFTP_FILETIME", + "SSH_SFTP_TRANS_INIT", + "SSH_SFTP_UPLOAD_INIT", + "SSH_SFTP_CREATE_DIRS_INIT", + "SSH_SFTP_CREATE_DIRS", + "SSH_SFTP_CREATE_DIRS_MKDIR", + "SSH_SFTP_READDIR_INIT", + "SSH_SFTP_READDIR", + "SSH_SFTP_READDIR_LINK", + "SSH_SFTP_READDIR_BOTTOM", + "SSH_SFTP_READDIR_DONE", + "SSH_SFTP_DOWNLOAD_INIT", + "SSH_SFTP_DOWNLOAD_STAT", + "SSH_SFTP_CLOSE", + "SSH_SFTP_SHUTDOWN", + "SSH_SCP_TRANS_INIT", + "SSH_SCP_UPLOAD_INIT", + "SSH_SCP_DOWNLOAD_INIT", + "SSH_SCP_DOWNLOAD", + "SSH_SCP_DONE", + "SSH_SCP_SEND_EOF", + "SSH_SCP_WAIT_EOF", + "SSH_SCP_WAIT_CLOSE", + "SSH_SCP_CHANNEL_FREE", + "SSH_SESSION_DISCONNECT", + "SSH_SESSION_FREE", + "QUIT" + }; + /* a precaution to make sure the lists are in sync */ + DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST); + return ((size_t)state < CURL_ARRAYSIZE(names)) ? names[state] : ""; +} +#endif /* CURLVERBOSE */ + +/* + * SSH State machine related code + */ +/* This is the ONLY way to change SSH state! */ +void Curl_ssh_set_state(struct Curl_easy *data, + struct ssh_conn *sshc, + sshstate nowstate) +{ +#ifdef CURLVERBOSE + if(sshc->state != nowstate) { + CURL_TRC_SSH(data, "[%s] -> [%s]", + Curl_ssh_statename(sshc->state), + Curl_ssh_statename(nowstate)); + } +#else + (void)data; +#endif + sshc->state = nowstate; +} #define MAX_SSHPATH_LEN 100000 /* arbitrary */ /* figure out the path to work with in this particular request */ CURLcode Curl_getworkingpath(struct Curl_easy *data, - char *homedir, /* when SFTP is used */ + const char *homedir, /* when SFTP is used */ char **path) /* returns the allocated real path to work with */ { @@ -51,7 +143,7 @@ curlx_dyn_init(&npath, MAX_SSHPATH_LEN); /* Check for /~/, indicating relative to the user's home directory */ - if((data->conn->handler->protocol & CURLPROTO_SCP) && + if((data->conn->scheme->protocol & CURLPROTO_SCP) && (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) { /* It is referenced to the home directory, so strip the leading '/~/' */ if(curlx_dyn_addn(&npath, &working_path[3], working_path_len - 3)) { @@ -59,7 +151,7 @@ return CURLE_OUT_OF_MEMORY; } } - else if((data->conn->handler->protocol & CURLPROTO_SFTP) && + else if((data->conn->scheme->protocol & CURLPROTO_SFTP) && (!strcmp("/~", working_path) || ((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) { if(curlx_dyn_add(&npath, homedir)) { @@ -193,19 +285,19 @@ } CURLcode Curl_ssh_range(struct Curl_easy *data, - const char *p, curl_off_t filesize, + const char *range, curl_off_t filesize, curl_off_t *startp, curl_off_t *sizep) { curl_off_t from, to; int to_t; - int from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX); + int from_t = curlx_str_number(&range, &from, CURL_OFF_T_MAX); if(from_t == STRE_OVERFLOW) return CURLE_RANGE_ERROR; - curlx_str_passblanks(&p); - (void)curlx_str_single(&p, '-'); + curlx_str_passblanks(&range); + (void)curlx_str_single(&range, '-'); - to_t = curlx_str_numblanks(&p, &to); - if((to_t == STRE_OVERFLOW) || (to_t && from_t) || *p) + to_t = curlx_str_numblanks(&range, &to); + if((to_t == STRE_OVERFLOW) || (to_t && from_t) || *range) return CURLE_RANGE_ERROR; if(from_t) {
diff --git a/Utilities/cmcurl/lib/vssh/vssh.h b/Utilities/cmcurl/lib/vssh/vssh.h index 4bc6d80..492108f 100644 --- a/Utilities/cmcurl/lib/vssh/vssh.h +++ b/Utilities/cmcurl/lib/vssh/vssh.h
@@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_PATH_H -#define HEADER_CURL_PATH_H +#ifndef HEADER_CURL_VSSH_VSSH_H +#define HEADER_CURL_VSSH_VSSH_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,12 +23,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "../urldata.h" +#ifdef USE_SSH + +#include "urldata.h" CURLcode Curl_getworkingpath(struct Curl_easy *data, - char *homedir, + const char *homedir, char **path); CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir); @@ -36,5 +38,5 @@ CURLcode Curl_ssh_range(struct Curl_easy *data, const char *range, curl_off_t filesize, curl_off_t *startp, curl_off_t *sizep); - -#endif /* HEADER_CURL_PATH_H */ +#endif /* USE_SSH */ +#endif /* HEADER_CURL_VSSH_VSSH_H */
diff --git a/Utilities/cmcurl/lib/vtls/apple.c b/Utilities/cmcurl/lib/vtls/apple.c index 9779e11..e28a40c 100644 --- a/Utilities/cmcurl/lib/vtls/apple.c +++ b/Utilities/cmcurl/lib/vtls/apple.c
@@ -38,21 +38,17 @@ https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html */ -#include "../curl_setup.h" - -#include "../urldata.h" -#include "../cfilters.h" -#include "../curl_trc.h" -#include "vtls.h" -#include "apple.h" +#include "curl_setup.h" #ifdef USE_APPLE_SECTRUST + +#include "urldata.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "vtls/vtls.h" +#include "vtls/apple.h" + #include <Security/Security.h> -#endif - - -#ifdef USE_APPLE_SECTRUST -#define SSL_SYSTEM_VERIFIER #if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && \ MAC_OS_X_VERSION_MAX_ALLOWED >= 101400) || \ @@ -134,7 +130,7 @@ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); if(!ssl_config->no_revoke) { if(__builtin_available(macOS 10.9, iOS 7, tvOS 9, watchOS 2, *)) { - /* Even without this set, validation will seemingly-unavoidably fail + /* Even without this set, validation seemingly-unavoidably fails * for certificates that trustd already knows to be revoked. * This policy further allows trustd to consult CRLs and OCSP data * to determine revocation status (which it may then cache). */ @@ -146,7 +142,7 @@ * of a cert being NOT REVOKED. Which not in general available for * certificates on the Internet. * It seems that applications using this policy are expected to PIN - * their certificate public keys or verification will fail. + * their certificate public keys or verification fails. * This does not seem to be what we want here. */ if(!ssl_config->revoke_best_effort) { revocation_flags |= kSecRevocationRequirePositiveResponse; @@ -233,7 +229,7 @@ result = SecTrustEvaluateWithError(trust, &error) ? CURLE_OK : CURLE_PEER_FAILED_VERIFICATION; if(error) { - CFIndex code = CFErrorGetCode(error); + VERBOSE(CFIndex code = CFErrorGetCode(error)); error_ref = CFErrorCopyDescription(error); if(error_ref) {
diff --git a/Utilities/cmcurl/lib/vtls/apple.h b/Utilities/cmcurl/lib/vtls/apple.h index dd1b5db..d86a56b 100644 --- a/Utilities/cmcurl/lib/vtls/apple.h +++ b/Utilities/cmcurl/lib/vtls/apple.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_APPLE_SECTRUST struct Curl_cfilter;
diff --git a/Utilities/cmcurl/lib/vtls/cipher_suite.c b/Utilities/cmcurl/lib/vtls/cipher_suite.c index 562a347..1fc6e9f 100644 --- a/Utilities/cmcurl/lib/vtls/cipher_suite.c +++ b/Utilities/cmcurl/lib/vtls/cipher_suite.c
@@ -21,11 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_MBEDTLS) || defined(USE_RUSTLS) -#include "cipher_suite.h" +#include "vtls/cipher_suite.h" /* * To support the CURLOPT_SSL_CIPHER_LIST option on SSL backends @@ -159,13 +159,11 @@ /* !checksrc! disable COMMANOSPACE all */ static const struct cs_entry cs_list[] = { /* TLS 1.3 ciphers */ -#if defined(USE_MBEDTLS) || defined(USE_RUSTLS) CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,), CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,), CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,), CS_ENTRY(0x1304, TLS,AES,128,CCM,SHA256,,,), CS_ENTRY(0x1305, TLS,AES,128,CCM,8,SHA256,,), -#endif /* TLS 1.2 ciphers */ CS_ENTRY(0xC02B, TLS,ECDHE,ECDSA,WITH,AES,128,GCM,SHA256), CS_ENTRY(0xC02B, ECDHE,ECDSA,AES128,GCM,SHA256,,,), @@ -232,8 +230,6 @@ CS_ENTRY(0xC031, ECDH,RSA,AES128,GCM,SHA256,,,), CS_ENTRY(0xC032, TLS,ECDH,RSA,WITH,AES,256,GCM,SHA384), CS_ENTRY(0xC032, ECDH,RSA,AES256,GCM,SHA384,,,), -#endif -#ifdef USE_MBEDTLS CS_ENTRY(0x0001, TLS,RSA,WITH,NULL,MD5,,,), CS_ENTRY(0x0001, NULL,MD5,,,,,,), CS_ENTRY(0x0002, TLS,RSA,WITH,NULL,SHA,,,), @@ -320,8 +316,6 @@ CS_ENTRY(0xC036, ECDHE,PSK,AES256,CBC,SHA,,,), CS_ENTRY(0xCCAB, TLS,PSK,WITH,CHACHA20,POLY1305,SHA256,,), CS_ENTRY(0xCCAB, PSK,CHACHA20,POLY1305,,,,,), -#endif -#ifdef USE_MBEDTLS CS_ENTRY(0xC09C, TLS,RSA,WITH,AES,128,CCM,,), CS_ENTRY(0xC09C, AES128,CCM,,,,,,), CS_ENTRY(0xC09D, TLS,RSA,WITH,AES,256,CCM,,), @@ -338,8 +332,6 @@ CS_ENTRY(0xC0AE, ECDHE,ECDSA,AES128,CCM8,,,,), CS_ENTRY(0xC0AF, TLS,ECDHE,ECDSA,WITH,AES,256,CCM,8), CS_ENTRY(0xC0AF, ECDHE,ECDSA,AES256,CCM8,,,,), -#endif -#ifdef USE_MBEDTLS /* entries marked ns are "non-standard", they are not in OpenSSL */ CS_ENTRY(0x0041, TLS,RSA,WITH,CAMELLIA,128,CBC,SHA,), CS_ENTRY(0x0041, CAMELLIA128,SHA,,,,,,),
diff --git a/Utilities/cmcurl/lib/vtls/cipher_suite.h b/Utilities/cmcurl/lib/vtls/cipher_suite.h index a17284f..0e185e3 100644 --- a/Utilities/cmcurl/lib/vtls/cipher_suite.h +++ b/Utilities/cmcurl/lib/vtls/cipher_suite.h
@@ -23,10 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_MBEDTLS) || defined(USE_RUSTLS) -#include <stdint.h> /* Lookup IANA id for cipher suite string, returns 0 if not recognized */ uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len);
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index 934273e..9b6a4fa 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -28,7 +28,7 @@ * Note: do not use the GnuTLS' *_t variable type names in this source code, * since they were not present in 1.0.X. */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_GNUTLS @@ -38,24 +38,26 @@ #include <gnutls/crypto.h> #include <nettle/sha2.h> -#include "../urldata.h" -#include "../curl_trc.h" -#include "keylog.h" -#include "gtls.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "apple.h" -#include "../vauth/vauth.h" -#include "../parsedate.h" -#include "../connect.h" /* for the connect timeout */ -#include "../progress.h" -#include "../strdup.h" -#include "../curlx/fopen.h" -#include "x509asn1.h" +#include "urldata.h" +#include "curl_trc.h" +#include "vtls/keylog.h" +#include "vtls/gtls.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/apple.h" +#include "vauth/vauth.h" +#include "parsedate.h" +#include "connect.h" /* for the connect timeout */ +#include "progress.h" +#include "curlx/strdup.h" +#include "curlx/fopen.h" +#include "vtls/x509asn1.h" /* Enable GnuTLS debugging by defining GTLSDEBUG */ -/*#define GTLSDEBUG */ +#if 0 +#define GTLSDEBUG +#endif #ifdef GTLSDEBUG static void tls_log_func(int level, const char *str) @@ -97,7 +99,7 @@ if(result) { /* !checksrc! disable ERRNOVAR 1 */ gnutls_transport_set_errno(backend->gtls.session, - (CURLE_AGAIN == result) ? EAGAIN : EINVAL); + (result == CURLE_AGAIN) ? EAGAIN : EINVAL); return -1; } return (ssize_t)nwritten; @@ -125,12 +127,12 @@ } result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zd", blen, result, nread); + CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zu", blen, result, nread); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ gnutls_transport_set_errno(backend->gtls.session, - (CURLE_AGAIN == result) ? EAGAIN : EINVAL); + (result == CURLE_AGAIN) ? EAGAIN : EINVAL); return -1; } else if(nread == 0) @@ -166,7 +168,7 @@ Curl_tls_keylog_close(); } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static void showtime(struct Curl_easy *data, const char *text, time_t stamp) { struct tm buffer; @@ -201,10 +203,15 @@ f = curlx_fopen(file, "rb"); if(!f) return loaded_file; - if(fseek(f, 0, SEEK_END) != 0 || - (filelen = ftell(f)) < 0 || - fseek(f, 0, SEEK_SET) != 0 || - !(ptr = curlx_malloc((size_t)filelen))) + if(fseek(f, 0, SEEK_END) != 0) + goto out; + filelen = ftell(f); + if(filelen < 0 || filelen > CURL_MAX_INPUT_LENGTH) + goto out; + if(fseek(f, 0, SEEK_SET) != 0) + goto out; + ptr = curlx_malloc((size_t)filelen); + if(!ptr) goto out; if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) { curlx_free(ptr); @@ -255,6 +262,7 @@ return CURLE_AGAIN; } else if((rc < 0) && !gnutls_error_is_fatal(rc)) { +#ifdef CURLVERBOSE const char *strerr = NULL; if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { @@ -266,6 +274,7 @@ strerr = gnutls_strerror(rc); infof(data, "gnutls_handshake() warning: %s", strerr); +#endif return CURLE_AGAIN; } else if((rc < 0) && backend->gtls.io_result) { @@ -302,7 +311,7 @@ #define GNUTLS_CIPHERS "NORMAL:%PROFILE_MEDIUM:-ARCFOUR-128:" \ "-CTYPE-ALL:+CTYPE-X509" -/* If GnuTLS was compiled without support for SRP it will error out if SRP is +/* If GnuTLS was compiled without support for SRP it errors out if SRP is requested in the priority string, so treat it specially */ #define GNUTLS_SRP "+SRP" @@ -323,8 +332,8 @@ long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; - if((ssl_version == CURL_SSLVERSION_DEFAULT) || - (ssl_version == CURL_SSLVERSION_TLSv1)) + DEBUGASSERT(ssl_version != CURL_SSLVERSION_DEFAULT); + if(ssl_version <= CURL_SSLVERSION_TLSv1) ssl_version = CURL_SSLVERSION_TLSv1_0; if((ssl_version_max == CURL_SSLVERSION_MAX_NONE) || (ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT)) @@ -708,7 +717,7 @@ /* get the session ID data size */ gnutls_session_get_data(session, NULL, &sdata_len); - if(!sdata_len) /* gnutls does this for some version combinations */ + if(!sdata_len) /* GnuTLS does this for some version combinations */ return CURLE_OK; sdata = curlx_malloc(sdata_len); /* get a buffer for it */ @@ -723,7 +732,7 @@ "and store in cache", sdata_len, alpn ? alpn : "-", earlydata_max); if(quic_tp && quic_tp_len) { - qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len); + qtp_clone = curlx_memdup0((const char *)quic_tp, quic_tp_len); if(!qtp_clone) { curlx_free(sdata); return CURLE_OUT_OF_MEMORY; @@ -784,7 +793,7 @@ if(when) { /* after message has been processed */ struct Curl_easy *data = CF_DATA_CURRENT(cf); if(data) { - CURL_TRC_CF(data, cf, "handshake: %s message type %d", + CURL_TRC_CF(data, cf, "handshake: %s message type %u", incoming ? "incoming" : "outgoing", htype); switch(htype) { case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { @@ -816,7 +825,7 @@ #ifdef USE_GNUTLS_SRP if(conn_config->username) { /* Only add SRP to the cipher list if SRP is requested. Otherwise - * GnuTLS will disable TLS 1.3 support. */ + * GnuTLS disables TLS 1.3 support. */ result = curlx_dyn_add(&buf, priority); if(!result) result = curlx_dyn_add(&buf, ":" GNUTLS_SRP); @@ -1079,29 +1088,14 @@ struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; - CURLcode result = CURLE_OK; - *do_early_data = FALSE; connssl->earlydata_max = gnutls_record_get_max_early_data_size(backend->gtls.session); - if((!connssl->earlydata_max || connssl->earlydata_max == 0xFFFFFFFFUL)) { - /* Seems to be no GnuTLS way to signal no EarlyData in session */ - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); - } - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); - connssl->earlydata_state = ssl_earlydata_await; - connssl->state = ssl_connection_deferred; - result = Curl_alpn_set_negotiated(cf, data, connssl, - (const unsigned char *)scs->alpn, - scs->alpn ? strlen(scs->alpn) : 0); - *do_early_data = !result; - } - return result; + + /* Seems to be no GnuTLS way to signal no EarlyData in session */ + return Curl_on_session_reuse(cf, data, alpns, scs, do_early_data, + connssl->earlydata_max && + connssl->earlydata_max != 0xFFFFFFFFUL); } #endif @@ -1129,7 +1123,7 @@ Curl_alpn_copy(&alpns, alpns_requested); /* This might be a reconnect, so we check for a session ID in the cache - to speed up things. We need to do this before constructing the gnutls + to speed up things. We need to do this before constructing the GnuTLS session since we need to set flags depending on the kind of reuse. */ if(conn_config->cache_session && !conn_config->verifystatus) { result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs); @@ -1151,7 +1145,8 @@ else { infof(data, "SSL reusing session with ALPN '%s'", scs->alpn ? scs->alpn : "-"); - if(ssl_config->earlydata && scs->alpn && !cf->conn->connect_only) { + if(ssl_config->earlydata && scs->alpn && + !cf->conn->bits.connect_only) { bool do_early_data = FALSE; if(sess_reuse_cb) { result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); @@ -1191,7 +1186,7 @@ #endif /* convert the ALPN string from our arguments to a list of strings that - * gnutls wants and will convert internally back to this string for sending + * GnuTLS wants and does convert internally back to this string for sending * to the server. nice. */ if(!gtls_alpns_count && alpns.count) { size_t i; @@ -1319,14 +1314,14 @@ if(key) gnutls_pubkey_deinit(key); - Curl_safefree(buff1); + curlx_safefree(buff1); return result; } void Curl_gtls_report_handshake(struct Curl_easy *data, struct gtls_ctx *gctx) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { const char *ptr; gnutls_protocol_t version = gnutls_protocol_get_version(gctx->session); @@ -1379,7 +1374,7 @@ static void gtls_infof_cert(struct Curl_easy *data, gnutls_x509_crt_t x509_cert) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { gnutls_datum_t certfields; int rc, algo; @@ -1572,8 +1567,6 @@ result = Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, gtls_chain_get_der, chain, NULL, 0); *pverified = !result; - if(*pverified) - infof(data, " SSL certificate verified by Apple SecTrust."); return result; } #endif /* USE_APPLE_SECTRUST */ @@ -1594,7 +1587,7 @@ long * const certverifyresult = &ssl_config->certverifyresult; (void)cf; - /* This function will return the peer's raw certificate (chain) as sent by + /* This function returns the peer's raw certificate (chain) as sent by the peer. These certificates are in raw format (DER encoded for X.509). In case of a X.509 then a certificate list may be present. The first certificate in the list is the peer's certificate, following the @@ -1626,7 +1619,7 @@ if(data->set.ssl.certinfo && chain.certs) { if(chain.num_certs > MAX_ALLOWED_CERT_AMOUNT) { - failf(data, "%u certificates is more than allowed (%u)", + failf(data, "%u certificates is more than allowed (%d)", chain.num_certs, MAX_ALLOWED_CERT_AMOUNT); result = CURLE_SSL_CONNECT_ERROR; goto out; @@ -1652,7 +1645,7 @@ if(config->verifypeer) { bool verified = FALSE; unsigned int verify_status = 0; - /* This function will try to verify the peer's certificate and return + /* This function tries to verify the peer's certificate and return its status (trusted, invalid etc.). The value of status should be one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd. To avoid denial of service attacks some default @@ -1708,7 +1701,7 @@ /* initialize an X.509 certificate structure. */ if(gnutls_x509_crt_init(&x509_cert)) { - failf(data, "failed to init gnutls x509_crt"); + failf(data, "failed to init GnuTLS x509_crt"); *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; result = CURLE_SSL_CONNECT_ERROR; goto out; @@ -1792,7 +1785,7 @@ if(config->issuercert) { gnutls_datum_t issuerp; if(gnutls_x509_crt_init(&x509_issuer)) { - failf(data, "failed to init gnutls x509_crt for issuer"); + failf(data, "failed to init GnuTLS x509_crt for issuer"); result = CURLE_SSL_ISSUER_ERROR; goto out; } @@ -1811,7 +1804,7 @@ config->issuercert ? config->issuercert : "none"); } - /* This function will check if the given certificate's subject matches the + /* This function checks if the given certificate's subject matches the given hostname. This is a basic implementation of the matching described in RFC2818 (HTTPS), which takes into account wildcards, and the subject alternative name PKIX extension. Returns non zero on success, and zero on @@ -1905,7 +1898,7 @@ goto out; } else if(!n) { - /* gnutls is buggy, it *SHOULD* return the amount of bytes it took in. + /* GnuTLS is buggy, it *SHOULD* return the amount of bytes it took in. * Instead it returns 0 if everything was written. */ n = (ssize_t)blen; } @@ -2069,7 +2062,6 @@ ssize_t nwritten; size_t remain = blen; - (void)data; DEBUGASSERT(backend); *pnwritten = 0; @@ -2186,7 +2178,6 @@ struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; - (void)data; DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "close"); if(backend->gtls.session) { @@ -2215,7 +2206,6 @@ CURLcode result = CURLE_OK; ssize_t nread; - (void)data; DEBUGASSERT(backend); nread = gnutls_record_recv(backend->gtls.session, buf, blen); @@ -2299,7 +2289,9 @@ SSLSUPP_HTTPS_PROXY | SSLSUPP_CAINFO_BLOB | SSLSUPP_CIPHER_LIST | - SSLSUPP_CA_CACHE, + SSLSUPP_CA_CACHE | + SSLSUPP_ISSUERCERT | + SSLSUPP_CRLFILE, sizeof(struct gtls_ssl_backend_data),
diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h index ecdca0a..49106dd 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.h +++ b/Utilities/cmcurl/lib/vtls/gtls.h
@@ -23,12 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_GNUTLS #include <gnutls/gnutls.h> -#include "../curlx/timeval.h" + +#include "curlx/timeval.h" #ifdef HAVE_GNUTLS_SRP /* the function exists */ @@ -88,7 +89,7 @@ struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - const struct alpn_spec *alpns, + const struct alpn_spec *alpns_requested, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data,
diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.c b/Utilities/cmcurl/lib/vtls/hostcheck.c index 9706336..ad09b83 100644 --- a/Utilities/cmcurl/lib/vtls/hostcheck.c +++ b/Utilities/cmcurl/lib/vtls/hostcheck.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_OPENSSL) || defined(USE_SCHANNEL) /* these backends use functions from this file */ @@ -32,9 +32,10 @@ #ifdef HAVE_NETINET_IN6_H #include <netinet/in6.h> #endif -#include "../curl_memrchr.h" -#include "hostcheck.h" -#include "../hostip.h" + +#include "curl_memrchr.h" +#include "vtls/hostcheck.h" +#include "hostip.h" /* check the two input strings with given length, but do not assume they end in nul-bytes */
diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.h b/Utilities/cmcurl/lib/vtls/hostcheck.h index d62c721..d8e1a52 100644 --- a/Utilities/cmcurl/lib/vtls/hostcheck.h +++ b/Utilities/cmcurl/lib/vtls/hostcheck.h
@@ -23,12 +23,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_OPENSSL) || defined(USE_SCHANNEL) /* returns TRUE if there is a match */ -bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen, +bool Curl_cert_hostcheck(const char *match, size_t matchlen, const char *hostname, size_t hostlen); #endif
diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c index ace9cab..4ae2387 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.c +++ b/Utilities/cmcurl/lib/vtls/keylog.c
@@ -21,28 +21,24 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" +#include "vtls/keylog.h" -#if defined(USE_OPENSSL) || \ - defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || \ - (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ - defined(USE_QUICHE) || \ +#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ defined(USE_RUSTLS) -#include "keylog.h" -#include "../escape.h" -#include "../curlx/fopen.h" +#include "escape.h" +#include "curlx/fopen.h" /* The fp for the open SSLKEYLOGFILE, or NULL if not open */ static FILE *keylog_file_fp; +/* Used for verbose logging */ +static char *keylog_file_name; void Curl_tls_keylog_open(void) { - char *keylog_file_name; - if(!keylog_file_fp) { - keylog_file_name = curl_getenv("SSLKEYLOGFILE"); + keylog_file_name = getenv("SSLKEYLOGFILE"); if(keylog_file_name) { keylog_file_fp = curlx_fopen(keylog_file_name, FOPEN_APPENDTEXT); if(keylog_file_fp) { @@ -56,7 +52,6 @@ keylog_file_fp = NULL; } } - Curl_safefree(keylog_file_name); } } } @@ -74,6 +69,11 @@ return keylog_file_fp != NULL; } +const char *Curl_tls_keylog_file_name(void) +{ + return keylog_file_name; +} + bool Curl_tls_keylog_write_line(const char *line) { /* The current maximum valid keylog line length LF and NUL is 195. */ @@ -107,8 +107,9 @@ const unsigned char *secret, size_t secretlen) { size_t pos, i; - unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 + - 2 * SECRET_MAXLEN + 1 + 1]; + unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + + (2 * CLIENT_RANDOM_SIZE) + 1 + + (2 * SECRET_MAXLEN) + 1 + 1]; if(!keylog_file_fp) { return FALSE; @@ -144,4 +145,16 @@ return TRUE; } -#endif /* TLS or QUIC backend */ +#else /* TLS backend */ + +bool Curl_tls_keylog_enabled(void) +{ + return FALSE; +} + +const char *Curl_tls_keylog_file_name(void) +{ + return NULL; +} + +#endif /* TLS backend */
diff --git a/Utilities/cmcurl/lib/vtls/keylog.h b/Utilities/cmcurl/lib/vtls/keylog.h index ec82abf..68ded47 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.h +++ b/Utilities/cmcurl/lib/vtls/keylog.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) @@ -53,12 +53,17 @@ bool Curl_tls_keylog_enabled(void); /* + * Returns a pointer to the filename keys are being written to, if enabled. + */ +const char *Curl_tls_keylog_file_name(void); + +/* * Appends a key log file entry. * Returns true iff the key log file is open and a valid entry was provided. */ bool Curl_tls_keylog_write(const char *label, - const unsigned char client_random[32], - const unsigned char *secret, size_t secretlen); + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen); /* * Appends a line to the key log file, ensure it is terminated by an LF.
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index dff45a3..e282266 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -27,7 +27,7 @@ * but vtls.c should ever call or use these functions. * */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_MBEDTLS @@ -56,17 +56,18 @@ #ifdef MBEDTLS_DEBUG #include <mbedtls/debug.h> #endif -#include "cipher_suite.h" -#include "../urldata.h" -#include "../curl_trc.h" -#include "mbedtls.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "x509asn1.h" -#include "../connect.h" /* for the connect timeout */ -#include "../strdup.h" -#include "../curl_sha256.h" + +#include "vtls/cipher_suite.h" +#include "urldata.h" +#include "curl_trc.h" +#include "vtls/mbedtls.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/x509asn1.h" +#include "connect.h" /* for the connect timeout */ +#include "curlx/strdup.h" +#include "curl_sha256.h" /* ALPN for http2 */ #if defined(USE_HTTP2) && defined(MBEDTLS_SSL_ALPN) @@ -141,7 +142,7 @@ result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %d, %zu", blen, result, nwritten); - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_WRITE; return result ? -1 : (int)nwritten; } @@ -163,7 +164,7 @@ result = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &nread); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %d, %zu", blen, result, nread); - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_READ; /* nread is never larger than int here */ return result ? -1 : (int)nread; @@ -172,8 +173,8 @@ /* See: * https://web.archive.org/web/20200921194007/tls.mbed.org/discussions/generic/howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der */ -#define RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE) -#define ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES) +#define RSA_PUB_DER_MAX_BYTES (38 + (2 * MBEDTLS_MPI_MAX_SIZE)) +#define ECP_PUB_DER_MAX_BYTES (30 + (2 * MBEDTLS_ECP_MAX_BYTES)) #define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) @@ -198,8 +199,8 @@ #endif ; + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -245,10 +246,10 @@ return CURLE_OK; } -/* TLS_ECJPAKE_WITH_AES_128_CCM_8 (0xC0FF) is marked experimental - in mbedTLS. The number is not reserved by IANA nor is the - cipher suite present in other SSL implementations. Provide - provisional support for specifying the cipher suite here. */ +/* TLS_ECJPAKE_WITH_AES_128_CCM_8 (0xC0FF) is marked experimental in mbedTLS. + The number is not reserved by IANA nor is the cipher suite present in other + SSL implementations. Provide provisional support for specifying the cipher + suite here. */ #ifdef MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 static int mbed_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, bool prefer_rfc) @@ -264,9 +265,11 @@ { uint16_t id = Curl_cipher_suite_walk_str(str, end); size_t len = *end - *str; + static const char ecjpake_suite[] = "TLS_ECJPAKE_WITH_AES_128_CCM_8"; if(!id) { - if(curl_strnequal("TLS_ECJPAKE_WITH_AES_128_CCM_8", *str, len)) + if((len == sizeof(ecjpake_suite) - 1) && + curl_strnequal(ecjpake_suite, *str, len)) id = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8; } return id; @@ -389,7 +392,7 @@ static void mbed_dump_cert_info(struct Curl_easy *data, const mbedtls_x509_crt *crt) { -#if defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(MBEDTLS_X509_REMOVE_INFO) +#if !defined(CURLVERBOSE) || defined(MBEDTLS_X509_REMOVE_INFO) (void)data, (void)crt; #else const size_t bufsize = 16384; @@ -422,7 +425,7 @@ cert_count++; if(cert_count > MAX_ALLOWED_CERT_AMOUNT) { - infof(data, "Certificates is more than allowed (%u), skipping certinfo", + infof(data, "More certificates than allowed (%d), skipping certinfo", MAX_ALLOWED_CERT_AMOUNT); return; } @@ -468,62 +471,46 @@ return 0; } -static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ (ca_info_blob ? NULL : conn_config->CAfile); const bool verifypeer = conn_config->verifypeer; const char * const ssl_capath = conn_config->CApath; - char * const ssl_cert = ssl_config->primary.clientcert; - const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; #ifdef MBEDTLS_PEM_PARSE_C + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); const char * const ssl_cert_type = ssl_config->cert_type; #endif - const char * const ssl_crlfile = ssl_config->primary.CRLfile; - const char *hostname = connssl->peer.hostname; int ret = -1; char errorbuf[128]; - DEBUGASSERT(backend); - DEBUGASSERT(!backend->initialized); - - if((conn_config->version == CURL_SSLVERSION_SSLv2) || - (conn_config->version == CURL_SSLVERSION_SSLv3)) { - failf(data, "Not supported SSL version"); - return CURLE_NOT_BUILT_IN; - } - - /* Load the trusted CA */ mbedtls_x509_crt_init(&backend->cacert); if(ca_info_blob && verifypeer) { #ifdef MBEDTLS_PEM_PARSE_C - /* if DER or a null-terminated PEM just process using + /* if DER or a null-terminated PEM process using mbedtls_x509_crt_parse(). */ if((ssl_cert_type && curl_strequal(ssl_cert_type, "DER")) || ((char *)(ca_info_blob->data))[ca_info_blob->len - 1] == '\0') { - - ret = mbedtls_x509_crt_parse(&backend->cacert, - ca_info_blob->data, + ret = mbedtls_x509_crt_parse(&backend->cacert, ca_info_blob->data, ca_info_blob->len); } else { /* they say it is PEM and it is not null-terminated */ - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to - be null-terminated if the data is PEM encoded (even when - provided the exact length). The function accepts PEM or DER - formats, but we cannot assume if the user passed in a PEM - format cert that it is null-terminated. */ - unsigned char *newblob = Curl_memdup0(ca_info_blob->data, - ca_info_blob->len); + /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be + null-terminated if the data is PEM encoded (even when provided the + exact length). The function accepts PEM or DER formats, but we cannot + assume if the user passed in a PEM format cert that it is + null-terminated. */ + unsigned char *newblob = curlx_memdup0(ca_info_blob->data, + ca_info_blob->len); if(!newblob) return CURLE_OUT_OF_MEMORY; @@ -532,12 +519,10 @@ curlx_free(newblob); } #else - /* DER encoded certs do not need to be null terminated - because it is a binary format. So if we are not compiling - with PEM_PARSE we can avoid the extra memory copies - altogether. */ - ret = mbedtls_x509_crt_parse_der(&backend->cacert, - ca_info_blob->data, + /* DER encoded certs do not need to be null-terminated because it is a + binary format. Thus, if we are not compiling with PEM_PARSE we can avoid + the extra memory copies altogether. */ + ret = mbedtls_x509_crt_parse_der(&backend->cacert, ca_info_blob->data, ca_info_blob->len); #endif @@ -583,7 +568,24 @@ #endif } - /* Load the client certificate */ + return CURLE_OK; +} + +static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + char * const ssl_cert = ssl_config->primary.clientcert; + const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; +#ifdef MBEDTLS_PEM_PARSE_C + const char * const ssl_cert_type = ssl_config->cert_type; +#endif + int ret = -1; + char errorbuf[128]; + mbedtls_x509_crt_init(&backend->clicert); if(ssl_cert) { @@ -605,7 +607,7 @@ if(ssl_cert_blob) { #ifdef MBEDTLS_PEM_PARSE_C - /* if DER or a null-terminated PEM just process using + /* if DER or a null-terminated PEM process using mbedtls_x509_crt_parse(). */ if((ssl_cert_type && curl_strequal(ssl_cert_type, "DER")) || ((char *)(ssl_cert_blob->data))[ssl_cert_blob->len - 1] == '\0') { @@ -616,13 +618,13 @@ } else { /* they say it is PEM and it is not null-terminated */ - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to - be null-terminated if the data is PEM encoded (even when - provided the exact length). The function accepts PEM or DER - formats, but we cannot assume if the user passed in a PEM - format cert that it is null-terminated. */ - unsigned char *newblob = Curl_memdup0(ssl_cert_blob->data, - ssl_cert_blob->len); + /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be + null-terminated if the data is PEM encoded (even when provided the + exact length). The function accepts PEM or DER formats, but we cannot + assume if the user passed in a PEM format cert that it is + null-terminated. */ + unsigned char *newblob = curlx_memdup0(ssl_cert_blob->data, + ssl_cert_blob->len); if(!newblob) return CURLE_OUT_OF_MEMORY; ret = mbedtls_x509_crt_parse(&backend->clicert, newblob, @@ -630,24 +632,34 @@ curlx_free(newblob); } #else - /* DER encoded certs do not need to be null terminated - because it is a binary format. So if we are not compiling - with PEM_PARSE we can avoid the extra memory copies - altogether. */ - ret = mbedtls_x509_crt_parse_der(&backend->clicert, - ssl_cert_blob->data, + /* DER encoded certs do not need to be null-terminated because it is a + binary format. Thus, if we are not compiling with PEM_PARSE we can avoid + the extra memory copies altogether. */ + ret = mbedtls_x509_crt_parse_der(&backend->clicert, ssl_cert_blob->data, ssl_cert_blob->len); #endif if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedTLS: error reading client cert data %s: (-0x%04X) %s", - ssl_config->key, -ret, errorbuf); + failf(data, "mbedTLS: error reading client cert blob: (-0x%04X) %s", + -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } - /* Load the client private key */ + return CURLE_OK; +} + +static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + int ret = -1; + char errorbuf[128]; + mbedtls_pk_init(&backend->pk); if(ssl_config->key || ssl_config->key_blob) { @@ -656,12 +668,13 @@ #if MBEDTLS_VERSION_NUMBER >= 0x04000000 ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, ssl_config->key_passwd); - if(ret == 0 && !(mbedtls_pk_can_do_psa(&backend->pk, - PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), - PSA_KEY_USAGE_SIGN_HASH) || - mbedtls_pk_can_do_psa(&backend->pk, - MBEDTLS_PK_ALG_ECDSA(PSA_ALG_ANY_HASH), - PSA_KEY_USAGE_SIGN_HASH))) + if(ret == 0 && + !(mbedtls_pk_can_do_psa(&backend->pk, + PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), + PSA_KEY_USAGE_SIGN_HASH) || + mbedtls_pk_can_do_psa(&backend->pk, + MBEDTLS_PK_ALG_ECDSA(PSA_ALG_ANY_HASH), + PSA_KEY_USAGE_SIGN_HASH))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #else ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, @@ -693,12 +706,13 @@ ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0); - if(ret == 0 && !(mbedtls_pk_can_do_psa(&backend->pk, - PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), - PSA_KEY_USAGE_SIGN_HASH) || - mbedtls_pk_can_do_psa(&backend->pk, - MBEDTLS_PK_ALG_ECDSA(PSA_ALG_ANY_HASH), - PSA_KEY_USAGE_SIGN_HASH))) + if(ret == 0 && + !(mbedtls_pk_can_do_psa(&backend->pk, + PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), + PSA_KEY_USAGE_SIGN_HASH) || + mbedtls_pk_can_do_psa(&backend->pk, + MBEDTLS_PK_ALG_ECDSA(PSA_ALG_ANY_HASH), + PSA_KEY_USAGE_SIGN_HASH))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #else ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, @@ -720,13 +734,25 @@ } } - /* Load the CRL */ + return CURLE_OK; +} + +static CURLcode mbed_load_crl(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + const char * const ssl_crlfile = ssl_config->primary.CRLfile; + #ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl_init(&backend->crl); if(ssl_crlfile) { + char errorbuf[128]; #ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); + int ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); @@ -736,18 +762,36 @@ return CURLE_SSL_CRL_BADFILE; } #else + (void)errorbuf; failf(data, "mbedTLS: functions that use the file system not built in"); return CURLE_NOT_BUILT_IN; #endif } #else + (void)backend; if(ssl_crlfile) { failf(data, "mbedTLS: CRL support not built in"); return CURLE_NOT_BUILT_IN; } #endif - infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->peer.port); + return CURLE_OK; +} + +static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + int ret; + CURLcode result; + char errorbuf[128]; + + infof(data, "mbedTLS: Connecting to %s:%d", + connssl->peer.hostname, connssl->peer.port); mbedtls_ssl_config_init(&backend->config); ret = mbedtls_ssl_config_defaults(&backend->config, @@ -772,11 +816,13 @@ #endif #if defined(MBEDTLS_SSL_SESSION_TICKETS) && \ - MBEDTLS_VERSION_NUMBER >= 0x03060100 && MBEDTLS_VERSION_NUMBER < 0x04000000 - /* New in mbedTLS 3.6.1, need to enable, default is now disabled. - 4.0.0 enabled it by default for TLSv1.3. */ - mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config, - MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); + defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + MBEDTLS_VERSION_NUMBER >= 0x03060100 && \ + MBEDTLS_VERSION_NUMBER < 0x04000000 + /* New in mbedTLS 3.6.1, need to enable, default is now disabled. 4.0.0 + enabled it by default for TLSv1.3. */ + mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets( + &backend->config, MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); #endif /* Always let mbedTLS verify certificates, if verifypeer or verifyhost are @@ -792,20 +838,21 @@ mbedtls_ssl_conf_cert_profile(&backend->config, &mbedtls_x509_crt_profile_next); - ret = mbed_set_ssl_version_min_max(data, backend, conn_config); - if(ret != CURLE_OK) - return ret; + result = mbed_set_ssl_version_min_max(data, backend, conn_config); + if(result) + return result; #if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, &rng.drbg); #endif - ret = mbedtls_ssl_setup(&backend->ssl, &backend->config); + ret = mbedtls_ssl_setup(&backend->ssl, + &backend->config); if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedTLS: ssl_setup failed: (-0x%04X) %s", - -ret, errorbuf); + failf(data, "mbedTLS: ssl_setup failed: " + "(-0x%04X) %s", -ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -816,16 +863,15 @@ #ifndef MBEDTLS_SSL_PROTO_TLS1_3 if(conn_config->cipher_list) { - CURLcode result = mbed_set_selected_ciphers(data, backend, - conn_config->cipher_list, - NULL); + result = mbed_set_selected_ciphers(data, backend, + conn_config->cipher_list, NULL); #else if(conn_config->cipher_list || conn_config->cipher_list13) { - CURLcode result = mbed_set_selected_ciphers(data, backend, - conn_config->cipher_list, - conn_config->cipher_list13); + result = mbed_set_selected_ciphers(data, backend, + conn_config->cipher_list, + conn_config->cipher_list13); #endif - if(result != CURLE_OK) { + if(result) { failf(data, "mbedTLS: failed to set cipher suites"); return result; } @@ -848,11 +894,9 @@ /* Check if there is a cached ID we can/should use here! */ if(Curl_ssl_scache_use(cf, data)) { struct Curl_ssl_session *sc_session = NULL; - CURLcode result; - - result = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key, - &sc_session); - if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) { + CURLcode sresult = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key, + &sc_session); + if(!sresult && sc_session && sc_session->sdata && sc_session->sdata_len) { mbedtls_ssl_session session; mbedtls_ssl_session_init(&session); @@ -870,27 +914,28 @@ } mbedtls_ssl_session_free(&session); } - Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session); + Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, + sc_session); } - mbedtls_ssl_conf_ca_chain(&backend->config, - &backend->cacert, + mbedtls_ssl_conf_ca_chain(&backend->config, &backend->cacert, #ifdef MBEDTLS_X509_CRL_PARSE_C - &backend->crl); + &backend->crl #else - NULL); + NULL #endif + ); if(ssl_config->key || ssl_config->key_blob) { - mbedtls_ssl_conf_own_cert(&backend->config, - &backend->clicert, &backend->pk); + mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert, + &backend->pk); } if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and - the name to set in the SNI extension. So even if curl connects to a - host specified as an IP address, this function must be used. */ + the name to set in the SNI extension. Thus even if curl connects to + a host specified as an IP address, this function must be used. */ failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -917,30 +962,59 @@ /* give application a chance to interfere with mbedTLS set up. */ if(data->set.ssl.fsslctx) { - CURLcode result = (*data->set.ssl.fsslctx)(data, &backend->config, - data->set.ssl.fsslctxp); - if(result != CURLE_OK) { + result = (*data->set.ssl.fsslctx)(data, &backend->config, + data->set.ssl.fsslctxp); + if(result) failf(data, "error signaled by ssl ctx callback"); - return result; - } } - connssl->connecting_state = ssl_connect_2; + return result; +} +static CURLcode mbed_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); + CURLcode result; + + if((conn_config->version == CURL_SSLVERSION_SSLv2) || + (conn_config->version == CURL_SSLVERSION_SSLv3)) { + failf(data, "Not supported SSL version"); + return CURLE_NOT_BUILT_IN; + } + + result = mbed_load_cacert(cf, data); + if(!result) + result = mbed_load_clicert(cf, data); + if(!result) + result = mbed_load_privkey(cf, data); + if(!result) + result = mbed_load_crl(cf, data); + if(!result) + result = mbed_configure_ssl(cf, data); + if(result) + return result; + + connssl->connecting_state = ssl_connect_2; return CURLE_OK; } +#if defined(MBEDTLS_PK_WRITE_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +#define HAVE_PINNED_PUBKEY +#endif + static CURLcode mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { -#if defined(MBEDTLS_PK_WRITE_C) || defined(HAS_ALPN_MBEDTLS) +#if defined(HAVE_PINNED_PUBKEY) || defined(HAS_ALPN_MBEDTLS) CURLcode result; #endif int ret; struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; -#ifdef MBEDTLS_PK_WRITE_C +#ifdef HAVE_PINNED_PUBKEY #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : @@ -986,7 +1060,7 @@ mbedtls_ssl_get_version(&backend->ssl), cipher_str); } -#ifdef MBEDTLS_PK_WRITE_C +#ifdef HAVE_PINNED_PUBKEY if(pinnedpubkey) { int size; const mbedtls_x509_crt *peercert; @@ -1133,13 +1207,12 @@ CURLcode result = CURLE_OK; int nwritten; - (void)data; DEBUGASSERT(backend); *pnwritten = 0; connssl->io_need = CURL_SSL_IO_NEED_NONE; /* mbedTLS is picky when a mbedtls_ssl_write() was previously blocked. * It requires to be called with the same amount of bytes again, or it - * will lose bytes, e.g. reporting all was sent but they were not. + * loses bytes, e.g. reporting all was sent but they were not. * Remember the blocked length and use that when set. */ if(backend->send_blocked) { DEBUGASSERT(backend->send_blocked_len <= len); @@ -1282,15 +1355,15 @@ (void)data; DEBUGASSERT(backend); - if(backend->initialized) { - mbedtls_pk_free(&backend->pk); - mbedtls_x509_crt_free(&backend->clicert); - mbedtls_x509_crt_free(&backend->cacert); + mbedtls_pk_free(&backend->pk); + mbedtls_x509_crt_free(&backend->clicert); + mbedtls_x509_crt_free(&backend->cacert); #ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl_free(&backend->crl); + mbedtls_x509_crl_free(&backend->crl); #endif - Curl_safefree(backend->ciphersuites); - mbedtls_ssl_config_free(&backend->config); + curlx_safefree(backend->ciphersuites); + mbedtls_ssl_config_free(&backend->config); + if(backend->initialized) { mbedtls_ssl_free(&backend->ssl); backend->initialized = FALSE; } @@ -1305,7 +1378,6 @@ CURLcode result = CURLE_OK; int nread; - (void)data; DEBUGASSERT(backend); *pnread = 0; connssl->io_need = CURL_SSL_IO_NEED_NONE; @@ -1372,7 +1444,7 @@ struct Curl_easy *data, bool *done) { - CURLcode retcode; + CURLcode result; struct ssl_connect_data *connssl = cf->ctx; /* check if the connection has already been established */ @@ -1385,15 +1457,15 @@ connssl->io_need = CURL_SSL_IO_NEED_NONE; if(ssl_connect_1 == connssl->connecting_state) { - retcode = mbed_connect_step1(cf, data); - if(retcode) - return retcode; + result = mbed_connect_step1(cf, data); + if(result) + return result; } if(ssl_connect_2 == connssl->connecting_state) { - retcode = mbed_connect_step2(cf, data); - if(retcode) - return retcode; + result = mbed_connect_step2(cf, data); + if(result) + return result; } if(ssl_connect_3 == connssl->connecting_state) { @@ -1404,9 +1476,9 @@ if(mbedtls_ssl_get_version_number(&backend->ssl) <= MBEDTLS_SSL_VERSION_TLS1_2) { - retcode = mbed_new_session(cf, data); - if(retcode) - return retcode; + result = mbed_new_session(cf, data); + if(result) + return result; } connssl->connecting_state = ssl_connect_done; } @@ -1441,11 +1513,9 @@ ret = mbedtls_ctr_drbg_seed(&rng.drbg, mbedtls_entropy_func, &rng.entropy, NULL, 0); - if(ret) { - failf(NULL, " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", - (unsigned int)-ret); + if(ret) + /* mbedtls_ctr_drbg_seed returned error */ return 0; - } /* To prevent an adversary from reading your random data, you can enable prediction resistance. @@ -1516,13 +1586,19 @@ SSLSUPP_CA_PATH | SSLSUPP_CAINFO_BLOB | SSLSUPP_CERTINFO | +#ifdef HAVE_PINNED_PUBKEY SSLSUPP_PINNEDPUBKEY | +#endif SSLSUPP_SSL_CTX | #ifdef MBEDTLS_SSL_PROTO_TLS1_3 /* requires mbedTLS 3.6.0+ */ SSLSUPP_TLS13_CIPHERSUITES | #endif SSLSUPP_HTTPS_PROXY | - SSLSUPP_CIPHER_LIST, + SSLSUPP_CIPHER_LIST | +#ifdef MBEDTLS_X509_CRL_PARSE_C + SSLSUPP_CRLFILE | +#endif + 0, sizeof(struct mbed_ssl_backend_data),
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.h b/Utilities/cmcurl/lib/vtls/mbedtls.h index 3876fe3..d8a0a06 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.h +++ b/Utilities/cmcurl/lib/vtls/mbedtls.h
@@ -24,7 +24,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_MBEDTLS
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 84e6eaa..4629ca4 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -25,44 +25,37 @@ * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code * but vtls.c should ever call or use these functions. */ -#include "../curl_setup.h" +#include "curl_setup.h" -#if defined(USE_QUICHE) || defined(USE_OPENSSL) +#ifdef USE_OPENSSL -/* Wincrypt must be included before anything that could include OpenSSL. */ -#ifdef USE_WIN32_CRYPTO -#include <wincrypt.h> -/* Undefine wincrypt conflicting symbols for BoringSSL. */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE +#include "urldata.h" +#include "curl_trc.h" +#include "httpsrr.h" +#include "formdata.h" /* for the boundary function */ +#include "url.h" /* for the ssl config check function */ +#include "curlx/inet_pton.h" +#include "vtls/openssl.h" +#include "connect.h" +#include "cf-dns.h" +#include "progress.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vauth/vauth.h" +#include "vtls/keylog.h" +#include "vtls/hostcheck.h" +#include "transfer.h" +#include "multiif.h" +#include "curlx/strerr.h" +#include "curlx/strparse.h" +#include "curlx/strcopy.h" +#include "curlx/strdup.h" +#include "vtls/apple.h" +#ifdef USE_ECH +#include "curlx/base64.h" #endif -#include "../urldata.h" -#include "../curl_trc.h" -#include "../formdata.h" /* for the boundary function */ -#include "../url.h" /* for the ssl config check function */ -#include "../curlx/inet_pton.h" -#include "openssl.h" -#include "../connect.h" -#include "../progress.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "../vauth/vauth.h" -#include "keylog.h" -#include "hostcheck.h" -#include "../transfer.h" -#include "../multiif.h" -#include "../curlx/strerr.h" -#include "../curlx/strparse.h" -#include "../curlx/strcopy.h" -#include "../strdup.h" -#include "apple.h" - #include <openssl/rand.h> #include <openssl/x509v3.h> #ifndef OPENSSL_NO_DSA @@ -107,8 +100,6 @@ #include <openssl/store.h> /* this is used in the following conditions to make them easier to read */ #define OPENSSL_HAS_PROVIDERS - -static void ossl_provider_cleanup(struct Curl_easy *data); #endif /* AWS-LC fixed a bug with large buffers in v1.61.0 which also introduced @@ -181,8 +172,6 @@ #endif #define ossl_valsize_t numcert_t -static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl); - static CURLcode push_certinfo(struct Curl_easy *data, BIO *mem, const char *label, int num) WARN_UNUSED_RESULT; @@ -246,7 +235,7 @@ return result; for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) { - ASN1_OBJECT *obj; + const ASN1_OBJECT *obj; X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, (ossl_valsize_t)i); BUF_MEM *biomem; char namebuf[128]; @@ -262,7 +251,8 @@ namebuf[sizeof(namebuf) - 1] = 0; if(!X509V3_EXT_print(bio_out, ext, 0, 0)) - ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); + ASN1_STRING_print(bio_out, + (const ASN1_STRING *)X509_EXTENSION_get_data(ext)); BIO_get_mem_ptr(bio_out, &biomem); result = Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data, @@ -289,7 +279,7 @@ #else RSA_get0_key(rsa, &n, &e, NULL); #endif /* HAVE_EVP_PKEY_GET_PARAMS */ - BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0); + BIO_printf(mem, "%d", (int)(n ? BN_num_bits(n) : 0)); result = push_certinfo(data, mem, "RSA Public Key", i); if(!result) { result = print_pubkey_BN(rsa, n, i); @@ -371,6 +361,13 @@ return result; } +#ifdef HAVE_OPENSSL3 +/* from OpenSSL commit fc756e594ed5a27af378 */ +typedef const X509_PUBKEY pubkeytype_t; +#else +typedef X509_PUBKEY pubkeytype_t; +#endif + static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) { CURLcode result; @@ -387,7 +384,7 @@ numcerts = sk_X509_num(sk); if(numcerts > MAX_ALLOWED_CERT_AMOUNT) { - failf(data, "%d certificates is more than allowed (%u)", (int)numcerts, + failf(data, "%d certificates is more than allowed (%d)", (int)numcerts, MAX_ALLOWED_CERT_AMOUNT); return CURLE_SSL_CONNECT_ERROR; } @@ -435,7 +432,7 @@ { const X509_ALGOR *sigalg = NULL; - X509_PUBKEY *xpubkey = NULL; + pubkeytype_t *xpubkey = NULL; ASN1_OBJECT *pubkeyoid = NULL; X509_get0_signature(&psig, &sigalg, x); @@ -518,10 +515,6 @@ return result; } -#endif /* USE_QUICHE || USE_OPENSSL */ - -#ifdef USE_OPENSSL - static int ossl_bio_cf_create(BIO *bio) { BIO_set_shutdown(bio, 1); @@ -592,7 +585,7 @@ BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) BIO_set_retry_write(bio); return -1; } @@ -621,7 +614,7 @@ BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) BIO_set_retry_read(bio); } else { @@ -632,7 +625,7 @@ } /* Before returning server replies to the SSL instance, we need - * to have setup the x509 store or verification will fail. */ + * to have setup the x509 store or verification fails. */ if(!octx->x509_store_setup) { r2 = Curl_ssl_setup_x509_store(cf, data, octx); if(r2) { @@ -664,6 +657,7 @@ BIO_meth_free(m); } +#ifndef HAVE_KEYLOG_UPSTREAM #ifdef HAVE_KEYLOG_CALLBACK static void ossl_keylog_callback(const SSL *ssl, const char *line) { @@ -709,6 +703,7 @@ master_key, master_key_length); } #endif /* !HAVE_KEYLOG_CALLBACK */ +#endif /* HAVE_KEYLOG_UPSTREAM */ static const char *SSL_ERROR_to_str(int err) { @@ -897,10 +892,9 @@ #endif -static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine); +static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name); #ifdef OPENSSL_HAS_PROVIDERS -static CURLcode ossl_set_provider(struct Curl_easy *data, - const char *provider); +static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname); #endif static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob, @@ -1025,8 +1019,8 @@ SSL_CTX* ctx, const char *key_file, const char *key_passwd) -#ifdef USE_OPENSSL_ENGINE { +#ifdef USE_OPENSSL_ENGINE EVP_PKEY *priv_key = NULL; /* Implicitly use pkcs11 engine if none was provided and the @@ -1069,22 +1063,20 @@ return 0; } return 1; -} #else -{ (void)ctx; (void)key_file; (void)key_passwd; failf(data, "SSL_FILETYPE_ENGINE not supported for private key"); return 0; -} #endif +} static int providercheck(struct Curl_easy *data, SSL_CTX* ctx, const char *key_file) -#ifdef OPENSSL_HAS_PROVIDERS { +#ifdef OPENSSL_HAS_PROVIDERS char error_buffer[256]; /* Implicitly use pkcs11 provider if none was provided and the * key_file is a PKCS#11 URI */ @@ -1158,22 +1150,20 @@ return 0; } return 1; -} #else -{ (void)ctx; (void)key_file; failf(data, "SSL_FILETYPE_PROVIDER not supported for private key"); return 0; -} #endif +} static int engineload(struct Curl_easy *data, SSL_CTX* ctx, const char *cert_file) +{ /* ENGINE_CTRL_GET_CMD_FROM_NAME supported by OpenSSL, LibreSSL <=3.8.3 */ #if defined(USE_OPENSSL_ENGINE) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME) -{ char error_buffer[256]; /* Implicitly use pkcs11 engine if none was provided and the * cert_file is a PKCS#11 URI */ @@ -1203,8 +1193,7 @@ } /* Load the certificate from the engine */ - if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, - 0, ¶ms, NULL, 1)) { + if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, 0, ¶ms, NULL, 1)) { failf(data, "ssl engine cannot load client cert with id '%s' [%s]", cert_file, ossl_strerror(ERR_get_error(), error_buffer, @@ -1231,21 +1220,19 @@ return 0; } return 1; -} #else -{ (void)ctx; (void)cert_file; failf(data, "SSL_FILETYPE_ENGINE not supported for certificate"); return 0; -} #endif +} static int providerload(struct Curl_easy *data, SSL_CTX* ctx, const char *cert_file) -#ifdef OPENSSL_HAS_PROVIDERS { +#ifdef OPENSSL_HAS_PROVIDERS char error_buffer[256]; /* Implicitly use pkcs11 provider if none was provided and the * cert_file is a PKCS#11 URI */ @@ -1309,15 +1296,13 @@ return 0; } return 1; -} #else -{ (void)ctx; (void)cert_file; failf(data, "SSL_FILETYPE_PROVIDER not supported for certificate"); return 0; -} #endif +} static int pkcs12load(struct Curl_easy *data, SSL_CTX* ctx, @@ -1337,7 +1322,7 @@ if(!cert_bio) { failf(data, "BIO_new_mem_buf NULL, " OSSL_PACKAGE " error %s", ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); + sizeof(error_buffer))); return 0; } } @@ -1346,7 +1331,7 @@ if(!cert_bio) { failf(data, "BIO_new return NULL, " OSSL_PACKAGE " error %s", ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); + sizeof(error_buffer))); return 0; } @@ -1521,7 +1506,7 @@ return CURLE_BAD_FUNCTION_ARGUMENT; } - if((!key_file) && (!key_blob)) { + if(!key_file && !key_blob) { key_file = cert_file; key_blob = cert_blob; } @@ -1607,9 +1592,9 @@ return CURLE_OK; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE /* returns non-zero on failure */ -static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d) +static CURLcode x509_name_oneline(const X509_NAME *a, struct dynbuf *d) { BIO *bio_out = BIO_new(BIO_s_mem()); BUF_MEM *biomem; @@ -1652,7 +1637,9 @@ 0; OPENSSL_init_ssl(flags, NULL); +#ifndef HAVE_KEYLOG_UPSTREAM Curl_tls_keylog_open(); +#endif return 1; } @@ -1660,7 +1647,9 @@ /* Global cleanup */ static void ossl_cleanup(void) { +#ifndef HAVE_KEYLOG_UPSTREAM Curl_tls_keylog_close(); +#endif } /* Selects an OpenSSL crypto engine or provider. @@ -1761,7 +1750,7 @@ } OSSL_LIB_CTX_free(data->state.libctx); data->state.libctx = NULL; - Curl_safefree(data->state.propq); + curlx_safefree(data->state.propq); data->state.provider_loaded = FALSE; } @@ -1856,7 +1845,6 @@ CURLcode result = CURLE_OK; char buf[1024]; int nread = -1, err; - unsigned long sslerr; size_t i; DEBUGASSERT(octx); @@ -1935,7 +1923,7 @@ CURL_TRC_CF(data, cf, "SSL shutdown not received, but closed"); *done = TRUE; break; - case SSL_ERROR_NONE: /* just did not get anything */ + case SSL_ERROR_NONE: /* did not get anything */ case SSL_ERROR_WANT_READ: /* SSL has send its notify and now wants to read the reply * from the server. We are not really interested in that. */ @@ -1949,12 +1937,14 @@ default: /* Server seems to have closed the connection without sending us * a close notify. */ - sslerr = ERR_get_error(); - CURL_TRC_CF(data, cf, "SSL shutdown, ignore recv error: '%s', errno %d", - (sslerr ? - ossl_strerror(sslerr, buf, sizeof(buf)) : - SSL_ERROR_to_str(err)), - SOCKERRNO); + { + VERBOSE(unsigned long sslerr = ERR_get_error()); + CURL_TRC_CF(data, cf, "SSL shutdown, ignore recv error: '%s', errno %d", + (sslerr ? + ossl_strerror(sslerr, buf, sizeof(buf)) : + SSL_ERROR_to_str(err)), + SOCKERRNO); + } *done = TRUE; result = CURLE_OK; break; @@ -2115,9 +2105,9 @@ case GEN_DNS: /* name/pattern comparison */ /* The OpenSSL man page explicitly says: "In general it cannot be assumed that the data returned by ASN1_STRING_data() is null - terminated or does not contain embedded nulls." But also that - "The actual format of the data will depend on the actual string - type itself: for example for an IA5String the data will be ASCII" + terminated or does not contain embedded nulls.", but also that + "The actual format of the data depends on the actual string + type itself: for example for an IA5String the data is ASCII" It has been however verified that in 0.9.6 and 0.9.7, IA5String is always null-terminated. @@ -2168,19 +2158,19 @@ bool free_cn = FALSE; /* The following is done because of a bug in 0.9.6b */ - X509_NAME *name = X509_get_subject_name(server_cert); + const X509_NAME *name = X509_get_subject_name(server_cert); if(name) { int j; while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) i = j; } - /* we have the name entry and we will now convert this to a string + /* we have the name entry and we now convert this to a string that we can use for comparison. Doing this we support BMPstring, UTF8, etc. */ if(i >= 0) { - ASN1_STRING *tmp = + const ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input @@ -2382,31 +2372,6 @@ static const char *ssl_msg_type(int ssl_ver, int msg) { -#ifdef SSL2_VERSION_MAJOR /* OpenSSL 1.0.2, LibreSSL <=3.9.2 */ - if(ssl_ver == SSL2_VERSION_MAJOR) { - switch(msg) { - case SSL2_MT_ERROR: - return "Error"; - case SSL2_MT_CLIENT_HELLO: - return "Client hello"; - case SSL2_MT_CLIENT_MASTER_KEY: - return "Client key"; - case SSL2_MT_CLIENT_FINISHED: - return "Client finished"; - case SSL2_MT_SERVER_HELLO: - return "Server hello"; - case SSL2_MT_SERVER_VERIFY: - return "Server verify"; - case SSL2_MT_SERVER_FINISHED: - return "Server finished"; - case SSL2_MT_REQUEST_CERTIFICATE: - return "Request CERT"; - case SSL2_MT_CLIENT_CERTIFICATE: - return "Client CERT"; - } - } - else -#endif if(ssl_ver == SSL3_VERSION_MAJOR) { switch(msg) { case SSL3_MT_HELLO_REQUEST: @@ -2505,11 +2470,6 @@ return; switch(ssl_ver) { -#ifdef SSL2_VERSION /* removed in recent versions */ - case SSL2_VERSION: - verstr = "SSLv2"; - break; -#endif #ifdef SSL3_VERSION case SSL3_VERSION: verstr = "SSLv3"; @@ -2549,16 +2509,18 @@ && content_type != SSL3_RT_INNER_CONTENT_TYPE #endif ) { - const char *msg_name, *tls_rt_name; + const char *msg_name = "Truncated message"; + const char *tls_rt_name; char ssl_buf[1024]; - int msg_type, txt_len; + int msg_type = 0; + int txt_len; /* the info given when the version is zero is not that useful for us */ ssl_ver >>= 8; /* check the upper 8 bits only below */ /* SSLv2 does not seem to have TLS record-type headers, so OpenSSL - * always pass-up content-type as 0. But the interesting message-type + * always pass-up content-type as 0, but the interesting message-type * is at 'buf[0]'. */ if(ssl_ver == SSL3_VERSION_MAJOR && content_type) @@ -2567,15 +2529,21 @@ tls_rt_name = ""; if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) { - msg_type = *(const char *)buf; - msg_name = "Change cipher spec"; + if(len) { + msg_type = *(const unsigned char *)buf; + msg_name = "Change cipher spec"; + } } else if(content_type == SSL3_RT_ALERT) { - msg_type = (((const char *)buf)[0] << 8) + ((const char *)buf)[1]; - msg_name = SSL_alert_desc_string_long(msg_type); + if(len >= 2) { + msg_type = + (((const unsigned char *)buf)[0] << 8) + + ((const unsigned char *)buf)[1]; + msg_name = SSL_alert_desc_string_long(msg_type); + } } - else { - msg_type = *(const char *)buf; + else if(len) { + msg_type = *(const unsigned char *)buf; msg_name = ssl_msg_type(ssl_ver, msg_type); } @@ -2591,9 +2559,9 @@ (void)ssl; } -static CURLcode -ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx, - unsigned int ssl_version_min) +static CURLcode ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, + SSL_CTX *ctx, + unsigned int ssl_version_min) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); /* first, TLS min version... */ @@ -2647,7 +2615,7 @@ case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */ default: /* SSL_CTX_set_max_proto_version states that: setting the maximum to 0 - will enable protocol versions up to the highest version supported by + enables protocol versions up to the highest version supported by the library */ ossl_ssl_version_max = 0; break; @@ -2714,7 +2682,7 @@ earlydata_max = SSL_SESSION_get_max_early_data(session); #endif if(quic_tp && quic_tp_len) { - qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len); + qtp_clone = curlx_memdup0((const char *)quic_tp, quic_tp_len); if(!qtp_clone) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -2724,7 +2692,7 @@ result = Curl_ssl_session_create2(der_session_buf, der_session_size, ietf_tls_id, alpn, (curl_off_t)time(NULL) + - SSL_SESSION_get_timeout(session), + SSL_SESSION_get_timeout(session), earlydata_max, qtp_clone, quic_tp_len, &sc_session); der_session_buf = NULL; /* took ownership of sdata */ @@ -2749,8 +2717,7 @@ struct Curl_easy *data = CF_DATA_CURRENT(cf); struct ssl_connect_data *connssl = cf->ctx; Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid, - SSL_version(ssl), connssl->negotiated.alpn, - NULL, 0); + SSL_version(ssl), connssl->negotiated.alpn, NULL, 0); } return 0; } @@ -2762,7 +2729,7 @@ BIO *cbio = NULL; STACK_OF(X509_INFO) *inf = NULL; - /* everything else is just a reference */ + /* everything else is a reference */ int i, count = 0; X509_INFO *itmp = NULL; @@ -2820,6 +2787,7 @@ #ifdef USE_WIN32_CRYPTO static CURLcode ossl_win_load_store(struct Curl_easy *data, + struct Curl_cfilter *cf, const char *win_store, X509_STORE *store, bool *padded) @@ -2832,11 +2800,13 @@ hStore = CertOpenSystemStoreA(0, win_store); if(hStore) { PCCERT_CONTEXT pContext = NULL; - /* The array of enhanced key usage OIDs will vary per certificate and + /* The array of enhanced key usage OIDs varies per certificate and is declared outside of the loop so that rather than malloc/free each iteration we can grow it with realloc, when necessary. */ CERT_ENHKEY_USAGE *enhkey_usage = NULL; DWORD enhkey_usage_size = 0; + VERBOSE(size_t total = 0); + VERBOSE(size_t imported = 0); /* This loop makes a best effort to import all valid certificates from the MS root store. If a certificate cannot be imported it is @@ -2853,8 +2823,10 @@ if(!pContext) break; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - else { + VERBOSE(++total); + +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) + { char cert_name[256]; if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, cert_name, sizeof(cert_name))) @@ -2890,8 +2862,8 @@ * depending on what is found. For more details see * CertGetEnhancedKeyUsage doc. */ - if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { - if(req_size && req_size > enhkey_usage_size) { + if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size) && req_size) { + if(req_size > enhkey_usage_size) { void *tmp = curlx_realloc(enhkey_usage, req_size); if(!tmp) { @@ -2942,7 +2914,8 @@ such as duplicate certificate, which is allowed by MS but not OpenSSL. */ if(X509_STORE_add_cert(store, x509) == 1) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + VERBOSE(++imported); +#ifdef DEBUGBUILD infof(data, "SSL: Imported cert"); #endif *padded = TRUE; @@ -2954,6 +2927,10 @@ CertFreeCertificateContext(pContext); CertCloseStore(hStore, 0); + CURL_TRC_CF(data, cf, + "ossl_win_load_store() found: %zu imported: %zu certs in %s.", + total, imported, win_store); + if(result) return result; } @@ -2969,7 +2946,7 @@ /* Import certificates from the Windows root certificate store if requested. https://stackoverflow.com/questions/9507184/ - https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 + https://github.com/d3x0r/SACK/blob/ff15424d3c581b86d40f818532e5a400c516d39d/src/netlib/ssl_layer.c#L1410 https://datatracker.ietf.org/doc/html/rfc5280 */ const char *win_stores[] = { "ROOT", /* Trusted Root Certification Authorities */ @@ -2981,7 +2958,7 @@ *padded = FALSE; for(i = 0; i < CURL_ARRAYSIZE(win_stores); ++i) { bool store_added = FALSE; - result = ossl_win_load_store(data, win_stores[i], store, &store_added); + result = ossl_win_load_store(data, cf, win_stores[i], store, &store_added); if(result) return result; if(store_added) { @@ -3096,7 +3073,7 @@ #ifdef CURL_CA_FALLBACK if(octx->store_is_empty) { - /* verifying the peer without any CA certificates will not + /* verifying the peer without any CA certificates does not work so use OpenSSL's built-in default as fallback */ X509_STORE_set_default_paths(store); infof(data, " OpenSSL default paths (fallback)"); @@ -3141,7 +3118,7 @@ * revocation */ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); if(!lookup || - (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { + (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM))) { failf(data, "error loading CRL file: %s", ssl_crlfile); return CURLE_SSL_CRL_BADFILE; } @@ -3243,7 +3220,7 @@ !ossl_cached_x509_store_expired(data, share) && !ossl_cached_x509_store_different(cf, data, share)) { store = share->store; - *pempty = share->store_is_empty; + *pempty = (bool)share->store_is_empty; } return store; @@ -3336,7 +3313,7 @@ result = ossl_populate_x509_store(cf, data, octx, store); if(result == CURLE_OK && cache_criteria_met) { - ossl_set_cached_x509_store(cf, data, store, octx->store_is_empty); + ossl_set_cached_x509_store(cf, data, store, (bool)octx->store_is_empty); } } @@ -3356,7 +3333,6 @@ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_cfg = Curl_ssl_cf_get_primary_config(cf); struct alpn_spec alpns; - char error_buffer[256]; CURLcode result; Curl_alpn_copy(&alpns, alpns_requested); @@ -3372,43 +3348,54 @@ SSL_SESSION *ssl_session = NULL; /* If OpenSSL does not accept the session from the cache, this - * is not an error. We just continue without it. */ + * is not an error. We continue without it. */ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid, (long)der_sessionid_size); if(ssl_session) { if(!SSL_set_session(octx->ssl, ssl_session)) { + VERBOSE(char error_buffer[256]); infof(data, "SSL: SSL_set_session not accepted, " "continuing without: %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); } else { - infof(data, "SSL reusing session with ALPN '%s'", - scs->alpn ? scs->alpn : "-"); - octx->reused_session = TRUE; + if(conn_cfg->verifypeer && + (SSL_get_verify_result(octx->ssl) != X509_V_OK)) { + /* Session was from unverified connection, cannot reuse here */ + SSL_set_session(octx->ssl, NULL); + infof(data, "SSL session not peer verified, not reusing"); + } + else { + infof(data, "SSL reusing session with ALPN '%s'", + scs->alpn ? scs->alpn : "-"); + octx->reused_session = TRUE; + infof(data, "SSL verify result: %lx", + SSL_get_verify_result(octx->ssl)); #ifdef HAVE_OPENSSL_EARLYDATA - if(ssl_config->earlydata && scs->alpn && - SSL_SESSION_get_max_early_data(ssl_session) && - !cf->conn->connect_only && - (SSL_version(octx->ssl) == TLS1_3_VERSION)) { - bool do_early_data = FALSE; - if(sess_reuse_cb) { - result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); - if(result) { - SSL_SESSION_free(ssl_session); - return result; + if(ssl_config->earlydata && scs->alpn && + SSL_SESSION_get_max_early_data(ssl_session) && + !cf->conn->bits.connect_only && + (SSL_version(octx->ssl) == TLS1_3_VERSION)) { + bool do_early_data = FALSE; + if(sess_reuse_cb) { + result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); + if(result) { + SSL_SESSION_free(ssl_session); + return result; + } + } + if(do_early_data) { + /* We only try the ALPN protocol the session used before, + * otherwise we might send early data for the wrong protocol */ + Curl_alpn_restrict_to(&alpns, scs->alpn); } } - if(do_early_data) { - /* We only try the ALPN protocol the session used before, - * otherwise we might send early data for the wrong protocol */ - Curl_alpn_restrict_to(&alpns, scs->alpn); - } - } #else - (void)ssl_config; - (void)sess_reuse_cb; + (void)ssl_config; + (void)sess_reuse_cb; #endif + } } SSL_SESSION_free(ssl_session); } @@ -3427,7 +3414,7 @@ failf(data, "Error determining ALPN"); return CURLE_SSL_CONNECT_ERROR; } - if(SSL_set_alpn_protos(octx->ssl, proto.data, (int)proto.len)) { + if(SSL_set_alpn_protos(octx->ssl, proto.data, proto.len)) { failf(data, "Error setting ALPN"); return CURLE_SSL_CONNECT_ERROR; } @@ -3437,6 +3424,16 @@ } #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST +bool Curl_ossl_need_httpsrr(struct Curl_easy *data) +{ + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +} + static CURLcode ossl_init_ech(struct ossl_ctx *octx, struct Curl_cfilter *cf, struct Curl_easy *data, @@ -3446,21 +3443,21 @@ size_t ech_config_len = 0; char *outername = data->set.str[STRING_ECH_PUBLIC]; int trying_ech_now = 0; - CURLcode result; + CURLcode result = CURLE_OK; - if(!ECH_ENABLED(data)) + if(!CURLECH_ENABLED(data)) return CURLE_OK; if(data->set.tls_ech & CURLECH_GREASE) { infof(data, "ECH: will GREASE ClientHello"); -# ifdef HAVE_BORINGSSL_LIKE +#ifdef HAVE_BORINGSSL_LIKE SSL_set_enable_ech_grease(octx->ssl, 1); -# else +#else SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); -# endif +#endif } else if(data->set.tls_ech & CURLECH_CLA_CFG) { -# ifdef HAVE_BORINGSSL_LIKE +#ifdef HAVE_BORINGSSL_LIKE /* have to do base64 decode here for BoringSSL */ const char *b64 = data->set.str[STRING_ECH_CONFIG]; @@ -3484,7 +3481,7 @@ } curlx_free(ech_config); trying_ech_now = 1; -# else +#else ech_config = (unsigned char *)data->set.str[STRING_ECH_CONFIG]; if(!ech_config) { infof(data, "ECH: ECHConfig from command line empty"); @@ -3498,53 +3495,41 @@ } else trying_ech_now = 1; -# endif +#endif /* HAVE_BORINGSSL_LIKE */ infof(data, "ECH: ECHConfig from command line"); } else { - struct Curl_dns_entry *dns = NULL; + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); - if(peer->hostname) - dns = Curl_dnscache_get(data, peer->hostname, peer->port, - cf->conn->ip_version); - if(!dns) { - infof(data, "ECH: requested but no DNS info available"); - if(data->set.tls_ech & CURLECH_HARD) - return CURLE_SSL_CONNECT_ERROR; - } - else { - struct Curl_https_rrinfo *rinfo = NULL; + if(rinfo && rinfo->echconfiglist) { + const unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; - rinfo = dns->hinfo; - if(rinfo && rinfo->echconfiglist) { - unsigned char *ecl = rinfo->echconfiglist; - size_t elen = rinfo->echconfiglist_len; - - infof(data, "ECH: ECHConfig from DoH HTTPS RR"); - if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { - infof(data, "ECH: SSL_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) - return CURLE_SSL_CONNECT_ERROR; - } - else { - trying_ech_now = 1; - infof(data, "ECH: imported ECHConfigList of length %zu", elen); - } - } - else { - infof(data, "ECH: requested but no ECHConfig available"); + infof(data, "ECH: ECHConfig from HTTPS RR"); + if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { + infof(data, "ECH: SSL_set1_ech_config_list failed"); if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } - Curl_resolv_unlink(data, &dns); + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %zu", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; } } -# ifdef HAVE_BORINGSSL_LIKE +#ifdef HAVE_BORINGSSL_LIKE + (void)peer; if(trying_ech_now && outername) { infof(data, "ECH: setting public_name not supported with BoringSSL"); return CURLE_SSL_CONNECT_ERROR; } -# else +#else if(trying_ech_now && outername) { infof(data, "ECH: inner: '%s', outer: '%s'", peer->hostname ? peer->hostname : "NULL", outername); @@ -3556,7 +3541,7 @@ return CURLE_SSL_CONNECT_ERROR; } } -# endif /* HAVE_BORINGSSL_LIKE */ +#endif /* HAVE_BORINGSSL_LIKE */ if(trying_ech_now && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) { infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); @@ -3565,7 +3550,13 @@ return CURLE_OK; } -#endif /* HAVE_SSL_SET1_ECH_CONFIG_LIST */ +#else /* HAVE_SSL_SET1_ECH_CONFIG_LIST */ +bool Curl_ossl_need_httpsrr(struct Curl_easy *data) +{ + (void)data; + return FALSE; +} +#endif /* else HAVE_SSL_SET1_ECH_CONFIG_LIST */ static CURLcode ossl_init_ssl(struct ossl_ctx *octx, struct Curl_cfilter *cf, @@ -3622,17 +3613,17 @@ *pmethod = NULL; *pssl_version_min = conn_config->version; + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); switch(peer->transport) { case TRNSPRT_TCP: /* check to see if we have been told to use an explicit SSL/TLS version */ switch(*pssl_version_min) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: - /* it will be handled later with the context options */ + /* it is handled later with the context options */ *pmethod = TLS_client_method(); break; case CURL_SSLVERSION_SSLv2: @@ -3655,11 +3646,7 @@ return CURLE_SSL_CONNECT_ERROR; } -#ifdef USE_OPENSSL_QUIC - *pmethod = OSSL_QUIC_client_method(); -#else *pmethod = TLS_method(); -#endif break; default: failf(data, "unsupported transport %d in SSL init", peer->transport); @@ -3743,8 +3730,8 @@ The enabled extension concerns the session management. I wonder how often libcurl stops a connection and then resumes a TLS session. Also, sending - the session data is some overhead. I suggest that you just use your - proposed patch (which explicitly disables TICKET). + the session data is some overhead. I suggest that you use your proposed + patch (which explicitly disables TICKET). If someone writes an application with libcurl and OpenSSL who wants to enable the feature, one can do this in the SSL callback. @@ -3776,13 +3763,13 @@ if(!ssl_config->enable_beast) ctx_options &= ~(ctx_option_t)SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + DEBUGASSERT(ssl_version_min != CURL_SSLVERSION_DEFAULT); switch(ssl_version_min) { case CURL_SSLVERSION_SSLv2: case CURL_SSLVERSION_SSLv3: return CURLE_NOT_BUILT_IN; /* "--tlsv<x.y>" options mean TLS >= version <x.y> */ - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */ case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */ case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */ @@ -3926,7 +3913,7 @@ SSL_CTX_set_verify(octx->ssl_ctx, SSL_VERIFY_NONE, NULL); /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ -#ifdef HAVE_KEYLOG_CALLBACK +#if !defined(HAVE_KEYLOG_UPSTREAM) && defined(HAVE_KEYLOG_CALLBACK) if(Curl_tls_keylog_enabled()) { SSL_CTX_set_keylog_callback(octx->ssl_ctx, ossl_keylog_callback); } @@ -3975,32 +3962,16 @@ bool *do_early_data) { struct ssl_connect_data *connssl = cf->ctx; - CURLcode result = CURLE_OK; - *do_early_data = FALSE; connssl->earlydata_max = scs->earlydata_max; - if(!connssl->earlydata_max) { - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); - } - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); - connssl->earlydata_state = ssl_earlydata_await; - connssl->state = ssl_connection_deferred; - result = Curl_alpn_set_negotiated(cf, data, connssl, - (const unsigned char *)scs->alpn, - scs->alpn ? strlen(scs->alpn) : 0); - *do_early_data = !result; - } - return result; + + return Curl_on_session_reuse(cf, data, alpns, scs, do_early_data, + connssl->earlydata_max); } void Curl_ossl_report_handshake(struct Curl_easy *data, struct ossl_ctx *octx) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { int psigtype_nid = NID_undef; const char *negotiated_group_name = NULL; @@ -4025,7 +3996,7 @@ #else (void)data; (void)octx; -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ } static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, @@ -4057,7 +4028,7 @@ #ifdef HAVE_SSL_SET0_WBIO /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works * without backward compat quirks. Every call takes one reference, so we - * up it and pass. SSL* then owns it and will free. + * up it and pass. SSL* then owns and frees it. * We check on the function in configure, since LibreSSL and friends * each have their own versions to add support for this. */ BIO_up_ref(bio); @@ -4080,33 +4051,34 @@ #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST /* If we have retry configs, then trace those out */ -static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl, - int reason) +static int ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl, + int reason) { CURLcode result = CURLE_OK; size_t rcl = 0; int rv = 1; -# ifndef HAVE_BORINGSSL_LIKE +#ifndef HAVE_BORINGSSL_LIKE char *inner = NULL; uint8_t *rcs = NULL; char *outer = NULL; -# else +#else const char *inner = NULL; const uint8_t *rcs = NULL; const char *outer = NULL; size_t out_name_len = 0; int servername_type = 0; -# endif +#endif + NOVERBOSE((void)reason); /* nothing to trace if not doing ECH */ - if(!ECH_ENABLED(data)) - return; -# ifndef HAVE_BORINGSSL_LIKE + if(!CURLECH_ENABLED(data)) + return rv; +#ifndef HAVE_BORINGSSL_LIKE rv = SSL_ech_get1_retry_config(ssl, &rcs, &rcl); -# else +#else SSL_get0_ech_retry_configs(ssl, &rcs, &rcl); rv = (int)rcl; -# endif +#endif if(rv && rcs) { char *b64str = NULL; @@ -4132,10 +4104,12 @@ } else infof(data, "ECH: no retry_configs (rv = %d)", rv); -# ifndef HAVE_BORINGSSL_LIKE - OPENSSL_free((void *)rcs); -# endif - return; +#ifndef HAVE_BORINGSSL_LIKE + OPENSSL_free(inner); + OPENSSL_free(rcs); + OPENSSL_free(outer); +#endif + return rv; } #endif @@ -4164,7 +4138,7 @@ octx->x509_store_setup = TRUE; } -#ifndef HAVE_KEYLOG_CALLBACK +#if !defined(HAVE_KEYLOG_UPSTREAM) && !defined(HAVE_KEYLOG_CALLBACK) /* If key logging is enabled, wait for the handshake to complete and then * proceed with logging secrets (for TLS 1.2 or older). */ @@ -4224,8 +4198,12 @@ reason = ERR_GET_REASON(errdetail); if((lib == ERR_LIB_SSL) && - ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || - (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { + ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) +/* Missing from OpenSSL 4+ OPENSSL_NO_DEPRECATED_3_0 builds */ +#ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED + || (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED) +#endif + )) { result = CURLE_PEER_FAILED_VERIFICATION; lerr = SSL_get_verify_result(octx->ssl); @@ -4251,11 +4229,11 @@ #endif #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST else if((lib == ERR_LIB_SSL) && -# ifndef HAVE_BORINGSSL_LIKE +#ifndef HAVE_BORINGSSL_LIKE (reason == SSL_R_ECH_REQUIRED)) { -# else +#else (reason == SSL_R_ECH_REJECTED)) { -# endif +#endif /* HAVE_BORINGSSL_LIKE */ /* trace retry_configs if we got some */ ossl_trace_ech_retry_configs(data, octx->ssl, reason); @@ -4277,7 +4255,7 @@ * (RST connection, etc.), OpenSSL gives no explanation whatsoever and * the SO_ERROR is also lost. */ - if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { + if(result == CURLE_SSL_CONNECT_ERROR && errdetail == 0) { char extramsg[80] = ""; int sockerr = SOCKERRNO; @@ -4297,42 +4275,52 @@ Curl_ossl_report_handshake(data, octx); #if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST) && !defined(HAVE_BORINGSSL_LIKE) - if(ECH_ENABLED(data)) { + if(CURLECH_ENABLED(data)) { char *inner = NULL, *outer = NULL; - const char *status = NULL; int rv; + VERBOSE(const char *status); rv = SSL_ech_get1_status(octx->ssl, &inner, &outer); switch(rv) { case SSL_ECH_STATUS_SUCCESS: - status = "succeeded"; + VERBOSE(status = "succeeded"); break; case SSL_ECH_STATUS_GREASE_ECH: - status = "sent GREASE, got retry-configs"; + VERBOSE(status = "sent GREASE, got retry-configs"); break; case SSL_ECH_STATUS_GREASE: - status = "sent GREASE"; + VERBOSE(status = "sent GREASE"); break; case SSL_ECH_STATUS_NOT_TRIED: - status = "not attempted"; + VERBOSE(status = "not attempted"); break; case SSL_ECH_STATUS_NOT_CONFIGURED: - status = "not configured"; + VERBOSE(status = "not configured"); break; case SSL_ECH_STATUS_BACKEND: - status = "backend (unexpected)"; + VERBOSE(status = "backend (unexpected)"); break; case SSL_ECH_STATUS_FAILED: - status = "failed"; + VERBOSE(status = "failed"); break; case SSL_ECH_STATUS_BAD_CALL: - status = "bad call (unexpected)"; + VERBOSE(status = "bad call (unexpected)"); break; - case SSL_ECH_STATUS_BAD_NAME: - status = "bad name (unexpected)"; + case SSL_ECH_STATUS_BAD_NAME: { + struct ssl_primary_config *conn_config = + Curl_ssl_cf_get_primary_config(cf); + if(!conn_config->verifypeer && !conn_config->verifyhost && + inner && !strcmp(inner, connssl->peer.hostname)) { + VERBOSE(status = "bad name (tolerated without peer verification)"); + rv = SSL_ECH_STATUS_SUCCESS; + } + else { + VERBOSE(status = "bad name (unexpected)"); + } break; + } default: - status = "unexpected status"; + VERBOSE(status = "unexpected status"); infof(data, "ECH: unexpected status %d", rv); } infof(data, "ECH: result: status is %s, inner is %s, outer is %s", @@ -4345,8 +4333,7 @@ /* trace retry_configs if we got some */ ossl_trace_ech_retry_configs(data, octx->ssl, 0); } - if(rv != SSL_ECH_STATUS_SUCCESS - && data->set.tls_ech & CURLECH_HARD) { + if(rv != SSL_ECH_STATUS_SUCCESS && (data->set.tls_ech & CURLECH_HARD)) { infof(data, "ECH: ech-hard failed"); return CURLE_SSL_CONNECT_ERROR; } @@ -4409,7 +4396,7 @@ /* * These checks are verifying we got back the same values as when we * sized the buffer. it is pretty weak since they should always be the - * same. But it gives us something to test. + * same, but it gives us something to test. */ if((len1 != len2) || !temp || ((temp - buff1) != len1)) break; /* failed */ @@ -4426,9 +4413,9 @@ return result; } -#if !(defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER < 0x3060000fL) && \ - !defined(HAVE_BORINGSSL_LIKE) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#ifdef CURLVERBOSE +#if !defined(HAVE_BORINGSSL_LIKE) && \ + !(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3060000fL) static void infof_certstack(struct Curl_easy *data, const SSL *ssl) { STACK_OF(X509) *certstack; @@ -4501,6 +4488,7 @@ #else #define infof_certstack(data, ssl) #endif +#endif /* CURLVERBOSE */ static CURLcode ossl_check_issuer(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -4595,7 +4583,7 @@ return result; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE #define MAX_CERT_NAME_LENGTH 2048 static CURLcode ossl_infof_cert(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -4646,7 +4634,7 @@ curlx_dyn_free(&dname); return result; } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ #ifdef USE_APPLE_SECTRUST struct ossl_certs_ctx { @@ -4698,8 +4686,15 @@ if(!chain.num_certs && (conn_config->verifypeer || conn_config->verifyhost)) { - failf(data, "SSL: could not get peer certificate"); - result = CURLE_PEER_FAILED_VERIFICATION; + if(!octx->reused_session) { + failf(data, "SSL: could not get peer certificate chain"); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else { + /* when session was reused, there is no peer cert chain */ + *pverified = FALSE; + return CURLE_OK; + } } else { #ifdef HAVE_BORINGSSL_LIKE @@ -4708,16 +4703,26 @@ unsigned char *ocsp_data = NULL; #endif long ocsp_len = 0; + bool ocsp_missing = FALSE; if(conn_config->verifystatus && !octx->reused_session) ocsp_len = (long)SSL_get_tlsext_status_ocsp_resp(octx->ssl, &ocsp_data); /* SSL_get_tlsext_status_ocsp_resp() returns the length of the OCSP response data or -1 if there is no OCSP response data. */ - if(ocsp_len < 0) + if(ocsp_len < 0) { ocsp_len = 0; /* no data available */ + ocsp_missing = TRUE; + } result = Curl_vtls_apple_verify(cf, data, peer, chain.num_certs, ossl_chain_get_der, &chain, ocsp_data, ocsp_len); + if(!result && ocsp_missing && conn_config->verifystatus && + !octx->reused_session) { + /* verified, but OCSP stapling is required and server sent none */ + *pverified = TRUE; + failf(data, "No OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } } *pverified = !result; return result; @@ -4759,7 +4764,7 @@ goto out; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE result = ossl_infof_cert(cf, data, server_cert); if(result) goto out; @@ -4775,6 +4780,7 @@ ossl_verify = SSL_get_verify_result(octx->ssl); ssl_config->certverifyresult = ossl_verify; + infof(data, "OpenSSL verify result: %lx", ossl_verify); verified = (ossl_verify == X509_V_OK); if(verified) @@ -4955,6 +4961,11 @@ connssl->io_need = CURL_SSL_IO_NEED_NONE; if(ssl_connect_1 == connssl->connecting_state) { + if(Curl_ossl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } CURL_TRC_CF(data, cf, "ossl_connect, step1"); result = ossl_connect_step1(cf, data); if(result) @@ -5020,7 +5031,7 @@ { struct ssl_connect_data *connssl = cf->ctx; (void)data; - return connssl->input_pending; + return (bool)connssl->input_pending; } static CURLcode ossl_send(struct Curl_cfilter *cf, @@ -5040,7 +5051,6 @@ CURLcode result = CURLE_OK; int nwritten; - (void)data; DEBUGASSERT(octx); *pnwritten = 0; ERR_clear_error(); @@ -5049,7 +5059,7 @@ memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; if(octx->blocked_ssl_write_len && (octx->blocked_ssl_write_len != memlen)) { /* The previous SSL_write() call was blocked, using that length. - * We need to use that again or OpenSSL will freak out. A shorter + * We need to use that again or OpenSSL freaks out. A shorter * length should not happen and is a bug in libcurl. */ if(octx->blocked_ssl_write_len > memlen) { DEBUGASSERT(0); @@ -5133,7 +5143,6 @@ CURLcode result = CURLE_OK; int nread; - (void)data; DEBUGASSERT(octx); *pnread = 0; @@ -5147,7 +5156,7 @@ *pnread = (size_t)nread; else { /* failed SSL_read */ - int err = SSL_get_error(octx->ssl, (int)nread); + int err = SSL_get_error(octx->ssl, nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -5202,7 +5211,7 @@ result = CURLE_RECV_ERROR; } else { - /* We should no longer get here nowadays. But handle + /* We should no longer get here nowadays, but handle * the error in case of some weirdness in the OSSL stack */ int sockerr = SOCKERRNO; if(sockerr) @@ -5235,13 +5244,15 @@ return result; } -static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, +static CURLcode ossl_get_channel_binding(struct Curl_easy *data, + int sockindex, struct dynbuf *binding) { X509 *cert; - int algo_nid; - const EVP_MD *algo_type; - const char *algo_name; + int mdnid; + bool no_digest_acceptable = FALSE; + const EVP_MD *algo_type = NULL; + const char *algo_name = NULL; unsigned int length; unsigned char buf[EVP_MAX_MD_SIZE]; @@ -5273,43 +5284,108 @@ /* No server certificate, do not do channel binding */ return CURLE_OK; - if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) { +#ifdef HAVE_OPENSSL3 + { + int pknid, secbits; + uint32_t flags; + EVP_PKEY *pkey = X509_get0_pubkey(cert); + + if(!X509_get_signature_info(cert, &mdnid, &pknid, &secbits, &flags)) { + failf(data, "certificate signature algorithm not recognized"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + + if(mdnid != NID_undef) { + if(mdnid == NID_md5 || mdnid == NID_sha1) { + algo_type = EVP_sha256(); + } + else + algo_type = EVP_get_digestbynid(mdnid); + } + else if(pkey && !EVP_PKEY_is_a(pkey, OBJ_nid2sn(pknid))) { + /* The cert's pkey is different from the algorithm used to sign + * the certificate. Since the reported `mdnid` is undefined, there + * is no digest algorithm available here. This happens in PQC + * and is accepted, resulting in no addition to the binding. */ + no_digest_acceptable = TRUE; + } + else if(pkey) { + /* cert's pkey type is the same as the cert signer (or same family). + * Ask for the mandatory/advisory digest algorithm for the pkey. + */ + char mdname[128] = ""; + int rc = EVP_PKEY_get_default_digest_name(pkey, mdname, sizeof(mdname)); + bool md_is_undef = !strcmp(mdname, "UNDEF"); + + if(rc == 2 && md_is_undef) { + /* OpenSSL declares "undef" the *mandatory* digest for this key. + * This is some PQC shit, accept it, no addition to binding. */ + no_digest_acceptable = TRUE; + } + else if(rc > 0 && mdname[0] != '\0' && !md_is_undef) { + infof(data, "Digest algorithm : %s%s (derived from public key)" + ", but unavailable", + mdname, rc == 2 ? " [mandatory]" : " [advisory]"); + } + } + } +#else /* HAVE_OPENSSL3 */ + + if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &mdnid, NULL)) { failf(data, "Unable to find digest NID for certificate signature algorithm"); result = CURLE_SSL_INVALIDCERTSTATUS; - goto error; + goto out; } /* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */ - if(algo_nid == NID_md5 || algo_nid == NID_sha1) { + if(mdnid == NID_md5 || mdnid == NID_sha1) { algo_type = EVP_sha256(); } else { - algo_type = EVP_get_digestbynid(algo_nid); + algo_type = EVP_get_digestbynid(mdnid); if(!algo_type) { - algo_name = OBJ_nid2sn(algo_nid); + algo_name = OBJ_nid2sn(mdnid); failf(data, "Could not find digest algorithm %s (NID %d)", - algo_name ? algo_name : "(null)", algo_nid); + algo_name ? algo_name : "(null)", mdnid); result = CURLE_SSL_INVALIDCERTSTATUS; - goto error; + goto out; } } - if(!X509_digest(cert, algo_type, buf, &length)) { - failf(data, "X509_digest() failed"); +#endif /* HAVE_OPENSSL3, else */ + + if(!algo_type) { + if(no_digest_acceptable) { + infof(data, "certificate exposes no signing digest algorithm, " + "nothing to add to channel binding"); + result = CURLE_OK; + goto out; + } + /* unacceptable, something is wrong, fail */ + algo_name = OBJ_nid2sn(mdnid); + failf(data, "Unable to find digest algorithm %s (NID %d) " + "for channel binding", algo_name ? algo_name : "(null)", mdnid); result = CURLE_SSL_INVALIDCERTSTATUS; - goto error; + goto out; + } + + if(!X509_digest(cert, algo_type, buf, &length)) { + failf(data, "X509_digest() failed for channel binding"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; } /* Append "tls-server-end-point:" */ result = curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1); if(result) - goto error; + goto out; /* Append digest */ result = curlx_dyn_addn(binding, buf, length); -error: +out: X509_free(cert); return result; } @@ -5359,7 +5435,7 @@ if(!rand_enough()) return CURLE_FAILED_INIT; } - /* RAND_bytes() returns 1 on success, 0 otherwise. */ + /* RAND_bytes() returns 1 on success, 0 otherwise. */ rc = RAND_bytes(entropy, (ossl_valsize_t)curlx_uztosi(length)); return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT; } @@ -5424,7 +5500,11 @@ #endif SSLSUPP_CA_CACHE | SSLSUPP_HTTPS_PROXY | - SSLSUPP_CIPHER_LIST, + SSLSUPP_CIPHER_LIST | + SSLSUPP_ISSUERCERT | + SSLSUPP_ISSUERCERT_BLOB | + SSLSUPP_SSL_EC_CURVES | + SSLSUPP_CRLFILE, sizeof(struct ossl_ctx),
diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h index aeeb8dd..61d4a17 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.h +++ b/Utilities/cmcurl/lib/vtls/openssl.h
@@ -23,9 +23,43 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_OPENSSL + +#ifdef USE_WIN32_CRYPTO +#include <wincrypt.h> +/* If <wincrypt.h> is included directly, or indirectly via <schannel.h>, + * <winldap.h>, <iphlpapi.h>, or something else, <wincrypt.h> does this: + * #define X509_NAME ((LPCSTR)7) + * + * In BoringSSL/AWC-LC's <openssl/base.h> there is: + * typedef struct X509_name_st X509_NAME; + * etc. + * + * The redefined symbols break these OpenSSL headers when included after + * <wincrypt.h>. + * The workaround is to undefine those defines here (and only here). + * + * For unity builds it may need to be repeated elsewhere too, e.g. in ldap.c, + * to apply to other sources using OpenSSL includes. Each compilation unit + * needs undefine them between the first <wincrypt.h> include and the first + * OpenSSL include. + * + * OpenSSL does this in <openssl/ssl.h> and <openssl/x509v3.h>, but it + * also does the #undef by including <openssl/ossl_typ.h>. <3.1.0 only does + * it on the first include. + * + * LibreSSL automatically undefines these symbols before using them. + */ +#undef X509_NAME +#undef X509_EXTENSIONS +#undef PKCS7_ISSUER_AND_SERIAL +#undef PKCS7_SIGNER_INFO +#undef OCSP_REQUEST +#undef OCSP_RESPONSE +#endif /* USE_WIN32_CRYPTO */ + /* * This header should only be needed to get included by vtls.c, openssl.c * and ngtcp2.c @@ -34,7 +68,7 @@ #include <openssl/ossl_typ.h> #include <openssl/ssl.h> -#include "../urldata.h" +#include "urldata.h" #if OPENSSL_VERSION_NUMBER >= 0x30000000L #define HAVE_OPENSSL3 /* non-fork OpenSSL 3.x or later */ @@ -44,6 +78,12 @@ #define HAVE_BORINGSSL_LIKE #endif +/* OpenSSL 3.5.0+ has built-in 'SSLKEYLOGFILE' support if built with + 'enable-sslkeylog' */ +#if OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_SSLKEYLOG) +#define HAVE_KEYLOG_UPSTREAM +#endif + /* * Whether SSL_CTX_set_keylog_callback is available. * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 @@ -73,7 +113,7 @@ CURLcode io_result; /* result of last BIO cfilter operation */ /* blocked writes need to retry with same length, remember it */ int blocked_ssl_write_len; -#ifndef HAVE_KEYLOG_CALLBACK +#if !defined(HAVE_KEYLOG_UPSTREAM) && !defined(HAVE_KEYLOG_CALLBACK) /* Set to true once a valid keylog entry has been created to avoid dupes. This is a bool and not a bitfield because it is passed by address. */ bool keylog_done; @@ -100,13 +140,16 @@ struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - const struct alpn_spec *alpns, + const struct alpn_spec *alpns_requested, Curl_ossl_ctx_setup_cb *cb_setup, void *cb_user_data, Curl_ossl_new_session_cb *cb_new_session, void *ssl_user_data, Curl_ossl_init_session_reuse_cb *sess_reuse_cb); +/* Is a resolved HTTPS-RR needed for initializing OpenSSL? */ +bool Curl_ossl_need_httpsrr(struct Curl_easy *data); + #ifndef HAVE_OPENSSL3 #define SSL_get1_peer_certificate SSL_get_peer_certificate #endif @@ -115,7 +158,7 @@ /** * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and - * easy handle `data`. Will allow reuse of a shared cache if suitable + * easy handle `data`. Allows reuse of a shared cache if suitable * and configured. */ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, @@ -132,7 +175,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, - SSL_SESSION *ssl_sessionid, + SSL_SESSION *session, int ietf_tls_id, const char *alpn, unsigned char *quic_tp,
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c index fc380b0..d886c85 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -23,22 +23,27 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_RUSTLS #include <rustls.h> -#include "../curlx/fopen.h" -#include "../curlx/strerr.h" -#include "../urldata.h" -#include "../curl_trc.h" -#include "vtls.h" -#include "vtls_int.h" -#include "rustls.h" -#include "keylog.h" -#include "cipher_suite.h" -#include "x509asn1.h" +#include "curlx/fopen.h" +#include "curlx/strerr.h" +#include "urldata.h" +#include "cf-dns.h" +#include "curl_trc.h" +#include "httpsrr.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/rustls.h" +#include "vtls/keylog.h" +#include "vtls/cipher_suite.h" +#include "vtls/x509asn1.h" +#ifdef USE_ECH +#include "curlx/base64.h" +#endif struct rustls_ssl_backend_data { const struct rustls_client_config *config; @@ -82,7 +87,7 @@ (void)data; DEBUGASSERT(ctx && ctx->backend); backend = (struct rustls_ssl_backend_data *)ctx->backend; - return backend->data_in_pending; + return (bool)backend->data_in_pending; } struct io_ctx { @@ -104,7 +109,7 @@ if(result) { nread = 0; /* !checksrc! disable ERRNOVAR 4 */ - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) ret = EAGAIN; else ret = EINVAL; @@ -129,7 +134,7 @@ buf, len, FALSE, &nwritten); if(result) { nwritten = 0; - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) ret = EAGAIN; else ret = EINVAL; @@ -293,8 +298,8 @@ * we get either an error or EAGAIN/EWOULDBLOCK. * * it is okay to call this function with plainbuf == NULL and plainlen == 0. - * In that case, it will not read anything into Rustls' plaintext input buffer. - * It will only drain Rustls' plaintext output buffer into the socket. + * In that case, it does not read anything into Rustls' plaintext input buffer. + * It only drains Rustls' plaintext output buffer into the socket. */ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *plainbuf, size_t plainlen, @@ -354,7 +359,7 @@ result = cr_flush_out(cf, data, rconn); if(result) { - if(CURLE_AGAIN == result) { + if(result == CURLE_AGAIN) { /* The TLS bytes may have been partially written, but we fail the * complete send() and remember how much we already added to Rustls. */ backend->plain_out_buffered = plainwritten; @@ -368,7 +373,7 @@ *pnwritten += (ssize_t)plainwritten; out: - CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zd", + CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zu", plainlen, result, *pnwritten); return result; } @@ -532,8 +537,8 @@ CURLcode result = CURLE_OK; rustls_result rr; + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -569,14 +574,14 @@ } #ifdef USE_ECH - if(ECH_ENABLED(data)) { + if(CURLECH_ENABLED(data)) { tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3; tls_versions_len = 1; infof(data, "rustls: ECH enabled, forcing TLSv1.3"); } #endif /* USE_ECH */ - cipher_suites = curlx_malloc(sizeof(*cipher_suites) * (cipher_suites_len)); + cipher_suites = curlx_malloc(sizeof(*cipher_suites) * cipher_suites_len); if(!cipher_suites) { result = CURLE_OUT_OF_MEMORY; goto cleanup; @@ -905,16 +910,26 @@ } #ifdef USE_ECH + +static bool cr_ech_need_httpsrr(struct Curl_easy *data) +{ + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +} + static CURLcode init_config_builder_ech(struct Curl_easy *data, - const struct ssl_connect_data *connssl, + struct Curl_cfilter *cf, struct rustls_client_config_builder *builder) { const rustls_hpke *hpke = rustls_supported_hpke(); unsigned char *ech_config = NULL; size_t ech_config_len = 0; struct Curl_dns_entry *dns = NULL; - struct Curl_https_rrinfo *rinfo = NULL; CURLcode result = CURLE_OK; rustls_result rr; @@ -959,16 +974,9 @@ } } else { - if(connssl->peer.hostname) { - dns = Curl_dnscache_get(data, connssl->peer.hostname, - connssl->peer.port, data->conn->ip_version); - } - if(!dns) { - failf(data, "rustls: ECH requested but no DNS info available"); - result = CURLE_SSL_CONNECT_ERROR; - goto cleanup; - } - rinfo = dns->hinfo; + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); + if(!rinfo || !rinfo->echconfiglist) { failf(data, "rustls: ECH requested but no ECHConfig available"); result = CURLE_SSL_CONNECT_ERROR; @@ -993,7 +1001,7 @@ curlx_free(ech_config); } if(dns) { - Curl_resolv_unlink(data, &dns); + Curl_dns_entry_unlink(data, &dns); } return result; } @@ -1064,8 +1072,8 @@ } #ifdef USE_ECH - if(ECH_ENABLED(data)) { - result = init_config_builder_ech(data, connssl, config_builder); + if(CURLECH_ENABLED(data)) { + result = init_config_builder_ech(data, cf, config_builder); if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) { rustls_client_config_builder_free(config_builder); return result; @@ -1116,7 +1124,7 @@ /* Given an established network connection, do a TLS handshake. * - * This function will set `*done` to true once the handshake is complete. + * This function sets `*done` to true once the handshake is complete. * This function never reads the value of `*done*`. */ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1127,22 +1135,33 @@ (struct rustls_ssl_backend_data *)connssl->backend; const struct rustls_connection *rconn = NULL; CURLcode tmperr = CURLE_OK; - int result; + CURLcode result; bool wants_read; bool wants_write; + ssize_t nread; DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state); *done = FALSE; - if(!backend->conn) { - result = cr_init_backend(cf, data, - (struct rustls_ssl_backend_data *)connssl->backend); - CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); - if(result != CURLE_OK) { - return result; +#ifdef USE_ECH + /* if we do ECH and need the HTTPS-RR information for it, + * we delay the connect until it arrives or DNS resolve fails. */ + if(cr_ech_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); + return CURLE_OK; } +#endif /* USE_ECH */ + + if(!backend->conn) { + result = + cr_init_backend(cf, data, + (struct rustls_ssl_backend_data *)connssl->backend); + CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); + if(result) + return result; connssl->state = ssl_connection_negotiating; } rconn = backend->conn; @@ -1171,6 +1190,7 @@ } /* REALLY Done with the handshake. */ { +#ifdef CURLVERBOSE const uint16_t proto = rustls_connection_get_protocol_version(rconn); const rustls_str ciphersuite_name = rustls_connection_get_negotiated_ciphersuite_name(rconn); @@ -1181,6 +1201,7 @@ ver = "TLSv1.3"; if(proto == RUSTLS_TLS_VERSION_TLSV1_2) ver = "TLSv1.2"; +#endif infof(data, "rustls: handshake complete, %s, ciphersuite: %.*s, " "key exchange group: %.*s", @@ -1196,7 +1217,7 @@ while(rustls_connection_get_peer_certificate(rconn, num_certs)) { num_certs++; if(num_certs > MAX_ALLOWED_CERT_AMOUNT) { - failf(data, "%zu certificates is more than allowed (%u)", + failf(data, "%zu certificates is more than allowed (%d)", num_certs, MAX_ALLOWED_CERT_AMOUNT); return CURLE_SSL_CONNECT_ERROR; } @@ -1260,7 +1281,13 @@ if(wants_read) { CURL_TRC_CF(data, cf, "rustls_connection wants us to read_tls."); - if(tls_recv_more(cf, data, &tmperr) < 0) { + nread = tls_recv_more(cf, data, &tmperr); + if(nread == 0) { + connssl->peer_closed = TRUE; + failf(data, "TLS connect error: Connection closed abruptly"); + return CURLE_SSL_CONNECT_ERROR; + } + if(nread < 0) { if(tmperr == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "reading would block"); connssl->io_need = CURL_SSL_IO_NEED_RECV; @@ -1398,7 +1425,8 @@ SSLSUPP_CIPHER_LIST | SSLSUPP_TLS13_CIPHERSUITES | SSLSUPP_CERTINFO | - SSLSUPP_ECH, + SSLSUPP_ECH | + SSLSUPP_CRLFILE, sizeof(struct rustls_ssl_backend_data), NULL, /* init */
diff --git a/Utilities/cmcurl/lib/vtls/rustls.h b/Utilities/cmcurl/lib/vtls/rustls.h index 2d3b0bb..b6ddbd1 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.h +++ b/Utilities/cmcurl/lib/vtls/rustls.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_RUSTLS
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 891dfc4..fb5ff0e 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -27,33 +27,33 @@ * Source file for all Schannel-specific code for the TLS/SSL layer. No code * but vtls.c should ever call or use these functions. */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL #ifndef USE_WINDOWS_SSPI -#error "cannot compile SCHANNEL support without SSPI." +#error "cannot compile Schannel support without SSPI." #endif -#include "schannel.h" -#include "schannel_int.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "../curl_trc.h" -#include "../connect.h" /* for the connect timeout */ -#include "../strdup.h" -#include "../strerror.h" -#include "../select.h" /* for the socket readiness */ -#include "../curlx/fopen.h" -#include "../curlx/multibyte.h" -#include "x509asn1.h" -#include "../system_win32.h" -#include "../curlx/version_win32.h" -#include "../rand.h" -#include "../curlx/strparse.h" -#include "../progress.h" -#include "../curl_sha256.h" +#include "vtls/schannel.h" +#include "vtls/schannel_int.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "curl_trc.h" +#include "connect.h" /* for the connect timeout */ +#include "curlx/strdup.h" +#include "strerror.h" +#include "select.h" /* for the socket readiness */ +#include "curlx/fopen.h" +#include "curlx/multibyte.h" +#include "vtls/x509asn1.h" +#include "system_win32.h" +#include "curlx/version_win32.h" +#include "rand.h" +#include "curlx/strparse.h" +#include "progress.h" +#include "curl_sha256.h" /* Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF() * and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These @@ -105,11 +105,6 @@ * #define failf(x, y, ...) curl_mprintf(y, __VA_ARGS__) */ -/* Offered when targeting Vista (XP SP2+) */ -#ifndef CALG_SHA_256 -#define CALG_SHA_256 0x0000800c -#endif - /* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ #ifndef PKCS12_NO_PERSIST_KEY #define PKCS12_NO_PERSIST_KEY 0x00008000 @@ -134,10 +129,6 @@ static bool s_win_has_alpn; #endif -static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *pinnedpubkey); - static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, void *BufDataPtr, unsigned long BufByteSize) { @@ -184,20 +175,20 @@ for(; i <= (ssl_version_max >> 16); ++i) { switch(i) { case CURL_SSLVERSION_TLSv1_0: - (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_0_CLIENT; break; case CURL_SSLVERSION_TLSv1_1: - (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_1_CLIENT; break; case CURL_SSLVERSION_TLSv1_2: - (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_2_CLIENT; break; case CURL_SSLVERSION_TLSv1_3: /* Windows Server 2022 and newer */ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { - (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_3_CLIENT; break; } else { /* Windows 10 and older */ @@ -209,7 +200,7 @@ return CURLE_OK; } -#define CIPHEROPTION(x) {#x, x} +#define CIPHEROPTION(x) { #x, x } struct algo { const char *name; @@ -225,103 +216,52 @@ CIPHEROPTION(CALG_MAC), CIPHEROPTION(CALG_RSA_SIGN), CIPHEROPTION(CALG_DSS_SIGN), -/* ifdefs for the options that are defined conditionally in wincrypt.h */ -#ifdef CALG_NO_SIGN CIPHEROPTION(CALG_NO_SIGN), -#endif CIPHEROPTION(CALG_RSA_KEYX), CIPHEROPTION(CALG_DES), -#ifdef CALG_3DES_112 CIPHEROPTION(CALG_3DES_112), -#endif CIPHEROPTION(CALG_3DES), CIPHEROPTION(CALG_DESX), CIPHEROPTION(CALG_RC2), CIPHEROPTION(CALG_RC4), CIPHEROPTION(CALG_SEAL), -#ifdef CALG_DH_SF CIPHEROPTION(CALG_DH_SF), -#endif CIPHEROPTION(CALG_DH_EPHEM), -#ifdef CALG_AGREEDKEY_ANY CIPHEROPTION(CALG_AGREEDKEY_ANY), -#endif -#ifdef CALG_HUGHES_MD5 CIPHEROPTION(CALG_HUGHES_MD5), -#endif CIPHEROPTION(CALG_SKIPJACK), -#ifdef CALG_TEK CIPHEROPTION(CALG_TEK), -#endif CIPHEROPTION(CALG_CYLINK_MEK), /* spellchecker:disable-line */ CIPHEROPTION(CALG_SSL3_SHAMD5), -#ifdef CALG_SSL3_MASTER CIPHEROPTION(CALG_SSL3_MASTER), -#endif -#ifdef CALG_SCHANNEL_MASTER_HASH CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH), -#endif -#ifdef CALG_SCHANNEL_MAC_KEY CIPHEROPTION(CALG_SCHANNEL_MAC_KEY), -#endif -#ifdef CALG_SCHANNEL_ENC_KEY CIPHEROPTION(CALG_SCHANNEL_ENC_KEY), -#endif -#ifdef CALG_PCT1_MASTER CIPHEROPTION(CALG_PCT1_MASTER), -#endif -#ifdef CALG_SSL2_MASTER CIPHEROPTION(CALG_SSL2_MASTER), -#endif -#ifdef CALG_TLS1_MASTER CIPHEROPTION(CALG_TLS1_MASTER), -#endif -#ifdef CALG_RC5 CIPHEROPTION(CALG_RC5), -#endif -#ifdef CALG_HMAC CIPHEROPTION(CALG_HMAC), -#endif -#ifdef CALG_TLS1PRF CIPHEROPTION(CALG_TLS1PRF), -#endif -#ifdef CALG_HASH_REPLACE_OWF CIPHEROPTION(CALG_HASH_REPLACE_OWF), -#endif -#ifdef CALG_AES_128 CIPHEROPTION(CALG_AES_128), -#endif -#ifdef CALG_AES_192 CIPHEROPTION(CALG_AES_192), -#endif -#ifdef CALG_AES_256 CIPHEROPTION(CALG_AES_256), -#endif -#ifdef CALG_AES CIPHEROPTION(CALG_AES), -#endif -#ifdef CALG_SHA_256 CIPHEROPTION(CALG_SHA_256), -#endif -#ifdef CALG_SHA_384 CIPHEROPTION(CALG_SHA_384), -#endif -#ifdef CALG_SHA_512 CIPHEROPTION(CALG_SHA_512), -#endif -#ifdef CALG_ECDH CIPHEROPTION(CALG_ECDH), -#endif +/* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ #ifdef CALG_ECMQV CIPHEROPTION(CALG_ECMQV), #endif -#ifdef CALG_ECDSA CIPHEROPTION(CALG_ECDSA), -#endif +/* Offered by mingw-w64 v7+. MS SDK 7.0A+. */ #ifdef CALG_ECDH_EPHEM CIPHEROPTION(CALG_ECDH_EPHEM), #endif - {NULL, 0}, + { NULL, 0 }, }; static int get_alg_id_by_name(const char *name) @@ -337,7 +277,7 @@ return 0; /* not found */ } -#define NUM_CIPHERS 47 /* There are 47 options listed above */ +#define NUM_CIPHERS 47 /* There are a maximum of 47 options listed above */ static CURLcode set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, ALG_ID *algIds) @@ -419,6 +359,347 @@ return CURLE_OK; } +static CURLcode get_client_cert(struct Curl_easy *data, + HCERTSTORE *out_cert_store, + PCCERT_CONTEXT *out_cert_context) +{ + PCCERT_CONTEXT client_cert = NULL; + HCERTSTORE client_cert_store = NULL; + CURLcode result = CURLE_OK; + + if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { + DWORD cert_store_name = 0; + TCHAR *cert_store_path = NULL; + TCHAR *cert_thumbprint_str = NULL; + TCHAR cert_thumbprint_buf[CERT_THUMBPRINT_STR_LEN + 1]; + CRYPT_HASH_BLOB cert_thumbprint; + BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; + HCERTSTORE cert_store = NULL; + FILE *fInCert = NULL; + void *certdata = NULL; + size_t certsize = 0; + bool blob = data->set.ssl.primary.cert_blob != NULL; + + if(blob) { + certdata = data->set.ssl.primary.cert_blob->data; + certsize = data->set.ssl.primary.cert_blob->len; + } + else { + TCHAR *cert_path = + curlx_convert_UTF8_to_tchar(data->set.ssl.primary.clientcert); + if(!cert_path) + return CURLE_OUT_OF_MEMORY; + + result = get_cert_location(cert_path, &cert_store_name, + &cert_store_path, &cert_thumbprint_str); + + /* 'cert_thumbprint_str' points in to the allocated 'cert_path', copy + the data. The string is verified to be CERT_THUMBPRINT_STR_LEN bytes + long within the get_cert_location() function. */ + if(!result && cert_thumbprint_str) { + memcpy(cert_thumbprint_buf, cert_thumbprint_str, + sizeof(cert_thumbprint_buf)); + cert_thumbprint_str = cert_thumbprint_buf; + } + + curlx_free(cert_path); + if(result && (data->set.ssl.primary.clientcert[0] != '\0')) + fInCert = curlx_fopen(data->set.ssl.primary.clientcert, "rb"); + + if(result && !fInCert) { + failf(data, "schannel: Failed to get certificate location" + " or file for %s", + data->set.ssl.primary.clientcert); + return result; + } + } + + if((fInCert || blob) && data->set.ssl.cert_type && + !curl_strequal(data->set.ssl.cert_type, "P12")) { + failf(data, "schannel: certificate format compatibility error " + "for %s", + blob ? "(memory blob)" : data->set.ssl.primary.clientcert); + curlx_free(cert_store_path); + if(fInCert) + curlx_fclose(fInCert); + return CURLE_SSL_CERTPROBLEM; + } + + if(fInCert || blob) { + /* Reading a .p12 or .pfx file, like the example at bottom of + https://learn.microsoft.com/archive/msdn-technet-forums/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 + */ + CRYPT_DATA_BLOB datablob; + WCHAR *pszPassword; + size_t pwd_len = 0; + int cert_find_flags; + const char *cert_showfilename_error = blob ? + "(memory blob)" : data->set.ssl.primary.clientcert; + curlx_free(cert_store_path); + if(fInCert) { + long cert_tell = 0; + bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; + if(continue_reading) + cert_tell = ftell(fInCert); + if(cert_tell < 0) + continue_reading = FALSE; + else + certsize = (size_t)cert_tell; + if(continue_reading) + continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; + if(continue_reading && (certsize < CURL_MAX_INPUT_LENGTH)) + certdata = curlx_malloc(certsize + 1); + if((!certdata) || + ((int) fread(certdata, certsize, 1, fInCert) != 1)) + continue_reading = FALSE; + curlx_fclose(fInCert); + if(!continue_reading) { + failf(data, "schannel: Failed to read cert file %s", + data->set.ssl.primary.clientcert); + curlx_free(certdata); + return CURLE_SSL_CERTPROBLEM; + } + } + + /* Convert key-pair data to the in-memory certificate store */ + datablob.pbData = (BYTE *)certdata; + datablob.cbData = (DWORD)certsize; + + if(data->set.ssl.key_passwd) + pwd_len = strlen(data->set.ssl.key_passwd); + pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); + if(pszPassword) { + int str_w_len = 0; + if(pwd_len > 0) + str_w_len = MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + data->set.ssl.key_passwd, + (int)pwd_len, + pszPassword, (int)(pwd_len + 1)); + + if((str_w_len >= 0) && (str_w_len <= (int)pwd_len)) + pszPassword[str_w_len] = 0; + else + pszPassword[0] = 0; + + cert_store = PFXImportCertStore(&datablob, pszPassword, + PKCS12_NO_PERSIST_KEY); + curlx_free(pszPassword); + } + if(!blob) + curlx_free(certdata); + if(!cert_store) { + DWORD errorcode = GetLastError(); + if(errorcode == ERROR_INVALID_PASSWORD) + failf(data, "schannel: Failed to import cert file %s, " + "password is bad", + cert_showfilename_error); + else + failf(data, "schannel: Failed to import cert file %s, " + "last error is 0x%08lx", + cert_showfilename_error, errorcode); + return CURLE_SSL_CERTPROBLEM; + } + + /* CERT_FIND_HAS_PRIVATE_KEY is only available in Windows 8 / Server + 2012, (NT v6.2). For earlier versions we use CURL_FIND_ANY. */ + if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) + cert_find_flags = CERT_FIND_HAS_PRIVATE_KEY; + else + cert_find_flags = CERT_FIND_ANY; + + client_cert = + CertFindCertificateInStore(cert_store, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + cert_find_flags, NULL, NULL); + + if(!client_cert) { + failf(data, "schannel: Failed to get certificate from file %s" + ", last error is 0x%08lx", + cert_showfilename_error, GetLastError()); + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + } + else { + cert_store = + CertOpenStore( +#ifdef UNICODE + CERT_STORE_PROV_SYSTEM_W, +#else + CERT_STORE_PROV_SYSTEM_A, +#endif + 0, + (HCRYPTPROV)NULL, + CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, + cert_store_path); + if(!cert_store) { + char *path_utf8 = + curlx_convert_tchar_to_UTF8(cert_store_path); + failf(data, "schannel: Failed to open cert store %lx %s, " + "last error is 0x%08lx", + cert_store_name, (path_utf8 ? path_utf8 : "(unknown)"), + GetLastError()); + curlx_free(cert_store_path); + curlx_free(path_utf8); + return CURLE_SSL_CERTPROBLEM; + } + curlx_free(cert_store_path); + + cert_thumbprint.pbData = cert_thumbprint_data; + cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; + + if(!CryptStringToBinary(cert_thumbprint_str, + CERT_THUMBPRINT_STR_LEN, + CRYPT_STRING_HEX, + cert_thumbprint_data, + &cert_thumbprint.cbData, + NULL, NULL)) { + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + + client_cert = + CertFindCertificateInStore(cert_store, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_HASH, &cert_thumbprint, NULL); + if(!client_cert) { + /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ + CertCloseStore(cert_store, 0); + failf(data, "schannel: client cert not found in cert store"); + return CURLE_SSL_CERTPROBLEM; + } + } + client_cert_store = cert_store; + } + + *out_cert_store = client_cert_store; + *out_cert_context = client_cert; + + return CURLE_OK; +} + +static CURLcode acquire_sspi_handle(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct schannel_ssl_backend_data *backend, + PCCERT_CONTEXT client_cert, + DWORD flags, + DWORD enabled_protocols) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + SECURITY_STATUS sspi_status = SEC_E_OK; + CURLcode result; + + /* 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)) { + + SCH_CREDENTIALS credentials = { 0 }; + TLS_PARAMETERS tls_parameters = { 0 }; + CRYPTO_SETTINGS crypto_settings[1]; + PCCERT_CONTEXT client_certs[1]; + + if(client_cert) + client_certs[0] = client_cert; + else + client_certs[0] = NULL; + + memset(crypto_settings, 0, sizeof(crypto_settings)); + + tls_parameters.pDisabledCrypto = crypto_settings; + + /* The number of blocked suites */ + tls_parameters.cDisabledCrypto = (DWORD)0; + credentials.pTlsParameters = &tls_parameters; + credentials.cTlsParameters = 1; + + credentials.dwVersion = SCH_CREDENTIALS_VERSION; + credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO; + + credentials.pTlsParameters->grbitDisabledProtocols = ~enabled_protocols; + + if(client_certs[0]) { + credentials.cCreds = 1; + credentials.paCred = client_certs; + } + + sspi_status = + Curl_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *)CURL_UNCONST(UNISP_NAME), + SECPKG_CRED_OUTBOUND, NULL, + &credentials, NULL, NULL, + &backend->cred->cred_handle, NULL); + } + else { + /* Pre-Windows 10 1809 or the user set a legacy algorithm list. + Schannel does 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 }; + PCCERT_CONTEXT client_certs[1]; + + if(client_cert) + client_certs[0] = client_cert; + else + client_certs[0] = NULL; + + schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + schannel_cred.dwFlags = flags; + schannel_cred.grbitEnabledProtocols = enabled_protocols; + + if(ciphers) { + if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { + infof(data, "schannel: WARNING: This version of Schannel " + "negotiates a less-secure TLS version than TLS 1.3 because the " + "user set an algorithm cipher list."); + } + result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); + if(result) { + failf(data, "schannel: Failed setting algorithm cipher list"); + return result; + } + } + else { + schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO; + } + + if(client_certs[0]) { + schannel_cred.cCreds = 1; + schannel_cred.paCred = client_certs; + } + + sspi_status = + Curl_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *)CURL_UNCONST(UNISP_NAME), + SECPKG_CRED_OUTBOUND, NULL, + &schannel_cred, NULL, NULL, + &backend->cred->cred_handle, NULL); + } + + if(sspi_status != SEC_E_OK) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: AcquireCredentialsHandle failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + switch(sspi_status) { + case SEC_E_INSUFFICIENT_MEMORY: + return CURLE_OUT_OF_MEMORY; + case SEC_E_NO_CREDENTIALS: + case SEC_E_SECPKG_NOT_FOUND: + case SEC_E_NOT_OWNER: + case SEC_E_UNKNOWN_CREDENTIALS: + case SEC_E_INTERNAL_ERROR: + default: + return CURLE_SSL_CONNECT_ERROR; + } + } + + return CURLE_OK; +} + static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -426,9 +707,8 @@ 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); - PCCERT_CONTEXT client_certs[1] = { NULL }; + PCCERT_CONTEXT client_cert = NULL; HCERTSTORE client_cert_store = NULL; - SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; /* setup Schannel API options */ @@ -488,8 +768,8 @@ else infof(data, "schannel: enabled automatic use of client certificate"); + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -509,213 +789,9 @@ return CURLE_SSL_CONNECT_ERROR; } - /* client certificate */ - if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { - DWORD cert_store_name = 0; - TCHAR *cert_store_path = NULL; - TCHAR *cert_thumbprint_str = NULL; - CRYPT_HASH_BLOB cert_thumbprint; - BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; - HCERTSTORE cert_store = NULL; - FILE *fInCert = NULL; - void *certdata = NULL; - size_t certsize = 0; - bool blob = data->set.ssl.primary.cert_blob != NULL; - TCHAR *cert_path = NULL; - if(blob) { - certdata = data->set.ssl.primary.cert_blob->data; - certsize = data->set.ssl.primary.cert_blob->len; - } - else { - cert_path = curlx_convert_UTF8_to_tchar( - data->set.ssl.primary.clientcert); - if(!cert_path) - return CURLE_OUT_OF_MEMORY; - - result = get_cert_location(cert_path, &cert_store_name, - &cert_store_path, &cert_thumbprint_str); - - if(result && (data->set.ssl.primary.clientcert[0] != '\0')) - fInCert = curlx_fopen(data->set.ssl.primary.clientcert, "rb"); - - if(result && !fInCert) { - failf(data, "schannel: Failed to get certificate location" - " or file for %s", - data->set.ssl.primary.clientcert); - curlx_free(cert_path); - return result; - } - } - - if((fInCert || blob) && data->set.ssl.cert_type && - !curl_strequal(data->set.ssl.cert_type, "P12")) { - failf(data, "schannel: certificate format compatibility error " - " for %s", - blob ? "(memory blob)" : data->set.ssl.primary.clientcert); - curlx_free(cert_store_path); - curlx_free(cert_path); - if(fInCert) - curlx_fclose(fInCert); - return CURLE_SSL_CERTPROBLEM; - } - - if(fInCert || blob) { - /* Reading a .P12 or .pfx file, like the example at bottom of - https://learn.microsoft.com/archive/msdn-technet-forums/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 - */ - CRYPT_DATA_BLOB datablob; - WCHAR *pszPassword; - size_t pwd_len = 0; - int str_w_len = 0; - int cert_find_flags; - const char *cert_showfilename_error = blob ? - "(memory blob)" : data->set.ssl.primary.clientcert; - curlx_free(cert_store_path); - curlx_free(cert_path); - if(fInCert) { - long cert_tell = 0; - bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; - if(continue_reading) - cert_tell = ftell(fInCert); - if(cert_tell < 0) - continue_reading = FALSE; - else - certsize = (size_t)cert_tell; - if(continue_reading) - continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; - if(continue_reading && (certsize < CURL_MAX_INPUT_LENGTH)) - certdata = curlx_malloc(certsize + 1); - if((!certdata) || - ((int) fread(certdata, certsize, 1, fInCert) != 1)) - continue_reading = FALSE; - curlx_fclose(fInCert); - if(!continue_reading) { - failf(data, "schannel: Failed to read cert file %s", - data->set.ssl.primary.clientcert); - curlx_free(certdata); - return CURLE_SSL_CERTPROBLEM; - } - } - - /* Convert key-pair data to the in-memory certificate store */ - datablob.pbData = (BYTE *)certdata; - datablob.cbData = (DWORD)certsize; - - if(data->set.ssl.key_passwd) - pwd_len = strlen(data->set.ssl.key_passwd); - pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); - if(pszPassword) { - if(pwd_len > 0) - str_w_len = MultiByteToWideChar(CP_UTF8, - MB_ERR_INVALID_CHARS, - data->set.ssl.key_passwd, - (int)pwd_len, - pszPassword, (int)(pwd_len + 1)); - - if((str_w_len >= 0) && (str_w_len <= (int)pwd_len)) - pszPassword[str_w_len] = 0; - else - pszPassword[0] = 0; - - if(Curl_isVistaOrGreater) - cert_store = PFXImportCertStore(&datablob, pszPassword, - PKCS12_NO_PERSIST_KEY); - else - cert_store = PFXImportCertStore(&datablob, pszPassword, 0); - - curlx_free(pszPassword); - } - if(!blob) - curlx_free(certdata); - if(!cert_store) { - DWORD errorcode = GetLastError(); - if(errorcode == ERROR_INVALID_PASSWORD) - failf(data, "schannel: Failed to import cert file %s, " - "password is bad", - cert_showfilename_error); - else - failf(data, "schannel: Failed to import cert file %s, " - "last error is 0x%08lx", - cert_showfilename_error, errorcode); - return CURLE_SSL_CERTPROBLEM; - } - - /* CERT_FIND_HAS_PRIVATE_KEY is only available in Windows 8 / Server - 2012, (NT v6.2). For earlier versions we use CURL_FIND_ANY. */ - if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) - cert_find_flags = CERT_FIND_HAS_PRIVATE_KEY; - else - cert_find_flags = CERT_FIND_ANY; - - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - cert_find_flags, NULL, NULL); - - if(!client_certs[0]) { - failf(data, "schannel: Failed to get certificate from file %s" - ", last error is 0x%08lx", - cert_showfilename_error, GetLastError()); - CertCloseStore(cert_store, 0); - return CURLE_SSL_CERTPROBLEM; - } - } - else { - cert_store = - CertOpenStore( -#ifdef UNICODE - CERT_STORE_PROV_SYSTEM_W, -#else - CERT_STORE_PROV_SYSTEM_A, -#endif - 0, - (HCRYPTPROV)NULL, - CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, - cert_store_path); - if(!cert_store) { - char *path_utf8 = - curlx_convert_tchar_to_UTF8(cert_store_path); - failf(data, "schannel: Failed to open cert store %lx %s, " - "last error is 0x%08lx", - cert_store_name, - (path_utf8 ? path_utf8 : "(unknown)"), - GetLastError()); - curlx_free(cert_store_path); - curlx_free(path_utf8); - curlx_free(cert_path); - return CURLE_SSL_CERTPROBLEM; - } - curlx_free(cert_store_path); - - cert_thumbprint.pbData = cert_thumbprint_data; - cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; - - if(!CryptStringToBinary(cert_thumbprint_str, - CERT_THUMBPRINT_STR_LEN, - CRYPT_STRING_HEX, - cert_thumbprint_data, - &cert_thumbprint.cbData, - NULL, NULL)) { - curlx_free(cert_path); - CertCloseStore(cert_store, 0); - return CURLE_SSL_CERTPROBLEM; - } - - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_HASH, &cert_thumbprint, NULL); - - curlx_free(cert_path); - - if(!client_certs[0]) { - /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ - CertCloseStore(cert_store, 0); - failf(data, "schannel: client cert not found in cert store"); - return CURLE_SSL_CERTPROBLEM; - } - } - client_cert_store = cert_store; - } + result = get_client_cert(data, &client_cert_store, &client_cert); + if(result) + return result; /* allocate memory for the reusable credential handle */ backend->cred = (struct Curl_schannel_cred *) @@ -723,8 +799,8 @@ if(!backend->cred) { failf(data, "schannel: unable to allocate memory"); - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); + if(client_cert) + CertFreeCertificateContext(client_cert); if(client_cert_store) CertCloseStore(client_cert_store, 0); @@ -737,106 +813,13 @@ */ backend->cred->client_cert_store = client_cert_store; - /* 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)) { + result = acquire_sspi_handle(cf, data, backend, client_cert, + flags, enabled_protocols); - SCH_CREDENTIALS credentials = { 0 }; - TLS_PARAMETERS tls_parameters = { 0 }; - CRYPTO_SETTINGS crypto_settings[1]; + if(client_cert) + CertFreeCertificateContext(client_cert); - memset(crypto_settings, 0, sizeof(crypto_settings)); - - tls_parameters.pDisabledCrypto = crypto_settings; - - /* The number of blocked suites */ - tls_parameters.cDisabledCrypto = (DWORD)0; - credentials.pTlsParameters = &tls_parameters; - credentials.cTlsParameters = 1; - - credentials.dwVersion = SCH_CREDENTIALS_VERSION; - credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO; - - credentials.pTlsParameters->grbitDisabledProtocols = - (DWORD)~enabled_protocols; - - if(client_certs[0]) { - credentials.cCreds = 1; - credentials.paCred = client_certs; - } - - sspi_status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(UNISP_NAME), - SECPKG_CRED_OUTBOUND, NULL, - &credentials, NULL, NULL, - &backend->cred->cred_handle, NULL); - } - else { - /* Pre-Windows 10 1809 or the user set a legacy algorithm list. - 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 }; - schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - schannel_cred.dwFlags = flags; - schannel_cred.grbitEnabledProtocols = enabled_protocols; - - if(ciphers) { - if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { - infof(data, "schannel: WARNING: This version of Schannel " - "negotiates a less-secure TLS version than TLS 1.3 because the " - "user set an algorithm cipher list."); - } - result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); - if(result) { - failf(data, "schannel: Failed setting algorithm cipher list"); - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); - return result; - } - } - else { - schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO; - } - - if(client_certs[0]) { - schannel_cred.cCreds = 1; - schannel_cred.paCred = client_certs; - } - - sspi_status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(UNISP_NAME), - SECPKG_CRED_OUTBOUND, NULL, - &schannel_cred, NULL, NULL, - &backend->cred->cred_handle, NULL); - } - - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); - - if(sspi_status != SEC_E_OK) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: AcquireCredentialsHandle failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - return CURLE_OUT_OF_MEMORY; - case SEC_E_NO_CREDENTIALS: - case SEC_E_SECPKG_NOT_FOUND: - case SEC_E_NOT_OWNER: - case SEC_E_UNKNOWN_CREDENTIALS: - case SEC_E_INTERNAL_ERROR: - default: - return CURLE_SSL_CONNECT_ERROR; - } - } - - return CURLE_OK; + return result; } static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, @@ -862,14 +845,6 @@ DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", connssl->peer.hostname, connssl->peer.port)); - if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, - VERSION_LESS_THAN_EQUAL)) { - /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and - algorithms that may not be supported by all servers. */ - infof(data, "schannel: Windows version is old and may not be able to " - "connect to some servers due to lack of SNI, algorithms, etc."); - } - #ifdef HAS_ALPN_SCHANNEL backend->use_alpn = connssl->alpn && s_win_has_alpn; #else @@ -939,18 +914,18 @@ unsigned short *list_len = NULL; struct alpn_proto_buf proto; - /* The first four bytes will be an unsigned int indicating number + /* The first four bytes is an unsigned int indicating number of bytes of data in the rest of the buffer. */ extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]); cur += (int)sizeof(unsigned int); - /* The next four bytes are an indicator that this buffer will contain + /* The next four bytes are an indicator that this buffer contains ALPN data, as opposed to NPN, for example. */ *(unsigned int *)(void *)&alpn_buffer[cur] = SecApplicationProtocolNegotiationExt_ALPN; cur += (int)sizeof(unsigned int); - /* The next two bytes will be an unsigned short indicating the number + /* The next two bytes is an unsigned short indicating the number of bytes used to list the preferred protocols. */ list_len = (unsigned short *)(void *)(&alpn_buffer[cur]); cur += (int)sizeof(unsigned short); @@ -989,13 +964,11 @@ InitSecBufferDesc(&outbuf_desc, &outbuf, 1); /* security request flags */ - backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_STREAM; - - if(!ssl_config->auto_client_cert) { - backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; - } + backend->req_flags = + ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM | + (!ssl_config->auto_client_cert ? ISC_REQ_USE_SUPPLIED_CREDS : 0); /* allocate memory for the security context handle */ backend->ctxt = (struct Curl_schannel_ctxt *) @@ -1021,7 +994,7 @@ if(sspi_status != SEC_I_CONTINUE_NEEDED) { char buffer[STRERROR_LEN]; - Curl_safefree(backend->ctxt); + curlx_safefree(backend->ctxt); switch(sspi_status) { case SEC_E_INSUFFICIENT_MEMORY: failf(data, "schannel: initial InitializeSecurityContext failed: %s", @@ -1115,6 +1088,132 @@ } } +static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *pinnedpubkey) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + CERT_CONTEXT *pCertContextServer = NULL; + + /* Result is returned to caller */ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + DEBUGASSERT(backend); + + /* if a path was not specified, do not pin */ + if(!pinnedpubkey) + return CURLE_OK; + + do { + SECURITY_STATUS sspi_status; + const char *x509_der; + DWORD x509_der_len; + struct Curl_X509certificate x509_parsed; + struct Curl_asn1Element *pubkey; + + sspi_status = + Curl_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))); + break; /* failed */ + } + + if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && + (pCertContextServer->cbCertEncoded > 0))) + break; + + x509_der = (const char *)pCertContextServer->pbCertEncoded; + x509_der_len = pCertContextServer->cbCertEncoded; + memset(&x509_parsed, 0, sizeof(x509_parsed)); + if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) + break; + + pubkey = &x509_parsed.subjectPublicKeyInfo; + if(!pubkey->header || pubkey->end <= pubkey->header) { + failf(data, "SSL: failed retrieving public key from server certificate"); + break; + } + + result = Curl_pin_peer_pubkey(data, + pinnedpubkey, + (const unsigned char *)pubkey->header, + (size_t)(pubkey->end - pubkey->header)); + if(result) { + failf(data, "SSL: public key does not match pinned public key"); + } + } while(0); + + if(pCertContextServer) + CertFreeCertificateContext(pCertContextServer); + + return result; +} + +static CURLcode ensure_encoding_size(struct Curl_easy *data, + struct sbuffer *encdata, + size_t min_length) +{ + size_t size; + DEBUGASSERT(encdata->length >= encdata->offset); + if(encdata->length < encdata->offset) + return CURLE_FAILED_INIT; + size = encdata->length - encdata->offset; + if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || encdata->length < min_length) { + unsigned char *buffer; + size_t length = encdata->offset + CURL_SCHANNEL_BUFFER_FREE_SIZE; + if(length < min_length) + length = min_length; + buffer = curlx_realloc(encdata->buffer, length); + if(!buffer) { + failf(data, "schannel: unable to re-allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + + encdata->buffer = buffer; + encdata->length = length; + SCH_DEV(infof(data, "schannel: encdata.buffer resized %zu", + encdata->length)); + } + return CURLE_OK; +} + +static CURLcode ensure_decoding_size(struct Curl_easy *data, + struct sbuffer *decdata, + size_t nowsize, + size_t len) +{ + size_t size = nowsize > CURL_SCHANNEL_BUFFER_FREE_SIZE ? + nowsize : CURL_SCHANNEL_BUFFER_FREE_SIZE; + DEBUGASSERT(decdata->length >= decdata->offset); + if(decdata->length < decdata->offset) + return CURLE_FAILED_INIT; + else if(decdata->length - decdata->offset < size || + decdata->length < len) { + /* increase internal decrypted data buffer */ + size_t length = decdata->offset + size; + unsigned char *buffer; + /* make sure that the requested amount of data fits */ + if(length < len) + length = len; + + buffer = curlx_realloc(decdata->buffer, length); + if(!buffer) { + failf(data, "schannel: unable to re-allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + decdata->buffer = buffer; + decdata->length = length; + } + return CURLE_OK; +} + static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1123,8 +1222,7 @@ (struct schannel_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); int i; - size_t nread = 0, written = 0; - unsigned char *reallocated_buffer; + size_t nread = 0; SecBuffer outbuf[3]; SecBufferDesc outbuf_desc; SecBuffer inbuf[2]; @@ -1146,58 +1244,43 @@ return CURLE_SSL_CONNECT_ERROR; /* buffer to store previously received and decrypted data */ - if(!backend->decdata_buffer) { - backend->decdata_offset = 0; - backend->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - backend->decdata_buffer = curlx_malloc(backend->decdata_length); - if(!backend->decdata_buffer) { + if(!backend->decdata.buffer) { + backend->decdata.offset = 0; + backend->decdata.length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + backend->decdata.buffer = curlx_malloc(backend->decdata.length); + if(!backend->decdata.buffer) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } } /* buffer to store previously received and encrypted data */ - if(!backend->encdata_buffer) { + if(!backend->encdata.buffer) { backend->encdata_is_incomplete = FALSE; - backend->encdata_offset = 0; - backend->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - backend->encdata_buffer = curlx_malloc(backend->encdata_length); - if(!backend->encdata_buffer) { + backend->encdata.offset = 0; + backend->encdata.length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + backend->encdata.buffer = curlx_malloc(backend->encdata.length); + if(!backend->encdata.buffer) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } } - /* if we need a bigger buffer to read a full message, increase buffer now */ - if(backend->encdata_length - backend->encdata_offset < - CURL_SCHANNEL_BUFFER_FREE_SIZE) { - /* increase internal encrypted data buffer */ - size_t reallocated_length = backend->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - reallocated_buffer = curlx_realloc(backend->encdata_buffer, - reallocated_length); - - if(!reallocated_buffer) { - failf(data, "schannel: unable to re-allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - else { - backend->encdata_buffer = reallocated_buffer; - backend->encdata_length = reallocated_length; - } - } + result = ensure_encoding_size(data, &backend->encdata, 0); + if(result) + return result; for(;;) { if(doread) { /* read encrypted handshake data from socket */ result = Curl_conn_cf_recv(cf->next, data, - (char *)(backend->encdata_buffer + - backend->encdata_offset), - backend->encdata_length - - backend->encdata_offset, + (char *)(backend->encdata.buffer + + backend->encdata.offset), + backend->encdata.length - + backend->encdata.offset, &nread); if(result == CURLE_AGAIN) { - if(!backend->encdata_offset || backend->encdata_is_incomplete) { + if(!backend->encdata.offset || backend->encdata_is_incomplete) { connssl->io_need = CURL_SSL_IO_NEED_RECV; DEBUGF(infof(data, "schannel: failed to receive handshake, " "need more data")); @@ -1215,7 +1298,7 @@ } else { /* increase encrypted data buffer offset */ - backend->encdata_offset += nread; + backend->encdata.offset += nread; backend->encdata_is_incomplete = FALSE; SCH_DEV(infof(data, "schannel: encrypted data got %zu", nread)); } @@ -1223,12 +1306,12 @@ SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); /* setup input buffers */ InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, - curlx_malloc(backend->encdata_offset), - curlx_uztoul(backend->encdata_offset)); + curlx_malloc(backend->encdata.offset), + curlx_uztoul(backend->encdata.offset)); InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&inbuf_desc, inbuf, 2); @@ -1244,15 +1327,15 @@ } /* copy received handshake data into input buffer */ - memcpy(inbuf[0].pvBuffer, backend->encdata_buffer, - backend->encdata_offset); + memcpy(inbuf[0].pvBuffer, backend->encdata.buffer, + backend->encdata.offset); - /* The socket must be writeable (or a poll error occurred) before we call + /* The socket must be writable (or a poll error occurred) before we call InitializeSecurityContext to continue processing the received TLS records. This is because that function is not idempotent and we do not support partial save/resume sending replies of handshake tokens. */ if(!SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), 0)) { - SCH_DEV(infof(data, "schannel: handshake waiting for writeable socket")); + SCH_DEV(infof(data, "schannel: handshake waiting for writable socket")); connssl->io_need = CURL_SSL_IO_NEED_SEND; curlx_free(inbuf[0].pvBuffer); return CURLE_OK; @@ -1265,7 +1348,7 @@ &outbuf_desc, &backend->ret_flags, NULL); /* free buffer for received handshake data */ - Curl_safefree(inbuf[0].pvBuffer); + curlx_safefree(inbuf[0].pvBuffer); /* check if the handshake was incomplete */ switch(sspi_status) { @@ -1283,6 +1366,7 @@ for(i = 0; i < 3; i++) { /* search for handshake tokens that need to be send */ if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { + size_t written = 0; DEBUGF(infof(data, "schannel: sending next handshake data: " "sending %lu bytes.", outbuf[i].cbBuffer)); @@ -1310,7 +1394,7 @@ case SEC_I_INCOMPLETE_CREDENTIALS: if(!(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { /* If the server has requested a client certificate, attempt to - continue the handshake without one. This will allow connections to + continue the handshake without one. This allows connections to servers which request a client certificate but do not require it. */ backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; @@ -1331,22 +1415,21 @@ inbuf[1].cbBuffer)); /* There are two cases where we could be getting extra data here: - 1) If we are renegotiating a connection and the handshake is already - complete (from the server perspective), it can encrypted app data - (not handshake data) in an extra buffer at this point. - 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a - connection and this extra data is part of the handshake. - We should process the data immediately; waiting for the socket to - be ready may fail since the server is done sending handshake data. + 1. If we are renegotiating a connection and the handshake is already + complete (from the server perspective), it can encrypted app data + (not handshake data) in an extra buffer at this point. + 2. (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a + connection and this extra data is part of the handshake. + We should process the data immediately; waiting for the socket to + be ready may fail since the server is done sending handshake data. */ /* check if the remaining data is less than the total amount and therefore begins after the already processed data */ - if(backend->encdata_offset > inbuf[1].cbBuffer) { - memmove(backend->encdata_buffer, - (backend->encdata_buffer + backend->encdata_offset) - - inbuf[1].cbBuffer, - inbuf[1].cbBuffer); - backend->encdata_offset = inbuf[1].cbBuffer; + if(backend->encdata.offset > inbuf[1].cbBuffer) { + memmove(backend->encdata.buffer, + (backend->encdata.buffer + backend->encdata.offset) - + inbuf[1].cbBuffer, inbuf[1].cbBuffer); + backend->encdata.offset = inbuf[1].cbBuffer; if(sspi_status == SEC_I_CONTINUE_NEEDED) { doread = FALSE; continue; @@ -1354,7 +1437,7 @@ } } else { - backend->encdata_offset = 0; + backend->encdata.offset = 0; } break; } @@ -1392,7 +1475,7 @@ } /* Verify the hostname manually when certificate verification is disabled, - because in that case Schannel will not verify it. */ + because in that case Schannel does not verify it. */ if(!conn_config->verifypeer && conn_config->verifyhost) return Curl_verify_host(cf, data); @@ -1485,7 +1568,7 @@ CertCloseStore(cred->client_cert_store, 0); cred->client_cert_store = NULL; } - Curl_safefree(cred); + curlx_safefree(cred); } } } @@ -1531,8 +1614,8 @@ if(backend->use_alpn) { sspi_status = Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_APPLICATION_PROTOCOL, - &alpn_result); + SECPKG_ATTR_APPLICATION_PROTOCOL, + &alpn_result); if(sspi_status != SEC_E_OK) { failf(data, "schannel: failed to retrieve ALPN result"); @@ -1577,8 +1660,8 @@ int certs_count = 0; sspi_status = Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &ccert_context); + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &ccert_context); if((sspi_status != SEC_E_OK) || !ccert_context) { failf(data, "schannel: failed to retrieve remote cert context"); @@ -1587,7 +1670,7 @@ traverse_cert_store(ccert_context, cert_counter_callback, &certs_count); if(certs_count > MAX_ALLOWED_CERT_AMOUNT) { - failf(data, "%d certificates is more than allowed (%u)", + failf(data, "%d certificates is more than allowed (%d)", certs_count, MAX_ALLOWED_CERT_AMOUNT); CertFreeCertificateContext(ccert_context); return CURLE_SSL_CONNECT_ERROR; @@ -1674,16 +1757,22 @@ SCH_RENEG_CALLER_IS_SEND }; -#define MAX_RENEG_BLOCK_TIME (7 * 1000) /* 7 seconds in milliseconds */ +/* The maximum time we allow for Schannel renegotiation which may in some + rare cases block either due to libcurl (waiting on the socket) or Windows + (waiting on an interactive security prompt). Note Schannel "renegotiation" + is not necessarily literal TLS renegotiation, but means DecryptMessage + returned SEC_I_RENEGOTIATE which means at least the security context needs + to be re-established. */ +#define MAX_RENEG_BLOCK_TIME (60 * 1000) /* 60 seconds in milliseconds */ /* This function renegotiates the connection due to a server request received by schannel_recv. This function returns CURLE_AGAIN if the renegotiation is incomplete. In that case, we remain in the renegotiation (connecting) stage and future calls to schannel_recv and schannel_send must call this function first to complete the renegotiation. */ -static CURLcode -schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, - enum schannel_renegotiate_caller_t caller) +static CURLcode schannel_recv_renegotiate( + struct Curl_cfilter *cf, struct Curl_easy *data, + enum schannel_renegotiate_caller_t caller) { CURLcode result; curl_socket_t sockfd; @@ -1762,11 +1851,11 @@ * occur if the user is waiting on the socket only in one direction. * * For example, if the user has called recv then they may not be waiting - * for a writeable socket and vice versa, so we block to avoid that. + * for a writable socket and vice versa, so we block to avoid that. * * In practice a wait is unlikely to occur. For caller recv if handshake - * data needs to be sent then we block for a writeable socket that should - * be writeable immediately except for OS resource constraints. For caller + * data needs to be sent then we block for a writable socket that should + * be writable immediately except for OS resource constraints. For caller * send if handshake data needs to be received then we block for a readable * socket, which could take some time, but it is more likely the user has * called recv since they had called it prior (only recv can start @@ -1801,7 +1890,7 @@ remaining = MAX_RENEG_BLOCK_TIME - elapsed; if(blocking) { - timeout_ms = Curl_timeleft_ms(data, FALSE); + timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; @@ -1822,7 +1911,7 @@ SCH_DEV(infof(data, "schannel: renegotiation wait until socket is" "%s%s for up to %" FMT_TIMEDIFF_T " ms", ((readfd != CURL_SOCKET_BAD) ? " readable" : ""), - ((writefd != CURL_SOCKET_BAD) ? " writeable" : ""), + ((writefd != CURL_SOCKET_BAD) ? " writable" : ""), timeout_ms)); what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, timeout_ms); @@ -1945,7 +2034,7 @@ sent. The unwritten encrypted bytes would be the first bytes to send on the next invocation. Here's the catch with this - if we tell the client that all the - bytes have been sent, will the client call this method again to + bytes have been sent, does the client call this method again to send the buffered data? Looking at who calls this function, it seems the answer is NO. */ @@ -1954,11 +2043,11 @@ while(len > *pnwritten) { size_t this_write = 0; int what; - timediff_t timeout_ms = Curl_timeleft_ms(data, FALSE); + timediff_t timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { /* we already got the timeout */ - failf(data, "schannel: timed out sending data " - "(bytes sent: %zu)", *pnwritten); + failf(data, "schannel: timed out sending data (bytes sent: %zu)", + *pnwritten); result = CURLE_OPERATION_TIMEDOUT; break; } @@ -1972,8 +2061,8 @@ break; } else if(what == 0) { - failf(data, "schannel: timed out sending data " - "(bytes sent: %zu)", *pnwritten); + failf(data, "schannel: timed out sending data (bytes sent: %zu)", + *pnwritten); result = CURLE_OPERATION_TIMEDOUT; break; } @@ -1999,7 +2088,7 @@ result = CURLE_SEND_ERROR; } - Curl_safefree(ptr); + curlx_safefree(ptr); if(len == *pnwritten) /* Encrypted message including header, data and trailer entirely sent. @@ -2015,14 +2104,9 @@ size_t size = 0; size_t nread = 0; struct ssl_connect_data *connssl = cf->ctx; - unsigned char *reallocated_buffer; - size_t reallocated_length; SecBuffer inbuf[4]; SecBufferDesc inbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; - /* we want the length of the encrypted buffer to be at least large enough - that it can hold all the bytes requested and some TLS record overhead. */ - size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; @@ -2054,7 +2138,7 @@ SCH_DEV(infof(data, "schannel: client wants to read %zu bytes", len)); - if(len && len <= backend->decdata_offset) { + if(len && len <= backend->decdata.offset) { SCH_DEV(infof(data, "schannel: enough decrypted data is already available")); goto cleanup; @@ -2074,68 +2158,56 @@ decrypt all encrypted cached data) so handle !len later in cleanup. */ else if(len && !backend->recv_connection_closed) { - /* increase enc buffer in order to fit the requested amount of data */ - size = backend->encdata_length - backend->encdata_offset; - if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || - backend->encdata_length < min_encdata_length) { - reallocated_length = backend->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(reallocated_length < min_encdata_length) { - reallocated_length = min_encdata_length; - } - reallocated_buffer = curlx_realloc(backend->encdata_buffer, - reallocated_length); - if(!reallocated_buffer) { - result = CURLE_OUT_OF_MEMORY; - failf(data, "schannel: unable to re-allocate memory"); - goto cleanup; - } + /* the encrypted buffer must be large enough to hold all the bytes + requested and some TLS record overhead. 'len' is a buffer size, so this + integer math cannot overflow. */ + const size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; - backend->encdata_buffer = reallocated_buffer; - backend->encdata_length = reallocated_length; - size = backend->encdata_length - backend->encdata_offset; - SCH_DEV(infof(data, "schannel: encdata_buffer resized %zu", - backend->encdata_length)); - } + /* make sure encrypt buffer fits the requested amount of data */ + result = ensure_encoding_size(data, &backend->encdata, min_encdata_length); + if(result) + goto cleanup; SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); /* read encrypted data from socket */ result = Curl_conn_cf_recv(cf->next, data, - (char *)(backend->encdata_buffer + - backend->encdata_offset), - size, &nread); + (char *)(backend->encdata.buffer + + backend->encdata.offset), + backend->encdata.length - + backend->encdata.offset, + &nread); if(result) { if(result == CURLE_AGAIN) SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN")); - else if(result == CURLE_RECV_ERROR) - infof(data, "schannel: recv returned CURLE_RECV_ERROR"); - else + else { infof(data, "schannel: recv returned error %d", result); + backend->recv_unrecoverable_err = result; + } } else if(nread == 0) { backend->recv_connection_closed = TRUE; DEBUGF(infof(data, "schannel: server closed the connection")); } else { - backend->encdata_offset += nread; + backend->encdata.offset += nread; backend->encdata_is_incomplete = FALSE; SCH_DEV(infof(data, "schannel: encrypted data got %zu", nread)); } } SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); /* decrypt loop */ - while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK && - (!len || backend->decdata_offset < len || + while(backend->encdata.offset > 0 && sspi_status == SEC_E_OK && + (!len || backend->decdata.offset < len || backend->recv_connection_closed)) { /* prepare data buffer for DecryptMessage call */ - InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata_buffer, - curlx_uztoul(backend->encdata_offset)); + InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata.buffer, + curlx_uztoul(backend->encdata.offset)); /* we need 3 more empty input buffers for possible output */ InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); @@ -2158,40 +2230,24 @@ SCH_DEV(infof(data, "schannel: decrypted data length: %lu", inbuf[1].cbBuffer)); - /* increase buffer in order to fit the received amount of data */ - size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? - inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(backend->decdata_length - backend->decdata_offset < size || - backend->decdata_length < len) { - /* increase internal decrypted data buffer */ - reallocated_length = backend->decdata_offset + size; - /* make sure that the requested amount of data fits */ - if(reallocated_length < len) { - reallocated_length = len; - } - reallocated_buffer = curlx_realloc(backend->decdata_buffer, - reallocated_length); - if(!reallocated_buffer) { - result = CURLE_OUT_OF_MEMORY; - failf(data, "schannel: unable to re-allocate memory"); - goto cleanup; - } - backend->decdata_buffer = reallocated_buffer; - backend->decdata_length = reallocated_length; - } + /* ensure the decode buffer fits the received amount of data */ + result = ensure_decoding_size(data, &backend->decdata, + inbuf[1].cbBuffer, len); + if(result) + goto cleanup; /* copy decrypted data to internal buffer */ size = inbuf[1].cbBuffer; if(size) { - memcpy(backend->decdata_buffer + backend->decdata_offset, + memcpy(backend->decdata.buffer + backend->decdata.offset, inbuf[1].pvBuffer, size); - backend->decdata_offset += size; + backend->decdata.offset += size; } SCH_DEV(infof(data, "schannel: decrypted data added: %zu", size)); SCH_DEV(infof(data, "schannel: decrypted cached: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + backend->decdata.offset, backend->decdata.length)); } /* check for remaining encrypted data */ @@ -2202,23 +2258,22 @@ /* check if the remaining data is less than the total amount * and therefore begins after the already processed data */ - if(backend->encdata_offset > inbuf[3].cbBuffer) { + if(backend->encdata.offset > inbuf[3].cbBuffer) { /* move remaining encrypted data forward to the beginning of buffer */ - memmove(backend->encdata_buffer, - (backend->encdata_buffer + backend->encdata_offset) - - inbuf[3].cbBuffer, - inbuf[3].cbBuffer); - backend->encdata_offset = inbuf[3].cbBuffer; + memmove(backend->encdata.buffer, + (backend->encdata.buffer + backend->encdata.offset) - + inbuf[3].cbBuffer, inbuf[3].cbBuffer); + backend->encdata.offset = inbuf[3].cbBuffer; } SCH_DEV(infof(data, "schannel: encrypted cached: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); } else { /* reset encrypted buffer offset, because there is no data remaining */ - backend->encdata_offset = 0; + backend->encdata.offset = 0; } /* check if server wants to renegotiate the connection context */ @@ -2245,7 +2300,7 @@ backend->recv_sspi_close_notify = TRUE; if(!backend->recv_connection_closed) backend->recv_connection_closed = TRUE; - /* We received the close notify just fine, any error we got + /* We received the close notify fine, any error we got * from the lower filters afterwards (e.g. the socket), is not * an error on the TLS data stream. That one ended here. */ if(result == CURLE_RECV_ERROR) @@ -2263,21 +2318,19 @@ goto cleanup; } else { -#ifndef CURL_DISABLE_VERBOSE_STRINGS char buffer[STRERROR_LEN]; failf(data, "schannel: failed to read data from server: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); -#endif result = CURLE_RECV_ERROR; goto cleanup; } } SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); SCH_DEV(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + backend->decdata.offset, backend->decdata.length)); cleanup: /* Warning- there is no guarantee the encdata state is valid at this point */ @@ -2289,7 +2342,7 @@ to a truncation attack however there is some browser precedent for ignoring the close_notify for compatibility reasons. */ - if(len && !backend->decdata_offset && backend->recv_connection_closed && + if(len && !backend->decdata.offset && backend->recv_connection_closed && !backend->recv_sspi_close_notify) { result = CURLE_RECV_ERROR; failf(data, "schannel: server closed abruptly (missing close_notify)"); @@ -2299,16 +2352,16 @@ if(result && result != CURLE_AGAIN) backend->recv_unrecoverable_err = result; - size = len < backend->decdata_offset ? len : backend->decdata_offset; + size = len < backend->decdata.offset ? len : backend->decdata.offset; if(size) { - memcpy(buf, backend->decdata_buffer, size); - memmove(backend->decdata_buffer, backend->decdata_buffer + size, - backend->decdata_offset - size); - backend->decdata_offset -= size; + memcpy(buf, backend->decdata.buffer, size); + memmove(backend->decdata.buffer, backend->decdata.buffer + size, + backend->decdata.offset - size); + backend->decdata.offset -= size; SCH_DEV(infof(data, "schannel: decrypted data returned %zu", size)); SCH_DEV(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + backend->decdata.offset, backend->decdata.length)); *pnread = size; return CURLE_OK; } @@ -2337,8 +2390,8 @@ DEBUGASSERT(backend); if(backend->ctxt) /* SSL/TLS is in use */ - return backend->decdata_offset > 0 || - (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) || + return backend->decdata.offset > 0 || + (backend->encdata.offset > 0 && !backend->encdata_is_incomplete) || backend->recv_connection_closed || backend->recv_sspi_close_notify || backend->recv_unrecoverable_err; @@ -2395,7 +2448,7 @@ InitSecBufferDesc(&BuffDesc, &Buffer, 1); sspi_status = Curl_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle, - &BuffDesc); + &BuffDesc); if(sspi_status != SEC_E_OK) { char buffer[STRERROR_LEN]; @@ -2503,7 +2556,7 @@ if(backend->ctxt) { DEBUGF(infof(data, "schannel: clear security context handle")); Curl_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); - Curl_safefree(backend->ctxt); + curlx_safefree(backend->ctxt); } /* free SSPI Schannel API credential handle */ @@ -2515,18 +2568,18 @@ } /* free internal buffer for received encrypted data */ - if(backend->encdata_buffer) { - Curl_safefree(backend->encdata_buffer); - backend->encdata_length = 0; - backend->encdata_offset = 0; + if(backend->encdata.buffer) { + curlx_safefree(backend->encdata.buffer); + backend->encdata.length = 0; + backend->encdata.offset = 0; backend->encdata_is_incomplete = FALSE; } /* free internal buffer for received decrypted data */ - if(backend->decdata_buffer) { - Curl_safefree(backend->decdata_buffer); - backend->decdata_length = 0; - backend->decdata_offset = 0; + if(backend->decdata.buffer) { + curlx_safefree(backend->decdata.buffer); + backend->decdata.length = 0; + backend->decdata.offset = 0; } } @@ -2580,74 +2633,6 @@ return Curl_win32_random(entropy, length); } -static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *pinnedpubkey) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct schannel_ssl_backend_data *backend = - (struct schannel_ssl_backend_data *)connssl->backend; - CERT_CONTEXT *pCertContextServer = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - DEBUGASSERT(backend); - - /* if a path was not specified, do not pin */ - if(!pinnedpubkey) - return CURLE_OK; - - do { - SECURITY_STATUS sspi_status; - const char *x509_der; - DWORD x509_der_len; - struct Curl_X509certificate x509_parsed; - struct Curl_asn1Element *pubkey; - - sspi_status = - Curl_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))); - break; /* failed */ - } - - if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (pCertContextServer->cbCertEncoded > 0))) - break; - - x509_der = (const char *)pCertContextServer->pbCertEncoded; - x509_der_len = pCertContextServer->cbCertEncoded; - memset(&x509_parsed, 0, sizeof(x509_parsed)); - if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) - break; - - pubkey = &x509_parsed.subjectPublicKeyInfo; - if(!pubkey->header || pubkey->end <= pubkey->header) { - failf(data, "SSL: failed retrieving public key from server certificate"); - break; - } - - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, - (const unsigned char *)pubkey->header, - (size_t)(pubkey->end - pubkey->header)); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - } - } while(0); - - if(pCertContextServer) - CertFreeCertificateContext(pCertContextServer); - - return result; -} - static void schannel_checksum(const unsigned char *input, size_t inputlen, unsigned char *checksum, @@ -2657,9 +2642,6 @@ { HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; - DWORD cbHashSize = 0; - DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); - DWORD dwChecksumLen = (DWORD)checksumlen; /* since this can fail in multiple ways, zero memory first so we never * return old data @@ -2671,6 +2653,10 @@ return; /* failed */ do { + DWORD cbHashSize = 0; + DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); + DWORD dwChecksumLen = (DWORD)checksumlen; + if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) break; /* failed */ @@ -2726,7 +2712,6 @@ struct schannel_cert_share *share; const struct ssl_general_config *cfg = &data->set.general_ssl; timediff_t timeout_ms; - timediff_t elapsed_ms; unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH]; DEBUGASSERT(multi); @@ -2752,7 +2737,8 @@ negative timeout means retain forever. */ timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; if(timeout_ms >= 0) { - elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &share->time); + timediff_t elapsed_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &share->time); if(elapsed_ms >= timeout_ms) { return NULL; }
diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h index f66bcf2..fe49e9a 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.h +++ b/Utilities/cmcurl/lib/vtls/schannel.h
@@ -24,55 +24,15 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4201) -#endif -#include <subauth.h> -#ifdef _MSC_VER -#pragma warning(pop) -#endif -/* Wincrypt must be included before anything that could include OpenSSL. */ -#ifdef USE_WIN32_CRYPTO -#include <wincrypt.h> -/* Undefine wincrypt conflicting symbols for BoringSSL. */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#endif - -#include <schnlsp.h> #include <schannel.h> -#include "../curl_sspi.h" -#include "../cfilters.h" -#include "../urldata.h" - -/* <wincrypt.h> has been included via the above <schnlsp.h>. - * Or in case of ldap.c, it was included via <winldap.h>. - * And since <wincrypt.h> has this: - * #define X509_NAME ((LPCSTR)7) - * - * And in BoringSSL's <openssl/base.h> there is: - * typedef struct X509_name_st X509_NAME; - * etc. - * - * this will cause all kinds of C-preprocessing paste errors in - * BoringSSL's <openssl/x509.h>: So just undefine those defines here - * (and only here). - */ -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) -#undef X509_NAME -#undef X509_CERT_PAIR -#undef X509_EXTENSIONS -#endif +#include "curl_sspi.h" +#include "cfilters.h" +#include "urldata.h" extern const struct Curl_ssl Curl_ssl_schannel;
diff --git a/Utilities/cmcurl/lib/vtls/schannel_int.h b/Utilities/cmcurl/lib/vtls/schannel_int.h index 0146bf8..496635f 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_int.h +++ b/Utilities/cmcurl/lib/vtls/schannel_int.h
@@ -24,12 +24,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL -#include "vtls.h" -#include "../curl_sha256.h" +#include "vtls/vtls.h" +#include "curl_sha256.h" #if defined(_MSC_VER) && (_MSC_VER < 1700) /* Workaround for warning: @@ -91,11 +91,6 @@ PTLS_PARAMETERS pTlsParameters; } SCH_CREDENTIALS, * PSCH_CREDENTIALS; -#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 -#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16 -#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16 -#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16 - #endif /* SCH_CREDENTIALS_VERSION */ struct Curl_schannel_cred { @@ -109,17 +104,19 @@ CtxtHandle ctxt_handle; }; +/* handle encoding/decoding buffers */ +struct sbuffer { + size_t length; + size_t offset; + unsigned char *buffer; +}; + struct schannel_ssl_backend_data { + struct sbuffer encdata; + struct sbuffer decdata; struct Curl_schannel_cred *cred; struct Curl_schannel_ctxt *ctxt; SecPkgContext_StreamSizes stream_sizes; - size_t encdata_length, decdata_length; - size_t encdata_offset, decdata_offset; - unsigned char *encdata_buffer, *decdata_buffer; - /* encdata_is_incomplete: if encdata contains only a partial record that - cannot be decrypted without another recv() (that is, status is - SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds - more bytes into encdata then set this back to false. */ unsigned long req_flags, ret_flags; CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ struct schannel_renegotiate_state { @@ -133,6 +130,10 @@ BIT(use_alpn); /* true if ALPN is used for this connection */ BIT(use_manual_cred_validation); /* true if manual cred validation is used */ BIT(sent_shutdown); + /* encdata_is_incomplete: if encdata contains only a partial record that + cannot be decrypted without another recv() (that is, status is + SEC_E_INCOMPLETE_MESSAGE) then set this true. after recv() adds more + bytes into encdata then set this back to false. */ BIT(encdata_is_incomplete); };
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index 5129b8b..9be6fe3 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -27,30 +27,31 @@ * Source file for Schannel-specific certificate verification. This code should * only be invoked by code in schannel.c. */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL + #ifndef USE_WINDOWS_SSPI -#error "cannot compile SCHANNEL support without SSPI." +#error "cannot compile Schannel support without SSPI." #endif -#include "schannel.h" -#include "schannel_int.h" +#include "vtls/schannel.h" +#include "vtls/schannel_int.h" -#include "../curlx/fopen.h" -#include "../curlx/inet_pton.h" -#include "vtls.h" -#include "vtls_int.h" -#include "../curl_trc.h" -#include "../strerror.h" -#include "../curlx/winapi.h" -#include "../curlx/multibyte.h" -#include "hostcheck.h" -#include "../curlx/version_win32.h" +#include "vtls/hostcheck.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "curl_trc.h" +#include "strerror.h" +#include "curlx/fopen.h" +#include "curlx/inet_pton.h" +#include "curlx/multibyte.h" +#include "curlx/version_win32.h" +#include "curlx/winapi.h" #define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend) -#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ +#define MAX_CAFILE_SIZE (1024 * 1024) /* 1 MiB */ #define BEGIN_CERT "-----BEGIN CERTIFICATE-----" #define END_CERT "\n-----END CERTIFICATE-----" @@ -261,7 +262,7 @@ /* * Read the CA file completely into memory before parsing it. This - * optimizes for the common case where the CA file will be relatively + * optimizes for the common case where the CA file is relatively * small ( < 1 MiB ). */ ca_file_handle = curlx_CreateFile(ca_file, @@ -289,7 +290,7 @@ } if(file_size.QuadPart > MAX_CAFILE_SIZE) { - failf(data, "schannel: CA file exceeds max size of %u bytes", + failf(data, "schannel: CA file exceeds max size of %d bytes", MAX_CAFILE_SIZE); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; @@ -335,7 +336,7 @@ if(ca_file_handle != INVALID_HANDLE_VALUE) { CloseHandle(ca_file_handle); } - Curl_safefree(ca_file_buffer); + curlx_safefree(ca_file_buffer); return result; } @@ -365,7 +366,7 @@ #ifndef CERT_NAME_SEARCH_ALL_NAMES_FLAG #define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 #endif - /* CertGetNameString will provide the 8-bit character string without + /* CertGetNameString provides the 8-bit character string without * any decoding */ DWORD name_flags = CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG; @@ -438,24 +439,22 @@ static bool get_num_host_info(struct num_ip_data *ip_blob, LPCSTR hostname) { struct in_addr ia; - struct in6_addr ia6; - bool result = FALSE; - int res = curlx_inet_pton(AF_INET, hostname, &ia); if(res) { ip_blob->size = sizeof(struct in_addr); memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr)); - result = TRUE; + return TRUE; } else { + struct in6_addr ia6; res = curlx_inet_pton(AF_INET6, hostname, &ia6); if(res) { ip_blob->size = sizeof(struct in6_addr); memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr)); - result = TRUE; + return TRUE; } } - return result; + return FALSE; } static bool get_alt_name_info(struct Curl_easy *data, @@ -463,20 +462,19 @@ PCERT_ALT_NAME_INFO *alt_name_info, LPDWORD alt_name_info_size) { - bool result = FALSE; PCERT_INFO cert_info = NULL; PCERT_EXTENSION extension = NULL; CRYPT_DECODE_PARA decode_para = { sizeof(CRYPT_DECODE_PARA), NULL, NULL }; if(!ctx) { failf(data, "schannel: Null certificate context."); - return result; + return FALSE; } cert_info = ctx->pCertInfo; if(!cert_info) { failf(data, "schannel: Null certificate info."); - return result; + return FALSE; } extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, @@ -484,7 +482,7 @@ cert_info->rgExtension); if(!extension) { failf(data, "schannel: CertFindExtension() returned no extension."); - return result; + return FALSE; } if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, @@ -496,11 +494,10 @@ alt_name_info, alt_name_info_size)) { failf(data, "schannel: CryptDecodeObjectEx() returned no alternate name " - "information."); - return result; + "information."); + return FALSE; } - result = TRUE; - return result; + return TRUE; } /* Verify the server's hostname */ @@ -572,7 +569,7 @@ goto cleanup; } - /* CertGetNameString guarantees that the returned name will not contain + /* CertGetNameString guarantees that the returned name does not contain * embedded null bytes. This appears to be undocumented behavior. */ cert_hostname_buff = (LPTSTR)curlx_malloc(len * sizeof(TCHAR)); @@ -650,7 +647,7 @@ cleanup: LocalFree(alt_name_info); - Curl_safefree(cert_hostname_buff); + curlx_safefree(cert_hostname_buff); if(pCertContextServer) CertFreeCertificateContext(pCertContextServer); @@ -763,7 +760,7 @@ else engine_config.cbSize = sizeof(struct cert_chain_engine_config_win7); - /* CertCreateCertificateChainEngine will check the expected size of the + /* CertCreateCertificateChainEngine checks the expected size of the * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size * does not match the expected size. When this occurs, it indicates that * CAINFO is not supported on the version of Windows in use.
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index be0c032..891facf 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -38,40 +38,41 @@ https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif -#include "../urldata.h" -#include "../cfilters.h" +#include "urldata.h" +#include "cfilters.h" -#include "vtls.h" /* generic SSL protos etc */ -#include "vtls_int.h" -#include "vtls_scache.h" +#include "vtls/vtls.h" /* generic SSL protos etc */ +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/keylog.h" -#include "openssl.h" /* OpenSSL versions */ -#include "gtls.h" /* GnuTLS versions */ -#include "wolfssl.h" /* wolfSSL versions */ -#include "schannel.h" /* Schannel SSPI version */ -#include "mbedtls.h" /* mbedTLS versions */ -#include "rustls.h" /* Rustls versions */ +#include "vtls/openssl.h" /* OpenSSL versions */ +#include "vtls/gtls.h" /* GnuTLS versions */ +#include "vtls/wolfssl.h" /* wolfSSL versions */ +#include "vtls/schannel.h" /* Schannel SSPI version */ +#include "vtls/mbedtls.h" /* mbedTLS versions */ +#include "vtls/rustls.h" /* Rustls versions */ -#include "../slist.h" -#include "../curl_trc.h" -#include "../strcase.h" -#include "../url.h" -#include "../progress.h" -#include "../curlx/fopen.h" -#include "../curl_sha256.h" -#include "../curlx/base64.h" -#include "../curlx/inet_pton.h" -#include "../connect.h" -#include "../select.h" -#include "../setopt.h" -#include "../strdup.h" -#include "../curlx/strcopy.h" +#include "slist.h" +#include "curl_trc.h" +#include "strcase.h" +#include "url.h" +#include "progress.h" +#include "curlx/fopen.h" +#include "curl_sha256.h" +#include "curlx/base64.h" +#include "curlx/inet_pton.h" +#include "connect.h" +#include "select.h" +#include "setopt.h" +#include "curlx/strdup.h" +#include "curlx/strcopy.h" #ifdef USE_APPLE_SECTRUST #include <Security/Security.h> @@ -133,7 +134,9 @@ static const struct alpn_spec ALPN_SPEC_H11 = { { ALPN_HTTP_1_1 }, 1 }; -#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ +static const struct alpn_spec ALPN_SPEC_H10_H11 = { + { ALPN_HTTP_1_0, ALPN_HTTP_1_1 }, 2 +}; #ifdef USE_HTTP2 static const struct alpn_spec ALPN_SPEC_H2 = { { ALPN_H2 }, 1 @@ -144,15 +147,20 @@ static const struct alpn_spec ALPN_SPEC_H11_H2 = { { ALPN_HTTP_1_1, ALPN_H2 }, 2 }; -#endif +#endif /* USE_HTTP2 */ -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_PROXY) static const struct alpn_spec *alpn_get_spec(http_majors wanted, http_majors preferred, + bool only_http_10, bool use_alpn) { if(!use_alpn) return NULL; + /* If HTTP/1.0 is the wanted protocol then use ALPN http/1.0 and http/1.1. + This is for compatibility reasons since some HTTP/1.0 servers with old + ALPN implementations understand ALPN http/1.1 but not http/1.0. */ + if(only_http_10 && (wanted & CURL_HTTP_V1x)) + return &ALPN_SPEC_H10_H11; #ifdef USE_HTTP2 if(wanted & CURL_HTTP_V2x) { if(wanted & CURL_HTTP_V1x) @@ -164,8 +172,6 @@ (void)wanted; (void)preferred; #endif - /* Use the ALPN protocol "http/1.1" for HTTP/1.x. - Avoid "http/1.0" because some servers do not support it. */ return &ALPN_SPEC_H11; } #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ @@ -267,22 +273,22 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc) { - Curl_safefree(sslc->CApath); - Curl_safefree(sslc->CAfile); - Curl_safefree(sslc->issuercert); - Curl_safefree(sslc->clientcert); - Curl_safefree(sslc->cipher_list); - Curl_safefree(sslc->cipher_list13); - Curl_safefree(sslc->pinned_key); - Curl_safefree(sslc->cert_blob); - Curl_safefree(sslc->ca_info_blob); - Curl_safefree(sslc->issuercert_blob); - Curl_safefree(sslc->curves); - Curl_safefree(sslc->signature_algorithms); - Curl_safefree(sslc->CRLfile); + curlx_safefree(sslc->CApath); + curlx_safefree(sslc->CAfile); + curlx_safefree(sslc->issuercert); + curlx_safefree(sslc->clientcert); + curlx_safefree(sslc->cipher_list); + curlx_safefree(sslc->cipher_list13); + curlx_safefree(sslc->pinned_key); + curlx_safefree(sslc->cert_blob); + curlx_safefree(sslc->ca_info_blob); + curlx_safefree(sslc->issuercert_blob); + curlx_safefree(sslc->curves); + curlx_safefree(sslc->signature_algorithms); + curlx_safefree(sslc->CRLfile); #ifdef USE_TLS_SRP - Curl_safefree(sslc->username); - Curl_safefree(sslc->password); + curlx_safefree(sslc->username); + curlx_safefree(sslc->password); #endif } @@ -295,7 +301,7 @@ #endif if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#ifdef USE_APPLE_SECTRUST +#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) sslc->native_ca_store = TRUE; #endif @@ -341,7 +347,7 @@ #ifndef CURL_DISABLE_PROXY sslc = &data->set.proxy_ssl; if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#ifdef USE_APPLE_SECTRUST +#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) sslc->native_ca_store = TRUE; #endif @@ -390,7 +396,7 @@ CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, struct connectdata *conn) { - /* Clone "primary" SSL configurations from the esay handle to + /* Clone "primary" SSL configurations from the easy handle to * the connection. They are used for connection cache matching and * probably outlive the easy handle */ if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) @@ -515,7 +521,7 @@ static void cf_ctx_free(struct ssl_connect_data *ctx) { if(ctx) { - Curl_safefree(ctx->negotiated.alpn); + curlx_safefree(ctx->negotiated.alpn); Curl_bufq_free(&ctx->earlydata); curlx_free(ctx->backend); curlx_free(ctx); @@ -672,12 +678,11 @@ /* get length bytes of randomness */ CURLcode Curl_ssl_random(struct Curl_easy *data, - unsigned char *entropy, - size_t length) + unsigned char *buffer, size_t length) { DEBUGASSERT(length == sizeof(int)); if(Curl_ssl->random) - return Curl_ssl->random(data, entropy, length); + return Curl_ssl->random(data, buffer, length); else return CURLE_NOT_BUILT_IN; } @@ -689,7 +694,7 @@ static CURLcode pubkey_pem_to_der(const char *pem, unsigned char **der, size_t *der_len) { - char *begin_pos, *end_pos; + const char *begin_pos, *end_pos; size_t pem_count, pem_len; CURLcode result; struct dynbuf pbuf; @@ -752,9 +757,6 @@ const unsigned char *pubkey, size_t pubkeylen) { CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif /* if a path was not specified, do not pin */ if(!pinnedpubkey) @@ -786,7 +788,7 @@ encode = curlx_base64_encode(sha256sumdigest, CURL_SHA256_DIGEST_LENGTH, &cert_hash, &cert_hash_len); - Curl_safefree(sha256sumdigest); + curlx_safefree(sha256sumdigest); if(encode) return encode; @@ -801,7 +803,7 @@ pinned_hash_len = end_pos ? (size_t)(end_pos - pinned_hash) : strlen(pinned_hash); - /* compare base64 sha256 digests" */ + /* compare base64 sha256 digests */ if(cert_hash_len == pinned_hash_len && !memcmp(cert_hash, pinned_hash, cert_hash_len)) { DEBUGF(infof(data, "public key hash matches pinned value")); @@ -814,14 +816,14 @@ /* next one or we are at the end */ pinned_hash = end_pos ? (end_pos + 1) : NULL; } - Curl_safefree(cert_hash); + curlx_safefree(cert_hash); } else { long filesize; size_t size, pem_len; CURLcode pem_read; struct dynbuf buf; - char unsigned *pem_ptr = NULL; + unsigned char *pem_ptr = NULL; size_t left; FILE *fp = curlx_fopen(pinnedpubkey, "rb"); if(!fp) @@ -868,7 +870,7 @@ } /* - * Otherwise we will assume it is PEM and try to decode it after placing + * Otherwise we assume it is PEM and try to decode it after placing * null-terminator */ pem_read = pubkey_pem_to_der(curlx_dyn_ptr(&buf), &pem_ptr, &pem_len); @@ -884,7 +886,7 @@ result = CURLE_OK; end: curlx_dyn_free(&buf); - Curl_safefree(pem_ptr); + curlx_safefree(pem_ptr); curlx_fclose(fp); } @@ -1061,7 +1063,7 @@ if(current != selected) { char *p = backends; - char *end = backends + sizeof(backends); + const char *end = backends + sizeof(backends); int i; selected = current; @@ -1178,12 +1180,12 @@ void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { - Curl_safefree(peer->sni); + curlx_safefree(peer->sni); if(peer->dispname != peer->hostname) curlx_free(peer->dispname); peer->dispname = NULL; - Curl_safefree(peer->hostname); - Curl_safefree(peer->scache_key); + curlx_safefree(peer->hostname); + curlx_safefree(peer->scache_key); peer->type = CURL_SSL_PEER_DNS; } @@ -1220,7 +1222,7 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, const char *tls_id, - int transport) + uint8_t transport) { const char *ehostname, *edispname; CURLcode result = CURLE_OUT_OF_MEMORY; @@ -1247,7 +1249,7 @@ { ehostname = cf->conn->host.name; edispname = cf->conn->host.dispname; - peer->port = cf->conn->remote_port; + peer->port = (uint16_t)cf->conn->remote_port; } /* hostname MUST exist and not be empty */ @@ -1366,6 +1368,13 @@ if(connssl->state == ssl_connection_complete) { connssl->handshake_done = *Curl_pgrs_now(data); } + if(Curl_tls_keylog_enabled()) { + infof(data, "SSLKEYLOGFILE set, all TLS secrets are logged to '%s'", + Curl_tls_keylog_file_name()); +#ifdef LIBRESSL_VERSION_NUMBER + infof(data, "Note LibreSSL only supports SSLKEYLOGFILE for TLS <= 1.2"); +#endif + } /* Connection can be deferred when sending early data */ DEBUGASSERT(connssl->state == ssl_connection_complete || connssl->state == ssl_connection_deferred); @@ -1392,7 +1401,7 @@ if(blen > connssl->earlydata_max) blen = connssl->earlydata_max; result = Curl_bufq_write(&connssl->earlydata, buf, blen, &nwritten); - CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd", + CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zu", blen, nwritten); if(result) return result; @@ -1454,16 +1463,16 @@ { struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; - bool result; + bool pending; CF_DATA_SAVE(save, cf, data); if(connssl->ssl_impl->data_pending && connssl->ssl_impl->data_pending(cf, data)) - result = TRUE; + pending = TRUE; else - result = cf->next->cft->has_data_pending(cf->next, data); + pending = cf->next->cft->has_data_pending(cf->next, data); CF_DATA_RESTORE(cf, save); - return result; + return pending; } static CURLcode ssl_cf_send(struct Curl_cfilter *cf, @@ -1683,7 +1692,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = { "SSL-PROXY", - CF_TYPE_SSL|CF_TYPE_PROXY, + CF_TYPE_SSL | CF_TYPE_PROXY, CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, @@ -1719,7 +1728,8 @@ #else ctx = cf_ctx_new(data, alpn_get_spec(data->state.http_neg.wanted, data->state.http_neg.preferred, - conn->bits.tls_enable_alpn)); + (bool)data->state.http_neg.only_10, + (bool)conn->bits.tls_enable_alpn)); #endif if(!ctx) { result = CURLE_OUT_OF_MEMORY; @@ -1770,7 +1780,7 @@ struct ssl_connect_data *ctx; CURLcode result; /* ALPN is default, but if user explicitly disables it, obey */ - bool use_alpn = data->set.ssl_enable_alpn; + bool use_alpn = (bool)data->set.ssl_enable_alpn; http_majors wanted = CURL_HTTP_V1x; (void)conn; @@ -1781,7 +1791,7 @@ } #endif - ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, use_alpn)); + ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, false, use_alpn)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -2042,7 +2052,7 @@ result = CURLE_SSL_CONNECT_ERROR; goto out; } - connssl->negotiated.alpn = Curl_memdup0((const char *)proto, proto_len); + connssl->negotiated.alpn = curlx_memdup0((const char *)proto, proto_len); if(!connssl->negotiated.alpn) return CURLE_OUT_OF_MEMORY; } @@ -2064,4 +2074,34 @@ return result; } +CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data, bool early_data_allowed) +{ + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result = CURLE_OK; + + *do_early_data = FALSE; + + if(!early_data_allowed) { + CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); + } + else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { + CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); + } + else { + infof(data, "SSL session allows %zu bytes of early data, " + "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); + connssl->earlydata_state = ssl_earlydata_await; + connssl->state = ssl_connection_deferred; + result = Curl_alpn_set_negotiated(cf, data, connssl, + (const unsigned char *)scs->alpn, + scs->alpn ? strlen(scs->alpn) : 0); + *do_early_data = !result; + } + return result; +} + #endif /* USE_SSL */
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index 112681a..6db67cf 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" struct connectdata; struct ssl_config_data; @@ -43,11 +43,21 @@ #define SSLSUPP_CA_CACHE (1 << 8) #define SSLSUPP_CIPHER_LIST (1 << 9) /* supports TLS 1.0-1.2 ciphersuites */ #define SSLSUPP_SIGNATURE_ALGORITHMS (1 << 10) /* supports TLS sigalgs */ +#define SSLSUPP_ISSUERCERT (1 << 11) /* supports CURLOPT_ISSUERCERT */ +#define SSLSUPP_SSL_EC_CURVES (1 << 12) /* supports CURLOPT_SSL_EC_CURVES */ +#define SSLSUPP_CRLFILE (1 << 13) /* supports CURLOPT_CRLFILE */ +#define SSLSUPP_ISSUERCERT_BLOB (1 << 14) /* CURLOPT_ISSUERCERT_BLOB */ #ifdef USE_ECH -# include "../curlx/base64.h" -# define ECH_ENABLED(__data__) \ - (__data__->set.tls_ech && !(__data__->set.tls_ech & CURLECH_DISABLE)) +/* CURLECH_ bits for the tls_ech option */ +#define CURLECH_DISABLE (1 << 0) +#define CURLECH_GREASE (1 << 1) +#define CURLECH_ENABLE (1 << 2) +#define CURLECH_HARD (1 << 3) +#define CURLECH_CLA_CFG (1 << 4) + +#define CURLECH_ENABLED(data) \ + ((data)->set.tls_ech && !((data)->set.tls_ech & CURLECH_DISABLE)) #endif /* USE_ECH */ #define ALPN_ACCEPTED "ALPN: server accepted " @@ -86,16 +96,14 @@ char *sni; /* SNI version of hostname or NULL if not usable */ char *scache_key; /* for lookups in session cache */ ssl_peer_type type; /* type of the peer information */ - int port; /* port we are talking to */ - int transport; /* one of TRNSPRT_* defines */ + uint16_t port; /* port we are talking to */ + uint8_t transport; /* one of TRNSPRT_* defines */ }; CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, const curl_ssl_backend ***avail); -#ifndef MAX_PINNED_PUBKEY_SIZE -#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1 MiB */ -#endif +#define MAX_PINNED_PUBKEY_SIZE (1024 * 1024) /* 1 MiB */ curl_sslbackend Curl_ssl_backend(void); @@ -132,7 +140,7 @@ bool proxy); /* Update certain connection SSL config flags after they have - * been changed on the easy handle. Will work for `verifypeer`, + * been changed on the easy handle. Works for `verifypeer`, * `verifyhost` and `verifystatus`. */ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); @@ -142,7 +150,7 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, const char *tls_id, - int transport); + uint8_t transport); /** * Free all allocated data and reset peer information. */ @@ -176,8 +184,8 @@ /* Functions to be used by SSL library adaptation functions */ /* get N random bytes into the buffer */ -CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer, - size_t length); +CURLcode Curl_ssl_random(struct Curl_easy *data, + unsigned char *buffer, size_t length); /* Check pinned public key. */ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, const char *pinnedpubkey, @@ -247,7 +255,7 @@ #else /* if not USE_SSL */ -/* When SSL support is not present, just define away these function calls */ +/* When SSL support is not present, define away these function calls */ #define Curl_ssl_init() 1 #define Curl_ssl_cleanup() Curl_nop_stmt #define Curl_ssl_close_all(x) Curl_nop_stmt @@ -255,7 +263,7 @@ #define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN #define Curl_ssl_engines_list(x) NULL #define Curl_ssl_free_certinfo(x) Curl_nop_stmt -#define Curl_ssl_random(x, y, z) ((void)x, CURLE_NOT_BUILT_IN) +#define Curl_ssl_random(x, y, z) ((void)(x), CURLE_NOT_BUILT_IN) #define Curl_ssl_cert_status_request() FALSE #define Curl_ssl_supports(a, b) FALSE #define Curl_ssl_cfilter_add(a, b, c) CURLE_NOT_BUILT_IN
diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h index ec72b64..6700ee7 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_int.h +++ b/Utilities/cmcurl/lib/vtls/vtls_int.h
@@ -23,19 +23,22 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" - -#include "../cfilters.h" -#include "../select.h" -#include "../urldata.h" -#include "vtls.h" +#include "curl_setup.h" #ifdef USE_SSL +#include "cfilters.h" +#include "select.h" +#include "urldata.h" +#include "vtls/vtls.h" + struct Curl_ssl; struct ssl_connect_data; +struct Curl_ssl_session; /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ +#define ALPN_HTTP_1_0_LENGTH 8 +#define ALPN_HTTP_1_0 "http/1.0" #define ALPN_HTTP_1_1_LENGTH 8 #define ALPN_HTTP_1_1 "http/1.1" #define ALPN_H2_LENGTH 2 @@ -99,12 +102,12 @@ ssl_earlydata_rejected } ssl_earlydata_state; -#define CURL_SSL_IO_NEED_NONE (0) +#define CURL_SSL_IO_NEED_NONE 0 #define CURL_SSL_IO_NEED_RECV (1 << 0) #define CURL_SSL_IO_NEED_SEND (1 << 1) /* Max earlydata payload we want to send */ -#define CURL_SSL_EARLY_MAX (64 * 1024) +#define CURL_SSL_EARLY_MAX (64 * 1024) /* Information in each SSL cfilter context: cf->ctx */ struct ssl_connect_data { @@ -198,6 +201,11 @@ */ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf); +CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data, bool early_data_allowed); #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */
diff --git a/Utilities/cmcurl/lib/vtls/vtls_scache.c b/Utilities/cmcurl/lib/vtls/vtls_scache.c index df04ec7..59fa256 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_scache.c +++ b/Utilities/cmcurl/lib/vtls/vtls_scache.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SSL @@ -29,25 +29,23 @@ #include <sys/types.h> #endif -#include "../urldata.h" -#include "../cfilters.h" +#include "urldata.h" +#include "cfilters.h" -#include "vtls.h" /* generic SSL protos etc */ -#include "vtls_int.h" -#include "vtls_scache.h" -#include "vtls_spack.h" +#include "vtls/vtls.h" /* generic SSL protos etc */ +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/vtls_spack.h" -#include "../strcase.h" -#include "../url.h" -#include "../llist.h" -#include "../curl_share.h" -#include "../curl_trc.h" -#include "../curl_sha256.h" -#include "../rand.h" +#include "strcase.h" +#include "url.h" +#include "llist.h" +#include "curl_share.h" +#include "curl_trc.h" +#include "curl_sha256.h" +#include "rand.h" -static bool cf_ssl_peer_key_is_global(const char *peer_key); - /* a peer+tls-config we cache sessions for */ struct Curl_ssl_scache_peer { char *ssl_peer_key; /* id for peer + relevant TLS configuration */ @@ -60,7 +58,7 @@ unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */ unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */ size_t max_sessions; - long age; /* just a number, the higher the more recent */ + long age; /* a number, the higher the more recent */ BIT(hmac_set); /* if key_salt and key_hmac are present */ BIT(exportable); /* sessions for this peer can be exported */ }; @@ -69,12 +67,241 @@ #define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC) +static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, + const char *name, + const char *path, + bool *is_local) +{ + if(path && path[0]) { + /* We try to add absolute paths, so that the session key can stay valid + * when used in another process with different CWD. When a path does not + * exist, this does not work. Then, we add the path as is. */ +#ifdef _WIN32 + char abspath[_MAX_PATH]; + if(_fullpath(abspath, path, _MAX_PATH)) + return curlx_dyn_addf(buf, ":%s-%s", name, abspath); + *is_local = TRUE; +#elif defined(HAVE_REALPATH) + if(path[0] != '/') { + char *abspath = realpath(path, NULL); + if(abspath) { + CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath); + /* !checksrc! disable BANNEDFUNC 1 */ + free(abspath); /* allocated by libc, free without memdebug */ + return r; + } + *is_local = TRUE; + } +#endif + return curlx_dyn_addf(buf, ":%s-%s", name, path); + } + return CURLE_OK; +} + +static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, + const char *name, + struct curl_blob *blob) +{ + CURLcode r = CURLE_OK; + if(blob && blob->len) { + unsigned char hash[CURL_SHA256_DIGEST_LENGTH]; + size_t i; + + r = curlx_dyn_addf(buf, ":%s-", name); + if(r) + goto out; + r = Curl_sha256it(hash, blob->data, blob->len); + if(r) + goto out; + for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) { + r = curlx_dyn_addf(buf, "%02x", hash[i]); + if(r) + goto out; + } + } +out: + return r; +} + +#define CURL_SSLS_LOCAL_SUFFIX ":L" +#define CURL_SSLS_GLOBAL_SUFFIX ":G" + +static bool cf_ssl_peer_key_is_global(const char *peer_key) +{ + size_t len = peer_key ? strlen(peer_key) : 0; + return (len > 2) && + (peer_key[len - 1] == 'G') && + (peer_key[len - 2] == ':'); +} + +CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, + const struct ssl_peer *peer, + const char *tls_id, + char **ppeer_key) +{ + struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); + struct dynbuf buf; + size_t key_len; + bool is_local = FALSE; + CURLcode r; + + *ppeer_key = NULL; + curlx_dyn_init(&buf, 10 * 1024); + + r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); + if(r) + goto out; + + switch(peer->transport) { + case TRNSPRT_TCP: + break; + case TRNSPRT_UDP: + r = curlx_dyn_add(&buf, ":UDP"); + break; + case TRNSPRT_QUIC: + r = curlx_dyn_add(&buf, ":QUIC"); + break; + case TRNSPRT_UNIX: + r = curlx_dyn_add(&buf, ":UNIX"); + break; + default: + r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); + break; + } + if(r) + goto out; + + if(!ssl->verifypeer) { + r = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); + if(r) + goto out; + } + if(!ssl->verifyhost) { + r = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); + if(r) + goto out; + } + if(ssl->verifystatus) { + r = curlx_dyn_add(&buf, ":VRFY-STATUS"); + if(r) + goto out; + } + if(!ssl->verifypeer || !ssl->verifyhost) { + if(cf->conn->bits.conn_to_host) { + r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); + if(r) + goto out; + } + if(cf->conn->bits.conn_to_port) { + r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); + if(r) + goto out; + } + } + + if(ssl->version || ssl->version_max) { + r = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, + (ssl->version_max >> 16)); + if(r) + goto out; + } + if(ssl->ssl_options) { + r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); + if(r) + goto out; + } + if(ssl->cipher_list) { + r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); + if(r) + goto out; + } + if(ssl->cipher_list13) { + r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); + if(r) + goto out; + } + if(ssl->curves) { + r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); + if(r) + goto out; + } + if(ssl->verifypeer) { + r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local); + if(r) + goto out; + if(ssl->ca_info_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); + if(r) + goto out; + } + if(ssl->issuercert_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob); + if(r) + goto out; + } + } + if(ssl->cert_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); + if(r) + goto out; + } + if(ssl->pinned_key && ssl->pinned_key[0]) { + r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); + if(r) + goto out; + } + + if(ssl->clientcert && ssl->clientcert[0]) { + r = curlx_dyn_add(&buf, ":CCERT"); + if(r) + goto out; + } +#ifdef USE_TLS_SRP + if(ssl->username || ssl->password) { + r = curlx_dyn_add(&buf, ":SRP-AUTH"); + if(r) + goto out; + } +#endif + + if(!tls_id || !tls_id[0]) { + r = CURLE_FAILED_INIT; + goto out; + } + r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); + if(r) + goto out; + + r = curlx_dyn_addf(&buf, is_local ? + CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); + if(r) + goto out; + + *ppeer_key = curlx_dyn_take(&buf, &key_len); + /* we added printable char, and dynbuf always null-terminates, no need + * to track length */ + +out: + curlx_dyn_free(&buf); + return r; +} + struct Curl_ssl_scache { unsigned int magic; struct Curl_ssl_scache_peer *peers; size_t peer_count; int default_lifetime_secs; long age; + BIT(is_locked); }; static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data) @@ -175,12 +402,12 @@ peer->sobj = NULL; } peer->sobj_free = NULL; - Curl_safefree(peer->clientcert); + curlx_safefree(peer->clientcert); #ifdef USE_TLS_SRP - Curl_safefree(peer->srp_username); - Curl_safefree(peer->srp_password); + curlx_safefree(peer->srp_username); + curlx_safefree(peer->srp_password); #endif - Curl_safefree(peer->ssl_peer_key); + curlx_safefree(peer->ssl_peer_key); peer->age = 0; peer->hmac_set = FALSE; } @@ -334,7 +561,7 @@ void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache) { - if(scache && GOOD_SCACHE(scache)) { + if(GOOD_SCACHE(scache)) { size_t i; scache->magic = 0; for(i = 0; i < scache->peer_count; ++i) { @@ -357,244 +584,26 @@ /* Lock shared SSL session data */ void Curl_ssl_scache_lock(struct Curl_easy *data) { - if(CURL_SHARE_ssl_scache(data)) - Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); + struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); + if(scache) { + if(CURL_SHARE_ssl_scache(data)) + Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_ACCESS_SINGLE); + DEBUGASSERT(!scache->is_locked); + scache->is_locked = TRUE; + } } /* Unlock shared SSL session data */ void Curl_ssl_scache_unlock(struct Curl_easy *data) { - if(CURL_SHARE_ssl_scache(data)) - Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); -} - -static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, - const char *name, - const char *path, - bool *is_local) -{ - if(path && path[0]) { - /* We try to add absolute paths, so that the session key can stay - * valid when used in another process with different CWD. However, - * when a path does not exist, this does not work. Then, we add - * the path as is. */ -#ifdef _WIN32 - char abspath[_MAX_PATH]; - if(_fullpath(abspath, path, _MAX_PATH)) - return curlx_dyn_addf(buf, ":%s-%s", name, abspath); - *is_local = TRUE; -#elif defined(HAVE_REALPATH) - if(path[0] != '/') { - char *abspath = realpath(path, NULL); - if(abspath) { - CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath); - /* !checksrc! disable BANNEDFUNC 1 */ - free(abspath); /* allocated by libc, free without memdebug */ - return r; - } - *is_local = TRUE; - } -#endif - return curlx_dyn_addf(buf, ":%s-%s", name, path); + struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); + if(scache) { + DEBUGASSERT(scache->is_locked); + scache->is_locked = FALSE; + if(CURL_SHARE_ssl_scache(data)) + Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); } - return CURLE_OK; -} - -static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, - const char *name, - struct curl_blob *blob) -{ - CURLcode r = CURLE_OK; - if(blob && blob->len) { - unsigned char hash[CURL_SHA256_DIGEST_LENGTH]; - size_t i; - - r = curlx_dyn_addf(buf, ":%s-", name); - if(r) - goto out; - r = Curl_sha256it(hash, blob->data, blob->len); - if(r) - goto out; - for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) { - r = curlx_dyn_addf(buf, "%02x", hash[i]); - if(r) - goto out; - } - } -out: - return r; -} - -#define CURL_SSLS_LOCAL_SUFFIX ":L" -#define CURL_SSLS_GLOBAL_SUFFIX ":G" - -static bool cf_ssl_peer_key_is_global(const char *peer_key) -{ - size_t len = peer_key ? strlen(peer_key) : 0; - return (len > 2) && - (peer_key[len - 1] == 'G') && - (peer_key[len - 2] == ':'); -} - -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, - const char *tls_id, - char **ppeer_key) -{ - struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); - struct dynbuf buf; - size_t key_len; - bool is_local = FALSE; - CURLcode r; - - *ppeer_key = NULL; - curlx_dyn_init(&buf, 10 * 1024); - - r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); - if(r) - goto out; - - switch(peer->transport) { - case TRNSPRT_TCP: - break; - case TRNSPRT_UDP: - r = curlx_dyn_add(&buf, ":UDP"); - break; - case TRNSPRT_QUIC: - r = curlx_dyn_add(&buf, ":QUIC"); - break; - case TRNSPRT_UNIX: - r = curlx_dyn_add(&buf, ":UNIX"); - break; - default: - r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); - break; - } - if(r) - goto out; - - if(!ssl->verifypeer) { - r = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); - if(r) - goto out; - } - if(!ssl->verifyhost) { - r = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); - if(r) - goto out; - } - if(ssl->verifystatus) { - r = curlx_dyn_add(&buf, ":VRFY-STATUS"); - if(r) - goto out; - } - if(!ssl->verifypeer || !ssl->verifyhost) { - if(cf->conn->bits.conn_to_host) { - r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); - if(r) - goto out; - } - if(cf->conn->bits.conn_to_port) { - r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); - if(r) - goto out; - } - } - - if(ssl->version || ssl->version_max) { - r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version, - (ssl->version_max >> 16)); - if(r) - goto out; - } - if(ssl->ssl_options) { - r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); - if(r) - goto out; - } - if(ssl->cipher_list) { - r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); - if(r) - goto out; - } - if(ssl->cipher_list13) { - r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); - if(r) - goto out; - } - if(ssl->curves) { - r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); - if(r) - goto out; - } - if(ssl->verifypeer) { - r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); - if(r) - goto out; - r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); - if(r) - goto out; - r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); - if(r) - goto out; - r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local); - if(r) - goto out; - if(ssl->cert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); - if(r) - goto out; - } - if(ssl->ca_info_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); - if(r) - goto out; - } - if(ssl->issuercert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob); - if(r) - goto out; - } - } - if(ssl->pinned_key && ssl->pinned_key[0]) { - r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); - if(r) - goto out; - } - - if(ssl->clientcert && ssl->clientcert[0]) { - r = curlx_dyn_add(&buf, ":CCERT"); - if(r) - goto out; - } -#ifdef USE_TLS_SRP - if(ssl->username || ssl->password) { - r = curlx_dyn_add(&buf, ":SRP-AUTH"); - if(r) - goto out; - } -#endif - - if(!tls_id || !tls_id[0]) { - r = CURLE_FAILED_INIT; - goto out; - } - r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); - if(r) - goto out; - - r = curlx_dyn_addf(&buf, is_local ? - CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); - if(r) - goto out; - - *ppeer_key = curlx_dyn_take(&buf, &key_len); - /* we just added printable char, and dynbuf always null-terminates, no need - * to track length */ - -out: - curlx_dyn_free(&buf); - return r; } static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, @@ -737,12 +746,19 @@ peer = cf_ssl_get_free_peer(scache); if(peer) { + char buffer[64]; const char *ccert = conn_config ? conn_config->clientcert : NULL; const char *username = NULL, *password = NULL; #ifdef USE_TLS_SRP username = conn_config ? conn_config->username : NULL; password = conn_config ? conn_config->password : NULL; #endif + if(!ccert && conn_config && conn_config->cert_blob) { + /* when using a client cert blob, create a name for it */ + curl_msnprintf(buffer, sizeof(buffer), + "cert-%p", conn_config->cert_blob->data); + ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */ + } result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, username, password, NULL, NULL); if(result) @@ -916,7 +932,7 @@ struct Curl_easy *data, const char *ssl_peer_key, void *sobj, - Curl_ssl_scache_obj_dtor *sobj_free) + Curl_ssl_scache_obj_dtor *sobj_dtor_cb) { struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -924,7 +940,7 @@ CURLcode result; DEBUGASSERT(sobj); - DEBUGASSERT(sobj_free); + DEBUGASSERT(sobj_dtor_cb); if(!scache) { result = CURLE_BAD_FUNCTION_ARGUMENT; @@ -937,12 +953,12 @@ goto out; } - cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free); + cf_ssl_scache_peer_set_obj(peer, sobj, sobj_dtor_cb); sobj = NULL; /* peer took ownership */ out: - if(sobj && sobj_free) - sobj_free(sobj); + if(sobj && sobj_dtor_cb) + sobj_dtor_cb(sobj); return result; } @@ -996,6 +1012,12 @@ #define CURL_SSL_TICKET_MAX (16 * 1024) +bool Curl_ssl_scache_is_locked(struct Curl_easy *data) +{ + struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); + return scache && scache->is_locked; +} + static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer) { CURLcode result; @@ -1078,49 +1100,47 @@ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); struct Curl_ssl_scache_peer *peer = NULL; struct Curl_ssl_session *s = NULL; - bool locked = FALSE; - CURLcode r; + CURLcode result; if(!scache) { - r = CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } if(!ssl_peer_key && (!shmac || !shmac_len)) { - r = CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } - r = Curl_ssl_session_unpack(data, sdata, sdata_len, &s); - if(r) + result = Curl_ssl_session_unpack(data, sdata, sdata_len, &s); + if(result) goto out; Curl_ssl_scache_lock(data); - locked = TRUE; if(ssl_peer_key) { - r = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer); - if(r) + result = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer); + if(result) goto out; } else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) { /* Either salt+hmac was garbled by caller or is from a curl version * that does things differently */ - r = CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } else { const unsigned char *salt = shmac; const unsigned char *hmac = shmac + sizeof(peer->key_salt); - r = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer); - if(r) + result = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer); + if(result) goto out; if(!peer) { peer = cf_ssl_get_free_peer(scache); if(peer) { - r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, - NULL, NULL, salt, hmac); - if(r) + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, + NULL, NULL, salt, hmac); + if(result) goto out; } } @@ -1136,10 +1156,10 @@ } out: - if(locked) + if(scache && scache->is_locked) Curl_ssl_scache_unlock(data); Curl_ssl_session_destroy(s); - return r; + return result; } CURLcode Curl_ssl_session_export(struct Curl_easy *data, @@ -1150,14 +1170,15 @@ struct Curl_ssl_scache_peer *peer; struct dynbuf sbuf, hbuf; struct Curl_llist_node *n; - size_t i, npeers = 0, ntickets = 0; + size_t i; curl_off_t now = time(NULL); CURLcode r = CURLE_OK; +#ifdef CURLVERBOSE + size_t npeers = 0, ntickets = 0; +#endif if(!export_fn) return CURLE_BAD_FUNCTION_ARGUMENT; - if(!scache) - return CURLE_OK; Curl_ssl_scache_lock(data); @@ -1175,7 +1196,7 @@ cf_scache_peer_remove_expired(peer, now); n = Curl_llist_head(&peer->sessions); if(n) - ++npeers; + VERBOSE(++npeers); while(n) { struct Curl_ssl_session *s = Curl_node_elem(n); if(!peer->hmac_set) { @@ -1203,10 +1224,9 @@ s->alpn, s->earlydata_max); if(r) goto out; - ++ntickets; + VERBOSE(++ntickets); n = Curl_node_next(n); } - } r = CURLE_OK; CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers",
diff --git a/Utilities/cmcurl/lib/vtls/vtls_scache.h b/Utilities/cmcurl/lib/vtls/vtls_scache.h index 2a99e77..a6a36f1 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_scache.h +++ b/Utilities/cmcurl/lib/vtls/vtls_scache.h
@@ -23,13 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" - -#include "../cfilters.h" -#include "../urldata.h" +#include "curl_setup.h" #ifdef USE_SSL +#include "cfilters.h" +#include "urldata.h" + struct Curl_cfilter; struct Curl_easy; struct Curl_ssl_scache; @@ -52,7 +52,7 @@ /* Create a key from peer and TLS configuration information that is * unique for how the connection filter wants to establish a TLS * connection to the peer. - * If the filter is a TLS proxy filter, it will use the proxy relevant + * If the filter is a TLS proxy filter, it uses the proxy relevant * information. * @param cf the connection filter wanting to use it * @param peer the peer the filter wants to talk to @@ -84,7 +84,7 @@ /* Unlock session cache mutex */ void Curl_ssl_scache_unlock(struct Curl_easy *data); -/* Get TLS session object from the cache for the ssl_peer_ey. +/* Get TLS session object from the cache for the ssl_peer_key. * scache mutex must be locked (see Curl_ssl_scache_lock). * Caller must make sure that the ownership of returned session object * is properly taken (e.g. its refcount is incremented @@ -112,7 +112,7 @@ * @param data the transfer involved * @param ssl_peer_key the key for lookup * @param sobj the TLS session object - * @param sobj_free_cb callback to free the session objectt + * @param sobj_free_cb callback to free the session object */ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -199,6 +199,8 @@ #ifdef USE_SSLS_EXPORT +bool Curl_ssl_scache_is_locked(struct Curl_easy *data); + CURLcode Curl_ssl_session_import(struct Curl_easy *data, const char *ssl_peer_key, const unsigned char *shmac, size_t shmac_len,
diff --git a/Utilities/cmcurl/lib/vtls/vtls_spack.c b/Utilities/cmcurl/lib/vtls/vtls_spack.c index 9eeb808..d96f4e4 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_spack.c +++ b/Utilities/cmcurl/lib/vtls/vtls_spack.c
@@ -21,15 +21,15 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) -#include "../urldata.h" -#include "../curl_trc.h" -#include "vtls_scache.h" -#include "vtls_spack.h" -#include "../strdup.h" +#include "urldata.h" +#include "curl_trc.h" +#include "vtls/vtls_scache.h" +#include "vtls/vtls_spack.h" +#include "curlx/strdup.h" #ifndef UINT16_MAX #define UINT16_MAX 0xffff @@ -152,7 +152,7 @@ return r; if(end - *src < slen) return CURLE_READ_ERROR; - *val = Curl_memdup0((const char *)(*src), slen); + *val = curlx_memdup0((const char *)(*src), slen); *src += slen; return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY; } @@ -182,7 +182,7 @@ return r; if(end - *src < data_len) return CURLE_READ_ERROR; - *val = Curl_memdup0((const char *)(*src), data_len); + *val = curlx_memdup0((const char *)(*src), data_len); *val_len = data_len; *src += data_len; return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY;
diff --git a/Utilities/cmcurl/lib/vtls/vtls_spack.h b/Utilities/cmcurl/lib/vtls/vtls_spack.h index 86796ee..4479384 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_spack.h +++ b/Utilities/cmcurl/lib/vtls/vtls_spack.h
@@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index 5103788..15c81c2 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -26,16 +26,18 @@ * but vtls.c should ever call or use these functions. * */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_WOLFSSL -#define WOLFSSL_OPTIONS_IGNORE_SYS #include <wolfssl/options.h> #include <wolfssl/version.h> -#if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */ -#error "wolfSSL version should be at least 3.4.6" +#if LIBWOLFSSL_VERSION_HEX < 0x05000000 /* wolfSSL 5.0.0 (2021-11-01) */ +#error "wolfSSL version should be at least 5.0.0" +#endif +#if defined(OPENSSL_COEXIST) && LIBWOLFSSL_VERSION_HEX < 0x05007006 +#error "wolfSSL 5.7.6 or newer is required to coexist with OpenSSL" #endif /* To determine what functions are available we rely on one or both of: @@ -51,21 +53,27 @@ #endif #endif -#include "../urldata.h" -#include "../curl_trc.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "keylog.h" -#include "../connect.h" /* for the connect timeout */ -#include "../progress.h" -#include "../strdup.h" -#include "../curlx/strcopy.h" -#include "x509asn1.h" +#include "urldata.h" +#include "curl_trc.h" +#include "httpsrr.h" +#include "cf-dns.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/keylog.h" +#include "connect.h" /* for the connect timeout */ +#include "progress.h" +#include "curlx/strdup.h" +#include "curlx/strcopy.h" +#include "vtls/x509asn1.h" +#ifdef USE_ECH +#include "curlx/base64.h" +#endif #include <wolfssl/ssl.h> #include <wolfssl/error-ssl.h> -#include "wolfssl.h" + +#include "vtls/wolfssl.h" /* KEEP_PEER_CERT is a product of the presence of build time symbol OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is @@ -100,16 +108,12 @@ #undef USE_BIO_CHAIN #endif -static CURLcode wssl_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done); - #ifdef OPENSSL_EXTRA /* * Availability note: * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in * wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that - * option is not set, then TLS 1.3 will not be logged. + * option is not set, then TLS 1.3 is not logged. * For TLS 1.2 and before, we use wolfSSL_get_keys(). * wolfSSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA * (--enable-opensslextra or --enable-all). @@ -168,7 +172,6 @@ unsigned char *ms, *sr, *cr; unsigned int msLen, srLen, crLen, i, x = 0; -#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */ /* wolfSSL_GetVersion is available since 3.13, we use it instead of * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or * --enable-all). Failing to perform this check could result in an unusable @@ -184,7 +187,6 @@ * is not directly usable. */ return; } -#endif if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != WOLFSSL_SUCCESS) { @@ -325,7 +327,7 @@ #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif - if(CURLE_AGAIN == result) { + if(result == CURLE_AGAIN) { wolfSSL_BIO_set_retry_write(bio); if(wssl->shutting_down && !wssl->io_send_blocked_len) wssl->io_send_blocked_len = blen; @@ -370,7 +372,7 @@ #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) wolfSSL_BIO_set_retry_read(bio); else if(nread == 0) connssl->peer_closed = TRUE; @@ -401,7 +403,7 @@ #else /* USE_BIO_CHAIN */ -#define wssl_bio_cf_init_methods() Curl_nop_stmt +#define wssl_bio_cf_init_methods() TRUE #define wssl_bio_cf_free_methods() Curl_nop_stmt #endif /* !USE_BIO_CHAIN */ @@ -446,7 +448,7 @@ goto out; } if(quic_tp && quic_tp_len) { - qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len); + qtp_clone = curlx_memdup0((const char *)quic_tp, quic_tp_len); if(!qtp_clone) { curlx_free(sdata); return CURLE_OUT_OF_MEMORY; @@ -501,36 +503,18 @@ bool *do_early_data) { struct ssl_connect_data *connssl = cf->ctx; - struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; - CURLcode result = CURLE_OK; - - *do_early_data = FALSE; #ifdef WOLFSSL_EARLY_DATA + struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; + connssl->earlydata_max = wolfSSL_SESSION_get_max_early_data( wolfSSL_get_session(wssl->ssl)); #else - (void)wssl; connssl->earlydata_max = 0; #endif - if(!connssl->earlydata_max) { - /* Seems to be no WolfSSL way to signal no EarlyData in session */ - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); - } - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); - connssl->earlydata_state = ssl_earlydata_await; - connssl->state = ssl_connection_deferred; - result = Curl_alpn_set_negotiated(cf, data, connssl, - (const unsigned char *)scs->alpn, - scs->alpn ? strlen(scs->alpn) : 0); - *do_early_data = !result; - } - return result; + /* Seems to be no wolfSSL way to signal no EarlyData in session */ + return Curl_on_session_reuse(cf, data, alpns, scs, do_early_data, + connssl->earlydata_max); } static CURLcode @@ -564,7 +548,7 @@ infof(data, "SSL reusing session with ALPN '%s'", scs->alpn ? scs->alpn : "-"); if(ssl_config->earlydata && - !cf->conn->connect_only && + !cf->conn->bits.connect_only && !strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) { bool do_early_data = FALSE; if(sess_reuse_cb) { @@ -676,7 +660,7 @@ return CURLE_SSL_CACERT_BADFILE; } else { - /* Just continue with a warning if no strict certificate + /* continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:"); @@ -890,8 +874,7 @@ } #endif -/* 4.2.0 (2019) */ -#if LIBWOLFSSL_VERSION_HEX < 0x04002000 || !defined(OPENSSL_EXTRA) +#ifndef OPENSSL_EXTRA static int wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX *ctx, int version) { int res; @@ -927,11 +910,11 @@ } #define wolfSSL_CTX_set_min_proto_version wssl_legacy_CTX_set_min_proto_version #define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version -#endif +#endif /* OPENSSL_EXTRA */ -static CURLcode client_certificate(struct Curl_easy *data, - struct ssl_config_data *ssl_config, - struct wssl_ctx *wctx) +static CURLcode wssl_client_cert(struct Curl_easy *data, + struct ssl_config_data *ssl_config, + struct wssl_ctx *wctx) { /* Load the client certificate, and private key */ #ifndef NO_FILESYSTEM @@ -1031,8 +1014,9 @@ { int res; *min_version = *max_version = 0; + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); + switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: *min_version = TLS1_VERSION; @@ -1093,80 +1077,29 @@ } #define QUIC_GROUPS "P-256:P-384:P-521" - -CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const struct alpn_spec *alpns_requested, - Curl_wssl_ctx_setup_cb *cb_setup, - void *cb_user_data, - void *ssl_user_data, - Curl_wssl_init_session_reuse_cb *sess_reuse_cb) -{ - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_primary_config *conn_config; - WOLFSSL_METHOD *req_method = NULL; - struct alpn_spec alpns; - char *curves; -#ifdef WOLFSSL_HAVE_KYBER - word16 pqkem = 0; - size_t idx = 0; -#endif - CURLcode result = CURLE_FAILED_INIT; - unsigned char transport; - int tls_min, tls_max; - - DEBUGASSERT(!wctx->ssl_ctx); - DEBUGASSERT(!wctx->ssl); - conn_config = Curl_ssl_cf_get_primary_config(cf); - if(!conn_config) { - result = CURLE_FAILED_INIT; - goto out; - } - Curl_alpn_copy(&alpns, alpns_requested); - DEBUGASSERT(cf->next); - transport = Curl_conn_cf_get_transport(cf->next, data); - -#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ - req_method = wolfSSLv23_client_method(); -#else - req_method = wolfTLS_client_method(); -#endif - if(!req_method) { - failf(data, "wolfSSL: could not create a client method"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - if(wctx->ssl_ctx) - wolfSSL_CTX_free(wctx->ssl_ctx); - - wctx->ssl_ctx = wolfSSL_CTX_new(req_method); - if(!wctx->ssl_ctx) { - failf(data, "wolfSSL: could not create a context"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - result = ssl_version(data, conn_config, wctx, &tls_min, &tls_max); - if(result) - goto out; - -#ifndef WOLFSSL_TLS13 - { - char *ciphers = conn_config->cipher_list; - if(ciphers) { - if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) { - failf(data, "failed setting cipher list: %s", ciphers); - result = CURLE_SSL_CIPHER; - goto out; - } - infof(data, "Cipher selection: %s", ciphers); - } - } -#else +#ifdef WOLFSSL_TLS13 #define MAX_CIPHER_LEN 4096 +#endif + +static CURLcode wssl_init_ciphers(struct Curl_easy *data, + struct wssl_ctx *wctx, + struct ssl_primary_config *conn_config, + int tls_min, int tls_max) +{ +#ifndef WOLFSSL_TLS13 + const char *ciphers = conn_config->cipher_list; + (void)tls_min; + (void)tls_max; + if(ciphers) { + if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + return CURLE_SSL_CIPHER; + } + infof(data, "Cipher selection: %s", ciphers); + } + return CURLE_OK; +#else + CURLcode result = CURLE_OK; if(conn_config->cipher_list || conn_config->cipher_list13) { const char *ciphers12 = conn_config->cipher_list; const char *ciphers13 = conn_config->cipher_list13; @@ -1190,45 +1123,276 @@ else result = wssl_add_default_ciphers(FALSE, &c); } - if(result) - goto out; - - if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) { - failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c)); - curlx_dyn_free(&c); - result = CURLE_SSL_CIPHER; - goto out; + if(!result) { + if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) { + failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c)); + result = CURLE_SSL_CIPHER; + } + else + infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c)); } - infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c)); curlx_dyn_free(&c); } + return result; #endif +} - curves = conn_config->curves; +static CURLcode wssl_init_curves(struct Curl_easy *data, + struct wssl_ctx *wctx, + struct ssl_primary_config *conn_config, + unsigned char transport +#ifdef WOLFSSL_HAVE_KYBER + , word16 *out_pqkem +#endif + ) +{ + char *curves = conn_config->curves; if(!curves && (transport == TRNSPRT_QUIC)) curves = (char *)CURL_UNCONST(QUIC_GROUPS); if(curves) { #ifdef WOLFSSL_HAVE_KYBER + size_t idx; for(idx = 0; gnm[idx].name != NULL; idx++) { - if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) { - pqkem = gnm[idx].group; + if(!strncmp(curves, gnm[idx].name, strlen(gnm[idx].name))) { + *out_pqkem = gnm[idx].group; break; } } - if(pqkem == 0) + if(*out_pqkem == 0) #endif { if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) { failf(data, "failed setting curves list: '%s'", curves); - result = CURLE_SSL_CIPHER; - goto out; + return CURLE_SSL_CIPHER; + } + } + } + return CURLE_OK; +} + +static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct alpn_spec *alpns, + void *ssl_user_data, + unsigned char transport, +#ifdef WOLFSSL_HAVE_KYBER + word16 pqkem, +#endif + Curl_wssl_init_session_reuse_cb + *sess_reuse_cb) +{ + /* Let's make an SSL structure */ + wctx->ssl = wolfSSL_new(wctx->ssl_ctx); + if(!wctx->ssl) { + failf(data, "SSL: could not create a handle"); + return CURLE_OUT_OF_MEMORY; + } + +#ifdef HAVE_EX_DATA + wolfSSL_set_app_data(wctx->ssl, ssl_user_data); +#else + (void)ssl_user_data; +#endif +#ifdef WOLFSSL_QUIC + if(transport == TRNSPRT_QUIC) + wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0); +#else + (void)transport; +#endif + +#ifdef WOLFSSL_HAVE_KYBER + if(pqkem) { + if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != + WOLFSSL_SUCCESS) { + failf(data, "unable to use PQ KEM"); + } + } +#endif + + /* Check if there is a cached ID we can/should use here! */ + if(Curl_ssl_scache_use(cf, data)) { + /* Set session from cache if there is one */ + (void)wssl_setup_session(cf, data, wctx, alpns, peer->scache_key, + sess_reuse_cb); + } + +#ifdef HAVE_ALPN + if(alpns->count) { + struct alpn_proto_buf proto; + memset(&proto, 0, sizeof(proto)); + Curl_alpn_to_proto_str(&proto, alpns); + + if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data, + (unsigned int)proto.len, + WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) + != WOLFSSL_SUCCESS) { + failf(data, "SSL: failed setting ALPN protocols"); + return CURLE_SSL_CONNECT_ERROR; + } + CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data); + } +#endif /* HAVE_ALPN */ + +#ifdef OPENSSL_EXTRA + if(Curl_tls_keylog_enabled()) { + /* Ensure the Client Random is preserved. */ + wolfSSL_KeepArrays(wctx->ssl); +#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) + wolfSSL_set_tls13_secret_cb(wctx->ssl, wssl_tls13_secret_callback, NULL); +#endif + } +#endif /* OPENSSL_EXTRA */ + +#ifdef HAVE_SECURE_RENEGOTIATION + if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) { + failf(data, "SSL: failed setting secure renegotiation"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* HAVE_SECURE_RENEGOTIATION */ + + return CURLE_OK; +} + +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG +static CURLcode wssl_init_ech(struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + int trying_ech_now = 0; + + if(data->set.str[STRING_ECH_PUBLIC]) { + infof(data, "ECH: outername not (yet) supported" + " with wolfSSL"); + return CURLE_SSL_CONNECT_ERROR; + } + if(data->set.tls_ech == CURLECH_GREASE) { + infof(data, "ECH: GREASE is done by default by" + " wolfSSL: no need to ask"); + } + if(data->set.tls_ech & CURLECH_CLA_CFG && + data->set.str[STRING_ECH_CONFIG]) { + char *b64val = data->set.str[STRING_ECH_CONFIG]; + word32 b64len = 0; + + b64len = (word32)strlen(b64val); + if(b64len && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, + b64len) != WOLFSSL_SUCCESS) { + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + trying_ech_now = 1; + infof(data, "ECH: ECHConfig from command line"); + } + } + else { + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); + + if(rinfo && rinfo->echconfiglist) { + const unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; + + infof(data, "ECH: ECHConfig from HTTPS RR"); + if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != + WOLFSSL_SUCCESS) { + infof(data, "ECH: wolfSSL_SetEchConfigs failed"); + if(data->set.tls_ech & CURLECH_HARD) { + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %zu", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) { + return CURLE_SSL_CONNECT_ERROR; } } } - result = client_certificate(data, ssl_config, wctx); + if(trying_ech_now && + wolfSSL_set_min_proto_version(wctx->ssl, TLS1_3_VERSION) != 1) { + infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); + return CURLE_SSL_CONNECT_ERROR; + } + return CURLE_OK; +} +#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ + +CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const struct alpn_spec *alpns_requested, + Curl_wssl_ctx_setup_cb *cb_setup, + void *cb_user_data, + void *ssl_user_data, + Curl_wssl_init_session_reuse_cb *sess_reuse_cb) +{ + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_primary_config *conn_config; + WOLFSSL_METHOD *req_method = NULL; + struct alpn_spec alpns; +#ifdef WOLFSSL_HAVE_KYBER + word16 pqkem = 0; +#endif + CURLcode result = CURLE_FAILED_INIT; + unsigned char transport; + int tls_min, tls_max; + + DEBUGASSERT(!wctx->ssl_ctx); + DEBUGASSERT(!wctx->ssl); + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) { + result = CURLE_FAILED_INIT; + goto out; + } + Curl_alpn_copy(&alpns, alpns_requested); + DEBUGASSERT(cf->next); + transport = Curl_conn_cf_get_transport(cf->next, data); + + req_method = wolfTLS_client_method(); + if(!req_method) { + failf(data, "wolfSSL: could not create a client method"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(wctx->ssl_ctx) + wolfSSL_CTX_free(wctx->ssl_ctx); + + wctx->ssl_ctx = wolfSSL_CTX_new(req_method); + if(!wctx->ssl_ctx) { + failf(data, "wolfSSL: could not create a context"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = ssl_version(data, conn_config, wctx, &tls_min, &tls_max); + if(result) + goto out; + + result = wssl_init_ciphers(data, wctx, conn_config, tls_min, tls_max); + if(result) + goto out; + + result = wssl_init_curves(data, wctx, conn_config, transport +#ifdef WOLFSSL_HAVE_KYBER + , &pqkem +#endif + ); + if(result) + goto out; + + result = wssl_client_cert(data, ssl_config, wctx); if(result) goto out; @@ -1236,9 +1400,8 @@ * 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. */ - wolfSSL_CTX_set_verify(wctx->ssl_ctx, - conn_config->verifypeer ? WOLFSSL_VERIFY_PEER : - WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_CTX_set_verify(wctx->ssl_ctx, conn_config->verifypeer ? + WOLFSSL_VERIFY_PEER : WOLFSSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(peer->sni) { @@ -1285,7 +1448,7 @@ #ifdef NO_FILESYSTEM else if(conn_config->verifypeer) { failf(data, "SSL: Certificates cannot be loaded because wolfSSL was built" - " with \"no file system\". Either disable peer verification" + " with no file system. Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); result = CURLE_SSL_CONNECT_ERROR; @@ -1293,158 +1456,20 @@ } #endif - /* Let's make an SSL structure */ - wctx->ssl = wolfSSL_new(wctx->ssl_ctx); - if(!wctx->ssl) { - failf(data, "SSL: could not create a handle"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - -#ifdef HAVE_EX_DATA - wolfSSL_set_app_data(wctx->ssl, ssl_user_data); -#else - (void)ssl_user_data; -#endif -#ifdef WOLFSSL_QUIC - if(transport == TRNSPRT_QUIC) - wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0); -#endif - + result = wssl_init_ssl_handle(wctx, cf, data, peer, &alpns, ssl_user_data, + transport, #ifdef WOLFSSL_HAVE_KYBER - if(pqkem) { - if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != WOLFSSL_SUCCESS) { - failf(data, "unable to use PQ KEM"); - } - } + pqkem, #endif - - /* Check if there is a cached ID we can/should use here! */ - if(Curl_ssl_scache_use(cf, data)) { - /* Set session from cache if there is one */ - (void)wssl_setup_session(cf, data, wctx, &alpns, - peer->scache_key, sess_reuse_cb); - } - -#ifdef HAVE_ALPN - if(alpns.count) { - struct alpn_proto_buf proto; - memset(&proto, 0, sizeof(proto)); - Curl_alpn_to_proto_str(&proto, &alpns); - - if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data, - (unsigned int)proto.len, - WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) { - failf(data, "SSL: failed setting ALPN protocols"); - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data); - } -#endif /* HAVE_ALPN */ - -#ifdef OPENSSL_EXTRA - if(Curl_tls_keylog_enabled()) { - /* Ensure the Client Random is preserved. */ - wolfSSL_KeepArrays(wctx->ssl); -#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) - wolfSSL_set_tls13_secret_cb(wctx->ssl, wssl_tls13_secret_callback, NULL); -#endif - } -#endif /* OPENSSL_EXTRA */ - -#ifdef HAVE_SECURE_RENEGOTIATION - if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) { - failf(data, "SSL: failed setting secure renegotiation"); - result = CURLE_SSL_CONNECT_ERROR; + sess_reuse_cb); + if(result) goto out; - } -#endif /* HAVE_SECURE_RENEGOTIATION */ #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG - if(ECH_ENABLED(data)) { - int trying_ech_now = 0; - - if(data->set.str[STRING_ECH_PUBLIC]) { - infof(data, "ECH: outername not (yet) supported with wolfSSL"); - result = CURLE_SSL_CONNECT_ERROR; + if(CURLECH_ENABLED(data)) { + result = wssl_init_ech(wctx, cf, data); + if(result) goto out; - } - if(data->set.tls_ech == CURLECH_GREASE) { - infof(data, "ECH: GREASE is done by default by wolfSSL: no need to ask"); - } - if(data->set.tls_ech & CURLECH_CLA_CFG && - data->set.str[STRING_ECH_CONFIG]) { - char *b64val = data->set.str[STRING_ECH_CONFIG]; - word32 b64len = 0; - - b64len = (word32)strlen(b64val); - if(b64len && - wolfSSL_SetEchConfigsBase64(wctx->ssl, - b64val, b64len) != WOLFSSL_SUCCESS) { - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - trying_ech_now = 1; - infof(data, "ECH: ECHConfig from command line"); - } - } - else { - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_dns_entry *dns = NULL; - - dns = Curl_dnscache_get(data, connssl->peer.hostname, connssl->peer.port, - cf->conn->ip_version); - if(!dns) { - infof(data, "ECH: requested but no DNS info available"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - struct Curl_https_rrinfo *rinfo = NULL; - - rinfo = dns->hinfo; - if(rinfo && rinfo->echconfiglist) { - unsigned char *ecl = rinfo->echconfiglist; - size_t elen = rinfo->echconfiglist_len; - - infof(data, "ECH: ECHConfig from DoH HTTPS RR"); - if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != - WOLFSSL_SUCCESS) { - infof(data, "ECH: wolfSSL_SetEchConfigs failed"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - trying_ech_now = 1; - infof(data, "ECH: imported ECHConfigList of length %ld", elen); - } - } - else { - infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - Curl_resolv_unlink(data, &dns); - } - } - - if(trying_ech_now && wolfSSL_set_min_proto_version(wctx->ssl, - TLS1_3_VERSION) != 1) { - infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } #endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ @@ -1462,6 +1487,21 @@ return result; } +bool Curl_wssl_need_httpsrr(struct Curl_easy *data) +{ +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +#else + (void)data; + return FALSE; +#endif +} + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. @@ -1479,9 +1519,8 @@ if(connssl->state == ssl_connection_complete) return CURLE_OK; - result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer, - connssl->alpn, NULL, NULL, cf, - wssl_on_session_reuse); + result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer, connssl->alpn, + NULL, NULL, cf, wssl_on_session_reuse); if(result) return result; @@ -1630,7 +1669,7 @@ int err = wolfSSL_get_error(wssl->ssl, rc); char error_buffer[256]; switch(err) { - case WOLFSSL_ERROR_NONE: /* just did not get anything */ + case WOLFSSL_ERROR_NONE: /* did not get anything */ case WOLFSSL_ERROR_WANT_READ: case WOLFSSL_ERROR_WANT_WRITE: return CURLE_AGAIN; @@ -1748,7 +1787,7 @@ } else if(DOMAIN_NAME_MISMATCH == detail) { /* There is no easy way to override only the CN matching. - * This will enable the override of both mismatching SubjectAltNames + * This enables the override of both mismatching SubjectAltNames * as also mismatching CN fields */ failf(data, " subject alt name(s) or common name do not match \"%s\"", connssl->peer.dispname); @@ -1759,7 +1798,7 @@ failf(data, " CA signer not available for verification"); return CURLE_SSL_CACERT_BADFILE; } - /* Just continue with a warning if no strict certificate + /* Continue with a warning if no strict certificate verification is required. */ infof(data, "CA signer not available for verification, " "continuing anyway"); @@ -1888,9 +1927,9 @@ struct wssl_ctx *wctx = (struct wssl_ctx *)connssl->backend; CURLcode result = CURLE_OK; char buf[1024]; - char error_buffer[256]; int nread = -1, err; size_t i; + VERBOSE(char error_buffer[256]); DEBUGASSERT(wctx); if(!wctx->ssl || cf->shutdown) { @@ -1959,7 +1998,7 @@ CURL_TRC_CF(data, cf, "SSL shutdown received"); *done = TRUE; break; - case WOLFSSL_ERROR_NONE: /* just did not get anything */ + case WOLFSSL_ERROR_NONE: /* did not get anything */ case WOLFSSL_ERROR_WANT_READ: /* wolfSSL has send its notify and now wants to read the reply * from the server. We are not really interested in that. */ @@ -2032,13 +2071,13 @@ case WOLFSSL_ERROR_NONE: case WOLFSSL_ERROR_WANT_READ: case WOLFSSL_ERROR_WANT_WRITE: - if(!wssl->io_result && connssl->peer_closed) { - CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen); - return CURLE_OK; + if(!wssl->io_result && !connssl->peer_closed) { + /* there is data pending, re-invoke wolfSSL_read() */ + CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); + return CURLE_AGAIN; } - /* there is data pending, re-invoke wolfSSL_read() */ - CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); - return CURLE_AGAIN; + /* fall through to default error handling below */ + FALLTHROUGH(); default: if(wssl->io_result == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); @@ -2065,11 +2104,7 @@ size_t Curl_wssl_version(char *buffer, size_t size) { -#if LIBWOLFSSL_VERSION_HEX >= 0x03006000 return curl_msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version()); -#elif defined(WOLFSSL_VERSION) - return curl_msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION); -#endif } static int wssl_init(void) @@ -2112,13 +2147,10 @@ void Curl_wssl_report_handshake(struct Curl_easy *data, struct wssl_ctx *wssl) { -#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) + (void)wssl; infof(data, "SSL connection using %s / %s", wolfSSL_get_version(wssl->ssl), wolfSSL_get_cipher_name(wssl->ssl)); -#else - infof(data, "SSL connected"); -#endif } static CURLcode wssl_connect(struct Curl_cfilter *cf, @@ -2139,6 +2171,15 @@ connssl->io_need = CURL_SSL_IO_NEED_NONE; if(ssl_connect_1 == connssl->connecting_state) { +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + /* if we do ECH and need the HTTPS-RR information for it, + * we delay the connect until it arrives or DNS resolve fails. */ + if(Curl_wssl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); + return CURLE_OK; + } +#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ result = wssl_connect_step1(cf, data); if(result) return result; @@ -2159,8 +2200,8 @@ } if(ssl_connect_3 == connssl->connecting_state) { - /* Once the handshake has errored, it stays in that state and will - * error again on every call. */ + /* Once the handshake has errored, it stays in that state and + * errors again on every call. */ if(wssl->hs_result) { result = wssl->hs_result; goto out; @@ -2286,7 +2327,8 @@ SSLSUPP_TLS13_CIPHERSUITES | #endif SSLSUPP_CA_CACHE | - SSLSUPP_CIPHER_LIST, + SSLSUPP_CIPHER_LIST | + SSLSUPP_SSL_EC_CURVES, sizeof(struct wssl_ctx),
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.h b/Utilities/cmcurl/lib/vtls/wolfssl.h index 736da9a..2490bf3 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.h +++ b/Utilities/cmcurl/lib/vtls/wolfssl.h
@@ -23,11 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_WOLFSSL -#include "../urldata.h" +#include "urldata.h" + +#include <wolfssl/options.h> struct alpn_spec; struct ssl_peer; @@ -65,12 +67,15 @@ struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - const struct alpn_spec *alpns, + const struct alpn_spec *alpns_requested, Curl_wssl_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data, Curl_wssl_init_session_reuse_cb *sess_reuse_cb); +/* Is a resolved HTTPS-RR needed for initializing wolfSSL? */ +bool Curl_wssl_need_httpsrr(struct Curl_easy *data); + CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, struct wssl_ctx *wssl);
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index e6fdb10..788dfb2 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c
@@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ defined(USE_MBEDTLS) || defined(USE_RUSTLS) @@ -36,12 +36,12 @@ #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ #endif -#include "../urldata.h" -#include "vtls.h" -#include "../curl_trc.h" -#include "../curlx/base64.h" -#include "x509asn1.h" -#include "../curlx/dynbuf.h" +#include "urldata.h" +#include "vtls/vtls.h" +#include "curl_trc.h" +#include "curlx/base64.h" +#include "vtls/x509asn1.h" +#include "curlx/dynbuf.h" /* * Constants. @@ -50,43 +50,8 @@ /* Largest supported ASN.1 structure. */ #define CURL_ASN1_MAX ((size_t)0x40000) /* 256K */ -/* ASN.1 classes. */ -/* #define CURL_ASN1_UNIVERSAL 0 */ -/* #define CURL_ASN1_APPLICATION 1 */ -/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */ -/* #define CURL_ASN1_PRIVATE 3 */ - -/* ASN.1 types. */ -#define CURL_ASN1_BOOLEAN 1 -#define CURL_ASN1_INTEGER 2 -#define CURL_ASN1_BIT_STRING 3 -#define CURL_ASN1_OCTET_STRING 4 -#define CURL_ASN1_NULL 5 -#define CURL_ASN1_OBJECT_IDENTIFIER 6 -/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */ -/* #define CURL_ASN1_INSTANCE_OF 8 */ -/* #define CURL_ASN1_REAL 9 */ -#define CURL_ASN1_ENUMERATED 10 -/* #define CURL_ASN1_EMBEDDED 11 */ -#define CURL_ASN1_UTF8_STRING 12 -/* #define CURL_ASN1_RELATIVE_OID 13 */ -/* #define CURL_ASN1_SEQUENCE 16 */ -/* #define CURL_ASN1_SET 17 */ -#define CURL_ASN1_NUMERIC_STRING 18 -#define CURL_ASN1_PRINTABLE_STRING 19 -#define CURL_ASN1_TELETEX_STRING 20 -/* #define CURL_ASN1_VIDEOTEX_STRING 21 */ -#define CURL_ASN1_IA5_STRING 22 -#define CURL_ASN1_UTC_TIME 23 -#define CURL_ASN1_GENERALIZED_TIME 24 -/* #define CURL_ASN1_GRAPHIC_STRING 25 */ -#define CURL_ASN1_VISIBLE_STRING 26 -/* #define CURL_ASN1_GENERAL_STRING 27 */ -#define CURL_ASN1_UNIVERSAL_STRING 28 -/* #define CURL_ASN1_CHARACTER_STRING 29 */ -#define CURL_ASN1_BMP_STRING 30 - #ifdef WANT_EXTRACT_CERTINFO + /* ASN.1 OID table entry. */ struct Curl_OID { const char *numoid; /* Dotted-numeric OID. */ @@ -146,7 +111,7 @@ { "2.16.840.1.101.3.4.2.2", "sha384" }, { "2.16.840.1.101.3.4.2.3", "sha512" }, { "1.2.840.113549.1.9.2", "unstructuredName" }, - { (const char *)NULL, (const char *)NULL } + { NULL, NULL } }; #endif /* WANT_EXTRACT_CERTINFO */ @@ -160,10 +125,6 @@ * Please note there is no pretension here to rewrite a full SSL library. */ -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) - WARN_UNUSED_RESULT; - #define CURL_ASN1_MAX_RECURSIONS 16 static const char *getASN1Element_(struct Curl_asn1Element *elem, @@ -199,32 +160,35 @@ b = (unsigned char)*beg++; if(!(b & 0x80)) len = b; - else if(!(b &= 0x7F)) { - /* Unspecified length. Since we have all the data, we can determine the - effective length by skipping element until an end element is found. */ - if(!elem->constructed) - return NULL; - elem->beg = beg; - while(beg < end && *beg) { - beg = getASN1Element_(&lelem, beg, end, lvl + 1); - if(!beg) - return NULL; - } - if(beg >= end) - return NULL; - elem->end = beg; - return beg + 1; - } - else if((unsigned)b > (size_t)(end - beg)) - return NULL; /* Does not fit in source. */ else { - /* Get long length. */ - len = 0; - do { - if(len & 0xFF000000L) - return NULL; /* Lengths > 32 bits are not supported. */ - len = (len << 8) | (unsigned char) *beg++; - } while(--b); + b &= 0x7F; + if(!b) { + /* Unspecified length. Since we have all the data, we can determine the + effective length by skipping element until an end element is found. */ + if(!elem->constructed) + return NULL; + elem->beg = beg; + while(beg < end && *beg) { + beg = getASN1Element_(&lelem, beg, end, lvl + 1); + if(!beg) + return NULL; + } + if(beg >= end) + return NULL; + elem->end = beg; + return beg + 1; + } + else if((unsigned)b > (size_t)(end - beg)) + return NULL; /* Does not fit in source. */ + else { + /* Get long length. */ + len = 0; + do { + if(len & 0xFF000000L) + return NULL; /* Lengths > 32 bits are not supported. */ + len = (len << 8) | (unsigned char) *beg++; + } while(--b); + } } if(len > (size_t)(end - beg)) return NULL; /* Element data does not fit in source. */ @@ -233,8 +197,13 @@ return elem->end; } -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) +/* + * @unittest 1657 + */ +UNITTEST const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end); +UNITTEST const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) { return getASN1Element_(elem, beg, end, 0); } @@ -255,17 +224,6 @@ return NULL; } -#ifdef UNITTESTS -/* used by unit1657.c */ -CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) -{ - if(getASN1Element(elem, beg, end)) - return CURLE_OK; - return CURLE_BAD_FUNCTION_ARGUMENT; -} -#endif - /* * Convert an ASN.1 Boolean value into its string representation. * @@ -275,9 +233,13 @@ static CURLcode bool2str(struct dynbuf *store, const char *beg, const char *end) { + uint8_t val; if(end - beg != 1) return CURLE_BAD_FUNCTION_ARGUMENT; - return curlx_dyn_add(store, *beg ? "TRUE" : "FALSE"); + val = (uint8_t)*beg; + if((val != 0xff) && (val != 0x00)) + return CURLE_BAD_FUNCTION_ARGUMENT; + return curlx_dyn_add(store, val ? "TRUE" : "FALSE"); } /* @@ -300,7 +262,12 @@ { /* Convert an ASN.1 bit string to a printable string. */ - if(++beg > end) + if(beg < end) { + uint8_t unused_bits = (uint8_t)*beg++; + if(unused_bits > 7) + return CURLE_BAD_FUNCTION_ARGUMENT; + } + else return CURLE_BAD_FUNCTION_ARGUMENT; return octet2str(store, beg, end); } @@ -312,8 +279,11 @@ */ static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end) { - unsigned int val = 0; + uint32_t uval = 0; + int32_t sval = 0; size_t n = end - beg; + size_t i; + const unsigned char *p = (const unsigned char *)beg; if(!n) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -321,14 +291,20 @@ if(n > 4) return octet2str(store, beg, end); - /* Represent integers <= 32-bit as a single value. */ - if(*beg & 0x80) - val = ~val; + if(p[0] & 0x80) + uval = ~0U; - do - val = (val << 8) | *(const unsigned char *)beg++; - while(beg < end); - return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val); + for(i = 0; i < n; i++) + uval = (uval << 8) | p[i]; + + sval = (int32_t)uval; + + /* small values are easier to read as decimals */ + if(sval > -10000 && sval < 10000) + return curlx_dyn_addf(store, "%d", sval); + + /* represent larger values as hex */ + return curlx_dyn_addf(store, "0x%x", uval); } /* @@ -369,7 +345,7 @@ return CURLE_BAD_FUNCTION_ARGUMENT; if(type == CURL_ASN1_UTF8_STRING) { - /* Just copy. */ + /* copy. */ if(inlength) result = curlx_dyn_addn(to, from, inlength); } @@ -417,32 +393,67 @@ } /* - * Convert an ASN.1 OID into its dotted string representation. + * Convert an ASN.1 OID into its dotted string representation. Each field is + * limited to unsigned 32-bit values. + * + * A minor limitation is in the second number, which can be no larger than 32 + * bits - 80 == 4294967215. * * Return error code. + * + * @unittest 1666 */ -static CURLcode encodeOID(struct dynbuf *store, - const char *beg, const char *end) +UNITTEST CURLcode encodeOID(struct dynbuf *store, + const char *beg, const char *end); +UNITTEST CURLcode encodeOID(struct dynbuf *store, + const char *beg, const char *end) { - unsigned int x; - unsigned int y; + uint32_t x; + uint32_t y; CURLcode result = CURLE_OK; - /* Process the first two numbers. */ - y = *(const unsigned char *)beg++; - x = y / 40; - y -= x * 40; + if(end <= beg) + return CURLE_BAD_FUNCTION_ARGUMENT; - result = curlx_dyn_addf(store, "%u.%u", x, y); + /* Process the first two numbers. The initial digit cannot be larger than + 2 */ + y = *(const unsigned char *)beg++; + if(y <= 80) + x = y / 40; + else + x = 2; + result = curlx_dyn_addf(store, "%u", x); + if(result) + return result; + + if(y & 0x80) { + uint32_t t = (y & 0x7f); /* the second number */ + if(!t) + /* reject leading 0x80 padding */ + return CURLE_BAD_FUNCTION_ARGUMENT; + do { + if((t & 0xFE000000) || (beg >= end)) + return CURLE_BAD_FUNCTION_ARGUMENT; + y = *(const unsigned char *)beg++; + t = (t << 7) | (y & 0x7F); + } while(y & 0x80); + y = t; + } + + result = curlx_dyn_addf(store, ".%u", y - (x * 40)); if(result) return result; /* Process the trailing numbers. */ - while(beg < end) { + while((beg < end) && !result) { x = 0; + y = 0; do { - if(x & 0xFF000000) - return CURLE_OK; + /* 0x80 as the first value is invalid since it only adds padding */ + if((y & 0x80) && !x) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if((x & 0xFE000000) || (beg == end)) + return CURLE_BAD_FUNCTION_ARGUMENT; y = *(const unsigned char *)beg++; x = (x << 7) | (y & 0x7F); } while(y & 0x80); @@ -458,32 +469,33 @@ */ static CURLcode OID2str(struct dynbuf *store, - const char *beg, const char *end, bool symbolic) + const char *beg, const char *end) { CURLcode result = CURLE_OK; if(beg < end) { - if(symbolic) { - struct dynbuf buf; - curlx_dyn_init(&buf, CURL_X509_STR_MAX); - result = encodeOID(&buf, beg, end); + struct dynbuf buf; + curlx_dyn_init(&buf, CURL_X509_STR_MAX); + result = encodeOID(&buf, beg, end); - if(!result) { - const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf)); - if(op) - result = curlx_dyn_add(store, op->textoid); - else - result = curlx_dyn_add(store, curlx_dyn_ptr(&buf)); - curlx_dyn_free(&buf); - } + if(!result) { + const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf)); + if(op) + result = curlx_dyn_add(store, op->textoid); + else + result = curlx_dyn_add(store, curlx_dyn_ptr(&buf)); } - else - result = encodeOID(store, beg, end); + curlx_dyn_free(&buf); } return result; } -static CURLcode GTime2str(struct dynbuf *store, - const char *beg, const char *end) +/* + * @unittest 1656 + */ +UNITTEST CURLcode GTime2str(struct dynbuf *store, + const char *beg, const char *end); +UNITTEST CURLcode GTime2str(struct dynbuf *store, + const char *beg, const char *end) { const char *tzp; const char *fracp; @@ -556,19 +568,10 @@ "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", beg, beg + 4, beg + 6, beg + 8, beg + 10, sec1, sec2, - fracl ? ".": "", (int)fracl, fracp, + fracl ? "." : "", (int)fracl, fracp, sep, (int)tzl, tzp); } -#ifdef UNITTESTS -/* used by unit1656.c */ -CURLcode Curl_x509_GTime2str(struct dynbuf *store, - const char *beg, const char *end) -{ - return GTime2str(store, beg, end); -} -#endif - /* * Convert an ASN.1 UTC time to a printable string. * @@ -602,11 +605,9 @@ tzp = "GMT"; end = tzp + 3; } - else - tzp++; tzl = end - tzp; - return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", + return curlx_dyn_addf(store, "%d%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", 20 - (*beg >= '5'), beg, beg + 2, beg + 4, beg + 6, beg + 8, sec, (int)tzl, tzp); @@ -616,17 +617,19 @@ * Convert an ASN.1 element to a printable string. * * Return error + * + * @unittest 1667 */ -static CURLcode ASN1tostr(struct dynbuf *store, - struct Curl_asn1Element *elem, int type) +UNITTEST CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem); +UNITTEST CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem) { CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + int type = elem->tag; if(elem->constructed) return result; /* No conversion of structured elements. */ - if(!type) - type = elem->tag; /* Type not forced: use element tag as type. */ - switch(type) { case CURL_ASN1_BOOLEAN: result = bool2str(store, elem->beg, elem->end); @@ -645,7 +648,7 @@ result = curlx_dyn_addn(store, "", 1); break; case CURL_ASN1_OBJECT_IDENTIFIER: - result = OID2str(store, elem->beg, elem->end, TRUE); + result = OID2str(store, elem->beg, elem->end); break; case CURL_ASN1_UTC_TIME: result = UTime2str(store, elem->beg, elem->end); @@ -710,7 +713,7 @@ goto error; } curlx_dyn_reset(&temp); - result = ASN1tostr(&temp, &oid, 0); + result = ASN1tostr(&temp, &oid); if(result) goto error; @@ -745,7 +748,7 @@ goto error; /* Generate value. */ - result = ASN1tostr(store, &value, 0); + result = ASN1tostr(store, &value); if(result) goto error; curlx_dyn_reset(&temp); @@ -903,7 +906,7 @@ if(!p) return CURLE_BAD_FUNCTION_ARGUMENT; } - return OID2str(store, oid.beg, oid.end, TRUE); + return OID2str(store, oid.beg, oid.end); } /* @@ -932,7 +935,7 @@ struct dynbuf *ptr) { size_t valuelen = curlx_dyn_len(ptr); - char *value = curlx_dyn_ptr(ptr); + const char *value = curlx_dyn_ptr(ptr); CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); @@ -954,7 +957,7 @@ /* Generate a certificate information record for the public key. */ - result = ASN1tostr(&out, elem, 0); + result = ASN1tostr(&out, elem); if(!result) { if(data->set.ssl.certinfo) result = ssl_push_certinfo_dyn(data, certnum, label, &out); @@ -979,11 +982,16 @@ * ECC public key is all the data, a value of type BIT STRING mapped to * OCTET STRING and should not be parsed as an ASN.1 value. */ - const size_t len = ((pubkey->end - pubkey->beg - 2) * 4); + const size_t dlen = pubkey->end - pubkey->beg; + size_t len; + if(dlen < 2) + /* too small */ + return 1; + len = (dlen - 2) * 4; if(!certnum) infof(data, " ECC Public Key (%zu bits)", len); if(data->set.ssl.certinfo) { - char q[sizeof(len) * 8 / 3 + 1]; + char q[(sizeof(len) * 8 / 3) + 1]; (void)curl_msnprintf(q, sizeof(q), "%zu", len); if(ssl_push_certinfo(data, certnum, "ECC Public Key", q)) return 1; @@ -1018,7 +1026,7 @@ if(!certnum) infof(data, " RSA Public Key (%zu bits)", len); if(data->set.ssl.certinfo) { - char r[sizeof(len) * 8 / 3 + 1]; + char r[(sizeof(len) * 8 / 3) + 1]; curl_msnprintf(r, sizeof(r), "%zu", len); if(ssl_push_certinfo(data, certnum, "RSA Public Key", r)) return 1; @@ -1137,7 +1145,7 @@ } /* Serial number. */ - result = ASN1tostr(&out, &cert.serialNumber, 0); + result = ASN1tostr(&out, &cert.serialNumber); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1160,7 +1168,7 @@ curlx_dyn_reset(&out); /* Start Date. */ - result = ASN1tostr(&out, &cert.notBefore, 0); + result = ASN1tostr(&out, &cert.notBefore); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1171,7 +1179,7 @@ curlx_dyn_reset(&out); /* Expire Date. */ - result = ASN1tostr(&out, &cert.notAfter, 0); + result = ASN1tostr(&out, &cert.notAfter); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1202,7 +1210,7 @@ curlx_dyn_reset(&out); /* Signature. */ - result = ASN1tostr(&out, &cert.signature, 0); + result = ASN1tostr(&out, &cert.signature); if(result) goto done; if(data->set.ssl.certinfo) {
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h index ba2c389..dda55ac 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.h +++ b/Utilities/cmcurl/lib/vtls/x509asn1.h
@@ -24,13 +24,52 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" + +/* forward-declare to make it visible in all builds */ +struct Curl_asn1Element; #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ defined(USE_MBEDTLS) || defined(USE_RUSTLS) -#include "../cfilters.h" -#include "../urldata.h" +#include "cfilters.h" +#include "urldata.h" + +/* ASN.1 classes. */ +/* #define CURL_ASN1_UNIVERSAL 0 */ +/* #define CURL_ASN1_APPLICATION 1 */ +/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */ +/* #define CURL_ASN1_PRIVATE 3 */ + +/* ASN.1 types. */ +#define CURL_ASN1_BOOLEAN 1 +#define CURL_ASN1_INTEGER 2 +#define CURL_ASN1_BIT_STRING 3 +#define CURL_ASN1_OCTET_STRING 4 +#define CURL_ASN1_NULL 5 +#define CURL_ASN1_OBJECT_IDENTIFIER 6 +/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */ +/* #define CURL_ASN1_INSTANCE_OF 8 */ +/* #define CURL_ASN1_REAL 9 */ +#define CURL_ASN1_ENUMERATED 10 +/* #define CURL_ASN1_EMBEDDED 11 */ +#define CURL_ASN1_UTF8_STRING 12 +/* #define CURL_ASN1_RELATIVE_OID 13 */ +/* #define CURL_ASN1_SEQUENCE 16 */ +/* #define CURL_ASN1_SET 17 */ +#define CURL_ASN1_NUMERIC_STRING 18 +#define CURL_ASN1_PRINTABLE_STRING 19 +#define CURL_ASN1_TELETEX_STRING 20 +/* #define CURL_ASN1_VIDEOTEX_STRING 21 */ +#define CURL_ASN1_IA5_STRING 22 +#define CURL_ASN1_UTC_TIME 23 +#define CURL_ASN1_GENERALIZED_TIME 24 +/* #define CURL_ASN1_GRAPHIC_STRING 25 */ +#define CURL_ASN1_VISIBLE_STRING 26 +/* #define CURL_ASN1_GENERAL_STRING 27 */ +#define CURL_ASN1_UNIVERSAL_STRING 28 +/* #define CURL_ASN1_CHARACTER_STRING 29 */ +#define CURL_ASN1_BMP_STRING 30 /* * Types. @@ -75,20 +114,6 @@ const char *beg, const char *end); CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, const char *beg, const char *end); - -#ifdef UNITTESTS -#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \ - defined(USE_RUSTLS) - -/* used by unit1656.c */ -CURLcode Curl_x509_GTime2str(struct dynbuf *store, - const char *beg, const char *end); -/* used by unit1657.c */ -CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end); -#endif /* USE_GNUTLS || USE_SCHANNEL || USE_MBEDTLS || RUSTLS */ -#endif /* UNITTESTS */ - #endif /* USE_GNUTLS || USE_WOLFSSL || USE_SCHANNEL || USE_MBEDTLS || USE_RUSTLS */ #endif /* HEADER_CURL_X509ASN1_H */
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c index 37cacae..d7840f1 100644 --- a/Utilities/cmcurl/lib/ws.c +++ b/Utilities/cmcurl/lib/ws.c
@@ -22,10 +22,11 @@ * ***************************************************************************/ #include "curl_setup.h" - -#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) - #include "urldata.h" +#include "ws.h" + +#ifndef CURL_DISABLE_WEBSOCKETS + #include "url.h" #include "bufq.h" #include "curlx/dynbuf.h" @@ -35,7 +36,6 @@ #include "sendf.h" #include "curl_trc.h" #include "multiif.h" -#include "ws.h" #include "easyif.h" #include "transfer.h" #include "select.h" @@ -52,18 +52,20 @@ |N|V|V|V| | | |1|2|3| | */ -#define WSBIT_FIN (0x80) -#define WSBIT_RSV1 (0x40) -#define WSBIT_RSV2 (0x20) -#define WSBIT_RSV3 (0x10) +#define WSBIT_FIN 0x80 +#define WSBIT_RSV1 0x40 +#define WSBIT_RSV2 0x20 +#define WSBIT_RSV3 0x10 #define WSBIT_RSV_MASK (WSBIT_RSV1 | WSBIT_RSV2 | WSBIT_RSV3) -#define WSBIT_OPCODE_CONT (0x0) -#define WSBIT_OPCODE_TEXT (0x1) -#define WSBIT_OPCODE_BIN (0x2) -#define WSBIT_OPCODE_CLOSE (0x8) -#define WSBIT_OPCODE_PING (0x9) -#define WSBIT_OPCODE_PONG (0xa) -#define WSBIT_OPCODE_MASK (0xf) +#define WSBIT_OPCODE_CONT 0x0 +#define WSBIT_OPCODE_TEXT 0x1 +#define WSBIT_OPCODE_BIN 0x2 +#define WSBIT_OPCODE_CLOSE 0x8 +#define WSBIT_OPCODE_PING 0x9 +#define WSBIT_OPCODE_PONG 0xa +#ifdef CURLVERBOSE +#define WSBIT_OPCODE_MASK 0xf +#endif #define WSBIT_MASK 0x80 @@ -114,8 +116,8 @@ * and keep track of boundaries. */ struct websocket { struct Curl_easy *data; /* used for write callback handling */ - struct ws_decoder dec; /* decode of we frames */ - struct ws_encoder enc; /* decode of we frames */ + struct ws_decoder dec; /* decode of ws frames */ + struct ws_encoder enc; /* encode of ws frames */ struct bufq recvbuf; /* raw data from the server */ struct bufq sendbuf; /* raw data to be sent to the server */ struct curl_ws_frame recvframe; /* the current WS FRAME received */ @@ -123,6 +125,7 @@ size_t sendbuf_payload; /* number of payload bytes in sendbuf */ }; +#ifdef CURLVERBOSE static const char *ws_frame_name_of_op(uint8_t firstbyte) { switch(firstbyte & WSBIT_OPCODE_MASK) { @@ -142,6 +145,7 @@ return "???"; } } +#endif static int ws_frame_firstbyte2flags(struct Curl_easy *data, uint8_t firstbyte, int cont_flags) @@ -288,6 +292,7 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, const char *msg) { + NOVERBOSE((void)msg); switch(dec->head_len) { case 0: break; @@ -378,7 +383,9 @@ } dec->head_len = 1; - /* ws_dec_info(dec, data, "seeing opcode"); */ +#if 0 + ws_dec_info(dec, data, "seeing opcode"); +#endif continue; } else if(dec->head_len == 1) { @@ -430,7 +437,9 @@ Curl_bufq_skip(inraw, 1); ++dec->head_len; if(dec->head_len < dec->head_total) { - /* ws_dec_info(dec, data, "decoding head"); */ +#if 0 + ws_dec_info(dec, data, "decoding head"); +#endif continue; } } @@ -445,7 +454,7 @@ break; case 10: if(dec->head[2] > 127) { - failf(data, "[WS] frame length longer than 63 bit not supported"); + failf(data, "[WS] frame length longer than 63 bits not supported"); return CURLE_RECV_ERROR; } dec->payload_len = @@ -486,7 +495,6 @@ size_t remain = curlx_sotouz_range(dec->payload_len - dec->payload_offset, 0, SIZE_MAX); - (void)data; while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen)) { if(inlen > remain) inlen = remain; @@ -556,7 +564,7 @@ dec->state = WS_DEC_INIT; break; default: - /* we covered all enums above, but some code analyzers are whimps */ + /* we covered all enums above, but some code analyzers are wimps */ result = CURLE_FAILED_INIT; } return result; @@ -614,7 +622,7 @@ size_t buflen, curl_off_t fragsize, unsigned int flags, - size_t *sent); + size_t *pnsent); static CURLcode ws_enc_add_pending(struct Curl_easy *data, struct websocket *ws); @@ -750,7 +758,7 @@ } if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { - failf(data, "[WS] decode ending with %zd frame bytes remaining", + failf(data, "[WS] decode ending with %zu frame bytes remaining", Curl_bufq_len(&ctx->buf)); return CURLE_RECV_ERROR; } @@ -771,6 +779,8 @@ static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, const char *msg) { + NOVERBOSE((void)enc); + NOVERBOSE((void)msg); CURL_TRC_WS(data, "WS-ENC: %s [%s%s payload=%" FMT_OFF_T "/%" FMT_OFF_T "]", msg, ws_frame_name_of_op(enc->firstbyte), @@ -837,14 +847,15 @@ return CURLE_SEND_ERROR; } - result = ws_frame_flags2firstbyte(data, flags, enc->contfragment, &firstb); + result = ws_frame_flags2firstbyte(data, flags, (bool)enc->contfragment, + &firstb); if(result) return result; /* fragmentation only applies to data frames (text/binary); * control frames (close/ping/pong) do not affect the CONT status */ if(flags & (CURLWS_TEXT | CURLWS_BINARY)) { - enc->contfragment = (flags & CURLWS_CONT) ? (bit)TRUE : (bit)FALSE; + enc->contfragment = (curl_bit)((flags & CURLWS_CONT) ? TRUE : FALSE); } if(flags & CURLWS_PING && payload_len > WS_MAX_CNTRL_LEN) { @@ -1038,9 +1049,9 @@ if((curl_off_t)buflen > (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) { /* too large buflen beyond payload length of frame */ - failf(data, "[WS] unaligned frame size (sending %zu instead of %" - FMT_OFF_T ")", - buflen, ws->enc.payload_remain + ws->sendbuf_payload); + failf(data, "[WS] unaligned frame size (sending %zu instead of " + "%" FMT_OFF_T ")", buflen, + (curl_off_t)(ws->enc.payload_remain + ws->sendbuf_payload)); return CURLE_BAD_FUNCTION_ARGUMENT; } } @@ -1178,7 +1189,7 @@ if(ctx->read_eos) ctx->eos = TRUE; *pnread = nread; - *peos = ctx->eos; + *peos = (bool)ctx->eos; goto out; } @@ -1388,7 +1399,7 @@ if(result) goto out; DEBUGASSERT(nread == nwritten); - k->keepon &= ~KEEP_RECV; /* read no more content */ + CURL_REQ_CLEAR_RECV(data); /* read no more content */ } else { /* !connect_only */ if(data->set.method == HTTPREQ_PUT) { @@ -1398,7 +1409,7 @@ goto out; if(!data->set.ws_raw_mode) { - /* Add our client readerr encoding WS BINARY frames */ + /* Add our client reader encoding WS BINARY frames */ result = Curl_creader_create(&ws_enc_reader, data, &ws_cr_encode, CURL_CR_CONTENT_ENCODE); if(result) @@ -1412,10 +1423,10 @@ /* start over with sending */ data->req.eos_read = FALSE; data->req.upload_done = FALSE; - k->keepon |= KEEP_SEND; + CURL_REQ_SET_SEND(data); } - /* And pass any additional data to the writers */ + /* Then pass any additional data to the writers */ if(nread) { result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread); if(result) @@ -1516,16 +1527,16 @@ return curl_easy_recv(data, buf, buflen, pnread); } -CURLcode curl_ws_recv(CURL *d, void *buffer, - size_t buflen, size_t *nread, +CURLcode curl_ws_recv(CURL *curl, void *buffer, + size_t buflen, size_t *recv, const struct curl_ws_frame **metap) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; struct connectdata *conn; struct websocket *ws; struct ws_collect ctx; - *nread = 0; + *recv = 0; *metap = NULL; if(!GOOD_EASY_HANDLE(data) || (buflen && !buffer)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1598,10 +1609,10 @@ update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, ctx.payload_len, ctx.bufidx); *metap = &ws->recvframe; - *nread = ws->recvframe.len; + *recv = ws->recvframe.len; CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" FMT_OFF_T ", %" FMT_OFF_T " left)", - buflen, *nread, ws->recvframe.offset, + buflen, *recv, ws->recvframe.offset, ws->recvframe.bytesleft); /* all's well, try to send any pending control. we do not know * when the application will call `curl_ws_send()` again. */ @@ -1678,6 +1689,9 @@ CURLcode result = CURLE_OK; size_t nwritten; + if(!data) + return result; + (void)ws; while(buflen) { result = Curl_xfer_send(data, buffer, buflen, FALSE, &nwritten); @@ -1693,7 +1707,7 @@ CURL_TRC_WS(data, "ws_send_raw_blocking() partial, %zu left to send", buflen); - left_ms = Curl_timeleft_ms(data, FALSE); + left_ms = Curl_timeleft_ms(data); if(left_ms < 0) { failf(data, "[WS] Timeout waiting for socket becoming writable"); return CURLE_SEND_ERROR; @@ -1735,6 +1749,8 @@ if(result) return result; result = ws_send_raw_blocking(data, ws, buffer, buflen); + if(!result) + *pnwritten = buflen; } else { /* We need any pending data to be sent or EAGAIN this call. */ @@ -1749,7 +1765,7 @@ return result; } -CURLcode curl_ws_send(CURL *d, const void *buffer_arg, +CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags) @@ -1757,7 +1773,7 @@ struct websocket *ws; const uint8_t *buffer = buffer_arg; CURLcode result = CURLE_OK; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; size_t ndummy; size_t *pnsent = sent ? sent : &ndummy; @@ -1837,11 +1853,11 @@ return Curl_http_setup_conn(data, conn); } -const struct curl_ws_frame *curl_ws_meta(CURL *d) +const struct curl_ws_frame *curl_ws_meta(CURL *curl) { /* we only return something for websocket, called from within the callback when not using raw mode */ - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn && !data->set.ws_raw_mode) { struct websocket *ws; @@ -1852,16 +1868,17 @@ return NULL; } -CURL_EXTERN CURLcode curl_ws_start_frame(CURL *d, +CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, unsigned int flags, curl_off_t frame_len) { struct websocket *ws; CURLcode result = CURLE_OK; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(data->set.ws_raw_mode) { failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled"); return CURLE_FAILED_INIT; @@ -1882,12 +1899,6 @@ goto out; } - if(data->set.ws_raw_mode) { - failf(data, "[WS] cannot start frame in raw mode"); - result = CURLE_SEND_ERROR; - goto out; - } - if(ws->enc.payload_remain) { failf(data, "[WS] previous frame not finished"); result = CURLE_SEND_ERROR; @@ -1904,8 +1915,7 @@ return result; } -const struct Curl_handler Curl_handler_ws = { - "WS", /* scheme */ +const struct Curl_protocol Curl_protocol_ws = { ws_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ @@ -1920,54 +1930,21 @@ ZERO_NULL, /* disconnect */ Curl_http_write_resp, /* write_resp */ Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ Curl_http_follow, /* follow */ - PORT_HTTP, /* defport */ - CURLPROTO_WS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL }; -#ifdef USE_SSL -const struct Curl_handler Curl_handler_wss = { - "WSS", /* scheme */ - ws_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - NULL, /* connecting */ - ZERO_NULL, /* doing */ - NULL, /* proto_pollset */ - Curl_http_doing_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - Curl_http_perform_pollset, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - Curl_http_write_resp, /* write_resp */ - Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - Curl_http_follow, /* follow */ - PORT_HTTPS, /* defport */ - CURLPROTO_WSS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL -}; -#endif - #else CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, - size_t *nread, + size_t *recv, const struct curl_ws_frame **metap) { (void)curl; (void)buffer; (void)buflen; - (void)nread; + (void)recv; (void)metap; return CURLE_NOT_BUILT_IN; } @@ -2002,4 +1979,4 @@ return CURLE_NOT_BUILT_IN; } -#endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_WEBSOCKETS */
diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h index a6e7706..71b5b3f 100644 --- a/Utilities/cmcurl/lib/ws.h +++ b/Utilities/cmcurl/lib/ws.h
@@ -27,16 +27,14 @@ #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +extern const struct Curl_protocol Curl_protocol_ws; + /* meta key for storing protocol meta at connection */ #define CURL_META_PROTO_WS_CONN "meta:proto:ws:conn" CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req); -CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len); - -extern const struct Curl_handler Curl_handler_ws; -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_wss; -#endif +CURLcode Curl_ws_accept(struct Curl_easy *data, + const char *mem, size_t nread); #else #define Curl_ws_request(x, y) CURLE_OK
diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt index 682db9c..d094dc3 100644 --- a/Utilities/cmlibarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/CMakeLists.txt
@@ -44,9 +44,11 @@ MATCHES "^(DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL|NONE)\$") ENDIF() +if(0) # XXX(cmake): not needed for build within cmake # On MacOS, prefer MacPorts libraries to system libraries. # I haven't come up with a compelling argument for this to be conditional. list(APPEND CMAKE_PREFIX_PATH /opt/local) +endif() # XXX(cmake): end # Enable @rpath in the install name. # detail in "cmake --help-policy CMP0042" SET(CMAKE_MACOSX_RPATH ON) @@ -1198,6 +1200,13 @@ ENDIF(HAVE_ICONV) ENDIF(NOT HAVE_ICONV AND LIBICONV_PATH) ENDIF(ICONV_INCLUDE_DIR) + + # XXX(cmake): iconv is required on non-Windows platforms + if(NOT HAVE_ICONV AND NOT WIN32) + message(FATAL_ERROR "iconv is required, but was not found.") + endif() + # XXX(cmake): end + # # Find locale_charset() for libiconv. #
diff --git a/Utilities/cmlibarchive/build/cmake/FindLIBGCC.cmake b/Utilities/cmlibarchive/build/cmake/FindLIBGCC.cmake index 5883ff8..7985f2b 100644 --- a/Utilities/cmlibarchive/build/cmake/FindLIBGCC.cmake +++ b/Utilities/cmlibarchive/build/cmake/FindLIBGCC.cmake
@@ -1,22 +1,22 @@ -# - Find libgcc -# Find the libgcc library. -# -# LIBGCC_LIBRARIES - List of libraries when using libgcc -# LIBGCC_FOUND - True if libgcc found. - -IF (LIBGCC_LIBRARY) - # Already in cache, be silent - SET(LIBGCC_FIND_QUIETLY TRUE) -ENDIF (LIBGCC_LIBRARY) - -FIND_LIBRARY(LIBGCC_LIBRARY NAMES gcc libgcc) - -# handle the QUIETLY and REQUIRED arguments and set LIBGCC_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBGCC DEFAULT_MSG LIBGCC_LIBRARY) - -IF(LIBGCC_FOUND) - SET(LIBGCC_LIBRARIES ${LIBGCC_LIBRARY}) - SET(HAVE_LIBGCC 1) -ENDIF(LIBGCC_FOUND) +# - Find libgcc +# Find the libgcc library. +# +# LIBGCC_LIBRARIES - List of libraries when using libgcc +# LIBGCC_FOUND - True if libgcc found. + +IF (LIBGCC_LIBRARY) + # Already in cache, be silent + SET(LIBGCC_FIND_QUIETLY TRUE) +ENDIF (LIBGCC_LIBRARY) + +FIND_LIBRARY(LIBGCC_LIBRARY NAMES gcc libgcc) + +# handle the QUIETLY and REQUIRED arguments and set LIBGCC_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBGCC DEFAULT_MSG LIBGCC_LIBRARY) + +IF(LIBGCC_FOUND) + SET(LIBGCC_LIBRARIES ${LIBGCC_LIBRARY}) + SET(HAVE_LIBGCC 1) +ENDIF(LIBGCC_FOUND)
diff --git a/Utilities/cmlibarchive/build/cmake/FindNettle.cmake b/Utilities/cmlibarchive/build/cmake/FindNettle.cmake index 54ec9f5..1f66610 100644 --- a/Utilities/cmlibarchive/build/cmake/FindNettle.cmake +++ b/Utilities/cmlibarchive/build/cmake/FindNettle.cmake
@@ -1,23 +1,23 @@ -# - Find Nettle -# Find the Nettle include directory and library -# -# NETTLE_INCLUDE_DIR - where to find <nettle/sha.h>, etc. -# NETTLE_LIBRARIES - List of libraries when using libnettle. -# NETTLE_FOUND - True if libnettle found. - -IF (NETTLE_INCLUDE_DIR) - # Already in cache, be silent - SET(NETTLE_FIND_QUIETLY TRUE) -ENDIF (NETTLE_INCLUDE_DIR) - -FIND_PATH(NETTLE_INCLUDE_DIR nettle/md5.h nettle/ripemd160.h nettle/sha.h) -FIND_LIBRARY(NETTLE_LIBRARY NAMES nettle libnettle) - -# handle the QUIETLY and REQUIRED arguments and set NETTLE_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(NETTLE DEFAULT_MSG NETTLE_LIBRARY NETTLE_INCLUDE_DIR) - -IF(NETTLE_FOUND) - SET(NETTLE_LIBRARIES ${NETTLE_LIBRARY}) -ENDIF(NETTLE_FOUND) +# - Find Nettle +# Find the Nettle include directory and library +# +# NETTLE_INCLUDE_DIR - where to find <nettle/sha.h>, etc. +# NETTLE_LIBRARIES - List of libraries when using libnettle. +# NETTLE_FOUND - True if libnettle found. + +IF (NETTLE_INCLUDE_DIR) + # Already in cache, be silent + SET(NETTLE_FIND_QUIETLY TRUE) +ENDIF (NETTLE_INCLUDE_DIR) + +FIND_PATH(NETTLE_INCLUDE_DIR nettle/md5.h nettle/ripemd160.h nettle/sha.h) +FIND_LIBRARY(NETTLE_LIBRARY NAMES nettle libnettle) + +# handle the QUIETLY and REQUIRED arguments and set NETTLE_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(NETTLE DEFAULT_MSG NETTLE_LIBRARY NETTLE_INCLUDE_DIR) + +IF(NETTLE_FOUND) + SET(NETTLE_LIBRARIES ${NETTLE_LIBRARY}) +ENDIF(NETTLE_FOUND)
diff --git a/Utilities/cmlibarchive/build/cmake/FindPCRE2POSIX.cmake b/Utilities/cmlibarchive/build/cmake/FindPCRE2POSIX.cmake index 4104f37..76bb7a4 100644 --- a/Utilities/cmlibarchive/build/cmake/FindPCRE2POSIX.cmake +++ b/Utilities/cmlibarchive/build/cmake/FindPCRE2POSIX.cmake
@@ -1,34 +1,34 @@ -# - Find pcre2posix -# Find the native PCRE2-8 and PCRE2-POSIX include and libraries -# -# PCRE2_INCLUDE_DIR - where to find pcre2posix.h, etc. -# PCRE2POSIX_LIBRARIES - List of libraries when using libpcre2-posix. -# PCRE2_LIBRARIES - List of libraries when using libpcre2-8. -# PCRE2POSIX_FOUND - True if libpcre2-posix found. -# PCRE2_FOUND - True if libpcre2-8 found. - -IF (PCRE2_INCLUDE_DIR) - # Already in cache, be silent - SET(PCRE2_FIND_QUIETLY TRUE) -ENDIF (PCRE2_INCLUDE_DIR) - -FIND_PATH(PCRE2_INCLUDE_DIR pcre2posix.h) -FIND_LIBRARY(PCRE2POSIX_LIBRARY NAMES pcre2-posix libpcre2-posix pcre2-posix-static) -FIND_LIBRARY(PCRE2_LIBRARY NAMES pcre2-8 libpcre2-8 pcre2-8-static) - -# handle the QUIETLY and REQUIRED arguments and set PCRE2POSIX_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2POSIX DEFAULT_MSG PCRE2POSIX_LIBRARY PCRE2_INCLUDE_DIR) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2 DEFAULT_MSG PCRE2_LIBRARY) - -IF(PCRE2POSIX_FOUND) - SET(PCRE2POSIX_LIBRARIES ${PCRE2POSIX_LIBRARY}) - SET(HAVE_LIBPCRE2POSIX 1) - SET(HAVE_PCRE2POSIX_H 1) -ENDIF(PCRE2POSIX_FOUND) - -IF(PCRE2_FOUND) - SET(PCRE2_LIBRARIES ${PCRE2_LIBRARY}) - SET(HAVE_LIBPCRE2 1) -ENDIF(PCRE2_FOUND) +# - Find pcre2posix +# Find the native PCRE2-8 and PCRE2-POSIX include and libraries +# +# PCRE2_INCLUDE_DIR - where to find pcre2posix.h, etc. +# PCRE2POSIX_LIBRARIES - List of libraries when using libpcre2-posix. +# PCRE2_LIBRARIES - List of libraries when using libpcre2-8. +# PCRE2POSIX_FOUND - True if libpcre2-posix found. +# PCRE2_FOUND - True if libpcre2-8 found. + +IF (PCRE2_INCLUDE_DIR) + # Already in cache, be silent + SET(PCRE2_FIND_QUIETLY TRUE) +ENDIF (PCRE2_INCLUDE_DIR) + +FIND_PATH(PCRE2_INCLUDE_DIR pcre2posix.h) +FIND_LIBRARY(PCRE2POSIX_LIBRARY NAMES pcre2-posix libpcre2-posix pcre2-posix-static) +FIND_LIBRARY(PCRE2_LIBRARY NAMES pcre2-8 libpcre2-8 pcre2-8-static) + +# handle the QUIETLY and REQUIRED arguments and set PCRE2POSIX_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2POSIX DEFAULT_MSG PCRE2POSIX_LIBRARY PCRE2_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2 DEFAULT_MSG PCRE2_LIBRARY) + +IF(PCRE2POSIX_FOUND) + SET(PCRE2POSIX_LIBRARIES ${PCRE2POSIX_LIBRARY}) + SET(HAVE_LIBPCRE2POSIX 1) + SET(HAVE_PCRE2POSIX_H 1) +ENDIF(PCRE2POSIX_FOUND) + +IF(PCRE2_FOUND) + SET(PCRE2_LIBRARIES ${PCRE2_LIBRARY}) + SET(HAVE_LIBPCRE2 1) +ENDIF(PCRE2_FOUND)
diff --git a/Utilities/cmlibarchive/build/cmake/FindPCREPOSIX.cmake b/Utilities/cmlibarchive/build/cmake/FindPCREPOSIX.cmake index 56cc17e..7cc40ec 100644 --- a/Utilities/cmlibarchive/build/cmake/FindPCREPOSIX.cmake +++ b/Utilities/cmlibarchive/build/cmake/FindPCREPOSIX.cmake
@@ -1,34 +1,34 @@ -# - Find pcreposix -# Find the native PCRE and PCREPOSIX include and libraries -# -# PCRE_INCLUDE_DIR - where to find pcreposix.h, etc. -# PCREPOSIX_LIBRARIES - List of libraries when using libpcreposix. -# PCRE_LIBRARIES - List of libraries when using libpcre. -# PCREPOSIX_FOUND - True if libpcreposix found. -# PCRE_FOUND - True if libpcre found. - -IF (PCRE_INCLUDE_DIR) - # Already in cache, be silent - SET(PCRE_FIND_QUIETLY TRUE) -ENDIF (PCRE_INCLUDE_DIR) - -FIND_PATH(PCRE_INCLUDE_DIR pcreposix.h) -FIND_LIBRARY(PCREPOSIX_LIBRARY NAMES pcreposix libpcreposix) -FIND_LIBRARY(PCRE_LIBRARY NAMES pcre libpcre) - -# handle the QUIETLY and REQUIRED arguments and set PCREPOSIX_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCREPOSIX DEFAULT_MSG PCREPOSIX_LIBRARY PCRE_INCLUDE_DIR) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY) - -IF(PCREPOSIX_FOUND) - SET(PCREPOSIX_LIBRARIES ${PCREPOSIX_LIBRARY}) - SET(HAVE_LIBPCREPOSIX 1) - SET(HAVE_PCREPOSIX_H 1) -ENDIF(PCREPOSIX_FOUND) - -IF(PCRE_FOUND) - SET(PCRE_LIBRARIES ${PCRE_LIBRARY}) - SET(HAVE_LIBPCRE 1) -ENDIF(PCRE_FOUND) +# - Find pcreposix +# Find the native PCRE and PCREPOSIX include and libraries +# +# PCRE_INCLUDE_DIR - where to find pcreposix.h, etc. +# PCREPOSIX_LIBRARIES - List of libraries when using libpcreposix. +# PCRE_LIBRARIES - List of libraries when using libpcre. +# PCREPOSIX_FOUND - True if libpcreposix found. +# PCRE_FOUND - True if libpcre found. + +IF (PCRE_INCLUDE_DIR) + # Already in cache, be silent + SET(PCRE_FIND_QUIETLY TRUE) +ENDIF (PCRE_INCLUDE_DIR) + +FIND_PATH(PCRE_INCLUDE_DIR pcreposix.h) +FIND_LIBRARY(PCREPOSIX_LIBRARY NAMES pcreposix libpcreposix) +FIND_LIBRARY(PCRE_LIBRARY NAMES pcre libpcre) + +# handle the QUIETLY and REQUIRED arguments and set PCREPOSIX_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCREPOSIX DEFAULT_MSG PCREPOSIX_LIBRARY PCRE_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY) + +IF(PCREPOSIX_FOUND) + SET(PCREPOSIX_LIBRARIES ${PCREPOSIX_LIBRARY}) + SET(HAVE_LIBPCREPOSIX 1) + SET(HAVE_PCREPOSIX_H 1) +ENDIF(PCREPOSIX_FOUND) + +IF(PCRE_FOUND) + SET(PCRE_LIBRARIES ${PCRE_LIBRARY}) + SET(HAVE_LIBPCRE 1) +ENDIF(PCRE_FOUND)
diff --git a/Utilities/cmlibarchive/build/cmake/config.h.in b/Utilities/cmlibarchive/build/cmake/config.h.in index 9530dd8..175f97d 100644 --- a/Utilities/cmlibarchive/build/cmake/config.h.in +++ b/Utilities/cmlibarchive/build/cmake/config.h.in
@@ -429,6 +429,9 @@ /* Define to 1 if you have the `getea' function. */ #cmakedefine HAVE_GETEA 1 +/* Define to 1 if you have the `getegid' function. */ +#cmakedefine HAVE_GETEGID 1 + /* Define to 1 if you have the `geteuid' function. */ #cmakedefine HAVE_GETEUID 1
diff --git a/Utilities/cmlibarchive/build/version b/Utilities/cmlibarchive/build/version index 3f78540..bd997da 100644 --- a/Utilities/cmlibarchive/build/version +++ b/Utilities/cmlibarchive/build/version
@@ -1 +1 @@ -3008005 +3008006
diff --git a/Utilities/cmlibarchive/libarchive/archive.h b/Utilities/cmlibarchive/libarchive/archive.h index e2356b8..63b4ecb 100644 --- a/Utilities/cmlibarchive/libarchive/archive.h +++ b/Utilities/cmlibarchive/libarchive/archive.h
@@ -34,7 +34,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3008005 +#define ARCHIVE_VERSION_NUMBER 3008006 #include <sys/stat.h> #include <stddef.h> /* for wchar_t */ @@ -174,7 +174,7 @@ /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.8.5" +#define ARCHIVE_VERSION_ONLY_STRING "3.8.6" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void);
diff --git a/Utilities/cmlibarchive/libarchive/archive_acl.c b/Utilities/cmlibarchive/libarchive/archive_acl.c index 4ee8055..2116fe8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_acl.c +++ b/Utilities/cmlibarchive/libarchive/archive_acl.c
@@ -1263,8 +1263,12 @@ tag = 0; s = field[n].start; - st = field[n].start + 1; len = field[n].end - field[n].start; + if (len == 0) { + ret = ARCHIVE_WARN; + continue; + } + st = s + 1; switch (*s) { case L'u':
diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h b/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h index 272f2f8..1f9298f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h
@@ -109,6 +109,10 @@ #include <nettle/version.h> #define ARCHIVE_CRYPTOR_USE_NETTLE 1 +#ifndef AES_MAX_KEY_SIZE +#define AES_MAX_KEY_SIZE AES256_KEY_SIZE +#endif + typedef struct { #if NETTLE_VERSION_MAJOR < 3 struct aes_ctx ctx;
diff --git a/Utilities/cmlibarchive/libarchive/archive_digest_private.h b/Utilities/cmlibarchive/libarchive/archive_digest_private.h index deb134e..5518799 100644 --- a/Utilities/cmlibarchive/libarchive/archive_digest_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_digest_private.h
@@ -177,6 +177,13 @@ #else #include <windows.h> #include <wincrypt.h> +/* Undefine indirect <wincrypt.h> symbols conflicting with BoringSSL/AWS-LC. */ +#undef X509_NAME +#undef X509_EXTENSIONS +#undef PKCS7_ISSUER_AND_SERIAL +#undef PKCS7_SIGNER_INFO +#undef OCSP_REQUEST +#undef OCSP_RESPONSE #define ARCHIVE_CRYPTO_WINCRYPT 1 typedef struct { int valid;
diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.h b/Utilities/cmlibarchive/libarchive/archive_entry.h index 8b8e73a..4ffad00 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry.h
@@ -28,7 +28,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3008005 +#define ARCHIVE_VERSION_NUMBER 3008006 /* * Note: archive_entry.h is for use outside of libarchive; the
diff --git a/Utilities/cmlibarchive/libarchive/archive_hmac.c b/Utilities/cmlibarchive/libarchive/archive_hmac.c index 210cca7..458092f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_hmac.c +++ b/Utilities/cmlibarchive/libarchive/archive_hmac.c
@@ -198,6 +198,7 @@ } #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H) +#include <nettle/version.h> static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) @@ -216,7 +217,12 @@ static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { +#if NETTLE_VERSION_MAJOR < 4 hmac_sha1_digest(ctx, (unsigned)*out_len, out); +#else + hmac_sha1_digest(ctx, out); + *out_len = SHA1_DIGEST_SIZE; +#endif } static void
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c b/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c index a910eef..9ec1e6c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c
@@ -122,13 +122,14 @@ archive_clear_error(a); do { + size_t len; if (filename == NULL) filename = ""; - mine = calloc(1, - sizeof(*mine) + strlen(filename)); + len = strlen(filename); + mine = calloc(1, sizeof(*mine) + len); if (mine == NULL) goto no_memory; - strcpy(mine->filename.m, filename); + memcpy(mine->filename.m, filename, len + 1); mine->block_size = block_size; mine->fd = -1; mine->buffer = NULL;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c index 2c8e453..9f187f8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c
@@ -149,6 +149,8 @@ if (signature != NULL && signature_len > 0) { state->signature_len = signature_len; state->signature = malloc(signature_len); + if (state->signature == NULL) + goto memerr; memcpy(state->signature, signature, signature_len); }
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c index f4368c8..9f0a594 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c
@@ -34,6 +34,9 @@ #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif #ifdef HAVE_BZLIB_H #include <cm3p/bzlib.h> #endif @@ -84,7 +87,7 @@ /* * ELF format */ -#define ELF_HDR_MIN_LEN 0x3f +#define ELF_HDR_MIN_LEN 0x40 /* sizeof(Elf64_Ehdr) */ #define ELF_HDR_EI_CLASS_OFFSET 0x04 #define ELF_HDR_EI_DATA_OFFSET 0x05 @@ -859,13 +862,18 @@ while (e_shnum > 0) { name_offset = (*dec32)(h + sec_tbl_offset); if (name_offset == data_sym_offset) { + uint64_t sel_offset; + if (format_64) { - min_addr = (*dec64)( + sel_offset = (*dec64)( h + sec_tbl_offset + 0x18); } else { - min_addr = (*dec32)( + sel_offset = (*dec32)( h + sec_tbl_offset + 0x10); } + if (sel_offset > SSIZE_MAX) + break; + min_addr = (ssize_t)sel_offset; break; } sec_tbl_offset += e_shentsize;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c index b7b31c2..5943694 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
@@ -383,8 +383,10 @@ NULL, NULL); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + archive_wstring_free(&cab->ws); free(cab); + } return (ARCHIVE_OK); } @@ -3199,6 +3201,10 @@ bitlen = hf->bitlen; len_avail = hf->len_size; hf->tree_used = 0; + /* Initialize table to invalid values */ + for (i = 0; i < tbl_size; i++) { + tbl[i] = (uint16_t)hf->len_size; + } for (i = 0; i < len_avail; i++) { uint16_t *p; int len, cnt;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c index 526096b..eb52f12 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c
@@ -1102,6 +1102,16 @@ ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } + + const char *pathname = archive_entry_pathname(entry); + if (pathname == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid hardlink entry with no pathname"); + free(le); + return (ARCHIVE_FATAL); + } + if (cpio->links_head != NULL) cpio->links_head->previous = le; le->next = cpio->links_head; @@ -1110,10 +1120,11 @@ le->dev = dev; le->ino = ino; le->links = archive_entry_nlink(entry) - 1; - le->name = strdup(archive_entry_pathname(entry)); + le->name = strdup(pathname); if (le->name == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); + free(le); return (ARCHIVE_FATAL); }
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c index cf6a147..ccdf490 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c
@@ -1101,6 +1101,13 @@ header_crc = lha_crc16(0, p, H3_FIXED_SIZE); __archive_read_consume(a, H3_FIXED_SIZE); + /* Reject rediculously large header */ + if (lha->header_size > 65536) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "LHa header size too large"); + return (ARCHIVE_FATAL); + } + /* Read extended headers */ err = lha_read_file_extended_header(a, lha, &header_crc, 4, lha->header_size - H3_FIXED_SIZE, &extdsize);
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c index 10c07b0..4a5a49c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c
@@ -300,7 +300,12 @@ struct mtree_entry *p, *q; mtree = (struct mtree *)(a->format->data); - + + /* Close any dangling file descriptor before freeing */ + if (mtree->fd >= 0) { + close(mtree->fd); + mtree->fd = -1; + } p = mtree->entries; while (p != NULL) { q = p->next;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c index f0e93ae..df80ee8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c
@@ -375,6 +375,7 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar, const uint8_t* buf, size_t size, int64_t offset); static void clear_data_ready_stack(struct rar5* rar); +static void rar5_deinit(struct rar5* rar); /* CDE_xxx = Circular Double Ended (Queue) return values. */ enum CDE_RETURN_VALUES { @@ -429,8 +430,7 @@ return CDE_OUT_OF_BOUNDS; } -/* Pushes a new element into the end of this circular deque object. If current - * size will exceed capacity, the oldest element will be overwritten. */ +/* Pushes a new element into the end of this circular deque object. */ static int cdeque_push_back(struct cdeque* d, void* item) { if(d == NULL) return CDE_PARAM; @@ -554,7 +554,11 @@ return NULL; } - cdeque_push_back(&rar->cstate.filters, cdeque_filter(f)); + if (CDE_OK != cdeque_push_back(&rar->cstate.filters, cdeque_filter(f))) { + free(f); + return NULL; + } + return f; } @@ -3040,7 +3044,9 @@ if(block_length < 4 || block_length > 0x400000 || filter_type > FILTER_ARM || - !is_valid_filter_block_start(rar, block_start)) + !is_valid_filter_block_start(rar, block_start) || + (rar->cstate.window_size > 0 && + (ssize_t)block_length > rar->cstate.window_size >> 1)) { archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filter encountered"); @@ -4339,7 +4345,7 @@ free(rar->vol.push_buf); free_filters(rar); - cdeque_free(&rar->cstate.filters); + rar5_deinit(rar); free(rar); a->format->data = NULL; @@ -4364,6 +4370,7 @@ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } +/* Must match deallocations in rar5_deinit */ static int rar5_init(struct rar5* rar) { memset(rar, 0, sizeof(struct rar5)); @@ -4379,6 +4386,11 @@ return ARCHIVE_OK; } +/* Must match allocations in rar5_init */ +static void rar5_deinit(struct rar5* rar) { + cdeque_free(&rar->cstate.filters); +} + int archive_read_support_format_rar5(struct archive *_a) { struct archive_read* ar; int ret; @@ -4415,8 +4427,9 @@ rar5_has_encrypted_entries); if(ret != ARCHIVE_OK) { - (void) rar5_cleanup(ar); + rar5_deinit(rar); + free(rar); } - return ret; + return ARCHIVE_OK; }
diff --git a/Utilities/cmlibarchive/libarchive/archive_string.c b/Utilities/cmlibarchive/libarchive/archive_string.c index b696419..a4955d4 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.c +++ b/Utilities/cmlibarchive/libarchive/archive_string.c
@@ -575,12 +575,7 @@ } else mbflag = MB_PRECOMPOSED; - /* FIXME(CMake#26903): Offer control over encoding conversion. - For now, we instead tolerate invalid characters as - libarchive 3.7.2 / CMake 3.30 and below did. */ - #if 0 mbflag |= MB_ERR_INVALID_CHARS; - #endif buffsize = dest->length + length + 1; do { @@ -777,7 +772,7 @@ int r; defchar_used = 0; - if (to_cp == CP_UTF8 || sc == NULL) + if (to_cp == CP_UTF8) dp = NULL; else dp = &defchar_used; @@ -1878,6 +1873,9 @@ const char * archive_string_conversion_charset_name(struct archive_string_conv *sc) { + if (sc == NULL) { + return "current locale"; + } if (sc->flag & SCONV_TO_CHARSET) return (sc->to_charset); else @@ -4128,7 +4126,12 @@ * character-set. */ if ((aes->aes_set & AES_SET_MBS) == 0) { const char *pm; /* unused */ - archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */ + if (archive_mstring_get_mbs(a, aes, &pm) != 0) { + /* We have another form, but failed to convert it to + * the native locale. Transitively, we've failed to + * convert it to the specified character set. */ + ret = -1; + } } /* If we already have an MBS form, use it to be translated to * specified character-set. */ @@ -4146,6 +4149,8 @@ if (length != NULL) *length = aes->aes_mbs_in_locale.length; } else { + /* Either we have no string in any form, + * or conversion failed and set 'ret != 0'. */ *p = NULL; if (length != NULL) *length = 0;
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c index 7c886a1..4651d9d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
@@ -1975,7 +1975,7 @@ return (a->lookup_gid)(a->lookup_gid_data, name, id); return (id); } - + int64_t archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id) { @@ -2406,7 +2406,7 @@ */ mode = final_mode & 0777 & ~a->user_umask; - /* + /* * Always create writable such that [f]setxattr() works if we're not * root. */ @@ -3024,7 +3024,7 @@ /* * We are not the last element and we want to * follow symlinks if they are a directory. - * + * * This is needed to extract hardlinks over * symlinks. */ @@ -3435,7 +3435,7 @@ le = new_fixup(a, path); if (le == NULL) return (ARCHIVE_FATAL); - le->fixup |=TODO_MODE_BASE; + le->fixup |= TODO_MODE_BASE; le->mode = mode_final; } return (ARCHIVE_OK); @@ -3447,8 +3447,17 @@ * don't add it to the fixup list here, as it's already been * added. */ - if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) - return (ARCHIVE_OK); + if (errno == EEXIST) { + if (la_stat(path, &st) == 0) { + if (S_ISDIR(st.st_mode)) + return (ARCHIVE_OK); + /* path exists but is not a directory */ + errno = ENOTDIR; + } else { + /* restore original errno */ + errno = EEXIST; + } + } archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path); @@ -4767,4 +4776,3 @@ #endif /* !_WIN32 || __CYGWIN__ */ -
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c index 14ef4cd..b67007a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c
@@ -293,6 +293,17 @@ } else sconv = gnutar->opt_sconv; + /* Sanity check. */ + if (archive_entry_pathname(entry) == NULL +#if defined(_WIN32) && !defined(__CYGWIN__) + && archive_entry_pathname_w(entry) == NULL +#endif + ) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Can't record entry in tar file without pathname"); + return ARCHIVE_FAILED; + } + /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || @@ -385,17 +396,30 @@ r = archive_entry_pathname_l(entry, &(gnutar->pathname), &(gnutar->pathname_length), sconv); if (r != 0) { + const char* p_mbs; if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for pathname"); ret = ARCHIVE_FATAL; goto exit_write_header; } - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Can't translate pathname '%s' to %s", - archive_entry_pathname(entry), - archive_string_conversion_charset_name(sconv)); - ret2 = ARCHIVE_WARN; + p_mbs = archive_entry_pathname(entry); + if (p_mbs) { + /* We have a wrongly-encoded MBS pathname. + * Warn and use it. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname '%s' to %s", p_mbs, + archive_string_conversion_charset_name(sconv)); + ret2 = ARCHIVE_WARN; + } else { + /* We have no MBS pathname. Fail. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname to %s", + archive_string_conversion_charset_name(sconv)); + return ARCHIVE_FAILED; + } } r = archive_entry_uname_l(entry, &(gnutar->uname), &(gnutar->uname_length), sconv);
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c index 4084eb4..97724c1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c
@@ -254,11 +254,11 @@ sconv = ustar->opt_sconv; /* Sanity check. */ + if (archive_entry_pathname(entry) == NULL #if defined(_WIN32) && !defined(__CYGWIN__) - if (archive_entry_pathname_w(entry) == NULL) { -#else - if (archive_entry_pathname(entry) == NULL) { + && archive_entry_pathname_w(entry) == NULL #endif + ) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't record entry in tar file without pathname"); return (ARCHIVE_FAILED); @@ -409,15 +409,28 @@ */ r = archive_entry_pathname_l(entry, &pp, ©_length, sconv); if (r != 0) { + const char* p_mbs; if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Can't translate pathname '%s' to %s", - pp, archive_string_conversion_charset_name(sconv)); - ret = ARCHIVE_WARN; + p_mbs = archive_entry_pathname(entry); + if (p_mbs) { + /* We have a wrongly-encoded MBS pathname. + * Warn and use it. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname '%s' to %s", p_mbs, + archive_string_conversion_charset_name(sconv)); + ret = ARCHIVE_WARN; + } else { + /* We have no MBS pathname. Fail. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname to %s", + archive_string_conversion_charset_name(sconv)); + return ARCHIVE_FAILED; + } } if (copy_length <= USTAR_name_size) memcpy(h + USTAR_name_offset, pp, copy_length);
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c index 2598fc0..37ba73a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c
@@ -232,7 +232,11 @@ sconv = v7tar->opt_sconv; /* Sanity check. */ - if (archive_entry_pathname(entry) == NULL) { + if (archive_entry_pathname(entry) == NULL +#if defined(_WIN32) && !defined(__CYGWIN__) + && archive_entry_pathname_w(entry) == NULL +#endif + ) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't record entry in tar file without pathname"); return (ARCHIVE_FAILED); @@ -382,15 +386,28 @@ */ r = archive_entry_pathname_l(entry, &pp, ©_length, sconv); if (r != 0) { + const char* p_mbs; if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Can't translate pathname '%s' to %s", - pp, archive_string_conversion_charset_name(sconv)); - ret = ARCHIVE_WARN; + p_mbs = archive_entry_pathname(entry); + if (p_mbs) { + /* We have a wrongly-encoded MBS pathname. + * Warn and use it. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname '%s' to %s", p_mbs, + archive_string_conversion_charset_name(sconv)); + ret = ARCHIVE_WARN; + } else { + /* We have no MBS pathname. Fail. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname to %s", + archive_string_conversion_charset_name(sconv)); + return ARCHIVE_FAILED; + } } if (strict && copy_length < V7TAR_name_size) memcpy(h + V7TAR_name_offset, pp, copy_length);
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c index eaebe14..e4a6499 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c
@@ -802,6 +802,17 @@ int version_needed = 10; #define MIN_VERSION_NEEDED(x) do { if (version_needed < x) { version_needed = x; } } while (0) + /* Sanity check. */ + if (archive_entry_pathname(entry) == NULL +#if defined(_WIN32) && !defined(__CYGWIN__) + && archive_entry_pathname_w(entry) == NULL +#endif + ) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Can't record entry in zip file without pathname"); + return ARCHIVE_FAILED; + } + /* Ignore types of entries that we don't support. */ type = archive_entry_filetype(entry); if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) { @@ -882,22 +893,33 @@ return (ARCHIVE_FATAL); } - if (sconv != NULL) { + { const char *p; size_t len; if (archive_entry_pathname_l(zip->entry, &p, &len, sconv) != 0) { + const char* p_mbs; if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Can't translate Pathname '%s' to %s", - archive_entry_pathname(zip->entry), - archive_string_conversion_charset_name(sconv)); - ret2 = ARCHIVE_WARN; + p_mbs = archive_entry_pathname(zip->entry); + if (p_mbs) { + /* We have a wrongly-encoded MBS pathname. Warn and use it. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname '%s' to %s", p_mbs, + archive_string_conversion_charset_name(sconv)); + ret2 = ARCHIVE_WARN; + } else { + /* We have no MBS pathname. Fail. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't translate pathname to %s", + archive_string_conversion_charset_name(sconv)); + return ARCHIVE_FAILED; + } } if (len > 0) archive_entry_set_pathname(zip->entry, p);
diff --git a/Utilities/cmlibuv/CMakeLists.txt b/Utilities/cmlibuv/CMakeLists.txt index 027705a..f095c30 100644 --- a/Utilities/cmlibuv/CMakeLists.txt +++ b/Utilities/cmlibuv/CMakeLists.txt
@@ -38,11 +38,15 @@ ) if(WIN32) list(APPEND uv_libraries - ws2_32 - psapi + advapi32 + dbghelp iphlpapi + ole32 + psapi shell32 + user32 userenv + ws2_32 ) list(APPEND uv_includes src/win @@ -57,7 +61,6 @@ ) list(APPEND uv_sources src/win/async.c - src/win/atomicops-inl.h src/win/core.c src/win/detect-wakeup.c src/win/dl.c @@ -98,7 +101,6 @@ ) list(APPEND uv_sources src/unix/async.c - src/unix/atomic-ops.h src/unix/core.c src/unix/dl.c src/unix/fs.c @@ -111,7 +113,6 @@ src/unix/poll.c src/unix/process.c src/unix/signal.c - src/unix/spinlock.h src/unix/stream.c src/unix/tcp.c src/unix/thread.c @@ -205,11 +206,7 @@ ) list(APPEND uv_defines _GNU_SOURCE) list(APPEND uv_sources - src/unix/epoll.c - src/unix/linux-core.c - src/unix/linux-inotify.c - src/unix/linux-syscalls.c - src/unix/linux-syscalls.h + src/unix/linux.c src/unix/procfs-exepath.c src/unix/proctitle.c src/unix/sysinfo-loadavg.c @@ -283,6 +280,7 @@ endif() if(CMAKE_SYSTEM_VERSION STREQUAL "5.10") list(APPEND uv_defines SUNOS_NO_IFADDRS) + list(APPEND uv_defines SUNOS_NO_PTHREAD_NAME) if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(i386|x86_64)$") list(APPEND uv_defines CMAKE_NO_MKDTEMP) endif()
diff --git a/Utilities/cmlibuv/LICENSE b/Utilities/cmlibuv/LICENSE index eb126da..6566365 100644 --- a/Utilities/cmlibuv/LICENSE +++ b/Utilities/cmlibuv/LICENSE
@@ -1,6 +1,3 @@ -libuv is licensed for use as follows: - -==== Copyright (c) 2015-present libuv project contributors. Permission is hereby granted, free of charge, to any person obtaining a copy @@ -20,47 +17,3 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==== - -This license applies to parts of libuv originating from the -https://github.com/joyent/libuv repository: - -==== - -Copyright Joyent, Inc. and other Node contributors. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. - -==== - -This license applies to all parts of libuv that are not externally -maintained libraries. - -The externally maintained libraries used by libuv are: - - - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. - - - inet_pton and inet_ntop implementations, contained in src/inet.c, are - copyright the Internet Systems Consortium, Inc., and licensed under the ISC - license. - - - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three - clause BSD license. - - - pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB. - Three clause BSD license.
diff --git a/Utilities/cmlibuv/include/uv.h b/Utilities/cmlibuv/include/uv.h index 94113d1..8e45dae 100644 --- a/Utilities/cmlibuv/include/uv.h +++ b/Utilities/cmlibuv/include/uv.h
@@ -23,10 +23,6 @@ #ifndef UV_H #define UV_H - -/* Include KWSys Large File Support configuration. */ -#include <cmsys/Configure.h> - #ifdef __cplusplus extern "C" { #endif @@ -35,6 +31,7 @@ #error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both." #endif +#ifndef UV_EXTERN #ifdef _WIN32 /* Windows - set up dll import/export decorators. */ # if defined(BUILDING_UV_SHARED) @@ -47,24 +44,25 @@ /* Building static library. */ # define UV_EXTERN /* nothing */ # endif -#elif __GNUC__ >= 4 +#elif defined(__GNUC__) # define UV_EXTERN __attribute__((visibility("default"))) -#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) /* Sun Studio >= 8 */ -# define UV_EXTERN __global #else # define UV_EXTERN /* nothing */ #endif +#endif /* UV_EXTERN */ #include "uv/errno.h" #include "uv/version.h" #include <stddef.h> #include <stdio.h> +#include <stdint.h> +#include <math.h> -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include <stdint.h> -#endif +/* Internal type, do not use. */ +struct uv__queue { + struct uv__queue* next; + struct uv__queue* prev; +}; #if defined(_WIN32) # include "uv/win.h" @@ -156,6 +154,9 @@ XX(EFTYPE, "inappropriate file type or format") \ XX(EILSEQ, "illegal byte sequence") \ XX(ESOCKTNOSUPPORT, "socket type not supported") \ + XX(ENODATA, "no data available") \ + XX(EUNATCH, "protocol driver not attached") \ + XX(ENOEXEC, "exec format error") \ #define UV_HANDLE_TYPE_MAP(XX) \ XX(ASYNC, async) \ @@ -251,12 +252,17 @@ typedef struct uv_interface_address_s uv_interface_address_t; typedef struct uv_dirent_s uv_dirent_t; typedef struct uv_passwd_s uv_passwd_t; +typedef struct uv_group_s uv_group_t; typedef struct uv_utsname_s uv_utsname_t; typedef struct uv_statfs_s uv_statfs_t; +typedef struct uv_metrics_s uv_metrics_t; + typedef enum { UV_LOOP_BLOCK_SIGNAL = 0, - UV_METRICS_IDLE_TIME + UV_METRICS_IDLE_TIME, + UV_LOOP_USE_IO_URING_SQPOLL +#define UV_LOOP_USE_IO_URING_SQPOLL UV_LOOP_USE_IO_URING_SQPOLL } uv_loop_option; typedef enum { @@ -286,13 +292,13 @@ UV_EXTERN int uv_loop_close(uv_loop_t* loop); /* * NOTE: - * This function is DEPRECATED (to be removed after 0.12), users should + * This function is DEPRECATED, users should * allocate the loop manually and use uv_loop_init instead. */ UV_EXTERN uv_loop_t* uv_loop_new(void); /* * NOTE: - * This function is DEPRECATED (to be removed after 0.12). Users should use + * This function is DEPRECATED. Users should use * uv_loop_close and free the memory manually instead. */ UV_EXTERN void uv_loop_delete(uv_loop_t*); @@ -348,11 +354,32 @@ void* buf, size_t buflen); +typedef enum { + UV_CLOCK_MONOTONIC, + UV_CLOCK_REALTIME +} uv_clock_id; + +/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */ typedef struct { long tv_sec; long tv_nsec; } uv_timespec_t; +typedef struct { + int64_t tv_sec; + int32_t tv_nsec; +} uv_timespec64_t; + +/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */ +typedef struct { + long tv_sec; + long tv_usec; +} uv_timeval_t; + +typedef struct { + int64_t tv_sec; + int32_t tv_usec; +} uv_timeval64_t; typedef struct { uint64_t st_dev; @@ -441,7 +468,7 @@ uv_handle_type type; \ /* private */ \ uv_close_cb close_cb; \ - void* handle_queue[2]; \ + struct uv__queue handle_queue; \ union { \ int fd; \ void* reserved[4]; \ @@ -575,11 +602,27 @@ UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay); +UV_EXTERN int uv_tcp_keepalive_ex(uv_tcp_t* handle, + int on, + unsigned int idle, + unsigned int intvl, + unsigned int cnt); UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable); enum uv_tcp_flags { /* Used with uv_tcp_bind, when an IPv6 address is used. */ - UV_TCP_IPV6ONLY = 1 + UV_TCP_IPV6ONLY = 1, + + /* Enable SO_REUSEPORT socket option when binding the handle. + * This allows completely duplicate bindings by multiple processes + * or threads if they all set SO_REUSEPORT before binding the port. + * Incoming connections are distributed across the participating + * listener sockets. + * + * This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+, + * FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now. + */ + UV_TCP_REUSEPORT = 2, }; UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle, @@ -620,10 +663,13 @@ UV_UDP_PARTIAL = 2, /* * Indicates if SO_REUSEADDR will be set when binding the handle. - * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other - * Unix platforms, it sets the SO_REUSEADDR flag. What that means is that - * multiple threads or processes can bind to the same address without error - * (provided they all set the flag) but only the last one to bind will receive + * This sets the SO_REUSEPORT socket flag on the BSDs (except for + * DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't + * have the capability of load balancing, as the opposite of what + * UV_UDP_REUSEPORT would do. On other Unix platforms, it sets the + * SO_REUSEADDR flag. What that means is that multiple threads or + * processes can bind to the same address without error (provided + * they all set the flag) but only the last one to bind will receive * any traffic, in effect "stealing" the port from the previous listener. */ UV_UDP_REUSEADDR = 4, @@ -647,6 +693,18 @@ */ UV_UDP_LINUX_RECVERR = 32, /* + * Indicates if SO_REUSEPORT will be set when binding the handle. + * This sets the SO_REUSEPORT socket option on supported platforms. + * Unlike UV_UDP_REUSEADDR, this flag will make multiple threads or + * processes that are binding to the same address and port "share" + * the port, which means incoming datagrams are distributed across + * the receiving sockets among threads or processes. + * + * This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+, + * FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now. + */ + UV_UDP_REUSEPORT = 64, + /* * Indicates that recvmmsg should be used, if available. */ UV_UDP_RECVMMSG = 256 @@ -686,6 +744,9 @@ UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle); UV_EXTERN int uv_udp_init_ex(uv_loop_t*, uv_udp_t* handle, unsigned int flags); UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock); +UV_EXTERN int uv_udp_open_ex(uv_udp_t* handle, + uv_os_sock_t sock, + unsigned int flags); UV_EXTERN int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags); @@ -722,6 +783,12 @@ const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr); +UV_EXTERN int uv_udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/], + unsigned int flags); UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb); @@ -745,10 +812,15 @@ typedef enum { /* Initial/normal terminal mode */ UV_TTY_MODE_NORMAL, - /* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */ + /* + * Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled). + * May become equivalent to UV_TTY_MODE_RAW_VT in future libuv versions. + */ UV_TTY_MODE_RAW, /* Binary-safe I/O mode for IPC (Unix-only) */ - UV_TTY_MODE_IO + UV_TTY_MODE_IO, + /* Raw input mode. On Windows ENABLE_VIRTUAL_TERMINAL_INPUT is also set. */ + UV_TTY_MODE_RAW_VT } uv_tty_mode_t; typedef enum { @@ -783,6 +855,10 @@ UV_EXTERN uv_handle_type uv_guess_handle(uv_file file); +enum { + UV_PIPE_NO_TRUNCATE = 1u << 0 +}; + /* * uv_pipe_t is a subclass of uv_stream_t. * @@ -799,10 +875,20 @@ UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc); UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file); UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name); +UV_EXTERN int uv_pipe_bind2(uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags); UV_EXTERN void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb); +UV_EXTERN int uv_pipe_connect2(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags, + uv_connect_cb cb); UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size); @@ -1169,6 +1255,12 @@ char* homedir; }; +struct uv_group_s { + char* groupname; + unsigned long gid; + char** members; +}; + struct uv_utsname_s { char sysname[256]; char release[256]; @@ -1187,7 +1279,8 @@ uint64_t f_bavail; uint64_t f_files; uint64_t f_ffree; - uint64_t f_spare[4]; + uint64_t f_frsize; + uint64_t f_spare[3]; }; typedef enum { @@ -1215,16 +1308,6 @@ UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd); typedef struct { - long tv_sec; - long tv_usec; -} uv_timeval_t; - -typedef struct { - int64_t tv_sec; - int32_t tv_usec; -} uv_timeval64_t; - -typedef struct { uv_timeval_t ru_utime; /* user CPU time used */ uv_timeval_t ru_stime; /* system CPU time used */ uint64_t ru_maxrss; /* maximum resident set size */ @@ -1244,11 +1327,15 @@ } uv_rusage_t; UV_EXTERN int uv_getrusage(uv_rusage_t* rusage); +UV_EXTERN int uv_getrusage_thread(uv_rusage_t* rusage); UV_EXTERN int uv_os_homedir(char* buffer, size_t* size); UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size); UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd); UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd); +UV_EXTERN int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid); +UV_EXTERN int uv_os_get_group(uv_group_t* grp, uv_uid_t gid); +UV_EXTERN void uv_os_free_group(uv_group_t* grp); UV_EXTERN uv_pid_t uv_os_getpid(void); UV_EXTERN uv_pid_t uv_os_getppid(void); @@ -1272,6 +1359,17 @@ UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority); UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority); +enum { + UV_THREAD_PRIORITY_HIGHEST = 2, + UV_THREAD_PRIORITY_ABOVE_NORMAL = 1, + UV_THREAD_PRIORITY_NORMAL = 0, + UV_THREAD_PRIORITY_BELOW_NORMAL = -1, + UV_THREAD_PRIORITY_LOWEST = -2, +}; + +UV_EXTERN int uv_thread_getpriority(uv_thread_t tid, int* priority); +UV_EXTERN int uv_thread_setpriority(uv_thread_t tid, int priority); + UV_EXTERN unsigned int uv_available_parallelism(void); UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); @@ -1308,6 +1406,15 @@ UV_EXTERN int uv_os_uname(uv_utsname_t* buffer); +struct uv_metrics_s { + uint64_t loop_count; + uint64_t events; + uint64_t events_waiting; + /* private */ + uint64_t* reserved[13]; +}; + +UV_EXTERN int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics); UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop); typedef enum { @@ -1510,6 +1617,8 @@ const char* path, int mode, uv_fs_cb cb); +#define UV_FS_UTIME_NOW (INFINITY) +#define UV_FS_UTIME_OMIT (NAN) UV_EXTERN int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1741,7 +1850,9 @@ UV_EXTERN uint64_t uv_get_free_memory(void); UV_EXTERN uint64_t uv_get_total_memory(void); UV_EXTERN uint64_t uv_get_constrained_memory(void); +UV_EXTERN uint64_t uv_get_available_memory(void); +UV_EXTERN int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts); UV_EXTERN uint64_t uv_hrtime(void); UV_EXTERN void uv_sleep(unsigned int msec); @@ -1800,6 +1911,7 @@ typedef void (*uv_thread_cb)(void* arg); UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg); +UV_EXTERN int uv_thread_detach(uv_thread_t* tid); typedef enum { UV_THREAD_NO_FLAGS = 0x00, @@ -1818,9 +1930,20 @@ const uv_thread_options_t* params, uv_thread_cb entry, void* arg); +UV_EXTERN int uv_thread_setaffinity(uv_thread_t* tid, + char* cpumask, + char* oldmask, + size_t mask_size); +UV_EXTERN int uv_thread_getaffinity(uv_thread_t* tid, + char* cpumask, + size_t mask_size); +UV_EXTERN int uv_thread_getcpu(void); UV_EXTERN uv_thread_t uv_thread_self(void); UV_EXTERN int uv_thread_join(uv_thread_t *tid); UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2); +UV_EXTERN int uv_thread_setname(const char* name); +UV_EXTERN int uv_thread_getname(uv_thread_t* tid, char* name, size_t size); + /* The presence of these unions force similar struct layout. */ #define XX(_, name) uv_ ## name ## _t name; @@ -1839,7 +1962,7 @@ void* data; /* Loop reference counting. */ unsigned int active_handles; - void* handle_queue[2]; + struct uv__queue handle_queue; union { void* unused; unsigned int count; @@ -1854,8 +1977,19 @@ UV_EXTERN void* uv_loop_get_data(const uv_loop_t*); UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data); +/* Unicode utilities needed for dealing with Windows. */ +UV_EXTERN size_t uv_utf16_length_as_wtf8(const uint16_t* utf16, + ssize_t utf16_len); +UV_EXTERN int uv_utf16_to_wtf8(const uint16_t* utf16, + ssize_t utf16_len, + char** wtf8_ptr, + size_t* wtf8_len_ptr); +UV_EXTERN ssize_t uv_wtf8_length_as_utf16(const char* wtf8); +UV_EXTERN void uv_wtf8_to_utf16(const char* wtf8, + uint16_t* utf16, + size_t utf16_len); + /* Don't export the private CPP symbols. */ -#undef UV_HANDLE_TYPE_PRIVATE #undef UV_REQ_TYPE_PRIVATE #undef UV_REQ_PRIVATE_FIELDS #undef UV_STREAM_PRIVATE_FIELDS @@ -1867,12 +2001,10 @@ #undef UV_TIMER_PRIVATE_FIELDS #undef UV_GETADDRINFO_PRIVATE_FIELDS #undef UV_GETNAMEINFO_PRIVATE_FIELDS -#undef UV_FS_REQ_PRIVATE_FIELDS #undef UV_WORK_PRIVATE_FIELDS #undef UV_FS_EVENT_PRIVATE_FIELDS #undef UV_SIGNAL_PRIVATE_FIELDS #undef UV_LOOP_PRIVATE_FIELDS -#undef UV_LOOP_PRIVATE_PLATFORM_FIELDS #undef UV__ERR #ifdef __cplusplus
diff --git a/Utilities/cmlibuv/include/uv/darwin.h b/Utilities/cmlibuv/include/uv/darwin.h index d226415..06962bf 100644 --- a/Utilities/cmlibuv/include/uv/darwin.h +++ b/Utilities/cmlibuv/include/uv/darwin.h
@@ -40,7 +40,7 @@ void* cf_state; \ uv_mutex_t cf_mutex; \ uv_sem_t cf_sem; \ - void* cf_signals[2]; \ + struct uv__queue cf_signals; \ #define UV_PLATFORM_FS_EVENT_FIELDS \ uv__io_t event_watcher; \ @@ -48,8 +48,8 @@ int realpath_len; \ int cf_flags; \ uv_async_t* cf_cb; \ - void* cf_events[2]; \ - void* cf_member[2]; \ + struct uv__queue cf_events; \ + struct uv__queue cf_member; \ int cf_error; \ uv_mutex_t cf_mutex; \
diff --git a/Utilities/cmlibuv/include/uv/errno.h b/Utilities/cmlibuv/include/uv/errno.h index 71906b3..ac00778 100644 --- a/Utilities/cmlibuv/include/uv/errno.h +++ b/Utilities/cmlibuv/include/uv/errno.h
@@ -413,7 +413,6 @@ #elif defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__NetBSD__) || \ defined(__OpenBSD__) # define UV__EHOSTDOWN (-64) @@ -457,4 +456,28 @@ # define UV__ESOCKTNOSUPPORT (-4025) #endif +/* FreeBSD defines ENODATA in /usr/include/c++/v1/errno.h which is only visible + * if C++ is being used. Define it directly to avoid problems when integrating + * libuv in a C++ project. + */ +#if defined(ENODATA) && !defined(_WIN32) +# define UV__ENODATA UV__ERR(ENODATA) +#elif defined(__FreeBSD__) +# define UV__ENODATA (-9919) +#else +# define UV__ENODATA (-4024) +#endif + +#if defined(EUNATCH) && !defined(_WIN32) +# define UV__EUNATCH UV__ERR(EUNATCH) +#else +# define UV__EUNATCH (-4023) +#endif + +#if defined(ENOEXEC) && !defined(_WIN32) +# define UV__ENOEXEC UV__ERR(ENOEXEC) +#else +# define UV__ENOEXEC (-4022) +#endif + #endif /* UV_ERRNO_H_ */
diff --git a/Utilities/cmlibuv/include/uv/linux.h b/Utilities/cmlibuv/include/uv/linux.h index 9b38405..9f22f8c 100644 --- a/Utilities/cmlibuv/include/uv/linux.h +++ b/Utilities/cmlibuv/include/uv/linux.h
@@ -28,7 +28,7 @@ int inotify_fd; \ #define UV_PLATFORM_FS_EVENT_FIELDS \ - void* watchers[2]; \ + struct uv__queue watchers; \ int wd; \ #endif /* UV_LINUX_H */
diff --git a/Utilities/cmlibuv/include/uv/stdint-msvc2008.h b/Utilities/cmlibuv/include/uv/stdint-msvc2008.h deleted file mode 100644 index d02608a..0000000 --- a/Utilities/cmlibuv/include/uv/stdint-msvc2008.h +++ /dev/null
@@ -1,247 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2008 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The name of the author may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include <limits.h> - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus -extern "C" { -#endif -# include <wchar.h> -#ifdef __cplusplus -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) - typedef signed char int8_t; - typedef signed short int16_t; - typedef signed int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#else - typedef signed __int8 int8_t; - typedef signed __int16 int16_t; - typedef signed __int32 int32_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef signed __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 signed int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -#define INTMAX_C INT64_C -#define UINTMAX_C UINT64_C - -#endif // __STDC_CONSTANT_MACROS ] - - -#endif // _MSC_STDINT_H_ ]
diff --git a/Utilities/cmlibuv/include/uv/threadpool.h b/Utilities/cmlibuv/include/uv/threadpool.h index 9708ebd..24ce916 100644 --- a/Utilities/cmlibuv/include/uv/threadpool.h +++ b/Utilities/cmlibuv/include/uv/threadpool.h
@@ -31,7 +31,7 @@ void (*work)(struct uv__work *w); void (*done)(struct uv__work *w, int status); struct uv_loop_s* loop; - void* wq[2]; + struct uv__queue wq; }; #endif /* UV_THREADPOOL_H_ */
diff --git a/Utilities/cmlibuv/include/uv/tree.h b/Utilities/cmlibuv/include/uv/tree.h index 2b28835..06bba08 100644 --- a/Utilities/cmlibuv/include/uv/tree.h +++ b/Utilities/cmlibuv/include/uv/tree.h
@@ -35,21 +35,7 @@ #endif /* - * This file defines data structures for different types of trees: - * splay trees and red-black trees. - * - * A splay tree is a self-organizing data structure. Every operation - * on the tree causes a splay to happen. The splay moves the requested - * node to the root of the tree and partly rebalances it. - * - * This has the benefit that request locality causes faster lookups as - * the requested nodes move to the top of the tree. On the other hand, - * every lookup causes memory writes. - * - * The Balance Theorem bounds the total access time for m operations - * and n inserts on an initially empty tree as O((m + n)lg n). The - * amortized cost for a sequence of m accesses to a splay tree is O(lg n); - * + * This file defines data structures for red-black trees. * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the @@ -61,239 +47,6 @@ * The maximum height of a red-black tree is 2lg (n+1). */ -#define SPLAY_HEAD(name, type) \ -struct name { \ - struct type *sph_root; /* root of the tree */ \ -} - -#define SPLAY_INITIALIZER(root) \ - { NULL } - -#define SPLAY_INIT(root) do { \ - (root)->sph_root = NULL; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ENTRY(type) \ -struct { \ - struct type *spe_left; /* left element */ \ - struct type *spe_right; /* right element */ \ -} - -#define SPLAY_LEFT(elm, field) (elm)->field.spe_left -#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right -#define SPLAY_ROOT(head) (head)->sph_root -#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) - -/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ -#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKLEFT(head, tmp, field) do { \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKRIGHT(head, tmp, field) do { \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ - SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ -} while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ - -#define SPLAY_PROTOTYPE(name, type, field, cmp) \ -void name##_SPLAY(struct name *, struct type *); \ -void name##_SPLAY_MINMAX(struct name *, int); \ -struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ -struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ - \ -/* Finds the node with the same key as elm */ \ -static __inline struct type * \ -name##_SPLAY_FIND(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) \ - return(NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) \ - return (head->sph_root); \ - return (NULL); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_NEXT(struct name *head, struct type *elm) \ -{ \ - name##_SPLAY(head, elm); \ - if (SPLAY_RIGHT(elm, field) != NULL) { \ - elm = SPLAY_RIGHT(elm, field); \ - while (SPLAY_LEFT(elm, field) != NULL) { \ - elm = SPLAY_LEFT(elm, field); \ - } \ - } else \ - elm = NULL; \ - return (elm); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_MIN_MAX(struct name *head, int val) \ -{ \ - name##_SPLAY_MINMAX(head, val); \ - return (SPLAY_ROOT(head)); \ -} - -/* Main splay operation. - * Moves node close to the key of elm to top - */ -#define SPLAY_GENERATE(name, type, field, cmp) \ -struct type * \ -name##_SPLAY_INSERT(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) { \ - SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ - } else { \ - int __comp; \ - name##_SPLAY(head, elm); \ - __comp = (cmp)(elm, (head)->sph_root); \ - if(__comp < 0) { \ - SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_RIGHT(elm, field) = (head)->sph_root; \ - SPLAY_LEFT((head)->sph_root, field) = NULL; \ - } else if (__comp > 0) { \ - SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \ - SPLAY_LEFT(elm, field) = (head)->sph_root; \ - SPLAY_RIGHT((head)->sph_root, field) = NULL; \ - } else \ - return ((head)->sph_root); \ - } \ - (head)->sph_root = (elm); \ - return (NULL); \ -} \ - \ -struct type * \ -name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *__tmp; \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) { \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ - } else { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ - name##_SPLAY(head, elm); \ - SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ - } \ - return (elm); \ - } \ - return (NULL); \ -} \ - \ -void \ -name##_SPLAY(struct name *head, struct type *elm) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - int __comp; \ - \ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ - __left = __right = &__node; \ - \ - while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) > 0){ \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} \ - \ -/* Splay with either the minimum or the maximum element \ - * Used to find minimum or maximum element in tree. \ - */ \ -void name##_SPLAY_MINMAX(struct name *head, int __comp) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - \ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ - __left = __right = &__node; \ - \ - for (;;) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} - -#define SPLAY_NEGINF -1 -#define SPLAY_INF 1 - -#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) -#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) -#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) -#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) -#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) -#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) - -#define SPLAY_FOREACH(x, name, head) \ - for ((x) = SPLAY_MIN(name, head); \ - (x) != NULL; \ - (x) = SPLAY_NEXT(name, head, x)) - /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ @@ -730,8 +483,8 @@ #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) -#define RB_NEXT(name, x, y) name##_RB_NEXT(y) -#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_NEXT(name, x) name##_RB_NEXT(x) +#define RB_PREV(name, x) name##_RB_PREV(x) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
diff --git a/Utilities/cmlibuv/include/uv/unix.h b/Utilities/cmlibuv/include/uv/unix.h index 7a5a3cb..93daee7 100644 --- a/Utilities/cmlibuv/include/uv/unix.h +++ b/Utilities/cmlibuv/include/uv/unix.h
@@ -66,7 +66,6 @@ # include "darwin.h" #elif defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # include "bsd.h" @@ -93,15 +92,12 @@ struct uv__io_s; struct uv_loop_s; -typedef void (*uv__io_cb)(struct uv_loop_s* loop, - struct uv__io_s* w, - unsigned int events); typedef struct uv__io_s uv__io_t; struct uv__io_s { - uv__io_cb cb; - void* pending_queue[2]; - void* watcher_queue[2]; + uintptr_t bits; + struct uv__queue pending_queue; + struct uv__queue watcher_queue; unsigned int pevents; /* Pending event mask i.e. mask at next tick. */ unsigned int events; /* Current event mask. */ int fd; @@ -241,21 +237,21 @@ #define UV_LOOP_PRIVATE_FIELDS \ unsigned long flags; \ int backend_fd; \ - void* pending_queue[2]; \ - void* watcher_queue[2]; \ + struct uv__queue pending_queue; \ + struct uv__queue watcher_queue; \ uv__io_t** watchers; \ unsigned int nwatchers; \ unsigned int nfds; \ - void* wq[2]; \ + struct uv__queue wq; \ uv_mutex_t wq_mutex; \ uv_async_t wq_async; \ uv_rwlock_t cloexec_lock; \ uv_handle_t* closing_handles; \ - void* process_handles[2]; \ - void* prepare_handles[2]; \ - void* check_handles[2]; \ - void* idle_handles[2]; \ - void* async_handles[2]; \ + struct uv__queue process_handles; \ + struct uv__queue prepare_handles; \ + struct uv__queue check_handles; \ + struct uv__queue idle_handles; \ + struct uv__queue async_handles; \ void (*async_unused)(void); /* TODO(bnoordhuis) Remove in libuv v2. */ \ uv__io_t async_io_watcher; \ int async_wfd; \ @@ -278,7 +274,7 @@ #define UV_PRIVATE_REQ_TYPES /* empty */ #define UV_WRITE_PRIVATE_FIELDS \ - void* queue[2]; \ + struct uv__queue queue; \ unsigned int write_index; \ uv_buf_t* bufs; \ unsigned int nbufs; \ @@ -286,13 +282,16 @@ uv_buf_t bufsml[4]; \ #define UV_CONNECT_PRIVATE_FIELDS \ - void* queue[2]; \ + struct uv__queue queue; \ #define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */ #define UV_UDP_SEND_PRIVATE_FIELDS \ - void* queue[2]; \ - struct sockaddr_storage addr; \ + struct uv__queue queue; \ + union { \ + struct sockaddr addr; \ + struct sockaddr_storage storage; \ + } u; \ unsigned int nbufs; \ uv_buf_t* bufs; \ ssize_t status; \ @@ -307,8 +306,8 @@ uv_connect_t *connect_req; \ uv_shutdown_t *shutdown_req; \ uv__io_t io_watcher; \ - void* write_queue[2]; \ - void* write_completed_queue[2]; \ + struct uv__queue write_queue; \ + struct uv__queue write_completed_queue; \ uv_connection_cb connection_cb; \ int delayed_error; \ int accepted_fd; \ @@ -321,35 +320,38 @@ uv_alloc_cb alloc_cb; \ uv_udp_recv_cb recv_cb; \ uv__io_t io_watcher; \ - void* write_queue[2]; \ - void* write_completed_queue[2]; \ + struct uv__queue write_queue; \ + struct uv__queue write_completed_queue; \ #define UV_PIPE_PRIVATE_FIELDS \ - const char* pipe_fname; /* strdup'ed */ + const char* pipe_fname; /* NULL or strdup'ed */ #define UV_POLL_PRIVATE_FIELDS \ uv__io_t io_watcher; #define UV_PREPARE_PRIVATE_FIELDS \ uv_prepare_cb prepare_cb; \ - void* queue[2]; \ + struct uv__queue queue; \ #define UV_CHECK_PRIVATE_FIELDS \ uv_check_cb check_cb; \ - void* queue[2]; \ + struct uv__queue queue; \ #define UV_IDLE_PRIVATE_FIELDS \ uv_idle_cb idle_cb; \ - void* queue[2]; \ + struct uv__queue queue; \ #define UV_ASYNC_PRIVATE_FIELDS \ uv_async_cb async_cb; \ - void* queue[2]; \ + struct uv__queue queue; \ int pending; \ #define UV_TIMER_PRIVATE_FIELDS \ uv_timer_cb timer_cb; \ - void* heap_node[3]; \ + union { \ + void* heap[3]; \ + struct uv__queue queue; \ + } node; \ uint64_t timeout; \ uint64_t repeat; \ uint64_t start_id; @@ -373,7 +375,7 @@ int retcode; #define UV_PROCESS_PRIVATE_FIELDS \ - void* queue[2]; \ + struct uv__queue queue; \ int status; \ #define UV_FS_PRIVATE_FIELDS \ @@ -438,6 +440,8 @@ # define UV_FS_O_DIRECT 0x04000 #elif defined(__linux__) && defined(__x86_64__) # define UV_FS_O_DIRECT 0x04000 +#elif defined(__linux__) && defined(__loongarch__) +# define UV_FS_O_DIRECT 0x04000 #elif defined(O_DIRECT) # define UV_FS_O_DIRECT O_DIRECT #else
diff --git a/Utilities/cmlibuv/include/uv/version.h b/Utilities/cmlibuv/include/uv/version.h index 9c9d292..b70cd6f 100644 --- a/Utilities/cmlibuv/include/uv/version.h +++ b/Utilities/cmlibuv/include/uv/version.h
@@ -31,8 +31,8 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 44 -#define UV_VERSION_PATCH 2 +#define UV_VERSION_MINOR 52 +#define UV_VERSION_PATCH 0 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX ""
diff --git a/Utilities/cmlibuv/include/uv/win.h b/Utilities/cmlibuv/include/uv/win.h index 2662b0a2..05dd7ca 100644 --- a/Utilities/cmlibuv/include/uv/win.h +++ b/Utilities/cmlibuv/include/uv/win.h
@@ -20,7 +20,7 @@ */ #ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0600 +# define _WIN32_WINNT 0x0A00 #endif #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) @@ -32,20 +32,12 @@ #include <winsock2.h> -#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) -typedef struct pollfd { - SOCKET fd; - short events; - short revents; -} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD; -#endif - #ifndef LOCALE_INVARIANT # define LOCALE_INVARIANT 0x007f #endif #include <mswsock.h> -// Disable the typedef in mstcpip.h of MinGW. +/* Disable the typedef in mstcpip.h of MinGW. */ #define _TCP_INITIAL_RTO_PARAMETERS _TCP_INITIAL_RTO_PARAMETERS__AVOID #define TCP_INITIAL_RTO_PARAMETERS TCP_INITIAL_RTO_PARAMETERS__AVOID #define PTCP_INITIAL_RTO_PARAMETERS PTCP_INITIAL_RTO_PARAMETERS__AVOID @@ -59,12 +51,7 @@ #include <signal.h> #include <fcntl.h> #include <sys/stat.h> - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "stdint-msvc2008.h" -#else -# include <stdint.h> -#endif +#include <stdint.h> #include "tree.h" #include "threadpool.h" @@ -75,6 +62,11 @@ # define S_IFLNK 0xA000 #endif +/* Define missing in Windows Kit Include\{VERSION}\ucrt\sys\stat.h */ +#if defined(_CRT_INTERNAL_NONSTDC_NAMES) && _CRT_INTERNAL_NONSTDC_NAMES && !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + /* Additional signals supported by uv_signal and or uv_kill. The CRT defines * the following signals already: * @@ -91,6 +83,7 @@ * variants (Linux and Darwin) */ #define SIGHUP 1 +#define SIGQUIT 3 #define SIGKILL 9 #define SIGWINCH 28 @@ -116,34 +109,23 @@ # define WSAID_ACCEPTEX \ {0xb5367df1, 0xcbac, 0x11cf, \ {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} -#endif -#ifndef WSAID_CONNECTEX # define WSAID_CONNECTEX \ {0x25a207b9, 0xddf3, 0x4660, \ {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}} -#endif -#ifndef WSAID_GETACCEPTEXSOCKADDRS # define WSAID_GETACCEPTEXSOCKADDRS \ {0xb5367df2, 0xcbac, 0x11cf, \ {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} -#endif -#ifndef WSAID_DISCONNECTEX # define WSAID_DISCONNECTEX \ {0x7fda2e11, 0x8630, 0x436f, \ {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} -#endif -#ifndef WSAID_TRANSMITFILE # define WSAID_TRANSMITFILE \ {0xb5367df0, 0xcbac, 0x11cf, \ {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} -#endif -#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) \ - || (defined(_MSC_VER) && _MSC_VER < 1500) typedef BOOL (PASCAL *LPFN_ACCEPTEX) (SOCKET sListenSocket, SOCKET sAcceptSocket, @@ -285,22 +267,23 @@ } uv_rwlock_t; typedef struct { - unsigned int n; - unsigned int count; + unsigned threshold; + unsigned in; uv_mutex_t mutex; - uv_sem_t turnstile1; - uv_sem_t turnstile2; + /* TODO: in v2 make this a uv_cond_t, without unused_ */ + CONDITION_VARIABLE cond; + unsigned out; } uv_barrier_t; typedef struct { DWORD tls_index; } uv_key_t; -#define UV_ONCE_INIT { 0, NULL } +#define UV_ONCE_INIT { 0, { NULL } } typedef struct uv_once_s { - unsigned char ran; - HANDLE event; + unsigned char unused; + INIT_ONCE init_once; } uv_once_t; /* Platform-specific definitions for uv_spawn support. */ @@ -359,14 +342,14 @@ uv_idle_t* next_idle_handle; \ /* This handle holds the peer sockets for the fast variant of uv_poll_t */ \ SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT]; \ - /* Counter to keep track of active tcp streams */ \ + /* No longer used. */ \ unsigned int active_tcp_streams; \ - /* Counter to keep track of active udp streams */ \ + /* No longer used. */ \ unsigned int active_udp_streams; \ /* Counter to started timer */ \ uint64_t timer_counter; \ /* Threadpool */ \ - void* wq[2]; \ + struct uv__queue wq; \ uv_mutex_t wq_mutex; \ uv_async_t wq_async; @@ -393,6 +376,7 @@ ULONG_PTR result; /* overlapped.Internal is reused to hold the result */\ HANDLE pipeHandle; \ DWORD duplex_flags; \ + WCHAR* name; \ } connect; \ } u; \ struct uv_req_s* next_req; @@ -494,7 +478,7 @@ uint32_t payload_remaining; \ uint64_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \ } ipc_data_frame; \ - void* ipc_xfer_queue[2]; \ + struct uv__queue ipc_xfer_queue; \ int ipc_xfer_queue_length; \ uv_write_t* non_overlapped_writes_tail; \ CRITICAL_SECTION readfile_thread_lock; \ @@ -508,15 +492,18 @@ struct { uv_pipe_connection_fields } conn; \ } pipe; -/* TODO: put the parser states in an union - TTY handles are always half-duplex +/* TODO: put the parser states in a union - TTY handles are always half-duplex * so read-state can safely overlap write-state. */ #define UV_TTY_PRIVATE_FIELDS \ HANDLE handle; \ union { \ struct { \ /* Used for readable TTY handles */ \ - /* TODO: remove me in v2.x. */ \ - HANDLE unused_; \ + union { \ + /* TODO: remove me in v2.x. */ \ + HANDLE unused_; \ + int mode; \ + } mode; \ uv_buf_t read_line_buffer; \ HANDLE read_raw_wait; \ /* Fields used for translating win keystrokes into vt100 characters */ \ @@ -558,7 +545,10 @@ unsigned char events; #define UV_TIMER_PRIVATE_FIELDS \ - void* heap_node[3]; \ + union { \ + void* heap[3]; \ + struct uv__queue queue; \ + } node; \ int unused; \ uint64_t timeout; \ uint64_t repeat; \ @@ -616,7 +606,7 @@ struct uv_process_exit_s { \ UV_REQ_FIELDS \ } exit_req; \ - BYTE* child_stdio_buffer; \ + void* unused; /* TODO: retained for ABI compat; remove this in v2.x. */ \ int exit_signal; \ HANDLE wait_handle; \ HANDLE process_handle; \
diff --git a/Utilities/cmlibuv/src/fs-poll.c b/Utilities/cmlibuv/src/fs-poll.c index 1bac1c5..1fadafd 100644 --- a/Utilities/cmlibuv/src/fs-poll.c +++ b/Utilities/cmlibuv/src/fs-poll.c
@@ -51,7 +51,7 @@ static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b); static void poll_cb(uv_fs_t* req); static void timer_cb(uv_timer_t* timer); -static void timer_close_cb(uv_handle_t* handle); +static void timer_close_cb(uv_handle_t* timer); static uv_stat_t zero_statbuf; @@ -139,6 +139,9 @@ struct poll_ctx* ctx; size_t required_len; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + if (!uv_is_active((uv_handle_t*)handle)) { *size = 0; return UV_EINVAL;
diff --git a/Utilities/cmlibuv/src/idna.c b/Utilities/cmlibuv/src/idna.c index 858b19d..4c876df 100644 --- a/Utilities/cmlibuv/src/idna.c +++ b/Utilities/cmlibuv/src/idna.c
@@ -1,4 +1,4 @@ -/* Copyright (c) 2011, 2018 Ben Noordhuis <info@bnoordhuis.nl> +/* Copyright libuv contributors. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,11 +18,56 @@ */ #include "uv.h" +#include "uv-common.h" #include "idna.h" #include <assert.h> #include <string.h> #include <limits.h> /* UINT_MAX */ + +static int32_t uv__wtf8_decode1(const char** input) { + uint32_t code_point; + uint8_t b1; + uint8_t b2; + uint8_t b3; + uint8_t b4; + + b1 = **input; + if (b1 <= 0x7F) + return b1; /* ASCII code point */ + if (b1 < 0xC2) + return -1; /* invalid: continuation byte */ + code_point = b1; + + b2 = *++*input; + if ((b2 & 0xC0) != 0x80) + return -1; /* invalid: not a continuation byte */ + code_point = (code_point << 6) | (b2 & 0x3F); + if (b1 <= 0xDF) + return 0x7FF & code_point; /* two-byte character */ + + b3 = *++*input; + if ((b3 & 0xC0) != 0x80) + return -1; /* invalid: not a continuation byte */ + code_point = (code_point << 6) | (b3 & 0x3F); + if (b1 <= 0xEF) + return 0xFFFF & code_point; /* three-byte character */ + + b4 = *++*input; + if ((b4 & 0xC0) != 0x80) + return -1; /* invalid: not a continuation byte */ + code_point = (code_point << 6) | (b4 & 0x3F); + if (b1 <= 0xF4) { + code_point &= 0x1FFFFF; + if (code_point <= 0x10FFFF) + return code_point; /* four-byte character */ + } + + /* code point too large */ + return -1; +} + + static unsigned uv__utf8_decode1_slow(const char** p, const char* pe, unsigned a) { @@ -89,6 +134,7 @@ return a; } + unsigned uv__utf8_decode1(const char** p, const char* pe) { unsigned a; @@ -102,6 +148,7 @@ return uv__utf8_decode1_slow(p, pe, a); } + static int uv__idna_toascii_label(const char* s, const char* se, char** d, char* de) { static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789"; @@ -267,7 +314,8 @@ return 0; } -long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { + +ssize_t uv__idna_toascii(const char* s, const char* se, char* d, char* de) { const char* si; const char* st; unsigned c; @@ -317,3 +365,196 @@ *d++ = '\0'; return d - ds; /* Number of bytes written. */ } + + +ssize_t uv_wtf8_length_as_utf16(const char* source_ptr) { + size_t w_target_len = 0; + int32_t code_point; + + do { + code_point = uv__wtf8_decode1(&source_ptr); + if (code_point < 0) + return -1; + if (code_point > 0xFFFF) + w_target_len++; + w_target_len++; + } while (*source_ptr++); + + return w_target_len; +} + + +void uv_wtf8_to_utf16(const char* source_ptr, + uint16_t* w_target, + size_t w_target_len) { + int32_t code_point; + + do { + code_point = uv__wtf8_decode1(&source_ptr); + /* uv_wtf8_length_as_utf16 should have been called and checked first. */ + assert(code_point >= 0); + if (code_point > 0xFFFF) { + assert(code_point <= 0x10FFFF); + *w_target++ = (((code_point - 0x10000) >> 10) + 0xD800); + *w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00; + w_target_len -= 2; + } else { + *w_target++ = code_point; + w_target_len -= 1; + } + } while (*source_ptr++); + + (void)w_target_len; + assert(w_target_len == 0); +} + + +static int32_t uv__get_surrogate_value(const uint16_t* w_source_ptr, + ssize_t w_source_len) { + uint16_t u; + uint16_t next; + + u = w_source_ptr[0]; + if (u >= 0xD800 && u <= 0xDBFF && w_source_len != 1) { + next = w_source_ptr[1]; + if (next >= 0xDC00 && next <= 0xDFFF) + return 0x10000 + ((u - 0xD800) << 10) + (next - 0xDC00); + } + return u; +} + + +size_t uv_utf16_length_as_wtf8(const uint16_t* w_source_ptr, + ssize_t w_source_len) { + size_t target_len; + int32_t code_point; + + target_len = 0; + while (w_source_len) { + code_point = uv__get_surrogate_value(w_source_ptr, w_source_len); + /* Can be invalid UTF-8 but must be valid WTF-8. */ + assert(code_point >= 0); + if (w_source_len < 0 && code_point == 0) + break; + if (code_point < 0x80) + target_len += 1; + else if (code_point < 0x800) + target_len += 2; + else if (code_point < 0x10000) + target_len += 3; + else { + target_len += 4; + w_source_ptr++; + if (w_source_len > 0) + w_source_len--; + } + w_source_ptr++; + if (w_source_len > 0) + w_source_len--; + } + + return target_len; +} + + +int uv_utf16_to_wtf8(const uint16_t* w_source_ptr, + ssize_t w_source_len, + char** target_ptr, + size_t* target_len_ptr) { + size_t target_len; + char* target; + char* target_end; + int32_t code_point; + + /* If *target_ptr is provided, then *target_len_ptr must be its length + * (excluding space for NUL), otherwise we will compute the target_len_ptr + * length and may return a new allocation in *target_ptr if target_ptr is + * provided. */ + if (target_ptr == NULL || *target_ptr == NULL) { + target_len = uv_utf16_length_as_wtf8(w_source_ptr, w_source_len); + if (target_len_ptr != NULL) + *target_len_ptr = target_len; + } else { + target_len = *target_len_ptr; + } + + if (target_ptr == NULL) + return 0; + + if (*target_ptr == NULL) { + target = uv__malloc(target_len + 1); + if (target == NULL) { + return UV_ENOMEM; + } + *target_ptr = target; + } else { + target = *target_ptr; + } + + target_end = target + target_len; + + while (target != target_end && w_source_len) { + code_point = uv__get_surrogate_value(w_source_ptr, w_source_len); + /* Can be invalid UTF-8 but must be valid WTF-8. */ + assert(code_point >= 0); + if (w_source_len < 0 && code_point == 0) { + w_source_len = 0; + break; + } + if (code_point < 0x80) { + *target++ = code_point; + } else if (code_point < 0x800) { + *target++ = 0xC0 | (code_point >> 6); + if (target == target_end) + break; + *target++ = 0x80 | (code_point & 0x3F); + } else if (code_point < 0x10000) { + *target++ = 0xE0 | (code_point >> 12); + if (target == target_end) + break; + *target++ = 0x80 | ((code_point >> 6) & 0x3F); + if (target == target_end) + break; + *target++ = 0x80 | (code_point & 0x3F); + } else { + *target++ = 0xF0 | (code_point >> 18); + if (target == target_end) + break; + *target++ = 0x80 | ((code_point >> 12) & 0x3F); + if (target == target_end) + break; + *target++ = 0x80 | ((code_point >> 6) & 0x3F); + if (target == target_end) + break; + *target++ = 0x80 | (code_point & 0x3F); + /* uv__get_surrogate_value consumed 2 input characters */ + w_source_ptr++; + if (w_source_len > 0) + w_source_len--; + } + target_len = target - *target_ptr; + w_source_ptr++; + if (w_source_len > 0) + w_source_len--; + } + + if (target != target_end && target_len_ptr != NULL) + /* Did not fill all of the provided buffer, so update the target_len_ptr + * output with the space used. */ + *target_len_ptr = target - *target_ptr; + + /* Check if input fit into target exactly. */ + if (w_source_len < 0 && target == target_end && w_source_ptr[0] == 0) + w_source_len = 0; + + *target++ = '\0'; + + /* Characters remained after filling the buffer, compute the remaining length now. */ + if (w_source_len) { + if (target_len_ptr != NULL) + *target_len_ptr = target_len + uv_utf16_length_as_wtf8(w_source_ptr, w_source_len); + return UV_ENOBUFS; + } + + return 0; +}
diff --git a/Utilities/cmlibuv/src/idna.h b/Utilities/cmlibuv/src/idna.h index 8e0c592..ea6b4df 100644 --- a/Utilities/cmlibuv/src/idna.h +++ b/Utilities/cmlibuv/src/idna.h
@@ -1,4 +1,4 @@ -/* Copyright (c) 2011, 2018 Ben Noordhuis <info@bnoordhuis.nl> +/* Copyright libuv contributors. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -26,6 +26,6 @@ * is the number of bytes written to |d|, including the trailing nul byte. * A return value < 0 is a libuv error code. |s| and |d| can not overlap. */ -long uv__idna_toascii(const char* s, const char* se, char* d, char* de); +ssize_t uv__idna_toascii(const char* s, const char* se, char* d, char* de); #endif /* UV_SRC_IDNA_H_ */
diff --git a/Utilities/cmlibuv/src/inet.c b/Utilities/cmlibuv/src/inet.c index 384c0f7..8c890dd 100644 --- a/Utilities/cmlibuv/src/inet.c +++ b/Utilities/cmlibuv/src/inet.c
@@ -20,12 +20,7 @@ #include <stdio.h> #include <string.h> - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include <stdint.h> -#endif +#include <stdint.h> #define UV__INET_ADDRSTRLEN 16 #define UV__INET6_ADDRSTRLEN 46 @@ -135,7 +130,7 @@ tp += strlen(tp); break; } - tp += sprintf(tp, "%x", words[i]); + tp += snprintf(tp, sizeof tmp - (tp - tmp), "%x", words[i]); } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words))
diff --git a/Utilities/cmlibuv/src/queue.h b/Utilities/cmlibuv/src/queue.h index ff3540a..5f8489e 100644 --- a/Utilities/cmlibuv/src/queue.h +++ b/Utilities/cmlibuv/src/queue.h
@@ -18,91 +18,73 @@ #include <stddef.h> -typedef void *QUEUE[2]; +#define uv__queue_data(pointer, type, field) \ + ((type*) ((char*) (pointer) - offsetof(type, field))) -/* Private macros. */ -#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) -#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) -#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q))) -#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q))) +#define uv__queue_foreach(q, h) \ + for ((q) = (h)->next; (q) != (h); (q) = (q)->next) -/* Public macros. */ -#define QUEUE_DATA(ptr, type, field) \ - ((type *) ((char *) (ptr) - offsetof(type, field))) +static inline void uv__queue_init(struct uv__queue* q) { + q->next = q; + q->prev = q; +} -/* Important note: mutating the list while QUEUE_FOREACH is - * iterating over its elements results in undefined behavior. - */ -#define QUEUE_FOREACH(q, h) \ - for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) +static inline int uv__queue_empty(const struct uv__queue* q) { + return q == q->next; +} -#define QUEUE_EMPTY(q) \ - ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) +static inline struct uv__queue* uv__queue_head(const struct uv__queue* q) { + return q->next; +} -#define QUEUE_HEAD(q) \ - (QUEUE_NEXT(q)) +static inline struct uv__queue* uv__queue_next(const struct uv__queue* q) { + return q->next; +} -#define QUEUE_INIT(q) \ - do { \ - QUEUE_NEXT(q) = (q); \ - QUEUE_PREV(q) = (q); \ - } \ - while (0) +static inline void uv__queue_add(struct uv__queue* h, struct uv__queue* n) { + h->prev->next = n->next; + n->next->prev = h->prev; + h->prev = n->prev; + h->prev->next = h; +} -#define QUEUE_ADD(h, n) \ - do { \ - QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \ - QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \ - QUEUE_PREV(h) = QUEUE_PREV(n); \ - QUEUE_PREV_NEXT(h) = (h); \ - } \ - while (0) +static inline void uv__queue_split(struct uv__queue* h, + struct uv__queue* q, + struct uv__queue* n) { + n->prev = h->prev; + n->prev->next = n; + n->next = q; + h->prev = q->prev; + h->prev->next = h; + q->prev = n; +} -#define QUEUE_SPLIT(h, q, n) \ - do { \ - QUEUE_PREV(n) = QUEUE_PREV(h); \ - QUEUE_PREV_NEXT(n) = (n); \ - QUEUE_NEXT(n) = (q); \ - QUEUE_PREV(h) = QUEUE_PREV(q); \ - QUEUE_PREV_NEXT(h) = (h); \ - QUEUE_PREV(q) = (n); \ - } \ - while (0) +static inline void uv__queue_move(struct uv__queue* h, struct uv__queue* n) { + if (uv__queue_empty(h)) + uv__queue_init(n); + else + uv__queue_split(h, h->next, n); +} -#define QUEUE_MOVE(h, n) \ - do { \ - if (QUEUE_EMPTY(h)) \ - QUEUE_INIT(n); \ - else { \ - QUEUE* q = QUEUE_HEAD(h); \ - QUEUE_SPLIT(h, q, n); \ - } \ - } \ - while (0) +static inline void uv__queue_insert_head(struct uv__queue* h, + struct uv__queue* q) { + q->next = h->next; + q->prev = h; + q->next->prev = q; + h->next = q; +} -#define QUEUE_INSERT_HEAD(h, q) \ - do { \ - QUEUE_NEXT(q) = QUEUE_NEXT(h); \ - QUEUE_PREV(q) = (h); \ - QUEUE_NEXT_PREV(q) = (q); \ - QUEUE_NEXT(h) = (q); \ - } \ - while (0) +static inline void uv__queue_insert_tail(struct uv__queue* h, + struct uv__queue* q) { + q->next = h; + q->prev = h->prev; + q->prev->next = q; + h->prev = q; +} -#define QUEUE_INSERT_TAIL(h, q) \ - do { \ - QUEUE_NEXT(q) = (h); \ - QUEUE_PREV(q) = QUEUE_PREV(h); \ - QUEUE_PREV_NEXT(q) = (q); \ - QUEUE_PREV(h) = (q); \ - } \ - while (0) - -#define QUEUE_REMOVE(q) \ - do { \ - QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \ - QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \ - } \ - while (0) +static inline void uv__queue_remove(struct uv__queue* q) { + q->prev->next = q->next; + q->next->prev = q->prev; +} #endif /* QUEUE_H_ */
diff --git a/Utilities/cmlibuv/src/random.c b/Utilities/cmlibuv/src/random.c index e75f77d..402678c 100644 --- a/Utilities/cmlibuv/src/random.c +++ b/Utilities/cmlibuv/src/random.c
@@ -61,7 +61,7 @@ # endif #elif defined(_WIN32) uv__once_init(); - rc = uv__random_rtlgenrandom(buf, buflen); + rc = uv__random_winrandom(buf, buflen); #else rc = uv__random_devurandom(buf, buflen); #endif @@ -82,7 +82,7 @@ uv_random_t* req; req = container_of(w, uv_random_t, work_req); - uv__req_unregister(req->loop, req); + uv__req_unregister(req->loop); if (status == 0) status = req->status;
diff --git a/Utilities/cmlibuv/src/thread-common.c b/Utilities/cmlibuv/src/thread-common.c new file mode 100644 index 0000000..c67c0a7 --- /dev/null +++ b/Utilities/cmlibuv/src/thread-common.c
@@ -0,0 +1,175 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "uv-common.h" + +#include <stdlib.h> +#ifndef _WIN32 +#include <pthread.h> +#endif + +#if defined(PTHREAD_BARRIER_SERIAL_THREAD) +STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t)); +#endif + +/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */ +#if defined(_AIX) || \ + defined(__OpenBSD__) || \ + !defined(PTHREAD_BARRIER_SERIAL_THREAD) +int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { + int rc; +#ifdef _WIN32 + uv_barrier_t* b; + b = barrier; + + if (barrier == NULL || count == 0) + return UV_EINVAL; +#else + struct _uv_barrier* b; + + if (barrier == NULL || count == 0) + return UV_EINVAL; + + b = uv__malloc(sizeof(*b)); + if (b == NULL) + return UV_ENOMEM; +#endif + + b->in = 0; + b->out = 0; + b->threshold = count; + + rc = uv_mutex_init(&b->mutex); + if (rc != 0) + goto error2; + + /* TODO(vjnash): remove these uv_cond_t casts in v2. */ + rc = uv_cond_init((uv_cond_t*) &b->cond); + if (rc != 0) + goto error; + +#ifndef _WIN32 + barrier->b = b; +#endif + return 0; + +error: + uv_mutex_destroy(&b->mutex); +error2: +#ifndef _WIN32 + uv__free(b); +#endif + return rc; +} + + +int uv_barrier_wait(uv_barrier_t* barrier) { + int last; +#ifdef _WIN32 + uv_barrier_t* b; + b = barrier; +#else + struct _uv_barrier* b; + + if (barrier == NULL || barrier->b == NULL) + return UV_EINVAL; + + b = barrier->b; +#endif + + uv_mutex_lock(&b->mutex); + + while (b->out != 0) + uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex); + + if (++b->in == b->threshold) { + b->in = 0; + b->out = b->threshold; + uv_cond_broadcast((uv_cond_t*) &b->cond); + } else { + do + uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex); + while (b->in != 0); + } + + last = (--b->out == 0); + if (last) + uv_cond_broadcast((uv_cond_t*) &b->cond); + + uv_mutex_unlock(&b->mutex); + return last; +} + + +void uv_barrier_destroy(uv_barrier_t* barrier) { +#ifdef _WIN32 + uv_barrier_t* b; + b = barrier; +#else + struct _uv_barrier* b; + b = barrier->b; +#endif + + uv_mutex_lock(&b->mutex); + + assert(b->in == 0); + while (b->out != 0) + uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex); + + if (b->in != 0) + abort(); + + uv_mutex_unlock(&b->mutex); + uv_mutex_destroy(&b->mutex); + uv_cond_destroy((uv_cond_t*) &b->cond); + +#ifndef _WIN32 + uv__free(barrier->b); + barrier->b = NULL; +#endif +} + +#else + +int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { + return UV__ERR(pthread_barrier_init(barrier, NULL, count)); +} + + +int uv_barrier_wait(uv_barrier_t* barrier) { + int rc; + + rc = pthread_barrier_wait(barrier); + if (rc != 0) + if (rc != PTHREAD_BARRIER_SERIAL_THREAD) + abort(); + + return rc == PTHREAD_BARRIER_SERIAL_THREAD; +} + + +void uv_barrier_destroy(uv_barrier_t* barrier) { + if (pthread_barrier_destroy(barrier)) + abort(); +} + +#endif
diff --git a/Utilities/cmlibuv/src/threadpool.c b/Utilities/cmlibuv/src/threadpool.c index e804c7c..2a129a5 100644 --- a/Utilities/cmlibuv/src/threadpool.c +++ b/Utilities/cmlibuv/src/threadpool.c
@@ -37,10 +37,10 @@ static unsigned int nthreads; static uv_thread_t* threads; static uv_thread_t default_threads[4]; -static QUEUE exit_message; -static QUEUE wq; -static QUEUE run_slow_work_message; -static QUEUE slow_io_pending_wq; +static struct uv__queue exit_message; +static struct uv__queue wq; +static struct uv__queue run_slow_work_message; +static struct uv__queue slow_io_pending_wq; static unsigned int slow_work_thread_threshold(void) { return (nthreads + 1) / 2; @@ -56,9 +56,10 @@ */ static void worker(void* arg) { struct uv__work* w; - QUEUE* q; + struct uv__queue* q; int is_slow_work; + uv_thread_setname("libuv-worker"); uv_sem_post((uv_sem_t*) arg); arg = NULL; @@ -68,49 +69,49 @@ /* Keep waiting while either no work is present or only slow I/O and we're at the threshold for that. */ - while (QUEUE_EMPTY(&wq) || - (QUEUE_HEAD(&wq) == &run_slow_work_message && - QUEUE_NEXT(&run_slow_work_message) == &wq && + while (uv__queue_empty(&wq) || + (uv__queue_head(&wq) == &run_slow_work_message && + uv__queue_next(&run_slow_work_message) == &wq && slow_io_work_running >= slow_work_thread_threshold())) { idle_threads += 1; uv_cond_wait(&cond, &mutex); idle_threads -= 1; } - q = QUEUE_HEAD(&wq); + q = uv__queue_head(&wq); if (q == &exit_message) { uv_cond_signal(&cond); uv_mutex_unlock(&mutex); break; } - QUEUE_REMOVE(q); - QUEUE_INIT(q); /* Signal uv_cancel() that the work req is executing. */ + uv__queue_remove(q); + uv__queue_init(q); /* Signal uv_cancel() that the work req is executing. */ is_slow_work = 0; if (q == &run_slow_work_message) { /* If we're at the slow I/O threshold, re-schedule until after all other work in the queue is done. */ if (slow_io_work_running >= slow_work_thread_threshold()) { - QUEUE_INSERT_TAIL(&wq, q); + uv__queue_insert_tail(&wq, q); continue; } /* If we encountered a request to run slow I/O work but there is none to run, that means it's cancelled => Start over. */ - if (QUEUE_EMPTY(&slow_io_pending_wq)) + if (uv__queue_empty(&slow_io_pending_wq)) continue; is_slow_work = 1; slow_io_work_running++; - q = QUEUE_HEAD(&slow_io_pending_wq); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + q = uv__queue_head(&slow_io_pending_wq); + uv__queue_remove(q); + uv__queue_init(q); /* If there is more slow I/O work, schedule it to be run as well. */ - if (!QUEUE_EMPTY(&slow_io_pending_wq)) { - QUEUE_INSERT_TAIL(&wq, &run_slow_work_message); + if (!uv__queue_empty(&slow_io_pending_wq)) { + uv__queue_insert_tail(&wq, &run_slow_work_message); if (idle_threads > 0) uv_cond_signal(&cond); } @@ -118,13 +119,13 @@ uv_mutex_unlock(&mutex); - w = QUEUE_DATA(q, struct uv__work, wq); + w = uv__queue_data(q, struct uv__work, wq); w->work(w); uv_mutex_lock(&w->loop->wq_mutex); w->work = NULL; /* Signal uv_cancel() that the work req is done executing. */ - QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq); + uv__queue_insert_tail(&w->loop->wq, &w->wq); uv_async_send(&w->loop->wq_async); uv_mutex_unlock(&w->loop->wq_mutex); @@ -139,12 +140,12 @@ } -static void post(QUEUE* q, enum uv__work_kind kind) { +static void post(struct uv__queue* q, enum uv__work_kind kind) { uv_mutex_lock(&mutex); if (kind == UV__WORK_SLOW_IO) { /* Insert into a separate queue. */ - QUEUE_INSERT_TAIL(&slow_io_pending_wq, q); - if (!QUEUE_EMPTY(&run_slow_work_message)) { + uv__queue_insert_tail(&slow_io_pending_wq, q); + if (!uv__queue_empty(&run_slow_work_message)) { /* Running slow I/O tasks is already scheduled => Nothing to do here. The worker that runs said other task will schedule this one as well. */ uv_mutex_unlock(&mutex); @@ -153,7 +154,7 @@ q = &run_slow_work_message; } - QUEUE_INSERT_TAIL(&wq, q); + uv__queue_insert_tail(&wq, q); if (idle_threads > 0) uv_cond_signal(&cond); uv_mutex_unlock(&mutex); @@ -191,12 +192,23 @@ static void init_threads(void) { + uv_thread_options_t config; unsigned int i; + size_t buflen; + char buf[16]; const char* val; + int err; + uv_sem_t sem; nthreads = ARRAY_SIZE(default_threads); - val = getenv("UV_THREADPOOL_SIZE"); + + buflen = ARRAY_SIZE(buf); + err = uv_os_getenv("UV_THREADPOOL_SIZE", buf, &buflen); + val = NULL; + if (err == 0) + val = buf; + if (val != NULL) nthreads = atoi(val); if (nthreads == 0) @@ -219,15 +231,18 @@ if (uv_mutex_init(&mutex)) abort(); - QUEUE_INIT(&wq); - QUEUE_INIT(&slow_io_pending_wq); - QUEUE_INIT(&run_slow_work_message); + uv__queue_init(&wq); + uv__queue_init(&slow_io_pending_wq); + uv__queue_init(&run_slow_work_message); if (uv_sem_init(&sem, 0)) abort(); + config.flags = UV_THREAD_HAS_STACK_SIZE; + config.stack_size = 8u << 20; /* 8 MB */ + for (i = 0; i < nthreads; i++) - if (uv_thread_create(threads + i, worker, &sem)) + if (uv_thread_create_ex(threads + i, &config, worker, &sem)) abort(); for (i = 0; i < nthreads; i++) @@ -271,15 +286,19 @@ } +/* TODO(bnoordhuis) teach libuv how to cancel file operations + * that go through io_uring instead of the thread pool. + */ static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) { int cancelled; + uv_once(&once, init_once); /* Ensure |mutex| is initialized. */ uv_mutex_lock(&mutex); uv_mutex_lock(&w->loop->wq_mutex); - cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL; + cancelled = !uv__queue_empty(&w->wq) && w->work != NULL; if (cancelled) - QUEUE_REMOVE(&w->wq); + uv__queue_remove(&w->wq); uv_mutex_unlock(&w->loop->wq_mutex); uv_mutex_unlock(&mutex); @@ -289,7 +308,7 @@ w->work = uv__cancelled; uv_mutex_lock(&loop->wq_mutex); - QUEUE_INSERT_TAIL(&loop->wq, &w->wq); + uv__queue_insert_tail(&loop->wq, &w->wq); uv_async_send(&loop->wq_async); uv_mutex_unlock(&loop->wq_mutex); @@ -300,22 +319,39 @@ void uv__work_done(uv_async_t* handle) { struct uv__work* w; uv_loop_t* loop; - QUEUE* q; - QUEUE wq; + struct uv__queue* q; + struct uv__queue wq; int err; + int nevents; loop = container_of(handle, uv_loop_t, wq_async); uv_mutex_lock(&loop->wq_mutex); - QUEUE_MOVE(&loop->wq, &wq); + uv__queue_move(&loop->wq, &wq); uv_mutex_unlock(&loop->wq_mutex); - while (!QUEUE_EMPTY(&wq)) { - q = QUEUE_HEAD(&wq); - QUEUE_REMOVE(q); + nevents = 0; + + while (!uv__queue_empty(&wq)) { + q = uv__queue_head(&wq); + uv__queue_remove(q); w = container_of(q, struct uv__work, wq); err = (w->work == uv__cancelled) ? UV_ECANCELED : 0; w->done(w, err); + nevents++; + } + + /* This check accomplishes 2 things: + * 1. Even if the queue was empty, the call to uv__work_done() should count + * as an event. Which will have been added by the event loop when + * calling this callback. + * 2. Prevents accidental wrap around in case nevents == 0 events == 0. + */ + if (nevents > 1) { + /* Subtract 1 to counter the call to uv__work_done(). */ + uv__metrics_inc_events(loop, nevents - 1); + if (uv__get_internal_fields(loop)->current_timeout == 0) + uv__metrics_inc_events_waiting(loop, nevents - 1); } } @@ -331,7 +367,7 @@ uv_work_t* req; req = container_of(w, uv_work_t, work_req); - uv__req_unregister(req->loop, req); + uv__req_unregister(req->loop); if (req->after_work_cb == NULL) return;
diff --git a/Utilities/cmlibuv/src/timer.c b/Utilities/cmlibuv/src/timer.c index bc680e7..7cfca3b 100644 --- a/Utilities/cmlibuv/src/timer.c +++ b/Utilities/cmlibuv/src/timer.c
@@ -22,7 +22,6 @@ #include "uv-common.h" #include "heap-inl.h" -#include <assert.h> #include <limits.h> @@ -40,8 +39,8 @@ const uv_timer_t* a; const uv_timer_t* b; - a = container_of(ha, uv_timer_t, heap_node); - b = container_of(hb, uv_timer_t, heap_node); + a = container_of(ha, uv_timer_t, node.heap); + b = container_of(hb, uv_timer_t, node.heap); if (a->timeout < b->timeout) return 1; @@ -60,6 +59,7 @@ handle->timer_cb = NULL; handle->timeout = 0; handle->repeat = 0; + uv__queue_init(&handle->node.queue); return 0; } @@ -73,8 +73,7 @@ if (uv__is_closing(handle) || cb == NULL) return UV_EINVAL; - if (uv__is_active(handle)) - uv_timer_stop(handle); + uv_timer_stop(handle); clamped_timeout = handle->loop->time + timeout; if (clamped_timeout < timeout) @@ -87,7 +86,7 @@ handle->start_id = handle->loop->timer_counter++; heap_insert(timer_heap(handle->loop), - (struct heap_node*) &handle->heap_node, + (struct heap_node*) &handle->node.heap, timer_less_than); uv__handle_start(handle); @@ -96,14 +95,16 @@ int uv_timer_stop(uv_timer_t* handle) { - if (!uv__is_active(handle)) - return 0; + if (uv__is_active(handle)) { + heap_remove(timer_heap(handle->loop), + (struct heap_node*) &handle->node.heap, + timer_less_than); + uv__handle_stop(handle); + } else { + uv__queue_remove(&handle->node.queue); + } - heap_remove(timer_heap(handle->loop), - (struct heap_node*) &handle->heap_node, - timer_less_than); - uv__handle_stop(handle); - + uv__queue_init(&handle->node.queue); return 0; } @@ -148,7 +149,7 @@ if (heap_node == NULL) return -1; /* block indefinitely */ - handle = container_of(heap_node, uv_timer_t, heap_node); + handle = container_of(heap_node, uv_timer_t, node.heap); if (handle->timeout <= loop->time) return 0; @@ -163,17 +164,30 @@ void uv__run_timers(uv_loop_t* loop) { struct heap_node* heap_node; uv_timer_t* handle; + struct uv__queue* queue_node; + struct uv__queue ready_queue; + + uv__queue_init(&ready_queue); for (;;) { heap_node = heap_min(timer_heap(loop)); if (heap_node == NULL) break; - handle = container_of(heap_node, uv_timer_t, heap_node); + handle = container_of(heap_node, uv_timer_t, node.heap); if (handle->timeout > loop->time) break; uv_timer_stop(handle); + uv__queue_insert_tail(&ready_queue, &handle->node.queue); + } + + while (!uv__queue_empty(&ready_queue)) { + queue_node = uv__queue_head(&ready_queue); + uv__queue_remove(queue_node); + uv__queue_init(queue_node); + handle = container_of(queue_node, uv_timer_t, node.queue); + uv_timer_again(handle); handle->timer_cb(handle); }
diff --git a/Utilities/cmlibuv/src/unix/aix.c b/Utilities/cmlibuv/src/unix/aix.c index 6a013d4..30a2a89 100644 --- a/Utilities/cmlibuv/src/unix/aix.c +++ b/Utilities/cmlibuv/src/unix/aix.c
@@ -131,11 +131,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { + uv__loop_internal_fields_t* lfields; struct pollfd events[1024]; struct pollfd pqry; struct pollfd* pe; struct poll_ctl pc; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; uint64_t base; uint64_t diff; @@ -150,16 +151,18 @@ int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + lfields = uv__get_internal_fields(loop); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); + + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); @@ -217,7 +220,7 @@ base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -226,22 +229,12 @@ } for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - + uv__io_poll_prepare(loop, NULL, timeout); nfds = pollset_poll(loop->backend_fd, events, ARRAY_SIZE(events), timeout); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); + uv__io_poll_check(loop, NULL); if (nfds == 0) { if (reset_timeout != 0) { @@ -315,20 +308,22 @@ have_signals = 1; } else { uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->revents); + uv__io_cb(loop, w, pe->revents); } nevents++; } + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + uv__signal_event(loop, &loop->signal_io_watcher, POLLIN); } loop->watchers[loop->nwatchers] = NULL; @@ -389,6 +384,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { perfstat_cpu_total_t ps_total; int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); @@ -425,7 +425,7 @@ static int uv__path_is_a_directory(char* filename) { struct stat statbuf; - if (stat(filename, &statbuf) < 0) + if (uv__stat(filename, &statbuf) < 0) return -1; /* failed: not a directory, assume it is a file */ if (statbuf.st_type == VDIR) @@ -616,7 +616,7 @@ while(n > 0) { *p = strchr(*p, '\n'); - if (!p) + if (!*p) return lines; (*p)++; @@ -700,7 +700,7 @@ /* This is the internal callback */ -static void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) { +void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) { char result_data[RDWR_BUF_SIZE]; int bytes, rc = 0; uv_fs_event_t* handle; @@ -751,7 +751,11 @@ handle->cb(handle, fname, events, 0); } -#endif +#else /* !HAVE_SYS_AHAFS_EVPRODS_H */ +void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) { + /* Stub function to satisfy the linker. */ +} +#endif /* HAVE_SYS_AHAFS_EVPRODS_H */ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { @@ -812,7 +816,7 @@ /* Setup/Initialize all the libuv routines */ uv__handle_start(handle); - uv__io_init(&handle->event_watcher, uv__ahafs_event, fd); + uv__io_init(&handle->event_watcher, UV__AHAFS_EVENT, fd); handle->path = uv__strdup(filename); handle->cb = cb; handle->dir_filename = NULL; @@ -1104,6 +1108,8 @@ struct ifreq *ifr, *p, flg; struct in6_ifreq if6; struct sockaddr_dl* sa_addr; + size_t namelen; + char* name; ifc.ifc_req = NULL; sock6fd = -1; @@ -1140,6 +1146,7 @@ #define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) /* Count all up and running ipv4/ipv6 addresses */ + namelen = 0; ifr = ifc.ifc_req; while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { p = ifr; @@ -1159,6 +1166,7 @@ if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) continue; + namelen += strlen(p->ifr_name) + 1; (*count)++; } @@ -1166,11 +1174,12 @@ goto cleanup; /* Alloc the return interface structs */ - *addresses = uv__calloc(*count, sizeof(**addresses)); - if (!(*addresses)) { + *addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen); + if (*addresses == NULL) { r = UV_ENOMEM; goto cleanup; } + name = (char*) &(*addresses)[*count]; address = *addresses; ifr = ifc.ifc_req; @@ -1194,7 +1203,9 @@ /* All conditions above must match count loop */ - address->name = uv__strdup(p->ifr_name); + namelen = strlen(p->ifr_name) + 1; + address->name = memcpy(name, p->ifr_name, namelen); + name += namelen; if (inet6) address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); @@ -1265,18 +1276,6 @@ } -void uv_free_interface_addresses(uv_interface_address_t* addresses, - int count) { - int i; - - for (i = 0; i < count; ++i) { - uv__free(addresses[i].name); - } - - uv__free(addresses); -} - - void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct pollfd* events; uintptr_t i;
diff --git a/Utilities/cmlibuv/src/unix/async.c b/Utilities/cmlibuv/src/unix/async.c index e1805c3..f7995fc 100644 --- a/Utilities/cmlibuv/src/unix/async.c +++ b/Utilities/cmlibuv/src/unix/async.c
@@ -24,9 +24,9 @@ #include "uv.h" #include "internal.h" -#include "atomic-ops.h" #include <errno.h> +#include <stdatomic.h> #include <stdio.h> /* snprintf() */ #include <assert.h> #include <stdlib.h> @@ -38,8 +38,37 @@ #include <sys/eventfd.h> #endif +#if UV__KQUEUE_EVFILT_USER +static uv_once_t kqueue_runtime_detection_guard = UV_ONCE_INIT; +static int kqueue_evfilt_user_support = 1; + + +static void uv__kqueue_runtime_detection(void) { + int kq; + struct kevent ev[2]; + struct timespec timeout = {0, 0}; + + /* Perform the runtime detection to ensure that kqueue with + * EVFILT_USER actually works. */ + kq = kqueue(); + EV_SET(ev, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER, + EV_ADD | EV_CLEAR, 0, 0, 0); + EV_SET(ev + 1, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER, + 0, NOTE_TRIGGER, 0, 0); + if (kevent(kq, ev, 2, ev, 1, &timeout) < 1 || + ev[0].filter != EVFILT_USER || + ev[0].ident != UV__KQUEUE_EVFILT_USER_IDENT || + ev[0].flags & EV_ERROR) + /* If we wind up here, we can assume that EVFILT_USER is defined but + * broken on the current system. */ + kqueue_evfilt_user_support = 0; + uv__close(kq); +} +#endif + static void uv__async_send(uv_loop_t* loop); static int uv__async_start(uv_loop_t* loop); +static void uv__cpu_relax(void); int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { @@ -52,8 +81,9 @@ uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC); handle->async_cb = async_cb; handle->pending = 0; + handle->u.fd = 0; /* This will be used as a busy flag. */ - QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue); + uv__queue_insert_tail(&loop->async_handles, &handle->queue); uv__handle_start(handle); return 0; @@ -61,46 +91,54 @@ int uv_async_send(uv_async_t* handle) { + _Atomic int* pending; + _Atomic int* busy; + + pending = (_Atomic int*) &handle->pending; + busy = (_Atomic int*) &handle->u.fd; + /* Do a cheap read first. */ - if (ACCESS_ONCE(int, handle->pending) != 0) + if (atomic_load_explicit(pending, memory_order_relaxed) != 0) return 0; - /* Tell the other thread we're busy with the handle. */ - if (cmpxchgi(&handle->pending, 0, 1) != 0) - return 0; + /* Set the loop to busy. */ + atomic_fetch_add(busy, 1); /* Wake up the other thread's event loop. */ - uv__async_send(handle->loop); + if (atomic_exchange(pending, 1) == 0) + uv__async_send(handle->loop); - /* Tell the other thread we're done. */ - if (cmpxchgi(&handle->pending, 1, 2) != 1) - abort(); + /* Set the loop to not-busy. */ + atomic_fetch_add(busy, -1); return 0; } -/* Only call this from the event loop thread. */ -static int uv__async_spin(uv_async_t* handle) { +/* Wait for the busy flag to clear before closing. + * Only call this from the event loop thread. */ +static void uv__async_spin(uv_async_t* handle) { + _Atomic int* pending; + _Atomic int* busy; int i; - int rc; + + pending = (_Atomic int*) &handle->pending; + busy = (_Atomic int*) &handle->u.fd; + + /* Set the pending flag first, so no new events will be added by other + * threads after this function returns. */ + atomic_store(pending, 1); for (;;) { - /* 997 is not completely chosen at random. It's a prime number, acyclical - * by nature, and should therefore hopefully dampen sympathetic resonance. + /* 997 is not completely chosen at random. It's a prime number, acyclic by + * nature, and should therefore hopefully dampen sympathetic resonance. */ for (i = 0; i < 997; i++) { - /* rc=0 -- handle is not pending. - * rc=1 -- handle is pending, other thread is still working with it. - * rc=2 -- handle is pending, other thread is done. - */ - rc = cmpxchgi(&handle->pending, 2, 0); - - if (rc != 1) - return rc; + if (atomic_load(busy) == 0) + return; /* Other thread is busy with this handle, spin until it's done. */ - cpu_relax(); + uv__cpu_relax(); } /* Yield the CPU. We may have preempted the other thread while it's @@ -114,21 +152,26 @@ void uv__async_close(uv_async_t* handle) { uv__async_spin(handle); - QUEUE_REMOVE(&handle->queue); + uv__queue_remove(&handle->queue); uv__handle_stop(handle); } -static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { char buf[1024]; ssize_t r; - QUEUE queue; - QUEUE* q; + struct uv__queue queue; + struct uv__queue* q; uv_async_t* h; + _Atomic int *pending; assert(w == &loop->async_io_watcher); +#if UV__KQUEUE_EVFILT_USER + for (;!kqueue_evfilt_user_support;) { +#else for (;;) { +#endif r = read(w->fd, buf, sizeof(buf)); if (r == sizeof(buf)) @@ -146,16 +189,18 @@ abort(); } - QUEUE_MOVE(&loop->async_handles, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - h = QUEUE_DATA(q, uv_async_t, queue); + uv__queue_move(&loop->async_handles, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_async_t, queue); - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&loop->async_handles, q); + uv__queue_remove(q); + uv__queue_insert_tail(&loop->async_handles, q); - if (0 == uv__async_spin(h)) - continue; /* Not pending. */ + /* Atomically fetch and clear pending flag */ + pending = (_Atomic int*) &h->pending; + if (atomic_exchange(pending, 0) == 0) + continue; if (h->async_cb == NULL) continue; @@ -182,6 +227,17 @@ len = sizeof(val); fd = loop->async_io_watcher.fd; /* eventfd */ } +#elif UV__KQUEUE_EVFILT_USER + struct kevent ev; + + if (kqueue_evfilt_user_support) { + fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */ + EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0); + r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL); + if (r == 0) + return; + abort(); + } #endif do @@ -202,6 +258,9 @@ static int uv__async_start(uv_loop_t* loop) { int pipefd[2]; int err; +#if UV__KQUEUE_EVFILT_USER + struct kevent ev; +#endif if (loop->async_io_watcher.fd != -1) return 0; @@ -213,34 +272,85 @@ pipefd[0] = err; pipefd[1] = -1; +#elif UV__KQUEUE_EVFILT_USER + uv_once(&kqueue_runtime_detection_guard, uv__kqueue_runtime_detection); + if (kqueue_evfilt_user_support) { + /* In order not to break the generic pattern of I/O polling, a valid + * file descriptor is required to take up a room in loop->watchers, + * thus we create one for that, but this fd will not be actually used, + * it's just a placeholder and magic number which is going to be closed + * during the cleanup, as other FDs. */ + err = uv__open_cloexec("/", O_RDONLY); + if (err < 0) + return err; + + pipefd[0] = err; + pipefd[1] = -1; + + /* When using EVFILT_USER event to wake up the kqueue, this event must be + * registered beforehand. Otherwise, calling kevent() to issue an + * unregistered EVFILT_USER event will get an ENOENT. + * Since uv__async_send() may happen before uv__io_poll() with multi-threads, + * we can't defer this registration of EVFILT_USER event as we did for other + * events, but must perform it right away. */ + EV_SET(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0); + err = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL); + if (err < 0) + return UV__ERR(errno); + } else { + err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); + if (err < 0) + return err; + } #else err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); if (err < 0) return err; #endif - uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]); - uv__io_start(loop, &loop->async_io_watcher, POLLIN); + err = uv__io_init_start(loop, &loop->async_io_watcher, UV__ASYNC_IO, + pipefd[0], POLLIN); + if (err < 0) { + uv__close(pipefd[0]); + if (pipefd[1] != -1) + uv__close(pipefd[1]); + return err; + } loop->async_wfd = pipefd[1]; +#if UV__KQUEUE_EVFILT_USER + /* Prevent the EVFILT_USER event from being added to kqueue redundantly + * and mistakenly later in uv__io_poll(). */ + if (kqueue_evfilt_user_support) + loop->async_io_watcher.events = loop->async_io_watcher.pevents; +#endif + return 0; } -int uv__async_fork(uv_loop_t* loop) { - if (loop->async_io_watcher.fd == -1) /* never started */ - return 0; - - uv__async_stop(loop); - - return uv__async_start(loop); -} - - void uv__async_stop(uv_loop_t* loop) { + struct uv__queue queue; + struct uv__queue* q; + uv_async_t* h; + if (loop->async_io_watcher.fd == -1) return; + /* Make sure no other thread is accessing the async handle fd after the loop + * cleanup. + */ + uv__queue_move(&loop->async_handles, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_async_t, queue); + + uv__queue_remove(q); + uv__queue_insert_tail(&loop->async_handles, q); + + uv__async_spin(h); + } + if (loop->async_wfd != -1) { if (loop->async_wfd != loop->async_io_watcher.fd) uv__close(loop->async_wfd); @@ -251,3 +361,60 @@ uv__close(loop->async_io_watcher.fd); loop->async_io_watcher.fd = -1; } + + +int uv__async_fork(uv_loop_t* loop) { + struct uv__queue queue; + struct uv__queue* q; + uv_async_t* h; + + if (loop->async_io_watcher.fd == -1) /* never started */ + return 0; + + uv__queue_move(&loop->async_handles, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_async_t, queue); + + uv__queue_remove(q); + uv__queue_insert_tail(&loop->async_handles, q); + + /* The state of any thread that set pending is now likely corrupt in this + * child because the user called fork, so just clear these flags and move + * on. Calling most libc functions after `fork` is declared to be undefined + * behavior anyways, unless async-signal-safe, for multithreaded programs + * like libuv, and nothing interesting in pthreads is async-signal-safe. + */ + h->pending = 0; + /* This is the busy flag, and we just abruptly lost all other threads. */ + h->u.fd = 0; + } + + /* Recreate these, since they still exist, but belong to the wrong pid now. */ + if (loop->async_wfd != -1) { + if (loop->async_wfd != loop->async_io_watcher.fd) + uv__close(loop->async_wfd); + loop->async_wfd = -1; + } + + uv__io_stop(loop, &loop->async_io_watcher, POLLIN); + uv__close(loop->async_io_watcher.fd); + loop->async_io_watcher.fd = -1; + + return uv__async_start(loop); +} + + +static void uv__cpu_relax(void) { +#if defined(__i386__) || defined(__x86_64__) + __asm__ __volatile__ ("rep; nop" ::: "memory"); /* a.k.a. PAUSE */ +#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) + __asm__ __volatile__ ("isb" ::: "memory"); +#elif (defined(__ppc__) || defined(__ppc64__)) && defined(__APPLE__) + __asm volatile ("" : : : "memory"); +#elif !defined(__APPLE__) && (defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__)) + __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory"); +#elif defined(__riscv) && __riscv_xlen == 64 + __asm__ volatile(".insn 0x0100000f" ::: "memory"); /* FENCE */ +#endif +}
diff --git a/Utilities/cmlibuv/src/unix/atomic-ops.h b/Utilities/cmlibuv/src/unix/atomic-ops.h deleted file mode 100644 index 2b58162..0000000 --- a/Utilities/cmlibuv/src/unix/atomic-ops.h +++ /dev/null
@@ -1,70 +0,0 @@ -/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UV_ATOMIC_OPS_H_ -#define UV_ATOMIC_OPS_H_ - -#include "internal.h" /* UV_UNUSED */ - -#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) -#include <atomic.h> -#endif - -UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); -UV_UNUSED(static void cpu_relax(void)); - -/* Prefer hand-rolled assembly over the gcc builtins because the latter also - * issue full memory barriers. - */ -UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { -#if defined(__i386__) || defined(__x86_64__) - int out; - __asm__ __volatile__ ("lock; cmpxchg %2, %1;" - : "=a" (out), "+m" (*(volatile int*) ptr) - : "r" (newval), "0" (oldval) - : "memory"); - return out; -#elif defined(_AIX) && defined(__ibmxl__) - /* FIXME: This is not actually atomic but XLClang 16.1 for AIX - does not provide __sync_val_compare_and_swap or an equivalent. - Its documentation suggests using C++11 atomics but this is C. */ - __compare_and_swap((volatile int*)ptr, &oldval, newval); - return oldval; -#elif defined(__MVS__) - /* Use hand-rolled assembly because codegen from builtin __plo_CSST results in - * a runtime bug. - */ - __asm(" cs %0,%2,%1 \n " : "+r"(oldval), "+m"(*ptr) : "r"(newval) :); - return oldval; -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) - return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval); -#else - return __sync_val_compare_and_swap(ptr, oldval, newval); -#endif -} - -UV_UNUSED(static void cpu_relax(void)) { -#if defined(__i386__) || defined(__x86_64__) - __asm__ __volatile__ ("rep; nop" ::: "memory"); /* a.k.a. PAUSE */ -#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) - __asm__ __volatile__ ("yield" ::: "memory"); -#elif (defined(__ppc__) || defined(__ppc64__)) && defined(__APPLE__) - __asm volatile ("" : : : "memory"); -#elif !defined(__APPLE__) && (defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__)) - __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory"); -#endif -} - -#endif /* UV_ATOMIC_OPS_H_ */
diff --git a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c index 11ca955..6829e2a 100644 --- a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c +++ b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c
@@ -65,13 +65,13 @@ return 0; } +/* TODO(bnoordhuis) share with linux.c */ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; struct ifaddrs* addrs; struct ifaddrs* ent; - uv_interface_address_t* address; -#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__) - int i; -#endif + size_t namelen; + char* name; *count = 0; *addresses = NULL; @@ -80,9 +80,11 @@ return UV__ERR(errno); /* Count the number of interfaces */ + namelen = 0; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) continue; + namelen += strlen(ent->ifa_name) + 1; (*count)++; } @@ -92,20 +94,22 @@ } /* Make sure the memory is initiallized to zero using calloc() */ - *addresses = uv__calloc(*count, sizeof(**addresses)); - + *addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen); if (*addresses == NULL) { freeifaddrs(addrs); return UV_ENOMEM; } + name = (char*) &(*addresses)[*count]; address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) continue; - address->name = uv__strdup(ent->ifa_name); + namelen = strlen(ent->ifa_name) + 1; + address->name = memcpy(name, ent->ifa_name, namelen); + name += namelen; if (ent->ifa_addr->sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); @@ -129,6 +133,8 @@ #if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__) /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + int i; + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) continue; @@ -149,15 +155,3 @@ return 0; } - - -void uv_free_interface_addresses(uv_interface_address_t* addresses, - int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(addresses[i].name); - } - - uv__free(addresses); -}
diff --git a/Utilities/cmlibuv/src/unix/bsd-proctitle.c b/Utilities/cmlibuv/src/unix/bsd-proctitle.c index b0c01e2..7616039 100644 --- a/Utilities/cmlibuv/src/unix/bsd-proctitle.c +++ b/Utilities/cmlibuv/src/unix/bsd-proctitle.c
@@ -29,7 +29,7 @@ static uv_mutex_t process_title_mutex; static uv_once_t process_title_mutex_once = UV_ONCE_INIT; static char* process_title; - +char* uv_saved_argv0; static void init_process_title_mutex_once(void) { if (uv_mutex_init(&process_title_mutex)) @@ -45,6 +45,7 @@ char** uv_setup_args(int argc, char** argv) { process_title = argc > 0 ? uv__strdup(argv[0]) : NULL; + uv_saved_argv0 = argc > 0 ? uv__strdup(argv[0]) : NULL; return argv; }
diff --git a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c index 394231d..2e2df8e 100644 --- a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c +++ b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c
@@ -1,6 +1,19 @@ #include "uv.h" #include "internal.h" +#ifndef uv__ahafs_event +void uv__ahafs_event(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +} +#endif + +void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +} + +#ifndef uv__inotify_read +void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +} +#endif + void uv__process_title_cleanup(void) { } @@ -11,12 +24,27 @@ return -EINVAL; } +void uv__tty_close(uv_tty_t* handle) { +} + +void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +} + void uv__udp_close(uv_udp_t* handle) { } void uv__udp_finish_close(uv_udp_t* handle) { } +#ifndef uv__fs_event +void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int events){} +#endif + +#ifndef uv__fs_event_read +void uv__fs_event_read(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +} +#endif + void uv__fs_poll_close(uv_fs_poll_t* handle) { } @@ -104,6 +132,15 @@ return -1; } +int uv__get_constrained_cpu(long long* quota) { + *quota = 0; + return -1; +} + +unsigned uv__kernel_version(void) { + return 0; +} + int uv__pipe2(int pipefd[2], int flags) { errno = ENOSYS; return -1; @@ -135,6 +172,9 @@ errno = ENOSYS; return -1; } + +void uv__statx_to_stat(const struct uv__statx* statxbuf, uv_stat_t* buf) { +} #endif #if defined(__linux__) || defined(__FreeBSD__) @@ -146,3 +186,9 @@ return -1; } #endif + +#if defined(_AIX) +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + return UV_ENOSYS; +} +#endif
diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c index 83e8b61..0f997a2 100644 --- a/Utilities/cmlibuv/src/unix/core.c +++ b/Utilities/cmlibuv/src/unix/core.c
@@ -41,19 +41,22 @@ #include <sys/uio.h> /* writev */ #include <sys/resource.h> /* getrusage */ #include <pwd.h> -#include <sched.h> +#include <grp.h> #include <sys/utsname.h> #include <sys/time.h> +#include <time.h> /* clock_gettime */ #ifdef __sun # include <sys/filio.h> -# include <sys/types.h> # include <sys/wait.h> #endif #if defined(__APPLE__) +# include <mach/mach.h> +# include <mach/thread_info.h> # include <sys/filio.h> -# endif /* defined(__APPLE__) */ +# include <sys/sysctl.h> +#endif /* defined(__APPLE__) */ #if defined(__APPLE__) && !TARGET_OS_IPHONE @@ -67,13 +70,14 @@ #if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__NetBSD__) || \ defined(__OpenBSD__) # include <sys/sysctl.h> # include <sys/filio.h> # include <sys/wait.h> +# include <sys/param.h> # if defined(__FreeBSD__) +# include <sys/cpuset.h> # define uv__accept4 accept4 # endif # if defined(__NetBSD__) @@ -81,11 +85,6 @@ # endif #endif -#if defined(__FreeBSD__) -# include <sys/param.h> -# include <sys/cpuset.h> -#endif - #if defined(__MVS__) # include <sys/ioctl.h> # include "zos-sys-info.h" @@ -94,9 +93,19 @@ #if defined(__linux__) # include <sched.h> # include <sys/syscall.h> +# define gettid() syscall(SYS_gettid) # define uv__accept4 accept4 #endif +#if defined(__FreeBSD__) +# include <sys/param.h> +# include <sys/cpuset.h> +#endif + +#if defined(__NetBSD__) +# include <sched.h> +#endif + #if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__) # include <sanitizer/linux_syscall_hooks.h> #endif @@ -113,6 +122,39 @@ STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len)); +/* https://github.com/libuv/libuv/issues/1674 */ +int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) { +#if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 + return UV_ENOSYS; +#else + struct timespec t; + int r; + + if (ts == NULL) + return UV_EFAULT; + + switch (clock_id) { + default: + return UV_EINVAL; + case UV_CLOCK_MONOTONIC: + r = clock_gettime(CLOCK_MONOTONIC, &t); + break; + case UV_CLOCK_REALTIME: + r = clock_gettime(CLOCK_REALTIME, &t); + break; + } + + if (r) + return UV__ERR(errno); + + ts->tv_sec = t.tv_sec; + ts->tv_nsec = t.tv_nsec; + + return 0; +#endif +} + + uint64_t uv_hrtime(void) { return uv__hrtime(UV_CLOCK_PRECISE); } @@ -130,7 +172,7 @@ break; case UV_TTY: - uv__stream_close((uv_stream_t*)handle); + uv__tty_close((uv_tty_t*)handle); break; case UV_TCP: @@ -237,11 +279,11 @@ int uv__getiovmax(void) { #if defined(IOV_MAX) return IOV_MAX; -#elif defined(_SC_IOV_MAX) - static int iovmax_cached = -1; +#elif defined(_SC_IOV_MAX) && !defined(__QNX__) + static _Atomic int iovmax_cached = -1; int iovmax; - iovmax = uv__load_relaxed(&iovmax_cached); + iovmax = atomic_load_explicit(&iovmax_cached, memory_order_relaxed); if (iovmax != -1) return iovmax; @@ -253,7 +295,7 @@ if (iovmax == -1) iovmax = 1; - uv__store_relaxed(&iovmax_cached, iovmax); + atomic_store_explicit(&iovmax_cached, iovmax, memory_order_relaxed); return iovmax; #else @@ -319,7 +361,7 @@ } uv__handle_unref(handle); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); if (handle->close_cb) { handle->close_cb(handle); @@ -355,7 +397,7 @@ static int uv__loop_alive(const uv_loop_t* loop) { return uv__has_active_handles(loop) || uv__has_active_reqs(loop) || - !QUEUE_EMPTY(&loop->pending_queue) || + !uv__queue_empty(&loop->pending_queue) || loop->closing_handles != NULL; } @@ -364,8 +406,8 @@ if (loop->stop_flag == 0 && /* uv__loop_alive(loop) && */ (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) && - QUEUE_EMPTY(&loop->pending_queue) && - QUEUE_EMPTY(&loop->idle_handles) && + uv__queue_empty(&loop->pending_queue) && + uv__queue_empty(&loop->idle_handles) && (loop->flags & UV_LOOP_REAP_CHILDREN) == 0 && loop->closing_handles == NULL) return uv__next_timeout(loop); @@ -374,7 +416,7 @@ int uv_backend_timeout(const uv_loop_t* loop) { - if (QUEUE_EMPTY(&loop->watcher_queue)) + if (uv__queue_empty(&loop->watcher_queue)) return uv__backend_timeout(loop); /* Need to call uv_run to update the backend fd state. */ return 0; @@ -395,12 +437,19 @@ if (!r) uv__update_time(loop); - while (r != 0 && loop->stop_flag == 0) { + /* Maintain backwards compatibility by processing timers before entering the + * while loop for UV_RUN_DEFAULT. Otherwise timers only need to be executed + * once, which should be done after polling in order to maintain proper + * execution order of the conceptual event loop. */ + if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) { uv__update_time(loop); uv__run_timers(loop); + } + while (r != 0 && loop->stop_flag == 0) { can_sleep = - QUEUE_EMPTY(&loop->pending_queue) && QUEUE_EMPTY(&loop->idle_handles); + uv__queue_empty(&loop->pending_queue) && + uv__queue_empty(&loop->idle_handles); uv__run_pending(loop); uv__run_idle(loop); @@ -410,11 +459,13 @@ if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT) timeout = uv__backend_timeout(loop); + uv__metrics_inc_loop_count(loop); + uv__io_poll(loop, timeout); /* Process immediate callbacks (e.g. write_cb) a small fixed number of * times to avoid loop starvation.*/ - for (r = 0; r < 8 && !QUEUE_EMPTY(&loop->pending_queue); r++) + for (r = 0; r < 8 && !uv__queue_empty(&loop->pending_queue); r++) uv__run_pending(loop); /* Run one final update on the provider_idle_time in case uv__io_poll @@ -427,18 +478,8 @@ uv__run_check(loop); uv__run_closing_handles(loop); - if (mode == UV_RUN_ONCE) { - /* UV_RUN_ONCE implies forward progress: at least one callback must have - * been invoked when it returns. uv__io_poll() can return without doing - * I/O (meaning: no callbacks) when its timeout expires - which means we - * have pending timers that satisfy the forward progress constraint. - * - * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from - * the check. - */ - uv__update_time(loop); - uv__run_timers(loop); - } + uv__update_time(loop); + uv__run_timers(loop); r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) @@ -716,7 +757,7 @@ int uv_cwd(char* buffer, size_t* size) { char scratch[1 + UV__PATH_MAX]; - if (buffer == NULL || size == NULL) + if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; /* Try to read directly into the user's buffer first... */ @@ -803,18 +844,18 @@ static void uv__run_pending(uv_loop_t* loop) { - QUEUE* q; - QUEUE pq; + struct uv__queue* q; + struct uv__queue pq; uv__io_t* w; - QUEUE_MOVE(&loop->pending_queue, &pq); + uv__queue_move(&loop->pending_queue, &pq); - while (!QUEUE_EMPTY(&pq)) { - q = QUEUE_HEAD(&pq); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - w = QUEUE_DATA(q, uv__io_t, pending_queue); - w->cb(loop, w, POLLOUT); + while (!uv__queue_empty(&pq)) { + q = uv__queue_head(&pq); + uv__queue_remove(q); + uv__queue_init(q); + w = uv__queue_data(q, uv__io_t, pending_queue); + uv__io_cb(loop, w, POLLOUT); } } @@ -830,7 +871,7 @@ return val; } -static void maybe_resize(uv_loop_t* loop, unsigned int len) { +static int maybe_resize(uv_loop_t* loop, unsigned int len) { uv__io_t** watchers; void* fake_watcher_list; void* fake_watcher_count; @@ -838,7 +879,7 @@ unsigned int i; if (len <= loop->nwatchers) - return; + return 0; /* Preserve fake watcher list and count at the end of the watchers */ if (loop->watchers != NULL) { @@ -854,7 +895,7 @@ (nwatchers + 2) * sizeof(loop->watchers[0])); if (watchers == NULL) - abort(); + return UV_ENOMEM; for (i = loop->nwatchers; i < nwatchers; i++) watchers[i] = NULL; watchers[nwatchers] = fake_watcher_list; @@ -862,34 +903,71 @@ loop->watchers = watchers; loop->nwatchers = nwatchers; + return 0; } -void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) { - assert(cb != NULL); +void uv__io_cb(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + switch (uv__io_cb_get(w)) { + case UV__AHAFS_EVENT: + uv__ahafs_event(loop, w, events); + break; + case UV__ASYNC_IO: + uv__async_io(loop, w, events); + break; + case UV__FS_EVENT: + uv__fs_event(loop, w, events); + break; + case UV__FS_EVENT_READ: + uv__fs_event_read(loop, w, events); + break; + case UV__INOTIFY_READ: + uv__inotify_read(loop, w, events); + break; + case UV__POLL_IO: + uv__poll_io(loop, w, events); + break; + case UV__SERVER_IO: + uv__server_io(loop, w, events); + break; + case UV__STREAM_IO: + uv__stream_io(loop, w, events); + break; + case UV__UDP_IO: + uv__udp_io(loop, w, events); + break; + default: + UNREACHABLE(); + } +} + + +void uv__io_init(uv__io_t* w, uv__io_cb_t cb, int fd) { assert(fd >= -1); - QUEUE_INIT(&w->pending_queue); - QUEUE_INIT(&w->watcher_queue); - w->cb = cb; + uv__queue_init(&w->pending_queue); + uv__queue_init(&w->watcher_queue); w->fd = fd; + w->bits = 0; w->events = 0; w->pevents = 0; - -#if defined(UV_HAVE_KQUEUE) - w->rcount = 0; - w->wcount = 0; -#endif /* defined(UV_HAVE_KQUEUE) */ + uv__io_cb_set(w, cb); } -void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +int uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + int err; + + assert(uv__io_cb_get(w) >= UV__AHAFS_EVENT); + assert(uv__io_cb_get(w) <= UV__UDP_IO); assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI))); assert(0 != events); assert(w->fd >= 0); assert(w->fd < INT_MAX); w->pevents |= events; - maybe_resize(loop, w->fd + 1); + err = maybe_resize(loop, w->fd + 1); + if (err) + return err; #if !defined(__sun) /* The event ports backend needs to rearm all file descriptors on each and @@ -897,16 +975,34 @@ * short-circuit here if the event mask is unchanged. */ if (w->events == w->pevents) - return; + return 0; #endif - if (QUEUE_EMPTY(&w->watcher_queue)) - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + if (uv__queue_empty(&w->watcher_queue)) + uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); if (loop->watchers[w->fd] == NULL) { loop->watchers[w->fd] = w; loop->nfds++; } + + return 0; +} + + +int uv__io_init_start(uv_loop_t* loop, + uv__io_t* w, + uv__io_cb_t cb, + int fd, + unsigned int events) { + int err; + + assert(fd > -1); + uv__io_init(w, cb, fd); + err = uv__io_start(loop, w, events); + if (err) + uv__io_init(w, UV__NO_IO_CB, -1); + return err; } @@ -926,8 +1022,8 @@ w->pevents &= ~events; if (w->pevents == 0) { - QUEUE_REMOVE(&w->watcher_queue); - QUEUE_INIT(&w->watcher_queue); + uv__queue_remove(&w->watcher_queue); + uv__queue_init(&w->watcher_queue); w->events = 0; if (w == loop->watchers[w->fd]) { @@ -936,14 +1032,14 @@ loop->nfds--; } } - else if (QUEUE_EMPTY(&w->watcher_queue)) - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + else if (uv__queue_empty(&w->watcher_queue)) + uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } void uv__io_close(uv_loop_t* loop, uv__io_t* w) { uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); - QUEUE_REMOVE(&w->pending_queue); + uv__queue_remove(&w->pending_queue); /* Remove stale events for this file descriptor */ if (w->fd != -1) @@ -952,8 +1048,8 @@ void uv__io_feed(uv_loop_t* loop, uv__io_t* w) { - if (QUEUE_EMPTY(&w->pending_queue)) - QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue); + if (uv__queue_empty(&w->pending_queue)) + uv__queue_insert_tail(&loop->pending_queue, &w->pending_queue); } @@ -964,15 +1060,48 @@ } +void uv__io_poll_prepare(uv_loop_t* loop, sigset_t* pset, int timeout) { + uv__loop_internal_fields_t* lfields; + + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + /* Store the current timeout in a location that's globally accessible so + * other locations like uv__work_done() can determine whether the queue + * of events in the callback were waiting when poll was called. + */ + lfields = uv__get_internal_fields(loop); + lfields->current_timeout = timeout; + + if (pset != NULL) + if (pthread_sigmask(SIG_BLOCK, pset, NULL)) + abort(); +} + +void uv__io_poll_check(uv_loop_t* loop, sigset_t* pset) { + if (pset != NULL) + if (pthread_sigmask(SIG_UNBLOCK, pset, NULL)) + abort(); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); +} + int uv__fd_exists(uv_loop_t* loop, int fd) { return (unsigned) fd < loop->nwatchers && loop->watchers[fd] != NULL; } -int uv_getrusage(uv_rusage_t* rusage) { +static int uv__getrusage(int who, uv_rusage_t* rusage) { struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage)) + if (getrusage(who, &usage)) return UV__ERR(errno); rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec; @@ -998,10 +1127,63 @@ rusage->ru_nivcsw = usage.ru_nivcsw; #endif + /* Most platforms report ru_maxrss in kilobytes; macOS and Solaris are + * the outliers because of course they are. + */ +#if defined(__APPLE__) + rusage->ru_maxrss /= 1024; /* macOS and iOS report bytes. */ +#elif defined(__sun) + rusage->ru_maxrss *= getpagesize() / 1024; /* Solaris reports pages. */ +#endif + return 0; } +int uv_getrusage(uv_rusage_t* rusage) { + return uv__getrusage(RUSAGE_SELF, rusage); +} + + +int uv_getrusage_thread(uv_rusage_t* rusage) { +#if defined(__APPLE__) + mach_msg_type_number_t count; + thread_basic_info_data_t info; + kern_return_t kr; + thread_t thread; + + thread = mach_thread_self(); + count = THREAD_BASIC_INFO_COUNT; + kr = thread_info(thread, + THREAD_BASIC_INFO, + (thread_info_t)&info, + &count); + + if (kr != KERN_SUCCESS) { + mach_port_deallocate(mach_task_self(), thread); + return UV_EINVAL; + } + + memset(rusage, 0, sizeof(*rusage)); + + rusage->ru_utime.tv_sec = info.user_time.seconds; + rusage->ru_utime.tv_usec = info.user_time.microseconds; + rusage->ru_stime.tv_sec = info.system_time.seconds; + rusage->ru_stime.tv_usec = info.system_time.microseconds; + + mach_port_deallocate(mach_task_self(), thread); + + return 0; + +#elif defined(RUSAGE_LWP) + return uv__getrusage(RUSAGE_LWP, rusage); +#elif defined(RUSAGE_THREAD) + return uv__getrusage(RUSAGE_THREAD, rusage); +#endif /* defined(__APPLE__) */ + return UV_ENOTSUP; +} + + int uv__open_cloexec(const char* path, int flags) { #if defined(O_CLOEXEC) int fd; @@ -1097,8 +1279,8 @@ if (r != UV_ENOENT) return r; - /* HOME is not set, so call uv__getpwuid_r() */ - r = uv__getpwuid_r(&pwd); + /* HOME is not set, so call uv_os_get_passwd() */ + r = uv_os_get_passwd(&pwd); if (r != 0) { return r; @@ -1171,11 +1353,10 @@ } -int uv__getpwuid_r(uv_passwd_t* pwd) { +static int uv__getpwuid_r(uv_passwd_t *pwd, uid_t uid) { struct passwd pw; struct passwd* result; char* buf; - uid_t uid; size_t bufsize; size_t name_size; size_t homedir_size; @@ -1185,8 +1366,6 @@ if (pwd == NULL) return UV_EINVAL; - uid = geteuid(); - /* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it * is frequently 1024 or 4096, so we can just use that directly. The pwent * will not usually be large. */ @@ -1245,24 +1424,100 @@ } -void uv_os_free_passwd(uv_passwd_t* pwd) { - if (pwd == NULL) - return; +int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) { +#if defined(__ANDROID__) && __ANDROID_API__ < 24 + /* This function getgrgid_r() was added in Android N (level 24) */ + return UV_ENOSYS; +#else + struct group gp; + struct group* result; + char* buf; + char* gr_mem; + size_t bufsize; + size_t name_size; + long members; + size_t mem_size; + int r; - /* - The memory for name, shell, and homedir are allocated in a single - uv__malloc() call. The base of the pointer is stored in pwd->username, so - that is the field that needs to be freed. - */ - uv__free(pwd->username); - pwd->username = NULL; - pwd->shell = NULL; - pwd->homedir = NULL; + if (grp == NULL) + return UV_EINVAL; + + /* Calling sysconf(_SC_GETGR_R_SIZE_MAX) would get the suggested size, but it + * is frequently 1024 or 4096, so we can just use that directly. The pwent + * will not usually be large. */ + for (bufsize = 2000;; bufsize *= 2) { + buf = uv__malloc(bufsize); + + if (buf == NULL) + return UV_ENOMEM; + + do + r = getgrgid_r(gid, &gp, buf, bufsize, &result); + while (r == EINTR); + + if (r != 0 || result == NULL) + uv__free(buf); + + if (r != ERANGE) + break; + } + + if (r != 0) + return UV__ERR(r); + + if (result == NULL) + return UV_ENOENT; + + /* Allocate memory for the groupname and members. */ + name_size = strlen(gp.gr_name) + 1; + members = 0; + mem_size = sizeof(char*); + for (r = 0; gp.gr_mem[r] != NULL; r++) { + mem_size += strlen(gp.gr_mem[r]) + 1 + sizeof(char*); + members++; + } + + gr_mem = uv__malloc(name_size + mem_size); + if (gr_mem == NULL) { + uv__free(buf); + return UV_ENOMEM; + } + + /* Copy the members */ + grp->members = (char**) gr_mem; + grp->members[members] = NULL; + gr_mem = (char*) &grp->members[members + 1]; + for (r = 0; r < members; r++) { + grp->members[r] = gr_mem; + strcpy(gr_mem, gp.gr_mem[r]); + gr_mem += strlen(gr_mem) + 1; + } + assert(gr_mem == (char*)grp->members + mem_size); + + /* Copy the groupname */ + grp->groupname = gr_mem; + memcpy(grp->groupname, gp.gr_name, name_size); +#ifndef __clang_analyzer__ /* deadcode.DeadStores */ + gr_mem += name_size; +#endif + + /* Copy the gid */ + grp->gid = gp.gr_gid; + + uv__free(buf); + + return 0; +#endif } int uv_os_get_passwd(uv_passwd_t* pwd) { - return uv__getpwuid_r(pwd); + return uv__getpwuid_r(pwd, geteuid()); +} + + +int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) { + return uv__getpwuid_r(pwd, uid); } @@ -1317,7 +1572,7 @@ fail: for (i = 0; i < cnt; i++) { - envitem = &(*envitems)[cnt]; + envitem = &(*envitems)[i]; uv__free(envitem->name); } uv__free(*envitems); @@ -1406,15 +1661,6 @@ } -int uv_cpumask_size(void) { -#if defined(__linux__) || defined(__FreeBSD__) - return CPU_SETSIZE; -#else - return UV_ENOTSUP; -#endif -} - - uv_os_fd_t uv_get_osfhandle(int fd) { return fd; } @@ -1432,8 +1678,19 @@ return getppid(); } +int uv_cpumask_size(void) { +#if UV__CPU_AFFINITY_SUPPORTED + return CPU_SETSIZE; +#else + return UV_ENOTSUP; +#endif +} int uv_os_getpriority(uv_pid_t pid, int* priority) { +#if defined(__QNX__) + /* QNX priority is not process-based */ + return UV_ENOSYS; +#else int r; if (priority == NULL) @@ -1447,10 +1704,15 @@ *priority = r; return 0; +#endif } int uv_os_setpriority(uv_pid_t pid, int priority) { +#if defined(__QNX__) + /* QNX priority is not process-based */ + return UV_ENOSYS; +#else if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW) return UV_EINVAL; @@ -1458,8 +1720,142 @@ return UV__ERR(errno); return 0; +#endif } +#ifndef CMAKE_BOOTSTRAP +/** + * If the function succeeds, the return value is 0. + * If the function fails, the return value is non-zero. + * for Linux, when schedule policy is SCHED_OTHER (default), priority is 0. + * So the output parameter priority is actually the nice value. +*/ +int uv_thread_getpriority(uv_thread_t tid, int* priority) { + int r; + int policy; + struct sched_param param; +#ifdef __linux__ + pid_t pid = gettid(); +#endif + + if (priority == NULL) + return UV_EINVAL; + + r = pthread_getschedparam(tid, &policy, ¶m); + if (r != 0) + return UV__ERR(r); + +#ifdef __linux__ + if (SCHED_OTHER == policy && pthread_equal(tid, pthread_self())) { + errno = 0; + r = getpriority(PRIO_PROCESS, pid); + if (r == -1 && errno != 0) + return UV__ERR(errno); + *priority = r; + return 0; + } +#endif + + *priority = param.sched_priority; + return 0; +} +#endif + +#ifdef __linux__ +static int set_nice_for_calling_thread(int priority) { + int r; + int nice; + + if (priority < UV_THREAD_PRIORITY_LOWEST || priority > UV_THREAD_PRIORITY_HIGHEST) + return UV_EINVAL; + + pid_t pid = gettid(); + nice = 0 - priority * 2; + r = setpriority(PRIO_PROCESS, pid, nice); + if (r != 0) + return UV__ERR(errno); + return 0; +} +#endif + +#ifndef CMAKE_BOOTSTRAP +/** + * If the function succeeds, the return value is 0. + * If the function fails, the return value is non-zero. +*/ +int uv_thread_setpriority(uv_thread_t tid, int priority) { +#if !defined(__GNU__) + int r; + int min; + int max; + int range; + int prio; + int policy; + struct sched_param param; + + if (priority < UV_THREAD_PRIORITY_LOWEST || priority > UV_THREAD_PRIORITY_HIGHEST) + return UV_EINVAL; + + r = pthread_getschedparam(tid, &policy, ¶m); + if (r != 0) + return UV__ERR(r); + +#ifdef __linux__ +/** + * for Linux, when schedule policy is SCHED_OTHER (default), priority must be 0, + * we should set the nice value in this case. +*/ + if (SCHED_OTHER == policy && pthread_equal(tid, pthread_self())) + return set_nice_for_calling_thread(priority); +#endif + +#ifdef __PASE__ + min = 1; + max = 127; +#else + min = sched_get_priority_min(policy); + max = sched_get_priority_max(policy); +#endif + + if (min == -1 || max == -1) + return UV__ERR(errno); + + range = max - min; + + switch (priority) { + case UV_THREAD_PRIORITY_HIGHEST: + prio = max; + break; + case UV_THREAD_PRIORITY_ABOVE_NORMAL: + prio = min + range * 3 / 4; + break; + case UV_THREAD_PRIORITY_NORMAL: + prio = min + range / 2; + break; + case UV_THREAD_PRIORITY_BELOW_NORMAL: + prio = min + range / 4; + break; + case UV_THREAD_PRIORITY_LOWEST: + prio = min; + break; + default: + return 0; + } + + if (param.sched_priority != prio) { + param.sched_priority = prio; + r = pthread_setschedparam(tid, policy, ¶m); + if (r != 0) + return UV__ERR(r); + } + + return 0; +#else /* !defined(__GNU__) */ + /* Simulate success on systems where thread priority is not implemented. */ + return 0; +#endif /* !defined(__GNU__) */ +} +#endif int uv_os_uname(uv_utsname_t* buffer) { struct utsname buf; @@ -1642,11 +2038,31 @@ return UV_EINVAL; } +#if defined(__linux__) || defined (__FreeBSD__) +# define uv__cpu_count(cpuset) CPU_COUNT(cpuset) +#elif defined(__NetBSD__) +static int uv__cpu_count(cpuset_t* set) { + int rc; + cpuid_t i; + + rc = 0; + for (i = 0;; i++) { + int r = cpuset_isset(i, set); + if (r < 0) + break; + if (r) + rc++; + } + + return rc; +} +#endif /* __NetBSD__ */ unsigned int uv_available_parallelism(void) { + long rc = -1; + #ifdef __linux__ cpu_set_t set; - long rc; memset(&set, 0, sizeof(set)); @@ -1655,29 +2071,124 @@ * before falling back to sysconf(_SC_NPROCESSORS_ONLN). */ if (0 == sched_getaffinity(0, sizeof(set), &set)) - rc = CPU_COUNT(&set); - else - rc = sysconf(_SC_NPROCESSORS_ONLN); - - if (rc < 1) - rc = 1; - - return (unsigned) rc; + rc = uv__cpu_count(&set); #elif defined(__MVS__) - int rc; - rc = __get_num_online_cpus(); if (rc < 1) rc = 1; return (unsigned) rc; -#else /* __linux__ */ - long rc; +#elif defined(__FreeBSD__) + cpuset_t set; - rc = sysconf(_SC_NPROCESSORS_ONLN); + memset(&set, 0, sizeof(set)); + + if (0 == cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(set), &set)) + rc = uv__cpu_count(&set); +#elif defined(__NetBSD__) + cpuset_t* set = cpuset_create(); + if (set != NULL) { + if (0 == sched_getaffinity_np(getpid(), cpuset_size(set), set)) + rc = uv__cpu_count(set); + cpuset_destroy(set); + } +#elif defined(__APPLE__) + int nprocs; + size_t i; + size_t len = sizeof(nprocs); + static const char *mib[] = { + "hw.activecpu", + "hw.logicalcpu", + "hw.ncpu" + }; + + for (i = 0; i < ARRAY_SIZE(mib); i++) { + if (0 == sysctlbyname(mib[i], &nprocs, &len, NULL, 0) && + len == sizeof(nprocs) && + nprocs > 0) { + rc = nprocs; + break; + } + } +#elif defined(__OpenBSD__) + int nprocs; + size_t i; + size_t len = sizeof(nprocs); + static int mib[][2] = { +# ifdef HW_NCPUONLINE + { CTL_HW, HW_NCPUONLINE }, +# endif + { CTL_HW, HW_NCPU } + }; + + for (i = 0; i < ARRAY_SIZE(mib); i++) { + if (0 == sysctl(mib[i], ARRAY_SIZE(mib[i]), &nprocs, &len, NULL, 0) && + len == sizeof(nprocs) && + nprocs > 0) { + rc = nprocs; + break; + } + } +#endif /* __linux__ */ + + if (rc < 0) + rc = sysconf(_SC_NPROCESSORS_ONLN); + +#ifdef __linux__ + { + long long quota = 0; + + if (uv__get_constrained_cpu("a) == 0) + if (quota > 0 && quota < rc) + rc = quota; + } +#endif /* __linux__ */ + if (rc < 1) rc = 1; return (unsigned) rc; -#endif /* __linux__ */ +} + +int uv__sock_reuseport(int fd) { + int on = 1; +#if defined(__FreeBSD__) && __FreeBSD__ >= 12 && defined(SO_REUSEPORT_LB) + /* FreeBSD 12 introduced a new socket option named SO_REUSEPORT_LB + * with the capability of load balancing, it's the substitution of + * the SO_REUSEPORTs on Linux and DragonFlyBSD. */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT_LB, &on, sizeof(on))) + return UV__ERR(errno); +#elif (defined(__linux__) || \ + defined(_AIX73) || \ + (defined(__DragonFly__) && __DragonFly_version >= 300600) || \ + (defined(UV__SOLARIS_11_4) && UV__SOLARIS_11_4)) && \ + defined(SO_REUSEPORT) + /* On Linux 3.9+, the SO_REUSEPORT implementation distributes connections + * evenly across all of the threads (or processes) that are blocked in + * accept() on the same port. As with TCP, SO_REUSEPORT distributes datagrams + * evenly across all of the receiving threads (or process). + * + * DragonFlyBSD 3.6.0 extended SO_REUSEPORT to distribute workload to + * available sockets, which made it the equivalent of Linux's SO_REUSEPORT. + * + * AIX 7.2.5 added the feature that would add the capability to distribute + * incoming connections or datagrams across all listening ports for SO_REUSEPORT. + * + * Solaris 11 supported SO_REUSEPORT, but it's implemented only for + * binding to the same address and port, without load balancing. + * Solaris 11.4 extended SO_REUSEPORT with the capability of load balancing. + */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))) + return UV__ERR(errno); +#else + (void) (fd); + (void) (on); + /* SO_REUSEPORTs do not have the capability of load balancing on platforms + * other than those mentioned above. The semantics are completely different, + * therefore we shouldn't enable it, but fail this operation to indicate that + * UV_[TCP/UDP]_REUSEPORT is not supported on these platforms. */ + return UV_ENOTSUP; +#endif + + return 0; }
diff --git a/Utilities/cmlibuv/src/unix/cygwin.c b/Utilities/cmlibuv/src/unix/cygwin.c index 169958d..4913108 100644 --- a/Utilities/cmlibuv/src/unix/cygwin.c +++ b/Utilities/cmlibuv/src/unix/cygwin.c
@@ -36,9 +36,45 @@ } int uv_resident_set_memory(size_t* rss) { - /* FIXME: read /proc/meminfo? */ - *rss = 0; + char buf[1024]; + const char* s; + long val; + int rc; + int i; + struct sysinfo si; + + /* rss: 24th element */ + rc = uv__slurp("/proc/self/stat", buf, sizeof(buf)); + if (rc < 0) + return rc; + + /* find the last ')' */ + s = strrchr(buf, ')'); + if (s == NULL) + goto err; + + for (i = 1; i <= 22; i++) { + s = strchr(s + 1, ' '); + if (s == NULL) + goto err; + } + + errno = 0; + val = strtol(s, NULL, 10); + if (val < 0 || errno != 0) + goto err; + + do + rc = sysinfo(&si); + while (rc == -1 && errno == EINTR); + if (rc == -1) + return UV__ERR(errno); + + *rss = val * si.mem_unit; return 0; + +err: + return UV_EINVAL; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { @@ -51,3 +87,7 @@ uint64_t uv_get_constrained_memory(void) { return 0; /* Memory constraints are unknown. */ } + +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +}
diff --git a/Utilities/cmlibuv/src/unix/darwin-proctitle.c b/Utilities/cmlibuv/src/unix/darwin-proctitle.c index 5288083..5e56429 100644 --- a/Utilities/cmlibuv/src/unix/darwin-proctitle.c +++ b/Utilities/cmlibuv/src/unix/darwin-proctitle.c
@@ -33,25 +33,9 @@ #include "darwin-stub.h" #endif - -static int uv__pthread_setname_np(const char* name) { - char namebuf[64]; /* MAXTHREADNAMESIZE */ - int err; - - strncpy(namebuf, name, sizeof(namebuf) - 1); - namebuf[sizeof(namebuf) - 1] = '\0'; - - err = pthread_setname_np(namebuf); - if (err) - return UV__ERR(err); - - return 0; -} - - int uv__set_process_title(const char* title) { #if TARGET_OS_IPHONE - return uv__pthread_setname_np(title); + return uv__thread_setname(title); #else CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, const char*, @@ -177,7 +161,7 @@ goto out; } - uv__pthread_setname_np(title); /* Don't care if it fails. */ + uv__thread_setname(title); /* Don't care if it fails. */ err = 0; out:
diff --git a/Utilities/cmlibuv/src/unix/darwin-stub.h b/Utilities/cmlibuv/src/unix/darwin-stub.h index 433e3ef..b93cf67 100644 --- a/Utilities/cmlibuv/src/unix/darwin-stub.h +++ b/Utilities/cmlibuv/src/unix/darwin-stub.h
@@ -27,7 +27,6 @@ struct CFArrayCallBacks; struct CFRunLoopSourceContext; struct FSEventStreamContext; -struct CFRange; typedef double CFAbsoluteTime; typedef double CFTimeInterval; @@ -43,23 +42,13 @@ typedef void* CFAllocatorRef; typedef void* CFArrayRef; typedef void* CFBundleRef; -typedef void* CFDataRef; typedef void* CFDictionaryRef; -typedef void* CFMutableDictionaryRef; -typedef struct CFRange CFRange; typedef void* CFRunLoopRef; typedef void* CFRunLoopSourceRef; typedef void* CFStringRef; typedef void* CFTypeRef; typedef void* FSEventStreamRef; -typedef uint32_t IOOptionBits; -typedef unsigned int io_iterator_t; -typedef unsigned int io_object_t; -typedef unsigned int io_service_t; -typedef unsigned int io_registry_entry_t; - - typedef void (*FSEventStreamCallback)(const FSEventStreamRef, void*, size_t, @@ -80,11 +69,6 @@ void* pad[3]; }; -struct CFRange { - CFIndex location; - CFIndex length; -}; - static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100; static const OSStatus noErr = 0;
diff --git a/Utilities/cmlibuv/src/unix/darwin-syscalls.h b/Utilities/cmlibuv/src/unix/darwin-syscalls.h new file mode 100644 index 0000000..dc2d1bd --- /dev/null +++ b/Utilities/cmlibuv/src/unix/darwin-syscalls.h
@@ -0,0 +1,17 @@ +#ifndef UV_DARWIN_SYSCALLS_H_ +#define UV_DARWIN_SYSCALLS_H_ + +#include <sys/types.h> +#include <sys/socket.h> + +/* https://github.com/apple/darwin-xnu/blob/master/bsd/sys/socket.h */ + +struct mmsghdr { + struct msghdr msg_hdr; + size_t msg_len; +}; + +ssize_t recvmsg_x(int s, const struct mmsghdr* msgp, u_int cnt, int flags); +ssize_t sendmsg_x(int s, const struct mmsghdr* msgp, u_int cnt, int flags); + +#endif /* UV_DARWIN_SYSCALLS_H_ */
diff --git a/Utilities/cmlibuv/src/unix/darwin.c b/Utilities/cmlibuv/src/unix/darwin.c index 62f04d3..a4e8129 100644 --- a/Utilities/cmlibuv/src/unix/darwin.c +++ b/Utilities/cmlibuv/src/unix/darwin.c
@@ -25,7 +25,6 @@ #include <stdint.h> #include <errno.h> -#include <dlfcn.h> #include <mach/mach.h> #include <mach/mach_time.h> #include <mach-o/dyld.h> /* _NSGetExecutablePath */ @@ -33,13 +32,15 @@ #include <sys/sysctl.h> #include <unistd.h> /* sysconf */ -#include "darwin-stub.h" +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101200 +# include <dlfcn.h> +# define mach_continuous_time uv__mach_continuous_time + static uint64_t (*uv__mach_continuous_time)(void); +#endif static uv_once_t once = UV_ONCE_INIT; -static uint64_t (*time_func)(void); static mach_timebase_info_data_t timebase; -typedef unsigned char UInt8; int uv__platform_loop_init(uv_loop_t* loop) { loop->cf_state = NULL; @@ -59,16 +60,18 @@ static void uv__hrtime_init_once(void) { if (KERN_SUCCESS != mach_timebase_info(&timebase)) abort(); - - time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time"); - if (time_func == NULL) - time_func = mach_absolute_time; +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101200 + uv__mach_continuous_time = (uint64_t (*)(void)) + dlsym(RTLD_DEFAULT, "mach_continuous_time"); + if (uv__mach_continuous_time == NULL) + uv__mach_continuous_time = mach_absolute_time; +#endif } uint64_t uv__hrtime(uv_clocktype_t type) { uv_once(&once, uv__hrtime_init_once); - return time_func() * timebase.numer / timebase.denom; + return mach_continuous_time() * timebase.numer / timebase.denom; } @@ -110,7 +113,7 @@ if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count) != KERN_SUCCESS) { - return UV_EINVAL; /* FIXME(bnoordhuis) Translate error. */ + return 0; } return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE); @@ -123,7 +126,7 @@ size_t size = sizeof(info); if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info; } @@ -134,6 +137,19 @@ } +uint64_t uv_get_available_memory(void) { + vm_statistics_data_t info; + mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); + + if (host_statistics(mach_host_self(), HOST_VM_INFO, + (host_info_t)&info, &count) != KERN_SUCCESS) { + return 0; + } + + return ((uint64_t) info.free_count + (uint64_t) info.inactive_count + (uint64_t) info.purgeable_count) * sysconf(_SC_PAGESIZE); +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -183,159 +199,17 @@ return 0; } -static int uv__get_cpu_speed(uint64_t* speed) { - /* IOKit */ - void (*pIOObjectRelease)(io_object_t); - kern_return_t (*pIOMasterPort)(mach_port_t, mach_port_t*); - CFMutableDictionaryRef (*pIOServiceMatching)(const char*); - kern_return_t (*pIOServiceGetMatchingServices)(mach_port_t, - CFMutableDictionaryRef, - io_iterator_t*); - io_service_t (*pIOIteratorNext)(io_iterator_t); - CFTypeRef (*pIORegistryEntryCreateCFProperty)(io_registry_entry_t, - CFStringRef, - CFAllocatorRef, - IOOptionBits); - - /* CoreFoundation */ - CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, - const char*, - CFStringEncoding); - CFStringEncoding (*pCFStringGetSystemEncoding)(void); - UInt8 *(*pCFDataGetBytePtr)(CFDataRef); - CFIndex (*pCFDataGetLength)(CFDataRef); - void (*pCFDataGetBytes)(CFDataRef, CFRange, UInt8*); - void (*pCFRelease)(CFTypeRef); - - void* core_foundation_handle; - void* iokit_handle; - int err; - - kern_return_t kr; - mach_port_t mach_port; - io_iterator_t it; - io_object_t service; - - mach_port = 0; - - err = UV_ENOENT; - core_foundation_handle = dlopen("/System/Library/Frameworks/" - "CoreFoundation.framework/" - "CoreFoundation", - RTLD_LAZY | RTLD_LOCAL); - iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/" - "IOKit", - RTLD_LAZY | RTLD_LOCAL); - - if (core_foundation_handle == NULL || iokit_handle == NULL) - goto out; - -#define V(handle, symbol) \ - do { \ - *(void **)(&p ## symbol) = dlsym((handle), #symbol); \ - if (p ## symbol == NULL) \ - goto out; \ - } \ - while (0) - V(iokit_handle, IOMasterPort); - V(iokit_handle, IOServiceMatching); - V(iokit_handle, IOServiceGetMatchingServices); - V(iokit_handle, IOIteratorNext); - V(iokit_handle, IOObjectRelease); - V(iokit_handle, IORegistryEntryCreateCFProperty); - V(core_foundation_handle, CFStringCreateWithCString); - V(core_foundation_handle, CFStringGetSystemEncoding); - V(core_foundation_handle, CFDataGetBytePtr); - V(core_foundation_handle, CFDataGetLength); - V(core_foundation_handle, CFDataGetBytes); - V(core_foundation_handle, CFRelease); -#undef V - -#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8) - - kr = pIOMasterPort(MACH_PORT_NULL, &mach_port); - assert(kr == KERN_SUCCESS); - CFMutableDictionaryRef classes_to_match - = pIOServiceMatching("IOPlatformDevice"); - kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it); - assert(kr == KERN_SUCCESS); - service = pIOIteratorNext(it); - - CFStringRef device_type_str = S("device_type"); - CFStringRef clock_frequency_str = S("clock-frequency"); - - while (service != 0) { - CFDataRef data; - data = pIORegistryEntryCreateCFProperty(service, - device_type_str, - NULL, - 0); - if (data) { - const UInt8* raw = pCFDataGetBytePtr(data); - if (strncmp((char*)raw, "cpu", 3) == 0 || - strncmp((char*)raw, "processor", 9) == 0) { - CFDataRef freq_ref; - freq_ref = pIORegistryEntryCreateCFProperty(service, - clock_frequency_str, - NULL, - 0); - if (freq_ref) { - const UInt8* freq_ref_ptr = pCFDataGetBytePtr(freq_ref); - CFIndex len = pCFDataGetLength(freq_ref); - if (len == 8) - memcpy(speed, freq_ref_ptr, 8); - else if (len == 4) { - uint32_t v; - memcpy(&v, freq_ref_ptr, 4); - *speed = v; - } else { - *speed = 0; - } - - pCFRelease(freq_ref); - pCFRelease(data); - break; - } - } - pCFRelease(data); - } - - service = pIOIteratorNext(it); - } - - pIOObjectRelease(it); - - err = 0; - - if (device_type_str != NULL) - pCFRelease(device_type_str); - if (clock_frequency_str != NULL) - pCFRelease(clock_frequency_str); - -out: - if (core_foundation_handle != NULL) - dlclose(core_foundation_handle); - - if (iokit_handle != NULL) - dlclose(iokit_handle); - - mach_port_deallocate(mach_task_self(), mach_port); - - return err; -} - int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), multiplier = ((uint64_t)1000L / ticks); char model[512]; + uint64_t cpuspeed; size_t size; unsigned int i; natural_t numcpus; mach_msg_type_number_t msg_type; processor_cpu_load_info_data_t *info; uv_cpu_info_t* cpu_info; - uint64_t cpuspeed; - int err; size = sizeof(model); if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) && @@ -343,9 +217,13 @@ return UV__ERR(errno); } - err = uv__get_cpu_speed(&cpuspeed); - if (err < 0) - return err; + cpuspeed = 0; + size = sizeof(cpuspeed); + sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0); + if (cpuspeed == 0) + /* If sysctl hw.cputype == CPU_TYPE_ARM64, the correct value is unavailable + * from Apple, but we can hard-code it here to a plausible value. */ + cpuspeed = 2400000000U; if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, (processor_info_array_t*)&info, @@ -371,7 +249,7 @@ cpu_info->cpu_times.irq = 0; cpu_info->model = uv__strdup(model); - cpu_info->speed = cpuspeed/1000000; + cpu_info->speed = (int)(cpuspeed / 1000000); } vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
diff --git a/Utilities/cmlibuv/src/unix/epoll.c b/Utilities/cmlibuv/src/unix/epoll.c deleted file mode 100644 index 97348e2..0000000 --- a/Utilities/cmlibuv/src/unix/epoll.c +++ /dev/null
@@ -1,422 +0,0 @@ -/* Copyright libuv contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "uv.h" -#include "internal.h" -#include <errno.h> -#include <sys/epoll.h> - -int uv__epoll_init(uv_loop_t* loop) { - int fd; - fd = epoll_create1(O_CLOEXEC); - - /* epoll_create1() can fail either because it's not implemented (old kernel) - * or because it doesn't understand the O_CLOEXEC flag. - */ - if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { - fd = epoll_create(256); - - if (fd != -1) - uv__cloexec(fd, 1); - } - - loop->backend_fd = fd; - if (fd == -1) - return UV__ERR(errno); - - return 0; -} - - -void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { - struct epoll_event* events; - struct epoll_event dummy; - uintptr_t i; - uintptr_t nfds; - - assert(loop->watchers != NULL); - assert(fd >= 0); - - events = (struct epoll_event*) loop->watchers[loop->nwatchers]; - nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; - if (events != NULL) - /* Invalidate events with same file descriptor */ - for (i = 0; i < nfds; i++) - if (events[i].data.fd == fd) - events[i].data.fd = -1; - - /* Remove the file descriptor from the epoll. - * This avoids a problem where the same file description remains open - * in another process, causing repeated junk epoll events. - * - * We pass in a dummy epoll_event, to work around a bug in old kernels. - */ - if (loop->backend_fd >= 0) { - /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that - * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings. - */ - memset(&dummy, 0, sizeof(dummy)); - epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); - } -} - - -int uv__io_check_fd(uv_loop_t* loop, int fd) { - struct epoll_event e; - int rc; - - memset(&e, 0, sizeof(e)); - e.events = POLLIN; - e.data.fd = -1; - - rc = 0; - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) - if (errno != EEXIST) - rc = UV__ERR(errno); - - if (rc == 0) - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) - abort(); - - return rc; -} - - -void uv__io_poll(uv_loop_t* loop, int timeout) { - /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes - * effectively infinite on 32 bits architectures. To avoid blocking - * indefinitely, we cap the timeout and poll again if necessary. - * - * Note that "30 minutes" is a simplification because it depends on - * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, - * that being the largest value I have seen in the wild (and only once.) - */ - static const int max_safe_timeout = 1789569; - static int no_epoll_pwait_cached; - static int no_epoll_wait_cached; - int no_epoll_pwait; - int no_epoll_wait; - struct epoll_event events[1024]; - struct epoll_event* pe; - struct epoll_event e; - int real_timeout; - QUEUE* q; - uv__io_t* w; - sigset_t sigset; - uint64_t sigmask; - uint64_t base; - int have_signals; - int nevents; - int count; - int nfds; - int fd; - int op; - int i; - int user_timeout; - int reset_timeout; - - if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); - return; - } - - memset(&e, 0, sizeof(e)); - - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - - w = QUEUE_DATA(q, uv__io_t, watcher_queue); - assert(w->pevents != 0); - assert(w->fd >= 0); - assert(w->fd < (int) loop->nwatchers); - - e.events = w->pevents; - e.data.fd = w->fd; - - if (w->events == 0) - op = EPOLL_CTL_ADD; - else - op = EPOLL_CTL_MOD; - - /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching - * events, skip the syscall and squelch the events after epoll_wait(). - */ - if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) { - if (errno != EEXIST) - abort(); - - assert(op == EPOLL_CTL_ADD); - - /* We've reactivated a file descriptor that's been watched before. */ - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e)) - abort(); - } - - w->events = w->pevents; - } - - sigmask = 0; - if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { - sigemptyset(&sigset); - sigaddset(&sigset, SIGPROF); - sigmask |= 1 << (SIGPROF - 1); - } - - assert(timeout >= -1); - base = loop->time; - count = 48; /* Benchmarks suggest this gives the best throughput. */ - real_timeout = timeout; - - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { - reset_timeout = 1; - user_timeout = timeout; - timeout = 0; - } else { - reset_timeout = 0; - user_timeout = 0; - } - - /* You could argue there is a dependency between these two but - * ultimately we don't care about their ordering with respect - * to one another. Worst case, we make a few system calls that - * could have been avoided because another thread already knows - * they fail with ENOSYS. Hardly the end of the world. - */ - no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached); - no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached); - - for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - /* See the comment for max_safe_timeout for an explanation of why - * this is necessary. Executive summary: kernel bug workaround. - */ - if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) - timeout = max_safe_timeout; - - if (sigmask != 0 && no_epoll_pwait != 0) - if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) - abort(); - - if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { - nfds = epoll_pwait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout, - &sigset); - if (nfds == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_epoll_pwait_cached, 1); - no_epoll_pwait = 1; - } - } else { - nfds = epoll_wait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout); - if (nfds == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_epoll_wait_cached, 1); - no_epoll_wait = 1; - } - } - - if (sigmask != 0 && no_epoll_pwait != 0) - if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) - abort(); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); - - if (nfds == 0) { - assert(timeout != -1); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == -1) - continue; - - if (timeout == 0) - return; - - /* We may have been inside the system call for longer than |timeout| - * milliseconds so we need to update the timestamp to avoid drift. - */ - goto update_timeout; - } - - if (nfds == -1) { - if (errno == ENOSYS) { - /* epoll_wait() or epoll_pwait() failed, try the other system call. */ - assert(no_epoll_wait == 0 || no_epoll_pwait == 0); - continue; - } - - if (errno != EINTR) - abort(); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == -1) - continue; - - if (timeout == 0) - return; - - /* Interrupted by a signal. Update timeout and poll again. */ - goto update_timeout; - } - - have_signals = 0; - nevents = 0; - - { - /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */ - union { - struct epoll_event* events; - uv__io_t* watchers; - } x; - - x.events = events; - assert(loop->watchers != NULL); - loop->watchers[loop->nwatchers] = x.watchers; - loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; - } - - for (i = 0; i < nfds; i++) { - pe = events + i; - fd = pe->data.fd; - - /* Skip invalidated events, see uv__platform_invalidate_fd */ - if (fd == -1) - continue; - - assert(fd >= 0); - assert((unsigned) fd < loop->nwatchers); - - w = loop->watchers[fd]; - - if (w == NULL) { - /* File descriptor that we've stopped watching, disarm it. - * - * Ignore all errors because we may be racing with another thread - * when the file descriptor is closed. - */ - epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe); - continue; - } - - /* Give users only events they're interested in. Prevents spurious - * callbacks when previous callback invocation in this loop has stopped - * the current watcher. Also, filters out events that users has not - * requested us to watch. - */ - pe->events &= w->pevents | POLLERR | POLLHUP; - - /* Work around an epoll quirk where it sometimes reports just the - * EPOLLERR or EPOLLHUP event. In order to force the event loop to - * move forward, we merge in the read/write events that the watcher - * is interested in; uv__read() and uv__write() will then deal with - * the error or hangup in the usual fashion. - * - * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user - * reads the available data, calls uv_read_stop(), then sometime later - * calls uv_read_start() again. By then, libuv has forgotten about the - * hangup and the kernel won't report EPOLLIN again because there's - * nothing left to read. If anything, libuv is to blame here. The - * current hack is just a quick bandaid; to properly fix it, libuv - * needs to remember the error/hangup event. We should get that for - * free when we switch over to edge-triggered I/O. - */ - if (pe->events == POLLERR || pe->events == POLLHUP) - pe->events |= - w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); - - if (pe->events != 0) { - /* Run signal watchers last. This also affects child process watchers - * because those are implemented in terms of signal watchers. - */ - if (w == &loop->signal_io_watcher) { - have_signals = 1; - } else { - uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->events); - } - - nevents++; - } - } - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (have_signals != 0) { - uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); - } - - loop->watchers[loop->nwatchers] = NULL; - loop->watchers[loop->nwatchers + 1] = NULL; - - if (have_signals != 0) - return; /* Event loop should cycle now so don't poll again. */ - - if (nevents != 0) { - if (nfds == ARRAY_SIZE(events) && --count != 0) { - /* Poll for more events but don't block this time. */ - timeout = 0; - continue; - } - return; - } - - if (timeout == 0) - return; - - if (timeout == -1) - continue; - -update_timeout: - assert(timeout > 0); - - real_timeout -= (loop->time - base); - if (real_timeout <= 0) - return; - - timeout = real_timeout; - } -} -
diff --git a/Utilities/cmlibuv/src/unix/freebsd.c b/Utilities/cmlibuv/src/unix/freebsd.c index 658ff26..a6de29c 100644 --- a/Utilities/cmlibuv/src/unix/freebsd.c +++ b/Utilities/cmlibuv/src/unix/freebsd.c
@@ -26,7 +26,12 @@ #include <errno.h> #include <paths.h> -#include <sys/user.h> +#if defined(__DragonFly__) +# include <sys/event.h> +# include <sys/kinfo.h> +#else +# include <sys/user.h> +#endif #include <sys/types.h> #include <sys/resource.h> #include <sys/sysctl.h> @@ -91,7 +96,7 @@ size_t size = sizeof(freecount); if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) freecount * sysconf(_SC_PAGESIZE); @@ -105,7 +110,7 @@ size_t size = sizeof(info); if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info; } @@ -116,6 +121,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -264,30 +274,6 @@ } -int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if __FreeBSD__ >= 11 && !defined(__DragonFly__) - return sendmmsg(fd, - (struct mmsghdr*) mmsg, - vlen, - 0 /* flags */); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if __FreeBSD__ >= 11 && !defined(__DragonFly__) - return recvmmsg(fd, - (struct mmsghdr*) mmsg, - vlen, - 0 /* flags */, - NULL /* timeout */); -#else - return errno = ENOSYS, -1; -#endif -} - ssize_t uv__fs_copy_file_range(int fd_in, off_t* off_in,
diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c index ce7076b..dea3a55 100644 --- a/Utilities/cmlibuv/src/unix/fs.c +++ b/Utilities/cmlibuv/src/unix/fs.c
@@ -31,6 +31,7 @@ #include <errno.h> #include <dlfcn.h> +#include <stdatomic.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -41,26 +42,15 @@ #include <sys/stat.h> #include <sys/time.h> #include <sys/uio.h> -#include <pthread.h> #include <unistd.h> #include <fcntl.h> #include <poll.h> -#if defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__OpenBSD__) || \ - defined(__NetBSD__) -# define HAVE_PREADV 1 -#else -# define HAVE_PREADV 0 -#endif - #if defined(__linux__) -# include "sys/utsname.h" +# include <sys/sendfile.h> #endif -#if defined(__linux__) || defined(__sun) +#if defined(__sun) # include <sys/sendfile.h> # include <sys/sysmacros.h> #endif @@ -79,7 +69,6 @@ #if defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # include <sys/param.h> @@ -150,7 +139,7 @@ #define POST \ do { \ if (cb != NULL) { \ - uv__req_register(loop, req); \ + uv__req_register(loop); \ uv__work_submit(loop, \ &req->work_req, \ UV__WORK_FAST_IO, \ @@ -214,60 +203,51 @@ } -UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) { +#if (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) \ + || defined(_AIX71) \ + || defined(__DragonFly__) \ + || defined(__FreeBSD__) \ + || defined(__HAIKU__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ + || defined(__linux__) \ + || defined(__sun) \ + || defined(__QNX__) +static struct timespec uv__fs_to_timespec(double time) { struct timespec ts; + + if (uv__isinf(time)) + return (struct timespec){UTIME_NOW, UTIME_NOW}; + if (uv__isnan(time)) + return (struct timespec){UTIME_OMIT, UTIME_OMIT}; + ts.tv_sec = time; ts.tv_nsec = (time - ts.tv_sec) * 1e9; - /* TODO(bnoordhuis) Remove this. utimesat() has nanosecond resolution but we - * stick to microsecond resolution for the sake of consistency with other - * platforms. I'm the original author of this compatibility hack but I'm - * less convinced it's useful nowadays. - */ - ts.tv_nsec -= ts.tv_nsec % 1000; - if (ts.tv_nsec < 0) { ts.tv_nsec += 1e9; ts.tv_sec -= 1; } return ts; } +#endif -UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) { - struct timeval tv; - tv.tv_sec = time; - tv.tv_usec = (time - tv.tv_sec) * 1e6; - if (tv.tv_usec < 0) { - tv.tv_usec += 1e6; - tv.tv_sec -= 1; - } - return tv; -} static ssize_t uv__fs_futime(uv_fs_t* req) { -#if defined(__linux__) \ +#if (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) \ || defined(_AIX71) \ + || defined(__DragonFly__) \ + || defined(__FreeBSD__) \ || defined(__HAIKU__) \ - || defined(__GNU__) + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ + || defined(__linux__) \ + || defined(__sun) \ + || defined(__QNX__) struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); return futimens(req->file, ts); -#elif defined(__APPLE__) \ - || defined(__DragonFly__) \ - || defined(__FreeBSD__) \ - || defined(__FreeBSD_kernel__) \ - || defined(__NetBSD__) \ - || defined(__OpenBSD__) \ - || defined(__sun) - struct timeval tv[2]; - tv[0] = uv__fs_to_timeval(req->atime); - tv[1] = uv__fs_to_timeval(req->mtime); -# if defined(__sun) - return futimesat(req->file, NULL, tv); -# else - return futimes(req->file, tv); -# endif #elif defined(__MVS__) attrib_t atr; memset(&atr, 0, sizeof(atr)); @@ -338,7 +318,7 @@ static uv_once_t once = UV_ONCE_INIT; int r; #ifdef O_CLOEXEC - static int no_cloexec_support; + static _Atomic int no_cloexec_support; #endif static const char pattern[] = "XXXXXX"; static const size_t pattern_size = sizeof(pattern) - 1; @@ -363,7 +343,8 @@ uv_once(&once, uv__mkostemp_initonce); #ifdef O_CLOEXEC - if (uv__load_relaxed(&no_cloexec_support) == 0 && uv__mkostemp != NULL) { + if (atomic_load_explicit(&no_cloexec_support, memory_order_relaxed) == 0 && + uv__mkostemp != NULL) { r = uv__mkostemp(path, O_CLOEXEC); if (r >= 0) @@ -376,7 +357,7 @@ /* We set the static variable so that next calls don't even try to use mkostemp. */ - uv__store_relaxed(&no_cloexec_support, 1); + atomic_store_explicit(&no_cloexec_support, 1, memory_order_relaxed); } #endif /* O_CLOEXEC */ @@ -434,139 +415,180 @@ } -#if !HAVE_PREADV -static ssize_t uv__fs_preadv(uv_file fd, - uv_buf_t* bufs, - unsigned int nbufs, - off_t off) { - uv_buf_t* buf; - uv_buf_t* end; - ssize_t result; - ssize_t rc; - size_t pos; +static ssize_t uv__preadv_or_pwritev_emul(int fd, + const struct iovec* bufs, + size_t nbufs, + off_t off, + int is_pread) { + ssize_t total; + ssize_t r; + size_t i; + size_t n; + void* p; - assert(nbufs > 0); + total = 0; + for (i = 0; i < (size_t) nbufs; i++) { + p = bufs[i].iov_base; + n = bufs[i].iov_len; - result = 0; - pos = 0; - buf = bufs + 0; - end = bufs + nbufs; - - for (;;) { do - rc = pread(fd, buf->base + pos, buf->len - pos, off + result); - while (rc == -1 && errno == EINTR); + if (is_pread) + r = pread(fd, p, n, off); + else + r = pwrite(fd, p, n, off); + while (r == -1 && errno == EINTR); - if (rc == 0) - break; + if (r == -1) { + if (total > 0) + return total; + return -1; + } - if (rc == -1 && result == 0) - return UV__ERR(errno); + off += r; + total += r; - if (rc == -1) - break; /* We read some data so return that, ignore the error. */ - - pos += rc; - result += rc; - - if (pos < buf->len) - continue; - - pos = 0; - buf += 1; - - if (buf == end) - break; + if ((size_t) r < n) + return total; } - return result; + return total; } + + +#ifdef __linux__ +typedef int uv__iovcnt; +#else +typedef size_t uv__iovcnt; #endif +static ssize_t uv__preadv_emul(int fd, + const struct iovec* bufs, + uv__iovcnt nbufs, + off_t off) { + return uv__preadv_or_pwritev_emul(fd, bufs, nbufs, off, /*is_pread*/1); +} + + +static ssize_t uv__pwritev_emul(int fd, + const struct iovec* bufs, + uv__iovcnt nbufs, + off_t off) { + return uv__preadv_or_pwritev_emul(fd, bufs, nbufs, off, /*is_pread*/0); +} + + +/* The function pointer cache is an uintptr_t because _Atomic void* + * doesn't work on macos/ios/etc... + */ +static ssize_t uv__preadv_or_pwritev(int fd, + const struct iovec* bufs, + size_t nbufs, + off_t off, + _Atomic uintptr_t* cache, + int is_pread) { + ssize_t (*f)(int, const struct iovec*, uv__iovcnt, off_t); + void* p; + + p = (void*) atomic_load_explicit(cache, memory_order_relaxed); + if (p == NULL) { +#ifdef RTLD_DEFAULT + /* Try _LARGEFILE_SOURCE version of preadv/pwritev first, + * then fall back to the plain version, for libcs like musl. + */ + p = dlsym(RTLD_DEFAULT, is_pread ? "preadv64" : "pwritev64"); + if (p == NULL) + p = dlsym(RTLD_DEFAULT, is_pread ? "preadv" : "pwritev"); + dlerror(); /* Clear errors. */ +#endif /* RTLD_DEFAULT */ + if (p == NULL) + p = is_pread ? uv__preadv_emul : uv__pwritev_emul; + atomic_store_explicit(cache, (uintptr_t) p, memory_order_relaxed); + } + + f = p; + return f(fd, bufs, nbufs, off); +} + + +static ssize_t uv__preadv(int fd, + const struct iovec* bufs, + size_t nbufs, + off_t off) { + static _Atomic uintptr_t cache; + return uv__preadv_or_pwritev(fd, bufs, nbufs, off, &cache, /*is_pread*/1); +} + + +static ssize_t uv__pwritev(int fd, + const struct iovec* bufs, + size_t nbufs, + off_t off) { + static _Atomic uintptr_t cache; + return uv__preadv_or_pwritev(fd, bufs, nbufs, off, &cache, /*is_pread*/0); +} + + static ssize_t uv__fs_read(uv_fs_t* req) { -#if defined(__linux__) - static int no_preadv; -#endif + const struct iovec* bufs; unsigned int iovmax; - ssize_t result; + size_t nbufs; + ssize_t r; + off_t off; + int fd; + + fd = req->file; + off = req->off; + bufs = (const struct iovec*) req->bufs; + nbufs = req->nbufs; iovmax = uv__getiovmax(); - if (req->nbufs > iovmax) - req->nbufs = iovmax; + if (nbufs > iovmax) + nbufs = iovmax; - if (req->off < 0) { - if (req->nbufs == 1) - result = read(req->file, req->bufs[0].base, req->bufs[0].len); - else - result = readv(req->file, (struct iovec*) req->bufs, req->nbufs); + r = 0; + if (off < 0) { + if (nbufs == 1) + r = read(fd, bufs->iov_base, bufs->iov_len); + else if (nbufs > 1) + r = readv(fd, bufs, nbufs); } else { - if (req->nbufs == 1) { - result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off); - goto done; - } - -#if HAVE_PREADV - result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); -#else -# if defined(__linux__) - if (uv__load_relaxed(&no_preadv)) retry: -# endif - { - result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off); - } -# if defined(__linux__) - else { - result = uv__preadv(req->file, - (struct iovec*)req->bufs, - req->nbufs, - req->off); - if (result == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_preadv, 1); - goto retry; - } - } -# endif -#endif + if (nbufs == 1) + r = pread(fd, bufs->iov_base, bufs->iov_len, off); + else if (nbufs > 1) + r = uv__preadv(fd, bufs, nbufs, off); } -done: - /* Early cleanup of bufs allocation, since we're done with it. */ - if (req->bufs != req->bufsml) - uv__free(req->bufs); - - req->bufs = NULL; - req->nbufs = 0; - #ifdef __PASE__ /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */ - if (result == -1 && errno == EOPNOTSUPP) { + if (r == -1 && errno == EOPNOTSUPP) { struct stat buf; ssize_t rc; - rc = fstat(req->file, &buf); + rc = uv__fstat(fd, &buf); if (rc == 0 && S_ISDIR(buf.st_mode)) { errno = EISDIR; } } #endif - return result; + /* We don't own the buffer list in the synchronous case. */ + if (req->cb != NULL) + if (req->bufs != req->bufsml) + uv__free(req->bufs); + + req->bufs = NULL; + req->nbufs = 0; + + return r; } -#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8) -#define UV_CONST_DIRENT uv__dirent_t -#else -#define UV_CONST_DIRENT const uv__dirent_t -#endif - - -static int uv__fs_scandir_filter(UV_CONST_DIRENT* dent) { +static int uv__fs_scandir_filter(const uv__dirent_t* dent) { return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0; } -static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) { +static int uv__fs_scandir_sort(const uv__dirent_t** a, const uv__dirent_t** b) { return strcmp((*a)->d_name, (*b)->d_name); } @@ -716,6 +738,11 @@ stat_fs->f_bavail = buf.f_bavail; stat_fs->f_files = buf.f_files; stat_fs->f_ffree = buf.f_ffree; +#if defined(__linux__) + stat_fs->f_frsize = buf.f_frsize; +#else + stat_fs->f_frsize = buf.f_bsize; +#endif req->ptr = stat_fs; return 0; } @@ -742,7 +769,7 @@ /* We may not have a real PATH_MAX. Read size of link. */ struct stat st; int ret; - ret = lstat(req->path, &st); + ret = uv__lstat(req->path, &st); if (ret != 0) return -1; if (!S_ISLNK(st.st_mode)) { @@ -792,14 +819,23 @@ static ssize_t uv__fs_realpath(uv_fs_t* req) { char* buf; + char* tmp; #if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L - buf = realpath(req->path, NULL); - if (buf == NULL) + tmp = realpath(req->path, NULL); + if (tmp == NULL) return -1; + buf = uv__strdup(tmp); + free(tmp); /* _Not_ uv__free. */ + if (buf == NULL) { + errno = ENOMEM; + return -1; + } #else ssize_t len; + (void)tmp; + len = uv__fs_pathmax_size(req->path); buf = uv__malloc(len + 1); @@ -934,31 +970,6 @@ #ifdef __linux__ -static unsigned uv__kernel_version(void) { - static unsigned cached_version; - struct utsname u; - unsigned version; - unsigned major; - unsigned minor; - unsigned patch; - - version = uv__load_relaxed(&cached_version); - if (version != 0) - return version; - - if (-1 == uname(&u)) - return 0; - - if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch)) - return 0; - - version = major * 65536 + minor * 256 + patch; - uv__store_relaxed(&cached_version, version); - - return version; -} - - /* Pre-4.20 kernels have a bug where CephFS uses the RADOS copy-from command * in copy_file_range() when it shouldn't. There is no workaround except to * fall back to a regular copy. @@ -995,10 +1006,10 @@ static ssize_t uv__fs_try_copy_file_range(int in_fd, off_t* off, int out_fd, size_t len) { - static int no_copy_file_range_support; + static _Atomic int no_copy_file_range_support; ssize_t r; - if (uv__load_relaxed(&no_copy_file_range_support)) { + if (atomic_load_explicit(&no_copy_file_range_support, memory_order_relaxed)) { errno = ENOSYS; return -1; } @@ -1017,7 +1028,7 @@ errno = ENOSYS; /* Use fallback. */ break; case ENOSYS: - uv__store_relaxed(&no_copy_file_range_support, 1); + atomic_store_explicit(&no_copy_file_range_support, 1, memory_order_relaxed); break; case EPERM: /* It's been reported that CIFS spuriously fails. @@ -1089,10 +1100,10 @@ return -1; } -#elif defined(__APPLE__) || \ - defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) +/* sendfile() on iOS(arm64) will throw SIGSYS signal cause crash. */ +#elif (defined(__APPLE__) && !TARGET_OS_IPHONE) \ + || defined(__DragonFly__) \ + || defined(__FreeBSD__) { off_t len; ssize_t r; @@ -1116,15 +1127,6 @@ #endif len = 0; r = sendfile(in_fd, out_fd, req->off, req->bufsml[0].len, NULL, &len, 0); -#elif defined(__FreeBSD_kernel__) - len = 0; - r = bsd_sendfile(in_fd, - out_fd, - req->off, - req->bufsml[0].len, - NULL, - &len, - 0); #else /* The darwin sendfile takes len as an input for the length to send, * so make sure to initialize it with the caller's value. */ @@ -1165,26 +1167,21 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { -#if defined(__linux__) \ - || defined(_AIX71) \ - || defined(__sun) \ - || defined(__HAIKU__) +#if (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) \ + || defined(_AIX71) \ + || defined(__DragonFly__) \ + || defined(__FreeBSD__) \ + || defined(__HAIKU__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ + || defined(__linux__) \ + || defined(__sun) \ + || defined(__QNX__) struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); return utimensat(AT_FDCWD, req->path, ts, 0); -#elif defined(__APPLE__) \ - || defined(__DragonFly__) \ - || defined(__FreeBSD__) \ - || defined(__FreeBSD_kernel__) \ - || defined(__NetBSD__) \ - || defined(__OpenBSD__) - struct timeval tv[2]; - tv[0] = uv__fs_to_timeval(req->atime); - tv[1] = uv__fs_to_timeval(req->mtime); - return utimes(req->path, tv); -#elif defined(_AIX) \ - && !defined(_AIX71) +#elif defined(_AIX) && !defined(_AIX71) struct utimbuf buf; buf.actime = req->atime; buf.modtime = req->mtime; @@ -1205,25 +1202,20 @@ static ssize_t uv__fs_lutime(uv_fs_t* req) { -#if defined(__linux__) || \ - defined(_AIX71) || \ - defined(__sun) || \ - defined(__HAIKU__) || \ - defined(__GNU__) || \ - defined(__OpenBSD__) +#if (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) \ + || defined(_AIX71) \ + || defined(__DragonFly__) \ + || defined(__FreeBSD__) \ + || defined(__HAIKU__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ + || defined(__linux__) \ + || defined(__sun) \ + || defined(__QNX__) struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW); -#elif defined(__APPLE__) || \ - defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__NetBSD__) - struct timeval tv[2]; - tv[0] = uv__fs_to_timeval(req->atime); - tv[1] = uv__fs_to_timeval(req->mtime); - return lutimes(req->path, tv); #else errno = ENOSYS; return -1; @@ -1232,71 +1224,45 @@ static ssize_t uv__fs_write(uv_fs_t* req) { -#if defined(__linux__) - static int no_pwritev; -#endif + const struct iovec* bufs; + size_t nbufs; ssize_t r; + off_t off; + int fd; - /* Serialize writes on OS X, concurrent write() and pwrite() calls result in - * data loss. We can't use a per-file descriptor lock, the descriptor may be - * a dup(). - */ -#if defined(__APPLE__) - static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + fd = req->file; + off = req->off; + bufs = (const struct iovec*) req->bufs; + nbufs = req->nbufs; - if (pthread_mutex_lock(&lock)) - abort(); -#endif - - if (req->off < 0) { - if (req->nbufs == 1) - r = write(req->file, req->bufs[0].base, req->bufs[0].len); - else - r = writev(req->file, (struct iovec*) req->bufs, req->nbufs); + r = 0; + if (off < 0) { + if (nbufs == 1) + r = write(fd, bufs->iov_base, bufs->iov_len); + else if (nbufs > 1) + r = writev(fd, bufs, nbufs); } else { - if (req->nbufs == 1) { - r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off); - goto done; - } -#if HAVE_PREADV - r = pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); -#else -# if defined(__linux__) - if (no_pwritev) retry: -# endif - { - r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off); - } -# if defined(__linux__) - else { - r = uv__pwritev(req->file, - (struct iovec*) req->bufs, - req->nbufs, - req->off); - if (r == -1 && errno == ENOSYS) { - no_pwritev = 1; - goto retry; - } - } -# endif -#endif + if (nbufs == 1) + r = pwrite(fd, bufs->iov_base, bufs->iov_len, off); + else if (nbufs > 1) + r = uv__pwritev(fd, bufs, nbufs, off); } -done: -#if defined(__APPLE__) - if (pthread_mutex_unlock(&lock)) - abort(); -#endif - return r; } + static ssize_t uv__fs_copyfile(uv_fs_t* req) { uv_fs_t fs_req; uv_file srcfd; uv_file dstfd; struct stat src_statsbuf; struct stat dst_statsbuf; +#if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + struct timeval times[2]; +#else + struct timespec times[2]; +#endif int dst_flags; int result; int err; @@ -1316,7 +1282,7 @@ return srcfd; /* Get the source file's mode. */ - if (fstat(srcfd, &src_statsbuf)) { + if (uv__fstat(srcfd, &src_statsbuf)) { err = UV__ERR(errno); goto out; } @@ -1344,7 +1310,7 @@ destination are not the same file. If they are the same, bail out early. */ if ((req->flags & UV_FS_COPYFILE_EXCL) == 0) { /* Get the destination file's mode. */ - if (fstat(dstfd, &dst_statsbuf)) { + if (uv__fstat(dstfd, &dst_statsbuf)) { err = UV__ERR(errno); goto out; } @@ -1358,10 +1324,68 @@ /* Truncate the file in case the destination already existed. */ if (ftruncate(dstfd, 0) != 0) { err = UV__ERR(errno); - goto out; + + /* ftruncate() on ceph-fuse fails with EACCES when the file is created + * with read only permissions. Since ftruncate() on a newly created + * file is a meaningless operation anyway, detect that condition + * and squelch the error. + */ + if (err != UV_EACCES) + goto out; + + if (dst_statsbuf.st_size > 0) + goto out; + + err = 0; } } + /** + * Change the timestamps of the destination file to match the source file. + */ +#if defined(__APPLE__) +# if MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + times[0].tv_sec = src_statsbuf.st_atimespec.tv_sec; + times[0].tv_usec = src_statsbuf.st_atimespec.tv_nsec / 1000; + times[1].tv_sec = src_statsbuf.st_mtimespec.tv_sec; + times[1].tv_usec = src_statsbuf.st_mtimespec.tv_nsec / 1000; +# else + times[0] = src_statsbuf.st_atimespec; + times[1] = src_statsbuf.st_mtimespec; +# endif +#elif defined(_AIX) + times[0].tv_sec = src_statsbuf.st_atime; + times[0].tv_nsec = src_statsbuf.st_atime_n; + times[1].tv_sec = src_statsbuf.st_mtime; + times[1].tv_nsec = src_statsbuf.st_mtime_n; +#else + times[0] = src_statsbuf.st_atim; + times[1] = src_statsbuf.st_mtim; +#endif + +#if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + if (futimes(dstfd, times) == -1) { + err = UV__ERR(errno); + goto out; + } +#else + if (futimens(dstfd, times) == -1) { + err = UV__ERR(errno); + goto out; + } +#endif + + /* + * Change the ownership and permissions of the destination file to match the + * source file. + * `cp -p` does not care about errors here, so we don't either. Reuse the + * `result` variable to silence a -Wunused-result warning. + */ +#ifndef __clang_analyzer__ /* deadcode.DeadStores */ + result = +#endif + fchown(dstfd, src_statsbuf.st_uid, src_statsbuf.st_gid); + if (fchmod(dstfd, src_statsbuf.st_mode) == -1) { err = UV__ERR(errno); #ifdef __linux__ @@ -1542,14 +1566,14 @@ uv_stat_t* buf) { STATIC_ASSERT(UV_ENOSYS != -1); #ifdef __linux__ - static int no_statx; + static _Atomic int no_statx; struct uv__statx statxbuf; int dirfd; int flags; int mode; int rc; - if (uv__load_relaxed(&no_statx)) + if (atomic_load_explicit(&no_statx, memory_order_relaxed)) return UV_ENOSYS; dirfd = AT_FDCWD; @@ -1583,30 +1607,11 @@ * implemented, rc might return 1 with 0 set as the error code in which * case we return ENOSYS. */ - uv__store_relaxed(&no_statx, 1); + atomic_store_explicit(&no_statx, 1, memory_order_relaxed); return UV_ENOSYS; } - buf->st_dev = makedev(statxbuf.stx_dev_major, statxbuf.stx_dev_minor); - buf->st_mode = statxbuf.stx_mode; - buf->st_nlink = statxbuf.stx_nlink; - buf->st_uid = statxbuf.stx_uid; - buf->st_gid = statxbuf.stx_gid; - buf->st_rdev = makedev(statxbuf.stx_rdev_major, statxbuf.stx_rdev_minor); - buf->st_ino = statxbuf.stx_ino; - buf->st_size = statxbuf.stx_size; - buf->st_blksize = statxbuf.stx_blksize; - buf->st_blocks = statxbuf.stx_blocks; - buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec; - buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec; - buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec; - buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec; - buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec; - buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec; - buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec; - buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec; - buf->st_flags = 0; - buf->st_gen = 0; + uv__statx_to_stat(&statxbuf, buf); return 0; #else @@ -1623,7 +1628,7 @@ if (ret != UV_ENOSYS) return ret; - ret = stat(path, &pbuf); + ret = uv__stat(path, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1639,7 +1644,7 @@ if (ret != UV_ENOSYS) return ret; - ret = lstat(path, &pbuf); + ret = uv__lstat(path, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1655,7 +1660,7 @@ if (ret != UV_ENOSYS) return ret; - ret = fstat(fd, &pbuf); + ret = uv__fstat(fd, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1798,7 +1803,7 @@ uv_fs_t* req; req = container_of(w, uv_fs_t, work_req); - uv__req_unregister(req->loop, req); + uv__req_unregister(req->loop); if (status == UV_ECANCELED) { assert(req->result == 0); @@ -1809,6 +1814,16 @@ } +void uv__fs_post(uv_loop_t* loop, uv_fs_t* req) { + uv__req_register(loop); + uv__work_submit(loop, + &req->work_req, + UV__WORK_FAST_IO, + uv__fs_work, + uv__fs_done); +} + + int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1850,6 +1865,9 @@ int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(CLOSE); req->file = file; + if (cb != NULL) + if (uv__iou_fs_close(loop, req)) + return 0; POST; } @@ -1897,6 +1915,9 @@ int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FDATASYNC); req->file = file; + if (cb != NULL) + if (uv__iou_fs_fsync_or_fdatasync(loop, req, /* IORING_FSYNC_DATASYNC */ 1)) + return 0; POST; } @@ -1904,6 +1925,9 @@ int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FSTAT); req->file = file; + if (cb != NULL) + if (uv__iou_fs_statx(loop, req, /* is_fstat */ 1, /* is_lstat */ 0)) + return 0; POST; } @@ -1911,6 +1935,9 @@ int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FSYNC); req->file = file; + if (cb != NULL) + if (uv__iou_fs_fsync_or_fdatasync(loop, req, /* no flags */ 0)) + return 0; POST; } @@ -1923,6 +1950,9 @@ INIT(FTRUNCATE); req->file = file; req->off = off; + if (cb != NULL) + if (uv__iou_fs_ftruncate(loop, req)) + return 0; POST; } @@ -1957,6 +1987,9 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(LSTAT); PATH; + if (cb != NULL) + if (uv__iou_fs_statx(loop, req, /* is_fstat */ 0, /* is_lstat */ 1)) + return 0; POST; } @@ -1968,6 +2001,9 @@ uv_fs_cb cb) { INIT(LINK); PATH2; + if (cb != NULL) + if (uv__iou_fs_link(loop, req)) + return 0; POST; } @@ -1980,6 +2016,9 @@ INIT(MKDIR); PATH; req->mode = mode; + if (cb != NULL) + if (uv__iou_fs_mkdir(loop, req)) + return 0; POST; } @@ -2018,6 +2057,9 @@ PATH; req->flags = flags; req->mode = mode; + if (cb != NULL) + if (uv__iou_fs_open(loop, req)) + return 0; POST; } @@ -2033,9 +2075,14 @@ if (bufs == NULL || nbufs == 0) return UV_EINVAL; + req->off = off; req->file = file; - + req->bufs = (uv_buf_t*) bufs; /* Safe, doesn't mutate |bufs| */ req->nbufs = nbufs; + + if (cb == NULL) + goto post; + req->bufs = req->bufsml; if (nbufs > ARRAY_SIZE(req->bufsml)) req->bufs = uv__malloc(nbufs * sizeof(*bufs)); @@ -2045,7 +2092,10 @@ memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); - req->off = off; + if (uv__iou_fs_read_or_write(loop, req, /* is_read */ 1)) + return 0; + +post: POST; } @@ -2123,6 +2173,9 @@ uv_fs_cb cb) { INIT(RENAME); PATH2; + if (cb != NULL) + if (uv__iou_fs_rename(loop, req)) + return 0; POST; } @@ -2153,6 +2206,9 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(STAT); PATH; + if (cb != NULL) + if (uv__iou_fs_statx(loop, req, /* is_fstat */ 0, /* is_lstat */ 0)) + return 0; POST; } @@ -2166,6 +2222,9 @@ INIT(SYMLINK); PATH2; req->flags = flags; + if (cb != NULL) + if (uv__iou_fs_symlink(loop, req)) + return 0; POST; } @@ -2173,6 +2232,9 @@ int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UNLINK); PATH; + if (cb != NULL) + if (uv__iou_fs_unlink(loop, req)) + return 0; POST; } @@ -2216,6 +2278,11 @@ memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); req->off = off; + + if (cb != NULL) + if (uv__iou_fs_read_or_write(loop, req, /* is_read */ 0)) + return 0; + POST; } @@ -2224,7 +2291,7 @@ if (req == NULL) return; - /* Only necessary for asychronous requests, i.e., requests with a callback. + /* Only necessary for asynchronous requests, i.e., requests with a callback. * Synchronous ones don't copy their arguments and have req->path and * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP and * UV_FS_MKSTEMP are the exception to the rule, they always allocate memory.
diff --git a/Utilities/cmlibuv/src/unix/fsevents.c b/Utilities/cmlibuv/src/unix/fsevents.c index bf4f1f6..7fb6bb2 100644 --- a/Utilities/cmlibuv/src/unix/fsevents.c +++ b/Utilities/cmlibuv/src/unix/fsevents.c
@@ -80,13 +80,13 @@ typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t; struct uv__cf_loop_signal_s { - QUEUE member; + struct uv__queue member; uv_fs_event_t* handle; uv__cf_loop_signal_type_t type; }; struct uv__fsevents_event_s { - QUEUE member; + struct uv__queue member; int events; char path[1]; }; @@ -98,7 +98,7 @@ FSEventStreamRef fsevent_stream; uv_sem_t fsevent_sem; uv_mutex_t fsevent_mutex; - void* fsevent_handles[2]; + struct uv__queue fsevent_handles; unsigned int fsevent_handle_count; }; @@ -132,7 +132,6 @@ static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)( CFAllocatorRef, const char*); -static CFStringEncoding (*pCFStringGetSystemEncoding)(void); static CFStringRef (*pkCFRunLoopDefaultMode); static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef, FSEventStreamCallback, @@ -141,7 +140,6 @@ FSEventStreamEventId, CFTimeInterval, FSEventStreamCreateFlags); -static void (*pFSEventStreamFlushSync)(FSEventStreamRef); static void (*pFSEventStreamInvalidate)(FSEventStreamRef); static void (*pFSEventStreamRelease)(FSEventStreamRef); static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef, @@ -152,22 +150,22 @@ #define UV__FSEVENTS_PROCESS(handle, block) \ do { \ - QUEUE events; \ - QUEUE* q; \ + struct uv__queue events; \ + struct uv__queue* q; \ uv__fsevents_event_t* event; \ int err; \ uv_mutex_lock(&(handle)->cf_mutex); \ /* Split-off all events and empty original queue */ \ - QUEUE_MOVE(&(handle)->cf_events, &events); \ + uv__queue_move(&(handle)->cf_events, &events); \ /* Get error (if any) and zero original one */ \ err = (handle)->cf_error; \ (handle)->cf_error = 0; \ uv_mutex_unlock(&(handle)->cf_mutex); \ /* Loop through events, deallocating each after processing */ \ - while (!QUEUE_EMPTY(&events)) { \ - q = QUEUE_HEAD(&events); \ - event = QUEUE_DATA(q, uv__fsevents_event_t, member); \ - QUEUE_REMOVE(q); \ + while (!uv__queue_empty(&events)) { \ + q = uv__queue_head(&events); \ + event = uv__queue_data(q, uv__fsevents_event_t, member); \ + uv__queue_remove(q); \ /* NOTE: Checking uv__is_active() is required here, because handle \ * callback may close handle and invoking it after it will lead to \ * incorrect behaviour */ \ @@ -195,14 +193,14 @@ /* Runs in CF thread, pushed event into handle's event list */ static void uv__fsevents_push_event(uv_fs_event_t* handle, - QUEUE* events, + struct uv__queue* events, int err) { assert(events != NULL || err != 0); uv_mutex_lock(&handle->cf_mutex); /* Concatenate two queues */ if (events != NULL) - QUEUE_ADD(&handle->cf_events, events); + uv__queue_add(&handle->cf_events, events); /* Propagate error */ if (err != 0) @@ -226,12 +224,12 @@ char* path; char* pos; uv_fs_event_t* handle; - QUEUE* q; + struct uv__queue* q; uv_loop_t* loop; uv__cf_loop_state_t* state; uv__fsevents_event_t* event; FSEventStreamEventFlags flags; - QUEUE head; + struct uv__queue head; loop = info; state = loop->cf_state; @@ -240,9 +238,9 @@ /* For each handle */ uv_mutex_lock(&state->fsevent_mutex); - QUEUE_FOREACH(q, &state->fsevent_handles) { - handle = QUEUE_DATA(q, uv_fs_event_t, cf_member); - QUEUE_INIT(&head); + uv__queue_foreach(q, &state->fsevent_handles) { + handle = uv__queue_data(q, uv_fs_event_t, cf_member); + uv__queue_init(&head); /* Process and filter out events */ for (i = 0; i < numEvents; i++) { @@ -278,10 +276,6 @@ path += handle->realpath_len; len -= handle->realpath_len; - /* Ignore events with path equal to directory itself */ - if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir)) - continue; - if (len == 0) { /* Since we're using fsevents to watch the file itself, * realpath == path, and we now need to get the basename of the file back @@ -320,10 +314,10 @@ event->events = UV_CHANGE; } - QUEUE_INSERT_TAIL(&head, &event->member); + uv__queue_insert_tail(&head, &event->member); } - if (!QUEUE_EMPTY(&head)) + if (!uv__queue_empty(&head)) uv__fsevents_push_event(handle, &head, 0); } uv_mutex_unlock(&state->fsevent_mutex); @@ -331,8 +325,9 @@ /* Runs in CF thread */ -static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { - uv__cf_loop_state_t* state; +static int uv__fsevents_create_stream(uv__cf_loop_state_t* state, + uv_loop_t* loop, + CFArrayRef paths) { FSEventStreamContext ctx; FSEventStreamRef ref; CFAbsoluteTime latency; @@ -373,10 +368,7 @@ flags); assert(ref != NULL); - state = loop->cf_state; - pFSEventStreamScheduleWithRunLoop(ref, - state->loop, - *pkCFRunLoopDefaultMode); + pFSEventStreamScheduleWithRunLoop(ref, state->loop, *pkCFRunLoopDefaultMode); if (!pFSEventStreamStart(ref)) { pFSEventStreamInvalidate(ref); pFSEventStreamRelease(ref); @@ -389,11 +381,7 @@ /* Runs in CF thread */ -static void uv__fsevents_destroy_stream(uv_loop_t* loop) { - uv__cf_loop_state_t* state; - - state = loop->cf_state; - +static void uv__fsevents_destroy_stream(uv__cf_loop_state_t* state) { if (state->fsevent_stream == NULL) return; @@ -408,10 +396,10 @@ /* Runs in CF thread, when there're new fsevent handles to add to stream */ -static void uv__fsevents_reschedule(uv_fs_event_t* handle, +static void uv__fsevents_reschedule(uv__cf_loop_state_t* state, + uv_loop_t* loop, uv__cf_loop_signal_type_t type) { - uv__cf_loop_state_t* state; - QUEUE* q; + struct uv__queue* q; uv_fs_event_t* curr; CFArrayRef cf_paths; CFStringRef* paths; @@ -419,7 +407,6 @@ int err; unsigned int path_count; - state = handle->loop->cf_state; paths = NULL; cf_paths = NULL; err = 0; @@ -438,7 +425,7 @@ uv_mutex_unlock(&state->fsevent_mutex); /* Destroy previous FSEventStream */ - uv__fsevents_destroy_stream(handle->loop); + uv__fsevents_destroy_stream(state); /* Any failure below will be a memory failure */ err = UV_ENOMEM; @@ -455,9 +442,9 @@ q = &state->fsevent_handles; for (; i < path_count; i++) { - q = QUEUE_NEXT(q); + q = uv__queue_next(q); assert(q != &state->fsevent_handles); - curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); + curr = uv__queue_data(q, uv_fs_event_t, cf_member); assert(curr->realpath != NULL); paths[i] = @@ -478,7 +465,7 @@ err = UV_ENOMEM; goto final; } - err = uv__fsevents_create_stream(handle->loop, cf_paths); + err = uv__fsevents_create_stream(state, loop, cf_paths); } final: @@ -495,8 +482,8 @@ /* Broadcast error to all handles */ uv_mutex_lock(&state->fsevent_mutex); - QUEUE_FOREACH(q, &state->fsevent_handles) { - curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); + uv__queue_foreach(q, &state->fsevent_handles) { + curr = uv__queue_data(q, uv_fs_event_t, cf_member); uv__fsevents_push_event(curr, NULL, err); } uv_mutex_unlock(&state->fsevent_mutex); @@ -563,10 +550,8 @@ V(core_foundation_handle, CFRunLoopStop); V(core_foundation_handle, CFRunLoopWakeUp); V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation); - V(core_foundation_handle, CFStringGetSystemEncoding); V(core_foundation_handle, kCFRunLoopDefaultMode); V(core_services_handle, FSEventStreamCreate); - V(core_services_handle, FSEventStreamFlushSync); V(core_services_handle, FSEventStreamInvalidate); V(core_services_handle, FSEventStreamRelease); V(core_services_handle, FSEventStreamScheduleWithRunLoop); @@ -617,7 +602,7 @@ if (err) goto fail_sem_init; - QUEUE_INIT(&loop->cf_signals); + uv__queue_init(&loop->cf_signals); err = uv_sem_init(&state->fsevent_sem, 0); if (err) @@ -627,7 +612,7 @@ if (err) goto fail_fsevent_mutex_init; - QUEUE_INIT(&state->fsevent_handles); + uv__queue_init(&state->fsevent_handles); state->fsevent_need_reschedule = 0; state->fsevent_handle_count = 0; @@ -686,7 +671,7 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) { uv__cf_loop_signal_t* s; uv__cf_loop_state_t* state; - QUEUE* q; + struct uv__queue* q; if (loop->cf_state == NULL) return; @@ -699,10 +684,10 @@ uv_mutex_destroy(&loop->cf_mutex); /* Free any remaining data */ - while (!QUEUE_EMPTY(&loop->cf_signals)) { - q = QUEUE_HEAD(&loop->cf_signals); - s = QUEUE_DATA(q, uv__cf_loop_signal_t, member); - QUEUE_REMOVE(q); + while (!uv__queue_empty(&loop->cf_signals)) { + q = uv__queue_head(&loop->cf_signals); + s = uv__queue_data(q, uv__cf_loop_signal_t, member); + uv__queue_remove(q); uv__free(s); } @@ -746,28 +731,28 @@ static void uv__cf_loop_cb(void* arg) { uv_loop_t* loop; uv__cf_loop_state_t* state; - QUEUE* item; - QUEUE split_head; + struct uv__queue* item; + struct uv__queue split_head; uv__cf_loop_signal_t* s; loop = arg; state = loop->cf_state; uv_mutex_lock(&loop->cf_mutex); - QUEUE_MOVE(&loop->cf_signals, &split_head); + uv__queue_move(&loop->cf_signals, &split_head); uv_mutex_unlock(&loop->cf_mutex); - while (!QUEUE_EMPTY(&split_head)) { - item = QUEUE_HEAD(&split_head); - QUEUE_REMOVE(item); + while (!uv__queue_empty(&split_head)) { + item = uv__queue_head(&split_head); + uv__queue_remove(item); - s = QUEUE_DATA(item, uv__cf_loop_signal_t, member); + s = uv__queue_data(item, uv__cf_loop_signal_t, member); /* This was a termination signal */ if (s->handle == NULL) pCFRunLoopStop(state->loop); else - uv__fsevents_reschedule(s->handle, s->type); + uv__fsevents_reschedule(state, loop, s->type); uv__free(s); } @@ -789,7 +774,7 @@ item->type = type; uv_mutex_lock(&loop->cf_mutex); - QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member); + uv__queue_insert_tail(&loop->cf_signals, &item->member); state = loop->cf_state; assert(state != NULL); @@ -804,6 +789,7 @@ /* Runs in UV loop to initialize handle */ int uv__fsevents_init(uv_fs_event_t* handle) { + char* buf; int err; uv__cf_loop_state_t* state; @@ -812,13 +798,17 @@ return err; /* Get absolute path to file */ - handle->realpath = realpath(handle->path, NULL); - if (handle->realpath == NULL) + buf = realpath(handle->path, NULL); + if (buf == NULL) return UV__ERR(errno); + handle->realpath = uv__strdup(buf); + free(buf); /* _Not_ uv__free. */ + if (handle->realpath == NULL) + return UV_ENOMEM; handle->realpath_len = strlen(handle->realpath); /* Initialize event queue */ - QUEUE_INIT(&handle->cf_events); + uv__queue_init(&handle->cf_events); handle->cf_error = 0; /* @@ -843,7 +833,7 @@ /* Insert handle into the list */ state = handle->loop->cf_state; uv_mutex_lock(&state->fsevent_mutex); - QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member); + uv__queue_insert_tail(&state->fsevent_handles, &handle->cf_member); state->fsevent_handle_count++; state->fsevent_need_reschedule = 1; uv_mutex_unlock(&state->fsevent_mutex); @@ -883,7 +873,7 @@ /* Remove handle from the list */ state = handle->loop->cf_state; uv_mutex_lock(&state->fsevent_mutex); - QUEUE_REMOVE(&handle->cf_member); + uv__queue_remove(&handle->cf_member); state->fsevent_handle_count--; state->fsevent_need_reschedule = 1; uv_mutex_unlock(&state->fsevent_mutex);
diff --git a/Utilities/cmlibuv/src/unix/getaddrinfo.c b/Utilities/cmlibuv/src/unix/getaddrinfo.c index 77337ac..6c44012 100644 --- a/Utilities/cmlibuv/src/unix/getaddrinfo.c +++ b/Utilities/cmlibuv/src/unix/getaddrinfo.c
@@ -89,9 +89,7 @@ } assert(!"unknown EAI_* error code"); abort(); -#ifndef __SUNPRO_C return 0; /* Pacify compiler. */ -#endif } @@ -109,7 +107,7 @@ uv_getaddrinfo_t* req; req = container_of(w, uv_getaddrinfo_t, work_req); - uv__req_unregister(req->loop, req); + uv__req_unregister(req->loop); /* See initialization in uv_getaddrinfo(). */ if (req->hints)
diff --git a/Utilities/cmlibuv/src/unix/getnameinfo.c b/Utilities/cmlibuv/src/unix/getnameinfo.c index b695081..e763ab1 100644 --- a/Utilities/cmlibuv/src/unix/getnameinfo.c +++ b/Utilities/cmlibuv/src/unix/getnameinfo.c
@@ -58,7 +58,7 @@ char* service; req = container_of(w, uv_getnameinfo_t, work_req); - uv__req_unregister(req->loop, req); + uv__req_unregister(req->loop); host = service = NULL; if (status == UV_ECANCELED) {
diff --git a/Utilities/cmlibuv/src/unix/haiku.c b/Utilities/cmlibuv/src/unix/haiku.c index cf17d83..0d3645f 100644 --- a/Utilities/cmlibuv/src/unix/haiku.c +++ b/Utilities/cmlibuv/src/unix/haiku.c
@@ -84,6 +84,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { area_info area; ssize_t cookie; @@ -115,7 +120,7 @@ int i; status_t status; system_info system; - uint32_t topology_count; + uint32 topology_count; /* Haiku expects address of uint32, not uint32_t */ uint64_t cpuspeed; uv_cpu_info_t* cpu_info;
diff --git a/Utilities/cmlibuv/src/unix/hurd.c b/Utilities/cmlibuv/src/unix/hurd.c index d19ea63..63c8781 100644 --- a/Utilities/cmlibuv/src/unix/hurd.c +++ b/Utilities/cmlibuv/src/unix/hurd.c
@@ -165,3 +165,8 @@ uint64_t uv_get_constrained_memory(void) { return 0; /* Memory constraints are unknown. */ } + + +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +}
diff --git a/Utilities/cmlibuv/src/unix/ibmi.c b/Utilities/cmlibuv/src/unix/ibmi.c index 580ea1f..d5adaad 100644 --- a/Utilities/cmlibuv/src/unix/ibmi.c +++ b/Utilities/cmlibuv/src/unix/ibmi.c
@@ -249,6 +249,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { SSTS0200 rcvr; @@ -389,6 +394,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { uv_interface_address_t* address; struct ifaddrs_pase *ifap = NULL, *cur; + size_t namelen; + char* name; int inet6, r = 0; *count = 0; @@ -398,6 +405,7 @@ return UV_ENOSYS; /* The first loop to get the size of the array to be allocated */ + namelen = 0; for (cur = ifap; cur; cur = cur->ifa_next) { if (!(cur->ifa_addr->sa_family == AF_INET6 || cur->ifa_addr->sa_family == AF_INET)) @@ -406,6 +414,7 @@ if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING)) continue; + namelen += strlen(cur->ifa_name) + 1; (*count)++; } @@ -415,11 +424,13 @@ } /* Alloc the return interface structs */ - *addresses = uv__calloc(*count, sizeof(**addresses)); + *addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen); if (*addresses == NULL) { Qp2freeifaddrs(ifap); return UV_ENOMEM; } + + name = (char*) &(*addresses)[*count]; address = *addresses; /* The second loop to fill in the array */ @@ -431,7 +442,9 @@ if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING)) continue; - address->name = uv__strdup(cur->ifa_name); + namelen = strlen(cur->ifa_name) + 1; + address->name = memcpy(name, cur->ifa_name, namelen); + name += namelen; inet6 = (cur->ifa_addr->sa_family == AF_INET6); @@ -492,16 +505,6 @@ } -void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { - int i; - - for (i = 0; i < count; ++i) { - uv__free(addresses[i].name); - } - - uv__free(addresses); -} - char** uv_setup_args(int argc, char** argv) { char exepath[UV__PATH_MAX]; char* s; @@ -536,3 +539,9 @@ void uv__process_title_cleanup(void) { } +void uv__ahafs_event(uv_loop_t* loop, + uv__io_t* event_watch, + unsigned int fflags) { + /* Stub function to satisfy the linker. */ +} +
diff --git a/Utilities/cmlibuv/src/unix/internal.h b/Utilities/cmlibuv/src/unix/internal.h index 1b10516..55af6d4 100644 --- a/Utilities/cmlibuv/src/unix/internal.h +++ b/Utilities/cmlibuv/src/unix/internal.h
@@ -26,21 +26,38 @@ #include <assert.h> #include <limits.h> /* _POSIX_PATH_MAX, PATH_MAX */ +#include <stdint.h> #include <stdlib.h> /* abort */ #include <string.h> /* strrchr */ #include <fcntl.h> /* O_CLOEXEC and O_NONBLOCK, if supported. */ #include <stdio.h> #include <errno.h> #include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#if defined(__APPLE__) || defined(__DragonFly__) || \ + defined(__FreeBSD__) || defined(__NetBSD__) +#include <sys/event.h> +#endif + +#define uv__msan_unpoison(p, n) \ + do { \ + (void) (p); \ + (void) (n); \ + } while (0) + +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# include <sanitizer/msan_interface.h> +# undef uv__msan_unpoison +# define uv__msan_unpoison __msan_unpoison +# endif +#endif #if defined(__STRICT_ANSI__) # define inline __inline #endif -#if defined(__linux__) -# include "linux-syscalls.h" -#endif /* __linux__ */ - #if defined(__MVS__) # include "os390-syscalls.h" #endif /* __MVS__ */ @@ -58,8 +75,11 @@ # include <poll.h> #endif /* _AIX */ -#if defined(__APPLE__) && !TARGET_OS_IPHONE -# include <AvailabilityMacros.h> +#if defined(__APPLE__) +# include "darwin-syscalls.h" +# if !TARGET_OS_IPHONE +# include <AvailabilityMacros.h> +# endif #endif /* @@ -89,14 +109,14 @@ # define pthread_sigmask(how, set, oldset) \ uv__pthread_sigmask(how, set, oldset) int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); -#elif defined(__ANDROID__) -int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); -# ifdef pthread_sigmask -# undef pthread_sigmask -# endif -# define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset) #endif +union uv__sockaddr { + struct sockaddr_in6 in6; + struct sockaddr_in in; + struct sockaddr addr; +}; + #define ACCESS_ONCE(type, var) \ (*(volatile type*) &(var)) @@ -133,6 +153,11 @@ /* Leans on the fact that, on Linux, POLLRDHUP == EPOLLRDHUP. */ #ifdef POLLRDHUP # define UV__POLLRDHUP POLLRDHUP +#elif defined(__QNX__) +/* On QNX, POLLRDHUP is not available and the 0x2000 workaround + * leads to undefined bahavior. + */ +# define UV__POLLRDHUP 0 #else # define UV__POLLRDHUP 0x2000 #endif @@ -156,7 +181,8 @@ /* loop flags */ enum { UV_LOOP_BLOCK_SIGPROF = 0x1, - UV_LOOP_REAP_CHILDREN = 0x2 + UV_LOOP_REAP_CHILDREN = 0x2, + UV_LOOP_ENABLE_IO_URING_SQPOLL = 0x4 }; /* flags of excluding ifaddr */ @@ -176,12 +202,42 @@ int fds[1]; }; +#ifdef __linux__ +struct uv__statx_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t unused0; +}; + +struct uv__statx { + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t unused0; + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + struct uv__statx_timestamp stx_atime; + struct uv__statx_timestamp stx_btime; + struct uv__statx_timestamp stx_ctime; + struct uv__statx_timestamp stx_mtime; + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + uint64_t unused1[14]; +}; +#endif /* __linux__ */ #if defined(_AIX) || \ defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__linux__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) @@ -212,12 +268,71 @@ int uv__close_nocheckstdio(int fd); int uv__close_nocancel(int fd); int uv__socket(int domain, int type, int protocol); +int uv__sock_reuseport(int fd); ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags); void uv__make_close_pending(uv_handle_t* handle); int uv__getiovmax(void); -void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd); -void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events); +typedef enum { + UV__NO_IO_CB, + UV__AHAFS_EVENT, + UV__ASYNC_IO, + UV__FS_EVENT, + UV__FS_EVENT_READ, + UV__INOTIFY_READ, + UV__POLL_IO, + UV__SIGNAL_EVENT, + UV__SERVER_IO, + UV__STREAM_IO, + UV__UDP_IO, +} uv__io_cb_t; + +#define uv__io_cb_get(w) ((uv__io_cb_t)((w)->bits & 15)) +#define uv__io_cb_set(w, cb) \ + do { \ + (w)->bits -= uv__io_cb_get(w); \ + (w)->bits |= (cb) & 15; \ + } while (0) + +void uv__ahafs_event(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__fs_event_read(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); + +#ifndef _AIX +#define uv__ahafs_event(loop, w, events) UNREACHABLE() +#endif + +#if !defined(__APPLE__) && \ + !defined(__DragonFly__) && \ + !defined(__FreeBSD__) && \ + !defined(__NetBSD__) && \ + !defined(__OpenBSD__) +#define uv__fs_event(loop, w, events) UNREACHABLE() +#endif + +#ifndef __linux__ +#define uv__inotify_read(loop, w, events) UNREACHABLE() +#endif + +#ifndef __sun__ +#define uv__fs_event_read(loop, w, events) UNREACHABLE() +#endif + +void uv__io_cb(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__io_init(uv__io_t* w, uv__io_cb_t cb, int fd); +int uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events); +int uv__io_init_start(uv_loop_t* loop, + uv__io_t* w, + uv__io_cb_t cb, + int fd, + unsigned int events); void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events); void uv__io_close(uv_loop_t* loop, uv__io_t* w); void uv__io_feed(uv_loop_t* loop, uv__io_t* w); @@ -225,6 +340,8 @@ int uv__io_check_fd(uv_loop_t* loop, int fd); void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */ int uv__io_fork(uv_loop_t* loop); +void uv__io_poll_prepare(uv_loop_t* loop, sigset_t* pset, int timeout); +void uv__io_poll_check(uv_loop_t* loop, sigset_t* pset); int uv__fd_exists(uv_loop_t* loop, int fd); /* async */ @@ -254,7 +371,14 @@ /* tcp */ int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); int uv__tcp_nodelay(int fd, int on); -int uv__tcp_keepalive(int fd, int on, unsigned int delay); +int uv__tcp_keepalive(int fd, + int on, + unsigned int idle, + unsigned int intvl, + unsigned int cnt); + +/* tty */ +void uv__tty_close(uv_tty_t* handle); /* pipe */ int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); @@ -268,7 +392,6 @@ /* platform specific */ uint64_t uv__hrtime(uv_clocktype_t type); int uv__kqueue_init(uv_loop_t* loop); -int uv__epoll_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop); void uv__platform_loop_delete(uv_loop_t* loop); void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); @@ -285,11 +408,12 @@ void uv__process_close(uv_process_t* handle); void uv__stream_close(uv_stream_t* handle); void uv__tcp_close(uv_tcp_t* handle); +int uv__thread_setname(const char* name); +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size); size_t uv__thread_stack_size(void); void uv__udp_close(uv_udp_t* handle); void uv__udp_finish_close(uv_udp_t* handle); FILE* uv__open_file(const char* path); -int uv__getpwuid_r(uv_passwd_t* pwd); int uv__search_path(const char* prog, char* buf, size_t* buflen); void uv__wait_children(uv_loop_t* loop); @@ -300,6 +424,40 @@ int uv__random_readpath(const char* path, void* buf, size_t buflen); int uv__random_sysctl(void* buf, size_t buflen); +/* io_uring */ +#if defined(__linux__) && !defined(CMAKE_BOOTSTRAP) +int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop, + uv_fs_t* req, + uint32_t fsync_flags); +int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_open(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_read_or_write(uv_loop_t* loop, + uv_fs_t* req, + int is_read); +int uv__iou_fs_rename(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_statx(uv_loop_t* loop, + uv_fs_t* req, + int is_fstat, + int is_lstat); +int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req); +#else +#define uv__iou_fs_close(loop, req) 0 +#define uv__iou_fs_ftruncate(loop, req) 0 +#define uv__iou_fs_fsync_or_fdatasync(loop, req, fsync_flags) 0 +#define uv__iou_fs_link(loop, req) 0 +#define uv__iou_fs_mkdir(loop, req) 0 +#define uv__iou_fs_open(loop, req) 0 +#define uv__iou_fs_read_or_write(loop, req, is_read) 0 +#define uv__iou_fs_rename(loop, req) 0 +#define uv__iou_fs_statx(loop, req, is_fstat, is_lstat) 0 +#define uv__iou_fs_symlink(loop, req) 0 +#define uv__iou_fs_unlink(loop, req) 0 +#endif + #if defined(__APPLE__) && !defined(CMAKE_BOOTSTRAP) int uv___stream_fd(const uv_stream_t* handle); #define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle))) @@ -333,8 +491,53 @@ return s + 1; } +UV_UNUSED(static int uv__fstat(int fd, struct stat* s)) { + int rc; + + rc = fstat(fd, s); + if (rc >= 0) + uv__msan_unpoison(s, sizeof(*s)); + + return rc; +} + +UV_UNUSED(static int uv__lstat(const char* path, struct stat* s)) { + int rc; + + rc = lstat(path, s); + if (rc >= 0) + uv__msan_unpoison(s, sizeof(*s)); + + return rc; +} + +UV_UNUSED(static int uv__stat(const char* path, struct stat* s)) { + int rc; + + rc = stat(path, s); + if (rc >= 0) + uv__msan_unpoison(s, sizeof(*s)); + + return rc; +} + #if defined(__linux__) -int uv__inotify_fork(uv_loop_t* loop, void* old_watchers); +void uv__fs_post(uv_loop_t* loop, uv_fs_t* req); +ssize_t +uv__fs_copy_file_range(int fd_in, + off_t* off_in, + int fd_out, + off_t* off_out, + size_t len, + unsigned int flags); +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf); +void uv__statx_to_stat(const struct uv__statx* statxbuf, uv_stat_t* buf); +ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags); +unsigned uv__kernel_version(void); #endif typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); @@ -344,22 +547,6 @@ struct sockaddr* name, int* namelen); -#if defined(__linux__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__DragonFly__) -#define HAVE_MMSG 1 -struct uv__mmsghdr { - struct msghdr msg_hdr; - unsigned int msg_len; -}; - -int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); -int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); -#else -#define HAVE_MMSG 0 -#endif - #if defined(__sun) #if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L size_t strnlen(const char* s, size_t maxlen); @@ -376,5 +563,46 @@ unsigned int flags); #endif +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 1301000) +#define UV__CPU_AFFINITY_SUPPORTED 1 +#else +#define UV__CPU_AFFINITY_SUPPORTED 0 +#endif + +#ifdef __linux__ +int uv__get_constrained_cpu(long long* quota); +#endif + +#if defined(__sun) && !defined(__illumos__) +#ifdef SO_FLOW_NAME +/* Since it's impossible to detect the Solaris 11.4 version via OS macros, + * so we check the presence of the socket option SO_FLOW_NAME that was first + * introduced to Solaris 11.4 and define a custom macro for determining 11.4. + */ +#define UV__SOLARIS_11_4 (1) +#else +#define UV__SOLARIS_11_4 (0) +#endif +#endif + +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) +/* EVFILT_USER is available since OS X 10.6, DragonFlyBSD 4.0, + * FreeBSD 8.1, and NetBSD 10.0. + * + * Note that even though EVFILT_USER is defined on the current system, + * it may still fail to work at runtime somehow. In that case, we fall + * back to pipe-based signaling. + */ +#define UV__KQUEUE_EVFILT_USER 1 +/* Magic number of identifier used for EVFILT_USER during runtime detection. + * There are no Google hits for this number when I create it. That way, + * people will be directed here if this number gets printed due to some + * kqueue error and they google for help. */ +#define UV__KQUEUE_EVFILT_USER_IDENT 0x1e7e7711 +#else +#define UV__KQUEUE_EVFILT_USER 0 +#endif + +extern char* uv_saved_argv0; #endif /* UV_UNIX_INTERNAL_H_ */
diff --git a/Utilities/cmlibuv/src/unix/kqueue.c b/Utilities/cmlibuv/src/unix/kqueue.c index 581c552..471cd45 100644 --- a/Utilities/cmlibuv/src/unix/kqueue.c +++ b/Utilities/cmlibuv/src/unix/kqueue.c
@@ -30,6 +30,9 @@ #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> +#if defined(__FreeBSD__) +#include <sys/user.h> +#endif #include <unistd.h> #include <fcntl.h> #include <time.h> @@ -45,8 +48,6 @@ #define EV_OOBAND EV_FLAG1 #endif -static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags); - int uv__kqueue_init(uv_loop_t* loop) { loop->backend_fd = kqueue(); @@ -60,7 +61,7 @@ #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 -static int uv__has_forked_with_cfrunloop; +static _Atomic int uv__has_forked_with_cfrunloop; #endif int uv__io_fork(uv_loop_t* loop) { @@ -82,7 +83,9 @@ process. So we sidestep the issue by pretending like we never started it in the first place. */ - uv__store_relaxed(&uv__has_forked_with_cfrunloop, 1); + atomic_store_explicit(&uv__has_forked_with_cfrunloop, + 1, + memory_order_relaxed); uv__free(loop->cf_state); loop->cf_state = NULL; } @@ -92,30 +95,73 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { - struct kevent ev; - int rc; + struct kevent ev[2]; + struct stat sb; +#ifdef __APPLE__ + char path[MAXPATHLEN]; +#endif - rc = 0; - EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0); - if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) - rc = UV__ERR(errno); + if (uv__fstat(fd, &sb)) + return UV__ERR(errno); - EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0); - if (rc == 0) - if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) - abort(); + /* On FreeBSD, kqueue only supports EVFILT_READ notification for regular files + * and always reports ready events for writing, resulting in busy-looping. + * + * On Darwin, DragonFlyBSD, NetBSD and OpenBSD, kqueue reports ready events for + * regular files as readable and writable only once, acting like an EV_ONESHOT. + * + * Neither of the above cases should be added to the kqueue. + */ + if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) + return UV_EINVAL; - return rc; +#ifdef __APPLE__ + /* On Darwin (both macOS and iOS), in addition to regular files, FIFOs also don't + * work properly with kqueue: the disconnection from the last writer won't trigger + * an event for kqueue in spite of what the man pages say. Thus, we also disallow + * the case of S_IFIFO. */ + if (S_ISFIFO(sb.st_mode)) { + /* File descriptors of FIFO, pipe and kqueue share the same type of file, + * therefore there is no way to tell them apart via stat.st_mode&S_IFMT. + * Fortunately, FIFO is the only one that has a persisted file on filesystem, + * from which we're able to make the distinction for it. */ + if (!fcntl(fd, F_GETPATH, path)) + return UV_EINVAL; + } +#endif + + EV_SET(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0); + EV_SET(ev + 1, fd, EVFILT_READ, EV_DELETE, 0, 0, 0); + if (kevent(loop->backend_fd, ev, 2, NULL, 0, NULL)) + return UV__ERR(errno); + + return 0; +} + + +static void uv__kqueue_delete(int kqfd, const struct kevent *ev) { + struct kevent change; + + EV_SET(&change, ev->ident, ev->filter, EV_DELETE, 0, 0, 0); + + if (0 == kevent(kqfd, &change, 1, NULL, 0, NULL)) + return; + + if (errno == EBADF || errno == ENOENT) + return; + + abort(); } void uv__io_poll(uv_loop_t* loop, int timeout) { + uv__loop_internal_fields_t* lfields; struct kevent events[1024]; struct kevent* ev; struct timespec spec; unsigned int nevents; unsigned int revents; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; uv_process_t* process; sigset_t* pset; @@ -134,18 +180,19 @@ int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } + lfields = uv__get_internal_fields(loop); nevents = 0; - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); @@ -155,7 +202,7 @@ fflags = 0; op = EV_ADD; - if (w->cb == uv__fs_event) { + if (UV__FS_EVENT == uv__io_cb_get(w)) { filter = EVFILT_VNODE; fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE; @@ -205,7 +252,7 @@ base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -214,38 +261,25 @@ } for (;; nevents = 0) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; } - if (pset != NULL) - pthread_sigmask(SIG_BLOCK, pset, NULL); - + uv__io_poll_prepare(loop, pset, timeout); nfds = kevent(loop->backend_fd, events, nevents, events, ARRAY_SIZE(events), timeout == -1 ? NULL : &spec); + uv__io_poll_check(loop, pset); if (nfds == -1) assert(errno == EINTR); - - if (pset != NULL) - pthread_sigmask(SIG_UNBLOCK, pset, NULL); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - uv__update_time(loop); + else if (nfds == 0) + /* Unlimited timeout should only return with events or signal. */ + assert(timeout != -1); if (nfds == 0 || nfds == -1) { /* If kqueue is empty or interrupted, we might still have children ready @@ -261,8 +295,6 @@ timeout = user_timeout; reset_timeout = 0; } else if (nfds == 0) { - /* Reached the user timeout value. */ - assert(timeout != -1); return; } @@ -282,8 +314,8 @@ /* Handle kevent NOTE_EXIT results */ if (ev->filter == EVFILT_PROC) { - QUEUE_FOREACH(q, &loop->process_handles) { - process = QUEUE_DATA(q, uv_process_t, queue); + uv__queue_foreach(q, &loop->process_handles) { + process = uv__queue_data(q, uv_process_t, queue); if (process->pid == fd) { process->flags |= UV_HANDLE_REAP; loop->flags |= UV_LOOP_REAP_CHILDREN; @@ -300,23 +332,27 @@ w = loop->watchers[fd]; if (w == NULL) { - /* File descriptor that we've stopped watching, disarm it. - * TODO: batch up. */ - struct kevent events[1]; - - EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); - if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) - if (errno != EBADF && errno != ENOENT) - abort(); - + /* File descriptor that we've stopped watching, disarm it. */ + uv__kqueue_delete(loop->backend_fd, ev); continue; } +#if UV__KQUEUE_EVFILT_USER + if (ev->filter == EVFILT_USER) { + w = &loop->async_io_watcher; + assert(fd == w->fd); + uv__metrics_update_idle_time(loop); + uv__io_cb(loop, w, w->events); + nevents++; + continue; + } +#endif + if (ev->filter == EVFILT_VNODE) { assert(w->events == POLLIN); assert(w->pevents == POLLIN); uv__metrics_update_idle_time(loop); - w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */ + uv__io_cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */ nevents++; continue; } @@ -324,47 +360,27 @@ revents = 0; if (ev->filter == EVFILT_READ) { - if (w->pevents & POLLIN) { + if (w->pevents & POLLIN) revents |= POLLIN; - w->rcount = ev->data; - } else { - /* TODO batch up */ - struct kevent events[1]; - EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); - if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) - if (errno != ENOENT) - abort(); - } + else + uv__kqueue_delete(loop->backend_fd, ev); + if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP)) revents |= UV__POLLRDHUP; } if (ev->filter == EV_OOBAND) { - if (w->pevents & UV__POLLPRI) { + if (w->pevents & UV__POLLPRI) revents |= UV__POLLPRI; - w->rcount = ev->data; - } else { - /* TODO batch up */ - struct kevent events[1]; - EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); - if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) - if (errno != ENOENT) - abort(); - } + else + uv__kqueue_delete(loop->backend_fd, ev); } if (ev->filter == EVFILT_WRITE) { - if (w->pevents & POLLOUT) { + if (w->pevents & POLLOUT) revents |= POLLOUT; - w->wcount = ev->data; - } else { - /* TODO batch up */ - struct kevent events[1]; - EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); - if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) - if (errno != ENOENT) - abort(); - } + else + uv__kqueue_delete(loop->backend_fd, ev); } if (ev->flags & EV_ERROR) @@ -380,7 +396,7 @@ have_signals = 1; } else { uv__metrics_update_idle_time(loop); - w->cb(loop, w, revents); + uv__io_cb(loop, w, revents); } nevents++; @@ -391,14 +407,16 @@ uv__wait_children(loop); } + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + uv__signal_event(loop, &loop->signal_io_watcher, POLLIN); } loop->watchers[loop->nwatchers] = NULL; @@ -454,7 +472,7 @@ } -static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) { +void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) { uv_fs_event_t* handle; struct kevent ev; int events; @@ -479,6 +497,26 @@ */ if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0) path = uv__basename_r(pathbuf); +#elif defined(F_KINFO) + /* We try to get the file info reference from the file descriptor. + * the struct's kf_structsize must be initialised beforehand + * whether with the KINFO_FILE_SIZE constant or this way. + */ + struct stat statbuf; + struct kinfo_file kf; + + if (handle->event_watcher.fd != -1 && + (!uv__fstat(handle->event_watcher.fd, &statbuf) && !(statbuf.st_mode & S_IFDIR))) { + /* we are purposely not using KINFO_FILE_SIZE here + * as it is not available on non intl archs + * and here it gives 1392 too on intel. + * anyway, the man page also mentions we can proceed + * this way. + */ + kf.kf_structsize = sizeof(kf); + if (fcntl(handle->event_watcher.fd, F_KINFO, &kf) == 0) + path = uv__basename_r(kf.kf_path); + } #endif handle->cb(handle, path, events, 0); @@ -507,6 +545,7 @@ const char* path, unsigned int flags) { int fd; + int r; #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 struct stat statbuf; #endif @@ -534,14 +573,14 @@ handle->realpath_len = 0; handle->cf_flags = flags; - if (fstat(fd, &statbuf)) + if (uv__fstat(fd, &statbuf)) goto fallback; /* FSEvents works only with directories */ if (!(statbuf.st_mode & S_IFDIR)) goto fallback; - if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) { - int r; + if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop, + memory_order_relaxed)) { /* The fallback fd is no longer needed */ uv__close_nocheckstdio(fd); handle->event_watcher.fd = -1; @@ -557,11 +596,16 @@ fallback: #endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ - uv__handle_start(handle); - uv__io_init(&handle->event_watcher, uv__fs_event, fd); - uv__io_start(handle->loop, &handle->event_watcher, POLLIN); + r = uv__io_init_start(handle->loop, + &handle->event_watcher, + UV__FS_EVENT, + fd, + POLLIN); - return 0; + if (!r) + uv__handle_start(handle); + + return r; } @@ -575,7 +619,8 @@ uv__handle_stop(handle); #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) + if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop, + memory_order_relaxed)) if (handle->cf_cb != NULL) r = uv__fsevents_close(handle); #endif
diff --git a/Utilities/cmlibuv/src/unix/linux-core.c b/Utilities/cmlibuv/src/unix/linux-core.c deleted file mode 100644 index 23a7daf..0000000 --- a/Utilities/cmlibuv/src/unix/linux-core.c +++ /dev/null
@@ -1,834 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their - * EPOLL* counterparts. We use the POLL* variants in this file because that - * is what libuv uses elsewhere. - */ - -#include "uv.h" -#include "internal.h" - -#include <inttypes.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <errno.h> - -#include <net/if.h> -#include <sys/epoll.h> -#include <sys/param.h> -#include <sys/prctl.h> -#include <sys/sysinfo.h> -#include <unistd.h> -#include <fcntl.h> -#include <time.h> - -#define HAVE_IFADDRS_H 1 - -# if defined(__ANDROID_API__) && __ANDROID_API__ < 24 -# undef HAVE_IFADDRS_H -#endif - -#ifdef __UCLIBC__ -# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32 -# undef HAVE_IFADDRS_H -# endif -#endif - -#ifdef HAVE_IFADDRS_H -# include <ifaddrs.h> -# include <sys/socket.h> -# include <net/ethernet.h> -# include <netpacket/packet.h> -#endif /* HAVE_IFADDRS_H */ - -/* Available from 2.6.32 onwards. */ -#ifndef CLOCK_MONOTONIC_COARSE -# define CLOCK_MONOTONIC_COARSE 6 -#endif - -/* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't - * include that file because it conflicts with <time.h>. We'll just have to - * define it ourselves. - */ -#ifndef CLOCK_BOOTTIME -# define CLOCK_BOOTTIME 7 -#endif - -static int read_models(unsigned int numcpus, uv_cpu_info_t* ci); -static int read_times(FILE* statfile_fp, - unsigned int numcpus, - uv_cpu_info_t* ci); -static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); -static uint64_t read_cpufreq(unsigned int cpunum); - -int uv__platform_loop_init(uv_loop_t* loop) { - - loop->inotify_fd = -1; - loop->inotify_watchers = NULL; - - return uv__epoll_init(loop); -} - - -int uv__io_fork(uv_loop_t* loop) { - int err; - void* old_watchers; - - old_watchers = loop->inotify_watchers; - - uv__close(loop->backend_fd); - loop->backend_fd = -1; - uv__platform_loop_delete(loop); - - err = uv__platform_loop_init(loop); - if (err) - return err; - - return uv__inotify_fork(loop, old_watchers); -} - - -void uv__platform_loop_delete(uv_loop_t* loop) { - if (loop->inotify_fd == -1) return; - uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN); - uv__close(loop->inotify_fd); - loop->inotify_fd = -1; -} - - - -uint64_t uv__hrtime(uv_clocktype_t type) { - static clock_t fast_clock_id = -1; - struct timespec t; - clock_t clock_id; - - /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has - * millisecond granularity or better. CLOCK_MONOTONIC_COARSE is - * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may - * decide to make a costly system call. - */ - /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE - * when it has microsecond granularity or better (unlikely). - */ - clock_id = CLOCK_MONOTONIC; - if (type != UV_CLOCK_FAST) - goto done; - - clock_id = uv__load_relaxed(&fast_clock_id); - if (clock_id != -1) - goto done; - - clock_id = CLOCK_MONOTONIC; - if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t)) - if (t.tv_nsec <= 1 * 1000 * 1000) - clock_id = CLOCK_MONOTONIC_COARSE; - - uv__store_relaxed(&fast_clock_id, clock_id); - -done: - - if (clock_gettime(clock_id, &t)) - return 0; /* Not really possible. */ - - return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; -} - - -int uv_resident_set_memory(size_t* rss) { - char buf[1024]; - const char* s; - ssize_t n; - long val; - int fd; - int i; - - do - fd = open("/proc/self/stat", O_RDONLY); - while (fd == -1 && errno == EINTR); - - if (fd == -1) - return UV__ERR(errno); - - do - n = read(fd, buf, sizeof(buf) - 1); - while (n == -1 && errno == EINTR); - - uv__close(fd); - if (n == -1) - return UV__ERR(errno); - buf[n] = '\0'; - - s = strchr(buf, ' '); - if (s == NULL) - goto err; - - s += 1; - if (*s != '(') - goto err; - - s = strchr(s, ')'); - if (s == NULL) - goto err; - - for (i = 1; i <= 22; i++) { - s = strchr(s + 1, ' '); - if (s == NULL) - goto err; - } - - errno = 0; - val = strtol(s, NULL, 10); - if (errno != 0) - goto err; - if (val < 0) - goto err; - - *rss = val * getpagesize(); - return 0; - -err: - return UV_EINVAL; -} - -int uv_uptime(double* uptime) { - static volatile int no_clock_boottime; - char buf[128]; - struct timespec now; - int r; - - /* Try /proc/uptime first, then fallback to clock_gettime(). */ - - if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf))) - if (1 == sscanf(buf, "%lf", uptime)) - return 0; - - /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available - * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system - * is suspended. - */ - if (no_clock_boottime) { - retry_clock_gettime: r = clock_gettime(CLOCK_MONOTONIC, &now); - } - else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) { - no_clock_boottime = 1; - goto retry_clock_gettime; - } - - if (r) - return UV__ERR(errno); - - *uptime = now.tv_sec; - return 0; -} - - -static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) { - unsigned int num; - char buf[1024]; - - if (!fgets(buf, sizeof(buf), statfile_fp)) - return UV_EIO; - - num = 0; - while (fgets(buf, sizeof(buf), statfile_fp)) { - if (strncmp(buf, "cpu", 3)) - break; - num++; - } - - if (num == 0) - return UV_EIO; - - *numcpus = num; - return 0; -} - - -int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { - unsigned int numcpus; - uv_cpu_info_t* ci; - int err; - FILE* statfile_fp; - - *cpu_infos = NULL; - *count = 0; - - statfile_fp = uv__open_file("/proc/stat"); - if (statfile_fp == NULL) - return UV__ERR(errno); - - err = uv__cpu_num(statfile_fp, &numcpus); - if (err < 0) - goto out; - - err = UV_ENOMEM; - ci = uv__calloc(numcpus, sizeof(*ci)); - if (ci == NULL) - goto out; - - err = read_models(numcpus, ci); - if (err == 0) - err = read_times(statfile_fp, numcpus, ci); - - if (err) { - uv_free_cpu_info(ci, numcpus); - goto out; - } - - /* read_models() on x86 also reads the CPU speed from /proc/cpuinfo. - * We don't check for errors here. Worst case, the field is left zero. - */ - if (ci[0].speed == 0) - read_speeds(numcpus, ci); - - *cpu_infos = ci; - *count = numcpus; - err = 0; - -out: - - if (fclose(statfile_fp)) - if (errno != EINTR && errno != EINPROGRESS) - abort(); - - return err; -} - - -static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) { - unsigned int num; - - for (num = 0; num < numcpus; num++) - ci[num].speed = read_cpufreq(num) / 1000; -} - - -/* Also reads the CPU frequency on ppc and x86. The other architectures only - * have a BogoMIPS field, which may not be very accurate. - * - * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup. - */ -static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { -#if defined(__PPC__) - static const char model_marker[] = "cpu\t\t: "; - static const char speed_marker[] = "clock\t\t: "; -#else - static const char model_marker[] = "model name\t: "; - static const char speed_marker[] = "cpu MHz\t\t: "; -#endif - const char* inferred_model; - unsigned int model_idx; - unsigned int speed_idx; - unsigned int part_idx; - char buf[1024]; - char* model; - FILE* fp; - int model_id; - - /* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */ - (void) &model_marker; - (void) &speed_marker; - (void) &speed_idx; - (void) &part_idx; - (void) &model; - (void) &buf; - (void) &fp; - (void) &model_id; - - model_idx = 0; - speed_idx = 0; - part_idx = 0; - -#if defined(__arm__) || \ - defined(__i386__) || \ - defined(__mips__) || \ - defined(__aarch64__) || \ - defined(__PPC__) || \ - defined(__x86_64__) - fp = uv__open_file("/proc/cpuinfo"); - if (fp == NULL) - return UV__ERR(errno); - - while (fgets(buf, sizeof(buf), fp)) { - if (model_idx < numcpus) { - if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) { - model = buf + sizeof(model_marker) - 1; - model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ - if (model == NULL) { - fclose(fp); - return UV_ENOMEM; - } - ci[model_idx++].model = model; - continue; - } - } -#if defined(__arm__) || defined(__mips__) || defined(__aarch64__) - if (model_idx < numcpus) { -#if defined(__arm__) - /* Fallback for pre-3.8 kernels. */ - static const char model_marker[] = "Processor\t: "; -#elif defined(__aarch64__) - static const char part_marker[] = "CPU part\t: "; - - /* Adapted from: https://github.com/karelzak/util-linux */ - struct vendor_part { - const int id; - const char* name; - }; - - static const struct vendor_part arm_chips[] = { - { 0x811, "ARM810" }, - { 0x920, "ARM920" }, - { 0x922, "ARM922" }, - { 0x926, "ARM926" }, - { 0x940, "ARM940" }, - { 0x946, "ARM946" }, - { 0x966, "ARM966" }, - { 0xa20, "ARM1020" }, - { 0xa22, "ARM1022" }, - { 0xa26, "ARM1026" }, - { 0xb02, "ARM11 MPCore" }, - { 0xb36, "ARM1136" }, - { 0xb56, "ARM1156" }, - { 0xb76, "ARM1176" }, - { 0xc05, "Cortex-A5" }, - { 0xc07, "Cortex-A7" }, - { 0xc08, "Cortex-A8" }, - { 0xc09, "Cortex-A9" }, - { 0xc0d, "Cortex-A17" }, /* Originally A12 */ - { 0xc0f, "Cortex-A15" }, - { 0xc0e, "Cortex-A17" }, - { 0xc14, "Cortex-R4" }, - { 0xc15, "Cortex-R5" }, - { 0xc17, "Cortex-R7" }, - { 0xc18, "Cortex-R8" }, - { 0xc20, "Cortex-M0" }, - { 0xc21, "Cortex-M1" }, - { 0xc23, "Cortex-M3" }, - { 0xc24, "Cortex-M4" }, - { 0xc27, "Cortex-M7" }, - { 0xc60, "Cortex-M0+" }, - { 0xd01, "Cortex-A32" }, - { 0xd03, "Cortex-A53" }, - { 0xd04, "Cortex-A35" }, - { 0xd05, "Cortex-A55" }, - { 0xd06, "Cortex-A65" }, - { 0xd07, "Cortex-A57" }, - { 0xd08, "Cortex-A72" }, - { 0xd09, "Cortex-A73" }, - { 0xd0a, "Cortex-A75" }, - { 0xd0b, "Cortex-A76" }, - { 0xd0c, "Neoverse-N1" }, - { 0xd0d, "Cortex-A77" }, - { 0xd0e, "Cortex-A76AE" }, - { 0xd13, "Cortex-R52" }, - { 0xd20, "Cortex-M23" }, - { 0xd21, "Cortex-M33" }, - { 0xd41, "Cortex-A78" }, - { 0xd42, "Cortex-A78AE" }, - { 0xd4a, "Neoverse-E1" }, - { 0xd4b, "Cortex-A78C" }, - }; - - if (strncmp(buf, part_marker, sizeof(part_marker) - 1) == 0) { - model = buf + sizeof(part_marker) - 1; - - errno = 0; - model_id = strtol(model, NULL, 16); - if ((errno != 0) || model_id < 0) { - fclose(fp); - return UV_EINVAL; - } - - for (part_idx = 0; part_idx < ARRAY_SIZE(arm_chips); part_idx++) { - if (model_id == arm_chips[part_idx].id) { - model = uv__strdup(arm_chips[part_idx].name); - if (model == NULL) { - fclose(fp); - return UV_ENOMEM; - } - ci[model_idx++].model = model; - break; - } - } - } -#else /* defined(__mips__) */ - static const char model_marker[] = "cpu model\t\t: "; -#endif - if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) { - model = buf + sizeof(model_marker) - 1; - model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ - if (model == NULL) { - fclose(fp); - return UV_ENOMEM; - } - ci[model_idx++].model = model; - continue; - } - } -#else /* !__arm__ && !__mips__ && !__aarch64__ */ - if (speed_idx < numcpus) { - if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) { - ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1); - continue; - } - } -#endif /* __arm__ || __mips__ || __aarch64__ */ - } - - fclose(fp); -#endif /* __arm__ || __i386__ || __mips__ || __PPC__ || __x86_64__ || __aarch__ */ - - /* Now we want to make sure that all the models contain *something* because - * it's not safe to leave them as null. Copy the last entry unless there - * isn't one, in that case we simply put "unknown" into everything. - */ - inferred_model = "unknown"; - if (model_idx > 0) - inferred_model = ci[model_idx - 1].model; - - while (model_idx < numcpus) { - model = uv__strndup(inferred_model, strlen(inferred_model)); - if (model == NULL) - return UV_ENOMEM; - ci[model_idx++].model = model; - } - - return 0; -} - - -static int read_times(FILE* statfile_fp, - unsigned int numcpus, - uv_cpu_info_t* ci) { - struct uv_cpu_times_s ts; - unsigned int ticks; - unsigned int multiplier; - uint64_t user; - uint64_t nice; - uint64_t sys; - uint64_t idle; - uint64_t dummy; - uint64_t irq; - uint64_t num; - uint64_t len; - char buf[1024]; - - ticks = (unsigned int)sysconf(_SC_CLK_TCK); - assert(ticks != (unsigned int) -1); - assert(ticks != 0); - multiplier = ((uint64_t)1000L / ticks); - - rewind(statfile_fp); - - if (!fgets(buf, sizeof(buf), statfile_fp)) - abort(); - - num = 0; - - while (fgets(buf, sizeof(buf), statfile_fp)) { - if (num >= numcpus) - break; - - if (strncmp(buf, "cpu", 3)) - break; - - /* skip "cpu<num> " marker */ - { - unsigned int n; - int r = sscanf(buf, "cpu%u ", &n); - assert(r == 1); - (void) r; /* silence build warning */ - for (len = sizeof("cpu0"); n /= 10; len++); - } - - /* Line contains user, nice, system, idle, iowait, irq, softirq, steal, - * guest, guest_nice but we're only interested in the first four + irq. - * - * Don't use %*s to skip fields or %ll to read straight into the uint64_t - * fields, they're not allowed in C89 mode. - */ - if (6 != sscanf(buf + len, - "%" PRIu64 " %" PRIu64 " %" PRIu64 - "%" PRIu64 " %" PRIu64 " %" PRIu64, - &user, - &nice, - &sys, - &idle, - &dummy, - &irq)) - abort(); - - ts.user = user * multiplier; - ts.nice = nice * multiplier; - ts.sys = sys * multiplier; - ts.idle = idle * multiplier; - ts.irq = irq * multiplier; - ci[num++].cpu_times = ts; - } - assert(num == numcpus); - - return 0; -} - - -static uint64_t read_cpufreq(unsigned int cpunum) { - uint64_t val; - char buf[1024]; - FILE* fp; - - snprintf(buf, - sizeof(buf), - "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", - cpunum); - - fp = uv__open_file(buf); - if (fp == NULL) - return 0; - - if (fscanf(fp, "%" PRIu64, &val) != 1) - val = 0; - - fclose(fp); - - return val; -} - - -#ifdef HAVE_IFADDRS_H -static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { - if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) - return 1; - if (ent->ifa_addr == NULL) - return 1; - /* - * On Linux getifaddrs returns information related to the raw underlying - * devices. We're not interested in this information yet. - */ - if (ent->ifa_addr->sa_family == PF_PACKET) - return exclude_type; - return !exclude_type; -} -#endif - -int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { -#ifndef HAVE_IFADDRS_H - *count = 0; - *addresses = NULL; - return UV_ENOSYS; -#else - struct ifaddrs *addrs, *ent; - uv_interface_address_t* address; - int i; - struct sockaddr_ll *sll; - - *count = 0; - *addresses = NULL; - - if (getifaddrs(&addrs)) - return UV__ERR(errno); - - /* Count the number of interfaces */ - for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) - continue; - - (*count)++; - } - - if (*count == 0) { - freeifaddrs(addrs); - return 0; - } - - /* Make sure the memory is initiallized to zero using calloc() */ - *addresses = uv__calloc(*count, sizeof(**addresses)); - if (!(*addresses)) { - freeifaddrs(addrs); - return UV_ENOMEM; - } - - address = *addresses; - - for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) - continue; - - address->name = uv__strdup(ent->ifa_name); - - if (ent->ifa_addr->sa_family == AF_INET6) { - address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); - } else { - address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); - } - - if (ent->ifa_netmask->sa_family == AF_INET6) { - address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); - } else { - address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); - } - - address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); - - address++; - } - - /* Fill in physical addresses for each interface */ - for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) - continue; - - address = *addresses; - - for (i = 0; i < (*count); i++) { - size_t namelen = strlen(ent->ifa_name); - /* Alias interface share the same physical address */ - if (strncmp(address->name, ent->ifa_name, namelen) == 0 && - (address->name[namelen] == 0 || address->name[namelen] == ':')) { - sll = (struct sockaddr_ll*)ent->ifa_addr; - memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); - } - address++; - } - } - - freeifaddrs(addrs); - - return 0; -#endif -} - - -void uv_free_interface_addresses(uv_interface_address_t* addresses, - int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(addresses[i].name); - } - - uv__free(addresses); -} - - -void uv__set_process_title(const char* title) { -#if defined(PR_SET_NAME) - prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ -#endif -} - - -static uint64_t uv__read_proc_meminfo(const char* what) { - uint64_t rc; - char* p; - char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ - - if (uv__slurp("/proc/meminfo", buf, sizeof(buf))) - return 0; - - p = strstr(buf, what); - - if (p == NULL) - return 0; - - p += strlen(what); - - rc = 0; - sscanf(p, "%" PRIu64 " kB", &rc); - - return rc * 1024; -} - - -uint64_t uv_get_free_memory(void) { - struct sysinfo info; - uint64_t rc; - - rc = uv__read_proc_meminfo("MemAvailable:"); - - if (rc != 0) - return rc; - - if (0 == sysinfo(&info)) - return (uint64_t) info.freeram * info.mem_unit; - - return 0; -} - - -uint64_t uv_get_total_memory(void) { - struct sysinfo info; - uint64_t rc; - - rc = uv__read_proc_meminfo("MemTotal:"); - - if (rc != 0) - return rc; - - if (0 == sysinfo(&info)) - return (uint64_t) info.totalram * info.mem_unit; - - return 0; -} - - -static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) { - char filename[256]; - char buf[32]; /* Large enough to hold an encoded uint64_t. */ - uint64_t rc; - - rc = 0; - snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/%s", cgroup, param); - if (0 == uv__slurp(filename, buf, sizeof(buf))) - sscanf(buf, "%" PRIu64, &rc); - - return rc; -} - - -uint64_t uv_get_constrained_memory(void) { - /* - * This might return 0 if there was a problem getting the memory limit from - * cgroups. This is OK because a return value of 0 signifies that the memory - * limit is unknown. - */ - return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes"); -} - - -void uv_loadavg(double avg[3]) { - struct sysinfo info; - char buf[128]; /* Large enough to hold all of /proc/loadavg. */ - - if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf))) - if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2])) - return; - - if (sysinfo(&info) < 0) - return; - - avg[0] = (double) info.loads[0] / 65536.0; - avg[1] = (double) info.loads[1] / 65536.0; - avg[2] = (double) info.loads[2] / 65536.0; -}
diff --git a/Utilities/cmlibuv/src/unix/linux-inotify.c b/Utilities/cmlibuv/src/unix/linux-inotify.c deleted file mode 100644 index c1bd260..0000000 --- a/Utilities/cmlibuv/src/unix/linux-inotify.c +++ /dev/null
@@ -1,327 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "uv.h" -#include "uv/tree.h" -#include "internal.h" - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <errno.h> - -#include <sys/inotify.h> -#include <sys/types.h> -#include <unistd.h> - -struct watcher_list { - RB_ENTRY(watcher_list) entry; - QUEUE watchers; - int iterating; - char* path; - int wd; -}; - -struct watcher_root { - struct watcher_list* rbh_root; -}; -#define CAST(p) ((struct watcher_root*)(p)) - - -static int compare_watchers(const struct watcher_list* a, - const struct watcher_list* b) { - if (a->wd < b->wd) return -1; - if (a->wd > b->wd) return 1; - return 0; -} - - -RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers) - - -static void uv__inotify_read(uv_loop_t* loop, - uv__io_t* w, - unsigned int revents); - -static void maybe_free_watcher_list(struct watcher_list* w, - uv_loop_t* loop); - -static int init_inotify(uv_loop_t* loop) { - int fd; - - if (loop->inotify_fd != -1) - return 0; - - fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (fd < 0) - return UV__ERR(errno); - - loop->inotify_fd = fd; - uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd); - uv__io_start(loop, &loop->inotify_read_watcher, POLLIN); - - return 0; -} - - -int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) { - /* Open the inotify_fd, and re-arm all the inotify watchers. */ - int err; - struct watcher_list* tmp_watcher_list_iter; - struct watcher_list* watcher_list; - struct watcher_list tmp_watcher_list; - QUEUE queue; - QUEUE* q; - uv_fs_event_t* handle; - char* tmp_path; - - if (old_watchers != NULL) { - /* We must restore the old watcher list to be able to close items - * out of it. - */ - loop->inotify_watchers = old_watchers; - - QUEUE_INIT(&tmp_watcher_list.watchers); - /* Note that the queue we use is shared with the start and stop() - * functions, making QUEUE_FOREACH unsafe to use. So we use the - * QUEUE_MOVE trick to safely iterate. Also don't free the watcher - * list until we're done iterating. c.f. uv__inotify_read. - */ - RB_FOREACH_SAFE(watcher_list, watcher_root, - CAST(&old_watchers), tmp_watcher_list_iter) { - watcher_list->iterating = 1; - QUEUE_MOVE(&watcher_list->watchers, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - handle = QUEUE_DATA(q, uv_fs_event_t, watchers); - /* It's critical to keep a copy of path here, because it - * will be set to NULL by stop() and then deallocated by - * maybe_free_watcher_list - */ - tmp_path = uv__strdup(handle->path); - assert(tmp_path != NULL); - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&watcher_list->watchers, q); - uv_fs_event_stop(handle); - - QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers); - handle->path = tmp_path; - } - watcher_list->iterating = 0; - maybe_free_watcher_list(watcher_list, loop); - } - - QUEUE_MOVE(&tmp_watcher_list.watchers, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - QUEUE_REMOVE(q); - handle = QUEUE_DATA(q, uv_fs_event_t, watchers); - tmp_path = handle->path; - handle->path = NULL; - err = uv_fs_event_start(handle, handle->cb, tmp_path, 0); - uv__free(tmp_path); - if (err) - return err; - } - } - - return 0; -} - - -static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) { - struct watcher_list w; - w.wd = wd; - return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w); -} - -static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) { - /* if the watcher_list->watchers is being iterated over, we can't free it. */ - if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) { - /* No watchers left for this path. Clean up. */ - RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w); - inotify_rm_watch(loop->inotify_fd, w->wd); - uv__free(w); - } -} - -static void uv__inotify_read(uv_loop_t* loop, - uv__io_t* dummy, - unsigned int events) { - const struct inotify_event* e; - struct watcher_list* w; - uv_fs_event_t* h; - QUEUE queue; - QUEUE* q; - const char* path; - ssize_t size; - const char *p; - /* needs to be large enough for sizeof(inotify_event) + strlen(path) */ - char buf[4096]; - - for (;;) { - do - size = read(loop->inotify_fd, buf, sizeof(buf)); - while (size == -1 && errno == EINTR); - - if (size == -1) { - assert(errno == EAGAIN || errno == EWOULDBLOCK); - break; - } - - assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */ - - /* Now we have one or more inotify_event structs. */ - for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { - e = (const struct inotify_event*) p; - - events = 0; - if (e->mask & (IN_ATTRIB|IN_MODIFY)) - events |= UV_CHANGE; - if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) - events |= UV_RENAME; - - w = find_watcher(loop, e->wd); - if (w == NULL) - continue; /* Stale event, no watchers left. */ - - /* inotify does not return the filename when monitoring a single file - * for modifications. Repurpose the filename for API compatibility. - * I'm not convinced this is a good thing, maybe it should go. - */ - path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path); - - /* We're about to iterate over the queue and call user's callbacks. - * What can go wrong? - * A callback could call uv_fs_event_stop() - * and the queue can change under our feet. - * So, we use QUEUE_MOVE() trick to safely iterate over the queue. - * And we don't free the watcher_list until we're done iterating. - * - * First, - * tell uv_fs_event_stop() (that could be called from a user's callback) - * not to free watcher_list. - */ - w->iterating = 1; - QUEUE_MOVE(&w->watchers, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - h = QUEUE_DATA(q, uv_fs_event_t, watchers); - - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&w->watchers, q); - - h->cb(h, path, events, 0); - } - /* done iterating, time to (maybe) free empty watcher_list */ - w->iterating = 0; - maybe_free_watcher_list(w, loop); - } - } -} - - -int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { - uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); - return 0; -} - - -int uv_fs_event_start(uv_fs_event_t* handle, - uv_fs_event_cb cb, - const char* path, - unsigned int flags) { - struct watcher_list* w; - size_t len; - int events; - int err; - int wd; - - if (uv__is_active(handle)) - return UV_EINVAL; - - err = init_inotify(handle->loop); - if (err) - return err; - - events = IN_ATTRIB - | IN_CREATE - | IN_MODIFY - | IN_DELETE - | IN_DELETE_SELF - | IN_MOVE_SELF - | IN_MOVED_FROM - | IN_MOVED_TO; - - wd = inotify_add_watch(handle->loop->inotify_fd, path, events); - if (wd == -1) - return UV__ERR(errno); - - w = find_watcher(handle->loop, wd); - if (w) - goto no_insert; - - len = strlen(path) + 1; - w = uv__malloc(sizeof(*w) + len); - if (w == NULL) - return UV_ENOMEM; - - w->wd = wd; - w->path = memcpy(w + 1, path, len); - QUEUE_INIT(&w->watchers); - w->iterating = 0; - RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w); - -no_insert: - uv__handle_start(handle); - QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers); - handle->path = w->path; - handle->cb = cb; - handle->wd = wd; - - return 0; -} - - -int uv_fs_event_stop(uv_fs_event_t* handle) { - struct watcher_list* w; - - if (!uv__is_active(handle)) - return 0; - - w = find_watcher(handle->loop, handle->wd); - assert(w != NULL); - - handle->wd = -1; - handle->path = NULL; - uv__handle_stop(handle); - QUEUE_REMOVE(&handle->watchers); - - maybe_free_watcher_list(w, handle->loop); - - return 0; -} - - -void uv__fs_event_close(uv_fs_event_t* handle) { - uv_fs_event_stop(handle); -}
diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.c b/Utilities/cmlibuv/src/unix/linux-syscalls.c deleted file mode 100644 index 5071cd5..0000000 --- a/Utilities/cmlibuv/src/unix/linux-syscalls.c +++ /dev/null
@@ -1,264 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "linux-syscalls.h" -#include <unistd.h> -#include <signal.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <errno.h> - -#if defined(__arm__) -# if defined(__thumb__) || defined(__ARM_EABI__) -# define UV_SYSCALL_BASE 0 -# else -# define UV_SYSCALL_BASE 0x900000 -# endif -#endif /* __arm__ */ - -#ifndef __NR_recvmmsg -# if defined(__x86_64__) -# define __NR_recvmmsg 299 -# elif defined(__arm__) -# define __NR_recvmmsg (UV_SYSCALL_BASE + 365) -# endif -#endif /* __NR_recvmsg */ - -#ifndef __NR_sendmmsg -# if defined(__x86_64__) -# define __NR_sendmmsg 307 -# elif defined(__arm__) -# define __NR_sendmmsg (UV_SYSCALL_BASE + 374) -# endif -#endif /* __NR_sendmmsg */ - -#ifndef __NR_utimensat -# if defined(__x86_64__) -# define __NR_utimensat 280 -# elif defined(__i386__) -# define __NR_utimensat 320 -# elif defined(__arm__) -# define __NR_utimensat (UV_SYSCALL_BASE + 348) -# endif -#endif /* __NR_utimensat */ - -#ifndef __NR_preadv -# if defined(__x86_64__) -# define __NR_preadv 295 -# elif defined(__i386__) -# define __NR_preadv 333 -# elif defined(__arm__) -# define __NR_preadv (UV_SYSCALL_BASE + 361) -# endif -#endif /* __NR_preadv */ - -#ifndef __NR_pwritev -# if defined(__x86_64__) -# define __NR_pwritev 296 -# elif defined(__i386__) -# define __NR_pwritev 334 -# elif defined(__arm__) -# define __NR_pwritev (UV_SYSCALL_BASE + 362) -# endif -#endif /* __NR_pwritev */ - -#ifndef __NR_dup3 -# if defined(__x86_64__) -# define __NR_dup3 292 -# elif defined(__i386__) -# define __NR_dup3 330 -# elif defined(__arm__) -# define __NR_dup3 (UV_SYSCALL_BASE + 358) -# endif -#endif /* __NR_pwritev */ - -#ifndef __NR_copy_file_range -# if defined(__x86_64__) -# define __NR_copy_file_range 326 -# elif defined(__i386__) -# define __NR_copy_file_range 377 -# elif defined(__s390__) -# define __NR_copy_file_range 375 -# elif defined(__arm__) -# define __NR_copy_file_range (UV_SYSCALL_BASE + 391) -# elif defined(__aarch64__) -# define __NR_copy_file_range 285 -# elif defined(__powerpc__) -# define __NR_copy_file_range 379 -# elif defined(__arc__) -# define __NR_copy_file_range 285 -# endif -#endif /* __NR_copy_file_range */ - -#ifndef __NR_statx -# if defined(__x86_64__) -# define __NR_statx 332 -# elif defined(__i386__) -# define __NR_statx 383 -# elif defined(__aarch64__) -# define __NR_statx 397 -# elif defined(__arm__) -# define __NR_statx (UV_SYSCALL_BASE + 397) -# elif defined(__ppc__) -# define __NR_statx 383 -# elif defined(__s390__) -# define __NR_statx 379 -# endif -#endif /* __NR_statx */ - -#ifndef __NR_getrandom -# if defined(__x86_64__) -# define __NR_getrandom 318 -# elif defined(__i386__) -# define __NR_getrandom 355 -# elif defined(__aarch64__) -# define __NR_getrandom 384 -# elif defined(__arm__) -# define __NR_getrandom (UV_SYSCALL_BASE + 384) -# elif defined(__ppc__) -# define __NR_getrandom 359 -# elif defined(__s390__) -# define __NR_getrandom 349 -# endif -#endif /* __NR_getrandom */ - -struct uv__mmsghdr; - -int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if defined(__i386__) - unsigned long args[4]; - int rc; - - args[0] = (unsigned long) fd; - args[1] = (unsigned long) mmsg; - args[2] = (unsigned long) vlen; - args[3] = /* flags */ 0; - - /* socketcall() raises EINVAL when SYS_SENDMMSG is not supported. */ - rc = syscall(/* __NR_socketcall */ 102, 20 /* SYS_SENDMMSG */, args); - if (rc == -1) - if (errno == EINVAL) - errno = ENOSYS; - - return rc; -#elif defined(__NR_sendmmsg) - return syscall(__NR_sendmmsg, fd, mmsg, vlen, /* flags */ 0); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if defined(__i386__) - unsigned long args[5]; - int rc; - - args[0] = (unsigned long) fd; - args[1] = (unsigned long) mmsg; - args[2] = (unsigned long) vlen; - args[3] = /* flags */ 0; - args[4] = /* timeout */ 0; - - /* socketcall() raises EINVAL when SYS_RECVMMSG is not supported. */ - rc = syscall(/* __NR_socketcall */ 102, 19 /* SYS_RECVMMSG */, args); - if (rc == -1) - if (errno == EINVAL) - errno = ENOSYS; - - return rc; -#elif defined(__NR_recvmmsg) - return syscall(__NR_recvmmsg, fd, mmsg, vlen, /* flags */ 0, /* timeout */ 0); -#else - return errno = ENOSYS, -1; -#endif -} - - -ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if !defined(__NR_preadv) || defined(__ANDROID_API__) && __ANDROID_API__ < 24 - return errno = ENOSYS, -1; -#else - return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#endif -} - - -ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if !defined(__NR_pwritev) || defined(__ANDROID_API__) && __ANDROID_API__ < 24 - return errno = ENOSYS, -1; -#else - return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#endif -} - - -int uv__dup3(int oldfd, int newfd, int flags) { -#if !defined(__NR_dup3) || defined(__ANDROID_API__) && __ANDROID_API__ < 21 - return errno = ENOSYS, -1; -#else - return syscall(__NR_dup3, oldfd, newfd, flags); -#endif -} - - -ssize_t -uv__fs_copy_file_range(int fd_in, - off_t* off_in, - int fd_out, - off_t* off_out, - size_t len, - unsigned int flags) -{ -#ifdef __NR_copy_file_range - return syscall(__NR_copy_file_range, - fd_in, - off_in, - fd_out, - off_out, - len, - flags); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__statx(int dirfd, - const char* path, - int flags, - unsigned int mask, - struct uv__statx* statxbuf) { -#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30 - return errno = ENOSYS, -1; -#else - return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); -#endif -} - - -ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) { -#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28 - return errno = ENOSYS, -1; -#else - return syscall(__NR_getrandom, buf, buflen, flags); -#endif -}
diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.h b/Utilities/cmlibuv/src/unix/linux-syscalls.h deleted file mode 100644 index b4d9082..0000000 --- a/Utilities/cmlibuv/src/unix/linux-syscalls.h +++ /dev/null
@@ -1,78 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef UV_LINUX_SYSCALL_H_ -#define UV_LINUX_SYSCALL_H_ - -#include <stdint.h> -#include <signal.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/socket.h> - -struct uv__statx_timestamp { - int64_t tv_sec; - uint32_t tv_nsec; - int32_t unused0; -}; - -struct uv__statx { - uint32_t stx_mask; - uint32_t stx_blksize; - uint64_t stx_attributes; - uint32_t stx_nlink; - uint32_t stx_uid; - uint32_t stx_gid; - uint16_t stx_mode; - uint16_t unused0; - uint64_t stx_ino; - uint64_t stx_size; - uint64_t stx_blocks; - uint64_t stx_attributes_mask; - struct uv__statx_timestamp stx_atime; - struct uv__statx_timestamp stx_btime; - struct uv__statx_timestamp stx_ctime; - struct uv__statx_timestamp stx_mtime; - uint32_t stx_rdev_major; - uint32_t stx_rdev_minor; - uint32_t stx_dev_major; - uint32_t stx_dev_minor; - uint64_t unused1[14]; -}; - -ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); -ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); -int uv__dup3(int oldfd, int newfd, int flags); -ssize_t -uv__fs_copy_file_range(int fd_in, - off_t* off_in, - int fd_out, - off_t* off_out, - size_t len, - unsigned int flags); -int uv__statx(int dirfd, - const char* path, - int flags, - unsigned int mask, - struct uv__statx* statxbuf); -ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags); - -#endif /* UV_LINUX_SYSCALL_H_ */
diff --git a/Utilities/cmlibuv/src/unix/linux.c b/Utilities/cmlibuv/src/unix/linux.c new file mode 100644 index 0000000..0ba2e98 --- /dev/null +++ b/Utilities/cmlibuv/src/unix/linux.c
@@ -0,0 +1,2738 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their + * EPOLL* counterparts. We use the POLL* variants in this file because that + * is what libuv uses elsewhere. + */ + +#include "uv.h" +#include "internal.h" + +#include <inttypes.h> +#include <stdatomic.h> +#include <stddef.h> /* offsetof */ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include <fcntl.h> +#include <ifaddrs.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <netpacket/packet.h> +#include <sys/epoll.h> +#include <sys/inotify.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/prctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/sysinfo.h> +#include <sys/sysmacros.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <time.h> +#include <unistd.h> + +#ifndef __NR_io_uring_setup +# define __NR_io_uring_setup 425 +#endif + +#ifndef __NR_io_uring_enter +# define __NR_io_uring_enter 426 +#endif + +#ifndef __NR_io_uring_register +# define __NR_io_uring_register 427 +#endif + +#ifndef __NR_copy_file_range +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined(__s390__) +# define __NR_copy_file_range 375 +# elif defined(__arm__) +# define __NR_copy_file_range 391 +# elif defined(__aarch64__) +# define __NR_copy_file_range 285 +# elif defined(__powerpc__) +# define __NR_copy_file_range 379 +# elif defined(__arc__) +# define __NR_copy_file_range 285 +# elif defined(__riscv) +# define __NR_copy_file_range 285 +# endif +#endif /* __NR_copy_file_range */ + +#ifndef __NR_statx +# if defined(__x86_64__) +# define __NR_statx 332 +# elif defined(__i386__) +# define __NR_statx 383 +# elif defined(__aarch64__) +# define __NR_statx 397 +# elif defined(__arm__) +# define __NR_statx 397 +# elif defined(__ppc__) +# define __NR_statx 383 +# elif defined(__s390__) +# define __NR_statx 379 +# elif defined(__riscv) +# define __NR_statx 291 +# endif +#endif /* __NR_statx */ + +#ifndef __NR_getrandom +# if defined(__x86_64__) +# define __NR_getrandom 318 +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__aarch64__) +# define __NR_getrandom 384 +# elif defined(__arm__) +# define __NR_getrandom 384 +# elif defined(__ppc__) +# define __NR_getrandom 359 +# elif defined(__s390__) +# define __NR_getrandom 349 +# elif defined(__riscv) +# define __NR_getrandom 278 +# endif +#endif /* __NR_getrandom */ + +enum { + UV__IORING_SETUP_SQPOLL = 2u, + UV__IORING_SETUP_NO_SQARRAY = 0x10000u, +}; + +enum { + UV__IORING_FEAT_SINGLE_MMAP = 1u, + UV__IORING_FEAT_NODROP = 2u, + UV__IORING_FEAT_RSRC_TAGS = 1024u, /* linux v5.13 */ +}; + +enum { + UV__IORING_OP_READV = 1, + UV__IORING_OP_WRITEV = 2, + UV__IORING_OP_FSYNC = 3, + UV__IORING_OP_OPENAT = 18, + UV__IORING_OP_CLOSE = 19, + UV__IORING_OP_STATX = 21, + UV__IORING_OP_EPOLL_CTL = 29, + UV__IORING_OP_RENAMEAT = 35, + UV__IORING_OP_UNLINKAT = 36, + UV__IORING_OP_MKDIRAT = 37, + UV__IORING_OP_SYMLINKAT = 38, + UV__IORING_OP_LINKAT = 39, + UV__IORING_OP_FTRUNCATE = 55, +}; + +enum { + UV__IORING_ENTER_GETEVENTS = 1u, + UV__IORING_ENTER_SQ_WAKEUP = 2u, +}; + +enum { + UV__IORING_SQ_NEED_WAKEUP = 1u, + UV__IORING_SQ_CQ_OVERFLOW = 2u, +}; + +struct uv__io_cqring_offsets { + uint32_t head; + uint32_t tail; + uint32_t ring_mask; + uint32_t ring_entries; + uint32_t overflow; + uint32_t cqes; + uint64_t reserved0; + uint64_t reserved1; +}; + +STATIC_ASSERT(40 == sizeof(struct uv__io_cqring_offsets)); + +struct uv__io_sqring_offsets { + uint32_t head; + uint32_t tail; + uint32_t ring_mask; + uint32_t ring_entries; + uint32_t flags; + uint32_t dropped; + uint32_t array; + uint32_t reserved0; + uint64_t reserved1; +}; + +STATIC_ASSERT(40 == sizeof(struct uv__io_sqring_offsets)); + +struct uv__io_uring_cqe { + uint64_t user_data; + int32_t res; + uint32_t flags; +}; + +STATIC_ASSERT(16 == sizeof(struct uv__io_uring_cqe)); + +struct uv__io_uring_sqe { + uint8_t opcode; + uint8_t flags; + uint16_t ioprio; + int32_t fd; + union { + uint64_t off; + uint64_t addr2; + }; + union { + uint64_t addr; + }; + uint32_t len; + union { + uint32_t rw_flags; + uint32_t fsync_flags; + uint32_t open_flags; + uint32_t statx_flags; + }; + uint64_t user_data; + union { + uint16_t buf_index; + uint64_t pad[3]; + }; +}; + +STATIC_ASSERT(64 == sizeof(struct uv__io_uring_sqe)); +STATIC_ASSERT(0 == offsetof(struct uv__io_uring_sqe, opcode)); +STATIC_ASSERT(1 == offsetof(struct uv__io_uring_sqe, flags)); +STATIC_ASSERT(2 == offsetof(struct uv__io_uring_sqe, ioprio)); +STATIC_ASSERT(4 == offsetof(struct uv__io_uring_sqe, fd)); +STATIC_ASSERT(8 == offsetof(struct uv__io_uring_sqe, off)); +STATIC_ASSERT(16 == offsetof(struct uv__io_uring_sqe, addr)); +STATIC_ASSERT(24 == offsetof(struct uv__io_uring_sqe, len)); +STATIC_ASSERT(28 == offsetof(struct uv__io_uring_sqe, rw_flags)); +STATIC_ASSERT(32 == offsetof(struct uv__io_uring_sqe, user_data)); +STATIC_ASSERT(40 == offsetof(struct uv__io_uring_sqe, buf_index)); + +struct uv__io_uring_params { + uint32_t sq_entries; + uint32_t cq_entries; + uint32_t flags; + uint32_t sq_thread_cpu; + uint32_t sq_thread_idle; + uint32_t features; + uint32_t reserved[4]; + struct uv__io_sqring_offsets sq_off; /* 40 bytes */ + struct uv__io_cqring_offsets cq_off; /* 40 bytes */ +}; + +STATIC_ASSERT(40 + 40 + 40 == sizeof(struct uv__io_uring_params)); +STATIC_ASSERT(40 == offsetof(struct uv__io_uring_params, sq_off)); +STATIC_ASSERT(80 == offsetof(struct uv__io_uring_params, cq_off)); + +STATIC_ASSERT(EPOLL_CTL_ADD < 4); +STATIC_ASSERT(EPOLL_CTL_DEL < 4); +STATIC_ASSERT(EPOLL_CTL_MOD < 4); + +struct watcher_list { + RB_ENTRY(watcher_list) entry; + struct uv__queue watchers; + int iterating; + char* path; + int wd; +}; + +struct watcher_root { + struct watcher_list* rbh_root; +}; + +static int uv__inotify_fork(uv_loop_t* loop, struct watcher_list* root); +static int compare_watchers(const struct watcher_list* a, + const struct watcher_list* b); +static void maybe_free_watcher_list(struct watcher_list* w, + uv_loop_t* loop); + +static void uv__epoll_ctl_flush(int epollfd, + struct uv__iou* ctl, + struct epoll_event (*events)[256]); + +static void uv__epoll_ctl_prep(int epollfd, + struct uv__iou* ctl, + struct epoll_event (*events)[256], + int op, + int fd, + struct epoll_event* e); + +RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers) + + +static struct watcher_root* uv__inotify_watchers(uv_loop_t* loop) { + /* This cast works because watcher_root is a struct with a pointer as its + * sole member. Such type punning is unsafe in the presence of strict + * pointer aliasing (and is just plain nasty) but that is why libuv + * is compiled with -fno-strict-aliasing. + */ + return (struct watcher_root*) &loop->inotify_watchers; +} + + +unsigned uv__kernel_version(void) { + static _Atomic unsigned cached_version; + struct utsname u; + unsigned version; + unsigned major; + unsigned minor; + unsigned patch; + char v_sig[256]; + char* needle; + + version = atomic_load_explicit(&cached_version, memory_order_relaxed); + if (version != 0) + return version; + + /* Check /proc/version_signature first as it's the way to get the mainline + * kernel version in Ubuntu. The format is: + * Ubuntu ubuntu_kernel_version mainline_kernel_version + * For example: + * Ubuntu 5.15.0-79.86-generic 5.15.111 + */ + if (0 == uv__slurp("/proc/version_signature", v_sig, sizeof(v_sig))) + if (3 == sscanf(v_sig, "Ubuntu %*s %u.%u.%u", &major, &minor, &patch)) + goto calculate_version; + + if (-1 == uname(&u)) + return 0; + + /* In Debian we need to check `version` instead of `release` to extract the + * mainline kernel version. This is an example of how it looks like: + * #1 SMP Debian 5.10.46-4 (2021-08-03) + */ + needle = strstr(u.version, "Debian "); + if (needle != NULL) + if (3 == sscanf(needle, "Debian %u.%u.%u", &major, &minor, &patch)) + goto calculate_version; + + if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch)) + return 0; + + /* Handle it when the process runs under the UNAME26 personality: + * + * - kernels >= 3.x identify as 2.6.40+x + * - kernels >= 4.x identify as 2.6.60+x + * + * UNAME26 is a poorly conceived hack that doesn't let us distinguish + * between 4.x kernels and 5.x/6.x kernels so we conservatively assume + * that 2.6.60+x means 4.x. + * + * Fun fact of the day: it's technically possible to observe the actual + * kernel version for a brief moment because uname() first copies out the + * real release string before overwriting it with the backcompat string. + */ + if (major == 2 && minor == 6) { + if (patch >= 60) { + major = 4; + minor = patch - 60; + patch = 0; + } else if (patch >= 40) { + major = 3; + minor = patch - 40; + patch = 0; + } + } + +calculate_version: + version = major * 65536 + minor * 256 + patch; + atomic_store_explicit(&cached_version, version, memory_order_relaxed); + + return version; +} + + +ssize_t +uv__fs_copy_file_range(int fd_in, + off_t* off_in, + int fd_out, + off_t* off_out, + size_t len, + unsigned int flags) +{ +#ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, + fd_in, + off_in, + fd_out, + off_out, + len, + flags); +#else + return errno = ENOSYS, -1; +#endif +} + + +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf) { +#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30 + return errno = ENOSYS, -1; +#else + int rc; + + rc = syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); + if (rc >= 0) + uv__msan_unpoison(statxbuf, sizeof(*statxbuf)); + + return rc; +#endif +} + + +ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) { +#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28 + return errno = ENOSYS, -1; +#else + ssize_t rc; + + rc = syscall(__NR_getrandom, buf, buflen, flags); + if (rc >= 0) + uv__msan_unpoison(buf, buflen); + + return rc; +#endif +} + + +int uv__io_uring_setup(int entries, struct uv__io_uring_params* params) { + return syscall(__NR_io_uring_setup, entries, params); +} + + +int uv__io_uring_enter(int fd, + unsigned to_submit, + unsigned min_complete, + unsigned flags) { + /* io_uring_enter used to take a sigset_t but it's unused + * in newer kernels unless IORING_ENTER_EXT_ARG is set, + * in which case it takes a struct io_uring_getevents_arg. + */ + return syscall(__NR_io_uring_enter, + fd, + to_submit, + min_complete, + flags, + NULL, + 0L); +} + + +int uv__io_uring_register(int fd, unsigned opcode, void* arg, unsigned nargs) { + return syscall(__NR_io_uring_register, fd, opcode, arg, nargs); +} + + +static int uv__use_io_uring(uint32_t flags) { +#if defined(__ANDROID_API__) + return 0; /* Possibly available but blocked by seccomp. */ +#elif defined(__arm__) && __SIZEOF_POINTER__ == 4 + /* See https://github.com/libuv/libuv/issues/4158. */ + return 0; /* All 32 bits kernels appear buggy. */ +#elif defined(__powerpc64__) || defined(__ppc64__) + /* See https://github.com/libuv/libuv/issues/4283. */ + return 0; /* Random SIGSEGV in signal handler. */ +#else + /* Ternary: unknown=0, yes=1, no=-1 */ + static _Atomic int use_io_uring; + char* val; + int use; + +#if defined(__hppa__) + /* io_uring first supported on parisc in 6.1, functional in .51 + * https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/ + */ + if (uv__kernel_version() < /*6.1.51*/0x060133) + return 0; +#endif + + /* SQPOLL is all kinds of buggy but epoll batching should work fine. */ + if (0 == (flags & UV__IORING_SETUP_SQPOLL)) + return 1; + + /* Older kernels have a bug where the sqpoll thread uses 100% CPU. */ + if (uv__kernel_version() < /*5.10.186*/0x050ABA) + return 0; + + use = atomic_load_explicit(&use_io_uring, memory_order_relaxed); + + if (use == 0) { + val = getenv("UV_USE_IO_URING"); + use = val != NULL && atoi(val) > 0 ? 1 : -1; + atomic_store_explicit(&use_io_uring, use, memory_order_relaxed); + } + + return use > 0; +#endif +} + + +static void uv__iou_init(int epollfd, + struct uv__iou* iou, + uint32_t entries, + uint32_t flags) { + struct uv__io_uring_params params; + struct epoll_event e; + size_t cqlen; + size_t sqlen; + size_t maxlen; + size_t sqelen; + unsigned kernel_version; + uint32_t* sqarray; + uint32_t i; + char* sq; + char* sqe; + int ringfd; + int no_sqarray; + + sq = MAP_FAILED; + sqe = MAP_FAILED; + + if (!uv__use_io_uring(flags)) + return; + + kernel_version = uv__kernel_version(); + no_sqarray = + UV__IORING_SETUP_NO_SQARRAY * (kernel_version >= /* 6.6 */0x060600); + + /* SQPOLL required CAP_SYS_NICE until linux v5.12 relaxed that requirement. + * Mostly academic because we check for a v5.13 kernel afterwards anyway. + */ + memset(¶ms, 0, sizeof(params)); + params.flags = flags | no_sqarray; + + if (flags & UV__IORING_SETUP_SQPOLL) + params.sq_thread_idle = 10; /* milliseconds */ + + /* Kernel returns a file descriptor with O_CLOEXEC flag set. */ + ringfd = uv__io_uring_setup(entries, ¶ms); + if (ringfd == -1) + return; + + /* IORING_FEAT_RSRC_TAGS is used to detect linux v5.13 but what we're + * actually detecting is whether IORING_OP_STATX works with SQPOLL. + */ + if (!(params.features & UV__IORING_FEAT_RSRC_TAGS)) + goto fail; + + /* Implied by IORING_FEAT_RSRC_TAGS but checked explicitly anyway. */ + if (!(params.features & UV__IORING_FEAT_SINGLE_MMAP)) + goto fail; + + /* Implied by IORING_FEAT_RSRC_TAGS but checked explicitly anyway. */ + if (!(params.features & UV__IORING_FEAT_NODROP)) + goto fail; + + sqlen = params.sq_off.array + params.sq_entries * sizeof(uint32_t); + cqlen = + params.cq_off.cqes + params.cq_entries * sizeof(struct uv__io_uring_cqe); + maxlen = sqlen < cqlen ? cqlen : sqlen; + sqelen = params.sq_entries * sizeof(struct uv__io_uring_sqe); + + sq = mmap(0, + maxlen, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, + ringfd, + 0); /* IORING_OFF_SQ_RING */ + + sqe = mmap(0, + sqelen, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, + ringfd, + 0x10000000ull); /* IORING_OFF_SQES */ + + if (sq == MAP_FAILED || sqe == MAP_FAILED) + goto fail; + + if (flags & UV__IORING_SETUP_SQPOLL) { + /* Only interested in completion events. To get notified when + * the kernel pulls items from the submission ring, add POLLOUT. + */ + memset(&e, 0, sizeof(e)); + e.events = POLLIN; + e.data.fd = ringfd; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ringfd, &e)) + goto fail; + } + + iou->sqhead = (uint32_t*) (sq + params.sq_off.head); + iou->sqtail = (uint32_t*) (sq + params.sq_off.tail); + iou->sqmask = *(uint32_t*) (sq + params.sq_off.ring_mask); + iou->sqflags = (uint32_t*) (sq + params.sq_off.flags); + iou->cqhead = (uint32_t*) (sq + params.cq_off.head); + iou->cqtail = (uint32_t*) (sq + params.cq_off.tail); + iou->cqmask = *(uint32_t*) (sq + params.cq_off.ring_mask); + iou->sq = sq; + iou->cqe = sq + params.cq_off.cqes; + iou->sqe = sqe; + iou->sqlen = sqlen; + iou->cqlen = cqlen; + iou->maxlen = maxlen; + iou->sqelen = sqelen; + iou->ringfd = ringfd; + iou->in_flight = 0; + + if (no_sqarray) + return; + + sqarray = (uint32_t*) (sq + params.sq_off.array); + for (i = 0; i <= iou->sqmask; i++) + sqarray[i] = i; /* Slot -> sqe identity mapping. */ + + return; + +fail: + if (sq != MAP_FAILED) + munmap(sq, maxlen); + + if (sqe != MAP_FAILED) + munmap(sqe, sqelen); + + uv__close(ringfd); +} + + +static void uv__iou_delete(struct uv__iou* iou) { + if (iou->ringfd > -1) { + munmap(iou->sq, iou->maxlen); + munmap(iou->sqe, iou->sqelen); + uv__close(iou->ringfd); + iou->ringfd = -1; + } +} + + +int uv__platform_loop_init(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + lfields->ctl.ringfd = -1; + lfields->iou.ringfd = -2; /* "uninitialized" */ + + loop->inotify_watchers = NULL; + loop->inotify_fd = -1; + loop->backend_fd = epoll_create1(O_CLOEXEC); + + if (loop->backend_fd == -1) + return UV__ERR(errno); + + uv__iou_init(loop->backend_fd, &lfields->ctl, 256, 0); + + return 0; +} + + +int uv__io_fork(uv_loop_t* loop) { + int err; + struct watcher_list* root; + + root = uv__inotify_watchers(loop)->rbh_root; + + uv__close(loop->backend_fd); + loop->backend_fd = -1; + + /* TODO(bnoordhuis) Loses items from the submission and completion rings. */ + uv__platform_loop_delete(loop); + + err = uv__platform_loop_init(loop); + if (err) + return err; + + return uv__inotify_fork(loop, root); +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + uv__iou_delete(&lfields->ctl); + uv__iou_delete(&lfields->iou); + + if (loop->inotify_fd != -1) { + uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN); + uv__close(loop->inotify_fd); + loop->inotify_fd = -1; + } +} + + +struct uv__invalidate { + struct epoll_event (*prep)[256]; + struct epoll_event* events; + int nfds; +}; + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + uv__loop_internal_fields_t* lfields; + struct uv__invalidate* inv; + struct epoll_event dummy; + int i; + + lfields = uv__get_internal_fields(loop); + inv = lfields->inv; + + /* Invalidate events with same file descriptor */ + if (inv != NULL) + for (i = 0; i < inv->nfds; i++) + if (inv->events[i].data.fd == fd) + inv->events[i].data.fd = -1; + + /* Remove the file descriptor from the epoll. + * This avoids a problem where the same file description remains open + * in another process, causing repeated junk epoll events. + * + * Perform EPOLL_CTL_DEL immediately instead of going through + * io_uring's submit queue, otherwise the file descriptor may + * be closed by the time the kernel starts the operation. + * + * We pass in a dummy epoll_event, to work around a bug in old kernels. + * + * Work around a bug in kernels 3.10 to 3.19 where passing a struct that + * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings. + */ + memset(&dummy, 0, sizeof(dummy)); + epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct epoll_event e; + int rc; + + memset(&e, 0, sizeof(e)); + e.events = POLLIN; + e.data.fd = -1; + + rc = 0; + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) + if (errno != EEXIST) + rc = UV__ERR(errno); + + if (rc == 0) + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) + abort(); + + return rc; +} + + +/* Caller must initialize SQE and call uv__iou_submit(). */ +static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou, + uv_loop_t* loop, + uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + uint32_t head; + uint32_t tail; + uint32_t mask; + uint32_t slot; + + /* Lazily create the ring. State machine: -2 means uninitialized, -1 means + * initialization failed. Anything else is a valid ring file descriptor. + */ + if (iou->ringfd == -2) { + /* By default, the SQPOLL is not created. Enable only if the loop is + * configured with UV_LOOP_USE_IO_URING_SQPOLL and the UV_USE_IO_URING + * environment variable is unset or a positive number. + */ + if (loop->flags & UV_LOOP_ENABLE_IO_URING_SQPOLL) + if (uv__use_io_uring(UV__IORING_SETUP_SQPOLL)) + uv__iou_init(loop->backend_fd, iou, 64, UV__IORING_SETUP_SQPOLL); + + if (iou->ringfd == -2) + iou->ringfd = -1; /* "failed" */ + } + + if (iou->ringfd == -1) + return NULL; + + head = atomic_load_explicit((_Atomic uint32_t*) iou->sqhead, + memory_order_acquire); + tail = *iou->sqtail; + mask = iou->sqmask; + + if ((head & mask) == ((tail + 1) & mask)) + return NULL; /* No room in ring buffer. TODO(bnoordhuis) maybe flush it? */ + + slot = tail & mask; + sqe = iou->sqe; + sqe = &sqe[slot]; + memset(sqe, 0, sizeof(*sqe)); + sqe->user_data = (uintptr_t) req; + + /* Pacify uv_cancel(). */ + req->work_req.loop = loop; + req->work_req.work = NULL; + req->work_req.done = NULL; + uv__queue_init(&req->work_req.wq); + + uv__req_register(loop); + iou->in_flight++; + + return sqe; +} + + +static void uv__iou_submit(struct uv__iou* iou) { + uint32_t flags; + + atomic_store_explicit((_Atomic uint32_t*) iou->sqtail, + *iou->sqtail + 1, + memory_order_release); + + flags = atomic_load_explicit((_Atomic uint32_t*) iou->sqflags, + memory_order_acquire); + + if (flags & UV__IORING_SQ_NEED_WAKEUP) + if (uv__io_uring_enter(iou->ringfd, 0, 0, UV__IORING_ENTER_SQ_WAKEUP)) + if (errno != EOWNERDEAD) /* Kernel bug. Harmless, ignore. */ + perror("libuv: io_uring_enter(wakeup)"); /* Can't happen. */ +} + + +int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + int kv; + + kv = uv__kernel_version(); + /* Work around a poorly understood bug in older kernels where closing a file + * descriptor pointing to /foo/bar results in ETXTBSY errors when trying to + * execve("/foo/bar") later on. The bug seems to have been fixed somewhere + * between 5.15.85 and 5.15.90. I couldn't pinpoint the responsible commit + * but good candidates are the several data race fixes. Interestingly, it + * seems to manifest only when running under Docker so the possibility of + * a Docker bug can't be completely ruled out either. Yay, computers. + * Also, disable on non-longterm versions between 5.16.0 (non-longterm) and + * 6.1.0 (longterm). Starting with longterm 6.1.x, the issue seems to be + * solved. + */ + if (kv < /* 5.15.90 */ 0x050F5A) + return 0; + + if (kv >= /* 5.16.0 */ 0x050A00 && kv < /* 6.1.0 */ 0x060100) + return 0; + + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->fd = req->file; + sqe->opcode = UV__IORING_OP_CLOSE; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + if (uv__kernel_version() < /* 6.9 */0x060900) + return 0; + + iou = &uv__get_internal_fields(loop)->iou; + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->fd = req->file; + sqe->off = req->off; + sqe->opcode = UV__IORING_OP_FTRUNCATE; + uv__iou_submit(iou); + + return 1; +} + +int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop, + uv_fs_t* req, + uint32_t fsync_flags) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + /* Little known fact: setting seq->off and seq->len turns + * it into an asynchronous sync_file_range() operation. + */ + sqe->fd = req->file; + sqe->fsync_flags = fsync_flags; + sqe->opcode = UV__IORING_OP_FSYNC; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + if (uv__kernel_version() < /* 5.15.0 */0x050F00) + return 0; + + iou = &uv__get_internal_fields(loop)->iou; + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->addr2 = (uintptr_t) req->new_path; + sqe->len = AT_FDCWD; + sqe->opcode = UV__IORING_OP_LINKAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + if (uv__kernel_version() < /* 5.15.0 */0x050F00) + return 0; + + iou = &uv__get_internal_fields(loop)->iou; + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->len = req->mode; + sqe->opcode = UV__IORING_OP_MKDIRAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_open(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->len = req->mode; + sqe->opcode = UV__IORING_OP_OPENAT; + sqe->open_flags = req->flags | O_CLOEXEC; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_rename(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->addr2 = (uintptr_t) req->new_path; + sqe->len = AT_FDCWD; + sqe->opcode = UV__IORING_OP_RENAMEAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + if (uv__kernel_version() < /* 5.15.0 */0x050F00) + return 0; + + iou = &uv__get_internal_fields(loop)->iou; + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->addr2 = (uintptr_t) req->new_path; + sqe->opcode = UV__IORING_OP_SYMLINKAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->opcode = UV__IORING_OP_UNLINKAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_read_or_write(uv_loop_t* loop, + uv_fs_t* req, + int is_read) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + /* If iovcnt is greater than IOV_MAX, cap it to IOV_MAX on reads and fallback + * to the threadpool on writes */ + if (req->nbufs > IOV_MAX) { + if (is_read) + req->nbufs = IOV_MAX; + else + return 0; + } + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->bufs; + sqe->fd = req->file; + sqe->len = req->nbufs; + sqe->off = req->off < 0 ? -1 : req->off; + sqe->opcode = is_read ? UV__IORING_OP_READV : UV__IORING_OP_WRITEV; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_statx(uv_loop_t* loop, + uv_fs_t* req, + int is_fstat, + int is_lstat) { + struct uv__io_uring_sqe* sqe; + struct uv__statx* statxbuf; + struct uv__iou* iou; + + statxbuf = uv__malloc(sizeof(*statxbuf)); + if (statxbuf == NULL) + return 0; + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) { + uv__free(statxbuf); + return 0; + } + + req->ptr = statxbuf; + + sqe->addr = (uintptr_t) req->path; + sqe->addr2 = (uintptr_t) statxbuf; + sqe->fd = AT_FDCWD; + sqe->len = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */ + sqe->opcode = UV__IORING_OP_STATX; + + if (is_fstat) { + sqe->addr = (uintptr_t) ""; + sqe->fd = req->file; + sqe->statx_flags |= 0x1000; /* AT_EMPTY_PATH */ + } + + if (is_lstat) + sqe->statx_flags |= AT_SYMLINK_NOFOLLOW; + + uv__iou_submit(iou); + + return 1; +} + + +void uv__statx_to_stat(const struct uv__statx* statxbuf, uv_stat_t* buf) { + buf->st_dev = makedev(statxbuf->stx_dev_major, statxbuf->stx_dev_minor); + buf->st_mode = statxbuf->stx_mode; + buf->st_nlink = statxbuf->stx_nlink; + buf->st_uid = statxbuf->stx_uid; + buf->st_gid = statxbuf->stx_gid; + buf->st_rdev = makedev(statxbuf->stx_rdev_major, statxbuf->stx_rdev_minor); + buf->st_ino = statxbuf->stx_ino; + buf->st_size = statxbuf->stx_size; + buf->st_blksize = statxbuf->stx_blksize; + buf->st_blocks = statxbuf->stx_blocks; + buf->st_atim.tv_sec = statxbuf->stx_atime.tv_sec; + buf->st_atim.tv_nsec = statxbuf->stx_atime.tv_nsec; + buf->st_mtim.tv_sec = statxbuf->stx_mtime.tv_sec; + buf->st_mtim.tv_nsec = statxbuf->stx_mtime.tv_nsec; + buf->st_ctim.tv_sec = statxbuf->stx_ctime.tv_sec; + buf->st_ctim.tv_nsec = statxbuf->stx_ctime.tv_nsec; + buf->st_birthtim.tv_sec = statxbuf->stx_btime.tv_sec; + buf->st_birthtim.tv_nsec = statxbuf->stx_btime.tv_nsec; + buf->st_flags = 0; + buf->st_gen = 0; +} + + +static void uv__iou_fs_statx_post(uv_fs_t* req) { + struct uv__statx* statxbuf; + uv_stat_t* buf; + + buf = &req->statbuf; + statxbuf = req->ptr; + req->ptr = NULL; + + if (req->result == 0) { + uv__msan_unpoison(statxbuf, sizeof(*statxbuf)); + uv__statx_to_stat(statxbuf, buf); + req->ptr = buf; + } + + uv__free(statxbuf); +} + + +static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) { + struct uv__io_uring_cqe* cqe; + struct uv__io_uring_cqe* e; + uv_fs_t* req; + uint32_t head; + uint32_t tail; + uint32_t mask; + uint32_t i; + uint32_t flags; + int nevents; + int rc; + + head = *iou->cqhead; + tail = atomic_load_explicit((_Atomic uint32_t*) iou->cqtail, + memory_order_acquire); + mask = iou->cqmask; + cqe = iou->cqe; + nevents = 0; + + for (i = head; i != tail; i++) { + e = &cqe[i & mask]; + + req = (uv_fs_t*) (uintptr_t) e->user_data; + assert(req->type == UV_FS); + + uv__req_unregister(loop); + iou->in_flight--; + + /* If the op is not supported by the kernel retry using the thread pool */ + if (e->res == -EOPNOTSUPP) { + uv__fs_post(loop, req); + continue; + } + + /* io_uring stores error codes as negative numbers, same as libuv. */ + req->result = e->res; + + switch (req->fs_type) { + case UV_FS_FSTAT: + case UV_FS_LSTAT: + case UV_FS_STAT: + uv__iou_fs_statx_post(req); + break; + default: /* Squelch -Wswitch warnings. */ + break; + } + + uv__metrics_update_idle_time(loop); + req->cb(req); + nevents++; + } + + atomic_store_explicit((_Atomic uint32_t*) iou->cqhead, + tail, + memory_order_release); + + /* Check whether CQE's overflowed, if so enter the kernel to make them + * available. Don't grab them immediately but in the next loop iteration to + * avoid loop starvation. */ + flags = atomic_load_explicit((_Atomic uint32_t*) iou->sqflags, + memory_order_acquire); + + if (flags & UV__IORING_SQ_CQ_OVERFLOW) { + do + rc = uv__io_uring_enter(iou->ringfd, 0, 0, UV__IORING_ENTER_GETEVENTS); + while (rc == -1 && errno == EINTR); + + if (rc < 0) + perror("libuv: io_uring_enter(getevents)"); /* Can't happen. */ + } + + uv__metrics_inc_events(loop, nevents); + if (uv__get_internal_fields(loop)->current_timeout == 0) + uv__metrics_inc_events_waiting(loop, nevents); +} + + +/* Only for EPOLL_CTL_ADD and EPOLL_CTL_MOD. EPOLL_CTL_DEL should always be + * executed immediately, otherwise the file descriptor may have been closed + * by the time the kernel starts the operation. + */ +static void uv__epoll_ctl_prep(int epollfd, + struct uv__iou* ctl, + struct epoll_event (*events)[256], + int op, + int fd, + struct epoll_event* e) { + /* FIXME: Avoid dangling pointer to uv__io_poll stack frame. */ +#ifndef __clang_analyzer__ /* core.StackAddressEscape */ + struct uv__io_uring_sqe* sqe; + struct epoll_event* pe; + uint32_t mask; + uint32_t slot; + + assert(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD); + assert(ctl->ringfd != -1); + + mask = ctl->sqmask; + slot = (*ctl->sqtail)++ & mask; + + pe = &(*events)[slot]; + *pe = *e; + + sqe = ctl->sqe; + sqe = &sqe[slot]; + + memset(sqe, 0, sizeof(*sqe)); + sqe->addr = (uintptr_t) pe; + sqe->fd = epollfd; + sqe->len = op; + sqe->off = fd; + sqe->opcode = UV__IORING_OP_EPOLL_CTL; + sqe->user_data = op | slot << 2 | (int64_t) fd << 32; + + if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask)) + uv__epoll_ctl_flush(epollfd, ctl, events); +#endif +} + + +static void uv__epoll_ctl_flush(int epollfd, + struct uv__iou* ctl, + struct epoll_event (*events)[256]) { + struct epoll_event oldevents[256]; + struct uv__io_uring_cqe* cqe; + uint32_t oldslot; + uint32_t slot; + uint32_t n; + int fd; + int op; + int rc; + + STATIC_ASSERT(sizeof(oldevents) == sizeof(*events)); + assert(ctl->ringfd != -1); + assert(*ctl->sqhead != *ctl->sqtail); + + n = *ctl->sqtail - *ctl->sqhead; + do + rc = uv__io_uring_enter(ctl->ringfd, n, n, UV__IORING_ENTER_GETEVENTS); + while (rc == -1 && errno == EINTR); + + if (rc < 0) + perror("libuv: io_uring_enter(getevents)"); /* Can't happen. */ + + if (rc != (int) n) + abort(); + + assert(*ctl->sqhead == *ctl->sqtail); + + memcpy(oldevents, *events, sizeof(*events)); + + /* Failed submissions are either EPOLL_CTL_DEL commands for file descriptors + * that have been closed, or EPOLL_CTL_ADD commands for file descriptors + * that we are already watching. Ignore the former and retry the latter + * with EPOLL_CTL_MOD. + */ + while (*ctl->cqhead != *ctl->cqtail) { + slot = (*ctl->cqhead)++ & ctl->cqmask; + + cqe = ctl->cqe; + cqe = &cqe[slot]; + + if (cqe->res == 0) + continue; + + fd = cqe->user_data >> 32; + op = 3 & cqe->user_data; + oldslot = 255 & (cqe->user_data >> 2); + + if (op == EPOLL_CTL_DEL) + continue; + + if (op != EPOLL_CTL_ADD) + abort(); + + if (cqe->res != -EEXIST) + abort(); + + uv__epoll_ctl_prep(epollfd, + ctl, + events, + EPOLL_CTL_MOD, + fd, + &oldevents[oldslot]); + } +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + uv__loop_internal_fields_t* lfields; + struct epoll_event events[1024]; + struct epoll_event prep[256]; + struct uv__invalidate inv; + struct epoll_event* pe; + struct epoll_event e; + struct uv__iou* ctl; + struct uv__iou* iou; + int real_timeout; + struct uv__queue* q; + uv__io_t* w; + sigset_t* sigmask; + sigset_t sigset; + uint64_t base; + int have_iou_events; + int have_signals; + int nevents; + int epollfd; + int count; + int nfds; + int fd; + int op; + int i; + int user_timeout; + int reset_timeout; + + lfields = uv__get_internal_fields(loop); + ctl = &lfields->ctl; + iou = &lfields->iou; + + sigmask = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGPROF); + sigmask = &sigset; + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + real_timeout = timeout; + + if (lfields->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + user_timeout = 0; + } + + epollfd = loop->backend_fd; + + memset(&e, 0, sizeof(e)); + + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + w = uv__queue_data(q, uv__io_t, watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); + + op = EPOLL_CTL_MOD; + if (w->events == 0) + op = EPOLL_CTL_ADD; + + w->events = w->pevents; + e.events = w->pevents; + e.data.fd = w->fd; + fd = w->fd; + + if (ctl->ringfd != -1) { + uv__epoll_ctl_prep(epollfd, ctl, &prep, op, fd, &e); + continue; + } + + if (!epoll_ctl(epollfd, op, fd, &e)) + continue; + + assert(op == EPOLL_CTL_ADD); + assert(errno == EEXIST); + + /* File descriptor that's been watched before, update event mask. */ + if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &e)) + abort(); + } + + inv.events = events; + inv.prep = &prep; + inv.nfds = -1; + + for (;;) { + if (loop->nfds == 0) + if (iou->in_flight == 0) + break; + + /* All event mask mutations should be visible to the kernel before + * we enter epoll_pwait(). + */ + if (ctl->ringfd != -1) + while (*ctl->sqhead != *ctl->sqtail) + uv__epoll_ctl_flush(epollfd, ctl, &prep); + + uv__io_poll_prepare(loop, NULL, timeout); + nfds = epoll_pwait(epollfd, events, ARRAY_SIZE(events), timeout, sigmask); + uv__io_poll_check(loop, NULL); + + if (nfds == -1) + assert(errno == EINTR); + else if (nfds == 0) + /* Unlimited timeout should only return with events or signal. */ + assert(timeout != -1); + + if (nfds == 0 || nfds == -1) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } else if (nfds == 0) { + return; + } + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + have_iou_events = 0; + have_signals = 0; + nevents = 0; + + inv.nfds = nfds; + lfields->inv = &inv; + + for (i = 0; i < nfds; i++) { + pe = events + i; + fd = pe->data.fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + + if (fd == iou->ringfd) { + uv__poll_io_uring(loop, iou); + have_iou_events = 1; + continue; + } + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * Ignore all errors because we may be racing with another thread + * when the file descriptor is closed. + * + * Perform EPOLL_CTL_DEL immediately instead of going through + * io_uring's submit queue, otherwise the file descriptor may + * be closed by the time the kernel starts the operation. + */ + epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, pe); + continue; + } + + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | POLLERR | POLLHUP; + + /* Work around an epoll quirk where it sometimes reports just the + * EPOLLERR or EPOLLHUP event. In order to force the event loop to + * move forward, we merge in the read/write events that the watcher + * is interested in; uv__read() and uv__write() will then deal with + * the error or hangup in the usual fashion. + * + * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user + * reads the available data, calls uv_read_stop(), then sometime later + * calls uv_read_start() again. By then, libuv has forgotten about the + * hangup and the kernel won't report EPOLLIN again because there's + * nothing left to read. If anything, libuv is to blame here. The + * current hack is just a quick bandaid; to properly fix it, libuv + * needs to remember the error/hangup event. We should get that for + * free when we switch over to edge-triggered I/O. + */ + if (pe->events == POLLERR || pe->events == POLLHUP) + pe->events |= + w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); + + if (pe->events != 0) { + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + uv__io_cb(loop, w, pe->events); + } + + nevents++; + } + } + + uv__metrics_inc_events(loop, nevents); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + uv__signal_event(loop, &loop->signal_io_watcher, POLLIN); + } + + lfields->inv = NULL; + + if (have_iou_events != 0) + break; /* Event loop should cycle now so don't poll again. */ + + if (have_signals != 0) + break; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + break; + } + +update_timeout: + if (timeout == 0) + break; + + if (timeout == -1) + continue; + + assert(timeout > 0); + + real_timeout -= (loop->time - base); + if (real_timeout <= 0) + break; + + timeout = real_timeout; + } + + if (ctl->ringfd != -1) + while (*ctl->sqhead != *ctl->sqtail) + uv__epoll_ctl_flush(epollfd, ctl, &prep); +} + +uint64_t uv__hrtime(uv_clocktype_t type) { + static _Atomic clock_t fast_clock_id = -1; + struct timespec t; + clock_t clock_id; + + /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has + * millisecond granularity or better. CLOCK_MONOTONIC_COARSE is + * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may + * decide to make a costly system call. + */ + /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE + * when it has microsecond granularity or better (unlikely). + */ + clock_id = CLOCK_MONOTONIC; + if (type != UV_CLOCK_FAST) + goto done; + + clock_id = atomic_load_explicit(&fast_clock_id, memory_order_relaxed); + if (clock_id != -1) + goto done; + + clock_id = CLOCK_MONOTONIC; + if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t)) + if (t.tv_nsec <= 1 * 1000 * 1000) + clock_id = CLOCK_MONOTONIC_COARSE; + + atomic_store_explicit(&fast_clock_id, clock_id, memory_order_relaxed); + +done: + + if (clock_gettime(clock_id, &t)) + return 0; /* Not really possible. */ + + return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; +} + + +int uv_resident_set_memory(size_t* rss) { + char buf[1024]; + const char* s; + long val; + int rc; + int i; + + /* rss: 24th element */ + rc = uv__slurp("/proc/self/stat", buf, sizeof(buf)); + if (rc < 0) + return rc; + + /* find the last ')' */ + s = strrchr(buf, ')'); + if (s == NULL) + goto err; + + for (i = 1; i <= 22; i++) { + s = strchr(s + 1, ' '); + if (s == NULL) + goto err; + } + + errno = 0; + val = strtol(s, NULL, 10); + if (val < 0 || errno != 0) + goto err; + + *rss = val * getpagesize(); + return 0; + +err: + return UV_EINVAL; +} + +int uv_uptime(double* uptime) { + struct timespec now; + char buf[128]; + + /* Consult /proc/uptime when present (common case), or fall back to + * clock_gettime. Why not always clock_gettime? It doesn't always return the + * right result under OpenVZ and possibly other containerized environments. + */ + if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf))) + if (1 == sscanf(buf, "%lf", uptime)) + return 0; + + if (clock_gettime(CLOCK_BOOTTIME, &now)) + return UV__ERR(errno); + + *uptime = now.tv_sec; + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** ci, int* count) { +#if defined(__PPC__) + static const char model_marker[] = "cpu\t\t: "; + static const char model_marker2[] = ""; +#elif defined(__arm__) + static const char model_marker[] = "model name\t: "; + static const char model_marker2[] = "Processor\t: "; +#elif defined(__aarch64__) + static const char model_marker[] = "CPU part\t: "; + static const char model_marker2[] = ""; +#elif defined(__mips__) + static const char model_marker[] = "cpu model\t\t: "; + static const char model_marker2[] = ""; +#elif defined(__loongarch__) + static const char model_marker[] = "cpu family\t\t: "; + static const char model_marker2[] = ""; +#else + static const char model_marker[] = "model name\t: "; + static const char model_marker2[] = ""; +#endif + static const char parts[] = +#ifdef __aarch64__ + "0x811\nARM810\n" "0x920\nARM920\n" "0x922\nARM922\n" + "0x926\nARM926\n" "0x940\nARM940\n" "0x946\nARM946\n" + "0x966\nARM966\n" "0xa20\nARM1020\n" "0xa22\nARM1022\n" + "0xa26\nARM1026\n" "0xb02\nARM11 MPCore\n" "0xb36\nARM1136\n" + "0xb56\nARM1156\n" "0xb76\nARM1176\n" "0xc05\nCortex-A5\n" + "0xc07\nCortex-A7\n" "0xc08\nCortex-A8\n" "0xc09\nCortex-A9\n" + "0xc0d\nCortex-A17\n" /* Originally A12 */ + "0xc0f\nCortex-A15\n" "0xc0e\nCortex-A17\n" "0xc14\nCortex-R4\n" + "0xc15\nCortex-R5\n" "0xc17\nCortex-R7\n" "0xc18\nCortex-R8\n" + "0xc20\nCortex-M0\n" "0xc21\nCortex-M1\n" "0xc23\nCortex-M3\n" + "0xc24\nCortex-M4\n" "0xc27\nCortex-M7\n" "0xc60\nCortex-M0+\n" + "0xd01\nCortex-A32\n" "0xd03\nCortex-A53\n" "0xd04\nCortex-A35\n" + "0xd05\nCortex-A55\n" "0xd06\nCortex-A65\n" "0xd07\nCortex-A57\n" + "0xd08\nCortex-A72\n" "0xd09\nCortex-A73\n" "0xd0a\nCortex-A75\n" + "0xd0b\nCortex-A76\n" "0xd0c\nNeoverse-N1\n" "0xd0d\nCortex-A77\n" + "0xd0e\nCortex-A76AE\n" "0xd13\nCortex-R52\n" "0xd20\nCortex-M23\n" + "0xd21\nCortex-M33\n" "0xd41\nCortex-A78\n" "0xd42\nCortex-A78AE\n" + "0xd4a\nNeoverse-E1\n" "0xd4b\nCortex-A78C\n" "0xd4f\nNeoverse-V2\n" +#endif + ""; + struct cpu { + unsigned long long freq, user, nice, sys, idle, irq; + unsigned model; + }; + FILE* fp; + char* p; + int found; + int n; + unsigned i; + unsigned cpu; + unsigned maxcpu; + unsigned size; + unsigned long long skip; + struct cpu (*cpus)[8192]; /* Kernel maximum. */ + struct cpu* c; + struct cpu t; + char (*model)[64]; + unsigned char bitmap[ARRAY_SIZE(*cpus) / 8]; + /* Assumption: even big.LITTLE systems will have only a handful + * of different CPU models. Most systems will just have one. + */ + char models[8][64]; + char buf[1024]; + + memset(bitmap, 0, sizeof(bitmap)); + memset(models, 0, sizeof(models)); + snprintf(*models, sizeof(*models), "unknown"); + maxcpu = 0; + + cpus = uv__calloc(ARRAY_SIZE(*cpus), sizeof(**cpus)); + if (cpus == NULL) + return UV_ENOMEM; + + fp = uv__open_file("/proc/stat"); + if (fp == NULL) { + uv__free(cpus); + return UV__ERR(errno); + } + + if (NULL == fgets(buf, sizeof(buf), fp)) + abort(); + + for (;;) { + memset(&t, 0, sizeof(t)); + + n = fscanf(fp, "cpu%u %llu %llu %llu %llu %llu %llu", + &cpu, &t.user, &t.nice, &t.sys, &t.idle, &skip, &t.irq); + + if (n != 7) + break; + + if (NULL == fgets(buf, sizeof(buf), fp)) + abort(); + + if (cpu >= ARRAY_SIZE(*cpus)) + continue; + + (*cpus)[cpu] = t; + + bitmap[cpu >> 3] |= 1 << (cpu & 7); + + if (cpu >= maxcpu) + maxcpu = cpu + 1; + } + + fclose(fp); + + fp = uv__open_file("/proc/cpuinfo"); + if (fp == NULL) + goto nocpuinfo; + + for (;;) { + if (1 != fscanf(fp, "processor\t: %u\n", &cpu)) + break; /* Parse error. */ + + while (fgets(buf, sizeof(buf), fp)) { + if (!strncmp(buf, model_marker, sizeof(model_marker) - 1)) { + p = buf + sizeof(model_marker) - 1; + goto parts; + } + if (!*model_marker2) + continue; + if (!strncmp(buf, model_marker2, sizeof(model_marker2) - 1)) { + p = buf + sizeof(model_marker2) - 1; + goto parts; + } + } + + goto next; /* Not found. */ + +parts: + n = (int) strcspn(p, "\n"); + + /* arm64: translate CPU part code to model name. */ + if (*parts) { + p = memmem(parts, sizeof(parts) - 1, p, n + 1); + if (p == NULL) + p = "unknown"; + else + p += n + 1; + n = (int) strcspn(p, "\n"); + } + + found = 0; + for (model = models; !found && model < ARRAY_END(models); model++) + found = !strncmp(p, *model, strlen(*model)); + + if (!found) + goto next; + + if (**model == '\0') + snprintf(*model, sizeof(*model), "%.*s", n, p); + + if (cpu < maxcpu) + (*cpus)[cpu].model = model - models; + +next: + while (fgets(buf, sizeof(buf), fp)) + if (*buf == '\n') + break; + } + + fclose(fp); + fp = NULL; + +nocpuinfo: + + n = 0; + for (cpu = 0; cpu < maxcpu; cpu++) { + if (!(bitmap[cpu >> 3] & (1 << (cpu & 7)))) + continue; + + n++; + snprintf(buf, sizeof(buf), + "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", cpu); + + fp = uv__open_file(buf); + if (fp == NULL) + continue; + + if (1 != fscanf(fp, "%llu", &(*cpus)[cpu].freq)) + abort(); + fclose(fp); + fp = NULL; + } + + size = n * sizeof(**ci) + sizeof(models); + *ci = uv__malloc(size); + *count = 0; + + if (*ci == NULL) { + uv__free(cpus); + return UV_ENOMEM; + } + + *count = n; + p = memcpy(*ci + n, models, sizeof(models)); + + i = 0; + for (cpu = 0; cpu < maxcpu; cpu++) { + if (!(bitmap[cpu >> 3] & (1 << (cpu & 7)))) + continue; + + c = *cpus + cpu; + + (*ci)[i++] = (uv_cpu_info_t) { + .model = p + c->model * sizeof(*model), + .speed = c->freq / 1000, + /* Note: sysconf(_SC_CLK_TCK) is fixed at 100 Hz, + * therefore the multiplier is always 1000/100 = 10. + */ + .cpu_times = (struct uv_cpu_times_s) { + .user = 10 * c->user, + .nice = 10 * c->nice, + .sys = 10 * c->sys, + .idle = 10 * c->idle, + .irq = 10 * c->irq, + }, + }; + } + + uv__free(cpus); + + return 0; +} + + +static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { + if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) + return 1; + if (ent->ifa_addr == NULL) + return 1; + /* + * On Linux getifaddrs returns information related to the raw underlying + * devices. We're not interested in this information yet. + */ + if (ent->ifa_addr->sa_family == PF_PACKET) + return exclude_type; + return !exclude_type; +} + +/* TODO(bnoordhuis) share with bsd-ifaddrs.c */ +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; + struct sockaddr_ll* sll; + struct ifaddrs* addrs; + struct ifaddrs* ent; + size_t namelen; + char* name; + int i; + + *count = 0; + *addresses = NULL; + + if (getifaddrs(&addrs)) + return UV__ERR(errno); + + /* Count the number of interfaces */ + namelen = 0; + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) + continue; + + namelen += strlen(ent->ifa_name) + 1; + (*count)++; + } + + if (*count == 0) { + freeifaddrs(addrs); + return 0; + } + + /* Make sure the memory is initiallized to zero using calloc() */ + *addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen); + if (*addresses == NULL) { + freeifaddrs(addrs); + return UV_ENOMEM; + } + + name = (char*) &(*addresses)[*count]; + address = *addresses; + + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) + continue; + + namelen = strlen(ent->ifa_name) + 1; + address->name = memcpy(name, ent->ifa_name, namelen); + name += namelen; + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); + } + + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); + } + + address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); + + address++; + } + + /* Fill in physical addresses for each interface */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) + continue; + + address = *addresses; + + for (i = 0; i < (*count); i++) { + size_t namelen = strlen(ent->ifa_name); + /* Alias interface share the same physical address */ + if (strncmp(address->name, ent->ifa_name, namelen) == 0 && + (address->name[namelen] == 0 || address->name[namelen] == ':')) { + sll = (struct sockaddr_ll*)ent->ifa_addr; + memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); + } + address++; + } + } + + freeifaddrs(addrs); + + return 0; +} + + +void uv__set_process_title(const char* title) { +#if defined(PR_SET_NAME) + prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ +#endif +} + + +static uint64_t uv__read_proc_meminfo(const char* what) { + uint64_t rc; + char* p; + char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ + + if (uv__slurp("/proc/meminfo", buf, sizeof(buf))) + return 0; + + p = strstr(buf, what); + + if (p == NULL) + return 0; + + p += strlen(what); + + rc = 0; + sscanf(p, "%" PRIu64 " kB", &rc); + + return rc * 1024; +} + + +uint64_t uv_get_free_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemAvailable:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.freeram * info.mem_unit; + + return 0; +} + + +uint64_t uv_get_total_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemTotal:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.totalram * info.mem_unit; + + return 0; +} + + +static uint64_t uv__read_uint64(const char* filename) { + char buf[32]; /* Large enough to hold an encoded uint64_t. */ + uint64_t rc; + + rc = 0; + if (0 == uv__slurp(filename, buf, sizeof(buf))) + if (1 != sscanf(buf, "%" PRIu64, &rc)) + if (0 == strcmp(buf, "max\n")) + rc = UINT64_MAX; + + return rc; +} + + +/* Given a buffer with the contents of a cgroup1 /proc/self/cgroups, + * finds the location and length of the memory controller mount path. + * This disregards the leading / for easy concatenation of paths. + * Returns NULL if the memory controller wasn't found. */ +static char* uv__cgroup1_find_memory_controller(char buf[static 1024], + int* n) { + char* p; + + /* Seek to the memory controller line. */ + p = strchr(buf, ':'); + while (p != NULL && strncmp(p, ":memory:", 8)) { + p = strchr(p, '\n'); + if (p != NULL) + p = strchr(p, ':'); + } + + if (p != NULL) { + /* Determine the length of the mount path. */ + p = p + strlen(":memory:/"); + *n = (int) strcspn(p, "\n"); + } + + return p; +} + +static void uv__get_cgroup1_memory_limits(char buf[static 1024], uint64_t* high, + uint64_t* max) { + char filename[4097]; + char* p; + int n; + uint64_t cgroup1_max; + + /* Find out where the controller is mounted. */ + p = uv__cgroup1_find_memory_controller(buf, &n); + if (p != NULL) { + snprintf(filename, sizeof(filename), + "/sys/fs/cgroup/memory/%.*s/memory.soft_limit_in_bytes", n, p); + *high = uv__read_uint64(filename); + + snprintf(filename, sizeof(filename), + "/sys/fs/cgroup/memory/%.*s/memory.limit_in_bytes", n, p); + *max = uv__read_uint64(filename); + + /* If the controller wasn't mounted, the reads above will have failed, + * as indicated by uv__read_uint64 returning 0. + */ + if (*high != 0 && *max != 0) + goto update_limits; + } + + /* Fall back to the limits of the global memory controller. */ + *high = uv__read_uint64("/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"); + *max = uv__read_uint64("/sys/fs/cgroup/memory/memory.limit_in_bytes"); + + /* uv__read_uint64 detects cgroup2's "max", so we need to separately detect + * cgroup1's maximum value (which is derived from LONG_MAX and PAGE_SIZE). + */ +update_limits: + cgroup1_max = LONG_MAX & ~(sysconf(_SC_PAGESIZE) - 1); + if (*high == cgroup1_max) + *high = UINT64_MAX; + if (*max == cgroup1_max) + *max = UINT64_MAX; +} + +static void uv__get_cgroup2_memory_limits(char buf[static 1024], uint64_t* high, + uint64_t* max) { + char filename[4097]; + char* p; + int n; + + /* Find out where the controller is mounted. */ + p = buf + strlen("0::/"); + n = (int) strcspn(p, "\n"); + + /* Read the memory limits of the controller. */ + snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%.*s/memory.max", n, p); + *max = uv__read_uint64(filename); + snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%.*s/memory.high", n, p); + *high = uv__read_uint64(filename); +} + +static uint64_t uv__get_cgroup_constrained_memory(char buf[static 1024]) { + uint64_t high; + uint64_t max; + + /* In the case of cgroupv2, we'll only have a single entry. */ + if (strncmp(buf, "0::/", 4)) + uv__get_cgroup1_memory_limits(buf, &high, &max); + else + uv__get_cgroup2_memory_limits(buf, &high, &max); + + if (high == 0 || max == 0) + return 0; + + return high < max ? high : max; +} + +uint64_t uv_get_constrained_memory(void) { + char buf[1024]; + + if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf))) + return 0; + + return uv__get_cgroup_constrained_memory(buf); +} + + +static uint64_t uv__get_cgroup1_current_memory(char buf[static 1024]) { + char filename[4097]; + uint64_t current; + char* p; + int n; + + /* Find out where the controller is mounted. */ + p = uv__cgroup1_find_memory_controller(buf, &n); + if (p != NULL) { + snprintf(filename, sizeof(filename), + "/sys/fs/cgroup/memory/%.*s/memory.usage_in_bytes", n, p); + current = uv__read_uint64(filename); + + /* If the controller wasn't mounted, the reads above will have failed, + * as indicated by uv__read_uint64 returning 0. + */ + if (current != 0) + return current; + } + + /* Fall back to the usage of the global memory controller. */ + return uv__read_uint64("/sys/fs/cgroup/memory/memory.usage_in_bytes"); +} + +static uint64_t uv__get_cgroup2_current_memory(char buf[static 1024]) { + char filename[4097]; + char* p; + int n; + + /* Find out where the controller is mounted. */ + p = buf + strlen("0::/"); + n = (int) strcspn(p, "\n"); + + snprintf(filename, sizeof(filename), + "/sys/fs/cgroup/%.*s/memory.current", n, p); + return uv__read_uint64(filename); +} + +uint64_t uv_get_available_memory(void) { + char buf[1024]; + uint64_t constrained; + uint64_t current; + uint64_t total; + + if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf))) + return 0; + + constrained = uv__get_cgroup_constrained_memory(buf); + if (constrained == 0) + return uv_get_free_memory(); + + total = uv_get_total_memory(); + if (constrained > total) + return uv_get_free_memory(); + + /* In the case of cgroupv2, we'll only have a single entry. */ + if (strncmp(buf, "0::/", 4)) + current = uv__get_cgroup1_current_memory(buf); + else + current = uv__get_cgroup2_current_memory(buf); + + /* memory usage can be higher than the limit (for short bursts of time) */ + if (constrained < current) + return 0; + + return constrained - current; +} + + +static int uv__get_cgroupv2_constrained_cpu(const char* cgroup, + long long* quota) { + static const char cgroup_mount[] = "/sys/fs/cgroup"; + const char* cgroup_trimmed; + char buf[1024]; + char path[256]; + char full_path[sizeof(path) + sizeof("/cpu.max")]; + char quota_buf[16]; + char* last_slash; + int cgroup_size; + long long limit; + long long min_quota; + long long period; + + if (strncmp(cgroup, "0::/", 4) != 0) + return UV_EINVAL; + + /* Trim ending \n by replacing it with a 0 */ + cgroup_trimmed = cgroup + sizeof("0::/") - 1; /* Skip the prefix "0::/" */ + cgroup_size = (int)strcspn(cgroup_trimmed, "\n"); /* Find the first \n */ + min_quota = LLONG_MAX; + + /* Construct the path to the cpu.max files */ + snprintf(path, sizeof(path), "%s/%.*s/cgroup.controllers", cgroup_mount, + cgroup_size, cgroup_trimmed); + + /* Read controllers, if not exists, not really a cgroup */ + if (uv__slurp(path, buf, sizeof(buf)) < 0) + return UV_EIO; + + snprintf(path, sizeof(path), "%s/%.*s", cgroup_mount, cgroup_size, + cgroup_trimmed); + + /* + * Traverse up the cgroup v2 hierarchy, starting from the current cgroup path. + * At each level, attempt to read the "cpu.max" file, which defines the CPU + * quota and period. + * + * This reflects how Linux applies cgroup limits hierarchically. + * + * e.g: given a path like /sys/fs/cgroup/foo/bar/baz, we check: + * - /sys/fs/cgroup/foo/bar/baz/cpu.max + * - /sys/fs/cgroup/foo/bar/cpu.max + * - /sys/fs/cgroup/foo/cpu.max + * - /sys/fs/cgroup/cpu.max + */ + while (strncmp(path, cgroup_mount, strlen(cgroup_mount)) == 0) { + snprintf(full_path, sizeof(full_path), "%s/cpu.max", path); + + /* Silently ignore and continue if the file does not exist */ + if (uv__slurp(full_path, quota_buf, sizeof(quota_buf)) < 0) + goto next; + + /* No limit, move on */ + if (strncmp(quota_buf, "max", 3) == 0) + goto next; + + /* Read cpu.max */ + if (sscanf(quota_buf, "%lld %lld", &limit, &period) != 2) + goto next; + + /* Can't divide by 0 */ + if (period == 0) + goto next; + + *quota = limit / period; + if (*quota == 0) + *quota = 1; + if (*quota < min_quota) + min_quota = *quota; + +next: + /* Move up one level in the cgroup hierarchy by trimming the last path. + * The loop ends once we reach the cgroup root mount point. + */ + last_slash = strrchr(path, '/'); + if (last_slash == NULL || strcmp(path, cgroup_mount) == 0) + break; + *last_slash = '\0'; + } + + return 0; +} + +static char* uv__cgroup1_find_cpu_controller(const char* cgroup, + int* cgroup_size) { + /* Seek to the cpu controller line. */ + char* cgroup_cpu = strstr(cgroup, ":cpu,"); + + if (cgroup_cpu != NULL) { + /* Skip the controller prefix to the start of the cgroup path. */ + cgroup_cpu += sizeof(":cpu,") - 1; + /* Determine the length of the cgroup path, excluding the newline. */ + *cgroup_size = (int)strcspn(cgroup_cpu, "\n"); + } + + return cgroup_cpu; +} + +static int uv__get_cgroupv1_constrained_cpu(const char* cgroup, + long long* quota) { + char path[256]; + char buf[1024]; + int cgroup_size; + char* cgroup_cpu; + long long period_length; + long long quota_per_period; + + cgroup_cpu = uv__cgroup1_find_cpu_controller(cgroup, &cgroup_size); + + if (cgroup_cpu == NULL) + return UV_EIO; + + /* Construct the path to the cpu.cfs_quota_us file */ + snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.cfs_quota_us", + cgroup_size, cgroup_cpu); + + /* Read cpu.cfs_quota_us */ + if (uv__slurp(path, buf, sizeof(buf)) < 0) + return UV_EIO; + + if (sscanf(buf, "%lld", "a_per_period) != 1) + return UV_EINVAL; + + /* Construct the path to the cpu.cfs_period_us file */ + snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.cfs_period_us", + cgroup_size, cgroup_cpu); + + /* Read cpu.cfs_period_us */ + if (uv__slurp(path, buf, sizeof(buf)) < 0) + return UV_EIO; + + if (sscanf(buf, "%lld", &period_length) != 1) + return UV_EINVAL; + + /* Can't divide by 0 */ + if (period_length == 0) + return UV_EINVAL; + + *quota = quota_per_period / period_length; + + return 0; +} + +int uv__get_constrained_cpu(long long* quota) { + char cgroup[1024]; + + /* Read the cgroup from /proc/self/cgroup */ + if (uv__slurp("/proc/self/cgroup", cgroup, sizeof(cgroup)) < 0) + return UV_EIO; + + /* Check if the system is using cgroup v2 by examining /proc/self/cgroup + * The entry for cgroup v2 is always in the format "0::$PATH" + * see https://docs.kernel.org/admin-guide/cgroup-v2.html */ + if (strncmp(cgroup, "0::/", 4) == 0) + return uv__get_cgroupv2_constrained_cpu(cgroup, quota); + else + return uv__get_cgroupv1_constrained_cpu(cgroup, quota); +} + + +void uv_loadavg(double avg[3]) { + struct sysinfo info; + char buf[128]; /* Large enough to hold all of /proc/loadavg. */ + + if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf))) + if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2])) + return; + + if (sysinfo(&info) < 0) + return; + + avg[0] = (double) info.loads[0] / 65536.0; + avg[1] = (double) info.loads[1] / 65536.0; + avg[2] = (double) info.loads[2] / 65536.0; +} + + +static int compare_watchers(const struct watcher_list* a, + const struct watcher_list* b) { + if (a->wd < b->wd) return -1; + if (a->wd > b->wd) return 1; + return 0; +} + + +static int init_inotify(uv_loop_t* loop) { + int err; + int fd; + + if (loop->inotify_fd != -1) + return 0; + + fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd < 0) + return UV__ERR(errno); + + err = uv__io_init_start(loop, &loop->inotify_read_watcher, UV__INOTIFY_READ, + fd, POLLIN); + if (err) { + uv__close(fd); + return err; + } + + loop->inotify_fd = fd; + return 0; +} + + +static int uv__inotify_fork(uv_loop_t* loop, struct watcher_list* root) { + /* Open the inotify_fd, and re-arm all the inotify watchers. */ + int err; + struct watcher_list* tmp_watcher_list_iter; + struct watcher_list* watcher_list; + struct watcher_list tmp_watcher_list; + struct uv__queue queue; + struct uv__queue* q; + uv_fs_event_t* handle; + char* tmp_path; + + if (root == NULL) + return 0; + + /* We must restore the old watcher list to be able to close items + * out of it. + */ + loop->inotify_watchers = root; + + uv__queue_init(&tmp_watcher_list.watchers); + /* Note that the queue we use is shared with the start and stop() + * functions, making uv__queue_foreach unsafe to use. So we use the + * uv__queue_move trick to safely iterate. Also don't free the watcher + * list until we're done iterating. c.f. uv__inotify_read. + */ + RB_FOREACH_SAFE(watcher_list, watcher_root, + uv__inotify_watchers(loop), tmp_watcher_list_iter) { + watcher_list->iterating = 1; + uv__queue_move(&watcher_list->watchers, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + handle = uv__queue_data(q, uv_fs_event_t, watchers); + /* It's critical to keep a copy of path here, because it + * will be set to NULL by stop() and then deallocated by + * maybe_free_watcher_list + */ + tmp_path = uv__strdup(handle->path); + assert(tmp_path != NULL); + uv__queue_remove(q); + uv__queue_insert_tail(&watcher_list->watchers, q); + uv_fs_event_stop(handle); + + uv__queue_insert_tail(&tmp_watcher_list.watchers, &handle->watchers); + handle->path = tmp_path; + } + watcher_list->iterating = 0; + maybe_free_watcher_list(watcher_list, loop); + } + + uv__queue_move(&tmp_watcher_list.watchers, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + uv__queue_remove(q); + handle = uv__queue_data(q, uv_fs_event_t, watchers); + tmp_path = handle->path; + handle->path = NULL; + err = uv_fs_event_start(handle, handle->cb, tmp_path, 0); + uv__free(tmp_path); + if (err) + return err; + } + + return 0; +} + + +static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) { + struct watcher_list w; + w.wd = wd; + return RB_FIND(watcher_root, uv__inotify_watchers(loop), &w); +} + + +static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) { + /* if the watcher_list->watchers is being iterated over, we can't free it. */ + if ((!w->iterating) && uv__queue_empty(&w->watchers)) { + /* No watchers left for this path. Clean up. */ + RB_REMOVE(watcher_root, uv__inotify_watchers(loop), w); + inotify_rm_watch(loop->inotify_fd, w->wd); + uv__free(w); + } +} + + +void uv__inotify_read(uv_loop_t* loop, uv__io_t* dummy, unsigned int events) { + const struct inotify_event* e; + struct watcher_list* w; + uv_fs_event_t* h; + struct uv__queue queue; + struct uv__queue* q; + const char* path; + ssize_t size; + const char *p; + /* needs to be large enough for sizeof(inotify_event) + strlen(path) */ + char buf[4096]; + + for (;;) { + do + size = read(loop->inotify_fd, buf, sizeof(buf)); + while (size == -1 && errno == EINTR); + + if (size == -1) { + assert(errno == EAGAIN || errno == EWOULDBLOCK); + break; + } + + assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */ + + /* Now we have one or more inotify_event structs. */ + for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { + e = (const struct inotify_event*) p; + + events = 0; + if (e->mask & (IN_ATTRIB|IN_MODIFY)) + events |= UV_CHANGE; + if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) + events |= UV_RENAME; + + w = find_watcher(loop, e->wd); + if (w == NULL) + continue; /* Stale event, no watchers left. */ + + /* inotify does not return the filename when monitoring a single file + * for modifications. Repurpose the filename for API compatibility. + * I'm not convinced this is a good thing, maybe it should go. + */ + path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path); + + /* We're about to iterate over the queue and call user's callbacks. + * What can go wrong? + * A callback could call uv_fs_event_stop() + * and the queue can change under our feet. + * So, we use uv__queue_move() trick to safely iterate over the queue. + * And we don't free the watcher_list until we're done iterating. + * + * First, + * tell uv_fs_event_stop() (that could be called from a user's callback) + * not to free watcher_list. + */ + w->iterating = 1; + uv__queue_move(&w->watchers, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_fs_event_t, watchers); + + uv__queue_remove(q); + uv__queue_insert_tail(&w->watchers, q); + + h->cb(h, path, events, 0); + } + /* done iterating, time to (maybe) free empty watcher_list */ + w->iterating = 0; + maybe_free_watcher_list(w, loop); + } + } +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, + uv_fs_event_cb cb, + const char* path, + unsigned int flags) { + struct watcher_list* w; + uv_loop_t* loop; + size_t len; + int events; + int err; + int wd; + + if (uv__is_active(handle)) + return UV_EINVAL; + + loop = handle->loop; + + err = init_inotify(loop); + if (err) + return err; + + events = IN_ATTRIB + | IN_CREATE + | IN_MODIFY + | IN_DELETE + | IN_DELETE_SELF + | IN_MOVE_SELF + | IN_MOVED_FROM + | IN_MOVED_TO; + + wd = inotify_add_watch(loop->inotify_fd, path, events); + if (wd == -1) + return UV__ERR(errno); + + w = find_watcher(loop, wd); + if (w) + goto no_insert; + + len = strlen(path) + 1; + w = uv__malloc(sizeof(*w) + len); + if (w == NULL) + return UV_ENOMEM; + + w->wd = wd; + w->path = memcpy(w + 1, path, len); + uv__queue_init(&w->watchers); + w->iterating = 0; + RB_INSERT(watcher_root, uv__inotify_watchers(loop), w); + +no_insert: + uv__handle_start(handle); + uv__queue_insert_tail(&w->watchers, &handle->watchers); + handle->path = w->path; + handle->cb = cb; + handle->wd = wd; + + return 0; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + struct watcher_list* w; + + if (!uv__is_active(handle)) + return 0; + + w = find_watcher(handle->loop, handle->wd); + assert(w != NULL); + + handle->wd = -1; + handle->path = NULL; + uv__handle_stop(handle); + uv__queue_remove(&handle->watchers); + + maybe_free_watcher_list(w, handle->loop); + + return 0; +} + + +void uv__fs_event_close(uv_fs_event_t* handle) { + uv_fs_event_stop(handle); +}
diff --git a/Utilities/cmlibuv/src/unix/loop-watcher.c b/Utilities/cmlibuv/src/unix/loop-watcher.c index b8c1c2a..2db8b51 100644 --- a/Utilities/cmlibuv/src/unix/loop-watcher.c +++ b/Utilities/cmlibuv/src/unix/loop-watcher.c
@@ -32,7 +32,7 @@ int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ if (uv__is_active(handle)) return 0; \ if (cb == NULL) return UV_EINVAL; \ - QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \ + uv__queue_insert_head(&handle->loop->name##_handles, &handle->queue); \ handle->name##_cb = cb; \ uv__handle_start(handle); \ return 0; \ @@ -40,21 +40,21 @@ \ int uv_##name##_stop(uv_##name##_t* handle) { \ if (!uv__is_active(handle)) return 0; \ - QUEUE_REMOVE(&handle->queue); \ + uv__queue_remove(&handle->queue); \ uv__handle_stop(handle); \ return 0; \ } \ \ void uv__run_##name(uv_loop_t* loop) { \ uv_##name##_t* h; \ - QUEUE queue; \ - QUEUE* q; \ - QUEUE_MOVE(&loop->name##_handles, &queue); \ - while (!QUEUE_EMPTY(&queue)) { \ - q = QUEUE_HEAD(&queue); \ - h = QUEUE_DATA(q, uv_##name##_t, queue); \ - QUEUE_REMOVE(q); \ - QUEUE_INSERT_TAIL(&loop->name##_handles, q); \ + struct uv__queue queue; \ + struct uv__queue* q; \ + uv__queue_move(&loop->name##_handles, &queue); \ + while (!uv__queue_empty(&queue)) { \ + q = uv__queue_head(&queue); \ + h = uv__queue_data(q, uv_##name##_t, queue); \ + uv__queue_remove(q); \ + uv__queue_insert_tail(&loop->name##_handles, q); \ h->name##_cb(h); \ } \ } \
diff --git a/Utilities/cmlibuv/src/unix/loop.c b/Utilities/cmlibuv/src/unix/loop.c index 8939926..da9fa3c 100644 --- a/Utilities/cmlibuv/src/unix/loop.c +++ b/Utilities/cmlibuv/src/unix/loop.c
@@ -32,12 +32,11 @@ void* saved_data; int err; - saved_data = loop->data; memset(loop, 0, sizeof(*loop)); loop->data = saved_data; - lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields)); + lfields = uv__calloc(1, sizeof(*lfields)); if (lfields == NULL) return UV_ENOMEM; loop->internal_fields = lfields; @@ -45,22 +44,25 @@ err = uv_mutex_init(&lfields->loop_metrics.lock); if (err) goto fail_metrics_mutex_init; + memset(&lfields->loop_metrics.metrics, + 0, + sizeof(lfields->loop_metrics.metrics)); heap_init((struct heap*) &loop->timer_heap); - QUEUE_INIT(&loop->wq); - QUEUE_INIT(&loop->idle_handles); - QUEUE_INIT(&loop->async_handles); - QUEUE_INIT(&loop->check_handles); - QUEUE_INIT(&loop->prepare_handles); - QUEUE_INIT(&loop->handle_queue); + uv__queue_init(&loop->wq); + uv__queue_init(&loop->idle_handles); + uv__queue_init(&loop->async_handles); + uv__queue_init(&loop->check_handles); + uv__queue_init(&loop->prepare_handles); + uv__queue_init(&loop->handle_queue); loop->active_handles = 0; loop->active_reqs.count = 0; loop->nfds = 0; loop->watchers = NULL; loop->nwatchers = 0; - QUEUE_INIT(&loop->pending_queue); - QUEUE_INIT(&loop->watcher_queue); + uv__queue_init(&loop->pending_queue); + uv__queue_init(&loop->watcher_queue); loop->closing_handles = NULL; uv__update_time(loop); @@ -81,8 +83,8 @@ uv__signal_global_once_init(); err = uv__process_init(loop); if (err) - goto fail_signal_init; - QUEUE_INIT(&loop->process_handles); + goto fail_process_init; + uv__queue_init(&loop->process_handles); err = uv_rwlock_init(&loop->cloexec_lock); if (err) @@ -108,11 +110,15 @@ uv_rwlock_destroy(&loop->cloexec_lock); fail_rwlock_init: +fail_process_init: uv__signal_loop_cleanup(loop); - -fail_signal_init: uv__platform_loop_delete(loop); + if (loop->backend_fd != -1) { + uv__close(loop->backend_fd); + loop->backend_fd = -1; + } + fail_platform_init: uv_mutex_destroy(&lfields->loop_metrics.lock); @@ -149,9 +155,9 @@ if (w == NULL) continue; - if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) { + if (w->pevents != 0 && uv__queue_empty(&w->watcher_queue)) { w->events = 0; /* Force re-registration in uv__io_poll. */ - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } } @@ -177,7 +183,7 @@ } uv_mutex_lock(&loop->wq_mutex); - assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!"); + assert(uv__queue_empty(&loop->wq) && "thread pool work queue not empty!"); assert(!uv__has_active_reqs(loop)); uv_mutex_unlock(&loop->wq_mutex); uv_mutex_destroy(&loop->wq_mutex); @@ -189,8 +195,8 @@ uv_rwlock_destroy(&loop->cloexec_lock); #if 0 - assert(QUEUE_EMPTY(&loop->pending_queue)); - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->pending_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); assert(loop->nfds == 0); #endif @@ -214,6 +220,14 @@ return 0; } +#if defined(__linux__) + if (option == UV_LOOP_USE_IO_URING_SQPOLL) { + loop->flags |= UV_LOOP_ENABLE_IO_URING_SQPOLL; + return 0; + } +#endif + + if (option != UV_LOOP_BLOCK_SIGNAL) return UV_ENOSYS;
diff --git a/Utilities/cmlibuv/src/unix/netbsd.c b/Utilities/cmlibuv/src/unix/netbsd.c index c66333f..fa21e98 100644 --- a/Utilities/cmlibuv/src/unix/netbsd.c +++ b/Utilities/cmlibuv/src/unix/netbsd.c
@@ -103,7 +103,7 @@ int which[] = {CTL_VM, VM_UVMEXP}; if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } @@ -120,7 +120,7 @@ size_t size = sizeof(info); if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info; } @@ -131,6 +131,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { kvm_t *kd = NULL; struct kinfo_proc2 *kinfo = NULL;
diff --git a/Utilities/cmlibuv/src/unix/openbsd.c b/Utilities/cmlibuv/src/unix/openbsd.c index f32a94d..a6d58b9 100644 --- a/Utilities/cmlibuv/src/unix/openbsd.c +++ b/Utilities/cmlibuv/src/unix/openbsd.c
@@ -59,54 +59,13 @@ int uv_exepath(char* buffer, size_t* size) { - int mib[4]; - char **argsbuf = NULL; - size_t argsbuf_size = 100U; - size_t exepath_size; - pid_t mypid; - int err; - if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - mypid = getpid(); - for (;;) { - err = UV_ENOMEM; - argsbuf = uv__reallocf(argsbuf, argsbuf_size); - if (argsbuf == NULL) - goto out; - mib[0] = CTL_KERN; - mib[1] = KERN_PROC_ARGS; - mib[2] = mypid; - mib[3] = KERN_PROC_ARGV; - if (sysctl(mib, ARRAY_SIZE(mib), argsbuf, &argsbuf_size, NULL, 0) == 0) { - break; - } - if (errno != ENOMEM) { - err = UV__ERR(errno); - goto out; - } - argsbuf_size *= 2U; - } + if (uv_saved_argv0 == NULL) + return UV_EINVAL; - if (argsbuf[0] == NULL) { - err = UV_EINVAL; /* FIXME(bnoordhuis) More appropriate error. */ - goto out; - } - - *size -= 1; - exepath_size = strlen(argsbuf[0]); - if (*size > exepath_size) - *size = exepath_size; - - memcpy(buffer, argsbuf[0], *size); - buffer[*size] = '\0'; - err = 0; - -out: - uv__free(argsbuf); - - return err; + return uv__search_path(uv_saved_argv0, buffer, size); } @@ -116,7 +75,7 @@ int which[] = {CTL_VM, VM_UVMEXP}; if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } @@ -128,7 +87,7 @@ size_t size = sizeof(info); if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info; } @@ -139,6 +98,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { struct kinfo_proc kinfo; size_t page_size = getpagesize(); @@ -206,8 +170,16 @@ which[1] = HW_CPUSPEED; size = sizeof(cpuspeed); - if (sysctl(which, ARRAY_SIZE(which), &cpuspeed, &size, NULL, 0)) + cpuspeed = 0; + /* + * HW_CPUSPEED can return EOPNOTSUPP if cpuspeed is 0, + * so ignore that and continue the flow, because we + * still care about the rest of the CPU info. + */ + if (sysctl(which, ARRAY_SIZE(which), &cpuspeed, &size, NULL, 0) && + (errno != EOPNOTSUPP)) { goto error; + } size = sizeof(info); for (i = 0; i < numcpus; i++) {
diff --git a/Utilities/cmlibuv/src/unix/os390-syscalls.c b/Utilities/cmlibuv/src/unix/os390-syscalls.c index 5861aaa..7f90c27 100644 --- a/Utilities/cmlibuv/src/unix/os390-syscalls.c +++ b/Utilities/cmlibuv/src/unix/os390-syscalls.c
@@ -27,7 +27,7 @@ #include <termios.h> #include <sys/msg.h> -static QUEUE global_epoll_queue; +static struct uv__queue global_epoll_queue; static uv_mutex_t global_epoll_lock; static uv_once_t once = UV_ONCE_INIT; @@ -178,18 +178,18 @@ static void child_fork(void) { - QUEUE* q; + struct uv__queue* q; uv_once_t child_once = UV_ONCE_INIT; /* reset once */ memcpy(&once, &child_once, sizeof(child_once)); /* reset epoll list */ - while (!QUEUE_EMPTY(&global_epoll_queue)) { + while (!uv__queue_empty(&global_epoll_queue)) { uv__os390_epoll* lst; - q = QUEUE_HEAD(&global_epoll_queue); - QUEUE_REMOVE(q); - lst = QUEUE_DATA(q, uv__os390_epoll, member); + q = uv__queue_head(&global_epoll_queue); + uv__queue_remove(q); + lst = uv__queue_data(q, uv__os390_epoll, member); uv__free(lst->items); lst->items = NULL; lst->size = 0; @@ -201,7 +201,7 @@ static void epoll_init(void) { - QUEUE_INIT(&global_epoll_queue); + uv__queue_init(&global_epoll_queue); if (uv_mutex_init(&global_epoll_lock)) abort(); @@ -225,7 +225,7 @@ lst->items[lst->size - 1].revents = 0; uv_once(&once, epoll_init); uv_mutex_lock(&global_epoll_lock); - QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member); + uv__queue_insert_tail(&global_epoll_queue, &lst->member); uv_mutex_unlock(&global_epoll_lock); } @@ -352,14 +352,14 @@ int epoll_file_close(int fd) { - QUEUE* q; + struct uv__queue* q; uv_once(&once, epoll_init); uv_mutex_lock(&global_epoll_lock); - QUEUE_FOREACH(q, &global_epoll_queue) { + uv__queue_foreach(q, &global_epoll_queue) { uv__os390_epoll* lst; - lst = QUEUE_DATA(q, uv__os390_epoll, member); + lst = uv__queue_data(q, uv__os390_epoll, member); if (fd < lst->size && lst->items != NULL && lst->items[fd].fd != -1) lst->items[fd].fd = -1; } @@ -371,7 +371,7 @@ void epoll_queue_close(uv__os390_epoll* lst) { /* Remove epoll instance from global queue */ uv_mutex_lock(&global_epoll_lock); - QUEUE_REMOVE(&lst->member); + uv__queue_remove(&lst->member); uv_mutex_unlock(&global_epoll_lock); /* Free resources */
diff --git a/Utilities/cmlibuv/src/unix/os390-syscalls.h b/Utilities/cmlibuv/src/unix/os390-syscalls.h index 9f50417..d5f3bcf 100644 --- a/Utilities/cmlibuv/src/unix/os390-syscalls.h +++ b/Utilities/cmlibuv/src/unix/os390-syscalls.h
@@ -45,7 +45,7 @@ }; typedef struct { - QUEUE member; + struct uv__queue member; struct pollfd* items; unsigned long size; int msg_queue;
diff --git a/Utilities/cmlibuv/src/unix/os390.c b/Utilities/cmlibuv/src/unix/os390.c index 3b16318..c049884 100644 --- a/Utilities/cmlibuv/src/unix/os390.c +++ b/Utilities/cmlibuv/src/unix/os390.c
@@ -19,6 +19,7 @@ * IN THE SOFTWARE. */ +#include "uv.h" #include "internal.h" #include <sys/ioctl.h> #include <net/if.h> @@ -30,6 +31,7 @@ #include <sys/msg.h> #include <sys/resource.h> #include "zos-base.h" +#include "zos-sys-info.h" #if defined(__clang__) #include "csrsic.h" #else @@ -66,9 +68,6 @@ /* Total number of frames currently on all available frame queues. */ #define RCEAFC_OFFSET 0x088 -/* CPC model length from the CSRSI Service. */ -#define CPCMODEL_LENGTH 16 - /* Pointer to the home (current) ASCB. */ #define PSAAOLD 0x224 @@ -198,6 +197,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { char* ascb; char* rax; @@ -253,9 +257,12 @@ idx = 0; while (idx < *count) { cpu_info->speed = *(int*)(info.siv1v2si22v1.si22v1cpucapability); - cpu_info->model = uv__malloc(CPCMODEL_LENGTH + 1); - memset(cpu_info->model, '\0', CPCMODEL_LENGTH + 1); - memcpy(cpu_info->model, info.siv1v2si11v1.si11v1cpcmodel, CPCMODEL_LENGTH); + cpu_info->model = uv__malloc(ZOSCPU_MODEL_LENGTH + 1); + if (cpu_info->model == NULL) { + uv_free_cpu_info(*cpu_infos, idx); + return UV_ENOMEM; + } + __get_cpu_model(cpu_info->model, ZOSCPU_MODEL_LENGTH + 1); cpu_info->cpu_times.user = cpu_usage_avg; /* TODO: implement the following */ cpu_info->cpu_times.sys = 0; @@ -803,13 +810,14 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { static const int max_safe_timeout = 1789569; + uv__loop_internal_fields_t* lfields; struct epoll_event events[1024]; struct epoll_event* pe; struct epoll_event e; uv__os390_epoll* ep; int have_signals; int real_timeout; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; uint64_t base; int count; @@ -821,17 +829,19 @@ int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } - while (!QUEUE_EMPTY(&loop->watcher_queue)) { + lfields = uv__get_internal_fields(loop); + + while (!uv__queue_empty(&loop->watcher_queue)) { uv_stream_t* stream; - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); @@ -872,7 +882,7 @@ int nevents = 0; have_signals = 0; - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -882,24 +892,15 @@ nfds = 0; for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) timeout = max_safe_timeout; + uv__io_poll_prepare(loop, NULL, timeout); nfds = epoll_wait(loop->ep, events, ARRAY_SIZE(events), timeout); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ base = loop->time; - SAVE_ERRNO(uv__update_time(loop)); + uv__io_poll_check(loop, NULL); + if (nfds == 0) { assert(timeout != -1); @@ -992,20 +993,22 @@ have_signals = 1; } else { uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->events); + uv__io_cb(loop, w, pe->events); } nevents++; } } + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + uv__signal_event(loop, &loop->signal_io_watcher, POLLIN); } loop->watchers[loop->nwatchers] = NULL;
diff --git a/Utilities/cmlibuv/src/unix/pipe.c b/Utilities/cmlibuv/src/unix/pipe.c index e9b88c1..e5660e7 100644 --- a/Utilities/cmlibuv/src/unix/pipe.c +++ b/Utilities/cmlibuv/src/unix/pipe.c
@@ -30,6 +30,21 @@ #include <stdlib.h> +/* Does the file path contain embedded nul bytes? */ +static int includes_invalid_nul(const char *s, size_t n) { + if (n == 0) + return 0; +#ifdef __linux__ + /* Accept abstract socket namespace paths, throughout which nul bytes have + * no special significance ("\0foo\0bar"). + */ + if (s[0] == '\0') + return 0; +#endif + return NULL != memchr(s, '\0', n); +} + + int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); handle->shutdown_req = NULL; @@ -41,24 +56,66 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + return uv_pipe_bind2(handle, name, strlen(name), 0); +} + + +int uv_pipe_bind2(uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags) { struct sockaddr_un saddr; - const char* pipe_fname = NULL; + char* pipe_fname = NULL; int sockfd = -1; int err; + socklen_t addrlen; + + if (flags & ~UV_PIPE_NO_TRUNCATE) + return UV_EINVAL; + + if (name == NULL) + return UV_EINVAL; + + /* namelen==0 on Linux means autobind the listen socket in the abstract + * socket namespace, see `man 7 unix` for details. + */ +#if !defined(__linux__) + if (namelen == 0) + return UV_EINVAL; +#endif + + if (includes_invalid_nul(name, namelen)) + return UV_EINVAL; + + if (flags & UV_PIPE_NO_TRUNCATE) + if (namelen > sizeof(saddr.sun_path)) + return UV_EINVAL; + + /* Truncate long paths. Documented behavior. */ + if (namelen > sizeof(saddr.sun_path)) + namelen = sizeof(saddr.sun_path); /* Already bound? */ if (uv__stream_fd(handle) >= 0) return UV_EINVAL; - if (uv__is_closing(handle)) { - return UV_EINVAL; - } - /* Make a copy of the file name, it outlives this function's scope. */ - pipe_fname = uv__strdup(name); - if (pipe_fname == NULL) - return UV_ENOMEM; - /* We've got a copy, don't touch the original any more. */ - name = NULL; + if (uv__is_closing(handle)) + return UV_EINVAL; + + /* Make a copy of the file path unless it is an abstract socket. + * We unlink the file later but abstract sockets disappear + * automatically since they're not real file system entities. + */ + if (*name == '\0') { + addrlen = offsetof(struct sockaddr_un, sun_path) + namelen; + } else { + pipe_fname = uv__malloc(namelen + 1); + if (pipe_fname == NULL) + return UV_ENOMEM; + memcpy(pipe_fname, name, namelen); + pipe_fname[namelen] = '\0'; + addrlen = sizeof saddr; + } err = uv__socket(AF_UNIX, SOCK_STREAM, 0); if (err < 0) @@ -66,10 +123,10 @@ sockfd = err; memset(&saddr, 0, sizeof saddr); - uv__strscpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); + memcpy(&saddr.sun_path, name, namelen); saddr.sun_family = AF_UNIX; - if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) { + if (bind(sockfd, (struct sockaddr*)&saddr, addrlen)) { err = UV__ERR(errno); /* Convert ENOENT to EACCES for compatibility with Windows. */ if (err == UV_ENOENT) @@ -81,12 +138,12 @@ /* Success. */ handle->flags |= UV_HANDLE_BOUND; - handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */ + handle->pipe_fname = pipe_fname; /* NULL or a copy of |name| */ handle->io_watcher.fd = sockfd; return 0; err_socket: - uv__free((void*)pipe_fname); + uv__free(pipe_fname); return err; } @@ -111,9 +168,8 @@ return UV__ERR(errno); handle->connection_cb = cb; - handle->io_watcher.cb = uv__server_io; - uv__io_start(handle->loop, &handle->io_watcher, POLLIN); - return 0; + uv__io_cb_set(&handle->io_watcher, UV__SERVER_IO); + return uv__io_start(handle->loop, &handle->io_watcher, POLLIN); } @@ -174,10 +230,56 @@ uv_pipe_t* handle, const char* name, uv_connect_cb cb) { + int err; + + err = uv_pipe_connect2(req, handle, name, strlen(name), 0, cb); + + if (err) { + handle->delayed_error = err; + handle->connect_req = req; + + uv__req_init(handle->loop, req, UV_CONNECT); + req->handle = (uv_stream_t*) handle; + req->cb = cb; + uv__queue_init(&req->queue); + + /* Force callback to run on next tick in case of error. */ + uv__io_feed(handle->loop, &handle->io_watcher); + } +} + + +int uv_pipe_connect2(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags, + uv_connect_cb cb) { struct sockaddr_un saddr; int new_sock; int err; int r; + socklen_t addrlen; + + if (flags & ~UV_PIPE_NO_TRUNCATE) + return UV_EINVAL; + + if (name == NULL) + return UV_EINVAL; + + if (namelen == 0) + return UV_EINVAL; + + if (includes_invalid_nul(name, namelen)) + return UV_EINVAL; + + if (flags & UV_PIPE_NO_TRUNCATE) + if (namelen > sizeof(saddr.sun_path)) + return UV_EINVAL; + + /* Truncate long paths. Documented behavior. */ + if (namelen > sizeof(saddr.sun_path)) + namelen = sizeof(saddr.sun_path); new_sock = (uv__stream_fd(handle) == -1); @@ -189,12 +291,16 @@ } memset(&saddr, 0, sizeof saddr); - uv__strscpy(saddr.sun_path, name, sizeof(saddr.sun_path)); + memcpy(&saddr.sun_path, name, namelen); saddr.sun_family = AF_UNIX; + if (*name == '\0') + addrlen = offsetof(struct sockaddr_un, sun_path) + namelen; + else + addrlen = sizeof saddr; + do { - r = connect(uv__stream_fd(handle), - (struct sockaddr*)&saddr, sizeof saddr); + r = connect(uv__stream_fd(handle), (struct sockaddr*)&saddr, addrlen); } while (r == -1 && errno == EINTR); @@ -226,14 +332,15 @@ handle->connect_req = req; uv__req_init(handle->loop, req, UV_CONNECT); - req->handle = (uv_stream_t*)handle; + req->handle = (uv_stream_t*) handle; req->cb = cb; - QUEUE_INIT(&req->queue); + uv__queue_init(&req->queue); /* Force callback to run on next tick in case of error. */ if (err) uv__io_feed(handle->loop, &handle->io_watcher); + return 0; } @@ -241,10 +348,20 @@ uv__peersockfunc func, char* buffer, size_t* size) { +#if defined(__linux__) + static const int is_linux = 1; +#else + static const int is_linux = 0; +#endif struct sockaddr_un sa; socklen_t addrlen; + size_t slop; + char* p; int err; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + addrlen = sizeof(sa); memset(&sa, 0, addrlen); err = uv__getsockpeername((const uv_handle_t*) handle, @@ -256,17 +373,20 @@ return err; } -#if defined(__linux__) - if (sa.sun_path[0] == 0) - /* Linux abstract namespace */ + slop = 1; + if (is_linux && sa.sun_path[0] == '\0') { + /* Linux abstract namespace. Not zero-terminated. */ + slop = 0; addrlen -= offsetof(struct sockaddr_un, sun_path); - else -#endif - addrlen = strlen(sa.sun_path); + } else { + p = memchr(sa.sun_path, '\0', sizeof(sa.sun_path)); + if (p == NULL) + p = ARRAY_END(sa.sun_path); + addrlen = p - sa.sun_path; + } - - if ((size_t)addrlen >= *size) { - *size = addrlen + 1; + if ((size_t)addrlen + slop > *size) { + *size = addrlen + slop; return UV_ENOBUFS; } @@ -324,13 +444,18 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { - unsigned desired_mode; - struct stat pipe_stat; - char* name_buffer; + char name_buffer[1 + UV__PATH_MAX]; + int desired_mode; size_t name_len; + const char* name; + int fd; int r; - if (handle == NULL || uv__stream_fd(handle) == -1) + if (handle == NULL) + return UV_EBADF; + + fd = uv__stream_fd(handle); + if (fd == -1) return UV_EBADF; if (mode != UV_READABLE && @@ -338,53 +463,45 @@ mode != (UV_WRITABLE | UV_READABLE)) return UV_EINVAL; - /* Unfortunately fchmod does not work on all platforms, we will use chmod. */ - name_len = 0; - r = uv_pipe_getsockname(handle, NULL, &name_len); - if (r != UV_ENOBUFS) - return r; - - name_buffer = uv__malloc(name_len); - if (name_buffer == NULL) - return UV_ENOMEM; - - r = uv_pipe_getsockname(handle, name_buffer, &name_len); - if (r != 0) { - uv__free(name_buffer); - return r; - } - - /* stat must be used as fstat has a bug on Darwin */ - if (stat(name_buffer, &pipe_stat) == -1) { - uv__free(name_buffer); - return -errno; - } - desired_mode = 0; if (mode & UV_READABLE) desired_mode |= S_IRUSR | S_IRGRP | S_IROTH; if (mode & UV_WRITABLE) desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - /* Exit early if pipe already has desired mode. */ - if ((pipe_stat.st_mode & desired_mode) == desired_mode) { - uv__free(name_buffer); - return 0; - } + /* fchmod on macOS and (Free|Net|Open)BSD does not support UNIX sockets. */ + if (fchmod(fd, desired_mode)) + if (errno != EINVAL && errno != EOPNOTSUPP) + return UV__ERR(errno); - pipe_stat.st_mode |= desired_mode; + /* Fall back to chmod. */ + name_len = sizeof(name_buffer); + r = uv_pipe_getsockname(handle, name_buffer, &name_len); + if (r != 0) + return r; + name = name_buffer; - r = chmod(name_buffer, pipe_stat.st_mode); - uv__free(name_buffer); + /* On some platforms, getsockname returns an empty string, and we try with pipe_fname. */ + if (name_len == 0 && handle->pipe_fname != NULL) + name = handle->pipe_fname; - return r != -1 ? 0 : UV__ERR(errno); + if (chmod(name, desired_mode)) + return UV__ERR(errno); + + return 0; } int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) { uv_os_fd_t temp[2]; int err; -#if defined(__FreeBSD__) || defined(__linux__) +#if defined(__linux__) || \ + defined(__FreeBSD__) || \ + defined(__OpenBSD__) || \ + defined(__DragonFly__) || \ + defined(__NetBSD__) || \ + defined(__illumos__) || \ + (defined(UV__SOLARIS_11_4) && UV__SOLARIS_11_4) int flags = O_CLOEXEC; if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE))
diff --git a/Utilities/cmlibuv/src/unix/poll.c b/Utilities/cmlibuv/src/unix/poll.c index 7a12e2d..60c3c20 100644 --- a/Utilities/cmlibuv/src/unix/poll.c +++ b/Utilities/cmlibuv/src/unix/poll.c
@@ -24,10 +24,9 @@ #include <unistd.h> #include <assert.h> -#include <errno.h> -static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv_poll_t* handle; int pevents; @@ -88,7 +87,7 @@ return err; uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL); - uv__io_init(&handle->io_watcher, uv__poll_io, fd); + uv__io_init(&handle->io_watcher, UV__POLL_IO, fd); handle->poll_cb = NULL; return 0; }
diff --git a/Utilities/cmlibuv/src/unix/posix-hrtime.c b/Utilities/cmlibuv/src/unix/posix-hrtime.c index 870b45c..6f6faa3 100644 --- a/Utilities/cmlibuv/src/unix/posix-hrtime.c +++ b/Utilities/cmlibuv/src/unix/posix-hrtime.c
@@ -60,15 +60,16 @@ #else #include <stdint.h> +#include <stdlib.h> #include <time.h> -#undef NANOSEC -#define NANOSEC ((uint64_t) 1e9) - uint64_t uv__hrtime(uv_clocktype_t type) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); + struct timespec t; + + if (clock_gettime(CLOCK_MONOTONIC, &t)) + abort(); + + return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; } #endif
diff --git a/Utilities/cmlibuv/src/unix/posix-poll.c b/Utilities/cmlibuv/src/unix/posix-poll.c index 0f4bf93..5b7d75c 100644 --- a/Utilities/cmlibuv/src/unix/posix-poll.c +++ b/Utilities/cmlibuv/src/unix/posix-poll.c
@@ -132,11 +132,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { + uv__loop_internal_fields_t* lfields; sigset_t* pset; sigset_t set; uint64_t time_base; uint64_t time_diff; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; size_t i; unsigned int nevents; @@ -148,17 +149,19 @@ int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } - /* Take queued watchers and add their fds to our poll fds array. */ - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + lfields = uv__get_internal_fields(loop); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + /* Take queued watchers and add their fds to our poll fds array. */ + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); + + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); @@ -179,7 +182,7 @@ assert(timeout >= -1); time_base = loop->time; - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -192,25 +195,9 @@ * our caller then we need to loop around and poll() again. */ for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - if (pset != NULL) - if (pthread_sigmask(SIG_BLOCK, pset, NULL)) - abort(); + uv__io_poll_prepare(loop, pset, timeout); nfds = poll(loop->poll_fds, (nfds_t)loop->poll_fds_used, timeout); - if (pset != NULL) - if (pthread_sigmask(SIG_UNBLOCK, pset, NULL)) - abort(); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); + uv__io_poll_check(loop, pset); if (nfds == 0) { if (reset_timeout != 0) { @@ -285,21 +272,23 @@ have_signals = 1; } else { uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->revents); + uv__io_cb(loop, w, pe->revents); } nevents++; } } + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + uv__signal_event(loop, &loop->signal_io_watcher, POLLIN); } loop->poll_fds_iterating = 0;
diff --git a/Utilities/cmlibuv/src/unix/process.c b/Utilities/cmlibuv/src/unix/process.c index 729a44b..32ce2a4 100644 --- a/Utilities/cmlibuv/src/unix/process.c +++ b/Utilities/cmlibuv/src/unix/process.c
@@ -60,7 +60,8 @@ extern char **environ; #endif -#if defined(__linux__) || defined(__GLIBC__) +#if defined(__linux__) || \ + defined(__GNU__) # include <grp.h> #endif @@ -87,6 +88,7 @@ #define UV_USE_SIGCHLD #endif + #ifdef UV_USE_SIGCHLD static void uv__chld(uv_signal_t* handle, int signum) { assert(signum == SIGCHLD); @@ -121,17 +123,17 @@ int status; int options; pid_t pid; - QUEUE pending; - QUEUE* q; - QUEUE* h; + struct uv__queue pending; + struct uv__queue* q; + struct uv__queue* h; - QUEUE_INIT(&pending); + uv__queue_init(&pending); h = &loop->process_handles; - q = QUEUE_HEAD(h); + q = uv__queue_head(h); while (q != h) { - process = QUEUE_DATA(q, uv_process_t, queue); - q = QUEUE_NEXT(q); + process = uv__queue_data(q, uv_process_t, queue); + q = uv__queue_next(q); #ifndef UV_USE_SIGCHLD if ((process->flags & UV_HANDLE_REAP) == 0) @@ -162,18 +164,18 @@ assert(pid == process->pid); process->status = status; - QUEUE_REMOVE(&process->queue); - QUEUE_INSERT_TAIL(&pending, &process->queue); + uv__queue_remove(&process->queue); + uv__queue_insert_tail(&pending, &process->queue); } h = &pending; - q = QUEUE_HEAD(h); + q = uv__queue_head(h); while (q != h) { - process = QUEUE_DATA(q, uv_process_t, queue); - q = QUEUE_NEXT(q); + process = uv__queue_data(q, uv_process_t, queue); + q = uv__queue_next(q); - QUEUE_REMOVE(&process->queue); - QUEUE_INIT(&process->queue); + uv__queue_remove(&process->queue); + uv__queue_init(&process->queue); uv__handle_stop(process); if (process->exit_cb == NULL) @@ -189,18 +191,27 @@ process->exit_cb(process, exit_status, term_signal); } - assert(QUEUE_EMPTY(&pending)); + assert(uv__queue_empty(&pending)); } /* * Used for initializing stdio streams like options.stdin_stream. Returns * zero on success. See also the cleanup section in uv_spawn(). */ +#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) +/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be + * avoided. Since this isn't called on those targets, the function + * doesn't even need to be defined for them. + */ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { int mask; int fd; + int ret; + int size; + int i; mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM; + size = 64 * 1024; switch (container->flags & mask) { case UV_IGNORE: @@ -210,8 +221,17 @@ assert(container->data.stream != NULL); if (container->data.stream->type != UV_NAMED_PIPE) return UV_EINVAL; - else - return uv_socketpair(SOCK_STREAM, 0, fds, 0, 0); + else { + ret = uv_socketpair(SOCK_STREAM, 0, fds, 0, 0); + + if (ret == 0) + for (i = 0; i < 2; i++) { + setsockopt(fds[i], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + setsockopt(fds[i], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); + } + } + + return ret; case UV_INHERIT_FD: case UV_INHERIT_STREAM: @@ -282,11 +302,6 @@ } -#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) -/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be - * avoided. Since this isn't called on those targets, the function - * doesn't even need to be defined for them. - */ static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], @@ -448,7 +463,6 @@ uv__write_errno(error_fd); } -#endif #if defined(UV_USE_APPLE_POSIX_SPAWN) @@ -729,7 +743,7 @@ if (options->file == NULL) return ENOENT; - /* The environment for the child process is that of the parent unless overriden + /* The environment for the child process is that of the parent unless overridden * by options->env */ char** env = environ; if (options->env != NULL) @@ -995,6 +1009,7 @@ return err; } +#endif /* ISN'T TARGET_OS_TV || TARGET_OS_WATCH */ int uv_spawn(uv_loop_t* loop, uv_process_t* process, @@ -1037,7 +1052,7 @@ UV_PROCESS_WINDOWS_USE_PARENT_ERROR_MODE))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); - QUEUE_INIT(&process->queue); + uv__queue_init(&process->queue); process->status = 0; stdio_count = options->stdio_count; @@ -1100,7 +1115,7 @@ process->pid = pid; process->exit_cb = options->exit_cb; - QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue); + uv__queue_insert_tail(&loop->process_handles, &process->queue); uv__handle_start(process); } @@ -1121,6 +1136,7 @@ return exec_errorno; error: + uv__queue_remove(&process->handle_queue); if (pipes != NULL) { for (i = 0; i < stdio_count; i++) { if (i < options->stdio_count) @@ -1162,10 +1178,10 @@ void uv__process_close(uv_process_t* handle) { - QUEUE_REMOVE(&handle->queue); + uv__queue_remove(&handle->queue); uv__handle_stop(handle); #ifdef UV_USE_SIGCHLD - if (QUEUE_EMPTY(&handle->loop->process_handles)) + if (uv__queue_empty(&handle->loop->process_handles)) uv_signal_stop(&handle->loop->child_watcher); #endif }
diff --git a/Utilities/cmlibuv/src/unix/pthread-fixes.c b/Utilities/cmlibuv/src/unix/pthread-fixes.c deleted file mode 100644 index 022d79c..0000000 --- a/Utilities/cmlibuv/src/unix/pthread-fixes.c +++ /dev/null
@@ -1,58 +0,0 @@ -/* Copyright (c) 2013, Sony Mobile Communications AB - * Copyright (c) 2012, Google Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/* Android versions < 4.1 have a broken pthread_sigmask. */ -#include "uv-common.h" - -#include <errno.h> -#include <pthread.h> -#include <signal.h> - -int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) { - static int workaround; - int err; - - if (uv__load_relaxed(&workaround)) { - return sigprocmask(how, set, oset); - } else { - err = pthread_sigmask(how, set, oset); - if (err) { - if (err == EINVAL && sigprocmask(how, set, oset) == 0) { - uv__store_relaxed(&workaround, 1); - return 0; - } else { - return -1; - } - } - } - - return 0; -}
diff --git a/Utilities/cmlibuv/src/unix/qnx.c b/Utilities/cmlibuv/src/unix/qnx.c index ca148d3..d873165 100644 --- a/Utilities/cmlibuv/src/unix/qnx.c +++ b/Utilities/cmlibuv/src/unix/qnx.c
@@ -28,6 +28,14 @@ #include <sys/memmsg.h> #include <sys/syspage.h> #include <sys/procfs.h> +#include <time.h> +#include <stdlib.h> +#include <inttypes.h> +#if __QNX__ >= 800 +#define cpuinfo_val cpuinfo +#else +#define cpuinfo_val new_cpuinfo +#endif static void get_mem_info(uint64_t* totalmem, uint64_t* freemem) { @@ -67,18 +75,44 @@ } +static uint64_t uv__read_pidin_info(const char* what) { + uint64_t rc; + char* p; + char buf[2048]; + + FILE* fp = popen("pidin info", "r"); + if (fp == NULL) + return 0; + + size_t sz = fread(buf, 1, sizeof(buf) - 1, fp); + buf[sz] = '\0'; + + pclose(fp); + + p = strstr(buf, what); + if (p == NULL) + return 0; + + p += strlen(what); + + rc = 0; + sscanf(p, "%" PRIu64 " MB", &rc); + + return rc * 1024 * 1024; +} + uint64_t uv_get_free_memory(void) { - uint64_t totalmem; uint64_t freemem; - get_mem_info(&totalmem, &freemem); + + freemem = uv__read_pidin_info("FreeMem:"); return freemem; } uint64_t uv_get_total_memory(void) { uint64_t totalmem; - uint64_t freemem; - get_mem_info(&totalmem, &freemem); + + totalmem = uv__read_pidin_info("MB/"); return totalmem; } @@ -88,6 +122,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { int fd; procfs_asinfo asinfo; @@ -108,15 +147,17 @@ int uv_uptime(double* uptime) { - struct qtime_entry* qtime = _SYSPAGE_ENTRY(_syspage_ptr, qtime); - *uptime = (qtime->nsec / 1000000000.0); + struct timespec ts; + if(clock_gettime(CLOCK_MONOTONIC, &ts)) + return UV__ERR(errno); + *uptime = (double)ts.tv_sec; return 0; } int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { struct cpuinfo_entry* cpuinfo = - (struct cpuinfo_entry*)_SYSPAGE_ENTRY(_syspage_ptr, new_cpuinfo); + (struct cpuinfo_entry*)_SYSPAGE_ENTRY(_syspage_ptr, cpuinfo_val); size_t cpuinfo_size = _SYSPAGE_ELEMENT_SIZE(_syspage_ptr, cpuinfo); struct strings_entry* strings = _SYSPAGE_ENTRY(_syspage_ptr, strings); int num_cpus = _syspage_ptr->num_cpu;
diff --git a/Utilities/cmlibuv/src/unix/random-devurandom.c b/Utilities/cmlibuv/src/unix/random-devurandom.c index 05e52a5..d6336f2 100644 --- a/Utilities/cmlibuv/src/unix/random-devurandom.c +++ b/Utilities/cmlibuv/src/unix/random-devurandom.c
@@ -40,7 +40,7 @@ if (fd < 0) return fd; - if (fstat(fd, &s)) { + if (uv__fstat(fd, &s)) { uv__close(fd); return UV__ERR(errno); }
diff --git a/Utilities/cmlibuv/src/unix/random-getrandom.c b/Utilities/cmlibuv/src/unix/random-getrandom.c index bcc9408..054eccf 100644 --- a/Utilities/cmlibuv/src/unix/random-getrandom.c +++ b/Utilities/cmlibuv/src/unix/random-getrandom.c
@@ -24,8 +24,6 @@ #ifdef __linux__ -#include "linux-syscalls.h" - #define uv__random_getrandom_init() 0 #else /* !__linux__ */
diff --git a/Utilities/cmlibuv/src/unix/signal.c b/Utilities/cmlibuv/src/unix/signal.c index bb70523..91fb149 100644 --- a/Utilities/cmlibuv/src/unix/signal.c +++ b/Utilities/cmlibuv/src/unix/signal.c
@@ -45,7 +45,6 @@ uv_signal_cb signal_cb, int signum, int oneshot); -static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events); static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2); static void uv__signal_stop(uv_signal_t* handle); static void uv__signal_unregister_handler(int signum); @@ -195,7 +194,7 @@ for (handle = uv__signal_first_handle(signum); handle != NULL && handle->signum == signum; - handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) { + handle = RB_NEXT(uv__signal_tree_s, handle)) { int r; msg.signum = signum; @@ -259,26 +258,34 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) { + int* pipefd; int err; /* Return if already initialized. */ - if (loop->signal_pipefd[0] != -1) + pipefd = loop->signal_pipefd; + if (pipefd[0] != -1) return 0; - err = uv__make_pipe(loop->signal_pipefd, UV_NONBLOCK_PIPE); + err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); if (err) return err; - uv__io_init(&loop->signal_io_watcher, - uv__signal_event, - loop->signal_pipefd[0]); - uv__io_start(loop, &loop->signal_io_watcher, POLLIN); + err = uv__io_init_start(loop, &loop->signal_io_watcher, UV__SIGNAL_EVENT, + pipefd[0], POLLIN); + if (err) { + uv__close(pipefd[0]); + uv__close(pipefd[1]); + pipefd[0] = -1; + pipefd[1] = -1; + } - return 0; + return err; } int uv__signal_loop_fork(uv_loop_t* loop) { + struct uv__queue* q; + if (loop->signal_pipefd[0] == -1) return 0; uv__io_stop(loop, &loop->signal_io_watcher, POLLIN); @@ -286,21 +293,34 @@ uv__close(loop->signal_pipefd[1]); loop->signal_pipefd[0] = -1; loop->signal_pipefd[1] = -1; + + uv__queue_foreach(q, &loop->handle_queue) { + uv_handle_t* handle = uv__queue_data(q, uv_handle_t, handle_queue); + uv_signal_t* sh; + + if (handle->type != UV_SIGNAL) + continue; + + sh = (uv_signal_t*) handle; + sh->caught_signals = 0; + sh->dispatched_signals = 0; + } + return uv__signal_loop_once_init(loop); } void uv__signal_loop_cleanup(uv_loop_t* loop) { - QUEUE* q; + struct uv__queue* q; /* Stop all the signal watchers that are still attached to this loop. This * ensures that the (shared) signal tree doesn't contain any invalid entries * entries, and that signal handlers are removed when appropriate. - * It's safe to use QUEUE_FOREACH here because the handles and the handle + * It's safe to use uv__queue_foreach here because the handles and the handle * queue are not modified by uv__signal_stop(). */ - QUEUE_FOREACH(q, &loop->handle_queue) { - uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue); + uv__queue_foreach(q, &loop->handle_queue) { + uv_handle_t* handle = uv__queue_data(q, uv_handle_t, handle_queue); if (handle->type == UV_SIGNAL) uv__signal_stop((uv_signal_t*) handle); @@ -415,9 +435,7 @@ } -static void uv__signal_event(uv_loop_t* loop, - uv__io_t* w, - unsigned int events) { +void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv__signal_msg_t* msg; uv_signal_t* handle; char buf[sizeof(uv__signal_msg_t) * 32];
diff --git a/Utilities/cmlibuv/src/unix/spinlock.h b/Utilities/cmlibuv/src/unix/spinlock.h deleted file mode 100644 index a20c83c..0000000 --- a/Utilities/cmlibuv/src/unix/spinlock.h +++ /dev/null
@@ -1,53 +0,0 @@ -/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UV_SPINLOCK_H_ -#define UV_SPINLOCK_H_ - -#include "internal.h" /* ACCESS_ONCE, UV_UNUSED */ -#include "atomic-ops.h" - -#define UV_SPINLOCK_INITIALIZER { 0 } - -typedef struct { - int lock; -} uv_spinlock_t; - -UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)); -UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)); -UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)); -UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)); - -UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) { - ACCESS_ONCE(int, spinlock->lock) = 0; -} - -UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) { - while (!uv_spinlock_trylock(spinlock)) cpu_relax(); -} - -UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) { - ACCESS_ONCE(int, spinlock->lock) = 0; -} - -UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) { - /* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing. - * Not really critical until we have locks that are (frequently) contended - * for by several threads. - */ - return 0 == cmpxchgi(&spinlock->lock, 0, 1); -} - -#endif /* UV_SPINLOCK_H_ */
diff --git a/Utilities/cmlibuv/src/unix/stream.c b/Utilities/cmlibuv/src/unix/stream.c index 1ce7525..7f710c1 100644 --- a/Utilities/cmlibuv/src/unix/stream.c +++ b/Utilities/cmlibuv/src/unix/stream.c
@@ -60,10 +60,19 @@ }; #endif /* defined(__APPLE__) */ +union uv__cmsg { + struct cmsghdr hdr; + /* This cannot be larger because of the IBMi PASE limitation that + * the total size of control messages cannot exceed 256 bytes. + */ + char pad[256]; +}; + +STATIC_ASSERT(256 == sizeof(union uv__cmsg)); + static void uv__stream_connect(uv_stream_t*); static void uv__write(uv_stream_t* stream); static void uv__read(uv_stream_t* stream); -static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); static void uv__write_callbacks(uv_stream_t* stream); static size_t uv__write_req_size(uv_write_t* req); static void uv__drain(uv_stream_t* stream); @@ -84,8 +93,8 @@ stream->accepted_fd = -1; stream->queued_fds = NULL; stream->delayed_error = 0; - QUEUE_INIT(&stream->write_queue); - QUEUE_INIT(&stream->write_completed_queue); + uv__queue_init(&stream->write_queue); + uv__queue_init(&stream->write_completed_queue); stream->write_queue_size = 0; if (loop->emfile_fd == -1) { @@ -103,7 +112,7 @@ stream->select = NULL; #endif /* defined(__APPLE_) */ - uv__io_init(&stream->io_watcher, uv__stream_io, -1); + uv__io_init(&stream->io_watcher, UV__STREAM_IO, -1); } @@ -407,7 +416,7 @@ /* TODO Use delay the user passed in. */ if ((stream->flags & UV_HANDLE_TCP_KEEPALIVE) && - uv__tcp_keepalive(fd, 1, 60)) { + uv__tcp_keepalive(fd, 1, 60, 1, 10)) { return UV__ERR(errno); } } @@ -429,15 +438,15 @@ void uv__stream_flush_write_queue(uv_stream_t* stream, int error) { uv_write_t* req; - QUEUE* q; - while (!QUEUE_EMPTY(&stream->write_queue)) { - q = QUEUE_HEAD(&stream->write_queue); - QUEUE_REMOVE(q); + struct uv__queue* q; + while (!uv__queue_empty(&stream->write_queue)) { + q = uv__queue_head(&stream->write_queue); + uv__queue_remove(q); - req = QUEUE_DATA(q, uv_write_t, queue); + req = uv__queue_data(q, uv_write_t, queue); req->error = error; - QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); + uv__queue_insert_tail(&stream->write_completed_queue, &req->queue); } } @@ -447,7 +456,7 @@ assert(stream->flags & UV_HANDLE_CLOSED); if (stream->connect_req) { - uv__req_unregister(stream->loop, stream->connect_req); + uv__req_unregister(stream->loop); stream->connect_req->cb(stream->connect_req, UV_ECANCELED); stream->connect_req = NULL; } @@ -495,76 +504,34 @@ } -#if defined(UV_HAVE_KQUEUE) -# define UV_DEC_BACKLOG(w) w->rcount--; -#else -# define UV_DEC_BACKLOG(w) /* no-op */ -#endif /* defined(UV_HAVE_KQUEUE) */ - - void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv_stream_t* stream; int err; + int fd; stream = container_of(w, uv_stream_t, io_watcher); assert(events & POLLIN); assert(stream->accepted_fd == -1); assert(!(stream->flags & UV_HANDLE_CLOSING)); - uv__io_start(stream->loop, &stream->io_watcher, POLLIN); + fd = uv__stream_fd(stream); + err = uv__accept(fd); - /* connection_cb can close the server socket while we're - * in the loop so check it on each iteration. - */ - while (uv__stream_fd(stream) != -1) { - assert(stream->accepted_fd == -1); + if (err == UV_EMFILE || err == UV_ENFILE) + err = uv__emfile_trick(loop, fd); /* Shed load. */ -#if defined(UV_HAVE_KQUEUE) - if (w->rcount <= 0) - return; -#endif /* defined(UV_HAVE_KQUEUE) */ + if (err < 0) + return; - err = uv__accept(uv__stream_fd(stream)); - if (err < 0) { - if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK)) - return; /* Not an error. */ + stream->accepted_fd = err; + stream->connection_cb(stream, 0); - if (err == UV_ECONNABORTED) - continue; /* Ignore. Nothing we can do about that. */ - - if (err == UV_EMFILE || err == UV_ENFILE) { - err = uv__emfile_trick(loop, uv__stream_fd(stream)); - if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK)) - break; - } - - stream->connection_cb(stream, err); - continue; - } - - UV_DEC_BACKLOG(w) - stream->accepted_fd = err; - stream->connection_cb(stream, 0); - - if (stream->accepted_fd != -1) { - /* The user hasn't yet accepted called uv_accept() */ - uv__io_stop(loop, &stream->io_watcher, POLLIN); - return; - } - - if (stream->type == UV_TCP && - (stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) { - /* Give other processes a chance to accept connections. */ - struct timespec timeout = { 0, 1 }; - nanosleep(&timeout, NULL); - } - } + if (stream->accepted_fd != -1) + /* The user hasn't yet accepted called uv_accept() */ + uv__io_stop(loop, &stream->io_watcher, POLLIN); } -#undef UV_DEC_BACKLOG - - int uv_accept(uv_stream_t* server, uv_stream_t* client) { int err; @@ -659,13 +626,13 @@ uv_shutdown_t* req; int err; - assert(QUEUE_EMPTY(&stream->write_queue)); + assert(uv__queue_empty(&stream->write_queue)); if (!(stream->flags & UV_HANDLE_CLOSING)) { uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); uv__stream_osx_interrupt_select(stream); } - if (!(stream->flags & UV_HANDLE_SHUTTING)) + if (!uv__is_stream_shutting(stream)) return; req = stream->shutdown_req; @@ -674,8 +641,7 @@ if ((stream->flags & UV_HANDLE_CLOSING) || !(stream->flags & UV_HANDLE_SHUT)) { stream->shutdown_req = NULL; - stream->flags &= ~UV_HANDLE_SHUTTING; - uv__req_unregister(stream->loop, req); + uv__req_unregister(stream->loop); err = 0; if (stream->flags & UV_HANDLE_CLOSING) @@ -731,7 +697,8 @@ do { len = n < buf->len ? n : buf->len; - buf->base += len; + if (buf->len != 0) + buf->base += len; buf->len -= len; buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */ n -= len; @@ -747,7 +714,7 @@ uv_stream_t* stream = req->handle; /* Pop the req off tcp->write_queue. */ - QUEUE_REMOVE(&req->queue); + uv__queue_remove(&req->queue); /* Only free when there was no error. On error, we touch up write_queue_size * right before making the callback. The reason we don't do that right away @@ -764,7 +731,7 @@ /* Add it to the write_completed_queue where it will have its * callback called in the near future. */ - QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); + uv__queue_insert_tail(&stream->write_completed_queue, &req->queue); uv__io_feed(stream->loop, &stream->io_watcher); } @@ -812,18 +779,14 @@ if (send_handle != NULL) { int fd_to_send; struct msghdr msg; - struct cmsghdr *cmsg; - union { - char data[64]; - struct cmsghdr alias; - } scratch; + union uv__cmsg cmsg; if (uv__is_closing(send_handle)) return UV_EBADF; fd_to_send = uv__handle_fd((uv_handle_t*) send_handle); - memset(&scratch, 0, sizeof(scratch)); + memset(&cmsg, 0, sizeof(cmsg)); assert(fd_to_send >= 0); @@ -833,20 +796,13 @@ msg.msg_iovlen = iovcnt; msg.msg_flags = 0; - msg.msg_control = &scratch.alias; + msg.msg_control = &cmsg.hdr; msg.msg_controllen = CMSG_SPACE(sizeof(fd_to_send)); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send)); - - /* silence aliasing warning */ - { - void* pv = CMSG_DATA(cmsg); - int* pi = pv; - *pi = fd_to_send; - } + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_RIGHTS; + cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(fd_to_send)); + memcpy(CMSG_DATA(&cmsg.hdr), &fd_to_send, sizeof(fd_to_send)); do n = sendmsg(uv__stream_fd(stream), &msg, 0); @@ -881,18 +837,25 @@ } static void uv__write(uv_stream_t* stream) { - QUEUE* q; + struct uv__queue* q; uv_write_t* req; ssize_t n; + int count; assert(uv__stream_fd(stream) >= 0); + /* Prevent loop starvation when the consumer of this stream read as fast as + * (or faster than) we can write it. This `count` mechanism does not need to + * change even if we switch to edge-triggered I/O. + */ + count = 32; + for (;;) { - if (QUEUE_EMPTY(&stream->write_queue)) + if (uv__queue_empty(&stream->write_queue)) return; - q = QUEUE_HEAD(&stream->write_queue); - req = QUEUE_DATA(q, uv_write_t, queue); + q = uv__queue_head(&stream->write_queue); + req = uv__queue_data(q, uv_write_t, queue); assert(req->handle == stream); n = uv__try_write(stream, @@ -905,10 +868,13 @@ req->send_handle = NULL; if (uv__write_req_update(stream, req, n)) { uv__write_req_finish(req); - return; /* TODO(bnoordhuis) Start trying to write the next request. */ + if (count-- > 0) + continue; /* Start trying to write the next request. */ + + return; } } else if (n != UV_EAGAIN) - break; + goto error; /* If this is a blocking stream, try again. */ if (stream->flags & UV_HANDLE_BLOCKING_WRITES) @@ -923,6 +889,7 @@ return; } +error: req->error = n; uv__write_req_finish(req); uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); @@ -932,20 +899,20 @@ static void uv__write_callbacks(uv_stream_t* stream) { uv_write_t* req; - QUEUE* q; - QUEUE pq; + struct uv__queue* q; + struct uv__queue pq; - if (QUEUE_EMPTY(&stream->write_completed_queue)) + if (uv__queue_empty(&stream->write_completed_queue)) return; - QUEUE_MOVE(&stream->write_completed_queue, &pq); + uv__queue_move(&stream->write_completed_queue, &pq); - while (!QUEUE_EMPTY(&pq)) { + while (!uv__queue_empty(&pq)) { /* Pop a req off write_completed_queue. */ - q = QUEUE_HEAD(&pq); - req = QUEUE_DATA(q, uv_write_t, queue); - QUEUE_REMOVE(q); - uv__req_unregister(stream->loop, req); + q = uv__queue_head(&pq); + req = uv__queue_data(q, uv_write_t, queue); + uv__queue_remove(q); + uv__req_unregister(stream->loop); if (req->bufs != NULL) { stream->write_queue_size -= uv__write_req_size(req); @@ -1010,82 +977,59 @@ } -#if defined(__PASE__) -/* on IBMi PASE the control message length can not exceed 256. */ -# define UV__CMSG_FD_COUNT 60 -#else -# define UV__CMSG_FD_COUNT 64 -#endif -#define UV__CMSG_FD_SIZE (UV__CMSG_FD_COUNT * sizeof(int)) - - static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) { struct cmsghdr* cmsg; + char* p; + char* pe; + int fd; + int err; + size_t count; + err = 0; for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { - char* start; - char* end; - int err; - void* pv; - int* pi; - unsigned int i; - unsigned int count; - if (cmsg->cmsg_type != SCM_RIGHTS) { fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n", cmsg->cmsg_type); continue; } - /* silence aliasing warning */ - pv = CMSG_DATA(cmsg); - pi = pv; + assert(cmsg->cmsg_len >= CMSG_LEN(0)); + count = cmsg->cmsg_len - CMSG_LEN(0); + assert(count % sizeof(fd) == 0); + count /= sizeof(fd); - /* Count available fds */ - start = (char*) cmsg; - end = (char*) cmsg + cmsg->cmsg_len; - count = 0; - while (start + CMSG_LEN(count * sizeof(*pi)) < end) - count++; - assert(start + CMSG_LEN(count * sizeof(*pi)) == end); + p = (void*) CMSG_DATA(cmsg); + pe = p + count * sizeof(fd); - for (i = 0; i < count; i++) { - /* Already has accepted fd, queue now */ - if (stream->accepted_fd != -1) { - err = uv__stream_queue_fd(stream, pi[i]); - if (err != 0) { - /* Close rest */ - for (; i < count; i++) - uv__close(pi[i]); - return err; - } - } else { - stream->accepted_fd = pi[i]; + while (p < pe) { + memcpy(&fd, p, sizeof(fd)); + p += sizeof(fd); + + if (err == 0) { + if (stream->accepted_fd == -1) + stream->accepted_fd = fd; + else + err = uv__stream_queue_fd(stream, fd); } + + if (err != 0) + uv__close(fd); } } - return 0; + return err; } -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wgnu-folding-constant" -# pragma clang diagnostic ignored "-Wvla-extension" -#endif - static void uv__read(uv_stream_t* stream) { uv_buf_t buf; ssize_t nread; struct msghdr msg; - char cmsg_space[CMSG_SPACE(UV__CMSG_FD_SIZE)]; + union uv__cmsg cmsg; int count; int err; int is_ipc; - stream->flags &= ~UV_HANDLE_READ_PARTIAL; - /* Prevent loop starvation when the data comes in as fast as (or faster than) * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O. */ @@ -1125,8 +1069,8 @@ msg.msg_name = NULL; msg.msg_namelen = 0; /* Set up to receive a descriptor even if one isn't in the message */ - msg.msg_controllen = sizeof(cmsg_space); - msg.msg_control = cmsg_space; + msg.msg_controllen = sizeof(cmsg); + msg.msg_control = &cmsg.hdr; do { nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0); @@ -1200,24 +1144,20 @@ #endif stream->read_cb(stream, nread, &buf); - /* Return if we didn't fill the buffer, there is no more data to read. */ - if (nread < buflen) { - stream->flags |= UV_HANDLE_READ_PARTIAL; + /* Save a system call and return if we didn't fill the buffer + * completely, on the assumption the next read() will fail with EOF. + * + * Devices like PTYs sometimes operate in a packet-like mode where + * they don't return all available data in a single read but we'll + * catch it on the next read because of level-triggered I/O. + */ + if (nread < buflen) return; - } } } } -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#undef UV__CMSG_FD_COUNT -#undef UV__CMSG_FD_SIZE - - int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { assert(stream->type == UV_TCP || stream->type == UV_TTY || @@ -1225,7 +1165,7 @@ if (!(stream->flags & UV_HANDLE_WRITABLE) || stream->flags & UV_HANDLE_SHUT || - stream->flags & UV_HANDLE_SHUTTING || + uv__is_stream_shutting(stream) || uv__is_closing(stream)) { return UV_ENOTCONN; } @@ -1238,17 +1178,16 @@ req->handle = stream; req->cb = cb; stream->shutdown_req = req; - stream->flags |= UV_HANDLE_SHUTTING; stream->flags &= ~UV_HANDLE_WRITABLE; - if (QUEUE_EMPTY(&stream->write_queue)) + if (uv__queue_empty(&stream->write_queue)) uv__io_feed(stream->loop, &stream->io_watcher); return 0; } -static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv_stream_t* stream; stream = container_of(w, uv_stream_t, io_watcher); @@ -1265,22 +1204,23 @@ assert(uv__stream_fd(stream) >= 0); - /* Ignore POLLHUP here. Even if it's set, there may still be data to read. */ - if (events & (POLLIN | POLLERR | POLLHUP)) + if (events & (POLLIN | POLLERR)) uv__read(stream); if (uv__stream_fd(stream) == -1) return; /* read_cb closed stream. */ /* Short-circuit iff POLLHUP is set, the user is still interested in read - * events and uv__read() reported a partial read but not EOF. If the EOF - * flag is set, uv__read() called read_cb with err=UV_EOF and we don't - * have to do anything. If the partial read flag is not set, we can't - * report the EOF yet because there is still data to read. + * events and uv__read() didn't see EOF. If the EOF flag is set, uv__read() + * called read_cb with err=UV_EOF and we don't have to do anything. + * + * POLLIN should not be set because, at least on Linux and possibly other + * operating systems, devices like PTYs sometimes produce partial reads even + * when more data is available. */ if ((events & POLLHUP) && + !(events & POLLIN) && (stream->flags & UV_HANDLE_READING) && - (stream->flags & UV_HANDLE_READ_PARTIAL) && !(stream->flags & UV_HANDLE_READ_EOF)) { uv_buf_t buf = { NULL, 0 }; uv__stream_eof(stream, &buf); @@ -1294,7 +1234,7 @@ uv__write_callbacks(stream); /* Write queue drained. */ - if (QUEUE_EMPTY(&stream->write_queue)) + if (uv__queue_empty(&stream->write_queue)) uv__drain(stream); } } @@ -1335,9 +1275,9 @@ return; stream->connect_req = NULL; - uv__req_unregister(stream->loop, req); + uv__req_unregister(stream->loop); - if (error < 0 || QUEUE_EMPTY(&stream->write_queue)) { + if (error < 0 || uv__queue_empty(&stream->write_queue)) { uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); } @@ -1357,12 +1297,18 @@ static int uv__check_before_write(uv_stream_t* stream, unsigned int nbufs, uv_stream_t* send_handle) { - assert(nbufs > 0); assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY) && "uv_write (unix) does not yet support other types of streams"); + /* We're not beholden to IOV_MAX but limit the buffer count to catch sign + * conversion bugs where a caller passes in a signed negative number that + * then gets converted to a really large unsigned number. + */ + if (nbufs < 1 || nbufs > 1024*1024) + return UV_EINVAL; + if (uv__stream_fd(stream) < 0) return UV_EBADF; @@ -1419,7 +1365,7 @@ req->handle = stream; req->error = 0; req->send_handle = send_handle; - QUEUE_INIT(&req->queue); + uv__queue_init(&req->queue); req->bufs = req->bufsml; if (nbufs > ARRAY_SIZE(req->bufsml)) @@ -1434,7 +1380,7 @@ stream->write_queue_size += uv__count_bufs(bufs, nbufs); /* Append the request to write_queue. */ - QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue); + uv__queue_insert_tail(&stream->write_queue, &req->queue); /* If the queue was empty when this function began, we should attempt to * do the write immediately. Otherwise start the write_watcher and wait
diff --git a/Utilities/cmlibuv/src/unix/sunos.c b/Utilities/cmlibuv/src/unix/sunos.c index 85e4d60..0793122 100644 --- a/Utilities/cmlibuv/src/unix/sunos.c +++ b/Utilities/cmlibuv/src/unix/sunos.c
@@ -28,10 +28,6 @@ #include <assert.h> #include <errno.h> -#if !defined(SUNOS_NO_IFADDRS) && _XOPEN_SOURCE < 600 -#define SUNOS_NO_IFADDRS -#endif - #ifndef SUNOS_NO_IFADDRS # include <ifaddrs.h> #endif @@ -152,7 +148,7 @@ struct port_event events[1024]; struct port_event* pe; struct timespec spec; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; sigset_t* pset; sigset_t set; @@ -170,16 +166,16 @@ int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); if (port_associate(loop->backend_fd, @@ -214,12 +210,6 @@ } for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; @@ -231,17 +221,13 @@ nfds = 1; saved_errno = 0; - if (pset != NULL) - pthread_sigmask(SIG_BLOCK, pset, NULL); - + uv__io_poll_prepare(loop, pset, timeout); err = port_getn(loop->backend_fd, events, ARRAY_SIZE(events), &nfds, timeout == -1 ? NULL : &spec); - - if (pset != NULL) - pthread_sigmask(SIG_UNBLOCK, pset, NULL); + uv__io_poll_check(loop, pset); if (err) { /* Work around another kernel bug: port_getn() may return events even @@ -255,12 +241,6 @@ } } - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); - if (events[0].portev_source == 0) { if (reset_timeout != 0) { timeout = user_timeout; @@ -311,7 +291,7 @@ have_signals = 1; } else { uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->portev_events); + uv__io_cb(loop, w, pe->portev_events); } nevents++; @@ -320,18 +300,20 @@ continue; /* Disabled by callback. */ /* Events Ports operates in oneshot mode, rearm timer on next run. */ - if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + if (w->pevents != 0 && uv__queue_empty(&w->watcher_queue)) + uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + uv__signal_event(loop, &loop->signal_io_watcher, POLLIN); } loop->watchers[loop->nwatchers] = NULL; @@ -419,6 +401,11 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { (void) getloadavg(avg, 3); } @@ -443,9 +430,7 @@ } -static void uv__fs_event_read(uv_loop_t* loop, - uv__io_t* w, - unsigned int revents) { +void uv__fs_event_read(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { uv_fs_event_t *handle = NULL; timespec_t timeout; port_event_t pe; @@ -543,8 +528,15 @@ } if (first_run) { - uv__io_init(&handle->loop->fs_event_watcher, uv__fs_event_read, portfd); - uv__io_start(handle->loop, &handle->loop->fs_event_watcher, POLLIN); + err = uv__io_init_start(handle->loop, + &handle->loop->fs_event_watcher, + UV__FS_EVENT_READ, + portfd, + POLLIN); + if (err) + uv__handle_stop(handle); + + return err; } return 0; @@ -593,6 +585,9 @@ #else /* !defined(PORT_SOURCE_FILE) */ +void uv__fs_event_read(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { +} + int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { return UV_ENOSYS; } @@ -823,6 +818,8 @@ uv_interface_address_t* address; struct ifaddrs* addrs; struct ifaddrs* ent; + size_t namelen; + char* name; *count = 0; *addresses = NULL; @@ -831,9 +828,11 @@ return UV__ERR(errno); /* Count the number of interfaces */ + namelen = 0; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (uv__ifaddr_exclude(ent)) continue; + namelen += strlen(ent->ifa_name) + 1; (*count)++; } @@ -842,19 +841,22 @@ return 0; } - *addresses = uv__malloc(*count * sizeof(**addresses)); - if (!(*addresses)) { + *addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen); + if (*addresses == NULL) { freeifaddrs(addrs); return UV_ENOMEM; } + name = (char*) &(*addresses)[*count]; address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (uv__ifaddr_exclude(ent)) continue; - address->name = uv__strdup(ent->ifa_name); + namelen = strlen(ent->ifa_name) + 1; + address->name = memcpy(name, ent->ifa_name, namelen); + name += namelen; if (ent->ifa_addr->sa_family == AF_INET6) { address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); @@ -881,17 +883,6 @@ } #endif /* SUNOS_NO_IFADDRS */ -void uv_free_interface_addresses(uv_interface_address_t* addresses, - int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(addresses[i].name); - } - - uv__free(addresses); -} - #if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L size_t strnlen(const char* s, size_t maxlen) {
diff --git a/Utilities/cmlibuv/src/unix/tcp.c b/Utilities/cmlibuv/src/unix/tcp.c index 73fc657..0ba94c0 100644 --- a/Utilities/cmlibuv/src/unix/tcp.c +++ b/Utilities/cmlibuv/src/unix/tcp.c
@@ -27,17 +27,51 @@ #include <assert.h> #include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> -static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { - struct sockaddr_storage saddr; +#if defined(CMAKE_BOOTSTRAP) && defined(__sun__) +# define SUNOS_NO_IFADDRS +#endif + +/* ifaddrs is not implemented on AIX and IBM i PASE */ +#if !defined(_AIX) && !defined(SUNOS_NO_IFADDRS) +#include <ifaddrs.h> +#endif + +static int maybe_bind_socket(int fd) { + union uv__sockaddr s; socklen_t slen; + + slen = sizeof(s); + memset(&s, 0, sizeof(s)); + + if (getsockname(fd, &s.addr, &slen)) + return UV__ERR(errno); + + if (s.addr.sa_family == AF_INET) + if (s.in.sin_port != 0) + return 0; /* Already bound to a port. */ + + if (s.addr.sa_family == AF_INET6) + if (s.in6.sin6_port != 0) + return 0; /* Already bound to a port. */ + + /* Bind to an arbitrary port. */ + if (bind(fd, &s.addr, slen)) + return UV__ERR(errno); + + return 0; +} + + +static int new_socket(uv_tcp_t* handle, int domain, unsigned int flags) { int sockfd; int err; - err = uv__socket(domain, SOCK_STREAM, 0); - if (err < 0) - return err; - sockfd = err; + sockfd = uv__socket(domain, SOCK_STREAM, 0); + if (sockfd < 0) + return sockfd; err = uv__stream_open((uv_stream_t*) handle, sockfd, flags); if (err) { @@ -45,74 +79,44 @@ return err; } - if (flags & UV_HANDLE_BOUND) { - /* Bind this new socket to an arbitrary port */ - slen = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) { - uv__close(sockfd); - return UV__ERR(errno); - } - - if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) { - uv__close(sockfd); - return UV__ERR(errno); - } - } + if (flags & UV_HANDLE_BOUND) + return maybe_bind_socket(sockfd); return 0; } -static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { - struct sockaddr_storage saddr; - socklen_t slen; +static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned int flags) { + int sockfd; + int err; - if (domain == AF_UNSPEC) { - handle->flags |= flags; - return 0; - } + if (domain == AF_UNSPEC) + goto out; - if (uv__stream_fd(handle) != -1) { + sockfd = uv__stream_fd(handle); + if (sockfd == -1) + return new_socket(handle, domain, flags); - if (flags & UV_HANDLE_BOUND) { + if (!(flags & UV_HANDLE_BOUND)) + goto out; - if (handle->flags & UV_HANDLE_BOUND) { - /* It is already bound to a port. */ - handle->flags |= flags; - return 0; - } + if (handle->flags & UV_HANDLE_BOUND) + goto out; /* Already bound to a port. */ - /* Query to see if tcp socket is bound. */ - slen = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) - return UV__ERR(errno); + err = maybe_bind_socket(sockfd); + if (err) + return err; - if ((saddr.ss_family == AF_INET6 && - ((struct sockaddr_in6*) &saddr)->sin6_port != 0) || - (saddr.ss_family == AF_INET && - ((struct sockaddr_in*) &saddr)->sin_port != 0)) { - /* Handle is already bound to a port. */ - handle->flags |= flags; - return 0; - } +out: - /* Bind to arbitrary port */ - if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) - return UV__ERR(errno); - } - - handle->flags |= flags; - return 0; - } - - return new_socket(handle, domain, flags); + handle->flags |= flags; + return 0; } int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { int domain; + int err; /* Use the lower 8 bits for the domain */ domain = flags & 0xFF; @@ -129,9 +133,12 @@ */ if (domain != AF_UNSPEC) { - int err = maybe_new_socket(tcp, domain, 0); + err = new_socket(tcp, domain, 0); if (err) { - QUEUE_REMOVE(&tcp->handle_queue); + uv__queue_remove(&tcp->handle_queue); + if (tcp->io_watcher.fd != -1) + uv__close(tcp->io_watcher.fd); + tcp->io_watcher.fd = -1; return err; } } @@ -164,6 +171,12 @@ if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) return UV__ERR(errno); + if (flags & UV_TCP_REUSEPORT) { + err = uv__sock_reuseport(tcp->io_watcher.fd); + if (err) + return err; + } + #ifndef __OpenBSD__ #ifdef IPV6_V6ONLY if (addr->sa_family == AF_INET6) { @@ -202,11 +215,77 @@ } +static int uv__is_ipv6_link_local(const struct sockaddr* addr) { + const struct sockaddr_in6* a6; + uint8_t b[2]; + + if (addr->sa_family != AF_INET6) + return 0; + + a6 = (const struct sockaddr_in6*) addr; + memcpy(b, &a6->sin6_addr, sizeof(b)); + + return b[0] == 0xFE && b[1] == 0x80; +} + + +static int uv__ipv6_link_local_scope_id(void) { + struct sockaddr_in6* a6; + int rv; +#if defined(_AIX) + /* AIX & IBM i do not have ifaddrs + * so fallback to use uv_interface_addresses */ + uv_interface_address_t* interfaces; + uv_interface_address_t* ifa; + int count, i; + + if (uv_interface_addresses(&interfaces, &count)) + return 0; + + rv = 0; + + for (ifa = interfaces; ifa != &interfaces[count]; ifa++) { + if (uv__is_ipv6_link_local((struct sockaddr*) &ifa->address)) { + rv = ifa->address.address6.sin6_scope_id; + break; + } + } + + uv_free_interface_addresses(interfaces, count); + +#elif defined(SUNOS_NO_IFADDRS) + rv = 0; +#else + struct ifaddrs* ifa; + struct ifaddrs* p; + + if (getifaddrs(&ifa)) + return 0; + + for (p = ifa; p != NULL; p = p->ifa_next) + if (p->ifa_addr != NULL) + if (uv__is_ipv6_link_local(p->ifa_addr)) + break; + + rv = 0; + if (p != NULL) { + a6 = (struct sockaddr_in6*) p->ifa_addr; + rv = a6->sin6_scope_id; + } + + freeifaddrs(ifa); +#endif /* defined(_AIX) */ + + return rv; +} + + int uv__tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, uv_connect_cb cb) { + struct sockaddr_in6 tmp6; int err; int r; @@ -224,6 +303,14 @@ if (err) return err; + if (uv__is_ipv6_link_local(addr)) { + memcpy(&tmp6, addr, sizeof(tmp6)); + if (tmp6.sin6_scope_id == 0) { + tmp6.sin6_scope_id = uv__ipv6_link_local_scope_id(); + addr = (void*) &tmp6; + } + } + do { errno = 0; r = connect(uv__stream_fd(handle), addr, addrlen); @@ -256,7 +343,7 @@ uv__req_init(handle->loop, req, UV_CONNECT); req->cb = cb; req->handle = (uv_stream_t*) handle; - QUEUE_INIT(&req->queue); + uv__queue_init(&req->queue); handle->connect_req = req; uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); @@ -317,7 +404,7 @@ struct linger l = { 1, 0 }; /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */ - if (handle->flags & UV_HANDLE_SHUTTING) + if (uv__is_stream_shutting(handle)) return UV_EINVAL; fd = uv__stream_fd(handle); @@ -338,24 +425,12 @@ int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { - static int single_accept_cached = -1; - unsigned long flags; - int single_accept; + unsigned int flags; int err; if (tcp->delayed_error) return tcp->delayed_error; - single_accept = uv__load_relaxed(&single_accept_cached); - if (single_accept == -1) { - const char* val = getenv("UV_TCP_SINGLE_ACCEPT"); - single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */ - uv__store_relaxed(&single_accept_cached, single_accept); - } - - if (single_accept) - tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; - flags = 0; #if defined(__MVS__) /* on zOS the listen call does not bind automatically @@ -375,10 +450,9 @@ tcp->flags |= UV_HANDLE_BOUND; /* Start listening for connections. */ - tcp->io_watcher.cb = uv__server_io; - uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN); + uv__io_cb_set(&tcp->io_watcher, UV__SERVER_IO); - return 0; + return uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN); } @@ -389,32 +463,117 @@ } -int uv__tcp_keepalive(int fd, int on, unsigned int delay) { +#if (defined(UV__SOLARIS_11_4) && !UV__SOLARIS_11_4) || \ + (defined(__DragonFly__) && __DragonFly_version < 500702) +/* DragonFlyBSD <500702 and Solaris <11.4 require millisecond units + * for TCP keepalive options. */ +#define UV_KEEPALIVE_FACTOR(x) (x *= 1000) +#else +#define UV_KEEPALIVE_FACTOR(x) +#endif +int uv__tcp_keepalive(int fd, + int on, + unsigned int idle, + unsigned int intvl, + unsigned int cnt) { if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) return UV__ERR(errno); -#ifdef TCP_KEEPIDLE - if (on) { - int intvl = 1; /* 1 second; same as default on Win32 */ - int cnt = 10; /* 10 retries; same as hardcoded on Win32 */ - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) - return UV__ERR(errno); - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) - return UV__ERR(errno); - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) - return UV__ERR(errno); - } + if (!on) + return 0; + + if (idle < 1 || intvl < 1 || cnt < 1) + return UV_EINVAL; + +#ifdef __sun + /* The implementation of TCP keep-alive on Solaris/SmartOS is a bit unusual + * compared to other Unix-like systems. + * Thus, we need to specialize it on Solaris. + * + * There are two keep-alive mechanisms on Solaris: + * - By default, the first keep-alive probe is sent out after a TCP connection is idle for two hours. + * If the peer does not respond to the probe within eight minutes, the TCP connection is aborted. + * You can alter the interval for sending out the first probe using the socket option TCP_KEEPALIVE_THRESHOLD + * in milliseconds or TCP_KEEPIDLE in seconds. + * The system default is controlled by the TCP ndd parameter tcp_keepalive_interval. The minimum value is ten seconds. + * The maximum is ten days, while the default is two hours. If you receive no response to the probe, + * you can use the TCP_KEEPALIVE_ABORT_THRESHOLD socket option to change the time threshold for aborting a TCP connection. + * The option value is an unsigned integer in milliseconds. The value zero indicates that TCP should never time out and + * abort the connection when probing. The system default is controlled by the TCP ndd parameter tcp_keepalive_abort_interval. + * The default is eight minutes. + * + * - The second implementation is activated if socket option TCP_KEEPINTVL and/or TCP_KEEPCNT are set. + * The time between each consequent probes is set by TCP_KEEPINTVL in seconds. + * The minimum value is ten seconds. The maximum is ten days, while the default is two hours. + * The TCP connection will be aborted after certain amount of probes, which is set by TCP_KEEPCNT, without receiving response. + */ + + /* Kernel expects at least 10 seconds for TCP_KEEPIDLE and TCP_KEEPINTVL. */ + if (idle < 10) + idle = 10; + if (intvl < 10) + intvl = 10; + /* Kernel expects at most 10 days for TCP_KEEPIDLE and TCP_KEEPINTVL. */ + if (idle > 10*24*60*60) + idle = 10*24*60*60; + if (intvl > 10*24*60*60) + intvl = 10*24*60*60; + + UV_KEEPALIVE_FACTOR(idle); + + /* `TCP_KEEPIDLE`, `TCP_KEEPINTVL`, and `TCP_KEEPCNT` were not available on Solaris + * until version 11.4, but let's take a chance here. */ +#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT) + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) + return UV__ERR(errno); + + UV_KEEPALIVE_FACTOR(intvl); + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) + return UV__ERR(errno); + + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) + return UV__ERR(errno); +#elif defined(TCP_KEEPALIVE_THRESHOLD) + /* Fall back to the first implementation of tcp-alive mechanism for older Solaris, + * simulate the tcp-alive mechanism on other platforms via `TCP_KEEPALIVE_THRESHOLD` + `TCP_KEEPALIVE_ABORT_THRESHOLD`. + */ + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &idle, sizeof(idle))) + return UV__ERR(errno); + + /* Note that the consequent probes will not be sent at equal intervals on Solaris, + * but will be sent using the exponential backoff algorithm. */ + unsigned int time_to_abort = intvl * cnt; + UV_KEEPALIVE_FACTOR(time_to_abort); + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, &time_to_abort, sizeof(time_to_abort))) + return UV__ERR(errno); +#else + return UV__ENOPROTOOPT; #endif - /* Solaris/SmartOS, if you don't support keep-alive, - * then don't advertise it in your system headers... - */ - /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */ -#if defined(TCP_KEEPALIVE) && !defined(__sun) - if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) +#else /* !defined(__sun) */ + + UV_KEEPALIVE_FACTOR(idle); +#ifdef TCP_KEEPIDLE + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) + return UV__ERR(errno); +#elif defined(TCP_KEEPALIVE) + /* Darwin/macOS uses TCP_KEEPALIVE in place of TCP_KEEPIDLE. */ + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle))) return UV__ERR(errno); #endif +#ifdef TCP_KEEPINTVL + UV_KEEPALIVE_FACTOR(intvl); + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) + return UV__ERR(errno); +#endif + +#ifdef TCP_KEEPCNT + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) + return UV__ERR(errno); +#endif + +#endif /* !defined(__sun) */ return 0; } @@ -437,11 +596,20 @@ } -int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { +int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int idle) { + return uv_tcp_keepalive_ex(handle, on, idle, 1, 10); +} + + +int uv_tcp_keepalive_ex(uv_tcp_t* handle, + int on, + unsigned int idle, + unsigned int intvl, + unsigned int cnt) { int err; if (uv__stream_fd(handle) != -1) { - err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay); + err = uv__tcp_keepalive(uv__stream_fd(handle), on, idle, intvl, cnt); if (err) return err; } @@ -451,7 +619,7 @@ else handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE; - /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge + /* TODO Store idle if uv__stream_fd(handle) == -1 but don't want to enlarge * uv_tcp_t with an int that's almost never used... */ @@ -460,10 +628,6 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { - if (enable) - handle->flags &= ~UV_HANDLE_TCP_SINGLE_ACCEPT; - else - handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; return 0; } @@ -476,7 +640,7 @@ int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) { uv_os_sock_t temp[2]; int err; -#if defined(__FreeBSD__) || defined(__linux__) +#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) int flags; flags = type | SOCK_CLOEXEC;
diff --git a/Utilities/cmlibuv/src/unix/thread.c b/Utilities/cmlibuv/src/unix/thread.c index 5a07b02..8994b5b 100644 --- a/Utilities/cmlibuv/src/unix/thread.c +++ b/Utilities/cmlibuv/src/unix/thread.c
@@ -23,6 +23,9 @@ #include "internal.h" #include <pthread.h> +#ifdef __OpenBSD__ +#include <pthread_np.h> +#endif #include <assert.h> #include <errno.h> @@ -41,127 +44,20 @@ #include <gnu/libc-version.h> /* gnu_get_libc_version() */ #endif +#if defined(__linux__) +# include <sched.h> +# define uv__cpu_set_t cpu_set_t +#elif defined(__FreeBSD__) +# include <sys/param.h> +# include <sys/cpuset.h> +# include <pthread_np.h> +# define uv__cpu_set_t cpuset_t +#endif + + #undef NANOSEC #define NANOSEC ((uint64_t) 1e9) -#if defined(PTHREAD_BARRIER_SERIAL_THREAD) -STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t)); -#endif - -/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */ -#if defined(_AIX) || \ - defined(__OpenBSD__) || \ - !defined(PTHREAD_BARRIER_SERIAL_THREAD) -int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { - struct _uv_barrier* b; - int rc; - - if (barrier == NULL || count == 0) - return UV_EINVAL; - - b = uv__malloc(sizeof(*b)); - if (b == NULL) - return UV_ENOMEM; - - b->in = 0; - b->out = 0; - b->threshold = count; - - rc = uv_mutex_init(&b->mutex); - if (rc != 0) - goto error2; - - rc = uv_cond_init(&b->cond); - if (rc != 0) - goto error; - - barrier->b = b; - return 0; - -error: - uv_mutex_destroy(&b->mutex); -error2: - uv__free(b); - return rc; -} - - -int uv_barrier_wait(uv_barrier_t* barrier) { - struct _uv_barrier* b; - int last; - - if (barrier == NULL || barrier->b == NULL) - return UV_EINVAL; - - b = barrier->b; - uv_mutex_lock(&b->mutex); - - if (++b->in == b->threshold) { - b->in = 0; - b->out = b->threshold; - uv_cond_signal(&b->cond); - } else { - do - uv_cond_wait(&b->cond, &b->mutex); - while (b->in != 0); - } - - last = (--b->out == 0); - uv_cond_signal(&b->cond); - - uv_mutex_unlock(&b->mutex); - return last; -} - - -void uv_barrier_destroy(uv_barrier_t* barrier) { - struct _uv_barrier* b; - - b = barrier->b; - uv_mutex_lock(&b->mutex); - - assert(b->in == 0); - while (b->out != 0) - uv_cond_wait(&b->cond, &b->mutex); - - if (b->in != 0) - abort(); - - uv_mutex_unlock(&b->mutex); - uv_mutex_destroy(&b->mutex); - uv_cond_destroy(&b->cond); - - uv__free(barrier->b); - barrier->b = NULL; -} - -#else - -int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { - return UV__ERR(pthread_barrier_init(barrier, NULL, count)); -} - - -int uv_barrier_wait(uv_barrier_t* barrier) { - int rc; - - rc = pthread_barrier_wait(barrier); - if (rc != 0) - if (rc != PTHREAD_BARRIER_SERIAL_THREAD) - abort(); - - return rc == PTHREAD_BARRIER_SERIAL_THREAD; -} - - -void uv_barrier_destroy(uv_barrier_t* barrier) { - if (pthread_barrier_destroy(barrier)) - abort(); -} - -#endif - - /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is * too small to safely receive signals on. * @@ -233,6 +129,12 @@ return uv_thread_create_ex(tid, ¶ms, entry, arg); } + +int uv_thread_detach(uv_thread_t *tid) { + return UV__ERR(pthread_detach(*tid)); +} + + int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, void (*entry)(void *arg), @@ -284,6 +186,106 @@ return UV__ERR(err); } +#if UV__CPU_AFFINITY_SUPPORTED + +int uv_thread_setaffinity(uv_thread_t* tid, + char* cpumask, + char* oldmask, + size_t mask_size) { + int i; + int r; + uv__cpu_set_t cpuset; + int cpumasksize; + + cpumasksize = uv_cpumask_size(); + if (cpumasksize < 0) + return cpumasksize; + if (mask_size < (size_t)cpumasksize) + return UV_EINVAL; + + if (oldmask != NULL) { + r = uv_thread_getaffinity(tid, oldmask, mask_size); + if (r < 0) + return r; + } + + CPU_ZERO(&cpuset); + for (i = 0; i < cpumasksize; i++) + if (cpumask[i]) + CPU_SET(i, &cpuset); + +#if defined(__ANDROID__) || defined(__OHOS__) + if (sched_setaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset)) + r = errno; + else + r = 0; +#else + r = pthread_setaffinity_np(*tid, sizeof(cpuset), &cpuset); +#endif + + return UV__ERR(r); +} + + +int uv_thread_getaffinity(uv_thread_t* tid, + char* cpumask, + size_t mask_size) { + int r; + int i; + uv__cpu_set_t cpuset; + int cpumasksize; + + cpumasksize = uv_cpumask_size(); + if (cpumasksize < 0) + return cpumasksize; + if (mask_size < (size_t)cpumasksize) + return UV_EINVAL; + + CPU_ZERO(&cpuset); +#if defined(__ANDROID__) || defined(__OHOS__) + if (sched_getaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset)) + r = errno; + else + r = 0; +#else + r = pthread_getaffinity_np(*tid, sizeof(cpuset), &cpuset); +#endif + if (r) + return UV__ERR(r); + for (i = 0; i < cpumasksize; i++) + cpumask[i] = !!CPU_ISSET(i, &cpuset); + + return 0; +} +#else +int uv_thread_setaffinity(uv_thread_t* tid, + char* cpumask, + char* oldmask, + size_t mask_size) { + return UV_ENOTSUP; +} + + +int uv_thread_getaffinity(uv_thread_t* tid, + char* cpumask, + size_t mask_size) { + return UV_ENOTSUP; +} +#endif /* defined(__linux__) || defined(UV_BSD_H) */ + +int uv_thread_getcpu(void) { +#if UV__CPU_AFFINITY_SUPPORTED + int cpu; + + cpu = sched_getcpu(); + if (cpu < 0) + return UV__ERR(errno); + + return cpu; +#else + return UV_ENOTSUP; +#endif +} uv_thread_t uv_thread_self(void) { return pthread_self(); @@ -298,6 +300,18 @@ return pthread_equal(*t1, *t2); } +int uv_thread_setname(const char* name) { + if (name == NULL) + return UV_EINVAL; + return uv__thread_setname(name); +} + +int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) { + if (name == NULL || size == 0) + return UV_EINVAL; + + return uv__thread_getname(tid, name, size); +} int uv_mutex_init(uv_mutex_t* mutex) { #if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK) @@ -585,7 +599,7 @@ uv_mutex_lock(&sem->mutex); sem->value++; if (sem->value == 1) - uv_cond_signal(&sem->cond); + uv_cond_signal(&sem->cond); /* Release one to replace us. */ uv_mutex_unlock(&sem->mutex); } @@ -798,11 +812,33 @@ abort(); } +#if defined(__APPLE__) && defined(__MACH__) + +void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { + int r; + + errno = 0; + r = pthread_cond_wait(cond, mutex); + + /* Workaround for a bug in OS X at least up to 13.6 + * See https://github.com/libuv/libuv/issues/4165 + */ + if (r == EINVAL) + if (errno == EBUSY) + return; + + if (r) + abort(); +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ + void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { if (pthread_cond_wait(cond, mutex)) abort(); } +#endif int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { int r; @@ -836,9 +872,7 @@ return UV_ETIMEDOUT; abort(); -#ifndef __SUNPRO_C return UV_EINVAL; /* Satisfy the compiler. */ -#endif } @@ -862,3 +896,82 @@ if (pthread_setspecific(*key, value)) abort(); } + +#if defined(_AIX) || defined(__MVS__) || defined(__PASE__) || \ + defined(SUNOS_NO_PTHREAD_NAME) +int uv__thread_setname(const char* name) { + return UV_ENOSYS; +} +#elif defined(__APPLE__) +int uv__thread_setname(const char* name) { + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + int err = pthread_setname_np(namebuf); + if (err) + return UV__ERR(errno); + return 0; +} +#elif defined(__NetBSD__) +int uv__thread_setname(const char* name) { + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + return UV__ERR(pthread_setname_np(pthread_self(), "%s", namebuf)); +} +#elif defined(__OpenBSD__) +int uv__thread_setname(const char* name) { + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + pthread_set_name_np(pthread_self(), namebuf); + return 0; +} +#else +int uv__thread_setname(const char* name) { + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + return UV__ERR(pthread_setname_np(pthread_self(), namebuf)); +} +#endif + +#if (defined(__ANDROID_API__) && __ANDROID_API__ < 26) || \ + defined(_AIX) || \ + defined(__MVS__) || \ + defined(__PASE__) || \ + defined(SUNOS_NO_PTHREAD_NAME) +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) { + return UV_ENOSYS; +} +#elif defined(__OpenBSD__) +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) { + char thread_name[UV_PTHREAD_MAX_NAMELEN_NP]; + pthread_get_name_np(*tid, thread_name, sizeof(thread_name)); + strncpy(name, thread_name, size - 1); + name[size - 1] = '\0'; + return 0; +} +#elif defined(__APPLE__) +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) { + char thread_name[UV_PTHREAD_MAX_NAMELEN_NP]; + if (pthread_getname_np(*tid, thread_name, sizeof(thread_name)) != 0) + return UV__ERR(errno); + + strncpy(name, thread_name, size - 1); + name[size - 1] = '\0'; + return 0; +} +#else +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) { + int r; + char thread_name[UV_PTHREAD_MAX_NAMELEN_NP]; + r = pthread_getname_np(*tid, thread_name, sizeof(thread_name)); + if (r != 0) + return UV__ERR(r); + + strncpy(name, thread_name, size - 1); + name[size - 1] = '\0'; + return 0; +} +#endif
diff --git a/Utilities/cmlibuv/src/unix/tty.c b/Utilities/cmlibuv/src/unix/tty.c index d794bd5..7a1784f 100644 --- a/Utilities/cmlibuv/src/unix/tty.c +++ b/Utilities/cmlibuv/src/unix/tty.c
@@ -21,8 +21,8 @@ #include "uv.h" #include "internal.h" -#include "spinlock.h" +#include <stdatomic.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> @@ -66,7 +66,7 @@ static int orig_termios_fd = -1; static struct termios orig_termios; -static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER; +static _Atomic int termios_spinlock; int uv__tcsetattr(int fd, int how, const struct termios *term) { int rc; @@ -83,7 +83,7 @@ static int uv__tty_is_slave(const int fd) { int result; -#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__linux__) || defined(__FreeBSD__) int dummy; result = ioctl(fd, TIOCGPTN, &dummy) != 0; @@ -115,7 +115,7 @@ } /* Lookup stat structure behind the file descriptor. */ - if (fstat(fd, &sb) != 0) + if (uv__fstat(fd, &sb) != 0) abort(); /* Assert character device. */ @@ -221,7 +221,7 @@ int rc = r; if (newfd != -1) uv__close(newfd); - QUEUE_REMOVE(&tty->handle_queue); + uv__queue_remove(&tty->handle_queue); do r = fcntl(fd, F_SETFL, saved_flags); while (r == -1 && errno == EINTR); @@ -279,9 +279,15 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { struct termios tmp; + int expected; int fd; int rc; + if (uv__is_raw_tty_mode(mode)) { + /* There is only a single raw TTY mode on UNIX. */ + mode = UV_TTY_MODE_RAW; + } + if (tty->mode == (int) mode) return 0; @@ -295,12 +301,16 @@ return UV__ERR(errno); /* This is used for uv_tty_reset_mode() */ - uv_spinlock_lock(&termios_spinlock); + do + expected = 0; + while (!atomic_compare_exchange_strong(&termios_spinlock, &expected, 1)); + if (orig_termios_fd == -1) { orig_termios = tty->orig_termios; orig_termios_fd = fd; } - uv_spinlock_unlock(&termios_spinlock); + + atomic_store(&termios_spinlock, 0); } tmp = tty->orig_termios; @@ -318,6 +328,8 @@ case UV_TTY_MODE_IO: uv__tty_make_raw(&tmp); break; + default: + UNREACHABLE(); } /* Apply changes after draining */ @@ -329,6 +341,37 @@ } +void uv__tty_close(uv_tty_t* handle) { + int expected; + int fd; + + fd = handle->io_watcher.fd; + if (fd == -1) + goto done; + + /* This is used for uv_tty_reset_mode() */ + do + expected = 0; + while (!atomic_compare_exchange_strong(&termios_spinlock, &expected, 1)); + + if (fd == orig_termios_fd) { + /* XXX(bnoordhuis) the tcsetattr is probably wrong when there are still + * other uv_tty_t handles active that refer to the same tty/pty but it's + * hard to recognize that particular situation without maintaining some + * kind of process-global data structure, and that still won't work in a + * multi-process setup. + */ + uv__tcsetattr(fd, TCSANOW, &orig_termios); + orig_termios_fd = -1; + } + + atomic_store(&termios_spinlock, 0); + +done: + uv__stream_close((uv_stream_t*) handle); +} + + int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { struct winsize ws; int err; @@ -364,7 +407,7 @@ if (isatty(file)) return UV_TTY; - if (fstat(file, &s)) { + if (uv__fstat(file, &s)) { #if defined(__PASE__) /* On ibmi receiving RST from TCP instead of FIN immediately puts fd into * an error state. fstat will return EINVAL, getsockname will also return @@ -450,14 +493,15 @@ int err; saved_errno = errno; - if (!uv_spinlock_trylock(&termios_spinlock)) - return UV_EBUSY; /* In uv_tty_set_mode(). */ + + if (atomic_exchange(&termios_spinlock, 1)) + return UV_EBUSY; /* In uv_tty_set_mode() or uv__tty_close(). */ err = 0; if (orig_termios_fd != -1) err = uv__tcsetattr(orig_termios_fd, TCSANOW, &orig_termios); - uv_spinlock_unlock(&termios_spinlock); + atomic_store(&termios_spinlock, 0); errno = saved_errno; return err;
diff --git a/Utilities/cmlibuv/src/unix/udp.c b/Utilities/cmlibuv/src/unix/udp.c index 83acf13..20dc85f 100644 --- a/Utilities/cmlibuv/src/unix/udp.c +++ b/Utilities/cmlibuv/src/unix/udp.c
@@ -32,6 +32,10 @@ #endif #include <sys/un.h> +#if defined(__linux__) +#include <linux/errqueue.h> +#endif + #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif @@ -40,50 +44,17 @@ # define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif -union uv__sockaddr { - struct sockaddr_in6 in6; - struct sockaddr_in in; - struct sockaddr addr; -}; - static void uv__udp_run_completed(uv_udp_t* handle); -static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents); -static void uv__udp_recvmsg(uv_udp_t* handle); +static void uv__udp_recvmsg(uv_udp_t* handle, int flag); static void uv__udp_sendmsg(uv_udp_t* handle); static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain, unsigned int flags); +static int uv__udp_sendmsg1(int fd, + const uv_buf_t* bufs, + unsigned int nbufs, + const struct sockaddr* addr); -#if HAVE_MMSG - -#define UV__MMSG_MAXWIDTH 20 - -static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf); -static void uv__udp_sendmmsg(uv_udp_t* handle); - -static int uv__recvmmsg_avail; -static int uv__sendmmsg_avail; -static uv_once_t once = UV_ONCE_INIT; - -static void uv__udp_mmsg_init(void) { - int ret; - int s; - s = uv__socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) - return; - ret = uv__sendmmsg(s, NULL, 0); - if (ret == 0 || errno != ENOSYS) { - uv__sendmmsg_avail = 1; - uv__recvmmsg_avail = 1; - } else { - ret = uv__recvmmsg(s, NULL, 0); - if (ret == 0 || errno != ENOSYS) - uv__recvmmsg_avail = 1; - } - uv__close(s); -} - -#endif void uv__udp_close(uv_udp_t* handle) { uv__io_close(handle->loop, &handle->io_watcher); @@ -98,18 +69,18 @@ void uv__udp_finish_close(uv_udp_t* handle) { uv_udp_send_t* req; - QUEUE* q; + struct uv__queue* q; assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT)); assert(handle->io_watcher.fd == -1); - while (!QUEUE_EMPTY(&handle->write_queue)) { - q = QUEUE_HEAD(&handle->write_queue); - QUEUE_REMOVE(q); + while (!uv__queue_empty(&handle->write_queue)) { + q = uv__queue_head(&handle->write_queue); + uv__queue_remove(q); - req = QUEUE_DATA(q, uv_udp_send_t, queue); + req = uv__queue_data(q, uv_udp_send_t, queue); req->status = UV_ECANCELED; - QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); } uv__udp_run_completed(handle); @@ -126,17 +97,17 @@ static void uv__udp_run_completed(uv_udp_t* handle) { uv_udp_send_t* req; - QUEUE* q; + struct uv__queue* q; assert(!(handle->flags & UV_HANDLE_UDP_PROCESSING)); handle->flags |= UV_HANDLE_UDP_PROCESSING; - while (!QUEUE_EMPTY(&handle->write_completed_queue)) { - q = QUEUE_HEAD(&handle->write_completed_queue); - QUEUE_REMOVE(q); + while (!uv__queue_empty(&handle->write_completed_queue)) { + q = uv__queue_head(&handle->write_completed_queue); + uv__queue_remove(q); - req = QUEUE_DATA(q, uv_udp_send_t, queue); - uv__req_unregister(handle->loop, req); + req = uv__queue_data(q, uv_udp_send_t, queue); + uv__req_unregister(handle->loop); handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs); handle->send_queue_count--; @@ -157,7 +128,7 @@ req->send_cb(req, req->status); } - if (QUEUE_EMPTY(&handle->write_queue)) { + if (uv__queue_empty(&handle->write_queue)) { /* Pending queue and completion queue empty, stop watcher. */ uv__io_stop(handle->loop, &handle->io_watcher, POLLOUT); if (!uv__io_active(&handle->io_watcher, POLLIN)) @@ -168,31 +139,74 @@ } -static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { +#if defined(__linux__) +static int uv__udp_recvmsg_errqueue(uv_udp_t* handle, + struct msghdr* h, + uv_buf_t* buf, + const struct sockaddr* peer, + int flags) { + struct cmsghdr* cmsg; + struct sock_extended_err* serr; + struct sockaddr* offender; + + if (!(h->msg_flags & MSG_ERRQUEUE)) + return 0; + + flags |= UV_UDP_LINUX_RECVERR; + for (cmsg = CMSG_FIRSTHDR(h); cmsg != NULL; cmsg = CMSG_NXTHDR(h, cmsg)) { + if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) || + (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) { + serr = (struct sock_extended_err*) CMSG_DATA(cmsg); + + offender = SO_EE_OFFENDER(serr); + handle->recv_cb(handle, + UV__ERR(serr->ee_errno), + buf, + offender, + flags); + return 1; /* handled */ + } + } + return 0; +} +#endif + + +void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { uv_udp_t* handle; handle = container_of(w, uv_udp_t, io_watcher); assert(handle->type == UV_UDP); if (revents & POLLIN) - uv__udp_recvmsg(handle); + uv__udp_recvmsg(handle, 0); - if (revents & POLLOUT) { + /* Just Linux support for now. */ +#if defined(__linux__) + if (revents & POLLERR) + uv__udp_recvmsg(handle, MSG_ERRQUEUE); +#endif + + if (revents & POLLOUT && !uv__is_closing(handle)) { uv__udp_sendmsg(handle); uv__udp_run_completed(handle); } } -#if HAVE_MMSG -static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { - struct sockaddr_in6 peers[UV__MMSG_MAXWIDTH]; - struct iovec iov[UV__MMSG_MAXWIDTH]; - struct uv__mmsghdr msgs[UV__MMSG_MAXWIDTH]; +static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf, int flag) { +#if defined(__linux__) || defined(__FreeBSD__) || \ + (defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) + struct sockaddr_in6 peers[20]; + struct iovec iov[ARRAY_SIZE(peers)]; + struct mmsghdr msgs[ARRAY_SIZE(peers)]; ssize_t nread; uv_buf_t chunk_buf; size_t chunks; int flags; size_t k; +#if defined(__linux__) + char control[ARRAY_SIZE(peers)][64]; +#endif #ifdef __clang_analyzer__ /* Tell clang-analyzer the array is initialized. @@ -215,11 +229,24 @@ msgs[k].msg_hdr.msg_control = NULL; msgs[k].msg_hdr.msg_controllen = 0; msgs[k].msg_hdr.msg_flags = 0; + msgs[k].msg_len = 0; +#if defined(__linux__) + if (flag & MSG_ERRQUEUE) { + msgs[k].msg_hdr.msg_control = control[k]; + msgs[k].msg_hdr.msg_controllen = sizeof(control[k]); + } +#endif } +#if defined(__APPLE__) do - nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks); + nread = recvmsg_x(handle->io_watcher.fd, msgs, chunks, MSG_DONTWAIT); while (nread == -1 && errno == EINTR); +#else + do + nread = recvmmsg(handle->io_watcher.fd, msgs, chunks, flag, NULL); + while (nread == -1 && errno == EINTR); +#endif if (nread < 1) { if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK) @@ -234,6 +261,13 @@ flags |= UV_UDP_PARTIAL; chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len); +#if defined(__linux__) + if ((flag & MSG_ERRQUEUE) && + uv__udp_recvmsg_errqueue(handle, &msgs[k].msg_hdr, &chunk_buf, + (const struct sockaddr*) &peers[k], flags)) { + continue; + } +#endif handle->recv_cb(handle, msgs[k].msg_len, &chunk_buf, @@ -246,16 +280,21 @@ handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE); } return nread; +#else /* __linux__ || ____FreeBSD__ || __APPLE__ */ + return UV_ENOSYS; +#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */ } -#endif -static void uv__udp_recvmsg(uv_udp_t* handle) { +static void uv__udp_recvmsg(uv_udp_t* handle, int flag) { struct sockaddr_storage peer; struct msghdr h; ssize_t nread; uv_buf_t buf; int flags; int count; +#if defined(__linux__) + char control[256]; +#endif assert(handle->recv_cb != NULL); assert(handle->alloc_cb != NULL); @@ -274,14 +313,12 @@ } assert(buf.base != NULL); -#if HAVE_MMSG if (uv_udp_using_recvmmsg(handle)) { - nread = uv__udp_recvmmsg(handle, &buf); + nread = uv__udp_recvmmsg(handle, &buf, flag); if (nread > 0) count -= nread; continue; } -#endif memset(&h, 0, sizeof(h)); memset(&peer, 0, sizeof(peer)); @@ -289,25 +326,36 @@ h.msg_namelen = sizeof(peer); h.msg_iov = (void*) &buf; h.msg_iovlen = 1; - - do { - nread = recvmsg(handle->io_watcher.fd, &h, 0); +#if defined(__linux__) + if (flag & MSG_ERRQUEUE) { + h.msg_control = control; + h.msg_controllen = sizeof(control); } +#endif + + do + nread = recvmsg(handle->io_watcher.fd, &h, flag); while (nread == -1 && errno == EINTR); - if (nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) - handle->recv_cb(handle, 0, &buf, NULL, 0); - else - handle->recv_cb(handle, UV__ERR(errno), &buf, NULL, 0); - } - else { - flags = 0; + flags = 0; + if (nread != -1) if (h.msg_flags & MSG_TRUNC) flags |= UV_UDP_PARTIAL; - handle->recv_cb(handle, nread, &buf, (const struct sockaddr*) &peer, flags); +#if defined(__linux__) + if ((flag & MSG_ERRQUEUE) && + uv__udp_recvmsg_errqueue(handle, &h, &buf, (void*) &peer, flags)) { + count--; + continue; } +#endif + + if (nread != -1) + handle->recv_cb(handle, nread, &buf, (void*) &peer, flags); + else if (errno == EAGAIN || errno == EWOULDBLOCK) + handle->recv_cb(handle, 0, &buf, NULL, 0); + else + handle->recv_cb(handle, UV__ERR(errno), &buf, NULL, 0); count--; } /* recv_cb callback may decide to pause or close the handle */ @@ -317,175 +365,25 @@ && handle->recv_cb != NULL); } -#if HAVE_MMSG -static void uv__udp_sendmmsg(uv_udp_t* handle) { - uv_udp_send_t* req; - struct uv__mmsghdr h[UV__MMSG_MAXWIDTH]; - struct uv__mmsghdr *p; - QUEUE* q; - ssize_t npkts; - size_t pkts; - size_t i; - - if (QUEUE_EMPTY(&handle->write_queue)) - return; - -write_queue_drain: - for (pkts = 0, q = QUEUE_HEAD(&handle->write_queue); - pkts < UV__MMSG_MAXWIDTH && q != &handle->write_queue; - ++pkts, q = QUEUE_HEAD(q)) { - assert(q != NULL); - req = QUEUE_DATA(q, uv_udp_send_t, queue); - assert(req != NULL); - - p = &h[pkts]; - memset(p, 0, sizeof(*p)); - if (req->addr.ss_family == AF_UNSPEC) { - p->msg_hdr.msg_name = NULL; - p->msg_hdr.msg_namelen = 0; - } else { - p->msg_hdr.msg_name = &req->addr; - if (req->addr.ss_family == AF_INET6) - p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6); - else if (req->addr.ss_family == AF_INET) - p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in); - else if (req->addr.ss_family == AF_UNIX) - p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un); - else { - assert(0 && "unsupported address family"); - abort(); - } - } - h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs; - h[pkts].msg_hdr.msg_iovlen = req->nbufs; - } - - do - npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts); - while (npkts == -1 && errno == EINTR); - - if (npkts < 1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - return; - for (i = 0, q = QUEUE_HEAD(&handle->write_queue); - i < pkts && q != &handle->write_queue; - ++i, q = QUEUE_HEAD(&handle->write_queue)) { - assert(q != NULL); - req = QUEUE_DATA(q, uv_udp_send_t, queue); - assert(req != NULL); - - req->status = UV__ERR(errno); - QUEUE_REMOVE(&req->queue); - QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); - } - uv__io_feed(handle->loop, &handle->io_watcher); - return; - } - - /* Safety: npkts known to be >0 below. Hence cast from ssize_t - * to size_t safe. - */ - for (i = 0, q = QUEUE_HEAD(&handle->write_queue); - i < (size_t)npkts && q != &handle->write_queue; - ++i, q = QUEUE_HEAD(&handle->write_queue)) { - assert(q != NULL); - req = QUEUE_DATA(q, uv_udp_send_t, queue); - assert(req != NULL); - - req->status = req->bufs[0].len; - - /* Sending a datagram is an atomic operation: either all data - * is written or nothing is (and EMSGSIZE is raised). That is - * why we don't handle partial writes. Just pop the request - * off the write queue and onto the completed queue, done. - */ - QUEUE_REMOVE(&req->queue); - QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); - } - - /* couldn't batch everything, continue sending (jump to avoid stack growth) */ - if (!QUEUE_EMPTY(&handle->write_queue)) - goto write_queue_drain; - uv__io_feed(handle->loop, &handle->io_watcher); - return; -} -#endif - -static void uv__udp_sendmsg(uv_udp_t* handle) { - uv_udp_send_t* req; - struct msghdr h; - QUEUE* q; - ssize_t size; - -#if HAVE_MMSG - uv_once(&once, uv__udp_mmsg_init); - if (uv__sendmmsg_avail) { - uv__udp_sendmmsg(handle); - return; - } -#endif - - while (!QUEUE_EMPTY(&handle->write_queue)) { - q = QUEUE_HEAD(&handle->write_queue); - assert(q != NULL); - - req = QUEUE_DATA(q, uv_udp_send_t, queue); - assert(req != NULL); - - memset(&h, 0, sizeof h); - if (req->addr.ss_family == AF_UNSPEC) { - h.msg_name = NULL; - h.msg_namelen = 0; - } else { - h.msg_name = &req->addr; - if (req->addr.ss_family == AF_INET6) - h.msg_namelen = sizeof(struct sockaddr_in6); - else if (req->addr.ss_family == AF_INET) - h.msg_namelen = sizeof(struct sockaddr_in); - else if (req->addr.ss_family == AF_UNIX) - h.msg_namelen = sizeof(struct sockaddr_un); - else { - assert(0 && "unsupported address family"); - abort(); - } - } - h.msg_iov = (struct iovec*) req->bufs; - h.msg_iovlen = req->nbufs; - - do { - size = sendmsg(handle->io_watcher.fd, &h, 0); - } while (size == -1 && errno == EINTR); - - if (size == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - break; - } - - req->status = (size == -1 ? UV__ERR(errno) : size); - - /* Sending a datagram is an atomic operation: either all data - * is written or nothing is (and EMSGSIZE is raised). That is - * why we don't handle partial writes. Just pop the request - * off the write queue and onto the completed queue, done. - */ - QUEUE_REMOVE(&req->queue); - QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); - uv__io_feed(handle->loop, &handle->io_watcher); - } -} /* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional - * refinements for programs that use multicast. + * refinements for programs that use multicast. Therefore we preferentially + * set SO_REUSEPORT over SO_REUSEADDR here, but we set SO_REUSEPORT only + * when that socket option doesn't have the capability of load balancing. + * Otherwise, we fall back to SO_REUSEADDR. * - * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that - * are different from the BSDs: it _shares_ the port rather than steal it - * from the current listener. While useful, it's not something we can emulate - * on other platforms so we don't enable it. + * Linux as of 3.9, DragonflyBSD 3.6, AIX 7.2.5 have the SO_REUSEPORT socket + * option but with semantics that are different from the BSDs: it _shares_ + * the port rather than steals it from the current listener. While useful, + * it's not something we can emulate on other platforms so we don't enable it. * * zOS does not support getsockname with SO_REUSEPORT option when using * AF_UNIX. + * + * Solaris 11.4: SO_REUSEPORT will not load balance when SO_REUSEADDR + * is also set, but it's not valid for every socket type. */ -static int uv__set_reuse(int fd) { +static int uv__sock_reuseaddr(int fd) { int yes; yes = 1; @@ -501,7 +399,18 @@ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) return UV__ERR(errno); } -#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__) +#elif defined(SO_REUSEPORT) && defined(UV__SOLARIS_11_4) && UV__SOLARIS_11_4 + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) { + if (errno != ENOPROTOOPT) + return UV__ERR(errno); + /* Not all socket types accept SO_REUSEPORT. */ + errno = 0; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) + return UV__ERR(errno); + } +#elif defined(SO_REUSEPORT) && \ + !defined(__linux__) && !defined(__GNU__) && \ + !defined(__illumos__) && !defined(__DragonFly__) && !defined(_AIX73) if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) return UV__ERR(errno); #else @@ -544,7 +453,8 @@ int fd; /* Check for bad flags. */ - if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR)) + if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | + UV_UDP_REUSEPORT | UV_UDP_LINUX_RECVERR)) return UV_EINVAL; /* Cannot set IPv6-only mode on non-IPv6 socket. */ @@ -567,7 +477,13 @@ } if (flags & UV_UDP_REUSEADDR) { - err = uv__set_reuse(fd); + err = uv__sock_reuseaddr(fd); + if (err) + return err; + } + + if (flags & UV_UDP_REUSEPORT) { + err = uv__sock_reuseport(fd); if (err) return err; } @@ -723,7 +639,7 @@ } while (r == -1 && errno == EINTR); if (r == -1) { -#if defined(BSD) /* The macro BSD is from sys/param.h */ +#if defined(BSD) || defined(__QNX__) /* The macro BSD is from sys/param.h */ if (errno != EAFNOSUPPORT && errno != EINVAL) return UV__ERR(errno); #else @@ -760,11 +676,11 @@ empty_queue = (handle->send_queue_count == 0); uv__req_init(handle->loop, req, UV_UDP_SEND); - assert(addrlen <= sizeof(req->addr)); + assert(addrlen <= sizeof(req->u.storage)); if (addr == NULL) - req->addr.ss_family = AF_UNSPEC; + req->u.storage.ss_family = AF_UNSPEC; else - memcpy(&req->addr, addr, addrlen); + memcpy(&req->u.storage, addr, addrlen); req->send_cb = send_cb; req->handle = handle; req->nbufs = nbufs; @@ -774,14 +690,14 @@ req->bufs = uv__malloc(nbufs * sizeof(bufs[0])); if (req->bufs == NULL) { - uv__req_unregister(handle->loop, req); + uv__req_unregister(handle->loop); return UV_ENOMEM; } memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs); handle->send_queue_count++; - QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue); + uv__queue_insert_tail(&handle->write_queue, &req->queue); uv__handle_start(handle); if (empty_queue && !(handle->flags & UV_HANDLE_UDP_PROCESSING)) { @@ -791,7 +707,7 @@ * away. In such cases the `io_watcher` has to be queued for asynchronous * write. */ - if (!QUEUE_EMPTY(&handle->write_queue)) + if (!uv__queue_empty(&handle->write_queue)) uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); } else { uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); @@ -807,10 +723,9 @@ const struct sockaddr* addr, unsigned int addrlen) { int err; - struct msghdr h; - ssize_t size; - assert(nbufs > 0); + if (nbufs < 1) + return UV_EINVAL; /* already sending a message */ if (handle->send_queue_count != 0) @@ -824,24 +739,11 @@ assert(handle->flags & UV_HANDLE_UDP_CONNECTED); } - memset(&h, 0, sizeof h); - h.msg_name = (struct sockaddr*) addr; - h.msg_namelen = addrlen; - h.msg_iov = (struct iovec*) bufs; - h.msg_iovlen = nbufs; + err = uv__udp_sendmsg1(handle->io_watcher.fd, bufs, nbufs, addr); + if (err) + return err; - do { - size = sendmsg(handle->io_watcher.fd, &h, 0); - } while (size == -1 && errno == EINTR); - - if (size == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - return UV_EAGAIN; - else - return UV__ERR(errno); - } - - return size; + return uv__count_bufs(bufs, nbufs); } @@ -943,8 +845,9 @@ !defined(__NetBSD__) && \ !defined(__ANDROID__) && \ !defined(__DragonFly__) && \ - !defined(__QNX__) && \ - !defined(__GNU__) + (!defined(__APPLE__) || (MAC_OS_X_VERSION_MAX_ALLOWED >= 1070)) && \ + !defined(__GNU__) && \ + !defined(QNX_IOPKT) static int uv__udp_set_source_membership4(uv_udp_t* handle, const struct sockaddr_in* multicast_addr, const char* interface_addr, @@ -1058,28 +961,31 @@ handle->recv_cb = NULL; handle->send_queue_size = 0; handle->send_queue_count = 0; - uv__io_init(&handle->io_watcher, uv__udp_io, fd); - QUEUE_INIT(&handle->write_queue); - QUEUE_INIT(&handle->write_completed_queue); + uv__io_init(&handle->io_watcher, UV__UDP_IO, fd); + uv__queue_init(&handle->write_queue); + uv__queue_init(&handle->write_completed_queue); return 0; } int uv_udp_using_recvmmsg(const uv_udp_t* handle) { -#if HAVE_MMSG - if (handle->flags & UV_HANDLE_UDP_RECVMMSG) { - uv_once(&once, uv__udp_mmsg_init); - return uv__recvmmsg_avail; - } +#if defined(__linux__) || defined(__FreeBSD__) || \ + (defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) + if (handle->flags & UV_HANDLE_UDP_RECVMMSG) + return 1; #endif return 0; } -int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { +int uv_udp_open_ex(uv_udp_t* handle, uv_os_sock_t sock, unsigned int flags) { int err; + /* Check for bad flags. */ + if (flags & ~(UV_UDP_REUSEADDR | UV_UDP_REUSEPORT)) + return UV_EINVAL; + /* Check for already active socket. */ if (handle->io_watcher.fd != -1) return UV_EBUSY; @@ -1091,9 +997,17 @@ if (err) return err; - err = uv__set_reuse(sock); - if (err) - return err; + if (flags & UV_UDP_REUSEADDR) { + err = uv__sock_reuseaddr(sock); + if (err) + return err; + } + + if (flags & UV_UDP_REUSEPORT) { + err = uv__sock_reuseport(sock); + if (err) + return err; + } handle->io_watcher.fd = sock; if (uv__udp_is_connected(handle)) @@ -1103,6 +1017,15 @@ } +int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { + /* + * Keep backward compatibility, always set REUSEADDR. + * Refs: https://github.com/libuv/libuv/issues/4551 + */ + return uv_udp_open_ex(handle, sock, UV_UDP_REUSEADDR); +} + + int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, @@ -1136,8 +1059,9 @@ !defined(__NetBSD__) && \ !defined(__ANDROID__) && \ !defined(__DragonFly__) && \ - !defined(__QNX__) && \ - !defined(__GNU__) + (!defined(__APPLE__) || (MAC_OS_X_VERSION_MAX_ALLOWED >= 1070)) && \ + !defined(__GNU__) && \ + !defined(QNX_IOPKT) int err; union uv__sockaddr mcast_addr; union uv__sockaddr src_addr; @@ -1420,3 +1344,195 @@ return 0; } + + +static int uv__udp_prep_pkt(struct msghdr* h, + const uv_buf_t* bufs, + const unsigned int nbufs, + const struct sockaddr* addr) { + memset(h, 0, sizeof(*h)); + h->msg_name = (void*) addr; + h->msg_iov = (void*) bufs; + h->msg_iovlen = nbufs; + if (addr == NULL) + return 0; + switch (addr->sa_family) { + case AF_INET: + h->msg_namelen = sizeof(struct sockaddr_in); + return 0; + case AF_INET6: + h->msg_namelen = sizeof(struct sockaddr_in6); + return 0; + case AF_UNIX: + h->msg_namelen = sizeof(struct sockaddr_un); + return 0; + case AF_UNSPEC: + h->msg_name = NULL; + return 0; + } + return UV_EINVAL; +} + + +static int uv__udp_sendmsg1(int fd, + const uv_buf_t* bufs, + unsigned int nbufs, + const struct sockaddr* addr) { + struct msghdr h; + int r; + + if ((r = uv__udp_prep_pkt(&h, bufs, nbufs, addr))) + return r; + + do + r = sendmsg(fd, &h, 0); + while (r == -1 && errno == EINTR); + + if (r < 0) { + r = UV__ERR(errno); + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + r = UV_EAGAIN; + return r; + } + + /* UDP sockets don't EOF so we don't have to handle r=0 specially, + * that only happens when the input was a zero-sized buffer. + */ + return 0; +} + + +static int uv__udp_sendmsgv(int fd, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/]) { + unsigned int i; + int nsent; + int r; + + r = 0; + nsent = 0; + +#if defined(__linux__) || defined(__FreeBSD__) || \ + (defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) || \ + (defined(__sun__) && defined(MSG_WAITFORONE)) || defined(__QNX__) + if (count > 1) { + for (i = 0; i < count; /*empty*/) { + struct mmsghdr m[20]; + unsigned int n; + + for (n = 0; i < count && n < ARRAY_SIZE(m); i++, n++) + if ((r = uv__udp_prep_pkt(&m[n].msg_hdr, bufs[i], nbufs[i], addrs[i]))) + goto exit; + + do +#if defined(__APPLE__) + r = sendmsg_x(fd, m, n, MSG_DONTWAIT); +#else + r = sendmmsg(fd, m, n, 0); +#endif + while (r == -1 && errno == EINTR); + + if (r < 1) + goto exit; + + nsent += r; + i += r; + } + + goto exit; + } +#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || + * (defined(__sun__) && defined(MSG_WAITFORONE)) || defined(__QNX__) + */ + + for (i = 0; i < count; i++, nsent++) + if ((r = uv__udp_sendmsg1(fd, bufs[i], nbufs[i], addrs[i]))) + goto exit; /* goto to avoid unused label warning. */ + +exit: + + if (nsent > 0) + return nsent; + + if (r < 0) { + r = UV__ERR(errno); + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + r = UV_EAGAIN; + } + + return r; +} + + +static void uv__udp_sendmsg(uv_udp_t* handle) { + enum { N = 20 }; + struct sockaddr* addrs[N]; + unsigned int nbufs[N]; + uv_buf_t* bufs[N]; + struct uv__queue* q; + uv_udp_send_t* req; + int n; + + if (uv__queue_empty(&handle->write_queue)) + return; + +again: + n = 0; + q = uv__queue_head(&handle->write_queue); + do { + req = uv__queue_data(q, uv_udp_send_t, queue); + addrs[n] = &req->u.addr; + nbufs[n] = req->nbufs; + bufs[n] = req->bufs; + q = uv__queue_next(q); + n++; + } while (n < N && q != &handle->write_queue); + + n = uv__udp_sendmsgv(handle->io_watcher.fd, n, bufs, nbufs, addrs); + while (n > 0) { + q = uv__queue_head(&handle->write_queue); + req = uv__queue_data(q, uv_udp_send_t, queue); + req->status = uv__count_bufs(req->bufs, req->nbufs); + uv__queue_remove(&req->queue); + uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); + n--; + } + + if (n == 0) { + if (uv__queue_empty(&handle->write_queue)) + goto feed; + goto again; + } + + if (n == UV_EAGAIN) + return; + + /* Register the error against first request in queue because that + * is the request that uv__udp_sendmsgv tried but failed to send, + * because if it did send any requests, it won't return an error. + */ + q = uv__queue_head(&handle->write_queue); + req = uv__queue_data(q, uv_udp_send_t, queue); + req->status = n; + uv__queue_remove(&req->queue); + uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); +feed: + uv__io_feed(handle->loop, &handle->io_watcher); +} + + +int uv__udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/]) { + int fd; + + fd = handle->io_watcher.fd; + if (fd == -1) + return UV_EINVAL; + + return uv__udp_sendmsgv(fd, count, bufs, nbufs, addrs); +}
diff --git a/Utilities/cmlibuv/src/uv-common.c b/Utilities/cmlibuv/src/uv-common.c index d8f7b0e..050bc9d 100644 --- a/Utilities/cmlibuv/src/uv-common.c +++ b/Utilities/cmlibuv/src/uv-common.c
@@ -128,6 +128,39 @@ return 0; } + +void uv_os_free_passwd(uv_passwd_t* pwd) { + if (pwd == NULL) + return; + + /* On unix, the memory for name, shell, and homedir are allocated in a single + * uv__malloc() call. The base of the pointer is stored in pwd->username, so + * that is the field that needs to be freed. + */ + uv__free(pwd->username); +#ifdef _WIN32 + uv__free(pwd->homedir); +#endif + pwd->username = NULL; + pwd->shell = NULL; + pwd->homedir = NULL; +} + + +void uv_os_free_group(uv_group_t *grp) { + if (grp == NULL) + return; + + /* The memory for is allocated in a single uv__malloc() call. The base of the + * pointer is stored in grp->members, so that is the only field that needs to + * be freed. + */ + uv__free(grp->members); + grp->members = NULL; + grp->groupname = NULL; +} + + #define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t); size_t uv_handle_size(uv_handle_type type) { @@ -482,6 +515,25 @@ } +int uv_udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/], + unsigned int flags) { + if (count < 1) + return UV_EINVAL; + + if (flags != 0) + return UV_EINVAL; + + if (handle->send_queue_count > 0) + return UV_EAGAIN; + + return uv__udp_try_send2(handle, count, bufs, nbufs, addrs); +} + + int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) { @@ -502,17 +554,17 @@ #endif void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) { - QUEUE queue; - QUEUE* q; + struct uv__queue queue; + struct uv__queue* q; uv_handle_t* h; - QUEUE_MOVE(&loop->handle_queue, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - h = QUEUE_DATA(q, uv_handle_t, handle_queue); + uv__queue_move(&loop->handle_queue, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_handle_t, handle_queue); - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&loop->handle_queue, q); + uv__queue_remove(q); + uv__queue_insert_tail(&loop->handle_queue, q); if (h->flags & UV_HANDLE_INTERNAL) continue; walk_cb(h, arg); @@ -522,14 +574,22 @@ static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) { const char* type; - QUEUE* q; + struct uv__queue* q; uv_handle_t* h; - if (loop == NULL) - loop = uv_default_loop(); + if (stream == NULL) + stream = stderr; - QUEUE_FOREACH(q, &loop->handle_queue) { - h = QUEUE_DATA(q, uv_handle_t, handle_queue); + if (loop == NULL) { + loop = uv_default_loop(); + if (loop == NULL) { + fprintf(stream, "uv_default_loop() failed\n"); + return; + } + } + + uv__queue_foreach(q, &loop->handle_queue) { + h = uv__queue_data(q, uv_handle_t, handle_queue); if (only_active && !uv__is_active(h)) continue; @@ -610,6 +670,9 @@ int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) { size_t required_len; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + if (!uv__is_active(handle)) { *size = 0; return UV_EINVAL; @@ -652,14 +715,22 @@ void uv__fs_scandir_cleanup(uv_fs_t* req) { uv__dirent_t** dents; + unsigned int* nbufs; + unsigned int i; + unsigned int n; - unsigned int* nbufs = uv__get_nbufs(req); + if (req->result >= 0) { + dents = req->ptr; + nbufs = uv__get_nbufs(req); - dents = req->ptr; - if (*nbufs > 0 && *nbufs != (unsigned int) req->result) - (*nbufs)--; - for (; *nbufs < (unsigned int) req->result; (*nbufs)++) - uv__fs_scandir_free(dents[*nbufs]); + i = 0; + if (*nbufs > 0) + i = *nbufs - 1; + + n = (unsigned int) req->result; + for (; i < n; i++) + uv__fs_scandir_free(dents[i]); + } uv__fs_scandir_free(req->ptr); req->ptr = NULL; @@ -807,7 +878,7 @@ int uv_loop_close(uv_loop_t* loop) { - QUEUE* q; + struct uv__queue* q; uv_handle_t* h; #ifndef NDEBUG void* saved_data; @@ -816,8 +887,8 @@ if (uv__has_active_reqs(loop)) return UV_EBUSY; - QUEUE_FOREACH(q, &loop->handle_queue) { - h = QUEUE_DATA(q, uv_handle_t, handle_queue); + uv__queue_foreach(q, &loop->handle_queue) { + h = uv__queue_data(q, uv_handle_t, handle_queue); if (!(h->flags & UV_HANDLE_INTERNAL)) return UV_EBUSY; } @@ -881,12 +952,17 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { +#ifdef __linux__ + (void) &count; + uv__free(cpu_infos); +#else int i; for (i = 0; i < count; i++) uv__free(cpu_infos[i].model); uv__free(cpu_infos); +#endif /* __linux__ */ } @@ -900,7 +976,7 @@ void uv_library_shutdown(void) { static int was_shutdown; - if (uv__load_relaxed(&was_shutdown)) + if (uv__exchange_int_relaxed(&was_shutdown, 1)) return; uv__process_title_cleanup(); @@ -911,7 +987,6 @@ #else uv__threadpool_cleanup(); #endif - uv__store_relaxed(&was_shutdown, 1); } @@ -957,6 +1032,15 @@ } +int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics) { + memcpy(metrics, + &uv__get_loop_metrics(loop)->metrics, + sizeof(*metrics)); + + return 0; +} + + uint64_t uv_metrics_idle_time(uv_loop_t* loop) { uv__loop_metrics_t* loop_metrics; uint64_t entry_time; @@ -972,3 +1056,11 @@ idle_time += uv_hrtime() - entry_time; return idle_time; } + +/* OS390 needs a different implementation, already provided in os390.c. */ +#ifndef __MVS__ +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + uv__free(addresses); +} +#endif /* !__MVS__ */
diff --git a/Utilities/cmlibuv/src/uv-common.h b/Utilities/cmlibuv/src/uv-common.h index 6001b0c..5452a54 100644 --- a/Utilities/cmlibuv/src/uv-common.h +++ b/Utilities/cmlibuv/src/uv-common.h
@@ -30,18 +30,18 @@ #include <assert.h> #include <stdarg.h> #include <stddef.h> - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include <stdint.h> -#endif +#include <stdint.h> +#include <string.h> #include "uv.h" #include "uv/tree.h" #include "queue.h" #include "strscpy.h" +#ifndef _MSC_VER +# include <stdatomic.h> +#endif + #if EDOM > 0 # define UV__ERR(x) (-(x)) #else @@ -53,19 +53,25 @@ #endif #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define ARRAY_END(a) ((a) + ARRAY_SIZE(a)) #define container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - offsetof(type, member))) +/* C11 defines static_assert to be a macro which calls _Static_assert. */ +#if defined(static_assert) +#define STATIC_ASSERT(expr) static_assert(expr, #expr) +#else #define STATIC_ASSERT(expr) \ void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)]) +#endif -#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 7) -#define uv__load_relaxed(p) __atomic_load_n(p, __ATOMIC_RELAXED) -#define uv__store_relaxed(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) +#ifdef _MSC_VER +#define uv__exchange_int_relaxed(p, v) \ + InterlockedExchangeNoFence((LONG volatile*)(p), v) #else -#define uv__load_relaxed(p) (*p) -#define uv__store_relaxed(p, v) do *p = v; while (0) +#define uv__exchange_int_relaxed(p, v) \ + atomic_exchange_explicit((_Atomic int*)(p), v, memory_order_relaxed) #endif #define UV__UDP_DGRAM_MAXSIZE (64 * 1024) @@ -83,9 +89,7 @@ /* Used by streams. */ UV_HANDLE_LISTENING = 0x00000040, UV_HANDLE_CONNECTION = 0x00000080, - UV_HANDLE_SHUTTING = 0x00000100, UV_HANDLE_SHUT = 0x00000200, - UV_HANDLE_READ_PARTIAL = 0x00000400, UV_HANDLE_READ_EOF = 0x00000800, /* Used by streams and UDP handles. */ @@ -121,7 +125,7 @@ /* Only used by uv_tty_t handles. */ UV_HANDLE_TTY_READABLE = 0x01000000, - UV_HANDLE_TTY_RAW = 0x02000000, + UV_HANDLE_UNUSED0 = 0x02000000, UV_HANDLE_TTY_SAVED_POSITION = 0x04000000, UV_HANDLE_TTY_SAVED_ATTRIBUTES = 0x08000000, @@ -136,6 +140,10 @@ UV_HANDLE_REAP = 0x10000000 }; +static inline int uv__is_raw_tty_mode(uv_tty_mode_t m) { + return m == UV_TTY_MODE_RAW || m == UV_TTY_MODE_RAW_VT; +} + int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); void uv__loop_close(uv_loop_t* loop); @@ -187,6 +195,12 @@ const struct sockaddr* addr, unsigned int addrlen); +int uv__udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/]); + int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb, uv_udp_recv_cb recv_cb); @@ -229,13 +243,13 @@ #define uv__has_active_reqs(loop) \ ((loop)->active_reqs.count > 0) -#define uv__req_register(loop, req) \ +#define uv__req_register(loop) \ do { \ (loop)->active_reqs.count++; \ } \ while (0) -#define uv__req_unregister(loop, req) \ +#define uv__req_unregister(loop) \ do { \ assert(uv__has_active_reqs(loop)); \ (loop)->active_reqs.count--; \ @@ -263,6 +277,14 @@ #define uv__is_closing(h) \ (((h)->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)) != 0) +#if defined(_WIN32) +# define uv__is_stream_shutting(h) \ + (h->stream.conn.shutdown_req != NULL) +#else +# define uv__is_stream_shutting(h) \ + (h->shutdown_req != NULL) +#endif + #define uv__handle_start(h) \ do { \ if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break; \ @@ -311,7 +333,7 @@ (h)->loop = (loop_); \ (h)->type = (type_); \ (h)->flags = UV_HANDLE_REF; /* Ref the loop when active. */ \ - QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue); \ + uv__queue_insert_tail(&(loop_)->handle_queue, &(h)->handle_queue); \ uv__handle_platform_init(h); \ } \ while (0) @@ -337,7 +359,7 @@ #define uv__req_init(loop, req, typ) \ do { \ UV_REQ_INIT(req, typ); \ - uv__req_register(loop, req); \ + uv__req_register(loop); \ } \ while (0) @@ -347,6 +369,21 @@ #define uv__get_loop_metrics(loop) \ (&uv__get_internal_fields(loop)->loop_metrics) +#define uv__metrics_inc_loop_count(loop) \ + do { \ + uv__get_loop_metrics(loop)->metrics.loop_count++; \ + } while (0) + +#define uv__metrics_inc_events(loop, e) \ + do { \ + uv__get_loop_metrics(loop)->metrics.events += (e); \ + } while (0) + +#define uv__metrics_inc_events_waiting(loop, e) \ + do { \ + uv__get_loop_metrics(loop)->metrics.events_waiting += (e); \ + } while (0) + /* Allocator prototypes */ void *uv__calloc(size_t count, size_t size); char *uv__strdup(const char* s); @@ -360,6 +397,7 @@ typedef struct uv__loop_internal_fields_s uv__loop_internal_fields_t; struct uv__loop_metrics_s { + uv_metrics_t metrics; uint64_t provider_entry_time; uint64_t provider_idle_time; uv_mutex_t lock; @@ -368,9 +406,68 @@ void uv__metrics_update_idle_time(uv_loop_t* loop); void uv__metrics_set_provider_entry_time(uv_loop_t* loop); +#ifdef __linux__ +struct uv__iou { + uint32_t* sqhead; + uint32_t* sqtail; + uint32_t sqmask; + uint32_t* sqflags; + uint32_t* cqhead; + uint32_t* cqtail; + uint32_t cqmask; + void* sq; /* pointer to munmap() on event loop teardown */ + void* cqe; /* pointer to array of struct uv__io_uring_cqe */ + void* sqe; /* pointer to array of struct uv__io_uring_sqe */ + size_t sqlen; + size_t cqlen; + size_t maxlen; + size_t sqelen; + int ringfd; + uint32_t in_flight; +}; +#endif /* __linux__ */ + struct uv__loop_internal_fields_s { unsigned int flags; uv__loop_metrics_t loop_metrics; + int current_timeout; +#ifdef __linux__ + struct uv__iou ctl; + struct uv__iou iou; + void* inv; /* used by uv__platform_invalidate_fd() */ +#endif /* __linux__ */ }; +#if defined(_WIN32) +# define UV_PTHREAD_MAX_NAMELEN_NP 32767 +#elif defined(__APPLE__) +# define UV_PTHREAD_MAX_NAMELEN_NP 64 +#elif defined(__NetBSD__) || defined(__illumos__) +# define UV_PTHREAD_MAX_NAMELEN_NP PTHREAD_MAX_NAMELEN_NP +#elif defined (__linux__) +# define UV_PTHREAD_MAX_NAMELEN_NP 16 +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +# define UV_PTHREAD_MAX_NAMELEN_NP (MAXCOMLEN + 1) +#else +# define UV_PTHREAD_MAX_NAMELEN_NP 16 +#endif + +/* Open-coded so downstream users don't have to link libm. */ +static inline int uv__isinf(double d) { + uint64_t v; + + STATIC_ASSERT(sizeof(v) == sizeof(d)); + memcpy(&v, &d, sizeof(v)); + return (v << 1 >> 53) == 2047 && !(v << 12); +} + +/* Open-coded so downstream users don't have to link libm. */ +static inline int uv__isnan(double d) { + uint64_t v; + + STATIC_ASSERT(sizeof(v) == sizeof(d)); + memcpy(&v, &d, sizeof(v)); + return (v << 1 >> 53) == 2047 && !!(v << 12); +} + #endif /* UV_COMMON_H_ */
diff --git a/Utilities/cmlibuv/src/win/async.c b/Utilities/cmlibuv/src/win/async.c index b904676..4c2cd26 100644 --- a/Utilities/cmlibuv/src/win/async.c +++ b/Utilities/cmlibuv/src/win/async.c
@@ -23,10 +23,28 @@ #include "uv.h" #include "internal.h" -#include "atomicops-inl.h" #include "handle-inl.h" #include "req-inl.h" +#ifdef _MSC_VER /* MSVC */ + +/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less + * efficient than InterlockedExchange, but InterlockedExchange8 does not exist, + * and interlocked operations on larger targets might require the target to be + * aligned. */ +#pragma intrinsic(_InterlockedOr8) + +static char uv__atomic_exchange_set(char volatile* target) { + return _InterlockedOr8(target, 1); +} + +#else /* GCC, Clang in mingw mode */ + +static char uv__atomic_exchange_set(char volatile* target) { + return __sync_fetch_and_or(target, 1); +} + +#endif /* _MSC_VER */ void uv__async_endgame(uv_loop_t* loop, uv_async_t* handle) { if (handle->flags & UV_HANDLE_CLOSING &&
diff --git a/Utilities/cmlibuv/src/win/atomicops-inl.h b/Utilities/cmlibuv/src/win/atomicops-inl.h deleted file mode 100644 index 2f984c6..0000000 --- a/Utilities/cmlibuv/src/win/atomicops-inl.h +++ /dev/null
@@ -1,61 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef UV_WIN_ATOMICOPS_INL_H_ -#define UV_WIN_ATOMICOPS_INL_H_ - -#include "uv.h" -#include "internal.h" - - -/* Atomic set operation on char */ -#ifdef _MSC_VER /* MSVC */ - -/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less - * efficient than InterlockedExchange, but InterlockedExchange8 does not exist, - * and interlocked operations on larger targets might require the target to be - * aligned. */ -#pragma intrinsic(_InterlockedOr8) - -static char INLINE uv__atomic_exchange_set(char volatile* target) { - return _InterlockedOr8(target, 1); -} - -#else /* GCC, Clang in mingw mode */ - -static inline char uv__atomic_exchange_set(char volatile* target) { -#if defined(__i386__) || defined(__x86_64__) - /* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ - const char one = 1; - char old_value; - __asm__ __volatile__ ("lock xchgb %0, %1\n\t" - : "=r"(old_value), "=m"(*target) - : "0"(one), "m"(*target) - : "memory"); - return old_value; -#else - return __sync_fetch_and_or(target, 1); -#endif -} - -#endif - -#endif /* UV_WIN_ATOMICOPS_INL_H_ */
diff --git a/Utilities/cmlibuv/src/win/core.c b/Utilities/cmlibuv/src/win/core.c index 67af93e..317238f 100644 --- a/Utilities/cmlibuv/src/win/core.c +++ b/Utilities/cmlibuv/src/win/core.c
@@ -114,7 +114,7 @@ failed_loops_realloc: uv_mutex_unlock(&uv__loops_lock); - return ERROR_OUTOFMEMORY; + return UV_ENOMEM; } @@ -245,6 +245,9 @@ err = uv_mutex_init(&lfields->loop_metrics.lock); if (err) goto fail_metrics_mutex_init; + memset(&lfields->loop_metrics.metrics, + 0, + sizeof(lfields->loop_metrics.metrics)); /* To prevent uninitialized memory access, loop->time must be initialized * to zero before calling uv_update_time for the first time. @@ -252,8 +255,8 @@ loop->time = 0; uv_update_time(loop); - QUEUE_INIT(&loop->wq); - QUEUE_INIT(&loop->handle_queue); + uv__queue_init(&loop->wq); + uv__queue_init(&loop->handle_queue); loop->active_reqs.count = 0; loop->active_handles = 0; @@ -279,9 +282,6 @@ memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets); - loop->active_tcp_streams = 0; - loop->active_udp_streams = 0; - loop->timer_counter = 0; loop->stop_flag = 0; @@ -358,7 +358,7 @@ } uv_mutex_lock(&loop->wq_mutex); - assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!"); + assert(uv__queue_empty(&loop->wq) && "thread pool work queue not empty!"); assert(!uv__has_active_reqs(loop)); uv_mutex_unlock(&loop->wq_mutex); uv_mutex_destroy(&loop->wq_mutex); @@ -423,99 +423,24 @@ } -static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) { - DWORD bytes; - ULONG_PTR key; - OVERLAPPED* overlapped; - uv_req_t* req; - int repeat; - uint64_t timeout_time; - uint64_t user_timeout; - int reset_timeout; - - timeout_time = loop->time + timeout; - - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { - reset_timeout = 1; - user_timeout = timeout; - timeout = 0; - } else { - reset_timeout = 0; - } - - for (repeat = 0; ; repeat++) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - GetQueuedCompletionStatus(loop->iocp, - &bytes, - &key, - &overlapped, - timeout); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - /* Placed here because on success the loop will break whether there is an - * empty package or not, or if GetQueuedCompletionStatus returned early then - * the timeout will be updated and the loop will run again. In either case - * the idle time will need to be updated. - */ - uv__metrics_update_idle_time(loop); - - if (overlapped) { - /* Package was dequeued */ - req = uv__overlapped_to_req(overlapped); - uv__insert_pending_req(loop, req); - - /* Some time might have passed waiting for I/O, - * so update the loop time here. - */ - uv_update_time(loop); - } else if (GetLastError() != WAIT_TIMEOUT) { - /* Serious error */ - uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); - } else if (timeout > 0) { - /* GetQueuedCompletionStatus can occasionally return a little early. - * Make sure that the desired timeout target time is reached. - */ - uv_update_time(loop); - if (timeout_time > loop->time) { - timeout = (DWORD)(timeout_time - loop->time); - /* The first call to GetQueuedCompletionStatus should return very - * close to the target time and the second should reach it, but - * this is not stated in the documentation. To make sure a busy - * loop cannot happen, the timeout is increased exponentially - * starting on the third round. - */ - timeout += repeat ? (1 << (repeat - 1)) : 0; - continue; - } - } - break; - } -} - - static void uv__poll(uv_loop_t* loop, DWORD timeout) { + uv__loop_internal_fields_t* lfields; BOOL success; uv_req_t* req; OVERLAPPED_ENTRY overlappeds[128]; + OVERLAPPED* overlapped; ULONG count; ULONG i; int repeat; uint64_t timeout_time; uint64_t user_timeout; + uint64_t actual_timeout; int reset_timeout; + lfields = uv__get_internal_fields(loop); timeout_time = loop->time + timeout; - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -524,18 +449,26 @@ } for (repeat = 0; ; repeat++) { + actual_timeout = timeout; + /* Only need to set the provider_entry_time if timeout != 0. The function * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. */ if (timeout != 0) uv__metrics_set_provider_entry_time(loop); - success = pGetQueuedCompletionStatusEx(loop->iocp, - overlappeds, - ARRAY_SIZE(overlappeds), - &count, - timeout, - FALSE); + /* Store the current timeout in a location that's globally accessible so + * other locations like uv__work_done() can determine whether the queue + * of events in the callback were waiting when poll was called. + */ + lfields->current_timeout = timeout; + + success = GetQueuedCompletionStatusEx(loop->iocp, + overlappeds, + ARRAY_SIZE(overlappeds), + &count, + timeout, + FALSE); if (reset_timeout != 0) { timeout = user_timeout; @@ -543,9 +476,9 @@ } /* Placed here because on success the loop will break whether there is an - * empty package or not, or if GetQueuedCompletionStatus returned early then - * the timeout will be updated and the loop will run again. In either case - * the idle time will need to be updated. + * empty package or not, or if GetQueuedCompletionStatusEx returned early + * then the timeout will be updated and the loop will run again. In either + * case the idle time will need to be updated. */ uv__metrics_update_idle_time(loop); @@ -555,7 +488,12 @@ * meant only to wake us up. */ if (overlappeds[i].lpOverlapped) { - req = uv__overlapped_to_req(overlappeds[i].lpOverlapped); + uv__metrics_inc_events(loop, 1); + if (actual_timeout == 0) + uv__metrics_inc_events_waiting(loop, 1); + + overlapped = overlappeds[i].lpOverlapped; + req = container_of(overlapped, uv_req_t, u.io.overlapped); uv__insert_pending_req(loop, req); } } @@ -589,6 +527,177 @@ } +#define DELEGATE_STREAM_REQ(loop, req, method, handle_at) \ + do { \ + switch (((uv_handle_t*) (req)->handle_at)->type) { \ + case UV_TCP: \ + uv__process_tcp_##method##_req(loop, \ + (uv_tcp_t*) ((req)->handle_at), \ + req); \ + break; \ + \ + case UV_NAMED_PIPE: \ + uv__process_pipe_##method##_req(loop, \ + (uv_pipe_t*) ((req)->handle_at), \ + req); \ + break; \ + \ + case UV_TTY: \ + uv__process_tty_##method##_req(loop, \ + (uv_tty_t*) ((req)->handle_at), \ + req); \ + break; \ + \ + default: \ + assert(0); \ + } \ + } while (0) + + +static void uv__process_reqs(uv_loop_t* loop) { + uv_req_t* req; + uv_req_t* first; + uv_req_t* next; + + if (loop->pending_reqs_tail == NULL) + return; + + first = loop->pending_reqs_tail->next_req; + next = first; + loop->pending_reqs_tail = NULL; + + while (next != NULL) { + req = next; + next = req->next_req != first ? req->next_req : NULL; + + switch (req->type) { + case UV_READ: + DELEGATE_STREAM_REQ(loop, req, read, data); + break; + + case UV_WRITE: + DELEGATE_STREAM_REQ(loop, (uv_write_t*) req, write, handle); + break; + + case UV_ACCEPT: + DELEGATE_STREAM_REQ(loop, req, accept, data); + break; + + case UV_CONNECT: + DELEGATE_STREAM_REQ(loop, (uv_connect_t*) req, connect, handle); + break; + + case UV_SHUTDOWN: + DELEGATE_STREAM_REQ(loop, (uv_shutdown_t*) req, shutdown, handle); + break; + + case UV_UDP_RECV: + uv__process_udp_recv_req(loop, (uv_udp_t*) req->data, req); + break; + + case UV_UDP_SEND: + uv__process_udp_send_req(loop, + ((uv_udp_send_t*) req)->handle, + (uv_udp_send_t*) req); + break; + + case UV_WAKEUP: + uv__process_async_wakeup_req(loop, (uv_async_t*) req->data, req); + break; + + case UV_SIGNAL_REQ: + uv__process_signal_req(loop, (uv_signal_t*) req->data, req); + break; + + case UV_POLL_REQ: + uv__process_poll_req(loop, (uv_poll_t*) req->data, req); + break; + + case UV_PROCESS_EXIT: + uv__process_proc_exit(loop, (uv_process_t*) req->data); + break; + + case UV_FS_EVENT_REQ: + uv__process_fs_event_req(loop, req, (uv_fs_event_t*) req->data); + break; + + default: + assert(0); + } + } +} + +#undef DELEGATE_STREAM_REQ + +static void uv__process_endgames(uv_loop_t* loop) { + uv_handle_t* handle; + + while (loop->endgame_handles) { + handle = loop->endgame_handles; + loop->endgame_handles = handle->endgame_next; + + handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED; + + switch (handle->type) { + case UV_TCP: + uv__tcp_endgame(loop, (uv_tcp_t*) handle); + break; + + case UV_NAMED_PIPE: + uv__pipe_endgame(loop, (uv_pipe_t*) handle); + break; + + case UV_TTY: + uv__tty_endgame(loop, (uv_tty_t*) handle); + break; + + case UV_UDP: + uv__udp_endgame(loop, (uv_udp_t*) handle); + break; + + case UV_POLL: + uv__poll_endgame(loop, (uv_poll_t*) handle); + break; + + case UV_TIMER: + uv__timer_close((uv_timer_t*) handle); + uv__handle_close(handle); + break; + + case UV_PREPARE: + case UV_CHECK: + case UV_IDLE: + uv__loop_watcher_endgame(loop, handle); + break; + + case UV_ASYNC: + uv__async_endgame(loop, (uv_async_t*) handle); + break; + + case UV_SIGNAL: + uv__signal_endgame(loop, (uv_signal_t*) handle); + break; + + case UV_PROCESS: + uv__process_endgame(loop, (uv_process_t*) handle); + break; + + case UV_FS_EVENT: + uv__fs_event_endgame(loop, (uv_fs_event_t*) handle); + break; + + case UV_FS_POLL: + uv__fs_poll_endgame(loop, (uv_fs_poll_t*) handle); + break; + + default: + assert(0); + break; + } + } +} + + int uv_run(uv_loop_t *loop, uv_run_mode mode) { DWORD timeout; int r; @@ -598,10 +707,16 @@ if (!r) uv_update_time(loop); - while (r != 0 && loop->stop_flag == 0) { + /* Maintain backwards compatibility by processing timers before entering the + * while loop for UV_RUN_DEFAULT. Otherwise timers only need to be executed + * once, which should be done after polling in order to maintain proper + * execution order of the conceptual event loop. */ + if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) { uv_update_time(loop); uv__run_timers(loop); + } + while (r != 0 && loop->stop_flag == 0) { can_sleep = loop->pending_reqs_tail == NULL && loop->idle_handles == NULL; uv__process_reqs(loop); @@ -612,10 +727,9 @@ if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); - if (pGetQueuedCompletionStatusEx) - uv__poll(loop, timeout); - else - uv__poll_wine(loop, timeout); + uv__metrics_inc_loop_count(loop); + + uv__poll(loop, timeout); /* Process immediate callbacks (e.g. write_cb) a small fixed number of * times to avoid loop starvation.*/ @@ -632,18 +746,8 @@ uv__check_invoke(loop); uv__process_endgames(loop); - if (mode == UV_RUN_ONCE) { - /* UV_RUN_ONCE implies forward progress: at least one callback must have - * been invoked when it returns. uv__io_poll() can return without doing - * I/O (meaning: no callbacks) when its timeout expires - which means we - * have pending timers that satisfy the forward progress constraint. - * - * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from - * the check. - */ - uv_update_time(loop); - uv__run_timers(loop); - } + uv_update_time(loop); + uv__run_timers(loop); r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) @@ -750,3 +854,26 @@ return 0; } + +void uv__insert_pending_req(uv_loop_t* loop, uv_req_t* req) { + req->next_req = NULL; + if (loop->pending_reqs_tail) { +#ifdef _DEBUG + /* Ensure the request is not already in the queue, or the queue + * will get corrupted. + */ + uv_req_t* current = loop->pending_reqs_tail; + do { + assert(req != current); + current = current->next_req; + } while (current != loop->pending_reqs_tail); +#endif + + req->next_req = loop->pending_reqs_tail->next_req; + loop->pending_reqs_tail->next_req = req; + loop->pending_reqs_tail = req; + } else { + req->next_req = req; + loop->pending_reqs_tail = req; + } +}
diff --git a/Utilities/cmlibuv/src/win/dl.c b/Utilities/cmlibuv/src/win/dl.c index 676be4d..7880c95 100644 --- a/Utilities/cmlibuv/src/win/dl.c +++ b/Utilities/cmlibuv/src/win/dl.c
@@ -27,18 +27,17 @@ int uv_dlopen(const char* filename, uv_lib_t* lib) { WCHAR filename_w[32768]; + ssize_t r; lib->handle = NULL; lib->errmsg = NULL; - if (!MultiByteToWideChar(CP_UTF8, - 0, - filename, - -1, - filename_w, - ARRAY_SIZE(filename_w))) { - return uv__dlerror(lib, filename, GetLastError()); - } + r = uv_wtf8_length_as_utf16(filename); + if (r < 0) + return uv__dlerror(lib, filename, ERROR_NO_UNICODE_TRANSLATION); + if ((size_t) r > ARRAY_SIZE(filename_w)) + return uv__dlerror(lib, filename, ERROR_INSUFFICIENT_BUFFER); + uv_wtf8_to_utf16(filename, filename_w, r); lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (lib->handle == NULL) {
diff --git a/Utilities/cmlibuv/src/win/error.c b/Utilities/cmlibuv/src/win/error.c index 3a269da..7abf906 100644 --- a/Utilities/cmlibuv/src/win/error.c +++ b/Utilities/cmlibuv/src/win/error.c
@@ -69,7 +69,6 @@ } switch (sys_errno) { - case ERROR_NOACCESS: return UV_EACCES; case WSAEACCES: return UV_EACCES; case ERROR_ELEVATION_REQUIRED: return UV_EACCES; case ERROR_CANT_ACCESS_FILE: return UV_EACCES; @@ -78,6 +77,7 @@ case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT; case WSAEWOULDBLOCK: return UV_EAGAIN; + case ERROR_NO_DATA: return UV_EAGAIN; case WSAEALREADY: return UV_EALREADY; case ERROR_INVALID_FLAGS: return UV_EBADF; case ERROR_INVALID_HANDLE: return UV_EBADF; @@ -95,7 +95,7 @@ case WSAECONNRESET: return UV_ECONNRESET; case ERROR_ALREADY_EXISTS: return UV_EEXIST; case ERROR_FILE_EXISTS: return UV_EEXIST; - case ERROR_BUFFER_OVERFLOW: return UV_EFAULT; + case ERROR_NOACCESS: return UV_EFAULT; case WSAEFAULT: return UV_EFAULT; case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH; case WSAEHOSTUNREACH: return UV_EHOSTUNREACH; @@ -126,6 +126,7 @@ case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; case WSAEMFILE: return UV_EMFILE; case WSAEMSGSIZE: return UV_EMSGSIZE; + case ERROR_BUFFER_OVERFLOW: return UV_ENAMETOOLONG; case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG; case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH; case WSAENETUNREACH: return UV_ENETUNREACH; @@ -157,7 +158,6 @@ case ERROR_ACCESS_DENIED: return UV_EPERM; case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM; case ERROR_BAD_PIPE: return UV_EPIPE; - case ERROR_NO_DATA: return UV_EPIPE; case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE; case WSAESHUTDOWN: return UV_EPIPE; case WSAEPROTONOSUPPORT: return UV_EPROTONOSUPPORT; @@ -168,6 +168,16 @@ case ERROR_INVALID_FUNCTION: return UV_EISDIR; case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG; case WSAESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT; + case ERROR_BAD_EXE_FORMAT: return UV_EFTYPE; default: return UV_UNKNOWN; } } + +int uv_translate_write_sys_error(int sys_errno) { + switch (sys_errno) { + case ERROR_BROKEN_PIPE: return UV_EPIPE; + case ERROR_NO_DATA: return UV_EPIPE; + default: + return uv_translate_sys_error(sys_errno); + } +}
diff --git a/Utilities/cmlibuv/src/win/fs-event.c b/Utilities/cmlibuv/src/win/fs-event.c index 6758c7c..fd77f2a 100644 --- a/Utilities/cmlibuv/src/win/fs-event.c +++ b/Utilities/cmlibuv/src/win/fs-event.c
@@ -114,7 +114,7 @@ } } - *file = wcsdup(filename); + *file = _wcsdup(filename); } else { if (dir) { *dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR)); @@ -157,14 +157,15 @@ uv_fs_event_cb cb, const char* path, unsigned int flags) { - int name_size, is_path_dir, size; - DWORD attr, last_error; - WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL; + int is_path_dir; + DWORD last_error; + WCHAR* dir, *pathw = NULL; DWORD short_path_buffer_len; WCHAR *short_path_buffer; - WCHAR* short_path, *long_path; + WCHAR* short_path = NULL; + HANDLE file_handle = INVALID_HANDLE_VALUE; + BY_HANDLE_FILE_INFORMATION info; - short_path = NULL; if (uv__is_active(handle)) return UV_EINVAL; @@ -176,61 +177,30 @@ uv__handle_start(handle); - /* Convert name to UTF16. */ + last_error = uv__convert_utf8_to_utf16(path, &pathw); + if (last_error) + goto error_uv; - name_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) * - sizeof(WCHAR); - pathw = (WCHAR*)uv__malloc(name_size); - if (!pathw) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } - - if (!MultiByteToWideChar(CP_UTF8, - 0, - path, - -1, - pathw, - name_size / sizeof(WCHAR))) { - return uv_translate_sys_error(GetLastError()); - } - - /* Determine whether path is a file or a directory. */ - attr = GetFileAttributesW(pathw); - if (attr == INVALID_FILE_ATTRIBUTES) { + file_handle = CreateFileW(pathw, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, + NULL); + if (file_handle == INVALID_HANDLE_VALUE) { last_error = GetLastError(); goto error; } - is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; + if (!GetFileInformationByHandle(file_handle, &info)) { + last_error = GetLastError(); + goto error; + } - if (is_path_dir) { - /* path is a directory, so that's the directory that we will watch. */ + is_path_dir = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; - /* Convert to long path. */ - size = GetLongPathNameW(pathw, NULL, 0); - - if (size) { - long_path = (WCHAR*)uv__malloc(size * sizeof(WCHAR)); - if (!long_path) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } - - size = GetLongPathNameW(pathw, long_path, size); - if (size) { - long_path[size] = '\0'; - } else { - uv__free(long_path); - long_path = NULL; - } - - if (long_path) { - uv__free(pathw); - pathw = long_path; - } - } - - dir_to_watch = pathw; - } else { + if (!is_path_dir) { /* * path is a file. So we split path into dir & file parts, and * watch the dir directory. @@ -265,26 +235,50 @@ goto error; } - dir_to_watch = dir; + uv__free(short_path); + short_path = NULL; uv__free(pathw); pathw = NULL; - } - handle->dir_handle = CreateFileW(dir_to_watch, - FILE_LIST_DIRECTORY, - FILE_SHARE_READ | FILE_SHARE_DELETE | - FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | - FILE_FLAG_OVERLAPPED, - NULL); - - if (dir) { + /* Open the containing directory and watch that instead. Events for + * other files are filtered out in uv__process_fs_event_req(). + * Not super efficient but c'est ça. + */ + CloseHandle(file_handle); + file_handle = CreateFileW(dir, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, + NULL); uv__free(dir); dir = NULL; + if (file_handle == INVALID_HANDLE_VALUE) { + last_error = GetLastError(); + goto error; + } + + if (!GetFileInformationByHandle(file_handle, &info)) { + last_error = GetLastError(); + goto error; + } + + /* Race with another process: directory foo in foo/bar was replaced + * with a file. Bail out with an error, we're not recursing upwards. + */ + if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + /* TODO(bnoordhuis) ERROR_DIRECTORY is translated to UV_ENOENT, + * there's currently nothing that maps to UV_ENOTDIR. + */ + last_error = ERROR_DIRECTORY; + goto error; + } } + handle->dir_handle = file_handle; + file_handle = INVALID_HANDLE_VALUE; + if (handle->dir_handle == INVALID_HANDLE_VALUE) { last_error = GetLastError(); goto error; @@ -333,6 +327,14 @@ return 0; error: + last_error = uv_translate_sys_error(last_error); + +error_uv: + if (file_handle != INVALID_HANDLE_VALUE) { + CloseHandle(file_handle); + file_handle = INVALID_HANDLE_VALUE; + } + if (handle->path) { uv__free(handle->path); handle->path = NULL; @@ -365,7 +367,7 @@ uv__free(short_path); - return uv_translate_sys_error(last_error); + return last_error; } @@ -571,7 +573,27 @@ } } else { err = GET_REQ_ERROR(req); - handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); + /* + * Check whether the ERROR_ACCESS_DENIED is caused by the watched directory + * being actually deleted (not an actual error) or a legit error. Retrieve + * FileStandardInfo to check whether the directory is pending deletion. + */ + FILE_STANDARD_INFO info; + if (err == ERROR_ACCESS_DENIED && + handle->dirw != NULL && + GetFileInformationByHandleEx(handle->dir_handle, + FileStandardInfo, + &info, + sizeof(info)) && + info.Directory && + info.DeletePending) { + uv__convert_utf16_to_utf8(handle->dirw, -1, &filename); + handle->cb(handle, filename, UV_RENAME, 0); + uv__free(filename); + filename = NULL; + } else { + handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); + } } if (handle->flags & UV_HANDLE_CLOSING) {
diff --git a/Utilities/cmlibuv/src/win/fs-fd-hash-inl.h b/Utilities/cmlibuv/src/win/fs-fd-hash-inl.h index 0b532af..94b4c3c 100644 --- a/Utilities/cmlibuv/src/win/fs-fd-hash-inl.h +++ b/Utilities/cmlibuv/src/win/fs-fd-hash-inl.h
@@ -69,11 +69,11 @@ static uv_mutex_t uv__fd_hash_mutex; static struct uv__fd_hash_entry_group_s - uv__fd_hash_entry_initial[UV__FD_HASH_SIZE * UV__FD_HASH_GROUP_SIZE]; + uv__fd_hash_entry_initial[UV__FD_HASH_SIZE]; static struct uv__fd_hash_bucket_s uv__fd_hash[UV__FD_HASH_SIZE]; -INLINE static void uv__fd_hash_init(void) { +static void uv__fd_hash_init(void) { size_t i; int err; @@ -82,10 +82,9 @@ uv_fatal_error(err, "uv_mutex_init"); } - for (i = 0; i < ARRAY_SIZE(uv__fd_hash); ++i) { + for (i = 0; i < ARRAY_SIZE(uv__fd_hash); i++) { uv__fd_hash[i].size = 0; - uv__fd_hash[i].data = - uv__fd_hash_entry_initial + i * UV__FD_HASH_GROUP_SIZE; + uv__fd_hash[i].data = &uv__fd_hash_entry_initial[i]; } } @@ -119,7 +118,7 @@ FIND_IN_GROUP_PTR(UV__FD_HASH_GROUP_SIZE); \ } while (0) -INLINE static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) { +static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) { FIND_COMMON_VARIABLES uv_mutex_lock(&uv__fd_hash_mutex); @@ -134,7 +133,7 @@ return entry_ptr != NULL; } -INLINE static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) { +static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) { FIND_COMMON_VARIABLES uv_mutex_lock(&uv__fd_hash_mutex); @@ -164,7 +163,7 @@ uv_mutex_unlock(&uv__fd_hash_mutex); } -INLINE static int uv__fd_hash_remove(int fd, struct uv__fd_info_s* info) { +static int uv__fd_hash_remove(int fd, struct uv__fd_info_s* info) { FIND_COMMON_VARIABLES uv_mutex_lock(&uv__fd_hash_mutex);
diff --git a/Utilities/cmlibuv/src/win/fs.c b/Utilities/cmlibuv/src/win/fs.c index 7923079..4092de0 100644 --- a/Utilities/cmlibuv/src/win/fs.c +++ b/Utilities/cmlibuv/src/win/fs.c
@@ -31,6 +31,11 @@ #include <stdio.h> #include "uv.h" + +/* <winioctl.h> requires <windows.h>, included via "uv.h" above, but needs to + be included before our "winapi.h", included via "internal.h" below. */ +#include <winioctl.h> + #include "internal.h" #include "req-inl.h" #include "handle-inl.h" @@ -41,6 +46,30 @@ #define UV_FS_FREE_PTR 0x0008 #define UV_FS_CLEANEDUP 0x0010 +#ifndef FILE_DISPOSITION_DELETE +#define FILE_DISPOSITION_DELETE 0x0001 +#endif /* FILE_DISPOSITION_DELETE */ + +#ifndef FILE_DISPOSITION_POSIX_SEMANTICS +#define FILE_DISPOSITION_POSIX_SEMANTICS 0x0002 +#endif /* FILE_DISPOSITION_POSIX_SEMANTICS */ + +#ifndef FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE +#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x0010 +#endif /* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE */ + +NTSTATUS uv__RtlUnicodeStringInit( + PUNICODE_STRING DestinationString, + PWSTR SourceString, + size_t SourceStringLen +) { + if (SourceStringLen > 0x7FFF) + return STATUS_INVALID_PARAMETER; + DestinationString->MaximumLength = DestinationString->Length = + SourceStringLen * sizeof(SourceString[0]); + DestinationString->Buffer = SourceString; + return STATUS_SUCCESS; +} #define INIT(subtype) \ do { \ @@ -53,7 +82,7 @@ #define POST \ do { \ if (cb != NULL) { \ - uv__req_register(loop, req); \ + uv__req_register(loop); \ uv__work_submit(loop, \ &req->work_req, \ UV__WORK_FAST_IO, \ @@ -92,13 +121,14 @@ return; \ } -#define MILLION ((int64_t) 1000 * 1000) -#define BILLION ((int64_t) 1000 * 1000 * 1000) +#define NSEC_PER_TICK 100 +#define TICKS_PER_SEC ((int64_t) 1e9 / NSEC_PER_TICK) +static const int64_t WIN_TO_UNIX_TICK_OFFSET = 11644473600 * TICKS_PER_SEC; static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) { - filetime -= 116444736 * BILLION; - ts->tv_sec = (long) (filetime / (10 * MILLION)); - ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U); + filetime -= WIN_TO_UNIX_TICK_OFFSET; + ts->tv_sec = filetime / TICKS_PER_SEC; + ts->tv_nsec = (filetime % TICKS_PER_SEC) * NSEC_PER_TICK; if (ts->tv_nsec < 0) { ts->tv_sec -= 1; ts->tv_nsec += 1e9; @@ -107,7 +137,7 @@ #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ - int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION); \ + int64_t bigtime = ((time) * TICKS_PER_SEC + WIN_TO_UNIX_TICK_OFFSET); \ (filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \ (filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \ } while(0) @@ -131,6 +161,17 @@ static DWORD uv__allocation_granularity; +typedef enum { + FS__STAT_PATH_SUCCESS, + FS__STAT_PATH_ERROR, + FS__STAT_PATH_TRY_SLOW +} fs__stat_path_return_t; + +static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf); +static void fs__stat_assign_statbuf(uv_stat_t* statbuf, + FILE_STAT_BASIC_INFORMATION stat_info, + int do_lstat); + void uv__fs_init(void) { SYSTEM_INFO system_info; @@ -142,172 +183,9 @@ } -INLINE static int fs__capture_path(uv_fs_t* req, const char* path, - const char* new_path, const int copy_path) { - char* buf; - char* pos; - ssize_t buf_sz = 0, path_len = 0, pathw_len = 0, new_pathw_len = 0; - - /* new_path can only be set if path is also set. */ - assert(new_path == NULL || path != NULL); - - if (path != NULL) { - pathw_len = MultiByteToWideChar(CP_UTF8, - 0, - path, - -1, - NULL, - 0); - if (pathw_len == 0) { - return GetLastError(); - } - - buf_sz += pathw_len * sizeof(WCHAR); - } - - if (path != NULL && copy_path) { - path_len = 1 + strlen(path); - buf_sz += path_len; - } - - if (new_path != NULL) { - new_pathw_len = MultiByteToWideChar(CP_UTF8, - 0, - new_path, - -1, - NULL, - 0); - if (new_pathw_len == 0) { - return GetLastError(); - } - - buf_sz += new_pathw_len * sizeof(WCHAR); - } - - - if (buf_sz == 0) { - req->file.pathw = NULL; - req->fs.info.new_pathw = NULL; - req->path = NULL; - return 0; - } - - buf = (char*) uv__malloc(buf_sz); - if (buf == NULL) { - return ERROR_OUTOFMEMORY; - } - - pos = buf; - - if (path != NULL) { - DWORD r = MultiByteToWideChar(CP_UTF8, - 0, - path, - -1, - (WCHAR*) pos, - pathw_len); - assert(r == (DWORD) pathw_len); - req->file.pathw = (WCHAR*) pos; - pos += r * sizeof(WCHAR); - } else { - req->file.pathw = NULL; - } - - if (new_path != NULL) { - DWORD r = MultiByteToWideChar(CP_UTF8, - 0, - new_path, - -1, - (WCHAR*) pos, - new_pathw_len); - assert(r == (DWORD) new_pathw_len); - req->fs.info.new_pathw = (WCHAR*) pos; - pos += r * sizeof(WCHAR); - } else { - req->fs.info.new_pathw = NULL; - } - - req->path = path; - if (path != NULL && copy_path) { - memcpy(pos, path, path_len); - assert(path_len == buf_sz - (pos - buf)); - req->path = pos; - } - - req->flags |= UV_FS_FREE_PATHS; - - return 0; -} - - - -INLINE static void uv__fs_req_init(uv_loop_t* loop, uv_fs_t* req, - uv_fs_type fs_type, const uv_fs_cb cb) { - uv__once_init(); - UV_REQ_INIT(req, UV_FS); - req->loop = loop; - req->flags = 0; - req->fs_type = fs_type; - req->sys_errno_ = 0; - req->result = 0; - req->ptr = NULL; - req->path = NULL; - req->cb = cb; - memset(&req->fs, 0, sizeof(req->fs)); -} - - -static int fs__wide_to_utf8(WCHAR* w_source_ptr, - DWORD w_source_len, +static int fs__readlink_handle(HANDLE handle, char** target_ptr, - uint64_t* target_len_ptr) { - int r; - int target_len; - char* target; - target_len = WideCharToMultiByte(CP_UTF8, - 0, - w_source_ptr, - w_source_len, - NULL, - 0, - NULL, - NULL); - - if (target_len == 0) { - return -1; - } - - if (target_len_ptr != NULL) { - *target_len_ptr = target_len; - } - - if (target_ptr == NULL) { - return 0; - } - - target = uv__malloc(target_len + 1); - if (target == NULL) { - SetLastError(ERROR_OUTOFMEMORY); - return -1; - } - - r = WideCharToMultiByte(CP_UTF8, - 0, - w_source_ptr, - w_source_len, - target, - target_len, - NULL, - NULL); - assert(r == target_len); - target[target_len] = '\0'; - *target_ptr = target; - return 0; -} - - -INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, - uint64_t* target_len_ptr) { + size_t* target_len_ptr) { char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer; WCHAR* w_target; @@ -369,6 +247,32 @@ } } + } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK) { + /* Real (Linux) symlink */ + char* buffer; + char* target; + size_t target_len; + + target_len = (reparse_data->ReparseDataLength - + sizeof(ULONG)); /* Version field */ + buffer = (char*) reparse_data->LinuxSymbolicLinkReparseBuffer.PathBuffer; + + if (target_len_ptr != NULL) { + *target_len_ptr = target_len; + } + + if (target_ptr != NULL) { + assert(*target_ptr == NULL); + target = uv__malloc(target_len + 1); + if (target == NULL) { + return UV_ENOMEM; + } + memcpy(target, buffer, target_len); + target[target_len] = '\0'; + *target_ptr = target; + } + return 0; + } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { /* Junction. */ w_target = reparse_data->MountPointReparseBuffer.PathBuffer + @@ -437,7 +341,103 @@ return -1; } - return fs__wide_to_utf8(w_target, w_target_len, target_ptr, target_len_ptr); + assert(target_ptr == NULL || *target_ptr == NULL); + return uv_utf16_to_wtf8(w_target, w_target_len, target_ptr, target_len_ptr); +} + + +static int fs__capture_path(uv_fs_t* req, + const char* path, + const char* new_path, + const int copy_path) { + WCHAR* buf; + WCHAR* pos; + size_t buf_sz = 0; + size_t path_len = 0; + ssize_t pathw_len = 0; + ssize_t new_pathw_len = 0; + + /* new_path can only be set if path is also set. */ + assert(new_path == NULL || path != NULL); + + if (path != NULL) { + pathw_len = uv_wtf8_length_as_utf16(path); + if (pathw_len < 0) + return ERROR_INVALID_NAME; + buf_sz += pathw_len * sizeof(WCHAR); + } + + if (path != NULL && copy_path) { + path_len = 1 + strlen(path); + buf_sz += path_len; + } + + if (new_path != NULL) { + new_pathw_len = uv_wtf8_length_as_utf16(new_path); + if (new_pathw_len < 0) + return ERROR_INVALID_NAME; + buf_sz += new_pathw_len * sizeof(WCHAR); + } + + + if (buf_sz == 0) { + req->file.pathw = NULL; + req->fs.info.new_pathw = NULL; + req->path = NULL; + return 0; + } + + buf = uv__malloc(buf_sz); + if (buf == NULL) { + return ERROR_OUTOFMEMORY; + } + + pos = buf; + + if (path != NULL) { + uv_wtf8_to_utf16(path, pos, pathw_len); + req->file.pathw = pos; + pos += pathw_len; + } else { + req->file.pathw = NULL; + } + + if (new_path != NULL) { + uv_wtf8_to_utf16(new_path, pos, new_pathw_len); + req->fs.info.new_pathw = pos; + pos += new_pathw_len; + } else { + req->fs.info.new_pathw = NULL; + } + + req->path = path; + if (path != NULL && copy_path) { + memcpy(pos, path, path_len); + assert(path_len == buf_sz - (pos - buf) * sizeof(WCHAR)); + req->path = (char*) pos; + } + + req->flags |= UV_FS_FREE_PATHS; + + return 0; +} + + +static void uv__fs_req_init(uv_loop_t* loop, + uv_fs_t* req, + uv_fs_type fs_type, + const uv_fs_cb cb) { + uv__once_init(); + UV_REQ_INIT(req, UV_FS); + req->loop = loop; + req->flags = 0; + req->fs_type = fs_type; + req->sys_errno_ = 0; + req->result = 0; + req->ptr = NULL; + req->path = NULL; + req->cb = cb; + memset(&req->fs, 0, sizeof(req->fs)); } @@ -473,8 +473,8 @@ /* Obtain the active umask. umask() never fails and returns the previous * umask. */ - current_umask = umask(0); - umask(current_umask); + current_umask = _umask(0); + _umask(current_umask); /* convert flags and mode to CreateFile parameters */ switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) { @@ -1122,30 +1122,23 @@ error = ERROR_INVALID_FLAGS; } - SET_REQ_WIN32_ERROR(req, error); + SET_REQ_UV_ERROR(req, uv_translate_write_sys_error(error), error); } } -void fs__rmdir(uv_fs_t* req) { - int result = _wrmdir(req->file.pathw); - if (result == -1) - SET_REQ_WIN32_ERROR(req, _doserrno); - else - SET_REQ_RESULT(req, 0); -} - - -void fs__unlink(uv_fs_t* req) { +static void fs__unlink_rmdir(uv_fs_t* req, BOOL isrmdir) { const WCHAR* pathw = req->file.pathw; HANDLE handle; - BY_HANDLE_FILE_INFORMATION info; + FILE_BASIC_INFO info; FILE_DISPOSITION_INFORMATION disposition; + FILE_DISPOSITION_INFORMATION_EX disposition_ex; IO_STATUS_BLOCK iosb; NTSTATUS status; + DWORD error; handle = CreateFileW(pathw, - FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE, + FILE_READ_ATTRIBUTES | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, @@ -1157,19 +1150,27 @@ return; } - if (!GetFileInformationByHandle(handle, &info)) { + if (!GetFileInformationByHandleEx(handle, FileBasicInfo, &info, sizeof info)) { SET_REQ_WIN32_ERROR(req, GetLastError()); CloseHandle(handle); return; } - if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - /* Do not allow deletion of directories, unless it is a symlink. When the - * path refers to a non-symlink directory, report EPERM as mandated by - * POSIX.1. */ + if (isrmdir && !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + /* Error if we're in rmdir mode but it is not a dir. + * TODO: change it to UV_NOTDIR in v2. */ + SET_REQ_UV_ERROR(req, UV_ENOENT, ERROR_DIRECTORY); + CloseHandle(handle); + return; + } + + if (!isrmdir && (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + /* If not explicitly allowed, do not allow deletion of directories, unless + * it is a symlink. When the path refers to a non-symlink directory, report + * EPERM as mandated by POSIX.1. */ /* Check if it is a reparse point. If it's not, it's a normal directory. */ - if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + if (!(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); CloseHandle(handle); return; @@ -1178,7 +1179,7 @@ /* Read the reparse point and check if it is a valid symlink. If not, don't * unlink. */ if (fs__readlink_handle(handle, NULL, NULL) < 0) { - DWORD error = GetLastError(); + error = GetLastError(); if (error == ERROR_SYMLINK_NOT_SUPPORTED) error = ERROR_ACCESS_DENIED; SET_REQ_WIN32_ERROR(req, error); @@ -1187,42 +1188,96 @@ } } - if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { - /* Remove read-only attribute */ - FILE_BASIC_INFORMATION basic = { 0 }; + /* Try posix delete first */ + disposition_ex.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS | + FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE; - basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) | - FILE_ATTRIBUTE_ARCHIVE; - - status = pNtSetInformationFile(handle, - &iosb, - &basic, - sizeof basic, - FileBasicInformation); - if (!NT_SUCCESS(status)) { - SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); - CloseHandle(handle); - return; - } - } - - /* Try to set the delete flag. */ - disposition.DeleteFile = TRUE; status = pNtSetInformationFile(handle, &iosb, - &disposition, - sizeof disposition, - FileDispositionInformation); + &disposition_ex, + sizeof disposition_ex, + FileDispositionInformationEx); if (NT_SUCCESS(status)) { SET_REQ_SUCCESS(req); } else { - SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); + /* If status == STATUS_CANNOT_DELETE here, given we set + * FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, STATUS_CANNOT_DELETE can only mean + * that there is an existing mapped view to the file, preventing delete. + * STATUS_CANNOT_DELETE maps to UV_EACCES so it's not specifically worth handling */ + error = pRtlNtStatusToDosError(status); + if (error == ERROR_NOT_SUPPORTED /* filesystem does not support posix deletion */ || + error == ERROR_INVALID_PARAMETER /* pre Windows 10 error */ || + error == ERROR_INVALID_FUNCTION /* pre Windows 10 1607 error */) { + /* posix delete not supported so try fallback */ + if (info.FileAttributes & FILE_ATTRIBUTE_READONLY) { + /* Remove read-only attribute */ + FILE_BASIC_INFORMATION basic = { 0 }; + + /* We opened the handle above without FILE_WRITE_ATTRIBUTES access, which + * is not required in the happy path. On windows, it would probably + * be ok to ask for them anyway, but Wine has a bug that causes such calls + * to fail (https://bugs.winehq.org/show_bug.cgi?id=50771). To work around + * this bug, we re-open the handle here */ + HANDLE write_attributes_handle; + + basic.FileAttributes = (info.FileAttributes & ~FILE_ATTRIBUTE_READONLY) | + FILE_ATTRIBUTE_ARCHIVE; + + write_attributes_handle = ReOpenFile(handle, FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, + FILE_FLAG_OPEN_REPARSE_POINT | + FILE_FLAG_BACKUP_SEMANTICS); + if (write_attributes_handle == INVALID_HANDLE_VALUE) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(handle); + return; + } + + status = pNtSetInformationFile(write_attributes_handle, + &iosb, + &basic, + sizeof basic, + FileBasicInformation); + CloseHandle(write_attributes_handle); + if (!NT_SUCCESS(status)) { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); + CloseHandle(handle); + return; + } + } + + /* Try to set the delete flag. */ + disposition.DeleteFile = TRUE; + status = pNtSetInformationFile(handle, + &iosb, + &disposition, + sizeof disposition, + FileDispositionInformation); + if (NT_SUCCESS(status)) { + SET_REQ_SUCCESS(req); + } else { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); + } + } else { + SET_REQ_WIN32_ERROR(req, error); + } } CloseHandle(handle); } +static void fs__rmdir(uv_fs_t* req) { + fs__unlink_rmdir(req, /*isrmdir*/1); +} + + +static void fs__unlink(uv_fs_t* req) { + fs__unlink_rmdir(req, /*isrmdir*/0); +} + + void fs__mkdir(uv_fs_t* req) { /* TODO: use req->mode. */ if (CreateDirectoryW(req->file.pathw, NULL)) { @@ -1248,7 +1303,7 @@ size_t len; uint64_t v; char* path; - + path = (char*)req->path; len = wcslen(req->file.pathw); ep = req->file.pathw + len; @@ -1259,7 +1314,7 @@ tries = TMP_MAX; do { - if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) { + if (uv__random_winrandom(&v, sizeof(v)) < 0) { SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE); goto clobber; } @@ -1427,7 +1482,8 @@ uv__dirent_t* dirent; size_t wchar_len; - size_t utf8_len; + size_t wtf8_len; + char* wtf8; /* Obtain a pointer to the current directory entry. */ position += next_entry_offset; @@ -1454,11 +1510,8 @@ info->FileName[1] == L'.') continue; - /* Compute the space required to store the filename as UTF-8. */ - utf8_len = WideCharToMultiByte( - CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL); - if (utf8_len == 0) - goto win32_error; + /* Compute the space required to store the filename as WTF-8. */ + wtf8_len = uv_utf16_length_as_wtf8(&info->FileName[0], wchar_len); /* Resize the dirent array if needed. */ if (dirents_used >= dirents_size) { @@ -1478,25 +1531,16 @@ * includes room for the first character of the filename, but `utf8_len` * doesn't count the NULL terminator at this point. */ - dirent = uv__malloc(sizeof *dirent + utf8_len); + dirent = uv__malloc(sizeof *dirent + wtf8_len); if (dirent == NULL) goto out_of_memory_error; dirents[dirents_used++] = dirent; /* Convert file name to UTF-8. */ - if (WideCharToMultiByte(CP_UTF8, - 0, - &info->FileName[0], - wchar_len, - &dirent->d_name[0], - utf8_len, - NULL, - NULL) == 0) - goto win32_error; - - /* Add a null terminator to the filename. */ - dirent->d_name[utf8_len] = '\0'; + wtf8 = &dirent->d_name[0]; + if (uv_utf16_to_wtf8(&info->FileName[0], wchar_len, &wtf8, &wtf8_len) != 0) + goto out_of_memory_error; /* Fill out the type field. */ if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE) @@ -1644,6 +1688,21 @@ dir = req->ptr; dirents = dir->dirents; memset(dirents, 0, dir->nentries * sizeof(*dir->dirents)); + + /* Initial FindFirstFile can fail with ERROR_FILE_NOT_FOUND, meaning + * no matches, which leaves dir_handle set to INVALID_HANDLE_VALUE, + * see fs__opendir(). Not an actual error, just means no results. + * + * sshfs-win apparently works that way but reading an empty directory + * on a regular drive doesn't trigger that particular code path. + * + * See https://github.com/libuv/libuv/issues/4952 + */ + if (dir->dir_handle == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT(req, 0); + return; + } + find_data = &dir->find_data; dirent_idx = 0; @@ -1670,12 +1729,12 @@ goto error; /* Copy file type. */ - if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) - dent.d_type = UV__DT_DIR; + if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) + dent.d_type = UV__DT_CHAR; else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) dent.d_type = UV__DT_LINK; - else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) - dent.d_type = UV__DT_CHAR; + else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + dent.d_type = UV__DT_DIR; else dent.d_type = UV__DT_FILE; @@ -1704,12 +1763,70 @@ SET_REQ_RESULT(req, 0); } -INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, - int do_lstat) { +static fs__stat_path_return_t fs__stat_path(WCHAR* path, + uv_stat_t* statbuf, + int do_lstat) { + FILE_STAT_BASIC_INFORMATION stat_info; + + /* Check if the new fast API is available. */ + if (!pGetFileInformationByName) { + return FS__STAT_PATH_TRY_SLOW; + } + + /* Check if the API call fails. */ + if (!pGetFileInformationByName(path, FileStatBasicByNameInfo, &stat_info, + sizeof(stat_info))) { + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_NOT_READY: + case ERROR_BAD_NET_NAME: + /* These errors aren't worth retrying with the slow path. */ + return FS__STAT_PATH_ERROR; + } + return FS__STAT_PATH_TRY_SLOW; + } + + /* A file handle is needed to get st_size for links. */ + if ((stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + return FS__STAT_PATH_TRY_SLOW; + } + + if (stat_info.DeviceType == FILE_DEVICE_NULL) { + fs__stat_assign_statbuf_null(statbuf); + return FS__STAT_PATH_SUCCESS; + } + + fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); + return FS__STAT_PATH_SUCCESS; +} + +static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, int do_lstat) { + size_t target_length = 0; + FILE_FS_DEVICE_INFORMATION device_info; FILE_ALL_INFORMATION file_info; FILE_FS_VOLUME_INFORMATION volume_info; NTSTATUS nt_status; IO_STATUS_BLOCK io_status; + FILE_STAT_BASIC_INFORMATION stat_info; + + nt_status = pNtQueryVolumeInformationFile(handle, + &io_status, + &device_info, + sizeof device_info, + FileFsDeviceInformation); + + /* Buffer overflow (a warning status code) is expected here. */ + if (NT_ERROR(nt_status)) { + SetLastError(pRtlNtStatusToDosError(nt_status)); + return -1; + } + + /* If it's NUL device set fields as reasonable as possible and return. */ + if (device_info.DeviceType == FILE_DEVICE_NULL) { + fs__stat_assign_statbuf_null(statbuf); + return 0; + } nt_status = pNtQueryInformationFile(handle, &io_status, @@ -1731,14 +1848,65 @@ /* Buffer overflow (a warning status code) is expected here. */ if (io_status.Status == STATUS_NOT_IMPLEMENTED) { - statbuf->st_dev = 0; + stat_info.VolumeSerialNumber.QuadPart = 0; } else if (NT_ERROR(nt_status)) { SetLastError(pRtlNtStatusToDosError(nt_status)); return -1; } else { - statbuf->st_dev = volume_info.VolumeSerialNumber; + stat_info.VolumeSerialNumber.LowPart = volume_info.VolumeSerialNumber; } + stat_info.DeviceType = device_info.DeviceType; + stat_info.FileAttributes = file_info.BasicInformation.FileAttributes; + stat_info.NumberOfLinks = file_info.StandardInformation.NumberOfLinks; + stat_info.FileId.QuadPart = + file_info.InternalInformation.IndexNumber.QuadPart; + stat_info.ChangeTime.QuadPart = + file_info.BasicInformation.ChangeTime.QuadPart; + stat_info.CreationTime.QuadPart = + file_info.BasicInformation.CreationTime.QuadPart; + stat_info.LastAccessTime.QuadPart = + file_info.BasicInformation.LastAccessTime.QuadPart; + stat_info.LastWriteTime.QuadPart = + file_info.BasicInformation.LastWriteTime.QuadPart; + stat_info.AllocationSize.QuadPart = + file_info.StandardInformation.AllocationSize.QuadPart; + + if (do_lstat && + (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + /* + * If reading the link fails, the reparse point is not a symlink and needs + * to be treated as a regular file. The higher level lstat function will + * detect this failure and retry without do_lstat if appropriate. + */ + if (fs__readlink_handle(handle, NULL, &target_length) != 0) { + return -1; + } + stat_info.EndOfFile.QuadPart = target_length; + } else { + stat_info.EndOfFile.QuadPart = + file_info.StandardInformation.EndOfFile.QuadPart; + } + + fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); + return 0; +} + +static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf) { + memset(statbuf, 0, sizeof(uv_stat_t)); + statbuf->st_mode = _S_IFCHR; + statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | + ((_S_IREAD | _S_IWRITE) >> 6); + statbuf->st_nlink = 1; + statbuf->st_blksize = 4096; + statbuf->st_rdev = FILE_DEVICE_NULL << 16; +} + +static void fs__stat_assign_statbuf(uv_stat_t* statbuf, + FILE_STAT_BASIC_INFORMATION stat_info, + int do_lstat) { + statbuf->st_dev = stat_info.VolumeSerialNumber.LowPart; + /* Todo: st_mode should probably always be 0666 for everyone. We might also * want to report 0777 if the file is a .exe or a directory. * @@ -1770,49 +1938,43 @@ * target. Otherwise, reparse points must be treated as regular files. */ if (do_lstat && - (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - /* - * If reading the link fails, the reparse point is not a symlink and needs - * to be treated as a regular file. The higher level lstat function will - * detect this failure and retry without do_lstat if appropriate. - */ - if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) - return -1; + (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { statbuf->st_mode |= S_IFLNK; + statbuf->st_size = stat_info.EndOfFile.QuadPart; } if (statbuf->st_mode == 0) { - if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (stat_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { statbuf->st_mode |= _S_IFDIR; statbuf->st_size = 0; } else { statbuf->st_mode |= _S_IFREG; - statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart; + statbuf->st_size = stat_info.EndOfFile.QuadPart; } } - if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY) + if (stat_info.FileAttributes & FILE_ATTRIBUTE_READONLY) statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6); else statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | ((_S_IREAD | _S_IWRITE) >> 6); uv__filetime_to_timespec(&statbuf->st_atim, - file_info.BasicInformation.LastAccessTime.QuadPart); + stat_info.LastAccessTime.QuadPart); uv__filetime_to_timespec(&statbuf->st_ctim, - file_info.BasicInformation.ChangeTime.QuadPart); + stat_info.ChangeTime.QuadPart); uv__filetime_to_timespec(&statbuf->st_mtim, - file_info.BasicInformation.LastWriteTime.QuadPart); + stat_info.LastWriteTime.QuadPart); uv__filetime_to_timespec(&statbuf->st_birthtim, - file_info.BasicInformation.CreationTime.QuadPart); + stat_info.CreationTime.QuadPart); - statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart; + statbuf->st_ino = stat_info.FileId.QuadPart; /* st_blocks contains the on-disk allocation size in 512-byte units. */ statbuf->st_blocks = - (uint64_t) file_info.StandardInformation.AllocationSize.QuadPart >> 9; + (uint64_t) stat_info.AllocationSize.QuadPart >> 9; - statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks; + statbuf->st_nlink = stat_info.NumberOfLinks; /* The st_blksize is supposed to be the 'optimal' number of bytes for reading * and writing to the disk. That is, for any definition of 'optimal' - it's @@ -1844,29 +2006,212 @@ statbuf->st_uid = 0; statbuf->st_rdev = 0; statbuf->st_gen = 0; - - return 0; } -INLINE static void fs__stat_prepare_path(WCHAR* pathw) { +static void fs__stat_prepare_path(WCHAR* pathw) { size_t len = wcslen(pathw); - /* TODO: ignore namespaced paths. */ if (len > 1 && pathw[len - 2] != L':' && (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) { pathw[len - 1] = '\0'; } } +static DWORD fs__stat_directory(WCHAR* path, + uv_stat_t* statbuf, + int do_lstat, + DWORD ret_error) { + HANDLE handle = INVALID_HANDLE_VALUE; + FILE_STAT_BASIC_INFORMATION stat_info; + FILE_ID_FULL_DIR_INFORMATION dir_info; + FILE_FS_VOLUME_INFORMATION volume_info; + FILE_FS_DEVICE_INFORMATION device_info; + IO_STATUS_BLOCK io_status; + NTSTATUS nt_status; + WCHAR* path_dirpath = NULL; + WCHAR* path_filename = NULL; + UNICODE_STRING FileMask; + size_t len; + size_t split; + WCHAR splitchar; + int includes_name; -INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, - int do_lstat, - uv_stat_t* statbuf) { + /* AKA strtok or wcscspn, in reverse. */ + len = wcslen(path); + split = len; + + includes_name = 0; + while (split > 0 && path[split - 1] != L'\\' && path[split - 1] != L'/' && + path[split - 1] != L':') { + /* check if the path contains a character other than /,\,:,. */ + if (path[split-1] != '.') { + includes_name = 1; + } + split--; + } + /* If the path is a relative path with a file name or a folder name */ + if (split == 0 && includes_name) { + path_dirpath = L"."; + /* If there is a slash or a backslash */ + } else if (path[split - 1] == L'\\' || path[split - 1] == L'/') { + path_dirpath = path; + /* If there is no filename, consider it as a relative folder path */ + if (!includes_name) { + split = len; + /* Else, split it */ + } else { + splitchar = path[split - 1]; + path[split - 1] = L'\0'; + } + /* e.g. "..", "c:" */ + } else { + path_dirpath = path; + split = len; + } + path_filename = &path[split]; + + len = 0; + while (1) { + if (path_filename[len] == L'\0') + break; + if (path_filename[len] == L'*' || path_filename[len] == L'?' || + path_filename[len] == L'>' || path_filename[len] == L'<' || + path_filename[len] == L'"') { + ret_error = ERROR_INVALID_NAME; + goto cleanup; + } + len++; + } + + /* Get directory handle */ + handle = CreateFileW(path_dirpath, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (handle == INVALID_HANDLE_VALUE) { + ret_error = GetLastError(); + goto cleanup; + } + + /* Get files in the directory */ + nt_status = uv__RtlUnicodeStringInit(&FileMask, path_filename, len); + if (!NT_SUCCESS(nt_status)) { + ret_error = pRtlNtStatusToDosError(nt_status); + goto cleanup; + } + nt_status = pNtQueryDirectoryFile(handle, + NULL, + NULL, + NULL, + &io_status, + &dir_info, + sizeof(dir_info), + FileIdFullDirectoryInformation, + TRUE, + &FileMask, + TRUE); + + /* Buffer overflow (a warning status code) is expected here since there isn't + * enough space to store the FileName, and actually indicates success. */ + if (!NT_SUCCESS(nt_status) && nt_status != STATUS_BUFFER_OVERFLOW) { + if (nt_status == STATUS_NO_MORE_FILES) + ret_error = ERROR_PATH_NOT_FOUND; + else + ret_error = pRtlNtStatusToDosError(nt_status); + goto cleanup; + } + + /* Assign values to stat_info */ + memset(&stat_info, 0, sizeof(FILE_STAT_BASIC_INFORMATION)); + stat_info.FileAttributes = dir_info.FileAttributes; + stat_info.CreationTime.QuadPart = dir_info.CreationTime.QuadPart; + stat_info.LastAccessTime.QuadPart = dir_info.LastAccessTime.QuadPart; + stat_info.LastWriteTime.QuadPart = dir_info.LastWriteTime.QuadPart; + if (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* A file handle is needed to get st_size for the link (from + * FSCTL_GET_REPARSE_POINT), which is required by posix, but we are here + * because getting the file handle failed. We could get just the + * ReparsePointTag by querying FILE_ID_EXTD_DIR_INFORMATION instead to make + * sure this really is a link before giving up here on the uv_fs_stat call, + * but that doesn't seem essential. */ + if (!do_lstat) + goto cleanup; + stat_info.EndOfFile.QuadPart = 0; + stat_info.AllocationSize.QuadPart = 0; + } else { + stat_info.EndOfFile.QuadPart = dir_info.EndOfFile.QuadPart; + stat_info.AllocationSize.QuadPart = dir_info.AllocationSize.QuadPart; + } + stat_info.ChangeTime.QuadPart = dir_info.ChangeTime.QuadPart; + stat_info.FileId.QuadPart = dir_info.FileId.QuadPart; + + /* Finish up by getting device info from the directory handle, + * since files presumably must live on their device. */ + nt_status = pNtQueryVolumeInformationFile(handle, + &io_status, + &volume_info, + sizeof volume_info, + FileFsVolumeInformation); + + /* Buffer overflow (a warning status code) is expected here. */ + if (io_status.Status == STATUS_NOT_IMPLEMENTED) { + stat_info.VolumeSerialNumber.QuadPart = 0; + } else if (NT_ERROR(nt_status)) { + ret_error = pRtlNtStatusToDosError(nt_status); + goto cleanup; + } else { + stat_info.VolumeSerialNumber.QuadPart = volume_info.VolumeSerialNumber; + } + + nt_status = pNtQueryVolumeInformationFile(handle, + &io_status, + &device_info, + sizeof device_info, + FileFsDeviceInformation); + + /* Buffer overflow (a warning status code) is expected here. */ + if (NT_ERROR(nt_status)) { + ret_error = pRtlNtStatusToDosError(nt_status); + goto cleanup; + } + + stat_info.DeviceType = device_info.DeviceType; + stat_info.NumberOfLinks = 1; /* No way to recover this info. */ + + fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); + ret_error = 0; + +cleanup: + if (split != 0) + path[split - 1] = splitchar; + if (handle != INVALID_HANDLE_VALUE) + CloseHandle(handle); + return ret_error; +} + +static DWORD fs__stat_impl_from_path(WCHAR* path, + int do_lstat, + uv_stat_t* statbuf) { HANDLE handle; DWORD flags; DWORD ret; + /* If new API exists, try to use it. */ + switch (fs__stat_path(path, statbuf, do_lstat)) { + case FS__STAT_PATH_SUCCESS: + return 0; + case FS__STAT_PATH_ERROR: + return GetLastError(); + case FS__STAT_PATH_TRY_SLOW: + break; + } + + /* If the new API does not exist, use the old API. */ flags = FILE_FLAG_BACKUP_SEMANTICS; if (do_lstat) flags |= FILE_FLAG_OPEN_REPARSE_POINT; @@ -1879,8 +2224,12 @@ flags, NULL); - if (handle == INVALID_HANDLE_VALUE) - return GetLastError(); + if (handle == INVALID_HANDLE_VALUE) { + ret = GetLastError(); + if (ret != ERROR_ACCESS_DENIED && ret != ERROR_SHARING_VIOLATION) + return ret; + return fs__stat_directory(path, statbuf, do_lstat, ret); + } if (fs__stat_handle(handle, statbuf, do_lstat) != 0) ret = GetLastError(); @@ -1892,7 +2241,7 @@ } -INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { +static void fs__stat_impl(uv_fs_t* req, int do_lstat) { DWORD error; error = fs__stat_impl_from_path(req->file.pathw, do_lstat, &req->statbuf); @@ -1915,6 +2264,37 @@ } +static int fs__fstat_handle(int fd, HANDLE handle, uv_stat_t* statbuf) { + DWORD file_type; + + /* Each file type is processed differently. */ + file_type = uv_guess_handle(fd); + switch (file_type) { + /* Disk files use the existing logic from fs__stat_handle. */ + case UV_FILE: + return fs__stat_handle(handle, statbuf, 0); + + /* Devices and pipes are processed identically. There is no more information + * for them from any API. Fields are set as reasonably as possible and the + * function returns. */ + case UV_TTY: + case UV_NAMED_PIPE: + memset(statbuf, 0, sizeof(uv_stat_t)); + statbuf->st_mode = file_type == UV_TTY ? _S_IFCHR : _S_IFIFO; + statbuf->st_nlink = 1; + statbuf->st_rdev = (file_type == UV_TTY ? FILE_DEVICE_CONSOLE : FILE_DEVICE_NAMED_PIPE) << 16; + statbuf->st_ino = (uintptr_t) handle; + return 0; + + /* If file type is unknown it is an error. */ + case UV_UNKNOWN_HANDLE: + default: + SetLastError(ERROR_INVALID_HANDLE); + return -1; + } +} + + static void fs__stat(uv_fs_t* req) { fs__stat_prepare_path(req->file.pathw); fs__stat_impl(req, 0); @@ -1940,7 +2320,7 @@ return; } - if (fs__stat_handle(handle, &req->statbuf, 0) != 0) { + if (fs__fstat_handle(fd, handle, &req->statbuf) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } @@ -1960,7 +2340,7 @@ } -INLINE static void fs__sync_impl(uv_fs_t* req) { +static void fs__sync_impl(uv_fs_t* req) { int fd = req->file.fd; int result; @@ -2221,7 +2601,7 @@ SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); goto fchmod_cleanup; } - /* Remeber to clear the flag later on */ + /* Remember to clear the flag later on */ clear_archive_flag = 1; } else { clear_archive_flag = 0; @@ -2266,23 +2646,38 @@ } -INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) { - FILETIME filetime_a, filetime_m; +static int fs__utime_handle(HANDLE handle, double atime, double mtime) { + FILETIME filetime_as, *filetime_a = &filetime_as; + FILETIME filetime_ms, *filetime_m = &filetime_ms; + FILETIME now; - TIME_T_TO_FILETIME(atime, &filetime_a); - TIME_T_TO_FILETIME(mtime, &filetime_m); + if (uv__isinf(atime) || uv__isinf(mtime)) + GetSystemTimeAsFileTime(&now); - if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) { + if (uv__isinf(atime)) + filetime_a = &now; + else if (uv__isnan(atime)) + filetime_a = NULL; + else + TIME_T_TO_FILETIME(atime, filetime_a); + + if (uv__isinf(mtime)) + filetime_m = &now; + else if (uv__isnan(mtime)) + filetime_m = NULL; + else + TIME_T_TO_FILETIME(mtime, filetime_m); + + if (!SetFileTime(handle, NULL, filetime_a, filetime_m)) return -1; - } return 0; } -INLINE static DWORD fs__utime_impl_from_path(WCHAR* path, - double atime, - double mtime, - int do_lutime) { +static DWORD fs__utime_impl_from_path(WCHAR* path, + double atime, + double mtime, + int do_lutime) { HANDLE handle; DWORD flags; DWORD ret; @@ -2312,7 +2707,7 @@ return ret; } -INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) { +static void fs__utime_impl(uv_fs_t* req, int do_lutime) { DWORD error; error = fs__utime_impl_from_path(req->file.pathw, @@ -2442,16 +2837,17 @@ path_buf[path_buf_len++] = path[i]; } - path_buf[path_buf_len++] = L'\\'; + if (add_slash) + path_buf[path_buf_len++] = L'\\'; len = path_buf_len - start; + /* Insert null terminator */ + path_buf[path_buf_len++] = L'\0'; + /* Set the info about the substitute name */ buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR); buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR); - /* Insert null terminator */ - path_buf[path_buf_len++] = L'\0'; - /* Copy the print name of the target path */ start = path_buf_len; add_slash = 0; @@ -2469,18 +2865,18 @@ path_buf[path_buf_len++] = path[i]; } len = path_buf_len - start; - if (len == 2) { + if (len == 2 || add_slash) { path_buf[path_buf_len++] = L'\\'; len++; } + /* Insert another null terminator */ + path_buf[path_buf_len++] = L'\0'; + /* Set the info about the print name */ buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR); buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR); - /* Insert another null terminator */ - path_buf[path_buf_len++] = L'\0'; - /* Calculate how much buffer space was actually used */ used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) + path_buf_len * sizeof(WCHAR); @@ -2603,8 +2999,12 @@ return; } + assert(req->ptr == NULL); if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) { - SET_REQ_WIN32_ERROR(req, GetLastError()); + DWORD error = GetLastError(); + SET_REQ_WIN32_ERROR(req, error); + if (error == ERROR_NOT_A_REPARSE_POINT) + req->result = UV_EINVAL; CloseHandle(handle); return; } @@ -2659,7 +3059,8 @@ return -1; } - r = fs__wide_to_utf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL); + assert(*realpath_ptr == NULL); + r = uv_utf16_to_wtf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL); uv__free(w_realpath_buf); return r; } @@ -2679,6 +3080,7 @@ return; } + assert(req->ptr == NULL); if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) { CloseHandle(handle); SET_REQ_WIN32_ERROR(req, GetLastError()); @@ -2707,67 +3109,35 @@ static void fs__statfs(uv_fs_t* req) { + FILE_FS_FULL_SIZE_INFORMATION info; + IO_STATUS_BLOCK io_status; uv_statfs_t* stat_fs; - DWORD sectors_per_cluster; - DWORD bytes_per_sector; - DWORD free_clusters; - DWORD total_clusters; - WCHAR* pathw; + NTSTATUS nt_status; + HANDLE handle; - pathw = req->file.pathw; -retry_get_disk_free_space: - if (0 == GetDiskFreeSpaceW(pathw, - §ors_per_cluster, - &bytes_per_sector, - &free_clusters, - &total_clusters)) { - DWORD err; - WCHAR* fpart; - size_t len; - DWORD ret; - BOOL is_second; + handle = CreateFileW(req->file.pathw, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); - err = GetLastError(); - is_second = pathw != req->file.pathw; - if (err != ERROR_DIRECTORY || is_second) { - if (is_second) - uv__free(pathw); - - SET_REQ_WIN32_ERROR(req, err); - return; - } - - len = MAX_PATH + 1; - pathw = uv__malloc(len * sizeof(*pathw)); - if (pathw == NULL) { - SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); - return; - } -retry_get_full_path_name: - ret = GetFullPathNameW(req->file.pathw, - len, - pathw, - &fpart); - if (ret == 0) { - uv__free(pathw); - SET_REQ_WIN32_ERROR(req, err); - return; - } else if (ret > len) { - len = ret; - pathw = uv__reallocf(pathw, len * sizeof(*pathw)); - if (pathw == NULL) { - SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); - return; - } - goto retry_get_full_path_name; - } - if (fpart != 0) - *fpart = L'\0'; - - goto retry_get_disk_free_space; + if (handle == INVALID_HANDLE_VALUE) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; } - if (pathw != req->file.pathw) { - uv__free(pathw); + + nt_status = pNtQueryVolumeInformationFile(handle, + &io_status, + &info, + sizeof info, + FileFsFullSizeInformation); + CloseHandle(handle); + + if (NT_ERROR(nt_status)) { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); + return; } stat_fs = uv__malloc(sizeof(*stat_fs)); @@ -2777,10 +3147,12 @@ } stat_fs->f_type = 0; - stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster; - stat_fs->f_blocks = total_clusters; - stat_fs->f_bfree = free_clusters; - stat_fs->f_bavail = free_clusters; + stat_fs->f_bsize = (uint64_t)info.SectorsPerAllocationUnit * + info.BytesPerSector; + stat_fs->f_frsize = stat_fs->f_bsize; + stat_fs->f_blocks = info.TotalAllocationUnits.QuadPart; + stat_fs->f_bfree = info.ActualAvailableAllocationUnits.QuadPart; + stat_fs->f_bavail = info.CallerAvailableAllocationUnits.QuadPart; stat_fs->f_files = 0; stat_fs->f_ffree = 0; req->ptr = stat_fs; @@ -2843,7 +3215,7 @@ uv_fs_t* req; req = container_of(w, uv_fs_t, work_req); - uv__req_unregister(req->loop, req); + uv__req_unregister(req->loop); if (status == UV_ECANCELED) { assert(req->result == 0);
diff --git a/Utilities/cmlibuv/src/win/getaddrinfo.c b/Utilities/cmlibuv/src/win/getaddrinfo.c index dfab860..4b8ee75 100644 --- a/Utilities/cmlibuv/src/win/getaddrinfo.c +++ b/Utilities/cmlibuv/src/win/getaddrinfo.c
@@ -71,10 +71,9 @@ DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo); #endif - -/* Adjust size value to be multiple of 4. Use to keep pointer aligned. - * Do we need different versions of this for different architectures? */ -#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2) +static size_t align_offset(size_t off, size_t alignment) { + return ((off + alignment - 1) / alignment) * alignment; +} #ifndef NDIS_IF_MAX_STRING_SIZE #define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE @@ -103,16 +102,7 @@ * Each size calculation is adjusted to avoid unaligned pointers. */ static void uv__getaddrinfo_done(struct uv__work* w, int status) { - uv_getaddrinfo_t* req; - int addrinfo_len = 0; - int name_len = 0; - size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo)); - struct addrinfoW* addrinfow_ptr; - struct addrinfo* addrinfo_ptr; - char* alloc_ptr = NULL; - char* cur_ptr = NULL; - - req = container_of(w, uv_getaddrinfo_t, work_req); + uv_getaddrinfo_t* req = container_of(w, uv_getaddrinfo_t, work_req); /* release input parameter memory */ uv__free(req->alloc); @@ -125,41 +115,44 @@ } if (req->retcode == 0) { + char* alloc_ptr = NULL; + size_t cur_off = 0; + size_t addrinfo_len; /* Convert addrinfoW to addrinfo. First calculate required length. */ - addrinfow_ptr = req->addrinfow; + struct addrinfoW* addrinfow_ptr = req->addrinfow; while (addrinfow_ptr != NULL) { - addrinfo_len += addrinfo_struct_len + - ALIGNED_SIZE(addrinfow_ptr->ai_addrlen); + cur_off = align_offset(cur_off, sizeof(void*)); + cur_off += sizeof(struct addrinfo); + /* TODO: This alignment could be smaller, if we could + portably get the alignment for sockaddr. */ + cur_off = align_offset(cur_off, sizeof(void*)); + cur_off += addrinfow_ptr->ai_addrlen; if (addrinfow_ptr->ai_canonname != NULL) { - name_len = WideCharToMultiByte(CP_UTF8, - 0, - addrinfow_ptr->ai_canonname, - -1, - NULL, - 0, - NULL, - NULL); - if (name_len == 0) { - req->retcode = uv_translate_sys_error(GetLastError()); + ssize_t name_len = + uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1); + if (name_len < 0) { + req->retcode = name_len; goto complete; } - addrinfo_len += ALIGNED_SIZE(name_len); + cur_off += name_len + 1; } addrinfow_ptr = addrinfow_ptr->ai_next; } /* allocate memory for addrinfo results */ - alloc_ptr = (char*)uv__malloc(addrinfo_len); + addrinfo_len = cur_off; + alloc_ptr = uv__malloc(addrinfo_len); /* do conversions */ if (alloc_ptr != NULL) { - cur_ptr = alloc_ptr; + struct addrinfo *addrinfo_ptr = (struct addrinfo *)alloc_ptr; + cur_off = 0; addrinfow_ptr = req->addrinfow; - while (addrinfow_ptr != NULL) { + for (;;) { + cur_off += sizeof(struct addrinfo); + assert(cur_off <= addrinfo_len); /* copy addrinfo struct data */ - assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len); - addrinfo_ptr = (struct addrinfo*)cur_ptr; addrinfo_ptr->ai_family = addrinfow_ptr->ai_family; addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype; addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol; @@ -169,48 +162,38 @@ addrinfo_ptr->ai_addr = NULL; addrinfo_ptr->ai_next = NULL; - cur_ptr += addrinfo_struct_len; - /* copy sockaddr */ if (addrinfo_ptr->ai_addrlen > 0) { - assert(cur_ptr + addrinfo_ptr->ai_addrlen <= - alloc_ptr + addrinfo_len); - memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen); - addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr; - cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen); + cur_off = align_offset(cur_off, sizeof(void *)); + addrinfo_ptr->ai_addr = (struct sockaddr *)(alloc_ptr + cur_off); + cur_off += addrinfo_ptr->ai_addrlen; + assert(cur_off <= addrinfo_len); + memcpy(addrinfo_ptr->ai_addr, + addrinfow_ptr->ai_addr, + addrinfo_ptr->ai_addrlen); } /* convert canonical name to UTF-8 */ if (addrinfow_ptr->ai_canonname != NULL) { - name_len = WideCharToMultiByte(CP_UTF8, - 0, - addrinfow_ptr->ai_canonname, + ssize_t name_len = addrinfo_len - cur_off; + addrinfo_ptr->ai_canonname = alloc_ptr + cur_off; + int r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, - NULL, - 0, - NULL, - NULL); - assert(name_len > 0); - assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len); - name_len = WideCharToMultiByte(CP_UTF8, - 0, - addrinfow_ptr->ai_canonname, - -1, - cur_ptr, - name_len, - NULL, - NULL); - assert(name_len > 0); - addrinfo_ptr->ai_canonname = cur_ptr; - cur_ptr += ALIGNED_SIZE(name_len); + addrinfo_ptr->ai_canonname, + (size_t*)&name_len); + assert(r == 0); + cur_off += name_len + 1; + assert(cur_off <= addrinfo_len); } - assert(cur_ptr <= alloc_ptr + addrinfo_len); /* set next ptr */ addrinfow_ptr = addrinfow_ptr->ai_next; - if (addrinfow_ptr != NULL) { - addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr; - } + if (addrinfow_ptr == NULL) + break; + cur_off = align_offset(cur_off, sizeof(void *)); + struct addrinfo *next_addrinfo_ptr = (struct addrinfo *)(alloc_ptr + cur_off); + addrinfo_ptr->ai_next = next_addrinfo_ptr; + addrinfo_ptr = next_addrinfo_ptr; } req->addrinfo = (struct addrinfo*)alloc_ptr; } else { @@ -225,7 +208,7 @@ } complete: - uv__req_unregister(req->loop, req); + uv__req_unregister(req->loop); /* finally do callback with converted result */ if (req->getaddrinfo_cb) @@ -261,12 +244,13 @@ const char* service, const struct addrinfo* hints) { char hostname_ascii[256]; - int nodesize = 0; - int servicesize = 0; - int hintssize = 0; - char* alloc_ptr = NULL; - int err; - long rc; + size_t off = 0; + size_t nodesize = 0; + size_t servicesize = 0; + size_t serviceoff = 0; + size_t hintssize = 0; + size_t hintoff = 0; + ssize_t rc; if (req == NULL || (node == NULL && service == NULL)) { return UV_EINVAL; @@ -286,56 +270,38 @@ hostname_ascii + sizeof(hostname_ascii)); if (rc < 0) return rc; - nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, hostname_ascii, - -1, NULL, 0) * sizeof(WCHAR)); - if (nodesize == 0) { - err = GetLastError(); - goto error; - } + nodesize = strlen(hostname_ascii) + 1; node = hostname_ascii; + off += nodesize * sizeof(WCHAR); } if (service != NULL) { - servicesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, - 0, - service, - -1, - NULL, - 0) * - sizeof(WCHAR)); - if (servicesize == 0) { - err = GetLastError(); - goto error; - } + rc = uv_wtf8_length_as_utf16(service); + if (rc < 0) + return rc; + servicesize = rc; + off = align_offset(off, sizeof(WCHAR)); + serviceoff = off; + off += servicesize * sizeof(WCHAR); } + if (hints != NULL) { - hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW)); + off = align_offset(off, sizeof(void *)); + hintoff = off; + hintssize = sizeof(struct addrinfoW); + off += hintssize; } /* allocate memory for inputs, and partition it as needed */ - alloc_ptr = (char*)uv__malloc(nodesize + servicesize + hintssize); - if (!alloc_ptr) { - err = WSAENOBUFS; - goto error; - } - - /* save alloc_ptr now so we can free if error */ - req->alloc = (void*)alloc_ptr; + req->alloc = uv__malloc(off); + if (!req->alloc) + return UV_ENOMEM; /* Convert node string to UTF16 into allocated memory and save pointer in the - * request. */ + * request. The node here has been converted to ascii. */ if (node != NULL) { - req->node = (WCHAR*)alloc_ptr; - if (MultiByteToWideChar(CP_UTF8, - 0, - node, - -1, - (WCHAR*) alloc_ptr, - nodesize / sizeof(WCHAR)) == 0) { - err = GetLastError(); - goto error; - } - alloc_ptr += nodesize; + req->node = (WCHAR*) req->alloc; + uv_wtf8_to_utf16(node, req->node, nodesize); } else { req->node = NULL; } @@ -343,24 +309,15 @@ /* Convert service string to UTF16 into allocated memory and save pointer in * the req. */ if (service != NULL) { - req->service = (WCHAR*)alloc_ptr; - if (MultiByteToWideChar(CP_UTF8, - 0, - service, - -1, - (WCHAR*) alloc_ptr, - servicesize / sizeof(WCHAR)) == 0) { - err = GetLastError(); - goto error; - } - alloc_ptr += servicesize; + req->service = (WCHAR*) ((char*) req->alloc + serviceoff); + uv_wtf8_to_utf16(service, req->service, servicesize); } else { req->service = NULL; } /* copy hints to allocated memory and save pointer in req */ if (hints != NULL) { - req->addrinfow = (struct addrinfoW*)alloc_ptr; + req->addrinfow = (struct addrinfoW*) ((char*) req->alloc + hintoff); req->addrinfow->ai_family = hints->ai_family; req->addrinfow->ai_socktype = hints->ai_socktype; req->addrinfow->ai_protocol = hints->ai_protocol; @@ -373,7 +330,7 @@ req->addrinfow = NULL; } - uv__req_register(loop, req); + uv__req_register(loop); if (getaddrinfo_cb) { uv__work_submit(loop, @@ -387,19 +344,11 @@ uv__getaddrinfo_done(&req->work_req, 0); return req->retcode; } - -error: - if (req != NULL) { - uv__free(req->alloc); - req->alloc = NULL; - } - return uv_translate_sys_error(err); } int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { NET_LUID luid; wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */ - DWORD bufsize; int r; if (buffer == NULL || size == NULL || *size == 0) @@ -415,31 +364,7 @@ if (r != 0) return uv_translate_sys_error(r); - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL); - - if (bufsize == 0) { - return uv_translate_sys_error(GetLastError()); - } else if (bufsize > *size) { - *size = bufsize; - return UV_ENOBUFS; - } - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - wname, - -1, - buffer, - *size, - NULL, - NULL); - - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); - - *size = bufsize - 1; - return 0; + return uv__copy_utf16_to_utf8(wname, -1, buffer, size); } int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
diff --git a/Utilities/cmlibuv/src/win/getnameinfo.c b/Utilities/cmlibuv/src/win/getnameinfo.c index b377338..6955495 100644 --- a/Utilities/cmlibuv/src/win/getnameinfo.c +++ b/Utilities/cmlibuv/src/win/getnameinfo.c
@@ -42,6 +42,7 @@ uv_getnameinfo_t* req; WCHAR host[NI_MAXHOST]; WCHAR service[NI_MAXSERV]; + size_t size; int ret; req = container_of(w, uv_getnameinfo_t, work_req); @@ -57,29 +58,17 @@ return; } - ret = WideCharToMultiByte(CP_UTF8, - 0, - host, - -1, - req->host, - sizeof(req->host), - NULL, - NULL); - if (ret == 0) { - req->retcode = uv_translate_sys_error(GetLastError()); + size = sizeof(req->host); + ret = uv__copy_utf16_to_utf8(host, -1, req->host, &size); + if (ret < 0) { + req->retcode = ret; return; } - ret = WideCharToMultiByte(CP_UTF8, - 0, - service, - -1, - req->service, - sizeof(req->service), - NULL, - NULL); - if (ret == 0) { - req->retcode = uv_translate_sys_error(GetLastError()); + size = sizeof(req->service); + ret = uv__copy_utf16_to_utf8(service, -1, req->service, &size); + if (ret < 0) { + req->retcode = ret; } } @@ -93,7 +82,7 @@ char* service; req = container_of(w, uv_getnameinfo_t, work_req); - uv__req_unregister(req->loop, req); + uv__req_unregister(req->loop); host = service = NULL; if (status == UV_ECANCELED) { @@ -135,7 +124,7 @@ } UV_REQ_INIT(req, UV_GETNAMEINFO); - uv__req_register(loop, req); + uv__req_register(loop); req->getnameinfo_cb = getnameinfo_cb; req->flags = flags;
diff --git a/Utilities/cmlibuv/src/win/handle-inl.h b/Utilities/cmlibuv/src/win/handle-inl.h index 5c843c2..e30d148 100644 --- a/Utilities/cmlibuv/src/win/handle-inl.h +++ b/Utilities/cmlibuv/src/win/handle-inl.h
@@ -75,7 +75,7 @@ #define uv__handle_close(handle) \ do { \ - QUEUE_REMOVE(&(handle)->handle_queue); \ + uv__queue_remove(&(handle)->handle_queue); \ uv__active_handle_rm((uv_handle_t*) (handle)); \ \ (handle)->flags |= UV_HANDLE_CLOSED; \ @@ -95,74 +95,6 @@ } -INLINE static void uv__process_endgames(uv_loop_t* loop) { - uv_handle_t* handle; - - while (loop->endgame_handles) { - handle = loop->endgame_handles; - loop->endgame_handles = handle->endgame_next; - - handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED; - - switch (handle->type) { - case UV_TCP: - uv__tcp_endgame(loop, (uv_tcp_t*) handle); - break; - - case UV_NAMED_PIPE: - uv__pipe_endgame(loop, (uv_pipe_t*) handle); - break; - - case UV_TTY: - uv__tty_endgame(loop, (uv_tty_t*) handle); - break; - - case UV_UDP: - uv__udp_endgame(loop, (uv_udp_t*) handle); - break; - - case UV_POLL: - uv__poll_endgame(loop, (uv_poll_t*) handle); - break; - - case UV_TIMER: - uv__timer_close((uv_timer_t*) handle); - uv__handle_close(handle); - break; - - case UV_PREPARE: - case UV_CHECK: - case UV_IDLE: - uv__loop_watcher_endgame(loop, handle); - break; - - case UV_ASYNC: - uv__async_endgame(loop, (uv_async_t*) handle); - break; - - case UV_SIGNAL: - uv__signal_endgame(loop, (uv_signal_t*) handle); - break; - - case UV_PROCESS: - uv__process_endgame(loop, (uv_process_t*) handle); - break; - - case UV_FS_EVENT: - uv__fs_event_endgame(loop, (uv_fs_event_t*) handle); - break; - - case UV_FS_POLL: - uv__fs_poll_endgame(loop, (uv_fs_poll_t*) handle); - break; - - default: - assert(0); - break; - } - } -} - INLINE static HANDLE uv__get_osfhandle(int fd) { /* _get_osfhandle() raises an assert in debug builds if the FD is invalid.
diff --git a/Utilities/cmlibuv/src/win/internal.h b/Utilities/cmlibuv/src/win/internal.h index 3e92a66..93ed2fa 100644 --- a/Utilities/cmlibuv/src/win/internal.h +++ b/Utilities/cmlibuv/src/win/internal.h
@@ -172,18 +172,8 @@ uv_req_t* req); void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, uv_write_t* req); -/* - * uv__process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working - * TODO: find a way to remove it - */ -void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, - uv_req_t* raw_req); -/* - * uv__process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working - * TODO: find a way to remove it - */ -void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, - uv_connect_t* req); +#define uv__process_tty_accept_req(loop, handle, req) abort() +#define uv__process_tty_connect_req(loop, handle, req) abort() void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown_t* req); @@ -271,9 +261,9 @@ uint64_t uv__hrtime(unsigned int scale); __declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall); -int uv__getpwuid_r(uv_passwd_t* pwd); -int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8); -int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16); +int uv__convert_utf16_to_utf8(const WCHAR* utf16, size_t utf16len, char** utf8); +int uv__copy_utf16_to_utf8(const WCHAR* utf16, size_t utf16len, char* utf8, size_t *size); +int uv__convert_utf8_to_utf16(const char* utf8, WCHAR** utf16); typedef int (WINAPI *uv__peersockfunc)(SOCKET, struct sockaddr*, int*); @@ -283,7 +273,7 @@ int* namelen, int delayed_error); -int uv__random_rtlgenrandom(void* buf, size_t buflen); +int uv__random_winrandom(void* buf, size_t buflen); /* @@ -344,4 +334,6 @@ */ void uv__init_detect_system_wakeup(void); +int uv_translate_write_sys_error(int sys_errno); + #endif /* UV_WIN_INTERNAL_H_ */
diff --git a/Utilities/cmlibuv/src/win/pipe.c b/Utilities/cmlibuv/src/win/pipe.c index 9984618..8f86a1fe 100644 --- a/Utilities/cmlibuv/src/win/pipe.c +++ b/Utilities/cmlibuv/src/win/pipe.c
@@ -49,13 +49,13 @@ /* Pipe prefix */ static char pipe_prefix[] = "\\\\?\\pipe"; -static const int pipe_prefix_len = sizeof(pipe_prefix) - 1; +static const size_t pipe_prefix_len = sizeof(pipe_prefix) - 1; /* IPC incoming xfer queue item. */ typedef struct { uv__ipc_socket_xfer_type_t xfer_type; uv__ipc_socket_xfer_info_t xfer_info; - QUEUE member; + struct uv__queue member; } uv__ipc_xfer_queue_item_t; /* IPC frame header flags. */ @@ -98,8 +98,16 @@ static void eof_timer_close_cb(uv_handle_t* handle); -static void uv__unique_pipe_name(char* ptr, char* name, size_t size) { - snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId()); +/* Does the file path contain embedded nul bytes? */ +static int includes_nul(const char *s, size_t n) { + if (n == 0) + return 0; + return NULL != memchr(s, '\0', n); +} + + +static void uv__unique_pipe_name(unsigned long long ptr, char* name, size_t size) { + snprintf(name, size, "\\\\?\\pipe\\uv\\%llu-%lu", ptr, GetCurrentProcessId()); } @@ -111,7 +119,7 @@ handle->name = NULL; handle->pipe.conn.ipc_remote_pid = 0; handle->pipe.conn.ipc_data_frame.payload_remaining = 0; - QUEUE_INIT(&handle->pipe.conn.ipc_xfer_queue); + uv__queue_init(&handle->pipe.conn.ipc_xfer_queue); handle->pipe.conn.ipc_xfer_queue_length = 0; handle->ipc = ipc; handle->pipe.conn.non_overlapped_writes_tail = NULL; @@ -191,7 +199,7 @@ if (pipe->u.fd == -1) CloseHandle(pipe->handle); else - close(pipe->u.fd); + _close(pipe->u.fd); pipe->u.fd = -1; pipe->handle = INVALID_HANDLE_VALUE; @@ -200,7 +208,7 @@ static int uv__pipe_server( HANDLE* pipeHandle_ptr, DWORD access, - char* name, size_t nameSize, char* random) { + char* name, size_t nameSize, unsigned long long random) { HANDLE pipeHandle; int err; @@ -241,7 +249,7 @@ static int uv__create_pipe_pair( HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr, unsigned int server_flags, unsigned int client_flags, - int inherit_client, char* random) { + int inherit_client, unsigned long long random) { /* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */ char pipe_name[64]; SECURITY_ATTRIBUTES sa; @@ -349,7 +357,12 @@ /* TODO: better source of local randomness than &fds? */ read_flags |= UV_READABLE_PIPE; write_flags |= UV_WRITABLE_PIPE; - err = uv__create_pipe_pair(&readh, &writeh, read_flags, write_flags, 0, (char*) &fds[0]); + err = uv__create_pipe_pair(&readh, + &writeh, + read_flags, + write_flags, + 0, + (uintptr_t) &fds[0]); if (err != 0) return err; temp[0] = _open_osfhandle((intptr_t) readh, 0); @@ -413,7 +426,7 @@ } err = uv__create_pipe_pair(&server_pipe, &client_pipe, - server_flags, client_flags, 1, (char*) server_pipe); + server_flags, client_flags, 1, (uintptr_t) server_pipe); if (err) goto error; @@ -637,13 +650,13 @@ if (handle->flags & UV_HANDLE_CONNECTION) { /* Free pending sockets */ - while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) { - QUEUE* q; + while (!uv__queue_empty(&handle->pipe.conn.ipc_xfer_queue)) { + struct uv__queue* q; SOCKET socket; - q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue); - QUEUE_REMOVE(q); - xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member); + q = uv__queue_head(&handle->pipe.conn.ipc_xfer_queue); + uv__queue_remove(q); + xfer_queue_item = uv__queue_data(q, uv__ipc_xfer_queue_item_t, member); /* Materialize socket and close it */ socket = WSASocketW(FROM_PROTOCOL_INFO, @@ -659,15 +672,10 @@ } handle->pipe.conn.ipc_xfer_queue_length = 0; - if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) { - UnregisterWait(handle->read_req.wait_handle); - handle->read_req.wait_handle = INVALID_HANDLE_VALUE; - } - if (handle->read_req.event_handle != NULL) { - CloseHandle(handle->read_req.event_handle); - handle->read_req.event_handle = NULL; - } + assert(handle->read_req.wait_handle == INVALID_HANDLE_VALUE); + if (handle->read_req.event_handle != NULL) { + CloseHandle(handle->read_req.event_handle); + handle->read_req.event_handle = NULL; } if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) @@ -694,28 +702,60 @@ /* Creates a pipe server. */ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + return uv_pipe_bind2(handle, name, strlen(name), 0); +} + + +int uv_pipe_bind2(uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags) { uv_loop_t* loop = handle->loop; - int i, err, nameSize; + int i, err; uv_pipe_accept_t* req; + char* name_copy; + + if (flags & ~UV_PIPE_NO_TRUNCATE) { + return UV_EINVAL; + } + + if (name == NULL) { + return UV_EINVAL; + } + + if (namelen == 0) { + return UV_EINVAL; + } + + if (includes_nul(name, namelen)) { + return UV_EINVAL; + } if (handle->flags & UV_HANDLE_BOUND) { return UV_EINVAL; } - if (!name) { - return UV_EINVAL; - } if (uv__is_closing(handle)) { return UV_EINVAL; } + + name_copy = uv__malloc(namelen + 1); + if (name_copy == NULL) { + return UV_ENOMEM; + } + + memcpy(name_copy, name, namelen); + name_copy[namelen] = '\0'; + if (!(handle->flags & UV_HANDLE_PIPESERVER)) { handle->pipe.serv.pending_instances = default_pending_pipe_instances; } + err = UV_ENOMEM; handle->pipe.serv.accept_reqs = (uv_pipe_accept_t*) uv__malloc(sizeof(uv_pipe_accept_t) * handle->pipe.serv.pending_instances); - if (!handle->pipe.serv.accept_reqs) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + if (handle->pipe.serv.accept_reqs == NULL) { + goto error; } for (i = 0; i < handle->pipe.serv.pending_instances; i++) { @@ -726,20 +766,12 @@ req->next_pending = NULL; } - /* Convert name to UTF16. */ - nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = uv__malloc(nameSize); - if (!handle->name) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } + /* TODO(bnoordhuis) Add converters that take a |length| parameter. */ + err = uv__convert_utf8_to_utf16(name_copy, &handle->name); + uv__free(name_copy); + name_copy = NULL; - if (!MultiByteToWideChar(CP_UTF8, - 0, - name, - -1, - handle->name, - nameSize / sizeof(WCHAR))) { - err = GetLastError(); + if (err) { goto error; } @@ -753,9 +785,11 @@ TRUE)) { err = GetLastError(); if (err == ERROR_ACCESS_DENIED) { - err = WSAEADDRINUSE; /* Translates to UV_EADDRINUSE. */ + err = UV_EADDRINUSE; } else if (err == ERROR_PATH_NOT_FOUND || err == ERROR_INVALID_NAME) { - err = WSAEACCES; /* Translates to UV_EACCES. */ + err = UV_EACCES; + } else { + err = uv_translate_sys_error(err); } goto error; } @@ -767,12 +801,13 @@ return 0; error: - if (handle->name) { - uv__free(handle->name); - handle->name = NULL; - } + uv__free(handle->pipe.serv.accept_reqs); + uv__free(handle->name); + uv__free(name_copy); + handle->pipe.serv.accept_reqs = NULL; + handle->name = NULL; - return uv_translate_sys_error(err); + return err; } @@ -792,15 +827,17 @@ /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait * up to 30 seconds for the pipe to become available with WaitNamedPipe. */ - while (WaitNamedPipeW(handle->name, 30000)) { + while (WaitNamedPipeW(req->u.connect.name, 30000)) { /* The pipe is now available, try to connect. */ - pipeHandle = open_named_pipe(handle->name, &duplex_flags); + pipeHandle = open_named_pipe(req->u.connect.name, &duplex_flags); if (pipeHandle != INVALID_HANDLE_VALUE) break; SwitchToThread(); } + uv__free(req->u.connect.name); + req->u.connect.name = NULL; if (pipeHandle != INVALID_HANDLE_VALUE) { SET_REQ_SUCCESS(req); req->u.connect.pipeHandle = pipeHandle; @@ -816,18 +853,70 @@ } -void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, - const char* name, uv_connect_cb cb) { - uv_loop_t* loop = handle->loop; - int err, nameSize; +void uv_pipe_connect(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + uv_connect_cb cb) { + uv_loop_t* loop; + int err; + + err = uv_pipe_connect2(req, handle, name, strlen(name), 0, cb); + + if (err) { + loop = handle->loop; + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, err); + uv__insert_pending_req(loop, (uv_req_t*) req); + handle->reqs_pending++; + REGISTER_HANDLE_REQ(loop, handle); + } +} + + +int uv_pipe_connect2(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags, + uv_connect_cb cb) { + uv_loop_t* loop; + int err; + size_t nameSize; HANDLE pipeHandle = INVALID_HANDLE_VALUE; DWORD duplex_flags; + char* name_copy; + loop = handle->loop; UV_REQ_INIT(req, UV_CONNECT); req->handle = (uv_stream_t*) handle; req->cb = cb; req->u.connect.pipeHandle = INVALID_HANDLE_VALUE; req->u.connect.duplex_flags = 0; + req->u.connect.name = NULL; + + if (flags & ~UV_PIPE_NO_TRUNCATE) { + return UV_EINVAL; + } + + if (name == NULL) { + return UV_EINVAL; + } + + if (namelen == 0) { + return UV_EINVAL; + } + + if (includes_nul(name, namelen)) { + return UV_EINVAL; + } + + name_copy = uv__malloc(namelen + 1); + if (name_copy == NULL) { + return UV_ENOMEM; + } + + memcpy(name_copy, name, namelen); + name_copy[namelen] = '\0'; if (handle->flags & UV_HANDLE_PIPESERVER) { err = ERROR_INVALID_PARAMETER; @@ -839,38 +928,41 @@ } uv__pipe_connection_init(handle); - /* Convert name to UTF16. */ - nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = uv__malloc(nameSize); - if (!handle->name) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } + /* TODO(bnoordhuis) Add converters that take a |length| parameter. */ + err = uv__convert_utf8_to_utf16(name_copy, &handle->name); + uv__free(name_copy); + name_copy = NULL; - if (!MultiByteToWideChar(CP_UTF8, - 0, - name, - -1, - handle->name, - nameSize / sizeof(WCHAR))) { - err = GetLastError(); + if (err) { + err = ERROR_NO_UNICODE_TRANSLATION; goto error; } pipeHandle = open_named_pipe(handle->name, &duplex_flags); if (pipeHandle == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_PIPE_BUSY) { + nameSize = (wcslen(handle->name) + 1) * sizeof(WCHAR); + req->u.connect.name = uv__malloc(nameSize); + if (!req->u.connect.name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + } + + memcpy(req->u.connect.name, handle->name, nameSize); + /* Wait for the server to make a pipe instance available. */ if (!QueueUserWorkItem(&pipe_connect_thread_proc, req, WT_EXECUTELONGFUNCTION)) { + uv__free(req->u.connect.name); + req->u.connect.name = NULL; err = GetLastError(); goto error; } - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); handle->reqs_pending++; - return; + return 0; } err = GetLastError(); @@ -882,10 +974,12 @@ SET_REQ_SUCCESS(req); uv__insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); - return; + REGISTER_HANDLE_REQ(loop, handle); + return 0; error: + uv__free(name_copy); + if (handle->name) { uv__free(handle->name); handle->name = NULL; @@ -898,8 +992,8 @@ SET_REQ_ERROR(req, err); uv__insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); - return; + REGISTER_HANDLE_REQ(loop, handle); + return 0; } @@ -1050,28 +1144,29 @@ uv_loop_t* loop = server->loop; uv_pipe_t* pipe_client; uv_pipe_accept_t* req; - QUEUE* q; + struct uv__queue* q; uv__ipc_xfer_queue_item_t* item; int err; if (server->ipc) { - if (QUEUE_EMPTY(&server->pipe.conn.ipc_xfer_queue)) { + if (uv__queue_empty(&server->pipe.conn.ipc_xfer_queue)) { /* No valid pending sockets. */ return WSAEWOULDBLOCK; } - q = QUEUE_HEAD(&server->pipe.conn.ipc_xfer_queue); - QUEUE_REMOVE(q); + q = uv__queue_head(&server->pipe.conn.ipc_xfer_queue); + uv__queue_remove(q); server->pipe.conn.ipc_xfer_queue_length--; - item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member); + item = uv__queue_data(q, uv__ipc_xfer_queue_item_t, member); err = uv__tcp_xfer_import( (uv_tcp_t*) client, item->xfer_type, &item->xfer_info); - if (err != 0) - return err; uv__free(item); + if (err != 0) + return err; + } else { pipe_client = (uv_pipe_t*) client; uv__pipe_connection_init(pipe_client); @@ -1322,13 +1417,12 @@ } if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - if (req->wait_handle == INVALID_HANDLE_VALUE) { - if (!RegisterWaitForSingleObject(&req->wait_handle, - req->event_handle, post_completion_read_wait, (void*) req, - INFINITE, WT_EXECUTEINWAITTHREAD)) { - SET_REQ_ERROR(req, GetLastError()); - goto error; - } + assert(req->wait_handle == INVALID_HANDLE_VALUE); + if (!RegisterWaitForSingleObject(&req->wait_handle, + req->event_handle, post_completion_read_wait, (void*) req, + INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { + SET_REQ_ERROR(req, GetLastError()); + goto error; } } } @@ -1356,16 +1450,16 @@ handle->read_cb = read_cb; handle->alloc_cb = alloc_cb; + if (handle->read_req.event_handle == NULL) { + handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL); + if (handle->read_req.event_handle == NULL) { + uv_fatal_error(GetLastError(), "CreateEvent"); + } + } + /* If reading was stopped and then started again, there could still be a read * request pending. */ if (!(handle->flags & UV_HANDLE_READ_PENDING)) { - if (handle->flags & UV_HANDLE_EMULATE_IOCP && - handle->read_req.event_handle == NULL) { - handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL); - if (handle->read_req.event_handle == NULL) { - uv_fatal_error(GetLastError(), "CreateEvent"); - } - } uv__pipe_queue_read(loop, handle); } @@ -1543,7 +1637,7 @@ req->u.io.queued_bytes = 0; } - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; POST_COMPLETION_FOR_REQ(loop, req); @@ -1591,7 +1685,7 @@ CloseHandle(req->event_handle); req->event_handle = NULL; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; return 0; @@ -1618,13 +1712,13 @@ if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (!RegisterWaitForSingleObject(&req->wait_handle, req->event_handle, post_completion_write_wait, (void*) req, - INFINITE, WT_EXECUTEINWAITTHREAD)) { + INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { return GetLastError(); } } } - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; @@ -1638,8 +1732,12 @@ /* If the both ends of the IPC pipe are owned by the same process, * the remote end pid may not yet be set. If so, do it here. * TODO: this is weird; it'd probably better to use a handshake. */ - if (*pid == 0) - *pid = GetCurrentProcessId(); + if (*pid == 0) { + GetNamedPipeClientProcessId(handle->handle, pid); + if (*pid == GetCurrentProcessId()) { + GetNamedPipeServerProcessId(handle->handle, pid); + } + } return *pid; } @@ -1790,7 +1888,7 @@ static void uv__pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle, - int error, uv_buf_t buf) { + DWORD error, uv_buf_t buf) { if (error == ERROR_BROKEN_PIPE) { uv__pipe_read_eof(loop, handle, buf); } else { @@ -1812,7 +1910,7 @@ item->xfer_type = xfer_type; item->xfer_info = *xfer_info; - QUEUE_INSERT_TAIL(&handle->pipe.conn.ipc_xfer_queue, &item->member); + uv__queue_insert_tail(&handle->pipe.conn.ipc_xfer_queue, &item->member); handle->pipe.conn.ipc_xfer_queue_length++; } @@ -1820,17 +1918,25 @@ /* Read an exact number of bytes from a pipe. If an error or end-of-file is * encountered before the requested number of bytes are read, an error is * returned. */ -static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) { - DWORD bytes_read, bytes_read_now; +static DWORD uv__pipe_read_exactly(uv_pipe_t* handle, void* buffer, DWORD count) { + uv_read_t* req; + DWORD bytes_read; + DWORD bytes_read_now; bytes_read = 0; while (bytes_read < count) { - if (!ReadFile(h, + req = &handle->read_req; + memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1); + if (!ReadFile(handle->handle, (char*) buffer + bytes_read, count - bytes_read, &bytes_read_now, - NULL)) { - return GetLastError(); + &req->u.io.overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) + return GetLastError(); + if (!GetOverlappedResult(handle->handle, &req->u.io.overlapped, &bytes_read_now, TRUE)) + return GetLastError(); } bytes_read += bytes_read_now; @@ -1841,16 +1947,19 @@ } -static DWORD uv__pipe_read_data(uv_loop_t* loop, - uv_pipe_t* handle, - DWORD suggested_bytes, - DWORD max_bytes) { - DWORD bytes_read; +static int uv__pipe_read_data(uv_loop_t* loop, + uv_pipe_t* handle, + DWORD* bytes_read, /* inout argument */ + DWORD max_bytes) { uv_buf_t buf; + uv_read_t* req; + DWORD r; + DWORD bytes_available; + int more; /* Ask the user for a buffer to read data into. */ buf = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, suggested_bytes, &buf); + handle->alloc_cb((uv_handle_t*) handle, *bytes_read, &buf); if (buf.base == NULL || buf.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); return 0; /* Break out of read loop. */ @@ -1859,33 +1968,77 @@ /* Ensure we read at most the smaller of: * (a) the length of the user-allocated buffer. * (b) the maximum data length as specified by the `max_bytes` argument. + * (c) the amount of data that can be read non-blocking */ if (max_bytes > buf.len) max_bytes = buf.len; - /* Read into the user buffer. */ - if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) { - uv__pipe_read_error_or_eof(loop, handle, GetLastError(), buf); - return 0; /* Break out of read loop. */ + if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { + /* The user failed to supply a pipe that can be used non-blocking or with + * threads. Try to estimate the amount of data that is safe to read without + * blocking, in a race-y way however. */ + bytes_available = 0; + if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &bytes_available, NULL)) { + r = GetLastError(); + } else { + if (max_bytes > bytes_available) + max_bytes = bytes_available; + *bytes_read = 0; + if (max_bytes == 0 || ReadFile(handle->handle, buf.base, max_bytes, bytes_read, NULL)) + r = ERROR_SUCCESS; + else + r = GetLastError(); + } + more = max_bytes < bytes_available; + } else { + /* Read into the user buffer. + * Prepare an Event so that we can cancel if it doesn't complete immediately. + */ + req = &handle->read_req; + memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1); + if (ReadFile(handle->handle, buf.base, max_bytes, bytes_read, &req->u.io.overlapped)) { + r = ERROR_SUCCESS; + } else { + r = GetLastError(); + *bytes_read = 0; + if (r == ERROR_IO_PENDING) { + r = CancelIoEx(handle->handle, &req->u.io.overlapped); + assert(r || GetLastError() == ERROR_NOT_FOUND); + if (GetOverlappedResult(handle->handle, &req->u.io.overlapped, bytes_read, TRUE)) { + r = ERROR_SUCCESS; + } else { + r = GetLastError(); + *bytes_read = 0; + } + } + } + more = *bytes_read == max_bytes; } /* Call the read callback. */ - handle->read_cb((uv_stream_t*) handle, bytes_read, &buf); + if (r == ERROR_SUCCESS || r == ERROR_OPERATION_ABORTED) + handle->read_cb((uv_stream_t*) handle, *bytes_read, &buf); + else + uv__pipe_read_error_or_eof(loop, handle, r, buf); - return bytes_read; + return more; } -static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) { - uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining; - int err; +static int uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) { + uint32_t* data_remaining; + DWORD err; + DWORD more; + DWORD bytes_read; + + data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining; if (*data_remaining > 0) { /* Read frame data payload. */ - DWORD bytes_read = - uv__pipe_read_data(loop, handle, *data_remaining, *data_remaining); + bytes_read = *data_remaining; + more = uv__pipe_read_data(loop, handle, &bytes_read, bytes_read); *data_remaining -= bytes_read; - return bytes_read; } else { /* Start of a new IPC frame. */ @@ -1896,7 +2049,7 @@ /* Read the IPC frame header. */ err = uv__pipe_read_exactly( - handle->handle, &frame_header, sizeof frame_header); + handle, &frame_header, sizeof frame_header); if (err) goto error; @@ -1932,21 +2085,24 @@ /* If no socket xfer info follows, return here. Data will be read in a * subsequent invocation of uv__pipe_read_ipc(). */ - if (xfer_type == UV__IPC_SOCKET_XFER_NONE) - return sizeof frame_header; /* Number of bytes read. */ + if (xfer_type != UV__IPC_SOCKET_XFER_NONE) { + /* Read transferred socket information. */ + err = uv__pipe_read_exactly(handle, &xfer_info, sizeof xfer_info); + if (err) + goto error; - /* Read transferred socket information. */ - err = uv__pipe_read_exactly(handle->handle, &xfer_info, sizeof xfer_info); - if (err) - goto error; - - /* Store the pending socket info. */ - uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info); - - /* Return number of bytes read. */ - return sizeof frame_header + sizeof xfer_info; + /* Store the pending socket info. */ + uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info); + } } + /* Return whether the caller should immediately try another read call to get + * more data. Calling uv__pipe_read_exactly will hang if there isn't data + * available, so we cannot do this unless we are guaranteed not to reach that. + */ + more = *data_remaining > 0; + return more; + invalid: /* Invalid frame. */ err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */ @@ -1960,12 +2116,20 @@ void uv__process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* req) { + DWORD err; + DWORD more; + DWORD bytes_requested; assert(handle->type == UV_NAMED_PIPE); handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING); DECREASE_PENDING_REQ_COUNT(handle); eof_timer_stop(handle); + if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(handle->read_req.wait_handle); + handle->read_req.wait_handle = INVALID_HANDLE_VALUE; + } + /* At this point, we're done with bookkeeping. If the user has stopped * reading the pipe in the meantime, there is nothing left to do, since there * is no callback that we can call. */ @@ -1974,7 +2138,7 @@ if (!REQ_SUCCESS(req)) { /* An error occurred doing the zero-read. */ - DWORD err = GET_REQ_ERROR(req); + err = GET_REQ_ERROR(req); /* If the read was cancelled by uv__pipe_interrupt_read(), the request may * indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to @@ -1985,34 +2149,19 @@ } else { /* The zero-read completed without error, indicating there is data * available in the kernel buffer. */ - DWORD avail; - - /* Get the number of bytes available. */ - avail = 0; - if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL)) - uv__pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); - - /* Read until we've either read all the bytes available, or the 'reading' - * flag is cleared. */ - while (avail > 0 && handle->flags & UV_HANDLE_READING) { + while (handle->flags & UV_HANDLE_READING && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + bytes_requested = 65536; /* Depending on the type of pipe, read either IPC frames or raw data. */ - DWORD bytes_read = - handle->ipc ? uv__pipe_read_ipc(loop, handle) - : uv__pipe_read_data(loop, handle, avail, (DWORD) -1); + if (handle->ipc) + more = uv__pipe_read_ipc(loop, handle); + else + more = uv__pipe_read_data(loop, handle, &bytes_requested, INT32_MAX); /* If no bytes were read, treat this as an indication that an error * occurred, and break out of the read loop. */ - if (bytes_read == 0) + if (more == 0) break; - - /* It is possible that more bytes were read than we thought were - * available. To prevent `avail` from underflowing, break out of the loop - * if this is the case. */ - if (bytes_read > avail) - break; - - /* Recompute the number of bytes available. */ - avail -= bytes_read; } } @@ -2033,17 +2182,15 @@ assert(handle->write_queue_size >= req->u.io.queued_bytes); handle->write_queue_size -= req->u.io.queued_bytes; - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); - if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - if (req->wait_handle != INVALID_HANDLE_VALUE) { - UnregisterWait(req->wait_handle); - req->wait_handle = INVALID_HANDLE_VALUE; - } - if (req->event_handle) { - CloseHandle(req->event_handle); - req->event_handle = NULL; - } + if (req->wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(req->wait_handle); + req->wait_handle = INVALID_HANDLE_VALUE; + } + if (req->event_handle) { + CloseHandle(req->event_handle); + req->event_handle = NULL; } err = GET_REQ_ERROR(req); @@ -2069,9 +2216,9 @@ uv__queue_non_overlapped_write(handle); } - if (handle->stream.conn.write_reqs_pending == 0) - if (handle->flags & UV_HANDLE_SHUTTING) - uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req); + if (handle->stream.conn.write_reqs_pending == 0 && + uv__is_stream_shutting(handle)) + uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req); DECREASE_PENDING_REQ_COUNT(handle); } @@ -2120,13 +2267,16 @@ assert(handle->type == UV_NAMED_PIPE); - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); err = 0; if (REQ_SUCCESS(req)) { pipeHandle = req->u.connect.pipeHandle; duplex_flags = req->u.connect.duplex_flags; - err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags); + if (handle->flags & UV_HANDLE_CLOSING) + err = UV_ECANCELED; + else + err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags); if (err) CloseHandle(pipeHandle); } else { @@ -2149,8 +2299,7 @@ /* Clear the shutdown_req field so we don't go here again. */ handle->stream.conn.shutdown_req = NULL; - handle->flags &= ~UV_HANDLE_SHUTTING; - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); if (handle->flags & UV_HANDLE_CLOSING) { /* Already closing. Cancel the shutdown. */ @@ -2342,7 +2491,10 @@ if (pipe->ipc) { assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); - pipe->pipe.conn.ipc_remote_pid = uv_os_getppid(); + GetNamedPipeClientProcessId(os_handle, &pipe->pipe.conn.ipc_remote_pid); + if (pipe->pipe.conn.ipc_remote_pid == GetCurrentProcessId()) { + GetNamedPipeServerProcessId(os_handle, &pipe->pipe.conn.ipc_remote_pid); + } assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1); } return 0; @@ -2355,7 +2507,6 @@ FILE_NAME_INFORMATION tmp_name_info; FILE_NAME_INFORMATION* name_info; WCHAR* name_buf; - unsigned int addrlen; unsigned int name_size; unsigned int name_len; int err; @@ -2366,46 +2517,7 @@ if (handle->name != NULL) { /* The user might try to query the name before we are connected, * and this is just easier to return the cached value if we have it. */ - name_buf = handle->name; - name_len = wcslen(name_buf); - - /* check how much space we need */ - addrlen = WideCharToMultiByte(CP_UTF8, - 0, - name_buf, - name_len, - NULL, - 0, - NULL, - NULL); - if (!addrlen) { - *size = 0; - err = uv_translate_sys_error(GetLastError()); - return err; - } else if (addrlen >= *size) { - *size = addrlen + 1; - err = UV_ENOBUFS; - goto error; - } - - addrlen = WideCharToMultiByte(CP_UTF8, - 0, - name_buf, - name_len, - buffer, - addrlen, - NULL, - NULL); - if (!addrlen) { - *size = 0; - err = uv_translate_sys_error(GetLastError()); - return err; - } - - *size = addrlen; - buffer[addrlen] = '\0'; - - return 0; + return uv__copy_utf16_to_utf8(handle->name, -1, buffer, size); } if (handle->handle == INVALID_HANDLE_VALUE) { @@ -2433,8 +2545,7 @@ name_info = uv__malloc(name_size); if (!name_info) { *size = 0; - err = UV_ENOMEM; - goto cleanup; + return UV_ENOMEM; } nt_status = pNtQueryInformationFile(handle->handle, @@ -2467,51 +2578,19 @@ name_len /= sizeof(WCHAR); - /* check how much space we need */ - addrlen = WideCharToMultiByte(CP_UTF8, - 0, - name_buf, - name_len, - NULL, - 0, - NULL, - NULL); - if (!addrlen) { + /* "\\\\.\\pipe" + name */ + if (*size < pipe_prefix_len) { *size = 0; - err = uv_translate_sys_error(GetLastError()); - goto error; - } else if (pipe_prefix_len + addrlen >= *size) { - /* "\\\\.\\pipe" + name */ - *size = pipe_prefix_len + addrlen + 1; - err = UV_ENOBUFS; - goto error; } - - memcpy(buffer, pipe_prefix, pipe_prefix_len); - addrlen = WideCharToMultiByte(CP_UTF8, - 0, - name_buf, - name_len, - buffer+pipe_prefix_len, - *size-pipe_prefix_len, - NULL, - NULL); - if (!addrlen) { - *size = 0; - err = uv_translate_sys_error(GetLastError()); - goto error; + else { + memcpy(buffer, pipe_prefix, pipe_prefix_len); + *size -= pipe_prefix_len; } - - addrlen += pipe_prefix_len; - *size = addrlen; - buffer[addrlen] = '\0'; - - err = 0; + err = uv__copy_utf16_to_utf8(name_buf, name_len, buffer+pipe_prefix_len, size); + *size += pipe_prefix_len; error: uv__free(name_info); - -cleanup: return err; } @@ -2524,6 +2603,9 @@ int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) { + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + if (handle->flags & UV_HANDLE_BOUND) return uv__pipe_getname(handle, buffer, size); @@ -2538,6 +2620,9 @@ int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) { + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + /* emulate unix behaviour */ if (handle->flags & UV_HANDLE_BOUND) return UV_ENOTCONN;
diff --git a/Utilities/cmlibuv/src/win/poll.c b/Utilities/cmlibuv/src/win/poll.c index bd531b0..a20867a 100644 --- a/Utilities/cmlibuv/src/win/poll.c +++ b/Utilities/cmlibuv/src/win/poll.c
@@ -211,15 +211,11 @@ protocol_info->iProtocol, protocol_info, 0, - WSA_FLAG_OVERLAPPED); + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); if (sock == INVALID_SOCKET) { return INVALID_SOCKET; } - if (!SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)) { - goto error; - }; - if (CreateIoCompletionPort((HANDLE) sock, iocp, (ULONG_PTR) sock, @@ -425,9 +421,8 @@ return uv_translate_sys_error(WSAGetLastError()); /* Try to obtain a base handle for the socket. This increases this chances that - * we find an AFD handle and are able to use the fast poll mechanism. This will - * always fail on windows XP/2k3, since they don't support the. SIO_BASE_HANDLE - * ioctl. */ + * we find an AFD handle and are able to use the fast poll mechanism. + */ #ifndef NDEBUG base_socket = INVALID_SOCKET; #endif
diff --git a/Utilities/cmlibuv/src/win/process-stdio.c b/Utilities/cmlibuv/src/win/process-stdio.c index 0db3572..181db92 100644 --- a/Utilities/cmlibuv/src/win/process-stdio.c +++ b/Utilities/cmlibuv/src/win/process-stdio.c
@@ -46,12 +46,12 @@ #define CHILD_STDIO_CRT_FLAGS(buffer, fd) \ *((unsigned char*) (buffer) + sizeof(int) + fd) -#define CHILD_STDIO_HANDLE(buffer, fd) \ - *((HANDLE*) ((unsigned char*) (buffer) + \ - sizeof(int) + \ - sizeof(unsigned char) * \ - CHILD_STDIO_COUNT((buffer)) + \ - sizeof(HANDLE) * (fd))) +#define CHILD_STDIO_HANDLE(buffer, fd) \ + ((void*) ((unsigned char*) (buffer) + \ + sizeof(int) + \ + sizeof(unsigned char) * \ + CHILD_STDIO_COUNT((buffer)) + \ + sizeof(HANDLE) * (fd))) /* CRT file descriptor mode flags */ @@ -194,7 +194,7 @@ CHILD_STDIO_COUNT(buffer) = count; for (i = 0; i < count; i++) { CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; - CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; + memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE)); } for (i = 0; i < count; i++) { @@ -215,14 +215,15 @@ * handles in the stdio buffer are initialized with. * INVALID_HANDLE_VALUE, which should be okay. */ if (i <= 2) { + HANDLE nul; DWORD access = (i == 0) ? FILE_GENERIC_READ : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES; - err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i), - access); + err = uv__create_nul_handle(&nul, access); if (err) goto error; + memcpy(CHILD_STDIO_HANDLE(buffer, i), &nul, sizeof(HANDLE)); CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; } break; @@ -247,7 +248,7 @@ if (err) goto error; - CHILD_STDIO_HANDLE(buffer, i) = child_pipe; + memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_pipe, sizeof(HANDLE)); CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; break; } @@ -263,7 +264,7 @@ * error. */ if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) { CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; - CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; + memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE)); break; } goto error; @@ -298,7 +299,7 @@ return -1; } - CHILD_STDIO_HANDLE(buffer, i) = child_handle; + memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE)); break; } @@ -334,7 +335,7 @@ if (err) goto error; - CHILD_STDIO_HANDLE(buffer, i) = child_handle; + memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE)); CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags; break; } @@ -359,7 +360,7 @@ count = CHILD_STDIO_COUNT(buffer); for (i = 0; i < count; i++) { - HANDLE handle = CHILD_STDIO_HANDLE(buffer, i); + HANDLE handle = uv__stdio_handle(buffer, i); if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); } @@ -374,7 +375,7 @@ count = CHILD_STDIO_COUNT(buffer); for (i = 0; i < count; i++) { - HANDLE handle = CHILD_STDIO_HANDLE(buffer, i); + HANDLE handle = uv__stdio_handle(buffer, i); if (handle != INVALID_HANDLE_VALUE) { SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); } @@ -412,5 +413,7 @@ HANDLE uv__stdio_handle(BYTE* buffer, int fd) { - return CHILD_STDIO_HANDLE(buffer, fd); + HANDLE handle; + memcpy(&handle, CHILD_STDIO_HANDLE(buffer, fd), sizeof(HANDLE)); + return handle; }
diff --git a/Utilities/cmlibuv/src/win/process.c b/Utilities/cmlibuv/src/win/process.c index 59db8f8..bd3b4e8 100644 --- a/Utilities/cmlibuv/src/win/process.c +++ b/Utilities/cmlibuv/src/win/process.c
@@ -26,12 +26,14 @@ #include <signal.h> #include <limits.h> #include <wchar.h> -#include <malloc.h> /* alloca */ #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" +#include <dbghelp.h> +#include <shlobj.h> +#include <psapi.h> /* GetModuleBaseNameW */ #define SIGKILL 9 @@ -120,34 +122,7 @@ static int uv__utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) { - int ws_len, r; - WCHAR* ws; - - ws_len = MultiByteToWideChar(CP_UTF8, - 0, - s, - -1, - NULL, - 0); - if (ws_len <= 0) { - return GetLastError(); - } - - ws = (WCHAR*) uv__malloc(ws_len * sizeof(WCHAR)); - if (ws == NULL) { - return ERROR_OUTOFMEMORY; - } - - r = MultiByteToWideChar(CP_UTF8, - 0, - s, - -1, - ws, - ws_len); - assert(r == ws_len); - - *ws_ptr = ws; - return 0; + return uv__convert_utf8_to_utf16(s, ws_ptr); } @@ -158,7 +133,6 @@ handle->exit_signal = 0; handle->wait_handle = INVALID_HANDLE_VALUE; handle->process_handle = INVALID_HANDLE_VALUE; - handle->child_stdio_buffer = NULL; handle->exit_cb_pending = 0; UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT); @@ -537,7 +511,7 @@ } } target[0] = L'\0'; - wcsrev(start); + _wcsrev(start); *(target++) = L'"'; return target; } @@ -555,21 +529,15 @@ /* Count the required size. */ for (arg = args; *arg; arg++) { - DWORD arg_len; + ssize_t arg_len; - arg_len = MultiByteToWideChar(CP_UTF8, - 0, - *arg, - -1, - NULL, - 0); - if (arg_len == 0) { - return GetLastError(); - } + arg_len = uv_wtf8_length_as_utf16(*arg); + if (arg_len < 0) + return arg_len; dst_len += arg_len; - if (arg_len > temp_buffer_len) + if ((size_t) arg_len > temp_buffer_len) temp_buffer_len = arg_len; arg_count++; @@ -580,34 +548,28 @@ dst_len = dst_len * 2 + arg_count * 2; /* Allocate buffer for the final command line. */ - dst = (WCHAR*) uv__malloc(dst_len * sizeof(WCHAR)); + dst = uv__malloc(dst_len * sizeof(WCHAR)); if (dst == NULL) { - err = ERROR_OUTOFMEMORY; + err = UV_ENOMEM; goto error; } /* Allocate temporary working buffer. */ - temp_buffer = (WCHAR*) uv__malloc(temp_buffer_len * sizeof(WCHAR)); + temp_buffer = uv__malloc(temp_buffer_len * sizeof(WCHAR)); if (temp_buffer == NULL) { - err = ERROR_OUTOFMEMORY; + err = UV_ENOMEM; goto error; } pos = dst; for (arg = args; *arg; arg++) { - DWORD arg_len; + ssize_t arg_len; /* Convert argument to wide char. */ - arg_len = MultiByteToWideChar(CP_UTF8, - 0, - *arg, - -1, - temp_buffer, - (int) (dst + dst_len - pos)); - if (arg_len == 0) { - err = GetLastError(); - goto error; - } + arg_len = uv_wtf8_length_as_utf16(*arg); + assert(arg_len > 0); + assert(temp_buffer_len >= (size_t) arg_len); + uv_wtf8_to_utf16(*arg, temp_buffer, arg_len); if (verbatim_arguments) { /* Copy verbatim. */ @@ -619,6 +581,7 @@ } *pos++ = *(arg + 1) ? L' ' : L'\0'; + assert(pos <= dst + dst_len); } uv__free(temp_buffer); @@ -633,11 +596,9 @@ } -int env_strncmp(const wchar_t* a, int na, const wchar_t* b) { +static int env_strncmp(const wchar_t* a, int na, const wchar_t* b) { wchar_t* a_eq; wchar_t* b_eq; - wchar_t* A; - wchar_t* B; int nb; int r; @@ -652,27 +613,8 @@ assert(b_eq); nb = b_eq - b; - A = alloca((na+1) * sizeof(wchar_t)); - B = alloca((nb+1) * sizeof(wchar_t)); - - r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na); - assert(r==na); - A[na] = L'\0'; - r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb); - assert(r==nb); - B[nb] = L'\0'; - - for (;;) { - wchar_t AA = *A++; - wchar_t BB = *B++; - if (AA < BB) { - return -1; - } else if (AA > BB) { - return 1; - } else if (!AA && !BB) { - return 0; - } - } + r = CompareStringOrdinal(a, na, b, nb, /*case insensitive*/TRUE); + return r - CSTR_EQUAL; } @@ -704,55 +646,46 @@ WCHAR* ptr; char** env; size_t env_len = 0; - int len; + size_t len; size_t i; - DWORD var_size; + size_t var_size; size_t env_block_count = 1; /* 1 for null-terminator */ WCHAR* dst_copy; WCHAR** ptr_copy; WCHAR** env_copy; - DWORD required_vars_value_len[ARRAY_SIZE(required_vars)]; + char* p; + size_t required_vars_value_len[ARRAY_SIZE(required_vars)]; /* first pass: determine size in UTF-16 */ for (env = env_block; *env; env++) { - int len; + ssize_t len; if (strchr(*env, '=')) { - len = MultiByteToWideChar(CP_UTF8, - 0, - *env, - -1, - NULL, - 0); - if (len <= 0) { - return GetLastError(); - } + len = uv_wtf8_length_as_utf16(*env); + if (len < 0) + return len; env_len += len; env_block_count++; } } /* second pass: copy to UTF-16 environment block */ - dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR)); - if (dst_copy == NULL && env_len > 0) { - return ERROR_OUTOFMEMORY; + len = env_block_count * sizeof(WCHAR*); + p = uv__malloc(len + env_len * sizeof(WCHAR)); + if (p == NULL) { + return UV_ENOMEM; } - env_copy = alloca(env_block_count * sizeof(WCHAR*)); + env_copy = (void*) &p[0]; + dst_copy = (void*) &p[len]; ptr = dst_copy; ptr_copy = env_copy; for (env = env_block; *env; env++) { + ssize_t len; if (strchr(*env, '=')) { - len = MultiByteToWideChar(CP_UTF8, - 0, - *env, - -1, - ptr, - (int) (env_len - (ptr - dst_copy))); - if (len <= 0) { - DWORD err = GetLastError(); - uv__free(dst_copy); - return err; - } + len = uv_wtf8_length_as_utf16(*env); + assert(len > 0); + assert((size_t) len <= env_len - (ptr - dst_copy)); + uv_wtf8_to_utf16(*env, ptr, len); *ptr_copy++ = ptr; ptr += len; } @@ -770,7 +703,7 @@ cmp = -1; } else { cmp = env_strncmp(required_vars[i].wide_eq, - required_vars[i].len, + required_vars[i].len, *ptr_copy); } if (cmp < 0) { @@ -792,8 +725,8 @@ /* final pass: copy, in sort order, and inserting required variables */ dst = uv__malloc((1+env_len) * sizeof(WCHAR)); if (!dst) { - uv__free(dst_copy); - return ERROR_OUTOFMEMORY; + uv__free(p); + return UV_ENOMEM; } for (ptr = dst, ptr_copy = env_copy, i = 0; @@ -837,7 +770,7 @@ assert(env_len == (size_t) (ptr - dst)); *ptr = L'\0'; - uv__free(dst_copy); + uv__free(p); *dst_ptr = dst; return 0; } @@ -964,10 +897,12 @@ *env = NULL, *cwd = NULL; STARTUPINFOW startup; PROCESS_INFORMATION info; - DWORD process_flags; + DWORD process_flags, cwd_len; + BYTE* child_stdio_buffer; uv__process_init(loop, process); process->exit_cb = options->exit_cb; + child_stdio_buffer = NULL; if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { return UV_ENOTSUP; @@ -997,30 +932,31 @@ err = uv__utf8_to_utf16_alloc(options->file, &application); if (err) - goto done; + goto done_uv; err = make_program_args( options->args, options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, &arguments); if (err) - goto done; + goto done_uv; if (options->env) { err = make_program_env(options->env, &env); if (err) - goto done; + goto done_uv; } if (options->cwd) { /* Explicit cwd */ err = uv__utf8_to_utf16_alloc(options->cwd, &cwd); if (err) - goto done; + goto done_uv; + cwd_len = wcslen(cwd); } else { /* Inherit cwd */ - DWORD cwd_len, r; + DWORD r; cwd_len = GetCurrentDirectoryW(0, NULL); if (!cwd_len) { @@ -1041,6 +977,15 @@ } } + /* If cwd is too long, shorten it */ + if (cwd_len >= MAX_PATH) { + cwd_len = GetShortPathNameW(cwd, cwd, cwd_len); + if (cwd_len == 0) { + err = GetLastError(); + goto done; + } + } + /* Get PATH environment variable. */ path = find_path(env); if (path == NULL) { @@ -1063,7 +1008,7 @@ } } - err = uv__stdio_create(loop, options, &process->child_stdio_buffer); + err = uv__stdio_create(loop, options, &child_stdio_buffer); if (err) goto done; @@ -1089,13 +1034,13 @@ startup.cbReserved2 = 0; startup.lpReserved2 = NULL; #else - startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); - startup.lpReserved2 = (BYTE*) process->child_stdio_buffer; + startup.cbReserved2 = uv__stdio_size(child_stdio_buffer); + startup.lpReserved2 = (BYTE*) child_stdio_buffer; #endif - startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); - startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); - startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); + startup.hStdInput = uv__stdio_handle(child_stdio_buffer, 0); + startup.hStdOutput = uv__stdio_handle(child_stdio_buffer, 1); + startup.hStdError = uv__stdio_handle(child_stdio_buffer, 2); process_flags = CREATE_UNICODE_ENVIRONMENT | CREATE_DEFAULT_ERROR_MODE; if (options->flags & UV_PROCESS_WINDOWS_USE_PARENT_ERROR_MODE) { @@ -1255,8 +1200,13 @@ * made or the handle is closed, whichever happens first. */ uv__handle_start(process); + goto done_uv; + /* Cleanup, whether we succeeded or failed. */ done: + err = uv_translate_sys_error(err); + + done_uv: uv__free(application); uv__free(application_path); uv__free(arguments); @@ -1264,13 +1214,13 @@ uv__free(env); uv__free(alloc_path); - if (process->child_stdio_buffer != NULL) { + if (child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ - uv__stdio_destroy(process->child_stdio_buffer); - process->child_stdio_buffer = NULL; + uv__stdio_destroy(child_stdio_buffer); + child_stdio_buffer = NULL; } - return uv_translate_sys_error(err); + return err; } @@ -1279,25 +1229,163 @@ return UV_EINVAL; } + /* Create a dump file for the targeted process, if the registry key + * `HKLM:Software\Microsoft\Windows\Windows Error Reporting\LocalDumps` + * exists. The location of the dumps can be influenced by the `DumpFolder` + * sub-key, which has a default value of `%LOCALAPPDATA%\CrashDumps`, see [0] + * for more detail. Note that if the dump folder does not exist, we attempt + * to create it, to match behavior with WER itself. + * [0]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps */ + if (signum == SIGQUIT) { + HKEY registry_key; + DWORD pid, ret; + WCHAR basename[MAX_PATH]; + + /* Get target process name. */ + GetModuleBaseNameW(process_handle, NULL, &basename[0], sizeof(basename)); + + /* Get PID of target process. */ + pid = GetProcessId(process_handle); + + /* Get LocalDumps directory path. */ + ret = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps", + 0, + KEY_QUERY_VALUE, + ®istry_key); + if (ret == ERROR_SUCCESS) { + HANDLE hDumpFile = NULL; + WCHAR dump_folder[MAX_PATH], dump_name[MAX_PATH]; + DWORD dump_folder_len = sizeof(dump_folder), key_type = 0; + ret = RegGetValueW(registry_key, + NULL, + L"DumpFolder", + RRF_RT_ANY, + &key_type, + (PVOID) dump_folder, + &dump_folder_len); + if (ret != ERROR_SUCCESS) { + /* Workaround for missing uuid.dll on MinGW. */ + static const GUID FOLDERID_LocalAppData_libuv = { + 0xf1b32785, 0x6fba, 0x4fcf, + {0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91} + }; + + /* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */ + WCHAR* localappdata; + SHGetKnownFolderPath(&FOLDERID_LocalAppData_libuv, + 0, + NULL, + &localappdata); + _snwprintf_s(dump_folder, + ARRAY_SIZE(dump_folder), + _TRUNCATE, + L"%ls\\CrashDumps", + localappdata); + CoTaskMemFree(localappdata); + } + RegCloseKey(registry_key); + + /* Create dump folder if it doesn't already exist. */ + CreateDirectoryW(dump_folder, NULL); + + /* Construct dump filename from process name and PID. */ + _snwprintf_s(dump_name, + ARRAY_SIZE(dump_name), + _TRUNCATE, + L"%ls\\%ls.%d.dmp", + dump_folder, + basename, + pid); + + hDumpFile = CreateFileW(dump_name, + GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hDumpFile != INVALID_HANDLE_VALUE) { + DWORD dump_options, sym_options; + FILE_DISPOSITION_INFO DeleteOnClose = { TRUE }; + + /* If something goes wrong while writing it out, delete the file. */ + SetFileInformationByHandle(hDumpFile, + FileDispositionInfo, + &DeleteOnClose, + sizeof(DeleteOnClose)); + + /* Tell wine to dump ELF modules as well. */ + sym_options = SymGetOptions(); + SymSetOptions(sym_options | 0x40000000); + +/* MiniDumpWithAvxXStateContext might be undef in server2012r2 or mingw < 12 */ +#ifndef MiniDumpWithAvxXStateContext +#define MiniDumpWithAvxXStateContext 0x00200000 +#endif + /* We default to a fairly complete dump. In the future, we may want to + * allow clients to customize what kind of dump to create. */ + dump_options = MiniDumpWithFullMemory | + MiniDumpIgnoreInaccessibleMemory | + MiniDumpWithAvxXStateContext; + + if (MiniDumpWriteDump(process_handle, + pid, + hDumpFile, + dump_options, + NULL, + NULL, + NULL)) { + /* Don't delete the file on close if we successfully wrote it out. */ + FILE_DISPOSITION_INFO DontDeleteOnClose = { FALSE }; + SetFileInformationByHandle(hDumpFile, + FileDispositionInfo, + &DontDeleteOnClose, + sizeof(DontDeleteOnClose)); + } + SymSetOptions(sym_options); + CloseHandle(hDumpFile); + } + } + } + switch (signum) { + case SIGQUIT: case SIGTERM: case SIGKILL: case SIGINT: { /* Unconditionally terminate the process. On Windows, killed processes * normally return 1. */ - DWORD status; int err; + DWORD status; if (TerminateProcess(process_handle, 1)) return 0; - /* If the process already exited before TerminateProcess was called,. + /* If the process already exited before TerminateProcess was called, * TerminateProcess will fail with ERROR_ACCESS_DENIED. */ err = GetLastError(); - if (err == ERROR_ACCESS_DENIED && - GetExitCodeProcess(process_handle, &status) && - status != STILL_ACTIVE) { - return UV_ESRCH; + if (err == ERROR_ACCESS_DENIED) { + /* First check using GetExitCodeProcess() with status different from + * STILL_ACTIVE (259). This check can be set incorrectly by the process, + * though that is uncommon. */ + if (GetExitCodeProcess(process_handle, &status) && + status != STILL_ACTIVE) { + return UV_ESRCH; + } + + /* But the process could have exited with code == STILL_ACTIVE, use then + * WaitForSingleObject with timeout zero. This is prone to a race + * condition as it could return WAIT_TIMEOUT because the handle might + * not have been signaled yet.That would result in returning the wrong + * error code here (UV_EACCES instead of UV_ESRCH), but we cannot fix + * the kernel synchronization issue that TerminateProcess is + * inconsistent with WaitForSingleObject with just the APIs available to + * us in user space. */ + if (WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) { + return UV_ESRCH; + } } return uv_translate_sys_error(err); @@ -1313,7 +1401,16 @@ if (status != STILL_ACTIVE) return UV_ESRCH; - return 0; + switch (WaitForSingleObject(process_handle, 0)) { + case WAIT_OBJECT_0: + return UV_ESRCH; + case WAIT_FAILED: + return uv_translate_sys_error(GetLastError()); + case WAIT_TIMEOUT: + return 0; + default: + return UV_UNKNOWN; + } } default: @@ -1348,7 +1445,7 @@ if (pid == 0) { process_handle = GetCurrentProcess(); } else { - process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, + process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid); }
diff --git a/Utilities/cmlibuv/src/win/req-inl.h b/Utilities/cmlibuv/src/win/req-inl.h index 9e20759..af6fb75 100644 --- a/Utilities/cmlibuv/src/win/req-inl.h +++ b/Utilities/cmlibuv/src/win/req-inl.h
@@ -53,16 +53,16 @@ (uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req)))) -#define REGISTER_HANDLE_REQ(loop, handle, req) \ +#define REGISTER_HANDLE_REQ(loop, handle) \ do { \ INCREASE_ACTIVE_COUNT((loop), (handle)); \ - uv__req_register((loop), (req)); \ + uv__req_register((loop)); \ } while (0) -#define UNREGISTER_HANDLE_REQ(loop, handle, req) \ +#define UNREGISTER_HANDLE_REQ(loop, handle) \ do { \ DECREASE_ACTIVE_COUNT((loop), (handle)); \ - uv__req_unregister((loop), (req)); \ + uv__req_unregister((loop)); \ } while (0) @@ -81,134 +81,6 @@ uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); \ } - -INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) { - return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped); -} - - -INLINE static void uv__insert_pending_req(uv_loop_t* loop, uv_req_t* req) { - req->next_req = NULL; - if (loop->pending_reqs_tail) { -#ifdef _DEBUG - /* Ensure the request is not already in the queue, or the queue - * will get corrupted. - */ - uv_req_t* current = loop->pending_reqs_tail; - do { - assert(req != current); - current = current->next_req; - } while(current != loop->pending_reqs_tail); -#endif - - req->next_req = loop->pending_reqs_tail->next_req; - loop->pending_reqs_tail->next_req = req; - loop->pending_reqs_tail = req; - } else { - req->next_req = req; - loop->pending_reqs_tail = req; - } -} - - -#define DELEGATE_STREAM_REQ(loop, req, method, handle_at) \ - do { \ - switch (((uv_handle_t*) (req)->handle_at)->type) { \ - case UV_TCP: \ - uv__process_tcp_##method##_req(loop, \ - (uv_tcp_t*) ((req)->handle_at), \ - req); \ - break; \ - \ - case UV_NAMED_PIPE: \ - uv__process_pipe_##method##_req(loop, \ - (uv_pipe_t*) ((req)->handle_at), \ - req); \ - break; \ - \ - case UV_TTY: \ - uv__process_tty_##method##_req(loop, \ - (uv_tty_t*) ((req)->handle_at), \ - req); \ - break; \ - \ - default: \ - assert(0); \ - } \ - } while (0) - - -INLINE static void uv__process_reqs(uv_loop_t* loop) { - uv_req_t* req; - uv_req_t* first; - uv_req_t* next; - - if (loop->pending_reqs_tail == NULL) - return; - - first = loop->pending_reqs_tail->next_req; - next = first; - loop->pending_reqs_tail = NULL; - - while (next != NULL) { - req = next; - next = req->next_req != first ? req->next_req : NULL; - - switch (req->type) { - case UV_READ: - DELEGATE_STREAM_REQ(loop, req, read, data); - break; - - case UV_WRITE: - DELEGATE_STREAM_REQ(loop, (uv_write_t*) req, write, handle); - break; - - case UV_ACCEPT: - DELEGATE_STREAM_REQ(loop, req, accept, data); - break; - - case UV_CONNECT: - DELEGATE_STREAM_REQ(loop, (uv_connect_t*) req, connect, handle); - break; - - case UV_SHUTDOWN: - DELEGATE_STREAM_REQ(loop, (uv_shutdown_t*) req, shutdown, handle); - break; - - case UV_UDP_RECV: - uv__process_udp_recv_req(loop, (uv_udp_t*) req->data, req); - break; - - case UV_UDP_SEND: - uv__process_udp_send_req(loop, - ((uv_udp_send_t*) req)->handle, - (uv_udp_send_t*) req); - break; - - case UV_WAKEUP: - uv__process_async_wakeup_req(loop, (uv_async_t*) req->data, req); - break; - - case UV_SIGNAL_REQ: - uv__process_signal_req(loop, (uv_signal_t*) req->data, req); - break; - - case UV_POLL_REQ: - uv__process_poll_req(loop, (uv_poll_t*) req->data, req); - break; - - case UV_PROCESS_EXIT: - uv__process_proc_exit(loop, (uv_process_t*) req->data); - break; - - case UV_FS_EVENT_REQ: - uv__process_fs_event_req(loop, req, (uv_fs_event_t*) req->data); - break; - - default: - assert(0); - } - } -} +void uv__insert_pending_req(uv_loop_t* loop, uv_req_t* req); #endif /* UV_WIN_REQ_INL_H_ */
diff --git a/Utilities/cmlibuv/src/win/signal.c b/Utilities/cmlibuv/src/win/signal.c index 8c79871..85730b2 100644 --- a/Utilities/cmlibuv/src/win/signal.c +++ b/Utilities/cmlibuv/src/win/signal.c
@@ -91,7 +91,7 @@ for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup); handle != NULL && handle->signum == signum; - handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) { + handle = RB_NEXT(uv_signal_tree_s, handle)) { unsigned long previous = InterlockedExchange( (volatile LONG*) &handle->pending_signum, signum);
diff --git a/Utilities/cmlibuv/src/win/stream.c b/Utilities/cmlibuv/src/win/stream.c index 292bf58..ac19d3b 100644 --- a/Utilities/cmlibuv/src/win/stream.c +++ b/Utilities/cmlibuv/src/win/stream.c
@@ -111,6 +111,23 @@ } +static int uv__check_before_write(uv_stream_t* handle, unsigned int nbufs) { + /* We're not beholden to IOV_MAX but limit the buffer count to catch sign + * conversion bugs where a caller passes in a signed negative number that + * then gets converted to a really large unsigned number. + */ + if (nbufs < 1 || nbufs > 1024*1024) { + return UV_EINVAL; + } + + if (!(handle->flags & UV_HANDLE_WRITABLE)) { + return UV_EPIPE; + } + + return 0; +} + + int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], @@ -119,8 +136,9 @@ uv_loop_t* loop = handle->loop; int err; - if (!(handle->flags & UV_HANDLE_WRITABLE)) { - return UV_EPIPE; + err = uv__check_before_write(handle, nbufs); + if (err != 0) { + return err; } err = ERROR_INVALID_PARAMETER; @@ -131,7 +149,7 @@ case UV_NAMED_PIPE: err = uv__pipe_write( loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb); - break; + return uv_translate_write_sys_error(err); case UV_TTY: err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb); break; @@ -156,25 +174,33 @@ return uv_write(req, handle, bufs, nbufs, cb); } + err = uv__check_before_write(handle, nbufs); + if (err != 0) { + return err; + } + if (handle->type != UV_NAMED_PIPE || !((uv_pipe_t*) handle)->ipc) { return UV_EINVAL; - } else if (!(handle->flags & UV_HANDLE_WRITABLE)) { - return UV_EPIPE; } err = uv__pipe_write( loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb); - return uv_translate_sys_error(err); + return uv_translate_write_sys_error(err); } int uv_try_write(uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs) { + int err; + + err = uv__check_before_write(stream, nbufs); + if (err != 0) { + return err; + } + if (stream->flags & UV_HANDLE_CLOSING) return UV_EBADF; - if (!(stream->flags & UV_HANDLE_WRITABLE)) - return UV_EPIPE; switch (stream->type) { case UV_TCP: @@ -204,7 +230,7 @@ uv_loop_t* loop = handle->loop; if (!(handle->flags & UV_HANDLE_WRITABLE) || - handle->flags & UV_HANDLE_SHUTTING || + uv__is_stream_shutting(handle) || uv__is_closing(handle)) { return UV_ENOTCONN; } @@ -214,10 +240,9 @@ req->cb = cb; handle->flags &= ~UV_HANDLE_WRITABLE; - handle->flags |= UV_HANDLE_SHUTTING; handle->stream.conn.shutdown_req = req; handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); if (handle->stream.conn.write_reqs_pending == 0) { if (handle->type == UV_NAMED_PIPE)
diff --git a/Utilities/cmlibuv/src/win/tcp.c b/Utilities/cmlibuv/src/win/tcp.c index b6aa4c5..66e005f 100644 --- a/Utilities/cmlibuv/src/win/tcp.c +++ b/Utilities/cmlibuv/src/win/tcp.c
@@ -30,14 +30,6 @@ /* - * Threshold of active tcp streams for which to preallocate tcp read buffers. - * (Due to node slab allocator performing poorly under this pattern, - * the optimization is temporarily disabled (threshold=0). This will be - * revisited once node allocator is improved.) - */ -const unsigned int uv_active_tcp_streams_threshold = 0; - -/* * Number of simultaneous pending AcceptEx calls. */ const unsigned int uv_simultaneous_server_accepts = 32; @@ -57,23 +49,99 @@ } -static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay) { +/* + * Check if Windows version is 10.0.16299 (Windows 10, version 1709) or later. + */ +static int uv__windows10_version1709(void) { + OSVERSIONINFOW os_info; + if (!pRtlGetVersion) + return 0; + pRtlGetVersion(&os_info); + if (os_info.dwMajorVersion < 10) + return 0; + if (os_info.dwMajorVersion > 10) + return 1; + if (os_info.dwMinorVersion > 0) + return 1; + return os_info.dwBuildNumber >= 16299; +} + + +static int uv__tcp_keepalive(uv_tcp_t* handle, + SOCKET socket, + int on, + unsigned int idle, + unsigned int intvl, + unsigned int cnt) { if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, - (const char*)&enable, - sizeof enable) == -1) { + (const char*)&on, + sizeof on) == -1) { return WSAGetLastError(); } - if (enable && setsockopt(socket, - IPPROTO_TCP, - TCP_KEEPALIVE, - (const char*)&delay, - sizeof delay) == -1) { - return WSAGetLastError(); + if (!on) + return 0; + + if (idle < 1 || intvl < 1 || cnt < 1) + return UV_EINVAL; + + /* Windows 10, version 1709 (build 10.0.16299) and later require second units + * for TCP keepalive options. */ + if (uv__windows10_version1709()) { + if (setsockopt(socket, + IPPROTO_TCP, + TCP_KEEPIDLE, + (const char*)&idle, + sizeof idle) == -1) { + return WSAGetLastError(); + } + + if (setsockopt(socket, + IPPROTO_TCP, + TCP_KEEPINTVL, + (const char*)&intvl, + sizeof intvl) == -1) { + return WSAGetLastError(); + } + + if (setsockopt(socket, + IPPROTO_TCP, + TCP_KEEPCNT, + (const char*)&cnt, + sizeof cnt) == -1) { + return WSAGetLastError(); + } + + return 0; } + /* For those versions prior to Windows 10 version 1709, + * we fall back to SIO_KEEPALIVE_VALS that expects millisecond units. + * The SIO_KEEPALIVE_VALS IOCTL is supported on Windows 2000 + * and later versions of the operating system. */ + struct tcp_keepalive keepalive; + keepalive.onoff = on; + keepalive.keepalivetime = idle * 1000; + keepalive.keepaliveinterval = intvl * 1000; + /* On Windows Vista and later, the number of keep-alive probes + * (data retransmissions) is set to 10 and cannot be changed. + * On Windows Server 2003, Windows XP, and Windows 2000, the default setting + * for number of keep-alive probes is 5 and cannot be changed programmatically. + */ + DWORD dummy; + if (WSAIoctl(socket, + SIO_KEEPALIVE_VALS, + (LPVOID) &keepalive, + sizeof keepalive, + NULL, + 0, + &dummy, + NULL, + NULL) == -1) + return WSAGetLastError(); + return 0; } @@ -134,7 +202,7 @@ /* TODO: Use stored delay. */ if (handle->flags & UV_HANDLE_TCP_KEEPALIVE) { - err = uv__tcp_keepalive(handle, socket, 1, 60); + err = uv__tcp_keepalive(handle, socket, 1, 60, 1, 10); if (err) return err; } @@ -183,14 +251,14 @@ sock = socket(domain, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { err = WSAGetLastError(); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); return uv_translate_sys_error(err); } err = uv__tcp_set_socket(handle->loop, handle, sock, domain, 0); if (err) { closesocket(sock); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); return uv_translate_sys_error(err); } @@ -214,8 +282,7 @@ assert(stream->flags & UV_HANDLE_CONNECTION); stream->stream.conn.shutdown_req = NULL; - stream->flags &= ~UV_HANDLE_SHUTTING; - UNREGISTER_HANDLE_REQ(loop, stream, req); + UNREGISTER_HANDLE_REQ(loop, stream); err = 0; if (stream->flags & UV_HANDLE_CLOSING) @@ -274,7 +341,6 @@ } uv__handle_close(handle); - loop->active_tcp_streams--; } @@ -296,6 +362,12 @@ DWORD err; int r; + /* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR. + * so we just return an error directly when UV_TCP_REUSEPORT is requested + * for binding the socket. */ + if (flags & UV_TCP_REUSEPORT) + return ERROR_NOT_SUPPORTED; + if (handle->socket == INVALID_SOCKET) { SOCKET sock; @@ -484,26 +556,9 @@ req = &handle->read_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); - /* - * Preallocate a read buffer if the number of active streams is below - * the threshold. - */ - if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) { - handle->flags &= ~UV_HANDLE_ZERO_READ; - handle->tcp.conn.read_buffer = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->tcp.conn.read_buffer); - if (handle->tcp.conn.read_buffer.base == NULL || - handle->tcp.conn.read_buffer.len == 0) { - handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tcp.conn.read_buffer); - return; - } - assert(handle->tcp.conn.read_buffer.base != NULL); - buf = handle->tcp.conn.read_buffer; - } else { - handle->flags |= UV_HANDLE_ZERO_READ; - buf.base = (char*) &uv_zero_; - buf.len = 0; - } + handle->flags |= UV_HANDLE_ZERO_READ; + buf.base = (char*) &uv_zero_; + buf.len = 0; /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); @@ -550,7 +605,7 @@ struct linger l = { 1, 0 }; /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */ - if (handle->flags & UV_HANDLE_SHUTTING) + if (uv__is_stream_shutting(handle)) return UV_EINVAL; if (0 != setsockopt(handle->socket, SOL_SOCKET, SO_LINGER, (const char*)&l, sizeof(l))) @@ -654,7 +709,6 @@ int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { - uv_loop_t* loop = server->loop; int err = 0; int family; @@ -716,8 +770,6 @@ } } - loop->active_tcp_streams++; - return err; } @@ -767,20 +819,6 @@ return 0; } -// Check if Windows version is 10.0.16299 or later -static int uv__is_fast_loopback_fail_supported(void) { - OSVERSIONINFOW os_info; - if (!pRtlGetVersion) - return 0; - pRtlGetVersion(&os_info); - if (os_info.dwMajorVersion < 10) - return 0; - if (os_info.dwMajorVersion > 10) - return 1; - if (os_info.dwMinorVersion > 0) - return 1; - return os_info.dwBuildNumber >= 16299; -} static int uv__tcp_try_connect(uv_connect_t* req, uv_tcp_t* handle, @@ -827,7 +865,7 @@ * is not reachable, instead of waiting for 2s. We do not care if this fails. * This only works on Windows version 10.0.16299 and later. */ - if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) { + if (uv__windows10_version1709() && uv__is_loopback(&converted)) { memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl)); retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; @@ -852,7 +890,7 @@ if (handle->delayed_error != 0) { /* Process the req without IOCP. */ handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); uv__insert_pending_req(loop, (uv_req_t*)req); return 0; } @@ -868,12 +906,12 @@ if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { /* Process the req without IOCP. */ handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); uv__insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(success)) { /* The req will be processed with IOCP. */ handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); } else { return WSAGetLastError(); } @@ -943,14 +981,14 @@ req->u.io.queued_bytes = 0; handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); uv__insert_pending_req(loop, (uv_req_t*) req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* Request queued by the kernel. */ req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs); handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); handle->write_queue_size += req->u.io.queued_bytes; if (handle->flags & UV_HANDLE_EMULATE_IOCP && !RegisterWaitForSingleObject(&req->wait_handle, @@ -964,7 +1002,7 @@ req->u.io.queued_bytes = 0; handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); SET_REQ_ERROR(req, WSAGetLastError()); uv__insert_pending_req(loop, (uv_req_t*) req); } @@ -1135,7 +1173,7 @@ assert(handle->write_queue_size >= req->u.io.queued_bytes); handle->write_queue_size -= req->u.io.queued_bytes; - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (req->wait_handle != INVALID_HANDLE_VALUE) { @@ -1163,7 +1201,7 @@ closesocket(handle->socket); handle->socket = INVALID_SOCKET; } - if (handle->flags & UV_HANDLE_SHUTTING) + if (uv__is_stream_shutting(handle)) uv__process_tcp_shutdown_req(loop, handle, handle->stream.conn.shutdown_req); @@ -1227,7 +1265,7 @@ assert(handle->type == UV_TCP); - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); err = 0; if (handle->delayed_error) { @@ -1248,7 +1286,6 @@ 0) == 0) { uv__connection_init((uv_stream_t*)handle); handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; - loop->active_tcp_streams++; } else { err = WSAGetLastError(); } @@ -1331,7 +1368,6 @@ tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } - tcp->loop->active_tcp_streams++; return 0; } @@ -1355,22 +1391,30 @@ } -int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) { +int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int idle) { + return uv_tcp_keepalive_ex(handle, on, idle, 1, 10); +} + +int uv_tcp_keepalive_ex(uv_tcp_t* handle, + int on, + unsigned int idle, + unsigned int intvl, + unsigned int cnt) { int err; if (handle->socket != INVALID_SOCKET) { - err = uv__tcp_keepalive(handle, handle->socket, enable, delay); + err = uv__tcp_keepalive(handle, handle->socket, on, idle, intvl, cnt); if (err) return uv_translate_sys_error(err); } - if (enable) { + if (on) { handle->flags |= UV_HANDLE_TCP_KEEPALIVE; } else { handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE; } - /* TODO: Store delay if handle->socket isn't created yet. */ + /* TODO: Store idle if handle->socket isn't created yet. */ return 0; } @@ -1432,7 +1476,7 @@ uv_tcp_non_ifs_lsp_ipv4; /* If there are non-ifs LSPs then try to obtain a base handle for the socket. - * This will always fail on Windows XP/3k. */ + */ if (non_ifs_lsp) { DWORD bytes; if (WSAIoctl(socket, @@ -1583,11 +1627,6 @@ return 0; } -#ifndef WSA_FLAG_NO_HANDLE_INHERIT -/* Added in Windows 7 SP1. Specify this to avoid race conditions, */ -/* but also manually clear the inherit flag in case this failed. */ -#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 -#endif int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) { SOCKET server = INVALID_SOCKET;
diff --git a/Utilities/cmlibuv/src/win/thread.c b/Utilities/cmlibuv/src/win/thread.c index d3b1c96..9c38e3d 100644 --- a/Utilities/cmlibuv/src/win/thread.c +++ b/Utilities/cmlibuv/src/win/thread.c
@@ -32,45 +32,23 @@ #include "uv.h" #include "internal.h" -static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) { - DWORD result; - HANDLE existing_event, created_event; +typedef void (*uv__once_cb)(void); - created_event = CreateEvent(NULL, 1, 0, NULL); - if (created_event == 0) { - /* Could fail in a low-memory situation? */ - uv_fatal_error(GetLastError(), "CreateEvent"); - } +typedef struct { + uv__once_cb callback; +} uv__once_data_t; - existing_event = InterlockedCompareExchangePointer(&guard->event, - created_event, - NULL); +static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) { + uv__once_data_t* data = param; - if (existing_event == NULL) { - /* We won the race */ - callback(); + data->callback(); - result = SetEvent(created_event); - assert(result); - guard->ran = 1; - - } else { - /* We lost the race. Destroy the event we created and wait for the existing - * one to become signaled. */ - CloseHandle(created_event); - result = WaitForSingleObject(existing_event, INFINITE); - assert(result == WAIT_OBJECT_0); - } + return TRUE; } - -void uv_once(uv_once_t* guard, void (*callback)(void)) { - /* Fast case - avoid WaitForSingleObject. */ - if (guard->ran) { - return; - } - - uv__once_inner(guard, callback); +void uv_once(uv_once_t* guard, uv__once_cb callback) { + uv__once_data_t data = { .callback = callback }; + InitOnceExecuteOnce(&guard->init_once, uv__once_inner, (void*) &data, NULL); } @@ -79,6 +57,9 @@ static uv_key_t uv__current_thread_key; static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT; +static uv_once_t uv__thread_name_once = UV_ONCE_INIT; +HRESULT (WINAPI *pGetThreadDescription)(HANDLE, PWSTR*); +HRESULT (WINAPI *pSetThreadDescription)(HANDLE, PCWSTR); static void uv__init_current_thread_key(void) { @@ -117,6 +98,15 @@ return uv_thread_create_ex(tid, ¶ms, entry, arg); } + +int uv_thread_detach(uv_thread_t *tid) { + if (CloseHandle(*tid) == 0) + return uv_translate_sys_error(GetLastError()); + + return 0; +} + + int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, void (*entry)(void *arg), @@ -180,6 +170,81 @@ return UV_EIO; } +int uv_thread_setaffinity(uv_thread_t* tid, + char* cpumask, + char* oldmask, + size_t mask_size) { + int i; + HANDLE hproc; + DWORD_PTR procmask; + DWORD_PTR sysmask; + DWORD_PTR threadmask; + DWORD_PTR oldthreadmask; + int cpumasksize; + + cpumasksize = uv_cpumask_size(); + assert(cpumasksize > 0); + if (mask_size < (size_t)cpumasksize) + return UV_EINVAL; + + hproc = GetCurrentProcess(); + if (!GetProcessAffinityMask(hproc, &procmask, &sysmask)) + return uv_translate_sys_error(GetLastError()); + + threadmask = 0; + for (i = 0; i < cpumasksize; i++) { + if (cpumask[i]) { + if (procmask & (1 << i)) + threadmask |= 1 << i; + else + return UV_EINVAL; + } + } + + oldthreadmask = SetThreadAffinityMask(*tid, threadmask); + if (oldthreadmask == 0) + return uv_translate_sys_error(GetLastError()); + + if (oldmask != NULL) { + for (i = 0; i < cpumasksize; i++) + oldmask[i] = (oldthreadmask >> i) & 1; + } + + return 0; +} + +int uv_thread_getaffinity(uv_thread_t* tid, + char* cpumask, + size_t mask_size) { + int i; + HANDLE hproc; + DWORD_PTR procmask; + DWORD_PTR sysmask; + DWORD_PTR threadmask; + int cpumasksize; + + cpumasksize = uv_cpumask_size(); + assert(cpumasksize > 0); + if (mask_size < (size_t)cpumasksize) + return UV_EINVAL; + + hproc = GetCurrentProcess(); + if (!GetProcessAffinityMask(hproc, &procmask, &sysmask)) + return uv_translate_sys_error(GetLastError()); + + threadmask = SetThreadAffinityMask(*tid, procmask); + if (threadmask == 0 || SetThreadAffinityMask(*tid, threadmask) == 0) + return uv_translate_sys_error(GetLastError()); + + for (i = 0; i < cpumasksize; i++) + cpumask[i] = (threadmask >> i) & 1; + + return 0; +} + +int uv_thread_getcpu(void) { + return GetCurrentProcessorNumber(); +} uv_thread_t uv_thread_self(void) { uv_thread_t key; @@ -216,6 +281,92 @@ } +static void uv__thread_name_init_once(void) { + HMODULE m; + + m = GetModuleHandleW(L"api-ms-win-core-processthreads-l1-1-3.dll"); + if (m != NULL) { + pGetThreadDescription = (void*) GetProcAddress(m, "GetThreadDescription"); + pSetThreadDescription = (void*) GetProcAddress(m, "SetThreadDescription"); + } +} + + +int uv_thread_setname(const char* name) { + HRESULT hr; + WCHAR* namew; + int err; + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + + uv_once(&uv__thread_name_once, uv__thread_name_init_once); + + if (pSetThreadDescription == NULL) + return UV_ENOSYS; + + if (name == NULL) + return UV_EINVAL; + + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + + namew = NULL; + err = uv__convert_utf8_to_utf16(namebuf, &namew); + if (err) + return err; + + hr = pSetThreadDescription(GetCurrentThread(), namew); + uv__free(namew); + if (FAILED(hr)) + return uv_translate_sys_error(HRESULT_CODE(hr)); + + return 0; +} + + +int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) { + HRESULT hr; + WCHAR* namew; + char* thread_name; + size_t buf_size; + int r; + DWORD exit_code; + + uv_once(&uv__thread_name_once, uv__thread_name_init_once); + + if (pGetThreadDescription == NULL) + return UV_ENOSYS; + + if (name == NULL || size == 0) + return UV_EINVAL; + + if (tid == NULL || *tid == NULL) + return UV_EINVAL; + + /* Check if the thread handle is valid */ + if (!GetExitCodeThread(*tid, &exit_code) || exit_code != STILL_ACTIVE) + return UV_ENOENT; + + namew = NULL; + thread_name = NULL; + hr = pGetThreadDescription(*tid, &namew); + if (FAILED(hr)) + return uv_translate_sys_error(HRESULT_CODE(hr)); + + buf_size = size; + r = uv__copy_utf16_to_utf8(namew, -1, name, &buf_size); + if (r == UV_ENOBUFS) { + r = uv__convert_utf16_to_utf8(namew, wcslen(namew), &thread_name); + if (r == 0) { + uv__strscpy(name, thread_name, size); + uv__free(thread_name); + } + } + + LocalFree(namew); + return r; +} + + int uv_mutex_init(uv_mutex_t* mutex) { InitializeCriticalSection(mutex); return 0; @@ -374,6 +525,7 @@ abort(); } + int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6))) return 0; @@ -383,69 +535,6 @@ } -int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { - int err; - - barrier->n = count; - barrier->count = 0; - - err = uv_mutex_init(&barrier->mutex); - if (err) - return err; - - err = uv_sem_init(&barrier->turnstile1, 0); - if (err) - goto error2; - - err = uv_sem_init(&barrier->turnstile2, 1); - if (err) - goto error; - - return 0; - -error: - uv_sem_destroy(&barrier->turnstile1); -error2: - uv_mutex_destroy(&barrier->mutex); - return err; - -} - - -void uv_barrier_destroy(uv_barrier_t* barrier) { - uv_sem_destroy(&barrier->turnstile2); - uv_sem_destroy(&barrier->turnstile1); - uv_mutex_destroy(&barrier->mutex); -} - - -int uv_barrier_wait(uv_barrier_t* barrier) { - int serial_thread; - - uv_mutex_lock(&barrier->mutex); - if (++barrier->count == barrier->n) { - uv_sem_wait(&barrier->turnstile2); - uv_sem_post(&barrier->turnstile1); - } - uv_mutex_unlock(&barrier->mutex); - - uv_sem_wait(&barrier->turnstile1); - uv_sem_post(&barrier->turnstile1); - - uv_mutex_lock(&barrier->mutex); - serial_thread = (--barrier->count == 0); - if (serial_thread) { - uv_sem_wait(&barrier->turnstile1); - uv_sem_post(&barrier->turnstile2); - } - uv_mutex_unlock(&barrier->mutex); - - uv_sem_wait(&barrier->turnstile2); - uv_sem_post(&barrier->turnstile2); - return serial_thread; -} - - int uv_key_create(uv_key_t* key) { key->tls_index = TlsAlloc(); if (key->tls_index == TLS_OUT_OF_INDEXES)
diff --git a/Utilities/cmlibuv/src/win/tty.c b/Utilities/cmlibuv/src/win/tty.c index 267ca64..66ca99c 100644 --- a/Utilities/cmlibuv/src/win/tty.c +++ b/Utilities/cmlibuv/src/win/tty.c
@@ -23,12 +23,7 @@ #include <io.h> #include <string.h> #include <stdlib.h> - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include <stdint.h> -#endif +#include <stdint.h> #ifndef COMMON_LVB_REVERSE_VIDEO # define COMMON_LVB_REVERSE_VIDEO 0x4000 @@ -63,6 +58,9 @@ #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif +#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT +#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 +#endif #define CURSOR_SIZE_SMALL 25 #define CURSOR_SIZE_LARGE 100 @@ -124,7 +122,10 @@ * handle signalling SIGWINCH */ -static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE; +static HANDLE uv__tty_console_handle_out = INVALID_HANDLE_VALUE; +static HANDLE uv__tty_console_handle_in = INVALID_HANDLE_VALUE; +static DWORD uv__tty_console_in_original_mode = (DWORD)-1; +static volatile LONG uv__tty_console_in_need_mode_reset = 0; static int uv__tty_console_height = -1; static int uv__tty_console_width = -1; static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE; @@ -164,24 +165,38 @@ static void uv__determine_vterm_state(HANDLE handle); void uv__console_init(void) { + DWORD dwMode; + if (uv_sem_init(&uv_tty_output_lock, 1)) abort(); - uv__tty_console_handle = CreateFileW(L"CONOUT$", - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - 0, - 0); - if (uv__tty_console_handle != INVALID_HANDLE_VALUE) { + uv__tty_console_handle_out = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + 0, + 0); + if (uv__tty_console_handle_out != INVALID_HANDLE_VALUE) { CONSOLE_SCREEN_BUFFER_INFO sb_info; + uv_mutex_init(&uv__tty_console_resize_mutex); + if (GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info)) { + uv__tty_console_width = sb_info.dwSize.X; + uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + } QueueUserWorkItem(uv__tty_console_resize_message_loop_thread, NULL, WT_EXECUTELONGFUNCTION); - uv_mutex_init(&uv__tty_console_resize_mutex); - if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) { - uv__tty_console_width = sb_info.dwSize.X; - uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + } + uv__tty_console_handle_in = CreateFileW(L"CONIN$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + 0, + OPEN_EXISTING, + 0, + 0); + if (uv__tty_console_handle_in != INVALID_HANDLE_VALUE) { + if (GetConsoleMode(uv__tty_console_handle_in, &dwMode)) { + uv__tty_console_in_original_mode = dwMode; } } } @@ -258,7 +273,9 @@ /* Initialize TTY input specific fields. */ tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE; /* TODO: remove me in v2.x. */ - tty->tty.rd.unused_ = NULL; + tty->tty.rd.mode.unused_ = NULL; + /* Partially overwrites unused_ again. */ + tty->tty.rd.mode.mode = 0; tty->tty.rd.read_line_buffer = uv_null_buf_; tty->tty.rd.read_raw_wait = NULL; @@ -349,6 +366,7 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { DWORD flags; + DWORD try_set_flags; unsigned char was_reading; uv_alloc_cb alloc_cb; uv_read_cb read_cb; @@ -358,14 +376,19 @@ return UV_EINVAL; } - if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) { + if ((int)mode == tty->tty.rd.mode.mode) { return 0; } + try_set_flags = 0; switch (mode) { case UV_TTY_MODE_NORMAL: flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; break; + case UV_TTY_MODE_RAW_VT: + try_set_flags = ENABLE_VIRTUAL_TERMINAL_INPUT; + InterlockedExchange(&uv__tty_console_in_need_mode_reset, 1); + /* fallthrough */ case UV_TTY_MODE_RAW: flags = ENABLE_WINDOW_INPUT; break; @@ -391,16 +414,16 @@ } uv_sem_wait(&uv_tty_output_lock); - if (!SetConsoleMode(tty->handle, flags)) { + if (!SetConsoleMode(tty->handle, flags | try_set_flags) && + !SetConsoleMode(tty->handle, flags)) { err = uv_translate_sys_error(GetLastError()); uv_sem_post(&uv_tty_output_lock); return err; } uv_sem_post(&uv_tty_output_lock); - /* Update flag. */ - tty->flags &= ~UV_HANDLE_TTY_RAW; - tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0; + /* Update mode. */ + tty->tty.rd.mode.mode = mode; /* If we just stopped reading, restart. */ if (was_reading) { @@ -487,9 +510,11 @@ uv_loop_t* loop; uv_tty_t* handle; uv_req_t* req; - DWORD bytes, read_bytes; + DWORD bytes; + size_t read_bytes; WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3]; - DWORD chars, read_chars; + DWORD chars; + DWORD read_chars; LONG status; COORD pos; BOOL read_console_success; @@ -530,16 +555,13 @@ NULL); if (read_console_success) { - read_bytes = WideCharToMultiByte(CP_UTF8, - 0, - utf16, - read_chars, - handle->tty.rd.read_line_buffer.base, - bytes, - NULL, - NULL); + read_bytes = bytes; + uv_utf16_to_wtf8(utf16, + read_chars, + &handle->tty.rd.read_line_buffer.base, + &read_bytes); SET_REQ_SUCCESS(req); - req->u.io.overlapped.InternalHigh = read_bytes; + req->u.io.overlapped.InternalHigh = (DWORD) read_bytes; } else { SET_REQ_ERROR(req, GetLastError()); } @@ -620,7 +642,7 @@ static void uv__tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) { - if (handle->flags & UV_HANDLE_TTY_RAW) { + if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) { uv__tty_queue_read_raw(loop, handle); } else { uv__tty_queue_read_line(loop, handle); @@ -701,14 +723,14 @@ DWORD records_left, records_read; uv_buf_t buf; - off_t buf_used; + _off_t buf_used; assert(handle->type == UV_TTY); assert(handle->flags & UV_HANDLE_TTY_READABLE); handle->flags &= ~UV_HANDLE_READ_PENDING; if (!(handle->flags & UV_HANDLE_READING) || - !(handle->flags & UV_HANDLE_TTY_RAW)) { + !(uv__is_raw_tty_mode(handle->tty.rd.mode.mode))) { goto out; } @@ -803,7 +825,9 @@ } if (KEV.uChar.UnicodeChar != 0) { - int prefix_len, char_len; + int prefix_len; + size_t char_len; + char* last_key_buf; /* Character key pressed */ if (KEV.uChar.UnicodeChar >= 0xD800 && @@ -824,38 +848,31 @@ prefix_len = 0; } - if (KEV.uChar.UnicodeChar >= 0xDC00 && - KEV.uChar.UnicodeChar < 0xE000) { + char_len = sizeof handle->tty.rd.last_key; + last_key_buf = &handle->tty.rd.last_key[prefix_len]; + if (handle->tty.rd.last_utf16_high_surrogate) { /* UTF-16 surrogate pair */ WCHAR utf16_buffer[2]; utf16_buffer[0] = handle->tty.rd.last_utf16_high_surrogate; utf16_buffer[1] = KEV.uChar.UnicodeChar; - char_len = WideCharToMultiByte(CP_UTF8, - 0, - utf16_buffer, - 2, - &handle->tty.rd.last_key[prefix_len], - sizeof handle->tty.rd.last_key, - NULL, - NULL); + if (uv_utf16_to_wtf8(utf16_buffer, + 2, + &last_key_buf, + &char_len)) + char_len = 0; + handle->tty.rd.last_utf16_high_surrogate = 0; } else { /* Single UTF-16 character */ - char_len = WideCharToMultiByte(CP_UTF8, - 0, - &KEV.uChar.UnicodeChar, - 1, - &handle->tty.rd.last_key[prefix_len], - sizeof handle->tty.rd.last_key, - NULL, - NULL); + if (uv_utf16_to_wtf8(&KEV.uChar.UnicodeChar, + 1, + &last_key_buf, + &char_len)) + char_len = 0; } - /* Whatever happened, the last character wasn't a high surrogate. */ - handle->tty.rd.last_utf16_high_surrogate = 0; - /* If the utf16 character(s) couldn't be converted something must be * wrong. */ - if (!char_len) { + if (char_len == 0) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); handle->read_cb((uv_stream_t*) handle, @@ -1061,7 +1078,7 @@ if (!(handle->flags & UV_HANDLE_READ_PENDING)) return 0; - if (handle->flags & UV_HANDLE_TTY_RAW) { + if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) { /* Cancel raw read. Write some bullshit event to force the console wait to * return. */ memset(&record, 0, sizeof record); @@ -2194,7 +2211,7 @@ handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); req->u.io.queued_bytes = 0; @@ -2230,7 +2247,7 @@ int err; handle->write_queue_size -= req->u.io.queued_bytes; - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); if (req->cb) { err = GET_REQ_ERROR(req); @@ -2239,11 +2256,11 @@ handle->stream.conn.write_reqs_pending--; - if (handle->stream.conn.write_reqs_pending == 0) - if (handle->flags & UV_HANDLE_SHUTTING) - uv__process_tty_shutdown_req(loop, - handle, - handle->stream.conn.shutdown_req); + if (handle->stream.conn.write_reqs_pending == 0 && + uv__is_stream_shutting(handle)) + uv__process_tty_shutdown_req(loop, + handle, + handle->stream.conn.shutdown_req); DECREASE_PENDING_REQ_COUNT(handle); } @@ -2257,7 +2274,7 @@ if (handle->u.fd == -1) CloseHandle(handle->handle); else - close(handle->u.fd); + _close(handle->u.fd); handle->u.fd = -1; handle->handle = INVALID_HANDLE_VALUE; @@ -2274,8 +2291,7 @@ assert(req); stream->stream.conn.shutdown_req = NULL; - stream->flags &= ~UV_HANDLE_SHUTTING; - UNREGISTER_HANDLE_REQ(loop, stream, req); + UNREGISTER_HANDLE_REQ(loop, stream); /* TTY shutdown is really just a no-op */ if (req->cb) { @@ -2304,28 +2320,18 @@ } -/* - * uv__process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working - * TODO: find a way to remove it - */ -void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, - uv_req_t* raw_req) { - abort(); -} - - -/* - * uv__process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working - * TODO: find a way to remove it - */ -void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, - uv_connect_t* req) { - abort(); -} - - int uv_tty_reset_mode(void) { - /* Not necessary to do anything. */ + /** + * Shells on Windows do know to reset output flags after a program exits, + * but not necessarily input flags, so we do that for them. + */ + if ( + uv__tty_console_handle_in != INVALID_HANDLE_VALUE && + uv__tty_console_in_original_mode != (DWORD)-1 && + InterlockedExchange(&uv__tty_console_in_need_mode_reset, 0) != 0 + ) { + SetConsoleMode(uv__tty_console_handle_in, uv__tty_console_in_original_mode); + } return 0; } @@ -2412,8 +2418,8 @@ /* Make sure to not overwhelm the system with resize events */ Sleep(33); WaitForSingleObject(uv__tty_console_resized, INFINITE); - uv__tty_console_signal_resize(); ResetEvent(uv__tty_console_resized); + uv__tty_console_signal_resize(); } return 0; } @@ -2422,14 +2428,13 @@ CONSOLE_SCREEN_BUFFER_INFO sb_info; int width, height; - if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) + if (!GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info)) return; width = sb_info.dwSize.X; height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; uv_mutex_lock(&uv__tty_console_resize_mutex); - assert(uv__tty_console_width != -1 && uv__tty_console_height != -1); if (width != uv__tty_console_width || height != uv__tty_console_height) { uv__tty_console_width = width; uv__tty_console_height = height;
diff --git a/Utilities/cmlibuv/src/win/udp.c b/Utilities/cmlibuv/src/win/udp.c index eaebc1e..08e358a 100644 --- a/Utilities/cmlibuv/src/win/udp.c +++ b/Utilities/cmlibuv/src/win/udp.c
@@ -29,11 +29,6 @@ #include "req-inl.h" -/* - * Threshold of active udp streams for which to preallocate udp read buffers. - */ -const unsigned int uv_active_udp_streams_threshold = 0; - /* A zero-size buffer for use by uv_udp_read */ static char uv_zero_[] = ""; int uv_udp_getpeername(const uv_udp_t* handle, @@ -151,14 +146,14 @@ sock = socket(domain, SOCK_DGRAM, 0); if (sock == INVALID_SOCKET) { err = WSAGetLastError(); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); return uv_translate_sys_error(err); } err = uv__udp_set_socket(handle->loop, handle, sock, domain); if (err) { closesocket(sock); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); return uv_translate_sys_error(err); } } @@ -205,6 +200,12 @@ if (handle->flags & UV_HANDLE_BOUND) return 0; + /* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR. + * so we just return an error directly when UV_UDP_REUSEPORT is requested + * for binding the socket. */ + if (flags & UV_UDP_REUSEPORT) + return UV_ENOTSUP; + if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) { /* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */ return ERROR_INVALID_PARAMETER; @@ -276,84 +277,35 @@ req = &handle->recv_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); - /* - * Preallocate a read buffer if the number of active streams is below - * the threshold. - */ - if (loop->active_udp_streams < uv_active_udp_streams_threshold) { - handle->flags &= ~UV_HANDLE_ZERO_READ; + handle->flags |= UV_HANDLE_ZERO_READ; - handle->recv_buffer = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &handle->recv_buffer); - if (handle->recv_buffer.base == NULL || handle->recv_buffer.len == 0) { - handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0); - return; - } - assert(handle->recv_buffer.base != NULL); + buf.base = (char*) uv_zero_; + buf.len = 0; + flags = MSG_PEEK; - buf = handle->recv_buffer; - memset(&handle->recv_from, 0, sizeof handle->recv_from); - handle->recv_from_len = sizeof handle->recv_from; - flags = 0; + result = handle->func_wsarecv(handle->socket, + (WSABUF*) &buf, + 1, + &bytes, + &flags, + &req->u.io.overlapped, + NULL); - result = handle->func_wsarecvfrom(handle->socket, - (WSABUF*) &buf, - 1, - &bytes, - &flags, - (struct sockaddr*) &handle->recv_from, - &handle->recv_from_len, - &req->u.io.overlapped, - NULL); - - if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { - /* Process the req without IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - req->u.io.overlapped.InternalHigh = bytes; - handle->reqs_pending++; - uv__insert_pending_req(loop, req); - } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { - /* The req will be processed with IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - handle->reqs_pending++; - } else { - /* Make this req pending reporting an error. */ - SET_REQ_ERROR(req, WSAGetLastError()); - uv__insert_pending_req(loop, req); - handle->reqs_pending++; - } - + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Process the req without IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + req->u.io.overlapped.InternalHigh = bytes; + handle->reqs_pending++; + uv__insert_pending_req(loop, req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* The req will be processed with IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; } else { - handle->flags |= UV_HANDLE_ZERO_READ; - - buf.base = (char*) uv_zero_; - buf.len = 0; - flags = MSG_PEEK; - - result = handle->func_wsarecv(handle->socket, - (WSABUF*) &buf, - 1, - &bytes, - &flags, - &req->u.io.overlapped, - NULL); - - if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { - /* Process the req without IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - req->u.io.overlapped.InternalHigh = bytes; - handle->reqs_pending++; - uv__insert_pending_req(loop, req); - } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { - /* The req will be processed with IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - handle->reqs_pending++; - } else { - /* Make this req pending reporting an error. */ - SET_REQ_ERROR(req, WSAGetLastError()); - uv__insert_pending_req(loop, req); - handle->reqs_pending++; - } + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, WSAGetLastError()); + uv__insert_pending_req(loop, req); + handle->reqs_pending++; } } @@ -376,7 +328,6 @@ handle->flags |= UV_HANDLE_READING; INCREASE_ACTIVE_COUNT(loop, handle); - loop->active_udp_streams++; handle->recv_cb = recv_cb; handle->alloc_cb = alloc_cb; @@ -393,7 +344,6 @@ int uv__udp_recv_stop(uv_udp_t* handle) { if (handle->flags & UV_HANDLE_READING) { handle->flags &= ~UV_HANDLE_READING; - handle->loop->active_udp_streams--; DECREASE_ACTIVE_COUNT(loop, handle); } @@ -432,7 +382,7 @@ handle->reqs_pending++; handle->send_queue_size += req->u.io.queued_bytes; handle->send_queue_count++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); uv__insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* Request queued by the kernel. */ @@ -440,7 +390,7 @@ handle->reqs_pending++; handle->send_queue_size += req->u.io.queued_bytes; handle->send_queue_count++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); } else { /* Send failed due to an error. */ return WSAGetLastError(); @@ -497,57 +447,68 @@ DWORD bytes, err, flags; struct sockaddr_storage from; int from_len; + int count; - /* Do a nonblocking receive. - * TODO: try to read multiple datagrams at once. FIONREAD maybe? */ - buf = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf); - if (buf.base == NULL || buf.len == 0) { - handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); - goto done; - } - assert(buf.base != NULL); + /* Prevent loop starvation when the data comes in as fast as + * (or faster than) we can read it. */ + count = 32; - memset(&from, 0, sizeof from); - from_len = sizeof from; + do { + /* Do at most `count` nonblocking receive. */ + buf = uv_buf_init(NULL, 0); + handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf); + if (buf.base == NULL || buf.len == 0) { + handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); + goto done; + } - flags = 0; + memset(&from, 0, sizeof from); + from_len = sizeof from; - if (WSARecvFrom(handle->socket, - (WSABUF*)&buf, - 1, - &bytes, - &flags, - (struct sockaddr*) &from, - &from_len, - NULL, - NULL) != SOCKET_ERROR) { + flags = 0; - /* Message received */ - handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0); - } else { - err = WSAGetLastError(); - if (err == WSAEMSGSIZE) { - /* Message truncated */ - handle->recv_cb(handle, - bytes, - &buf, - (const struct sockaddr*) &from, - UV_UDP_PARTIAL); - } else if (err == WSAEWOULDBLOCK) { - /* Kernel buffer empty */ - handle->recv_cb(handle, 0, &buf, NULL, 0); - } else if (err == WSAECONNRESET || err == WSAENETRESET) { - /* WSAECONNRESET/WSANETRESET is ignored because this just indicates - * that a previous sendto operation failed. - */ - handle->recv_cb(handle, 0, &buf, NULL, 0); + if (WSARecvFrom(handle->socket, + (WSABUF*)&buf, + 1, + &bytes, + &flags, + (struct sockaddr*) &from, + &from_len, + NULL, + NULL) != SOCKET_ERROR) { + + /* Message received */ + err = ERROR_SUCCESS; + handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0); } else { - /* Any other error that we want to report back to the user. */ - uv_udp_recv_stop(handle); - handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0); + err = WSAGetLastError(); + if (err == WSAEMSGSIZE) { + /* Message truncated */ + handle->recv_cb(handle, + bytes, + &buf, + (const struct sockaddr*) &from, + UV_UDP_PARTIAL); + } else if (err == WSAEWOULDBLOCK) { + /* Kernel buffer empty */ + handle->recv_cb(handle, 0, &buf, NULL, 0); + } else if (err == WSAECONNRESET || err == WSAENETRESET) { + /* WSAECONNRESET/WSANETRESET is ignored because this just indicates + * that a previous sendto operation failed. + */ + handle->recv_cb(handle, 0, &buf, NULL, 0); + } else { + /* Any other error that we want to report back to the user. */ + uv_udp_recv_stop(handle); + handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0); + } } } + while (err == ERROR_SUCCESS && + count-- > 0 && + /* The recv_cb callback may decide to pause or close the handle. */ + (handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)); } done: @@ -572,7 +533,7 @@ handle->send_queue_size -= req->u.io.queued_bytes; handle->send_queue_count--; - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); if (req->cb) { err = 0; @@ -947,11 +908,15 @@ } -int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { +int uv_udp_open_ex(uv_udp_t* handle, uv_os_sock_t sock, unsigned int flags) { WSAPROTOCOL_INFOW protocol_info; int opt_len; int err; + /* Check for bad flags. */ + if (flags & ~(UV_UDP_REUSEADDR | UV_UDP_REUSEPORT)) + return UV_EINVAL; + /* Detect the address family of the socket. */ opt_len = (int) sizeof protocol_info; if (getsockopt(sock, @@ -969,6 +934,25 @@ if (err) return uv_translate_sys_error(err); + /* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR. + * so we just return an error directly when UV_UDP_REUSEPORT is requested + * for binding the socket. */ + if (flags & UV_UDP_REUSEPORT) + return UV_ENOTSUP; + + if (flags & UV_UDP_REUSEADDR) { + DWORD yes = 1; + /* Set SO_REUSEADDR on the socket. */ + if (setsockopt(handle->socket, + SOL_SOCKET, + SO_REUSEADDR, + (char*) &yes, + sizeof yes) == SOCKET_ERROR) { + err = WSAGetLastError(); + return uv_translate_sys_error(err); + } + } + if (uv__udp_is_bound(handle)) handle->flags |= UV_HANDLE_BOUND; @@ -979,6 +963,11 @@ } +int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { + return uv_udp_open_ex(handle, sock, 0); +} + + #define SOCKOPT_SETTER(name, option4, option6, validate) \ int uv_udp_set_##name(uv_udp_t* handle, int value) { \ DWORD optval = (DWORD) value; \ @@ -1140,7 +1129,8 @@ struct sockaddr_storage converted; int err; - assert(nbufs > 0); + if (nbufs < 1) + return UV_EINVAL; if (addr != NULL) { err = uv__convert_to_localhost_if_unspecified(addr, &converted); @@ -1180,3 +1170,24 @@ return bytes; } + + +int uv__udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/]) { + int i; + int r; + + if (count > INT_MAX) + return UV_EINVAL; + + for (i = 0; i < (int) count; i++) { + r = uv_udp_try_send(handle, bufs[i], nbufs[i], addrs[i]); + if (r < 0) + return i > 0 ? i : (int) r; /* Error if first packet, else send count. */ + } + + return i; +}
diff --git a/Utilities/cmlibuv/src/win/util.c b/Utilities/cmlibuv/src/win/util.c index 9943205..fec07b2 100644 --- a/Utilities/cmlibuv/src/win/util.c +++ b/Utilities/cmlibuv/src/win/util.c
@@ -31,6 +31,7 @@ #include "internal.h" /* clang-format off */ +#include <sysinfoapi.h> #include <winsock2.h> #include <winperf.h> #include <iphlpapi.h> @@ -94,7 +95,7 @@ int uv_exepath(char* buffer, size_t* size_ptr) { - int utf8_len, utf16_buffer_len, utf16_len; + size_t utf8_len, utf16_buffer_len, utf16_len; WCHAR* utf16_buffer; int err; @@ -121,29 +122,18 @@ goto error; } - /* utf16_len contains the length, *not* including the terminating null. */ - utf16_buffer[utf16_len] = L'\0'; - /* Convert to UTF-8 */ - utf8_len = WideCharToMultiByte(CP_UTF8, - 0, - utf16_buffer, - -1, - buffer, - (int) *size_ptr, - NULL, - NULL); - if (utf8_len == 0) { - err = GetLastError(); - goto error; + utf8_len = *size_ptr - 1; /* Reserve space for NUL */ + err = uv_utf16_to_wtf8(utf16_buffer, utf16_len, &buffer, &utf8_len); + if (err == UV_ENOBUFS) { + utf8_len = *size_ptr - 1; + err = 0; } + *size_ptr = utf8_len; uv__free(utf16_buffer); - /* utf8_len *does* include the terminating null at this point, but the - * returned size shouldn't. */ - *size_ptr = utf8_len - 1; - return 0; + return err; error: uv__free(utf16_buffer); @@ -151,143 +141,104 @@ } +static int uv__cwd(WCHAR** buf, DWORD *len) { + WCHAR* p; + DWORD n; + DWORD t; + + t = GetCurrentDirectoryW(0, NULL); + for (;;) { + if (t == 0) + return uv_translate_sys_error(GetLastError()); + + /* |t| is the size of the buffer _including_ nul. */ + p = uv__malloc(t * sizeof(*p)); + if (p == NULL) + return UV_ENOMEM; + + /* |n| is the size of the buffer _excluding_ nul but _only on success_. + * If |t| was too small because another thread changed the working + * directory, |n| is the size the buffer should be _including_ nul. + * It therefore follows we must resize when n >= t and fail when n == 0. + */ + n = GetCurrentDirectoryW(t, p); + if (n > 0) + if (n < t) + break; + + uv__free(p); + t = n; + } + + /* The returned directory should not have a trailing slash, unless it points + * at a drive root, like c:\. Remove it if needed. + */ + t = n - 1; + if (p[t] == L'\\' && !(n == 3 && p[1] == L':')) { + p[t] = L'\0'; + n = t; + } + + *buf = p; + *len = n; + + return 0; +} + + int uv_cwd(char* buffer, size_t* size) { DWORD utf16_len; WCHAR *utf16_buffer; int r; - if (buffer == NULL || size == NULL) { + if (buffer == NULL || size == NULL || *size == 0) { return UV_EINVAL; } - utf16_len = GetCurrentDirectoryW(0, NULL); - if (utf16_len == 0) { - return uv_translate_sys_error(GetLastError()); - } - utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); - if (utf16_buffer == NULL) { - return UV_ENOMEM; - } + r = uv__cwd(&utf16_buffer, &utf16_len); + if (r < 0) + return r; - utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer); - if (utf16_len == 0) { - uv__free(utf16_buffer); - return uv_translate_sys_error(GetLastError()); - } + r = uv__copy_utf16_to_utf8(utf16_buffer, utf16_len, buffer, size); - /* utf16_len contains the length, *not* including the terminating null. */ - utf16_buffer[utf16_len] = L'\0'; - - /* The returned directory should not have a trailing slash, unless it points - * at a drive root, like c:\. Remove it if needed. */ - if (utf16_buffer[utf16_len - 1] == L'\\' && - !(utf16_len == 3 && utf16_buffer[1] == L':')) { - utf16_len--; - utf16_buffer[utf16_len] = L'\0'; - } - - /* Check how much space we need */ - r = WideCharToMultiByte(CP_UTF8, - 0, - utf16_buffer, - -1, - NULL, - 0, - NULL, - NULL); - if (r == 0) { - uv__free(utf16_buffer); - return uv_translate_sys_error(GetLastError()); - } else if (r > (int) *size) { - uv__free(utf16_buffer); - *size = r; - return UV_ENOBUFS; - } - - /* Convert to UTF-8 */ - r = WideCharToMultiByte(CP_UTF8, - 0, - utf16_buffer, - -1, - buffer, - *size > INT_MAX ? INT_MAX : (int) *size, - NULL, - NULL); uv__free(utf16_buffer); - if (r == 0) { - return uv_translate_sys_error(GetLastError()); - } - - *size = r - 1; - return 0; + return r; } int uv_chdir(const char* dir) { WCHAR *utf16_buffer; - size_t utf16_len, new_utf16_len; + DWORD utf16_len; WCHAR drive_letter, env_var[4]; + int r; - if (dir == NULL) { - return UV_EINVAL; - } - - utf16_len = MultiByteToWideChar(CP_UTF8, - 0, - dir, - -1, - NULL, - 0); - if (utf16_len == 0) { - return uv_translate_sys_error(GetLastError()); - } - utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); - if (utf16_buffer == NULL) { - return UV_ENOMEM; - } - - if (MultiByteToWideChar(CP_UTF8, - 0, - dir, - -1, - utf16_buffer, - utf16_len) == 0) { - uv__free(utf16_buffer); - return uv_translate_sys_error(GetLastError()); - } + /* Convert to UTF-16 */ + r = uv__convert_utf8_to_utf16(dir, &utf16_buffer); + if (r) + return r; if (!SetCurrentDirectoryW(utf16_buffer)) { uv__free(utf16_buffer); return uv_translate_sys_error(GetLastError()); } + /* uv__cwd() will return a new buffer. */ + uv__free(utf16_buffer); + utf16_buffer = NULL; + /* Windows stores the drive-local path in an "hidden" environment variable, * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update * this, so we'll have to do it. */ - new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer); - if (new_utf16_len > utf16_len ) { - uv__free(utf16_buffer); - utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR)); - if (utf16_buffer == NULL) { - /* When updating the environment variable fails, return UV_OK anyway. - * We did successfully change current working directory, only updating - * hidden env variable failed. */ - return 0; - } - new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer); - } - if (utf16_len == 0) { - uv__free(utf16_buffer); + r = uv__cwd(&utf16_buffer, &utf16_len); + if (r == UV_ENOMEM) { + /* When updating the environment variable fails, return UV_OK anyway. + * We did successfully change current working directory, only updating + * hidden env variable failed. */ return 0; } - - /* The returned directory should not have a trailing slash, unless it points - * at a drive root, like c:\. Remove it if needed. */ - if (utf16_buffer[utf16_len - 1] == L'\\' && - !(utf16_len == 3 && utf16_buffer[1] == L':')) { - utf16_len--; - utf16_buffer[utf16_len] = L'\0'; + if (r < 0) { + return r; } if (utf16_len < 2 || utf16_buffer[1] != L':') { @@ -330,7 +281,7 @@ memory_status.dwLength = sizeof(memory_status); if (!GlobalMemoryStatusEx(&memory_status)) { - return -1; + return 0; } return (uint64_t)memory_status.ullAvailPhys; @@ -342,7 +293,7 @@ memory_status.dwLength = sizeof(memory_status); if (!GlobalMemoryStatusEx(&memory_status)) { - return -1; + return 0; } return (uint64_t)memory_status.ullTotalPhys; @@ -354,31 +305,30 @@ } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + uv_pid_t uv_os_getpid(void) { return GetCurrentProcessId(); } uv_pid_t uv_os_getppid(void) { - int parent_pid = -1; - HANDLE handle; - PROCESSENTRY32 pe; - DWORD current_pid = GetCurrentProcessId(); + NTSTATUS nt_status; + PROCESS_BASIC_INFORMATION basic_info; - pe.dwSize = sizeof(PROCESSENTRY32); - handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - - if (Process32First(handle, &pe)) { - do { - if (pe.th32ProcessID == current_pid) { - parent_pid = pe.th32ParentProcessID; - break; - } - } while( Process32Next(handle, &pe)); + nt_status = pNtQueryInformationProcess(GetCurrentProcess(), + ProcessBasicInformation, + &basic_info, + sizeof(basic_info), + NULL); + if (NT_SUCCESS(nt_status)) { + return basic_info.InheritedFromUniqueProcessId; + } else { + return -1; } - - CloseHandle(handle); - return parent_pid; } @@ -398,29 +348,14 @@ uv__once_init(); - /* Find out how big the buffer for the wide-char title must be */ - length = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0); - if (!length) { - err = GetLastError(); - goto done; - } - - /* Convert to wide-char string */ - title_w = (WCHAR*)uv__malloc(sizeof(WCHAR) * length); - if (!title_w) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } - - length = MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w, length); - if (!length) { - err = GetLastError(); - goto done; - } + err = uv__convert_utf8_to_utf16(title, &title_w); + if (err) + return err; /* If the title must be truncated insert a \0 terminator there */ - if (length > MAX_TITLE_LENGTH) { + length = wcslen(title_w); + if (length >= MAX_TITLE_LENGTH) title_w[MAX_TITLE_LENGTH - 1] = L'\0'; - } if (!SetConsoleTitleW(title_w)) { err = GetLastError(); @@ -441,21 +376,20 @@ static int uv__get_process_title(void) { - WCHAR title_w[MAX_TITLE_LENGTH]; + WCHAR title_w[MAX_PATH]; + DWORD wlen; - if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) { - return -1; - } + wlen = GetModuleFileNameW(NULL, title_w, MAX_PATH); + if (wlen == 0) + return uv_translate_sys_error(GetLastError()); - if (uv__convert_utf16_to_utf8(title_w, -1, &process_title) != 0) - return -1; - - return 0; + return uv__convert_utf16_to_utf8(title_w, wlen, &process_title); } int uv_get_process_title(char* buffer, size_t size) { size_t len; + int r; if (buffer == NULL || size == 0) return UV_EINVAL; @@ -467,9 +401,12 @@ * If the process_title was never read before nor explicitly set, * we must query it with getConsoleTitleW */ - if (!process_title && uv__get_process_title() == -1) { - LeaveCriticalSection(&process_title_lock); - return uv_translate_sys_error(GetLastError()); + if (process_title == NULL) { + r = uv__get_process_title(); + if (r) { + LeaveCriticalSection(&process_title_lock); + return r; + } } assert(process_title); @@ -487,11 +424,43 @@ } +/* https://github.com/libuv/libuv/issues/1674 */ +int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) { + FILETIME ft; + int64_t t; + + if (ts == NULL) + return UV_EFAULT; + + switch (clock_id) { + case UV_CLOCK_MONOTONIC: + uv__once_init(); + t = uv__hrtime(UV__NANOSEC); + ts->tv_sec = t / 1000000000; + ts->tv_nsec = t % 1000000000; + return 0; + case UV_CLOCK_REALTIME: + GetSystemTimePreciseAsFileTime(&ft); + /* In 100-nanosecond increments from 1601-01-01 UTC because why not? */ + t = (int64_t) ft.dwHighDateTime << 32 | ft.dwLowDateTime; + /* Convert to UNIX epoch, 1970-01-01. Still in 100 ns increments. */ + t -= 116444736000000000ll; + /* Now convert to seconds and nanoseconds. */ + ts->tv_sec = t / 10000000; + ts->tv_nsec = t % 10000000 * 100; + return 0; + } + + return UV_EINVAL; +} + + uint64_t uv_hrtime(void) { uv__once_init(); return uv__hrtime(UV__NANOSEC); } + uint64_t uv__hrtime(unsigned int scale) { LARGE_INTEGER counter; double scaled_freq; @@ -537,19 +506,23 @@ unsigned int uv_available_parallelism(void) { - SYSTEM_INFO info; - unsigned rc; + DWORD_PTR procmask; + DWORD_PTR sysmask; + unsigned count; + unsigned i; /* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems * with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458 */ - GetSystemInfo(&info); + count = 0; + if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask)) + for (i = 0; i < 8u * sizeof(procmask); i++) + count += 1 & (procmask >> i); - rc = info.dwNumberOfProcessors; - if (rc < 1) - rc = 1; + if (count > 0) + return count; - return rc; + return 1; } @@ -678,71 +651,6 @@ } -static int is_windows_version_or_greater(DWORD os_major, - DWORD os_minor, - WORD service_pack_major, - WORD service_pack_minor) { - OSVERSIONINFOEX osvi; - DWORDLONG condition_mask = 0; - int op = VER_GREATER_EQUAL; - - /* Initialize the OSVERSIONINFOEX structure. */ - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - osvi.dwMajorVersion = os_major; - osvi.dwMinorVersion = os_minor; - osvi.wServicePackMajor = service_pack_major; - osvi.wServicePackMinor = service_pack_minor; - - /* Initialize the condition mask. */ - VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op); - VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op); - VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op); - VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op); - - /* Perform the test. */ - return (int) VerifyVersionInfo( - &osvi, - VER_MAJORVERSION | VER_MINORVERSION | - VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, - condition_mask); -} - - -static int address_prefix_match(int family, - struct sockaddr* address, - struct sockaddr* prefix_address, - int prefix_len) { - uint8_t* address_data; - uint8_t* prefix_address_data; - int i; - - assert(address->sa_family == family); - assert(prefix_address->sa_family == family); - - if (family == AF_INET6) { - address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr); - prefix_address_data = - (uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr); - } else { - address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr); - prefix_address_data = - (uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr); - } - - for (i = 0; i < prefix_len >> 3; i++) { - if (address_data[i] != prefix_address_data[i]) - return 0; - } - - if (prefix_len % 8) - return prefix_address_data[i] == - (address_data[i] & (0xff << (8 - prefix_len % 8))); - - return 1; -} - - int uv_interface_addresses(uv_interface_address_t** addresses_ptr, int* count_ptr) { IP_ADAPTER_ADDRESSES* win_address_buf; @@ -755,26 +663,13 @@ uv_interface_address_t* uv_address; int count; - - int is_vista_or_greater; ULONG flags; *addresses_ptr = NULL; *count_ptr = 0; - is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0); - if (is_vista_or_greater) { - flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | - GAA_FLAG_SKIP_DNS_SERVER; - } else { - /* We need at least XP SP1. */ - if (!is_windows_version_or_greater(5, 1, 1, 0)) - return UV_ENOTSUP; - - flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | - GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX; - } - + flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER; /* Fetch the size of the adapters reported by windows, and then get the list * itself. */ @@ -861,19 +756,8 @@ continue; /* Compute the size of the interface name. */ - name_size = WideCharToMultiByte(CP_UTF8, - 0, - adapter->FriendlyName, - -1, - NULL, - 0, - NULL, - FALSE); - if (name_size <= 0) { - uv__free(win_address_buf); - return uv_translate_sys_error(GetLastError()); - } - uv_address_buf_size += name_size; + name_size = uv_utf16_length_as_wtf8(adapter->FriendlyName, -1); + uv_address_buf_size += name_size + 1; /* Count the number of addresses associated with this interface, and * compute the size. */ @@ -903,30 +787,25 @@ adapter != NULL; adapter = adapter->Next) { IP_ADAPTER_UNICAST_ADDRESS* unicast_address; - int name_size; - size_t max_name_size; + size_t name_size; + int r; if (adapter->OperStatus != IfOperStatusUp || adapter->FirstUnicastAddress == NULL) continue; /* Convert the interface name to UTF8. */ - max_name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf; - if (max_name_size > (size_t) INT_MAX) - max_name_size = INT_MAX; - name_size = WideCharToMultiByte(CP_UTF8, - 0, - adapter->FriendlyName, - -1, - name_buf, - (int) max_name_size, - NULL, - FALSE); - if (name_size <= 0) { + name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf; + r = uv__copy_utf16_to_utf8(adapter->FriendlyName, + -1, + name_buf, + &name_size); + if (r) { uv__free(win_address_buf); uv__free(uv_address_buf); - return uv_translate_sys_error(GetLastError()); + return r; } + name_size += 1; /* Add NUL byte. */ /* Add an uv_interface_address_t element for every unicast address. */ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*) @@ -938,37 +817,8 @@ sa = unicast_address->Address.lpSockaddr; - /* XP has no OnLinkPrefixLength field. */ - if (is_vista_or_greater) { - prefix_len = - ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength; - } else { - /* Prior to Windows Vista the FirstPrefix pointed to the list with - * single prefix for each IP address assigned to the adapter. - * Order of FirstPrefix does not match order of FirstUnicastAddress, - * so we need to find corresponding prefix. - */ - IP_ADAPTER_PREFIX* prefix; - prefix_len = 0; - - for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) { - /* We want the longest matching prefix. */ - if (prefix->Address.lpSockaddr->sa_family != sa->sa_family || - prefix->PrefixLength <= prefix_len) - continue; - - if (address_prefix_match(sa->sa_family, sa, - prefix->Address.lpSockaddr, prefix->PrefixLength)) { - prefix_len = prefix->PrefixLength; - } - } - - /* If there is no matching prefix information, return a single-host - * subnet mask (e.g. 255.255.255.255 for IPv4). - */ - if (!prefix_len) - prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32; - } + prefix_len = + ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength; memset(uv_address, 0, sizeof *uv_address); @@ -1017,63 +867,101 @@ } -void uv_free_interface_addresses(uv_interface_address_t* addresses, - int count) { - uv__free(addresses); -} - - int uv_getrusage(uv_rusage_t *uv_rusage) { - FILETIME createTime, exitTime, kernelTime, userTime; - SYSTEMTIME kernelSystemTime, userSystemTime; - PROCESS_MEMORY_COUNTERS memCounters; - IO_COUNTERS ioCounters; + FILETIME create_time, exit_time, kernel_time, user_time; + SYSTEMTIME kernel_system_time, user_system_time; + PROCESS_MEMORY_COUNTERS mem_counters; + IO_COUNTERS io_counters; int ret; - ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime); + ret = GetProcessTimes(GetCurrentProcess(), + &create_time, + &exit_time, + &kernel_time, + &user_time); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } - ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime); + ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } - ret = FileTimeToSystemTime(&userTime, &userSystemTime); + ret = FileTimeToSystemTime(&user_time, &user_system_time); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } ret = GetProcessMemoryInfo(GetCurrentProcess(), - &memCounters, - sizeof(memCounters)); + &mem_counters, + sizeof(mem_counters)); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } - ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters); + ret = GetProcessIoCounters(GetCurrentProcess(), &io_counters); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } memset(uv_rusage, 0, sizeof(*uv_rusage)); - uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 + - userSystemTime.wMinute * 60 + - userSystemTime.wSecond; - uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000; + uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 + + user_system_time.wMinute * 60 + + user_system_time.wSecond; + uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000; - uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 + - kernelSystemTime.wMinute * 60 + - kernelSystemTime.wSecond; - uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000; + uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 + + kernel_system_time.wMinute * 60 + + kernel_system_time.wSecond; + uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000; - uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount; - uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024; + uv_rusage->ru_majflt = (uint64_t) mem_counters.PageFaultCount; + uv_rusage->ru_maxrss = (uint64_t) mem_counters.PeakWorkingSetSize / 1024; - uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount; - uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount; + uv_rusage->ru_oublock = (uint64_t) io_counters.WriteOperationCount; + uv_rusage->ru_inblock = (uint64_t) io_counters.ReadOperationCount; + + return 0; +} + + +int uv_getrusage_thread(uv_rusage_t* uv_rusage) { + FILETIME create_time, exit_time, kernel_time, user_time; + SYSTEMTIME kernel_system_time, user_system_time; + int ret; + + ret = GetThreadTimes(GetCurrentThread(), + &create_time, + &exit_time, + &kernel_time, + &user_time); + if (ret == 0) { + return uv_translate_sys_error(GetLastError()); + } + + ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time); + if (ret == 0) { + return uv_translate_sys_error(GetLastError()); + } + + ret = FileTimeToSystemTime(&user_time, &user_system_time); + if (ret == 0) { + return uv_translate_sys_error(GetLastError()); + } + + memset(uv_rusage, 0, sizeof(*uv_rusage)); + + uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 + + user_system_time.wMinute * 60 + + user_system_time.wSecond; + uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000; + + uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 + + kernel_system_time.wMinute * 60 + + kernel_system_time.wSecond; + uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000; return 0; } @@ -1090,11 +978,16 @@ r = uv_os_getenv("USERPROFILE", buffer, size); /* Don't return an error if USERPROFILE was not found. */ - if (r != UV_ENOENT) + if (r != UV_ENOENT) { + /* USERPROFILE is empty or invalid */ + if (r == 0 && *size < 3) { + return UV_ENOENT; + } return r; + } - /* USERPROFILE is not set, so call uv__getpwuid_r() */ - r = uv__getpwuid_r(&pwd); + /* USERPROFILE is not set, so call uv_os_get_passwd() */ + r = uv_os_get_passwd(&pwd); if (r != 0) { return r; @@ -1117,8 +1010,8 @@ int uv_os_tmpdir(char* buffer, size_t* size) { + int r; wchar_t *path; - DWORD bufsize; size_t len; if (buffer == NULL || size == NULL || *size == 0) @@ -1129,13 +1022,19 @@ if (len == 0) { return uv_translate_sys_error(GetLastError()); } + + /* tmp path is empty or invalid */ + if (len < 3) { + return UV_ENOENT; + } + /* Include space for terminating null char. */ len += 1; path = uv__malloc(len * sizeof(wchar_t)); if (path == NULL) { return UV_ENOMEM; } - len = GetTempPathW(len, path); + len = GetTempPathW(len, path); if (len == 0) { uv__free(path); @@ -1150,45 +1049,9 @@ path[len] = L'\0'; } - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL); - - if (bufsize == 0) { - uv__free(path); - return uv_translate_sys_error(GetLastError()); - } else if (bufsize > *size) { - uv__free(path); - *size = bufsize; - return UV_ENOBUFS; - } - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - path, - -1, - buffer, - *size, - NULL, - NULL); + r = uv__copy_utf16_to_utf8(path, len, buffer, size); uv__free(path); - - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); - - *size = bufsize - 1; - return 0; -} - - -void uv_os_free_passwd(uv_passwd_t* pwd) { - if (pwd == NULL) - return; - - uv__free(pwd->username); - uv__free(pwd->homedir); - pwd->username = NULL; - pwd->homedir = NULL; + return r; } @@ -1199,96 +1062,72 @@ * If utf16 is null terminated, utf16len can be set to -1, otherwise it must * be specified. */ -int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8) { - DWORD bufsize; +int uv__convert_utf16_to_utf8(const WCHAR* utf16, size_t utf16len, char** utf8) { + size_t utf8_len = 0; if (utf16 == NULL) return UV_EINVAL; - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - utf16, - utf16len, - NULL, - 0, - NULL, - NULL); - - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); - - /* Allocate the destination buffer adding an extra byte for the terminating - * NULL. If utf16len is not -1 WideCharToMultiByte will not add it, so - * we do it ourselves always, just in case. */ - *utf8 = uv__malloc(bufsize + 1); - - if (*utf8 == NULL) - return UV_ENOMEM; - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - utf16, - utf16len, - *utf8, - bufsize, - NULL, - NULL); - - if (bufsize == 0) { - uv__free(*utf8); - *utf8 = NULL; - return uv_translate_sys_error(GetLastError()); - } - - (*utf8)[bufsize] = '\0'; - return 0; + *utf8 = NULL; + return uv_utf16_to_wtf8(utf16, utf16len, utf8, &utf8_len); } /* * Converts a UTF-8 string into a UTF-16 one. The resulting string is * null-terminated. - * - * If utf8 is null terminated, utf8len can be set to -1, otherwise it must - * be specified. */ -int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) { +int uv__convert_utf8_to_utf16(const char* utf8, WCHAR** utf16) { int bufsize; if (utf8 == NULL) return UV_EINVAL; - /* Check how much space we need */ - bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, NULL, 0); + /* Check how much space we need (including NUL). */ + bufsize = uv_wtf8_length_as_utf16(utf8); + if (bufsize < 0) + return UV__EINVAL; - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); - - /* Allocate the destination buffer adding an extra byte for the terminating - * NULL. If utf8len is not -1 MultiByteToWideChar will not add it, so - * we do it ourselves always, just in case. */ - *utf16 = uv__malloc(sizeof(WCHAR) * (bufsize + 1)); + /* Allocate the destination buffer. */ + *utf16 = uv__malloc(sizeof(WCHAR) * bufsize); if (*utf16 == NULL) return UV_ENOMEM; /* Convert to UTF-16 */ - bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, *utf16, bufsize); + uv_wtf8_to_utf16(utf8, *utf16, bufsize); - if (bufsize == 0) { - uv__free(*utf16); - *utf16 = NULL; - return uv_translate_sys_error(GetLastError()); - } - - (*utf16)[bufsize] = L'\0'; return 0; } -int uv__getpwuid_r(uv_passwd_t* pwd) { +/* + * Converts a UTF-16 string into a UTF-8 one in an existing buffer. The + * resulting string is null-terminated. + * + * If utf16 is null terminated, utf16len can be set to -1, otherwise it must + * be specified. + */ +int uv__copy_utf16_to_utf8(const WCHAR* utf16buffer, size_t utf16len, char* utf8, size_t *size) { + int r; + + if (utf8 == NULL || size == NULL) + return UV_EINVAL; + + if (*size == 0) { + *size = uv_utf16_length_as_wtf8(utf16buffer, utf16len); + r = UV_ENOBUFS; + } else { + *size -= 1; /* Reserve space for NUL. */ + r = uv_utf16_to_wtf8(utf16buffer, utf16len, &utf8, size); + } + if (r == UV_ENOBUFS) + *size += 1; /* Add space for NUL. */ + return r; +} + + +static int uv__getpwuid_r(uv_passwd_t* pwd) { HANDLE token; wchar_t username[UNLEN + 1]; wchar_t *path; @@ -1366,6 +1205,16 @@ } +int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) { + return UV_ENOTSUP; +} + + +int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) { + return UV_ENOTSUP; +} + + int uv_os_environ(uv_env_item_t** envitems, int* count) { wchar_t* env; wchar_t* penv; @@ -1426,7 +1275,7 @@ FreeEnvironmentStringsW(env); for (i = 0; i < cnt; i++) { - envitem = &(*envitems)[cnt]; + envitem = &(*envitems)[i]; uv__free(envitem->name); } uv__free(*envitems); @@ -1442,14 +1291,13 @@ wchar_t* var; DWORD varlen; wchar_t* name_w; - DWORD bufsize; size_t len; int r; if (name == NULL || buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - r = uv__convert_utf8_to_utf16(name, -1, &name_w); + r = uv__convert_utf8_to_utf16(name, &name_w); if (r != 0) return r; @@ -1461,6 +1309,9 @@ SetLastError(ERROR_SUCCESS); len = GetEnvironmentVariableW(name_w, var, varlen); + if (len == 0) + r = uv_translate_sys_error(GetLastError()); + if (len < varlen) break; @@ -1482,43 +1333,8 @@ uv__free(name_w); name_w = NULL; - if (len == 0) { - r = GetLastError(); - if (r != ERROR_SUCCESS) { - r = uv_translate_sys_error(r); - goto fail; - } - } - - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL); - - if (bufsize == 0) { - r = uv_translate_sys_error(GetLastError()); - goto fail; - } else if (bufsize > *size) { - *size = bufsize; - r = UV_ENOBUFS; - goto fail; - } - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - var, - -1, - buffer, - *size, - NULL, - NULL); - - if (bufsize == 0) { - r = uv_translate_sys_error(GetLastError()); - goto fail; - } - - *size = bufsize - 1; - r = 0; + if (r == 0) + r = uv__copy_utf16_to_utf8(var, len, buffer, size); fail: @@ -1540,12 +1356,12 @@ if (name == NULL || value == NULL) return UV_EINVAL; - r = uv__convert_utf8_to_utf16(name, -1, &name_w); + r = uv__convert_utf8_to_utf16(name, &name_w); if (r != 0) return r; - r = uv__convert_utf8_to_utf16(value, -1, &value_w); + r = uv__convert_utf8_to_utf16(value, &value_w); if (r != 0) { uv__free(name_w); @@ -1570,7 +1386,7 @@ if (name == NULL) return UV_EINVAL; - r = uv__convert_utf8_to_utf16(name, -1, &name_w); + r = uv__convert_utf8_to_utf16(name, &name_w); if (r != 0) return r; @@ -1587,9 +1403,6 @@ int uv_os_gethostname(char* buffer, size_t* size) { WCHAR buf[UV_MAXHOSTNAMESIZE]; - size_t len; - char* utf8_str; - int convert_result; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; @@ -1602,22 +1415,7 @@ if (pGetHostNameW(buf, UV_MAXHOSTNAMESIZE) != 0) return uv_translate_sys_error(WSAGetLastError()); - convert_result = uv__convert_utf16_to_utf8(buf, -1, &utf8_str); - - if (convert_result != 0) - return convert_result; - - len = strlen(utf8_str); - if (len >= *size) { - *size = len + 1; - uv__free(utf8_str); - return UV_ENOBUFS; - } - - memcpy(buffer, utf8_str, len + 1); - uv__free(utf8_str); - *size = len; - return 0; + return uv__copy_utf16_to_utf8(buf, -1, buffer, size); } @@ -1714,6 +1512,58 @@ return r; } +int uv_thread_getpriority(uv_thread_t tid, int* priority) { + DWORD err; + int r; + + if (priority == NULL) + return UV_EINVAL; + + r = GetThreadPriority(tid); + if (r == THREAD_PRIORITY_ERROR_RETURN) { + err = GetLastError(); + if (err == ERROR_INVALID_HANDLE) + return UV_ESRCH; + return uv_translate_sys_error(err); + } + + *priority = r; + return 0; +} + +int uv_thread_setpriority(uv_thread_t tid, int priority) { + DWORD err; + int r; + + switch (priority) { + case UV_THREAD_PRIORITY_HIGHEST: + r = SetThreadPriority(tid, THREAD_PRIORITY_HIGHEST); + break; + case UV_THREAD_PRIORITY_ABOVE_NORMAL: + r = SetThreadPriority(tid, THREAD_PRIORITY_ABOVE_NORMAL); + break; + case UV_THREAD_PRIORITY_NORMAL: + r = SetThreadPriority(tid, THREAD_PRIORITY_NORMAL); + break; + case UV_THREAD_PRIORITY_BELOW_NORMAL: + r = SetThreadPriority(tid, THREAD_PRIORITY_BELOW_NORMAL); + break; + case UV_THREAD_PRIORITY_LOWEST: + r = SetThreadPriority(tid, THREAD_PRIORITY_LOWEST); + break; + default: + return 0; + } + + if (r == 0) { + err = GetLastError(); + if (err == ERROR_INVALID_HANDLE) + return UV_ESRCH; + return uv_translate_sys_error(err); + } + + return 0; +} int uv_os_uname(uv_utsname_t* buffer) { /* Implementation loosely based on @@ -1723,7 +1573,7 @@ HKEY registry_key; WCHAR product_name_w[256]; DWORD product_name_w_size; - int version_size; + size_t version_size; int processor_level; int r; @@ -1734,27 +1584,14 @@ os_info.dwOSVersionInfoSize = sizeof(os_info); os_info.szCSDVersion[0] = L'\0'; - /* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx() - if RtlGetVersion() is not available. */ - if (pRtlGetVersion) { - pRtlGetVersion(&os_info); - } else { - /* Silence GetVersionEx() deprecation warning. */ - #ifdef _MSC_VER - #pragma warning(suppress : 4996) - #endif - if (GetVersionExW(&os_info) == 0) { - r = uv_translate_sys_error(GetLastError()); - goto error; - } - } + pRtlGetVersion(&os_info); /* Populate the version field. */ version_size = 0; r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, - KEY_QUERY_VALUE, + KEY_QUERY_VALUE | KEY_WOW64_64KEY, ®istry_key); if (r == ERROR_SUCCESS) { @@ -1769,37 +1606,45 @@ RegCloseKey(registry_key); if (r == ERROR_SUCCESS) { - version_size = WideCharToMultiByte(CP_UTF8, - 0, - product_name_w, - -1, - buffer->version, - sizeof(buffer->version), - NULL, - NULL); - if (version_size == 0) { - r = uv_translate_sys_error(GetLastError()); - goto error; + /* Windows 11 shares dwMajorVersion with Windows 10 + * this workaround tries to disambiguate that by checking + * if the dwBuildNumber is from Windows 11 releases (>= 22000). + * + * This workaround replaces the ProductName key value + * from "Windows 10 *" to "Windows 11 *" */ + if (os_info.dwMajorVersion == 10 && + os_info.dwBuildNumber >= 22000 && + product_name_w_size >= ARRAY_SIZE(L"Windows 10")) { + /* If ProductName starts with "Windows 10" */ + if (wcsncmp(product_name_w, L"Windows 10", ARRAY_SIZE(L"Windows 10") - 1) == 0) { + /* Bump 10 to 11 */ + product_name_w[9] = '1'; + } } + + version_size = sizeof(buffer->version); + r = uv__copy_utf16_to_utf8(product_name_w, + -1, + buffer->version, + &version_size); + if (r) + goto error; } } /* Append service pack information to the version if present. */ if (os_info.szCSDVersion[0] != L'\0') { if (version_size > 0) - buffer->version[version_size - 1] = ' '; + buffer->version[version_size++] = ' '; - if (WideCharToMultiByte(CP_UTF8, - 0, - os_info.szCSDVersion, - -1, - buffer->version + version_size, - sizeof(buffer->version) - version_size, - NULL, - NULL) == 0) { - r = uv_translate_sys_error(GetLastError()); + version_size = sizeof(buffer->version) - version_size; + r = uv__copy_utf16_to_utf8(os_info.szCSDVersion, + -1, + buffer->version + + sizeof(buffer->version) - version_size, + &version_size); + if (r) goto error; - } } /* Populate the sysname field. */ @@ -1862,6 +1707,9 @@ case PROCESSOR_ARCHITECTURE_ARM: uv__strscpy(buffer->machine, "arm", sizeof(buffer->machine)); break; + case PROCESSOR_ARCHITECTURE_ARM64: + uv__strscpy(buffer->machine, "arm64", sizeof(buffer->machine)); + break; default: uv__strscpy(buffer->machine, "unknown", sizeof(buffer->machine)); break; @@ -1894,10 +1742,13 @@ return 0; } -int uv__random_rtlgenrandom(void* buf, size_t buflen) { +int uv__random_winrandom(void* buf, size_t buflen) { if (buflen == 0) return 0; + if (pProcessPrng != NULL && pProcessPrng(buf, buflen)) + return 0; + if (SystemFunction036(buf, buflen) == FALSE) return UV_EIO;
diff --git a/Utilities/cmlibuv/src/win/winapi.c b/Utilities/cmlibuv/src/win/winapi.c index 53147b8..791332f 100644 --- a/Utilities/cmlibuv/src/win/winapi.c +++ b/Utilities/cmlibuv/src/win/winapi.c
@@ -36,112 +36,160 @@ sNtQuerySystemInformation pNtQuerySystemInformation; sNtQueryInformationProcess pNtQueryInformationProcess; -/* Kernel32 function pointers */ -sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; - /* Powrprof.dll function pointer */ sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; +/* bcryptprimitives.dll function pointer */ +sProcessPrng pProcessPrng; + /* User32.dll function pointer */ sSetWinEventHook pSetWinEventHook; /* ws2_32.dll function pointer */ uv_sGetHostNameW pGetHostNameW; +/* api-ms-win-core-file-l2-1-4.dll function pointer */ +sGetFileInformationByName pGetFileInformationByName; + +#if _WIN32_WINNT < 0x0602 /* _WIN32_WINNT_WIN8 */ +sGetSystemTimePreciseAsFileTime pGetSystemTimePreciseAsFileTime; +#endif + void uv__winapi_init(void) { HMODULE ntdll_module; HMODULE powrprof_module; HMODULE user32_module; - HMODULE kernel32_module; HMODULE ws2_32_module; + HMODULE bcryptprimitives_module; + HMODULE api_win_core_file_module; +#if _WIN32_WINNT < 0x0602 /* _WIN32_WINNT_WIN8 */ + HMODULE kernel32_module; +#endif - ntdll_module = GetModuleHandleA("ntdll.dll"); + union { + FARPROC proc; + sRtlGetVersion pRtlGetVersion; + sRtlNtStatusToDosError pRtlNtStatusToDosError; + sNtDeviceIoControlFile pNtDeviceIoControlFile; + sNtQueryInformationFile pNtQueryInformationFile; + sNtSetInformationFile pNtSetInformationFile; + sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile; + sNtQueryDirectoryFile pNtQueryDirectoryFile; + sNtQuerySystemInformation pNtQuerySystemInformation; + sNtQueryInformationProcess pNtQueryInformationProcess; + sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; + sProcessPrng pProcessPrng; + sSetWinEventHook pSetWinEventHook; + uv_sGetHostNameW pGetHostNameW; + sGetFileInformationByName pGetFileInformationByName; +#if _WIN32_WINNT < 0x0602 /* _WIN32_WINNT_WIN8 */ + sGetSystemTimePreciseAsFileTime pGetSystemTimePreciseAsFileTime; +#endif + } u; + + ntdll_module = GetModuleHandleW(L"ntdll.dll"); if (ntdll_module == NULL) { - uv_fatal_error(GetLastError(), "GetModuleHandleA"); + uv_fatal_error(GetLastError(), "GetModuleHandleW"); } - pRtlGetVersion = (sRtlGetVersion) GetProcAddress(ntdll_module, - "RtlGetVersion"); + u.proc = GetProcAddress(ntdll_module, "RtlGetVersion"); + pRtlGetVersion = u.pRtlGetVersion; - pRtlNtStatusToDosError = (sRtlNtStatusToDosError) GetProcAddress( - ntdll_module, - "RtlNtStatusToDosError"); + u.proc = GetProcAddress(ntdll_module, "RtlNtStatusToDosError"); + pRtlNtStatusToDosError = u.pRtlNtStatusToDosError; if (pRtlNtStatusToDosError == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - pNtDeviceIoControlFile = (sNtDeviceIoControlFile) GetProcAddress( - ntdll_module, - "NtDeviceIoControlFile"); + u.proc = GetProcAddress(ntdll_module, "NtDeviceIoControlFile"); + pNtDeviceIoControlFile = u.pNtDeviceIoControlFile; if (pNtDeviceIoControlFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - pNtQueryInformationFile = (sNtQueryInformationFile) GetProcAddress( - ntdll_module, - "NtQueryInformationFile"); + u.proc = GetProcAddress(ntdll_module, "NtQueryInformationFile"); + pNtQueryInformationFile = u.pNtQueryInformationFile; if (pNtQueryInformationFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - pNtSetInformationFile = (sNtSetInformationFile) GetProcAddress( - ntdll_module, - "NtSetInformationFile"); + u.proc = GetProcAddress(ntdll_module, "NtSetInformationFile"); + pNtSetInformationFile = u.pNtSetInformationFile; if (pNtSetInformationFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - pNtQueryVolumeInformationFile = (sNtQueryVolumeInformationFile) - GetProcAddress(ntdll_module, "NtQueryVolumeInformationFile"); + u.proc = GetProcAddress(ntdll_module, "NtQueryVolumeInformationFile"); + pNtQueryVolumeInformationFile = u.pNtQueryVolumeInformationFile; if (pNtQueryVolumeInformationFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - pNtQueryDirectoryFile = (sNtQueryDirectoryFile) - GetProcAddress(ntdll_module, "NtQueryDirectoryFile"); - if (pNtQueryVolumeInformationFile == NULL) { + u.proc = GetProcAddress(ntdll_module, "NtQueryDirectoryFile"); + pNtQueryDirectoryFile = u.pNtQueryDirectoryFile; + if (pNtQueryDirectoryFile == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - pNtQuerySystemInformation = (sNtQuerySystemInformation) GetProcAddress( - ntdll_module, - "NtQuerySystemInformation"); + u.proc = GetProcAddress(ntdll_module, "NtQuerySystemInformation"); + pNtQuerySystemInformation = u.pNtQuerySystemInformation; if (pNtQuerySystemInformation == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - pNtQueryInformationProcess = (sNtQueryInformationProcess) GetProcAddress( - ntdll_module, - "NtQueryInformationProcess"); + u.proc = GetProcAddress(ntdll_module, "NtQueryInformationProcess"); + pNtQueryInformationProcess = u.pNtQueryInformationProcess; if (pNtQueryInformationProcess == NULL) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - kernel32_module = GetModuleHandleA("kernel32.dll"); - if (kernel32_module == NULL) { - uv_fatal_error(GetLastError(), "GetModuleHandleA"); - } - - pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress( - kernel32_module, - "GetQueuedCompletionStatusEx"); - - powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + powrprof_module = LoadLibraryExA("powrprof.dll", + NULL, + LOAD_LIBRARY_SEARCH_SYSTEM32); if (powrprof_module != NULL) { - pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification) - GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification"); + u.proc = GetProcAddress(powrprof_module, + "PowerRegisterSuspendResumeNotification"); + pPowerRegisterSuspendResumeNotification = + u.pPowerRegisterSuspendResumeNotification; } - user32_module = GetModuleHandleA("user32.dll"); + bcryptprimitives_module = LoadLibraryExA("bcryptprimitives.dll", + NULL, + LOAD_LIBRARY_SEARCH_SYSTEM32); + if (bcryptprimitives_module != NULL) { + u.proc = GetProcAddress(bcryptprimitives_module, "ProcessPrng"); + pProcessPrng = u.pProcessPrng; + } + + user32_module = GetModuleHandleW(L"user32.dll"); if (user32_module != NULL) { - pSetWinEventHook = (sSetWinEventHook) - GetProcAddress(user32_module, "SetWinEventHook"); + u.proc = GetProcAddress(user32_module, "SetWinEventHook"); + pSetWinEventHook = u.pSetWinEventHook; } - ws2_32_module = GetModuleHandleA("ws2_32.dll"); + ws2_32_module = GetModuleHandleW(L"ws2_32.dll"); if (ws2_32_module != NULL) { - pGetHostNameW = (uv_sGetHostNameW) GetProcAddress( - ws2_32_module, - "GetHostNameW"); + u.proc = GetProcAddress(ws2_32_module, "GetHostNameW"); + pGetHostNameW = u.pGetHostNameW; } + + api_win_core_file_module = + GetModuleHandleW(L"api-ms-win-core-file-l2-1-4.dll"); + if (api_win_core_file_module != NULL) { + u.proc = GetProcAddress(api_win_core_file_module, + "GetFileInformationByName"); + pGetFileInformationByName = u.pGetFileInformationByName; + } + +#if _WIN32_WINNT < 0x0602 /* _WIN32_WINNT_WIN8 */ + kernel32_module = GetModuleHandleW(L"kernel32.dll"); + if (kernel32_module != NULL) { + u.proc = GetProcAddress(kernel32_module, + "GetSystemTimePreciseAsFileTime"); + pGetSystemTimePreciseAsFileTime = u.pGetSystemTimePreciseAsFileTime; + if (pGetSystemTimePreciseAsFileTime == NULL) { + pGetSystemTimePreciseAsFileTime = GetSystemTimeAsFileTime; + } + } +#endif }
diff --git a/Utilities/cmlibuv/src/win/winapi.h b/Utilities/cmlibuv/src/win/winapi.h index e815f8f..ca4ecc1 100644 --- a/Utilities/cmlibuv/src/win/winapi.h +++ b/Utilities/cmlibuv/src/win/winapi.h
@@ -4125,48 +4125,64 @@ # define DEVICE_TYPE DWORD #endif -#ifndef VOLUME_NAME_DOS -# define VOLUME_NAME_DOS 0x0 +#ifndef NTDDI_WIN11_ZN +# define NTDDI_WIN11_ZN 0x0A00000E #endif -#ifndef MAPVK_VK_TO_VSC -# define MAPVK_VK_TO_VSC (0) +/* API is defined in newer SDKS */ +#if (NTDDI_VERSION < NTDDI_WIN11_ZN) +typedef struct _FILE_STAT_BASIC_INFORMATION { + LARGE_INTEGER FileId; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; + ULONG ReparseTag; + ULONG NumberOfLinks; + ULONG DeviceType; + ULONG DeviceCharacteristics; + ULONG Reserved; + LARGE_INTEGER VolumeSerialNumber; + FILE_ID_128 FileId128; +} FILE_STAT_BASIC_INFORMATION; #endif -/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does - * not. - */ -#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR) - typedef struct _REPARSE_DATA_BUFFER { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union { - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - struct { - ULONG StringCount; - WCHAR StringList[1]; - } AppExecLinkReparseBuffer; - }; - } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; -#endif +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + ULONG Version; + UCHAR PathBuffer[1]; + } LinuxSymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + struct { + ULONG StringCount; + WCHAR StringList[1]; + } AppExecLinkReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; typedef struct _IO_STATUS_BLOCK { union { @@ -4232,6 +4248,15 @@ FileNumaNodeInformation, FileStandardLinkInformation, FileRemoteProtocolInformation, + FileRenameInformationBypassAccessCheck, + FileLinkInformationBypassAccessCheck, + FileVolumeNameInformation, + FileIdInformation, + FileIdExtdDirectoryInformation, + FileReplaceCompletionInformation, + FileHardLinkFullIdInformation, + FileIdExtdBothDirectoryInformation, + FileDispositionInformationEx, /* based on https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_file_information_class */ FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; @@ -4266,6 +4291,22 @@ WCHAR FileName[1]; } FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; +typedef struct _FILE_ID_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION; + typedef struct _FILE_BASIC_INFORMATION { LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; @@ -4331,6 +4372,10 @@ BOOLEAN DeleteFile; } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; +typedef struct _FILE_DISPOSITION_INFORMATION_EX { + DWORD Flags; +} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX; + typedef struct _FILE_PIPE_LOCAL_INFORMATION { ULONG NamedPipeType; ULONG NamedPipeConfiguration; @@ -4435,6 +4480,14 @@ ULONG ByteOffsetForPartitionAlignment; } FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION; +typedef struct _PROCESS_BASIC_INFORMATION { + PVOID Reserved1; + PVOID PebBaseAddress; + PVOID Reserved2[2]; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; + typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { LARGE_INTEGER IdleTime; LARGE_INTEGER KernelTime; @@ -4448,6 +4501,10 @@ # define SystemProcessorPerformanceInformation 8 #endif +#ifndef ProcessBasicInformation +# define ProcessBasicInformation 0 +#endif + #ifndef ProcessConsoleHostProcess # define ProcessConsoleHostProcess 49 #endif @@ -4529,6 +4586,9 @@ #ifndef IO_REPARSE_TAG_SYMLINK # define IO_REPARSE_TAG_SYMLINK (0xA000000CL) #endif +#ifndef IO_REPARSE_TAG_LX_SYMLINK +# define IO_REPARSE_TAG_LX_SYMLINK (0xA000001DL) +#endif #ifndef IO_REPARSE_TAG_APPEXECLINK # define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL) #endif @@ -4619,16 +4679,6 @@ # define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 #endif -#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) \ - || (defined(_MSC_VER) && _MSC_VER < 1500) - typedef struct _OVERLAPPED_ENTRY { - ULONG_PTR lpCompletionKey; - LPOVERLAPPED lpOverlapped; - ULONG_PTR Internal; - DWORD dwNumberOfBytesTransferred; - } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY; -#endif - /* from wincon.h */ #ifndef ENABLE_INSERT_MODE # define ENABLE_INSERT_MODE 0x20 @@ -4675,14 +4725,6 @@ # define ERROR_MUI_FILE_NOT_LOADED 15105 #endif -typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx) - (HANDLE CompletionPort, - LPOVERLAPPED_ENTRY lpCompletionPortEntries, - ULONG ulCount, - PULONG ulNumEntriesRemoved, - DWORD dwMilliseconds, - BOOL fAlertable); - /* from powerbase.h */ #ifndef DEVICE_NOTIFY_CALLBACK # define DEVICE_NOTIFY_CALLBACK 2 @@ -4716,6 +4758,8 @@ HANDLE Recipient, _PHPOWERNOTIFY RegistrationHandle); +typedef BOOL (WINAPI *sProcessPrng)(/*_Out_*/PBYTE pbData, SIZE_T cbData); + /* from Winuser.h */ typedef VOID (CALLBACK* WINEVENTPROC) (HWINEVENTHOOK hWinEventHook, @@ -4748,6 +4792,24 @@ # define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17) #endif +/* from winnt.h */ +/* API is defined in newer SDKS */ +#if (NTDDI_VERSION < NTDDI_WIN11_ZN) +typedef enum _FILE_INFO_BY_NAME_CLASS { + FileStatByNameInfo, + FileStatLxByNameInfo, + FileCaseSensitiveByNameInfo, + FileStatBasicByNameInfo, + MaximumFileInfoByNameClass +} FILE_INFO_BY_NAME_CLASS; +#endif + +typedef BOOL(WINAPI* sGetFileInformationByName)( + PCWSTR FileName, + FILE_INFO_BY_NAME_CLASS FileInformationClass, + PVOID FileInfoBuffer, + ULONG FileInfoBufferSize); + /* Ntdll function pointers */ extern sRtlGetVersion pRtlGetVersion; extern sRtlNtStatusToDosError pRtlNtStatusToDosError; @@ -4759,15 +4821,18 @@ extern sNtQuerySystemInformation pNtQuerySystemInformation; extern sNtQueryInformationProcess pNtQueryInformationProcess; -/* Kernel32 function pointers */ -extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; - /* Powrprof.dll function pointer */ extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; +/* bcryptprimitives.dll function pointer */ +extern sProcessPrng pProcessPrng; + /* User32.dll function pointer */ extern sSetWinEventHook pSetWinEventHook; +/* api-ms-win-core-file-l2-1-4.dll function pointers */ +extern sGetFileInformationByName pGetFileInformationByName; + /* ws2_32.dll function pointer */ /* mingw doesn't have this definition, so let's declare it here locally */ typedef int (WINAPI *uv_sGetHostNameW) @@ -4775,4 +4840,11 @@ int); extern uv_sGetHostNameW pGetHostNameW; +#if _WIN32_WINNT < 0x0602 /* _WIN32_WINNT_WIN8 */ +typedef VOID (WINAPI *sGetSystemTimePreciseAsFileTime) + (LPFILETIME lpSystemTimeAsFileTime); +extern sGetSystemTimePreciseAsFileTime pGetSystemTimePreciseAsFileTime; +#define GetSystemTimePreciseAsFileTime pGetSystemTimePreciseAsFileTime +#endif + #endif /* UV_WIN_WINAPI_H_ */
diff --git a/Utilities/cmlibuv/src/win/winsock.h b/Utilities/cmlibuv/src/win/winsock.h index 153632c..e14b8b5 100644 --- a/Utilities/cmlibuv/src/win/winsock.h +++ b/Utilities/cmlibuv/src/win/winsock.h
@@ -38,10 +38,6 @@ # define SO_UPDATE_CONNECT_CONTEXT 0x7010 #endif -#ifndef TCP_KEEPALIVE -# define TCP_KEEPALIVE 3 -#endif - #ifndef IPV6_V6ONLY # define IPV6_V6ONLY 27 #endif @@ -62,6 +58,30 @@ # define MCAST_LEAVE_SOURCE_GROUP 46 #endif +#ifndef SIO_KEEPALIVE_VALS +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) +struct tcp_keepalive { + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; +}; +#endif + +/* + * TCP keepalive definitions on MinGW are located in <netinet/tcp.h>. + */ +#ifndef TCP_KEEPIDLE +#define TCP_KEEPIDLE 0x03 /* start keepalives after this period */ +#endif + +#ifndef TCP_KEEPINTVL +#define TCP_KEEPINTVL 0x11 /* interval between keepalives */ +#endif + +#ifndef TCP_KEEPCNT +#define TCP_KEEPCNT 0x10 /* number of keepalives before death */ +#endif + /* * TDI defines that are only in the DDK. * We only need receive flags so far. @@ -154,48 +174,6 @@ #define IOCTL_AFD_POLL \ _AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED) -#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) \ - || (defined(_MSC_VER) && _MSC_VER < 1500) -typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP { - /* FIXME: __C89_NAMELESS was removed */ - /* __C89_NAMELESS */ union { - ULONGLONG Alignment; - /* __C89_NAMELESS */ struct { - ULONG Length; - DWORD Flags; - }; - }; - struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next; - SOCKET_ADDRESS Address; - IP_PREFIX_ORIGIN PrefixOrigin; - IP_SUFFIX_ORIGIN SuffixOrigin; - IP_DAD_STATE DadState; - ULONG ValidLifetime; - ULONG PreferredLifetime; - ULONG LeaseLifetime; -} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP; - -typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH { - union { - ULONGLONG Alignment; - struct { - ULONG Length; - DWORD Flags; - }; - }; - struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next; - SOCKET_ADDRESS Address; - IP_PREFIX_ORIGIN PrefixOrigin; - IP_SUFFIX_ORIGIN SuffixOrigin; - IP_DAD_STATE DadState; - ULONG ValidLifetime; - ULONG PreferredLifetime; - ULONG LeaseLifetime; - UINT8 OnLinkPrefixLength; -} IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH; - -#endif - int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr, struct sockaddr_storage* storage);
diff --git a/Utilities/std/cm/ranges b/Utilities/std/cm/ranges new file mode 100644 index 0000000..54ab47b --- /dev/null +++ b/Utilities/std/cm/ranges
@@ -0,0 +1,126 @@ +// -*-c++-*- +// vim: set ft=cpp: + +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +# define CMake_HAVE_CXX_RANGES +#endif + +#if defined(CMake_HAVE_CXX_RANGES) +# include <ranges> +#else +# include <algorithm> +# include <iterator> +# include <tuple> +# include <vector> + +# include <cm/type_traits> +# include <cmext/type_traits> +#endif + +namespace cm { +namespace ranges { +namespace views { +#if defined(CMake_HAVE_CXX_RANGES) + +using std::ranges::views::keys; +using std::ranges::views::values; + +#else + +// associative containers +template < + typename Range, + cm::enable_if_t<cm::is_associative_container<Range>::value || + cm::is_unordered_associative_container<Range>::value, + int> = 0> +std::vector<typename Range::key_type> keys(Range const& range) +{ + using key_type = typename Range::key_type; + + std::vector<key_type> keys; + keys.reserve(range.size()); + + std::transform(range.begin(), range.end(), + std::back_inserter<std::vector<key_type>>(keys), + [](typename Range::value_type const& entry) -> key_type { + return entry.first; + }); + + return keys; +} + +template < + typename Range, + cm::enable_if_t<cm::is_associative_container<Range>::value || + cm::is_unordered_associative_container<Range>::value, + int> = 0> +std::vector<typename Range::mapped_type> values(Range const& range) +{ + using value_type = typename Range::mapped_type; + + std::vector<value_type> values; + values.reserve(range.size()); + + std::transform(range.begin(), range.end(), + std::back_inserter<std::vector<value_type>>(values), + [](typename Range::value_type const& entry) -> value_type { + return entry.second; + }); + + return values; +} + +// sequence containers with std::pair<> or std::tuple<> as elements +template <typename Range, + cm::enable_if_t<cm::is_sequence_container<Range>::value && + cm::is_tuple<1, typename Range::value_type>::value, + int> = 0> +std::vector<typename std::tuple_element<0, typename Range::value_type>::type> +keys(Range const& range) +{ + using key_type = + typename std::tuple_element<0, typename Range::value_type>::type; + + std::vector<key_type> keys; + keys.reserve(range.size()); + + std::transform(range.begin(), range.end(), + std::back_inserter<std::vector<key_type>>(keys), + [](typename Range::value_type const& entry) -> key_type { + return std::get<0>(entry); + }); + + return keys; +} + +template <typename Range, + cm::enable_if_t<cm::is_sequence_container<Range>::value && + cm::is_tuple<2, typename Range::value_type>::value, + int> = 0> +std::vector<typename std::tuple_element<1, typename Range::value_type>::type> +values(Range const& range) +{ + using value_type = + typename std::tuple_element<1, typename Range::value_type>::type; + + std::vector<value_type> values; + values.reserve(range.size()); + + std::transform(range.begin(), range.end(), + std::back_inserter<std::vector<value_type>>(values), + [](typename Range::value_type const& entry) -> value_type { + return std::get<1>(entry); + }); + + return values; +} + +#endif +} +} +namespace views = ranges::views; +}
diff --git a/Utilities/std/cmext/algorithm b/Utilities/std/cmext/algorithm index cc85b0a..2aa1ec5 100644 --- a/Utilities/std/cmext/algorithm +++ b/Utilities/std/cmext/algorithm
@@ -10,6 +10,7 @@ #include <memory> #include <utility> +#include <cm/ranges> #include <cm/type_traits> #include <cmext/iterator> #include <cmext/type_traits> @@ -251,4 +252,91 @@ } #endif +template < + typename Range, + cm::enable_if_t<cm::is_associative_container<Range>::value || + cm::is_unordered_associative_container<Range>::value, + int> = 0> +std::vector<typename Range::key_type> keys(Range const& range) +{ +#if defined(CMake_HAVE_CXX_RANGES) + using key_type = typename Range::key_type; + +# if __cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L) + return std::vector<key_type>{ std::from_range, std::views::keys(range) }; +# else + auto view = std::views::keys(range); + return std::vector<key_type>{ view.begin(), view.end() }; +# endif +#else + return cm::views::keys(range); +#endif +} + +template < + typename Range, + cm::enable_if_t<cm::is_associative_container<Range>::value || + cm::is_unordered_associative_container<Range>::value, + int> = 0> +std::vector<typename Range::mapped_type> values(Range const& range) +{ +#if defined(CMake_HAVE_CXX_RANGES) + using value_type = typename Range::mapped_type; + +# if __cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L) + return std::vector<value_type>{ std::from_range, std::views::values(range) }; +# else + auto view = std::views::values(range); + return std::vector<value_type>{ view.begin(), view.end() }; +# endif +#else + return cm::views::values(range); +#endif +} + +// sequence containers with std::pair<> or std::tuple<> as elements +template <typename Range, + cm::enable_if_t<cm::is_sequence_container<Range>::value && + cm::is_tuple<1, typename Range::value_type>::value, + int> = 0> +std::vector<typename std::tuple_element<0, typename Range::value_type>::type> +keys(Range const& range) +{ +#if defined(CMake_HAVE_CXX_RANGES) + using key_type = + typename std::tuple_element<0, typename Range::value_type>::type; + +# if __cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L) + return std::vector<key_type>{ std::from_range, std::views::keys(range) }; +# else + auto view = std::views::keys(range); + return std::vector<key_type>{ view.begin(), view.end() }; +# endif +#else + return cm::views::keys(range); +#endif +} + +template <typename Range, + cm::enable_if_t<cm::is_sequence_container<Range>::value && + cm::is_tuple<2, typename Range::value_type>::value, + int> = 0> +std::vector<typename std::tuple_element<1, typename Range::value_type>::type> +values(Range const& range) +{ +#if defined(CMake_HAVE_CXX_RANGES) + using value_type = + typename std::tuple_element<1, typename Range::value_type>::type; + +# if __cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L) + return std::vector<value_type>{ std::from_range, std::views::values(range) }; +# else + auto view = std::views::values(range); + return std::vector<value_type>{ view.begin(), view.end() }; +# endif +#else + return cm::views::values(range); +#endif +} + } // namespace cm
diff --git a/Utilities/std/cmext/type_traits b/Utilities/std/cmext/type_traits index 07fa448..922b8c3 100644 --- a/Utilities/std/cmext/type_traits +++ b/Utilities/std/cmext/type_traits
@@ -5,7 +5,9 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #pragma once +#include <cstddef> #include <memory> +#include <tuple> #include <cm/type_traits> @@ -39,6 +41,28 @@ { }; +// type traits for std::pair template +template <typename, typename = void_t<>> +struct is_pair : std::false_type +{ +}; +template <typename T> +struct is_pair<T, void_t<typename T::first_type, typename T::second_type>> + : std::true_type +{ +}; + +// type traits for std::tuple template +template <std::size_t N, typename T, typename = void_t<>> +struct is_tuple : std::false_type +{ +}; +template <std::size_t N, typename T> +struct is_tuple<N, T, void_t<typename std::tuple_element<N - 1, T>::type>> + : std::true_type +{ +}; + // type traits for containers template <typename, typename = void_t<>> struct is_container : std::false_type
diff --git a/bootstrap b/bootstrap index b7d608d..d56085b 100755 --- a/bootstrap +++ b/bootstrap
@@ -154,9 +154,6 @@ https://gitlab.kitware.com/cmake/cmake/-/issues/17137 Use CMake 3.9 or lower instead.' - cmake_system_hpux=true -else - cmake_system_hpux=false fi # Determine whether this is AIX @@ -166,27 +163,6 @@ cmake_system_aix=false fi -# Determine whether this is Linux -if echo "${cmake_system}" | grep Linux >/dev/null 2>&1; then - cmake_system_linux=true -else - cmake_system_linux=false -fi - -# Determine whether this is a PA-RISC machine -# This only works for Linux or HP-UX, not other PA-RISC OSs (BSD maybe?). Also -# may falsely detect parisc on HP-UX m68k -cmake_machine_parisc=false -if ${cmake_system_linux}; then - if uname -m | grep parisc >/dev/null 2>&1; then - cmake_machine_parisc=true - fi -elif ${cmake_system_hpux}; then - if uname -m | grep ia64 >/dev/null 2>&1; then : ; else - cmake_machine_parisc=true - fi -fi - # Choose the generator to use for bootstrapping. if ${cmake_system_mingw}; then # Bootstrapping from an MSYS prompt. @@ -311,12 +287,15 @@ cmCMakeLanguageCommand \ cmCMakeMinimumRequired \ cmList \ + cmCMakeDiagnosticCommand \ cmCMakePath \ cmCMakePathCommand \ cmCMakePolicyCommand \ cmCMakeString \ cmCPackPropertiesGenerator \ + cmCacheDocumentationTable \ cmCacheManager \ + cmCachePatternTable \ cmCommands \ cmCommonTargetGenerator \ cmComputeComponentGraph \ @@ -336,10 +315,14 @@ cmCxxModuleUsageEffects \ cmDefinePropertyCommand \ cmDefinitions \ + cmDiagnostics \ + cmDiagnosticContext \ + cmDiscoverTestsCommand \ cmDocumentationFormatter \ cmELF \ cmEnableLanguageCommand \ cmEnableTestingCommand \ + cmEnvironment \ cmEvaluatedTargetProperty \ cmExecProgramCommand \ cmExecuteProcessCommand \ @@ -360,6 +343,7 @@ cmFileCopier \ cmFileInstaller \ cmFileSet \ + cmFileSetMetadata \ cmFileTime \ cmFileTimeCache \ cmFileTimes \ @@ -385,14 +369,16 @@ cmGeneratorExpressionLexer \ cmGeneratorExpressionNode \ cmGeneratorExpressionParser \ + cmGeneratorFileSet \ + cmGeneratorFileSets \ cmGeneratorTarget \ cmGeneratorTarget_CompatibleInterface \ + cmGeneratorTarget_HeaderSetVerification \ cmGeneratorTarget_IncludeDirectories \ cmGeneratorTarget_Link \ cmGeneratorTarget_LinkDirectories \ cmGeneratorTarget_Options \ cmGeneratorTarget_Sources \ - cmGeneratorTarget_TargetPropertyEntry \ cmGeneratorTarget_TransitiveProperty \ cmGetCMakePropertyCommand \ cmGetDirectoryPropertyCommand \ @@ -424,6 +410,7 @@ cmInstallGenerator \ cmInstallGetRuntimeDependenciesGenerator \ cmInstallImportedRuntimeArtifactsGenerator \ + cmInstallDirs \ cmInstallRuntimeDependencySet \ cmInstallRuntimeDependencySetGenerator \ cmInstallScriptGenerator \ @@ -512,6 +499,7 @@ cmTargetPrecompileHeadersCommand \ cmTargetPropCommandBase \ cmTargetPropertyComputer \ + cmTargetPropertyEntry \ cmTargetSourcesCommand \ cmTargetTraceDependencies \ cmTest \ @@ -1765,7 +1753,7 @@ uv_c_flags="" if ${cmake_system_mingw}; then uv_c_flags="${uv_c_flags} -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600" - libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -lole32 -loleaut32 -luuid" + libs="${libs} -ladvapi32 -ldbghelp -liphlpapi -lole32 -loleaut32 -lpsapi -lshell32 -luser32 -luserenv -luuid -lws2_32" else case "${cmake_system}" in *AIX*)